[launchd-changes] [23784] branches/PR-6424345
source_changes at macosforge.org
source_changes at macosforge.org
Mon Jan 19 19:58:33 PST 2009
Revision: 23784
http://trac.macosforge.org/projects/launchd/changeset/23784
Author: nectar at apple.com
Date: 2009-01-19 19:58:30 -0800 (Mon, 19 Jan 2009)
Log Message:
-----------
Merge from trunk
Modified Paths:
--------------
branches/PR-6424345/launchd/src/launch_priv.h
branches/PR-6424345/launchd/src/launchctl.c
branches/PR-6424345/launchd/src/launchd.c
branches/PR-6424345/launchd/src/launchd.plist.5
branches/PR-6424345/launchd/src/launchd_core_logic.c
branches/PR-6424345/launchd/src/launchd_core_logic.h
branches/PR-6424345/launchd/src/launchd_runtime.c
branches/PR-6424345/launchd/src/launchd_unix_ipc.c
branches/PR-6424345/launchd/src/libbootstrap.c
branches/PR-6424345/launchd/src/liblaunch.c
branches/PR-6424345/launchd/src/libvproc.c
branches/PR-6424345/launchd/src/protocol_vproc.defs
branches/PR-6424345/launchd/src/vproc.h
branches/PR-6424345/launchd/src/vproc_priv.h
branches/PR-6424345/launchd.xcodeproj/project.pbxproj
Property Changed:
----------------
branches/PR-6424345/
Property changes on: branches/PR-6424345
___________________________________________________________________
Modified: svn:mergeinfo
- /branches/PR-5092682:23731-23742
/branches/PR-5898404:23681-23700
/branches/PR-5978442:23651-23701
/branches/PR-6132016:23719-23738
+ /branches/PR-5092682:23731-23742
/branches/PR-5898404:23681-23700
/branches/PR-5978442:23651-23701
/branches/PR-6132016:23719-23738
/trunk:23759-23783
Modified: branches/PR-6424345/launchd/src/launch_priv.h
===================================================================
--- branches/PR-6424345/launchd/src/launch_priv.h 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launch_priv.h 2009-01-20 03:58:30 UTC (rev 23784)
@@ -38,7 +38,8 @@
#define LAUNCH_KEY_SETRESOURCELIMITS "SetResourceLimits"
#define LAUNCH_KEY_GETRUSAGESELF "GetResourceUsageSelf"
#define LAUNCH_KEY_GETRUSAGECHILDREN "GetResourceUsageChildren"
-
+#define LAUNCH_KEY_SETPRIORITYLIST "SetPriorityList"
+
#define LAUNCHD_SOCKET_ENV "LAUNCHD_SOCKET"
#define LAUNCHD_SOCK_PREFIX _PATH_VARTMP "launchd"
#define LAUNCHD_TRUSTED_FD_ENV "__LAUNCHD_FD"
@@ -46,12 +47,17 @@
#define LAUNCH_KEY_BATCHCONTROL "BatchControl"
#define LAUNCH_KEY_BATCHQUERY "BatchQuery"
#define LAUNCHD_DO_APPLE_INTERNAL_LOGGING "__DoAppleInternalLogging__"
-
+
+#define LAUNCH_KEY_JETSAMLABEL "JetsamLabel"
+#define LAUNCH_KEY_JETSAMFRONTMOST "JetsamFrontmost"
+#define LAUNCH_KEY_JETSAMPRIORITY "JetsamPriority"
+
#define LAUNCH_JOBKEY_TRANSACTIONCOUNT "TransactionCount"
#define LAUNCH_JOBKEY_QUARANTINEDATA "QuarantineData"
#define LAUNCH_JOBKEY_SANDBOXPROFILE "SandboxProfile"
#define LAUNCH_JOBKEY_SANDBOXFLAGS "SandboxFlags"
#define LAUNCH_JOBKEY_SANDBOX_NAMED "Named"
+#define LAUNCH_JOBKEY_JETSAMPRIORITY "JetsamPriority"
#define LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL "EnterKernelDebuggerBeforeKill"
#define LAUNCH_JOBKEY_PERJOBMACHSERVICES "PerJobMachServices"
Modified: branches/PR-6424345/launchd/src/launchctl.c
===================================================================
--- branches/PR-6424345/launchd/src/launchctl.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launchctl.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -113,6 +113,9 @@
};
static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
+static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj);
+static CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr);
+static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict);
static bool launch_data_array_append(launch_data_t a, launch_data_t o);
static void distill_jobs(launch_data_t);
static void distill_config_file(launch_data_t);
@@ -121,6 +124,7 @@
static launch_data_t CF2launch_data(CFTypeRef);
static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
+static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL);
static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
static bool path_goodness_check(const char *path, bool forceload);
static void readpath(const char *, struct load_unload_state *);
@@ -163,9 +167,11 @@
static void do_single_user_mode(bool);
static bool do_single_user_mode2(void);
static void read_launchd_conf(void);
+static void read_environment_dot_plist(void);
static bool job_disabled_logic(launch_data_t obj);
static void fix_bogus_file_metadata(void);
static void do_file_init(void) __attribute__((constructor));
+static void setup_system_context(void);
typedef enum {
BOOTCACHE_START = 1,
@@ -185,6 +191,7 @@
static int setenv_cmd(int argc, char *const argv[]);
static int unsetenv_cmd(int argc, char *const argv[]);
static int getenv_and_export_cmd(int argc, char *const argv[]);
+static int wait4debugger_cmd(int argc, char *const argv[]);
static int limit_cmd(int argc, char *const argv[]);
static int stdio_cmd(int argc, char *const argv[]);
@@ -197,7 +204,8 @@
static int bslist_cmd(int argc, char *const argv[]);
static int _bstree_cmd(mach_port_t bsport, unsigned int depth);
static int bstree_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
-
+static int managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
+static int manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn));
static int help_cmd(int argc, char *const argv[]);
@@ -206,39 +214,44 @@
int (*func)(int argc, char *const argv[]);
const char *desc;
} cmds[] = {
- { "load", load_and_unload_cmd, "Load configuration files and/or directories" },
- { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" },
-// { "reload", reload_cmd, "Reload configuration files and/or directories" },
- { "start", start_stop_remove_cmd, "Start specified job" },
- { "stop", start_stop_remove_cmd, "Stop specified job" },
- { "submit", submit_cmd, "Submit a job from the command line" },
- { "remove", start_stop_remove_cmd, "Remove specified job" },
- { "bootstrap", bootstrap_cmd, "Bootstrap launchd" },
- { "list", list_cmd, "List jobs and information about jobs" },
- { "setenv", setenv_cmd, "Set an environmental variable in launchd" },
- { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" },
- { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" },
- { "export", getenv_and_export_cmd, "Export shell settings from launchd" },
- { "limit", limit_cmd, "View and adjust launchd resource limits" },
- { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" },
- { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" },
- { "shutdown", fyi_cmd, "Prepare for system shutdown" },
- { "singleuser", fyi_cmd, "Switch to single-user mode" },
- { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" },
- { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" },
- { "umask", umask_cmd, "Change launchd's umask" },
- { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" },
- { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" },
- { "bstree", bstree_cmd, "Show the entire Mach bootstrap tree. Requires root privileges." },
- { "exit", exit_cmd, "Exit the interactive invocation of launchctl" },
- { "quit", exit_cmd, "Quit the interactive invocation of launchctl" },
- { "help", help_cmd, "This help output" },
+ { "load", load_and_unload_cmd, "Load configuration files and/or directories" },
+ { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" },
+// { "reload", reload_cmd, "Reload configuration files and/or directories" },
+ { "start", start_stop_remove_cmd, "Start specified job" },
+ { "stop", start_stop_remove_cmd, "Stop specified job" },
+ { "submit", submit_cmd, "Submit a job from the command line" },
+ { "remove", start_stop_remove_cmd, "Remove specified job" },
+ { "bootstrap", bootstrap_cmd, "Bootstrap launchd" },
+ { "list", list_cmd, "List jobs and information about jobs" },
+ { "setenv", setenv_cmd, "Set an environmental variable in launchd" },
+ { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" },
+ { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" },
+ { "export", getenv_and_export_cmd, "Export shell settings from launchd" },
+ { "debug", wait4debugger_cmd, "Set the WaitForDebugger flag for the target job to true." },
+ { "limit", limit_cmd, "View and adjust launchd resource limits" },
+ { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" },
+ { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" },
+ { "shutdown", fyi_cmd, "Prepare for system shutdown" },
+ { "singleuser", fyi_cmd, "Switch to single-user mode" },
+ { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" },
+ { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" },
+ { "umask", umask_cmd, "Change launchd's umask" },
+ { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" },
+ { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" },
+ { "bstree", bstree_cmd, "Show the entire Mach bootstrap tree. Requires root privileges." },
+ { "managerpid", managerpid_cmd, "Print the PID of the launchd managing this Mach bootstrap." },
+ { "manageruid", manageruid_cmd, "Print the UID of the launchd managing this Mach bootstrap." },
+ { "exit", exit_cmd, "Exit the interactive invocation of launchctl" },
+ { "quit", exit_cmd, "Quit the interactive invocation of launchctl" },
+ { "help", help_cmd, "This help output" },
};
static bool istty;
static bool verbose;
static bool is_managed;
static bool do_apple_internal_magic;
+static bool system_context;
+static bool rootuser_context;
int
main(int argc, char *const argv[])
@@ -249,18 +262,10 @@
if (vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed_val) == NULL && is_managed_val) {
is_managed = true;
}
-
- if (getuid() == 0 && !is_managed) {
- mach_port_t root_bs = str2bsport("/");
- task_set_bootstrap_port(mach_task_self(), root_bs);
- mach_port_deallocate(mach_task_self(), bootstrap_port);
- bootstrap_port = root_bs;
- }
-
+
istty = isatty(STDIN_FILENO);
-
argc--, argv++;
-
+
if (argc > 0 && argv[0][0] == '-') {
char *flago;
@@ -269,6 +274,22 @@
case 'v':
verbose = true;
break;
+ case 'u':
+ if( argc > 1 ) {
+ if( strncmp(argv[1], "root", sizeof("root")) == 0 ) {
+ rootuser_context = true;
+ } else {
+ fprintf(stderr, "Unknown user: %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ argc--, argv++;
+ } else {
+ fprintf(stderr, "-u option requires an argument. Currently, only \"root\" is supported.\n");
+ }
+ break;
+ case '1':
+ system_context = true;
+ break;
default:
fprintf(stderr, "Unknown argument: '-%c'\n", *flago);
break;
@@ -277,6 +298,38 @@
argc--, argv++;
}
+ /* Running in the context of the root user's per-user launchd is only supported ... well
+ * in the root user's per-user context. I know it's confusing. I'm genuinely sorry.
+ */
+ if( rootuser_context ) {
+ int64_t manager_uid = -1, manager_pid = -1;
+ if( vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid) == NULL ) {
+ if( vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid) == NULL ) {
+ if( manager_uid || manager_uid == 1 ) {
+ fprintf(stderr, "Running in the root user's per-user context is not supported outside of the root user's bootstrap.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ } else if( !(system_context || rootuser_context) ) {
+ /* Running in the system context is implied when we're running as root and not running as a bootstrapper. */
+ system_context = ( !is_managed && getuid() == 0 );
+ }
+
+ if( system_context ) {
+ if( getuid() == 0 ) {
+ setup_system_context();
+ } else {
+ fprintf(stderr, "You must be root to run in the system context.\n");
+ exit(EXIT_FAILURE);
+ }
+ } else if( rootuser_context ) {
+ if( getuid() != 0 ) {
+ fprintf(stderr, "You must be root to run in the root user context.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
if (NULL == readline) {
fprintf(stderr, "missing library: readline\n");
exit(EXIT_FAILURE);
@@ -320,7 +373,7 @@
optind = 1;
optreset = 1;
-
+
for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
if (!strcmp(cmds[i].name, argv[0])) {
return cmds[i].func(argc, argv);
@@ -372,6 +425,96 @@
fclose(f);
}
+CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL)
+{
+ CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL);
+
+ CFErrorRef streamErr = NULL;
+ if( !CFReadStreamOpen(plistReadStream) ) {
+ streamErr = CFReadStreamCopyError(plistReadStream);
+ CFStringRef errString = CFErrorCopyDescription(streamErr);
+
+ CFShow(errString);
+
+ CFRelease(errString);
+ CFRelease(streamErr);
+ }
+
+ CFPropertyListRef plist = NULL;
+ if( plistReadStream ) {
+ CFStringRef errString = NULL;
+ CFPropertyListFormat plistFormat = 0;
+ plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString);
+ if( !plist ) {
+ CFShow(errString);
+ CFRelease(errString);
+ }
+ }
+
+ CFReadStreamClose(plistReadStream);
+ CFRelease(plistReadStream);
+
+ return plist;
+}
+
+#define CFReleaseIfNotNULL(cf) if( cf ) CFRelease(cf);
+void
+read_environment_dot_plist(void)
+{
+ CFStringRef plistPath = NULL;
+ CFURLRef plistURL = NULL;
+ CFDictionaryRef envPlist = NULL;
+ launch_data_t req = NULL, launch_env_dict = NULL, resp = NULL;
+
+ plistPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/.MacOSX/environment.plist"), getenv("HOME"));
+ if( !assumes(plistPath != NULL) ) {
+ goto out;
+ }
+
+ plistURL = CFURLCreateWithFileSystemPath(NULL, plistPath, kCFURLPOSIXPathStyle, false);
+ if( !assumes(plistURL != NULL) ) {
+ goto out;
+ }
+
+ envPlist = (CFDictionaryRef)CFPropertyListCreateFromFile(plistURL);
+ if( !assumes(envPlist != NULL) ) {
+ goto out;
+ }
+
+ launch_env_dict = CF2launch_data(envPlist);
+ if( !assumes(launch_env_dict != NULL) ) {
+ goto out;
+ }
+
+ req = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
+ if( !assumes(req != NULL) ) {
+ goto out;
+ }
+
+ launch_data_dict_insert(req, launch_env_dict, LAUNCH_KEY_SETUSERENVIRONMENT);
+ resp = launch_msg(req);
+ if( !assumes(resp != NULL) ) {
+ goto out;
+ }
+
+ if( !assumes(launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) ) {
+ goto out;
+ }
+
+ assumes(launch_data_get_errno(resp) == 0);
+out:
+ CFReleaseIfNotNULL(plistPath);
+ CFReleaseIfNotNULL(plistURL);
+ CFReleaseIfNotNULL(envPlist);
+ if( req ) {
+ launch_data_free(req);
+ }
+
+ if( resp ) {
+ launch_data_free(resp);
+ }
+}
+
int
unsetenv_cmd(int argc, char *const argv[])
{
@@ -486,6 +629,37 @@
return 0;
}
+int
+wait4debugger_cmd(int argc, char * const argv[])
+{
+ if( argc != 3 ) {
+ fprintf(stderr, "%s usage: debug <label> <value>\n", argv[0]);
+ return 1;
+ }
+
+ int result = 1;
+ int64_t inval = 0;
+ if( strncmp(argv[2], "true", sizeof("true")) == 0 ) {
+ inval = 1;
+ } else if( strncmp(argv[2], "false", sizeof("false")) != 0 ) {
+ inval = atoi(argv[2]);
+ inval &= 1;
+ }
+
+ vproc_t vp = vprocmgr_lookup_vproc(argv[1]);
+ if( vp ) {
+ vproc_err_t verr = vproc_swap_integer(vp, VPROC_GSK_WAITFORDEBUGGER, &inval, NULL);
+ if( verr ) {
+ fprintf(stderr, "Failed to set WaitForDebugger flag on %s.\n", argv[1]);
+ } else {
+ result = 0;
+ }
+ vproc_release(vp);
+ }
+
+ return result;
+}
+
void
unloadjob(launch_data_t job)
{
@@ -1229,6 +1403,149 @@
}
}
+static inline Boolean __is_launch_data_t(launch_data_t obj)
+{
+ Boolean result = true;
+
+ switch( launch_data_get_type(obj) ) {
+ case LAUNCH_DATA_STRING : break;
+ case LAUNCH_DATA_INTEGER : break;
+ case LAUNCH_DATA_REAL : break;
+ case LAUNCH_DATA_BOOL : break;
+ case LAUNCH_DATA_ARRAY : break;
+ case LAUNCH_DATA_DICTIONARY : break;
+ case LAUNCH_DATA_FD : break;
+ case LAUNCH_DATA_MACHPORT : break;
+ default : result = false;
+ }
+
+ return result;
+}
+
+static void __launch_data_iterate(launch_data_t obj, const char *key, CFMutableDictionaryRef dict)
+{
+ if( obj && __is_launch_data_t(obj) ) {
+ CFStringRef cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
+ CFTypeRef cfVal = CFTypeCreateFromLaunchData(obj);
+
+ if( cfVal ) {
+ CFDictionarySetValue(dict, cfKey, cfVal);
+ CFRelease(cfVal);
+ }
+ CFRelease(cfKey);
+ }
+}
+
+static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj)
+{
+ CFTypeRef cfObj = NULL;
+
+ switch( launch_data_get_type(obj) ) {
+ case LAUNCH_DATA_STRING :
+ {
+ const char *str = launch_data_get_string(obj);
+ cfObj = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
+
+ break;
+ }
+ case LAUNCH_DATA_INTEGER :
+ {
+ long long integer = launch_data_get_integer(obj);
+ cfObj = CFNumberCreate(NULL, kCFNumberLongLongType, &integer);
+
+ break;
+ }
+ case LAUNCH_DATA_REAL :
+ {
+ double real = launch_data_get_real(obj);
+ cfObj = CFNumberCreate(NULL, kCFNumberDoubleType, &real);
+
+ break;
+ }
+ case LAUNCH_DATA_BOOL :
+ {
+ bool yesno = launch_data_get_bool(obj);
+ cfObj = yesno ? kCFBooleanTrue : kCFBooleanFalse;
+
+ break;
+ }
+ case LAUNCH_DATA_ARRAY :
+ {
+ cfObj = (CFTypeRef)CFArrayCreateFromLaunchArray(obj);
+
+ break;
+ }
+ case LAUNCH_DATA_DICTIONARY :
+ {
+ cfObj = (CFTypeRef)CFDictionaryCreateFromLaunchDictionary(obj);
+
+ break;
+ }
+ case LAUNCH_DATA_FD :
+ {
+ int fd = launch_data_get_fd(obj);
+ cfObj = CFNumberCreate(NULL, kCFNumberIntType, &fd);
+
+ break;
+ }
+ case LAUNCH_DATA_MACHPORT :
+ {
+ mach_port_t port = launch_data_get_machport(obj);
+ cfObj = CFNumberCreate(NULL, kCFNumberIntType, &port);
+
+ break;
+ }
+ default : break;
+ }
+
+ return cfObj;
+}
+
+#pragma mark CFArray
+CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr)
+{
+ CFArrayRef result = NULL;
+ CFMutableArrayRef mutResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ if( launch_data_get_type(arr) == LAUNCH_DATA_ARRAY ) {
+ unsigned int count = launch_data_array_get_count(arr);
+ unsigned int i = 0;
+
+ for( i = 0; i < count; i++ ) {
+ launch_data_t launch_obj = launch_data_array_get_index(arr, i);
+ CFTypeRef obj = CFTypeCreateFromLaunchData(launch_obj);
+
+ if( obj ) {
+ CFArrayAppendValue(mutResult, obj);
+ CFRelease(obj);
+ }
+ }
+
+ result = CFArrayCreateCopy(NULL, mutResult);
+
+ CFRelease(mutResult);
+ }
+
+ return result;
+}
+
+#pragma mark CFDictionary / CFPropertyList
+static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict)
+{
+ CFDictionaryRef result = NULL;
+
+ if( launch_data_get_type(dict) == LAUNCH_DATA_DICTIONARY ) {
+ CFMutableDictionaryRef mutResult = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ launch_data_dict_iterate(dict, (void (*)(launch_data_t, const char *, void *))__launch_data_iterate, mutResult);
+
+ result = CFDictionaryCreateCopy(NULL, mutResult);
+ CFRelease(mutResult);
+ }
+
+ return result;
+}
+
void
myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
{
@@ -1495,6 +1812,26 @@
assumes(fwexec(rccleanup_tool, NULL) != -1);
}
+ if( path_check("/etc/rc.deferredinstall") ) {
+ int status = 0;
+ const char *deferredinstall_tool[] = { _PATH_BSHELL, "/etc/rc.deferredinstall", NULL };
+ if( assumes(fwexec(deferredinstall_tool, &status) != -1) ) {
+ if( WEXITSTATUS(status) == EXIT_SUCCESS ) {
+ if( do_apple_internal_magic ) {
+ fprintf(stdout, "Deferred install script completed successfully. Rebooting in 3 seconds...\n");
+ sleep(3);
+ }
+
+ assumes(remove(deferredinstall_tool[1]) != -1);
+ assumes(reboot(RB_AUTOBOOT) != -1);
+ exit(EXIT_FAILURE);
+ } else {
+ fprintf(stdout, "Deferred install script exited with status %i. Continuing boot and hoping it'll work...\n", WEXITSTATUS(status));
+ assumes(remove(deferredinstall_tool[1]) != -1);
+ }
+ }
+ }
+
empty_dir(_PATH_VARRUN, NULL);
empty_dir(_PATH_TMP, NULL);
remove(_PATH_NOLOGIN);
@@ -1505,7 +1842,9 @@
}
assumes(touch_file(_PATH_UTMPX, DEFFILEMODE) != -1);
+#if !TARGET_OS_EMBEDDED
assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1);
+#endif
#if HAVE_LIBAUDITD
/*
@@ -1528,17 +1867,20 @@
_vproc_set_global_on_demand(true);
- char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d",
-#if TARGET_OS_EMBEDDED
- "/var/mobile/Library/LaunchAgents",
+
+#if !TARGET_OS_EMBEDDED
+ char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", NULL };
+ int load_launchd_items_cnt = 4;
+#else
+ char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", "/var/mobile/Library/LaunchAgents", NULL };
+ int load_launchd_items_cnt = 5;
#endif
- NULL };
if (is_safeboot()) {
load_launchd_items[2] = "system";
}
- assumes(load_and_unload_cmd(4, load_launchd_items) == 0);
+ assumes(load_and_unload_cmd(load_launchd_items_cnt, load_launchd_items) == 0);
/*
* 5066316
@@ -1668,7 +2010,7 @@
if (is_safeboot()) {
load_launchd_items[4] = "system";
}
-
+
if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0 || strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
load_launchd_items[4] = "system";
if (!is_safeboot()) {
@@ -1708,6 +2050,18 @@
* agents.
*/
the_argc_user = 5;
+
+ /* We want to read environment.plist, which is in the user's home directory.
+ * Since the dance to mount a network home directory is fairly complex, all we
+ * can do is try and read environment.plist when bootstrapping the Aqua session,
+ * which is when we assume the home directory is present.
+ *
+ * The drawback here is that jobs bootstrapped in the Background session won't
+ * get the new environment until they quit and relaunch. But then again, they
+ * won't get the updated HOME directory or anything either. This is just a messy
+ * problem.
+ */
+ read_environment_dot_plist();
} else if (strcasecmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
/* If we're bootstrapping the StandardIO session, bootstrap the user's Background
* agents.
@@ -2115,39 +2469,68 @@
int
list_cmd(int argc, char *const argv[])
{
- launch_data_t resp, msg;
+ launch_data_t resp, msg = NULL;
int r = 0;
- if (argc > 2) {
- fprintf(stderr, "usage: %s list [label]\n", getprogname());
+ bool plist_output = false;
+ char *label = NULL;
+ if (argc > 3) {
+ fprintf(stderr, "usage: %s list [-x] [label]\n", getprogname());
return 1;
- } else if (argc == 2) {
+ } else if( argc >= 2 ) {
+ plist_output = ( strncmp(argv[1], "-x", sizeof("-x")) == 0 );
+ label = plist_output ? argv[2] : argv[1];
+ }
+
+ if( label ) {
msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
- launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB);
- } else if (vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
+ launch_data_dict_insert(msg, launch_data_new_string(label), LAUNCH_KEY_GETJOB);
+
+ resp = launch_msg(msg);
+ launch_data_free(msg);
+
+ if (resp == NULL) {
+ fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
+ r = 1;
+ } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
+ if( plist_output ) {
+ CFDictionaryRef respDict = CFDictionaryCreateFromLaunchDictionary(resp);
+ CFStringRef plistStr = NULL;
+ if( respDict ) {
+ CFDataRef plistData = CFPropertyListCreateXMLData(NULL, (CFPropertyListRef)respDict);
+ CFRelease(respDict);
+ if( plistData ) {
+ plistStr = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(plistData), CFDataGetLength(plistData), kCFStringEncodingUTF8, false);
+ CFRelease(plistData);
+ } else {
+ r = 1;
+ }
+ } else {
+ r = 1;
+ }
+
+ if( plistStr ) {
+ CFShow(plistStr);
+ r = 0;
+ }
+ } else {
+ print_obj(resp, NULL, NULL);
+ r = 0;
+ }
+ } else {
+ fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
+ r = 1;
+ }
+
+ launch_data_free(resp);
+ } else if( vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL ) {
fprintf(stdout, "PID\tStatus\tLabel\n");
launch_data_dict_iterate(resp, print_jobs, NULL);
launch_data_free(resp);
- return 0;
- } else {
- return 1;
+
+ r = 0;
}
- resp = launch_msg(msg);
- launch_data_free(msg);
-
- if (resp == NULL) {
- fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
- return 1;
- } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
- print_obj(resp, NULL, NULL);
- } else {
- fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
- r = 1;
- }
-
- launch_data_free(resp);
-
return r;
}
@@ -2494,6 +2877,24 @@
}
}
+void
+setup_system_context(void)
+{
+ if( geteuid() != 0 ) {
+ fprintf(stderr, "You must be the root user to perform this operation.\n");
+ return;
+ }
+
+ /* Use the system launchd's socket. */
+ setenv("__USE_SYSTEM_LAUNCHD", "1", 0);
+
+ /* Put ourselves in the system launchd's bootstrap. */
+ mach_port_t rootbs = str2bsport("/");
+ mach_port_deallocate(mach_task_self(), bootstrap_port);
+ task_set_bootstrap_port(mach_task_self(), rootbs);
+ bootstrap_port = rootbs;
+}
+
int
submit_cmd(int argc, char *const argv[])
{
@@ -2720,7 +3121,7 @@
return 1;
}
- #define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
+#define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
for (i = 0; i < service_cnt ; i++) {
fprintf(stdout, "%*s%-3s%s\n", depth, "", bport_state((service_actives[i])), service_names[i]);
@@ -2796,6 +3197,34 @@
return _bstree_cmd(str2bsport("/"), 4);
}
+int
+managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
+{
+ int64_t manager_pid = 0;
+ vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, (int64_t *)&manager_pid);
+ if( verr ) {
+ fprintf(stdout, "Unknown job manager!\n");
+ return 1;
+ }
+
+ fprintf(stdout, "%d\n", (pid_t)manager_pid);
+ return 0;
+}
+
+int
+manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
+{
+ int64_t manager_uid = 0;
+ vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, (int64_t *)&manager_uid);
+ if( verr ) {
+ fprintf(stdout, "Unknown job manager!\n");
+ return 1;
+ }
+
+ fprintf(stdout, "%lli\n", manager_uid);
+ return 0;
+}
+
bool
is_legacy_mach_job(launch_data_t obj)
{
@@ -2950,7 +3379,7 @@
goto out;
}
#endif
- fprintf(stderr, "Running fsck on the boot volume...\n");
+ fprintf(stdout, "Running fsck on the boot volume...\n");
if (fwexec(fsck_tool, NULL) != -1) {
goto out;
}
@@ -2960,7 +3389,7 @@
goto out;
}
- fprintf(stderr, "fsck failed!\n");
+ fprintf(stdout, "fsck failed!\n");
/* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */
assumes(reboot(RB_HALT) != -1);
@@ -2979,7 +3408,7 @@
#if TARGET_OS_EMBEDDED
if (path_check("/etc/fstab")) {
const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
- assumes(fwexec(mount_tool, true) != -1);
+ assumes(fwexec(mount_tool, NULL) != -1);
} else
#endif
{
@@ -2998,10 +3427,12 @@
const gid_t group;
const mode_t needed_bits;
const mode_t bad_bits;
+ const bool create;
} f[] = {
- { "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH },
- { _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
- { _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
+ { "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH, false },
+ { _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true },
+ { _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true },
+ { "/var/folders", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_ISUID | S_ISGID, true },
};
struct stat sb;
size_t i;
@@ -3013,7 +3444,16 @@
bool fix_id = false;
if (!assumes(stat(f[i].path, &sb) != -1)) {
- continue;
+ fprintf(stdout, "Crucial filesystem check: Path not present: %s. %s\n", f[i].path, f[i].create ? "Will create." : "");
+ if( f[i].create ) {
+ if( !assumes(mkdir(f[i].path, f[i].needed_bits) != -1) ) {
+ continue;
+ } else if( !assumes(stat(f[i].path, &sb) != -1) ) {
+ continue;
+ }
+ } else {
+ continue;
+ }
}
i_needed_bits = ~sb.st_mode & f[i].needed_bits;
Modified: branches/PR-6424345/launchd/src/launchd.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launchd.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -179,15 +179,13 @@
ipc_server_init();
runtime_log_push();
-
- int64_t now = runtime_get_wall_time();
struct passwd *pwent = getpwuid(getuid());
if( pwent ) {
strlcpy(g_username, pwent->pw_name, sizeof(g_username) - 1);
}
- runtime_syslog(LOG_NOTICE, "Per-user launchd for UID %u (%s) began at: %lld.%06llu", getuid(), g_username, now / USEC_PER_SEC, now % USEC_PER_SEC);
+ runtime_syslog(LOG_DEBUG, "Per-user launchd for UID %u (%s) has begun.", getuid(), g_username);
}
if( pid1_magic ) {
@@ -197,6 +195,8 @@
if( stat("/var/db/.launchd_flat_per_user_namespace", &sb) == 0 ) {
runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "Flat per-user Mach namespaces enabled.");
}
+ /* We just wanted to print status about the per-user namespace. PID 1 doesn't have a flat namespace. */
+ g_flat_mach_namespace = false;
}
monitor_networking_state();
@@ -204,9 +204,11 @@
if (pid1_magic) {
handle_pid1_crashes_separately();
} else {
+ #if !TARGET_OS_EMBEDDED
/* prime shared memory before the 'bootstrap_port' global is set to zero */
_vproc_transaction_begin();
_vproc_transaction_end();
+ #endif
}
if( pid1_magic ) {
@@ -217,7 +219,7 @@
}
jobmgr_init(sflag);
-
+
launchd_runtime_init2();
launchd_runtime();
Modified: branches/PR-6424345/launchd/src/launchd.plist.5
===================================================================
--- branches/PR-6424345/launchd/src/launchd.plist.5 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launchd.plist.5 2009-01-20 03:58:30 UTC (rev 23784)
@@ -279,8 +279,19 @@
function.
.It Sy NumberOfFiles <integer>
The maximum number of open files for this process.
+Setting this value in a system wide daemon will set the
+.Xr sysctl 3
+kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits) value in addition to the
+.Xr setrlimit 2
+values.
.It Sy NumberOfProcesses <integer>
The maximum number of simultaneous processes for this user id.
+Setting this value in a system wide daemon will set the
+.Xr sysctl 3
+kern.maxproc (SoftResourceLimits) or kern.maxprocperuid (HardResourceLimits)
+value in addition to the
+.Xr setrlimit 2
+values.
.It Sy ResidentSetSize <integer>
The maximum size (in bytes) to which a process's resident set size may grow.
This imposes a limit on the amount of physical memory to be given to a process;
@@ -451,5 +462,6 @@
.Sh SEE ALSO
.Xr launchctl 1 ,
.Xr launch 3 ,
+.Xr sysctl 3 ,
.Xr launchd 8 ,
.Xr plist 5
Modified: branches/PR-6424345/launchd/src/launchd_core_logic.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd_core_logic.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launchd_core_logic.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -82,7 +82,21 @@
#if HAVE_QUARANTINE
#include <quarantine.h>
#endif
+#if TARGET_OS_EMBEDDED
+#include <sys/kern_memorystatus.h>
+#else
+/* To make my life easier. */
+typedef struct jetsam_priority_entry {
+ pid_t pid;
+ uint32_t flags;
+} jetsam_priority_entry_t;
+enum {
+ kJetsamFlagsFrontmost = (1 << 0),
+ kJetsamFlagsKilled = (1 << 1)
+};
+#endif
+
#include "launch.h"
#include "launch_priv.h"
#include "launch_internal.h"
@@ -110,10 +124,11 @@
* If the job hasn't exited in the given number of seconds after sending
* it a SIGTERM, SIGKILL it. Can be overriden in the job plist.
*/
-#define LAUNCHD_MIN_JOB_RUN_TIME 10
-#define LAUNCHD_SAMPLE_TIMEOUT 1
-#define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20
-#define LAUNCHD_SIGKILL_TIMER 5
+#define LAUNCHD_MIN_JOB_RUN_TIME 10
+#define LAUNCHD_SAMPLE_TIMEOUT 1
+#define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20
+#define LAUNCHD_SIGKILL_TIMER 5
+#define LAUNCHD_JETSAM_PRIORITY_UNSET 0xdead1eebabell
#define SHUTDOWN_LOG_DIR "/var/log/shutdown"
@@ -230,6 +245,7 @@
struct envitem {
SLIST_ENTRY(envitem) sle;
+ bool one_shot;
char *value;
union {
const char key[0];
@@ -237,9 +253,10 @@
};
};
-static bool envitem_new(job_t j, const char *k, const char *v, bool global);
+static bool envitem_new(job_t j, const char *k, const char *v, bool global, bool one_shot);
static void envitem_delete(job_t j, struct envitem *ei, bool global);
static void envitem_setup(launch_data_t obj, const char *key, void *context);
+static void envitem_setup_one_shot(launch_data_t obj, const char *key, void *context);
struct limititem {
SLIST_ENTRY(limititem) sle;
@@ -306,6 +323,7 @@
SLIST_ENTRY(jobmgr_s) sle;
SLIST_HEAD(, jobmgr_s) submgrs;
LIST_HEAD(, job_s) jobs;
+ LIST_HEAD(, job_s) jetsam_jobs;
LIST_HEAD(, job_s) active_jobs[ACTIVE_JOB_HASH_SIZE];
LIST_HEAD(, machservice) ms_hash[MACHSERVICE_HASH_SIZE];
LIST_HEAD(, job_s) global_env_jobs;
@@ -317,9 +335,8 @@
unsigned int global_on_demand_cnt;
unsigned int hopefully_first_cnt;
unsigned int normal_active_cnt;
- unsigned int sent_stop_to_normal_jobs :1,
- sent_stop_to_hopefully_last_jobs:1,
- shutting_down :1,
+ unsigned int jetsam_jobs_cnt;
+ unsigned int shutting_down :1,
session_initialized :1,
killed_hopefully_first_jobs :1,
killed_normal_jobs :1,
@@ -338,16 +355,19 @@
static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name);
static job_t jobmgr_import2(jobmgr_t jm, launch_data_t pload);
static jobmgr_t jobmgr_parent(jobmgr_t jm);
+static jobmgr_t jobmgr_do_hopefully_first_shutdown_phase(jobmgr_t jm);
+static jobmgr_t jobmgr_do_normal_shutdown_phase(jobmgr_t jm);
+static jobmgr_t jobmgr_do_hopefully_last_shutdown_phase(jobmgr_t jm);
static jobmgr_t jobmgr_do_garbage_collection(jobmgr_t jm);
static bool jobmgr_label_test(jobmgr_t jm, const char *str);
static void jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev);
-static void jobmgr_log_stray_children(jobmgr_t jm);
+static void jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays);
static void jobmgr_kill_stray_child(jobmgr_t jm, pid_t p);
static void jobmgr_remove(jobmgr_t jm, bool expect_real_jobs);
static void jobmgr_dispatch_all(jobmgr_t jm, bool newmounthack);
static void jobmgr_dequeue_next_sample(jobmgr_t jm);
static job_t jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag);
-static job_t jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p);
+static job_t jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p, bool anon_okay);
static job_t jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon);
static jobmgr_t jobmgr_find_by_name(jobmgr_t jm, const char *where);
static job_t job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid);
@@ -364,8 +384,9 @@
#define AUTO_PICK_LEGACY_LABEL (const char *)(~0)
struct job_s {
- kq_callback kqjob_callback; /* MUST be first element of this structure for benefit of launchd's run loop. */
- LIST_ENTRY(job_s) sle;
+ kq_callback kqjob_callback; /* MUST be first element of this structure for benefit of launchd's run loop. */
+ LIST_ENTRY(job_s) sle;
+ LIST_ENTRY(job_s) jetsam_sle;
LIST_ENTRY(job_s) pid_hash_sle;
LIST_ENTRY(job_s) label_hash_sle;
LIST_ENTRY(job_s) global_env_sle;
@@ -384,7 +405,7 @@
cpu_type_t *j_binpref;
size_t j_binpref_cnt;
mach_port_t j_port;
- mach_port_t wait_reply_port; /* we probably should switch to a list of waiters */
+ mach_port_t wait_reply_port; /* we probably should switch to a list of waiters */
uid_t mach_uid;
jobmgr_t mgr;
size_t argc;
@@ -416,6 +437,7 @@
int log_redirect_fd;
int nice;
int stdout_err_fd;
+ long long jetsam_priority;
uint32_t timeout;
uint32_t exit_timeout;
uint64_t sent_signal_time;
@@ -449,6 +471,7 @@
start_pending :1, /* an event fired and the job should start, but not necessarily right away */
globargv :1, /* man launchd.plist --> EnableGlobbing */
wait4debugger :1, /* man launchd.plist --> WaitForDebugger */
+ wait4debugger_oneshot :1, /* One-shot WaitForDebugger. */
internal_exc_handler :1, /* MachExceptionHandler == true */
stall_before_exec :1, /* a hack to support an option of spawn_via_launchd() */
only_once :1, /* man launchd.plist --> LaunchOnlyOnce. Note: 5465184 Rename this to "HopefullyNeverExits" */
@@ -476,7 +499,9 @@
reap_after_sample :1, /* The job exited before sample did, so we should reap it after sample is done. */
nosy :1, /* The job has an OtherJobEnabled KeepAlive criterion. */
crashed :1, /* The job is the default Mach exception handler, and it crashed. */
- reaped :1; /* We've received NOTE_EXIT for the job. */
+ reaped :1, /* We've received NOTE_EXIT for the job. */
+ stopped :1, /* job_stop() was called. */
+ jetsam_frontmost :1; /* The job is considered "frontmost" by Jetsam. */
mode_t mask;
pid_t sample_pid;
const char label[0];
@@ -568,8 +593,13 @@
static size_t our_strhash(const char *s) __attribute__((pure));
static void extract_rcsid_substr(const char *i, char *o, size_t osz);
static void do_first_per_user_launchd_hack(void);
-static void do_unmounts(void);
+void eliminate_double_reboot(void);
+/* For Jetsam. */
+static void jetsam_priority_from_job(job_t j, bool front, jetsam_priority_entry_t *jp);
+static int job_cmp(const job_t *lhs, const job_t *rhs);
+int launchd_set_jetsam_priorities(launch_data_t priorities);
+
/* file local globals */
static size_t total_children;
static size_t total_anon_children;
@@ -652,7 +682,8 @@
if (unlikely(!j->p || j->anonymous)) {
return;
}
-
+
+#if !TARGET_OS_EMBEDDED
if (j->kill_via_shmem && !g_force_old_kill_path) {
if (j->shmem) {
if (!j->sent_kill_via_shmem) {
@@ -668,6 +699,7 @@
} else if( j->kill_via_shmem ) {
job_log(j, LOG_DEBUG, "Stopping transactional job the old-fashioned way.");
}
+#endif
j->sent_signal_time = runtime_get_opaque_time();
@@ -706,6 +738,8 @@
job_log(j, LOG_DEBUG, "Sent SIGTERM signal%s", extralog);
}
+
+ j->stopped = true;
}
launch_data_t
@@ -863,88 +897,12 @@
{
jobmgr_log(jm, LOG_NOTICE | LOG_CONSOLE, "Still alive with %lu/%lu (normal/anonymous) children", total_children, total_anon_children);
jobmgr_log_active_jobs(jm);
-
- if( pid1_magic && jm == root_jobmgr ) {
- /* Take the opportunity to shut the system down if it presents itself. */
- if( total_children == 0 ) {
- jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "No more non-anonymous children left. Shutting down the system.");
- jobmgr_log_stray_children(jm);
- jobmgr_remove(jm, false);
- }
-
- unsigned int unkilled_jobs = 0;
- job_t ji = NULL;
- char *type = NULL;
- if( !jm->killed_hopefully_first_jobs ) {
- LIST_FOREACH( ji, &jm->jobs, sle ) {
- if( ji->hopefully_exits_first && !ji->sent_sigkill && !ji->anonymous ) {
- job_log(ji, LOG_DEBUG | LOG_CONSOLE, "Hopefully First job has not yet been sent SIGKILL.");
- unkilled_jobs++;
- }
- }
-
- type = "hopefully first";
- if( unkilled_jobs == 0 ) {
- jm->killed_hopefully_first_jobs = true;
- }
- } else if( !jm->killed_normal_jobs ) {
- LIST_FOREACH( ji, &jm->jobs, sle ) {
- if( !ji->hopefully_exits_first && !ji->hopefully_exits_last && !ji->sent_sigkill && !ji->anonymous ) {
- job_log(ji, LOG_DEBUG | LOG_CONSOLE, "Normal job has not yet been sent SIGKILL.");
- unkilled_jobs++;
- }
- }
-
- type = "normal";
- if( unkilled_jobs == 0 ) {
- jm->killed_normal_jobs = true;
- }
- } else if( !jm->killed_hopefully_last_jobs ) {
- LIST_FOREACH( ji, &jm->jobs, sle ) {
- if( ji->hopefully_exits_last && !ji->sent_sigkill && !ji->anonymous ) {
- job_log(ji, LOG_DEBUG | LOG_CONSOLE, "Hopefully Last job has not yet been sent SIGKILL.");
- unkilled_jobs++;
- }
- }
-
- type = "hopefully last";
- if( unkilled_jobs == 0 ) {
- jm->killed_hopefully_last_jobs = true;
- }
- }
-
- /* Our club-over-the-head solution to rdar://problem/5054857.
- * If all remaining jobs in the PID 1 launchd have been sent SIGKILL, wash our hands of them. This way,
- * unkillable processes don't hold up shutdown for customers. Disable this behavior for AppleInternal,
- * since we still want to catch kernel bugs and processes that don't properly terminate when receiving
- * SIGKILL. See jobmgr_do_garbage_collection() for the rest.
- */
- if( unkilled_jobs == 0 && jm->killed_hopefully_last_jobs ) {
- if( !do_apple_internal_logging ) {
- jobmgr_assumes(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1);
- jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "All remaining non-anonymous jobs have died or been sent SIGKILL. Shutting down the system.");
- jobmgr_log_stray_children(jm);
- runtime_closelog(); /* hack to flush logs */
- jobmgr_remove(jm, true);
- } else {
- jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "All remaining non-anonymous jobs have died or been sent SIGKILL. On a customer-facing system, we would call reboot() at this point.");
- }
- } else if( unkilled_jobs != 0 ) {
- jobmgr_log(jm, LOG_NOTICE | LOG_CONSOLE, "Still have %u %s non-anonymous job%s that %s not been sent SIGKILL.", unkilled_jobs, type, unkilled_jobs > 1 ? "s" : "", unkilled_jobs > 1 ? "have" : "has");
- } else {
- jobmgr_do_garbage_collection(jm);
- }
- }
-
- runtime_closelog(); /* hack to flush logs */
}
jobmgr_t
jobmgr_shutdown(jobmgr_t jm)
{
jobmgr_t jmi, jmn;
- job_t ji;
-
jobmgr_log(jm, LOG_DEBUG, "Beginning job manager shutdown with flags: %s", reboot_flags_to_C_names(jm->reboot_flags));
jm->shutting_down = true;
@@ -952,15 +910,7 @@
SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
jobmgr_shutdown(jmi);
}
-
- if (jm->hopefully_first_cnt) {
- LIST_FOREACH(ji, &jm->jobs, sle) {
- if (ji->p && ji->hopefully_exits_first) {
- job_stop(ji);
- }
- }
- }
-
+
if (jm->parentmgr == NULL && pid1_magic) {
jobmgr_assumes(jm, kevent_mod((uintptr_t)jm, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 5, jm));
}
@@ -982,11 +932,11 @@
}
while( (ji = LIST_FIRST(&jm->jobs)) ) {
- if( !expect_real_jobs && !job_assumes(ji, ji->anonymous) ) {
+ if( !expect_real_jobs && ji->p && !job_assumes(ji, ji->anonymous) ) {
job_log(ji, LOG_WARNING | LOG_CONSOLE, "%s() called incorrectly with non-anonymous job still active. Forcing removal.", __func__);
job_remove(ji, true);
} else {
- if( !ji->anonymous ) {
+ if( !ji->anonymous && ji->p ) {
job_log(ji, LOG_WARNING | LOG_CONSOLE, "Job has overstayed its welcome. Forcing removal.");
job_remove(ji, true);
} else {
@@ -1007,18 +957,14 @@
runtime_del_weak_ref();
SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle);
} else if (pid1_magic) {
- jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "About to call: sync()");
- sync(); /* We're are going to rely on log timestamps to benchmark this call */
- jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Unmounting all filesystems except / and /dev");
- do_unmounts();
+ eliminate_double_reboot();
launchd_log_vm_stats();
+ jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "About to call: reboot(%s).", reboot_flags_to_C_names(jm->reboot_flags));
runtime_closelog();
- jobmgr_log(root_jobmgr, LOG_NOTICE | LOG_CONSOLE, "About to call: reboot(%s).", reboot_flags_to_C_names(jm->reboot_flags));
jobmgr_assumes(jm, reboot(jm->reboot_flags) != -1);
- runtime_closelog();
} else {
+ jobmgr_log(jm, LOG_DEBUG, "About to exit");
runtime_closelog();
- jobmgr_log(jm, LOG_DEBUG, "About to exit");
exit(EXIT_SUCCESS);
}
@@ -1166,14 +1112,18 @@
if (j->poll_for_vfs_changes) {
job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1);
}
-
if( j->exit_timeout ) {
/* Not a big deal if this fails. It means that the timer's already been freed. */
kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
}
-
+ if( j->jetsam_priority != LAUNCHD_JETSAM_PRIORITY_UNSET ) {
+ LIST_REMOVE(j, jetsam_sle);
+ j->mgr->jetsam_jobs_cnt--;
+ }
+
kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
-
+ kevent_mod((uintptr_t)j, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
+
LIST_REMOVE(j, sle);
LIST_REMOVE(j, label_hash_sle);
@@ -1493,7 +1443,8 @@
j->currently_ignored = true;
j->ondemand = true;
j->checkedin = true;
-
+ j->jetsam_priority = LAUNCHD_JETSAM_PRIORITY_UNSET;
+
if (prog) {
j->prog = strdup(prog);
if (!job_assumes(j, j->prog != NULL)) {
@@ -1832,6 +1783,15 @@
}
}
break;
+ case 'j':
+ case 'J':
+ if( strcasecmp(key, LAUNCH_JOBKEY_JETSAMPRIORITY) == 0 ) {
+ job_log(j, LOG_DEBUG, "Importing job with priority: %lld", value);
+ j->jetsam_priority = (typeof(j->jetsam_priority))value;
+ LIST_INSERT_HEAD(&j->mgr->jetsam_jobs, j, jetsam_sle);
+ j->mgr->jetsam_jobs_cnt++;
+ }
+ break;
case 'n':
case 'N':
if (strcasecmp(key, LAUNCH_JOBKEY_NICE) == 0) {
@@ -2275,18 +2235,18 @@
/* Should try and consolidate with job_mig_intran2() and jobmgr_find_by_pid(). */
job_t
-jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p)
+jobmgr_find_by_pid_deep(jobmgr_t jm, pid_t p, bool anon_okay)
{
job_t ji = NULL;
LIST_FOREACH( ji, &jm->active_jobs[ACTIVE_JOB_HASH(p)], pid_hash_sle ) {
- if (ji->p == p && !ji->anonymous) {
+ if (ji->p == p && (!ji->anonymous || (ji->anonymous && anon_okay)) ) {
return ji;
}
}
jobmgr_t jmi = NULL;
SLIST_FOREACH( jmi, &jm->submgrs, sle ) {
- if( (ji = jobmgr_find_by_pid_deep(jmi, p)) ) {
+ if( (ji = jobmgr_find_by_pid_deep(jmi, p, anon_okay)) ) {
break;
}
}
@@ -2341,7 +2301,6 @@
struct ldcred *ldc = runtime_get_caller_creds();
job_t jr;
-
jr = job_mig_intran2(root_jobmgr, p, ldc->pid);
if (!jobmgr_assumes(root_jobmgr, jr != NULL)) {
@@ -2483,6 +2442,8 @@
job_mig_swap_integer(j, VPROC_GSK_WEIRD_BOOTSTRAP, 0, 0, &junk);
}
+ j->wait4debugger_oneshot = false;
+
if (j->log_redirect_fd && !j->legacy_LS_job) {
job_log_stdouterr(j); /* one last chance */
@@ -2567,7 +2528,7 @@
td_sec = td / NSEC_PER_SEC;
td_usec = (td % NSEC_PER_SEC) / NSEC_PER_USEC;
- job_log(j, LOG_NOTICE, "Exited %llu.%06llu seconds after the first signal was sent", td_sec, td_usec);
+ job_log(j, LOG_DEBUG, "Exited %llu.%06llu seconds after the first signal was sent", td_sec, td_usec);
}
timeradd(&ru.ru_utime, &j->ru.ru_utime, &j->ru.ru_utime);
@@ -2593,7 +2554,7 @@
if (WIFSIGNALED(status)) {
int s = WTERMSIG(status);
- if (SIGKILL == s || SIGTERM == s) {
+ if ((SIGKILL == s || SIGTERM == s) && !j->stopped) {
job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s));
} else {
switch( s ) {
@@ -2629,6 +2590,13 @@
}
}
+ struct envitem *ei = NULL, *et = NULL;
+ SLIST_FOREACH_SAFE( ei, &j->env, sle, et ) {
+ if( ei->one_shot ) {
+ SLIST_REMOVE(&j->env, ei, envitem, sle);
+ }
+ }
+
if (j->hopefully_exits_first) {
j->mgr->hopefully_first_cnt--;
} else if (!j->anonymous && !j->hopefully_exits_last) {
@@ -2643,6 +2611,10 @@
j->lastlookup_gennum = 0;
j->p = 0;
+ if( !j->anonymous ) {
+ jobmgr_do_garbage_collection(j->mgr);
+ }
+
/*
* We need to someday evaluate other jobs and find those who wish to track the
* active/inactive state of this job. The current job_dispatch() logic makes
@@ -2895,7 +2867,7 @@
if (unlikely(rsz == 0)) {
job_log(j, LOG_DEBUG, "Standard out/error pipe closed");
close_log_redir = true;
- } else if (!job_assumes(j, rsz != -1)) {
+ } else if (!job_assumes(j, rsz != -1 && errno != EAGAIN)) {
close_log_redir = true;
} else {
buf[rsz] = '\0';
@@ -3043,8 +3015,8 @@
struct kinfo_proc kp;
size_t len = sizeof(kp);
- if (job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1)
- && job_assumes(j, len == sizeof(kp))) {
+ /* Sometimes, the kernel says it succeeded but really didn't. */
+ if (job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1) && len == sizeof(kp)) {
char newlabel[1000];
snprintf(newlabel, sizeof(newlabel), "%p.%s", j, kp.kp_proc.p_comm);
@@ -3097,6 +3069,9 @@
j->start_pending = true;
job_dispatch(j, false);
} else if (&j->exit_timeout == ident) {
+ if( !job_assumes(j, j->p != 0) ) {
+ return;
+ }
/*
* This block might be executed up to 3 times for a given (slow) job
* - once for the SAMPLE_TIMEOUT timer, at which point sampling is triggered
@@ -3153,6 +3128,7 @@
}
job_log(j, LOG_WARNING | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Killing", j->exit_timeout);
job_kill(j);
+ jobmgr_do_garbage_collection(j->mgr);
}
}
} else {
@@ -3374,7 +3350,8 @@
j->start_pending = false;
j->reaped = false;
j->crashed = false;
-
+ j->stopped = false;
+
runtime_add_ref();
total_children++;
LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
@@ -3480,7 +3457,7 @@
argv++;
}
- if (unlikely(j->wait4debugger)) {
+ if (unlikely(j->wait4debugger || j->wait4debugger_oneshot)) {
job_log(j, LOG_WARNING, "Spawned and waiting for the debugger to attach before continuing...");
spflags |= POSIX_SPAWN_START_SUSPENDED;
}
@@ -4112,9 +4089,17 @@
newmsg = alloca(newmsgsz);
if (err) {
+ #if !TARGET_OS_EMBEDDED
snprintf(newmsg, newmsgsz, "%s: %s", msg, strerror(err));
+ #else
+ snprintf(newmsg, newmsgsz, "(%s) %s: %s", j->label, msg, strerror(err));
+ #endif
} else {
+ #if !TARGET_OS_EMBEDDED
snprintf(newmsg, newmsgsz, "%s", msg);
+ #else
+ snprintf(newmsg, newmsgsz, "(%s) %s", j->label, msg);
+ #endif
}
if (unlikely(j->debug)) {
@@ -4221,7 +4206,6 @@
semaphoreitem_watch(job_t j, struct semaphoreitem *si)
{
char *parentdir, tmp_path[PATH_MAX];
- const char *which_path = si->what;
int saved_errno = 0;
int fflags = NOTE_DELETE|NOTE_RENAME;
@@ -4249,20 +4233,18 @@
/* See 5321044 for why we do the do-while loop and 5415523 for why ENOENT is checked */
do {
if (si->fd == -1) {
- if ((si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY))) == -1) {
- which_path = parentdir;
- si->watching_parent = true;
- si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY));
+ if ((si->fd = _fd(open(si->what, O_EVTONLY|O_NOCTTY))) == -1) {
+ si->watching_parent = job_assumes(j, (si->fd = _fd(open(parentdir, O_EVTONLY|O_NOCTTY))) != -1);
} else {
si->watching_parent = false;
}
}
if (si->fd == -1) {
- return job_log_error(j, LOG_ERR, "Path monitoring failed on \"%s\"%s", si->what, si->watching_parent ? " (Could not monitor parent directory)" : "");
+ return job_log_error(j, LOG_ERR, "Path monitoring failed on \"%s\"", si->what);
}
- job_log(j, LOG_DEBUG, "Watching %svnode: %d", si->watching_parent ? "parent ": "", si->fd);
+ job_log(j, LOG_DEBUG, "Watching %svnode (%s): %d", si->watching_parent ? "parent ": "", si->what, si->fd);
if (kevent_mod(si->fd, EVFILT_VNODE, EV_ADD, fflags, 0, j) == -1) {
saved_errno = errno;
@@ -4304,6 +4286,7 @@
case PATH_EXISTS:
case PATH_MISSING:
case DIR_NOT_EMPTY:
+ job_log(j, LOG_DEBUG, "P%s changed (%u): %s", si->watching_parent ? "arent path" : "ath", si->why, si->what);
break;
default:
continue;
@@ -4345,21 +4328,15 @@
}
if( !si->watching_parent ) {
- job_log(j, LOG_DEBUG, "Watch path modified: %s", si->what);
-
if (si->why == PATH_CHANGES) {
j->start_pending = true;
}
} else { /* Something happened to the parent directory. See if our target file appeared. */
- if( !invalidation_reason[0] ){
+ if( !invalidation_reason[0] ) {
job_assumes(j, runtime_close(si->fd) == 0);
si->fd = -1; /* this will get fixed in semaphoreitem_watch() */
semaphoreitem_watch(j, si);
}
-
- if( !si->watching_parent ) {
- j->start_pending = true;
- }
}
job_dispatch(j, false);
@@ -4625,7 +4602,7 @@
}
bool
-envitem_new(job_t j, const char *k, const char *v, bool global)
+envitem_new(job_t j, const char *k, const char *v, bool global, bool one_shot)
{
struct envitem *ei = calloc(1, sizeof(struct envitem) + strlen(k) + 1 + strlen(v) + 1);
@@ -4636,6 +4613,7 @@
strcpy(ei->key_init, k);
ei->value = ei->key_init + strlen(k) + 1;
strcpy(ei->value, v);
+ ei->one_shot = one_shot;
if (global) {
if (SLIST_EMPTY(&j->global_env)) {
@@ -4676,12 +4654,28 @@
}
if( strncmp(LAUNCHD_TRUSTED_FD_ENV, key, sizeof(LAUNCHD_TRUSTED_FD_ENV) - 1) != 0 ) {
- envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env);
+ envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env, false);
} else {
job_log(j, LOG_WARNING, "Ignoring reserved environmental variable: %s", key);
}
}
+void
+envitem_setup_one_shot(launch_data_t obj, const char *key, void *context)
+{
+ job_t j = context;
+
+ if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
+ return;
+ }
+
+ if( strncmp(LAUNCHD_TRUSTED_FD_ENV, key, sizeof(LAUNCHD_TRUSTED_FD_ENV) - 1) != 0 ) {
+ envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env, true);
+ } else {
+ job_log(j, LOG_WARNING, "Ignoring reserved environmental variable: %s", key);
+ }
+}
+
bool
limititem_update(job_t j, int w, rlim_t r)
{
@@ -4897,12 +4891,20 @@
wanted_state = true;
case PATH_MISSING:
if ((bool)(stat(si->what, &sb) == 0) == wanted_state) {
- if (si->fd != -1) {
- job_assumes(j, runtime_close(si->fd) == 0);
- si->fd = -1;
+ job_log(j, LOG_NOTICE, "KeepAlive: The following path %s: %s", wanted_state ? "exists" : "is missing", si->what);
+ return true;
+ } else {
+ if( wanted_state ) { /* File is not there but we wish it was. */
+ if( si->fd != -1 && !si->watching_parent ) { /* Need to be watching the parent now. */
+ job_assumes(j, runtime_close(si->fd) == 0);
+ si->fd = -1;
+ }
+ } else { /* File is there but we wish it wasn't. */
+ if( si->fd != -1 && si->watching_parent ) { /* Need to watch the file now. */
+ job_assumes(j, runtime_close(si->fd) == 0);
+ si->fd = -1;
+ }
}
- job_log(j, LOG_DEBUG, "KeepAlive: The following path %s: %s", wanted_state ? "exists" : "is missing", si->what);
- return true;
}
break;
case PATH_CHANGES:
@@ -4925,7 +4927,6 @@
job_active(job_t j)
{
struct machservice *ms;
-
if (j->p) {
return "PID is still valid";
}
@@ -5191,90 +5192,182 @@
}
jobmgr_t
-jobmgr_do_garbage_collection(jobmgr_t jm)
+jobmgr_do_hopefully_first_shutdown_phase(jobmgr_t jm)
{
- jobmgr_t jmi, jmn;
- job_t ji, jn;
-
- SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
- jobmgr_do_garbage_collection(jmi);
+ jobmgr_t _jm = jm;
+ if( !jm->shutting_down ) {
+ return _jm;
}
-
- if (likely(!jm->shutting_down)) {
- return jm;
+
+ bool should_proceed = !jm->killed_hopefully_first_jobs;
+
+ job_t ji = NULL, jn = NULL;
+ if( should_proceed ) {
+ jobmgr_log(jm, LOG_DEBUG, "Doing first phase of garbage collection.");
+ uint32_t unkilled_cnt = 0;
+ LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
+ if( jm->parentmgr ) {
+ job_log(ji, LOG_DEBUG, "Examining...");
+ }
+ if( ji->hopefully_exits_first ) {
+ bool active = job_active(ji);
+ if( active && !ji->stopped ) {
+ job_stop(ji);
+
+ /* We may have sent SIGKILL to the job in job_stop(). */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( ji->stopped ) {
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( !active ) {
+ job_remove(ji, false);
+ }
+ }
+ }
+
+ /* If we've killed everyone, move on. */
+ if( unkilled_cnt == 0 ) {
+ jm->killed_hopefully_first_jobs = true;
+ _jm = NULL;
+ }
+ } else {
+ jm->killed_hopefully_first_jobs = true;
+ _jm = NULL;
}
+
+ return _jm;
+}
- jobmgr_log(jm, LOG_DEBUG, "Garbage collecting.");
-
- if (jm->hopefully_first_cnt && !jm->killed_hopefully_first_jobs) {
- jobmgr_log(jm, LOG_DEBUG, "jm->hopefully_first_cnt = %u, jm->killed_hopefully_first_jobs = %s", jm->hopefully_first_cnt, jm->killed_hopefully_first_jobs ? "true" : "false");
- return jm;
+jobmgr_t
+jobmgr_do_normal_shutdown_phase(jobmgr_t jm)
+{
+ jobmgr_t _jm = jm;
+ if( !jm->shutting_down ) {
+ return _jm;
}
-
- if (jm->parentmgr && jm->parentmgr->shutting_down && jm->parentmgr->hopefully_first_cnt) {
- return jm;
- }
-
- if (!jm->sent_stop_to_normal_jobs) {
- jobmgr_log(jm, LOG_DEBUG, "Asking \"normal\" jobs to exit.");
-
- LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
- if (!job_active(ji)) {
- job_remove(ji, false);
- } else if (!ji->hopefully_exits_last) {
- job_stop(ji);
+
+ bool should_proceed = !jm->killed_normal_jobs;
+
+ job_t ji = NULL, jn = NULL;
+ if( should_proceed ) {
+ jobmgr_log(jm, LOG_DEBUG, "Doing second phase of garbage collection.");
+ uint32_t unkilled_cnt = 0;
+ LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
+ if( jm->parentmgr ) {
+ job_log(ji, LOG_DEBUG, "Examining...");
}
+
+ if( !(ji->hopefully_exits_first || ji->hopefully_exits_last) && !ji->anonymous ) {
+ bool active = job_active(ji);
+ if( active && !ji->stopped ) {
+ job_stop(ji);
+
+ /* We may have sent SIGKILL to the job in job_stop(). */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( ji->stopped ) {
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( !active ) {
+ job_remove(ji, false);
+ }
+ }
}
-
- jm->sent_stop_to_normal_jobs = true;
+
+ /* If we've killed everyone, move on. */
+ if( unkilled_cnt == 0 ) {
+ jm->killed_normal_jobs = true;
+ _jm = NULL;
+ }
+ } else {
+ jm->killed_normal_jobs = true;
+ _jm = NULL;
}
+
+ return _jm;
+}
- if (jm->normal_active_cnt && !jm->killed_normal_jobs) {
- jobmgr_log(jm, LOG_DEBUG, "jm->normal_active_cnt = %u, jm->killed_normal_jobs = %s", jm->normal_active_cnt, jm->killed_normal_jobs ? "true" : "false");
- return jm;
+jobmgr_t
+jobmgr_do_hopefully_last_shutdown_phase(jobmgr_t jm)
+{
+ jobmgr_t _jm = jm;
+ if( !jm->shutting_down ) {
+ return _jm;
}
-
- if (!jm->sent_stop_to_hopefully_last_jobs) {
- jobmgr_log(jm, LOG_DEBUG, "Asking \"hopefully last\" jobs to exit.");
-
- LIST_FOREACH(ji, &jm->jobs, sle) {
- if (ji->p && ji->anonymous) {
- continue;
- } else if (ji->p && job_assumes(ji, ji->hopefully_exits_last)) {
- job_stop(ji);
+
+ static bool killed_stray_jobs = false;
+ if( !killed_stray_jobs ) {
+ jobmgr_log_stray_children(jm, true);
+ killed_stray_jobs = true;
+ }
+
+ bool should_proceed = !jm->killed_hopefully_last_jobs && total_children != 0;
+
+ job_t ji = NULL, jn = NULL;
+ if( should_proceed ) {
+ jobmgr_log(jm, LOG_DEBUG, "Doing third phase of garbage collection.");
+ uint32_t unkilled_cnt = 0;
+ LIST_FOREACH_SAFE( ji, &jm->jobs, sle, jn ) {
+ if( jm->parentmgr ) {
+ job_log(ji, LOG_DEBUG, "Examining...");
}
+ if( ji->hopefully_exits_last ) {
+ bool active = job_active(ji);
+ if( active && !ji->stopped ) {
+ job_stop(ji);
+
+ /* We may have sent SIGKILL to the job in job_stop(). */
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( ji->stopped ) {
+ unkilled_cnt += !ji->sent_sigkill ? 1 : 0;
+ } else if( !active ) {
+ job_remove(ji, false);
+ }
+ }
}
-
- jm->sent_stop_to_hopefully_last_jobs = true;
+
+ /* If we've killed everyone, move on. */
+ if( unkilled_cnt == 0 ) {
+ jm->killed_hopefully_last_jobs = true;
+ _jm = NULL;
+ }
+ } else {
+ jm->killed_hopefully_last_jobs = true;
+ _jm = NULL;
}
+
+ return _jm;
+}
- if (!SLIST_EMPTY(&jm->submgrs)) {
- jobmgr_log(jm, LOG_DEBUG, "jm->submgrs not empty!");
+jobmgr_t
+jobmgr_do_garbage_collection(jobmgr_t jm)
+{
+ if( !jm->shutting_down ) {
return jm;
}
-
- jobmgr_t retval = NULL;
- if( pid1_magic && jm == root_jobmgr ) {
- if( total_children > 0 && !jm->killed_hopefully_last_jobs ) {
- retval = jm;
- } else {
- jobmgr_log_stray_children(jm);
- jobmgr_remove(jm, total_children == 0 ? false : true);
- }
- } else {
- LIST_FOREACH(ji, &jm->jobs, sle) {
- if (!ji->anonymous) {
- retval = jm;
+
+ jobmgr_t jmi = NULL, jmn = NULL;
+ SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
+ jobmgr_do_garbage_collection(jmi);
+ }
+
+ jobmgr_t _jm = jobmgr_do_hopefully_first_shutdown_phase(jm);
+ if( !_jm ) {
+ _jm = jobmgr_do_normal_shutdown_phase(jm) ? : jobmgr_do_hopefully_last_shutdown_phase(jm);
+ }
+
+ if( !_jm ) {
+ jobmgr_log(jm, LOG_NOTICE | LOG_CONSOLE, "Removing.");
+ jobmgr_log_stray_children(jm, false);
+
+ job_t ji = NULL;
+ LIST_FOREACH( ji, &jm->jobs, sle ) {
+ if( !job_assumes(ji, (ji->anonymous || ji->sent_sigkill) && ji->p) ) {
+ job_log(ji, LOG_NOTICE | LOG_CONSOLE, "Job should be gone but is not.");
}
}
- if( !retval ) {
- jobmgr_log_stray_children(jm);
- jobmgr_remove(jm, false);
- }
+ jobmgr_remove(jm, total_children != 0);
}
- return retval;
+ return _jm;
}
void
@@ -5326,16 +5419,12 @@
}
void
-jobmgr_log_stray_children(jobmgr_t jm)
+jobmgr_log_stray_children(jobmgr_t jm, bool kill_strays)
{
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
size_t i, kp_cnt = 0, kp_skipped = 0, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
struct kinfo_proc *kp;
- if (!do_apple_internal_logging) {
- return;
- }
-
if (likely(jm->parentmgr || !pid1_magic)) {
return;
}
@@ -5364,11 +5453,14 @@
continue;
}
- /* We might have some jobs hanging around that we've decided to shut down in spite of. If so, don't bother trying to kill them again. */
+ /* We might have some jobs hanging around that we've decided to shut down in spite of. */
job_t j = jobmgr_find_by_pid(jm, p_i, false);
if( !j || (j && j->anonymous) ) {
- jobmgr_log(jm, LOG_WARNING, "Stray %s %s at shutdown: PID %u PPID %u PGID %u %s", z, j ? "anonymous job" : "process", p_i, pp_i, pg_i, n);
- jobmgr_kill_stray_child(jm, p_i);
+ jobmgr_log(jm, LOG_WARNING | LOG_CONSOLE, "Stray %s %s at shutdown: PID %u PPID %u PGID %u %s", z, j ? "anonymous job" : "process", p_i, pp_i, pg_i, n);
+
+ if( kill_strays ) {
+ jobmgr_kill_stray_child(jm, p_i);
+ }
}
}
@@ -5533,11 +5625,19 @@
/* <rdar://problem/5042202> launchd-201: can't ssh in with AFP OD account (hangs) */
snprintf(buf, sizeof(buf), "0x%X:0:0", getuid());
- envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, false);
+ envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, false, false);
bootstrapper->weird_bootstrap = true;
jobmgr_assumes(jm, job_setup_machport(bootstrapper));
+ } else if( bootstrapper && strncmp(session_type, VPROCMGR_SESSION_SYSTEM, sizeof(VPROCMGR_SESSION_SYSTEM)) == 0 ) {
+ if( jobmgr_assumes(jm, pid1_magic) ) {
+ /* Have our system bootstrapper print out to the console. */
+ bootstrapper->stdoutpath = strdup(_PATH_CONSOLE);
+ #if TARGET_OS_EMBEDDED
+ bootstrapper->stderrpath = strdup(_PATH_CONSOLE);
+ #endif
+ }
}
-
+
jm->session_initialized = true;
return bootstrapper;
@@ -5592,18 +5692,29 @@
struct machservice *ms;
job_t target_j;
+ jobmgr_log(jm, LOG_DEBUG, "Looking up %sservice %s", target_pid ? "per-PID " : "", name);
+
if (target_pid) {
- //jobmgr_assumes(jm, !check_parent);
- if (unlikely((target_j = jobmgr_find_by_pid(jm, target_pid, false)) == NULL)) {
- return NULL;
+ /* This is a hack to let FileSyncAgent look up per-PID Mach services from the Background
+ * bootstrap in other bootstraps.
+ */
+
+ /* Start in the given bootstrap. */
+ if( unlikely((target_j = jobmgr_find_by_pid(jm, target_pid, false)) == NULL) ) {
+ /* If we fail, do a deep traversal. */
+ if (unlikely((target_j = jobmgr_find_by_pid_deep(root_jobmgr, target_pid, true)) == NULL)) {
+ jobmgr_log(jm, LOG_DEBUG, "Didn't find PID %i", target_pid);
+ return NULL;
+ }
}
-
+
SLIST_FOREACH(ms, &target_j->machservices, sle) {
if (ms->per_pid && strcmp(name, ms->name) == 0) {
return ms;
}
}
+ job_log(target_j, LOG_DEBUG, "Didn't find per-PID Mach service: %s", name);
return NULL;
}
@@ -5708,7 +5819,7 @@
}
if (ms->recv && job_assumes(j, !machservice_active(ms))) {
- job_log(j, LOG_NOTICE, "Closing receive right for %s", ms->name);
+ job_log(j, LOG_DEBUG, "Closing receive right for %s", ms->name);
job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS);
}
@@ -5718,7 +5829,7 @@
the_exception_server = 0;
}
- job_log(j, LOG_NOTICE, "Mach service deleted%s: %s", port_died ? " (port died)" : "", ms->name);
+ job_log(j, LOG_DEBUG, "Mach service deleted%s: %s", port_died ? " (port died)" : "", ms->name);
if (ms->special_port_num) {
SLIST_REMOVE(&special_ports, ms, machservice, special_port_sle);
@@ -6221,7 +6332,7 @@
}
if (unlikely(j->anonymous)) {
- job_log(j, LOG_ERR, "Anonymous job tried to setup shared memory");
+ job_log(j, LOG_DEBUG, "Anonymous job tried to setup shared memory");
return BOOTSTRAP_NOT_PRIVILEGED;
}
@@ -6350,14 +6461,14 @@
job_assumes(j, runtime_kill(otherj->p, SIGKILL) != -1);
return 0;
}
-
+ #if !TARGET_OS_EMBEDDED
if (__sync_bool_compare_and_swap(&j->shmem->vp_shmem_transaction_cnt, 0, -1)) {
j->shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING;
j->sent_kill_via_shmem = true;
job_assumes(j, runtime_kill(otherj->p, SIGKILL) != -1);
return 0;
}
-
+ #endif
return BOOTSTRAP_NOT_PRIVILEGED;
} else if (otherj->p) {
job_assumes(j, runtime_kill(otherj->p, sig) != -1);
@@ -6404,7 +6515,7 @@
vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
{
const char *action;
- launch_data_t input_obj, output_obj;
+ launch_data_t input_obj = NULL, output_obj = NULL;
size_t data_offset = 0;
size_t packed_size;
struct ldcred *ldc = runtime_get_caller_creds();
@@ -6476,8 +6587,13 @@
}
if (invalCnt) switch (inkey) {
- case VPROC_GSK_ENVIRONMENT:
-
+ case VPROC_GSK_ENVIRONMENT:
+ if( launch_data_get_type(input_obj) == LAUNCH_DATA_DICTIONARY ) {
+ if( j->p ) {
+ job_log(j, LOG_NOTICE, "Setting environment for a currently active job. This environment will take effect on the next invocation of the job.");
+ }
+ launch_data_dict_iterate(input_obj, envitem_setup_one_shot, j);
+ }
break;
case 0:
break;
@@ -6568,6 +6684,9 @@
job_log(j, LOG_DEBUG, "Reading transaction model status.");
*outval = j->kill_via_shmem;
break;
+ case VPROC_GSK_WAITFORDEBUGGER:
+ *outval = j->wait4debugger;
+ break;
case 0:
*outval = 0;
break;
@@ -6641,9 +6760,10 @@
j->kill_via_shmem = (bool)inval;
job_log(j, LOG_DEBUG, "j->kill_via_shmem = %s", j->kill_via_shmem ? "true" : "false");
}
+ break;
case VPROC_GSK_WEIRD_BOOTSTRAP:
if( job_assumes(j, j->weird_bootstrap) ) {
- job_log(j, LOG_NOTICE, "Unsetting weird bootstrap.");
+ job_log(j, LOG_DEBUG, "Unsetting weird bootstrap.");
mach_msg_size_t mxmsgsz = (typeof(mxmsgsz)) sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem);
@@ -6654,6 +6774,10 @@
job_assumes(j, runtime_add_mport(j->mgr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS);
j->weird_bootstrap = false;
}
+ break;
+ case VPROC_GSK_WAITFORDEBUGGER:
+ j->wait4debugger_oneshot = inval;
+ break;
case 0:
break;
default:
@@ -7160,7 +7284,7 @@
struct ldcred *ldc = runtime_get_caller_creds();
- /* Only allow root processes to look up children, even if we're in the per-user laucnhd.
+ /* Only allow root processes to look up children, even if we're in the per-user launchd.
* Otherwise, this could be used to cross sessions, which counts as a security vulnerability
* in a non-flat namespace.
*/
@@ -7288,7 +7412,7 @@
return BOOTSTRAP_NOT_PRIVILEGED;
}
- job_t j_for_pid = jobmgr_find_by_pid_deep(j->mgr, p);
+ job_t j_for_pid = jobmgr_find_by_pid_deep(j->mgr, p, false);
if( j_for_pid ) {
if( j_for_pid->kill_via_shmem ) {
if( j_for_pid->shmem ) {
@@ -7322,7 +7446,7 @@
/* This is so loginwindow doesn't try to quit GUI apps that have been launched
* directly by launchd as agents.
*/
- job_t j_for_pid = jobmgr_find_by_pid_deep(root_jobmgr, p);
+ job_t j_for_pid = jobmgr_find_by_pid_deep(root_jobmgr, p, false);
if( j_for_pid && !j_for_pid->anonymous && !j_for_pid->legacy_LS_job ) {
*managed = true;
}
@@ -7330,6 +7454,31 @@
return BOOTSTRAP_SUCCESS;
}
+kern_return_t
+job_mig_port_for_label(job_t j __attribute__((unused)), name_t label, mach_port_t *mp)
+{
+ struct ldcred *ldc = runtime_get_caller_creds();
+ kern_return_t kr = BOOTSTRAP_NOT_PRIVILEGED;
+
+ mach_port_t _mp = MACH_PORT_NULL;
+ if( ldc->euid == 0 || ldc->euid == geteuid() ) {
+ job_t target_j = job_find(label);
+ if( jobmgr_assumes(root_jobmgr, target_j != NULL) ) {
+ if( target_j->j_port == MACH_PORT_NULL ) {
+ job_assumes(target_j, job_setup_machport(target_j) == true);
+ }
+
+ _mp = target_j->j_port;
+ kr = _mp != MACH_PORT_NULL ? BOOTSTRAP_SUCCESS : BOOTSTRAP_NO_MEMORY;
+ } else {
+ kr = BOOTSTRAP_NO_MEMORY;
+ }
+ }
+
+ *mp = _mp;
+ return kr;
+}
+
jobmgr_t
jobmgr_find_by_name(jobmgr_t jm, const char *where)
{
@@ -7367,8 +7516,6 @@
}
}
}
-
- launchd_assumes(jmi != NULL);
jm_found:
return jmi;
@@ -7506,19 +7653,32 @@
}
kern_return_t
-job_mig_detach_from_console(job_t j, mach_port_t *new_bsport)
+job_mig_switch_to_session(job_t j, mach_port_t requestor_port, name_t session_name, mach_port_t *new_bsport)
{
- if( j->mgr == root_jobmgr ) {
+ job_log(j, LOG_NOTICE, "Job wants to move to %s session.", session_name);
+
+ if( !job_assumes(j, pid1_magic == false) ) {
+ job_log(j, LOG_WARNING, "Switching sessions is not allowed in the system Mach bootstrap.");
return BOOTSTRAP_NOT_PRIVILEGED;
}
if( !j->anonymous ) {
- job_log(j, LOG_NOTICE, "Non-anonymous job tried to move to Background session. Please set LimitLoadToSessionType in the launchd property list to \"Background\" instead.");
+ job_log(j, LOG_NOTICE, "Non-anonymous job tried to switch sessions. Please use LimitLoadToSessionType instead.");
return BOOTSTRAP_NOT_PRIVILEGED;
}
- job_log(j, LOG_NOTICE, "Detaching from console session.");
+ jobmgr_t target_jm = jobmgr_find_by_name(root_jobmgr, session_name);
+ if( target_jm == j->mgr ) {
+ job_log(j, LOG_WARNING, "Job tried to switch to its current session (%s).", session_name);
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+ target_jm = target_jm ? : jobmgr_new(j->mgr, requestor_port, MACH_PORT_NULL, false, session_name);
+ if( !job_assumes(j, target_jm != NULL) ) {
+ job_log(j, LOG_WARNING, "Could not find %s session!", session_name);
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
/* Remove the job from it's current job manager. */
LIST_REMOVE(j, sle);
LIST_REMOVE(j, pid_hash_sle);
@@ -7531,25 +7691,25 @@
}
}
- /* Put the job into the background job manager. */
- LIST_INSERT_HEAD(&root_jobmgr->jobs, j, sle);
- LIST_INSERT_HEAD(&root_jobmgr->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle);
+ /* Put the job into the target job manager. */
+ LIST_INSERT_HEAD(&target_jm->jobs, j, sle);
+ LIST_INSERT_HEAD(&target_jm->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle);
if( ji ) {
- LIST_INSERT_HEAD(&root_jobmgr->global_env_jobs, j, global_env_sle);
+ LIST_INSERT_HEAD(&target_jm->global_env_jobs, j, global_env_sle);
}
- /* Move our Mach services over. */
+ /* Move our Mach services over if we're not in a flat namespace. */
if( !g_flat_mach_namespace && !SLIST_EMPTY(&j->machservices) ) {
struct machservice *msi = NULL, *msit = NULL;
SLIST_FOREACH_SAFE( msi, &j->machservices, sle, msit ) {
LIST_REMOVE(msi, name_hash_sle);
- LIST_INSERT_HEAD(&root_jobmgr->ms_hash[hash_ms(msi->name)], msi, name_hash_sle);
+ LIST_INSERT_HEAD(&target_jm->ms_hash[hash_ms(msi->name)], msi, name_hash_sle);
}
}
- j->mgr = root_jobmgr;
- *new_bsport = root_jobmgr->jm_port;
+ j->mgr = target_jm;
+ *new_bsport = target_jm->jm_port;
return KERN_SUCCESS;
}
@@ -7726,7 +7886,7 @@
*subsetportp = jmr->jm_port;
jmr->created_via_subset = true;
- job_log(j, LOG_NOTICE, "Job created a subset named \"%s\"", jmr->name);
+ job_log(j, LOG_DEBUG, "Job created a subset named \"%s\"", jmr->name);
return BOOTSTRAP_SUCCESS;
}
@@ -8059,47 +8219,185 @@
free(w4r);
}
+size_t
+get_kern_max_proc(void)
+{
+ int mib[] = { CTL_KERN, KERN_MAXPROC };
+ int max = 100;
+ size_t max_sz = sizeof(max);
+
+ launchd_assumes(sysctl(mib, 2, &max, &max_sz, NULL, 0) != -1);
+
+ return max;
+}
+
+/* See rdar://problem/6271234 */
void
-do_unmounts(void)
+eliminate_double_reboot(void)
{
- struct statfs buf[250];
- int r, i, found, returned;
-
- do {
- returned = getfsstat(buf, (int) sizeof(buf), MNT_NOWAIT);
- found = 0;
-
- if (!launchd_assumes(returned != -1)) {
- return;
+ if( unlikely(!pid1_magic) ) {
+ return;
+ }
+
+ struct stat sb;
+ const char *argv[] = { "/etc/rc.deferredinstall", NULL };
+ char *try_again = "Will try again at next boot.";
+ int result = ~0;
+
+ if( unlikely(stat(argv[0], &sb) != -1) ) {
+ jobmgr_log(root_jobmgr, LOG_DEBUG | LOG_CONSOLE, "Going to run deferred install script.");
+
+ int wstatus;
+ pid_t p;
+
+ jobmgr_assumes(root_jobmgr, (errno = posix_spawnp(&p, argv[0], NULL, NULL, (char **)argv, environ)) == 0);
+
+ if (errno) {
+ jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Couldn't run deferred install script! %s", try_again);
+ goto out;
}
-
- /* Work backwards due to mounts on top of mounts */
- for (i = returned - 1; i >= 0; i--) {
- if (strcmp(buf[i].f_mntonname, "/") == 0) {
- continue;
- } else if (strncmp(buf[i].f_mntonname, "/dev", strlen("/dev")) == 0) {
- continue;
+
+ if( !jobmgr_assumes(root_jobmgr, waitpid(p, &wstatus, 0) != -1) ) {
+ jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Couldn't confirm that deferred install script exited successfully! %s", try_again);
+ goto out;
+ }
+
+ if( jobmgr_assumes(root_jobmgr, WIFEXITED(wstatus) != 0) ) {
+ if( jobmgr_assumes(root_jobmgr, (result = WEXITSTATUS(wstatus)) == EXIT_SUCCESS) ) {
+ jobmgr_log(root_jobmgr, LOG_DEBUG | LOG_CONSOLE, "Deferred install script completed successfully.");
+ } else {
+ jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Deferred install script exited with status %d. %s", WEXITSTATUS(wstatus), try_again);
}
+ } else {
+ jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Confirmed that deferred install script exited, but couldn't confirm that it was successful. %s", try_again);
+ }
+ }
+out:
+ if( result == 0 ) {
+ /* If the unlink(2) was to fail, it would be most likely fail with EBUSY. All the other
+ * failure cases for unlink(2) don't apply when we're running under PID 1 and have verified
+ * that the file exists. Outside of someone deliberately messing with us (like if /etc/rc.deferredinstall
+ * is actually a looping sym-link or a mount point for a filesystem) and I/O errors, we should be good.
+ */
+ if( !jobmgr_assumes(root_jobmgr, unlink(argv[0]) != -1) ) {
+ jobmgr_log(root_jobmgr, LOG_WARNING | LOG_CONSOLE, "Deferred install script couldn't be removed!");
+ }
+ }
+}
- r = unmount(buf[i].f_mntonname, 0);
+static void
+jetsam_priority_from_job(job_t j, bool front, jetsam_priority_entry_t *jp)
+{
+ jp->pid = j->p;
+ jp->flags |= front ? kJetsamFlagsFrontmost : 0;
+}
- runtime_syslog(LOG_DEBUG, "unmount(\"%s\", 0): %s", buf[i].f_mntonname, r == -1 ? strerror(errno) : "Success");
-
- if (r != -1) {
- found++;
- }
- }
- } while ((returned == (sizeof(buf) / sizeof(buf[0]))) && (found > 0));
+static int
+job_cmp(const job_t *lhs, const job_t *rhs)
+{
+ job_t _lhs = *lhs;
+ job_t _rhs = *rhs;
+ /* Sort in descending order. (Priority correlates to the soonishness with which you will be killed.) */
+ if( _lhs->jetsam_priority > _rhs->jetsam_priority ) {
+ return -1;
+ } else if( _lhs->jetsam_priority < _rhs->jetsam_priority ) {
+ return 1;
+ }
+
+ return 0;
}
-size_t
-get_kern_max_proc(void)
+int
+launchd_set_jetsam_priorities(launch_data_t priorities)
{
- int mib[] = { CTL_KERN, KERN_MAXPROC };
- int max = 100;
- size_t max_sz = sizeof(max);
+ if( !launchd_assumes(launch_data_get_type(priorities) == LAUNCH_DATA_ARRAY) ) {
+ return EINVAL;
+ }
- launchd_assumes(sysctl(mib, 2, &max, &max_sz, NULL, 0) != -1);
+ jobmgr_t jm = NULL;
+#if !TARGET_OS_EMBEDDED
+ /* For testing. */
+ jm = jobmgr_find_by_name(root_jobmgr, VPROCMGR_SESSION_AQUA);
+ if( !launchd_assumes(jm != NULL) ) {
+ return EINVAL;
+ }
+#else
+ /* Since this is for embedded, we can assume that the root job manager holds the Jetsam jobs. */
+ jm = root_jobmgr;
+#endif
+
+ size_t npris = launch_data_array_get_count(priorities);
+
+ job_t ji = NULL;
+ size_t i = 0;
+ for( i = 0; i < npris; i++ ) {
+ launch_data_t ldi = launch_data_array_get_index(priorities, i);
+ if( !launchd_assumes(launch_data_get_type(ldi) == LAUNCH_DATA_DICTIONARY) ) {
+ continue;
+ }
+
+ launch_data_t label = NULL;
+ if( !launchd_assumes(label = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMLABEL)) ) {
+ continue;
+ }
+ const char *_label = launch_data_get_string(label);
+
+ ji = job_find(_label);
+ if( !launchd_assumes(ji != NULL) ) {
+ continue;
+ }
+
+ launch_data_t pri;
+ long long _pri = 0;
+ if( !launchd_assumes(pri = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMPRIORITY)) ) {
+ continue;
+ }
+ _pri = launch_data_get_integer(pri);
+
+ if( ji->jetsam_priority == LAUNCHD_JETSAM_PRIORITY_UNSET ) {
+ LIST_INSERT_HEAD(&ji->mgr->jetsam_jobs, ji, jetsam_sle);
+ ji->mgr->jetsam_jobs_cnt++;
+ }
+ ji->jetsam_priority = _pri;
+
+ launch_data_t frontmost = NULL;
+ if( !(frontmost = launch_data_dict_lookup(ldi, LAUNCH_KEY_JETSAMFRONTMOST)) ) {
+ ji->jetsam_frontmost = false;
+ continue;
+ }
+ ji->jetsam_frontmost = launch_data_get_bool(frontmost);
+ }
- return max;
+ i = 0;
+ job_t *jobs = (job_t *)calloc(jm->jetsam_jobs_cnt, sizeof(job_t));
+ LIST_FOREACH( ji, &jm->jetsam_jobs, jetsam_sle ) {
+ if( ji->p ) {
+ jobs[i] = ji;
+ i++;
+ }
+ }
+ size_t totalpris = i;
+
+ int result = EINVAL;
+ if( launchd_assumes(totalpris > 0) ) {
+ qsort((void *)jobs, totalpris, sizeof(job_t), (int (*)(const void *, const void *))job_cmp);
+
+ jetsam_priority_entry_t *jpris = (jetsam_priority_entry_t *)calloc(totalpris, sizeof(jetsam_priority_entry_t));
+ if( !launchd_assumes(jpris != NULL) ) {
+ result = ENOMEM;
+ } else {
+ for( i = 0; i < totalpris; i++ ) {
+ jetsam_priority_from_job(jobs[i], jobs[i]->jetsam_frontmost, &jpris[i]);
+ }
+
+ int _result = 0;
+ launchd_assumes((_result = sysctlbyname("kern.memorystatus_priority_list", NULL, NULL, &jpris[0], totalpris * sizeof(jetsam_priority_entry_t))) != -1);
+ result = _result != 0 ? errno : 0;
+
+ free(jpris);
+ }
+ }
+ free(jobs);
+
+ return result;
}
Modified: branches/PR-6424345/launchd/src/launchd_core_logic.h
===================================================================
--- branches/PR-6424345/launchd/src/launchd_core_logic.h 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launchd_core_logic.h 2009-01-20 03:58:30 UTC (rev 23784)
@@ -56,4 +56,6 @@
void job_log(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
void job_set_pid_crashed(pid_t p);
+int launchd_set_jetsam_priorities(launch_data_t priorities);
+
#endif
Modified: branches/PR-6424345/launchd/src/launchd_runtime.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd_runtime.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launchd_runtime.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -131,7 +131,7 @@
bool do_apple_internal_logging;
bool low_level_debug;
bool g_force_old_kill_path = false;
-bool g_flat_mach_namespace = false;
+bool g_flat_mach_namespace = true;
mach_port_t
runtime_get_kernel_port(void)
@@ -592,10 +592,7 @@
kevi = &kev[i];
if (kevi->filter) {
-/* Leave on for SnowLeopard development. We should really try and identify what bugs would
- * cause kevi->udata to be invalid.
- */
-#if 1
+#if 0
Dl_info dli;
/* Check if kevi->udata was either malloc(3)ed or is a valid function pointer.
@@ -606,7 +603,7 @@
runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags);
(*((kq_callback *)kevi->udata))(kevi->udata, kevi);
runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END);
-#if 1
+#if 0
} else {
log_kevent_struct(LOG_EMERG, kevi, i);
}
@@ -1073,7 +1070,11 @@
tmp_options |= MACH_RCV_TIMEOUT;
if (!(tmp_options & MACH_SEND_TIMEOUT)) {
+ #if !TARGET_OS_EMBEDDED
to = busy_cnt ? runtime_idle_timeout : (_vproc_standby_timeout() * 1000);
+ #else
+ to = runtime_idle_timeout;
+ #endif
}
}
@@ -1510,7 +1511,9 @@
runtime_add_ref(void)
{
if (!pid1_magic) {
+ #if !TARGET_OS_EMBEDDED
_vproc_transaction_begin();
+ #endif
}
runtime_busy_cnt++;
}
@@ -1519,7 +1522,9 @@
runtime_del_ref(void)
{
if (!pid1_magic) {
+ #if !TARGET_OS_EMBEDDED
_vproc_transaction_end();
+ #endif
}
runtime_busy_cnt--;
}
@@ -1528,7 +1533,9 @@
runtime_add_weak_ref(void)
{
if (!pid1_magic) {
+ #if !TARGET_OS_EMBEDDED
_vproc_standby_begin();
+ #endif
}
runtime_standby_cnt++;
}
@@ -1537,7 +1544,9 @@
runtime_del_weak_ref(void)
{
if (!pid1_magic) {
+ #if !TARGET_OS_EMBEDDED
_vproc_standby_end();
+ #endif
}
runtime_standby_cnt--;
}
@@ -1734,7 +1743,7 @@
g_force_old_kill_path = true;
}
- if( !pid1_magic && stat("/var/db/.launchd_flat_per_user_namespace", &sb) == 0 ) {
- g_flat_mach_namespace = true;
+ if( !pid1_magic && stat("/var/db/.launchd_no_flat_per_user_namespace", &sb) == 0 ) {
+ g_flat_mach_namespace = false;
}
}
Modified: branches/PR-6424345/launchd/src/launchd_unix_ipc.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd_unix_ipc.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/launchd_unix_ipc.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -415,6 +415,8 @@
resp = job_export(j);
ipc_revoke_fds(resp);
}
+ } else if( !strcmp(cmd, LAUNCH_KEY_SETPRIORITYLIST) ) {
+ resp = launch_data_new_errno(launchd_set_jetsam_priorities(data));
}
rmc->resp = resp;
Modified: branches/PR-6424345/launchd/src/libbootstrap.c
===================================================================
--- branches/PR-6424345/launchd/src/libbootstrap.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/libbootstrap.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -21,6 +21,7 @@
#include "config.h"
#include "launch.h"
#include "launch_priv.h"
+#include "bootstrap.h"
#include "bootstrap_priv.h"
#include "vproc.h"
#include "vproc_priv.h"
Modified: branches/PR-6424345/launchd/src/liblaunch.c
===================================================================
--- branches/PR-6424345/launchd/src/liblaunch.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/liblaunch.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -208,30 +208,44 @@
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
+ /* The rules are as follows.
+ * - All users (including root) talk to their per-user launchd's by default.
+ * - If we have been invoked under sudo, talk to the system launchd.
+ * - If we're the root user and the __USE_SYSTEM_LAUNCHD environment variable is set, then
+ * talk to the system launchd.
+ */
if (where && where[0] != '\0') {
strncpy(sun.sun_path, where, sizeof(sun.sun_path));
- } else if ((!getenv("SUDO_COMMAND") || geteuid() != 0) && _vprocmgr_getsocket(spath) == 0) {
- size_t min_len;
-
- min_len = sizeof(sun.sun_path) < sizeof(spath) ? sizeof(sun.sun_path) : sizeof(spath);
-
- strncpy(sun.sun_path, spath, min_len);
} else {
- strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
+ if( _vprocmgr_getsocket(spath) == 0 ) {
+ if( (getenv("SUDO_COMMAND") || getenv("__USE_SYSTEM_LAUNCHD")) && geteuid() == 0 ) {
+ /* Talk to the system launchd. */
+ strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
+ } else {
+ /* Talk to our per-user launchd. */
+ size_t min_len;
+
+ min_len = sizeof(sun.sun_path) < sizeof(spath) ? sizeof(sun.sun_path) : sizeof(spath);
+
+ strncpy(sun.sun_path, spath, min_len);
+ }
+ }
}
-
+
if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
goto out_bad;
}
-
if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun))) {
goto out_bad;
}
}
- if (!(_lc->l = launchd_fdopen(lfd)))
+
+ if (!(_lc->l = launchd_fdopen(lfd))) {
goto out_bad;
- if (!(_lc->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY)))
+ }
+ if (!(_lc->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
goto out_bad;
+ }
return;
out_bad:
@@ -1265,26 +1279,5 @@
return -1;
}
-#define BEZEL_UI_HACK
-#ifdef BEZEL_UI_HACK
- #define BEZEL_UI_PATH "/System/Library/LoginPlugins/BezelServices.loginPlugin/Contents/Resources/BezelUI/BezelUIServer"
- #define BEZEL_UI_PLIST "/System/Library/LaunchAgents/com.apple.BezelUIServer.plist"
- #define BEZEL_UI_SERVICE "BezelUI"
-
- mach_port_t bezel_ui_server;
- struct stat sb;
- if (!(stat(BEZEL_UI_PLIST, &sb) == 0 && S_ISREG(sb.st_mode))) {
- if (bootstrap_create_server(bootstrap_port, BEZEL_UI_PATH, target_user, true, &bezel_ui_server) == BOOTSTRAP_SUCCESS) {
- mach_port_t srv;
-
- if (bootstrap_check_in(bezel_ui_server, BEZEL_UI_SERVICE, &srv) == BOOTSTRAP_SUCCESS) {
- mach_port_mod_refs(mach_task_self(), srv, MACH_PORT_RIGHT_RECEIVE, -1);
- }
-
- mach_port_deallocate(mach_task_self(), bezel_ui_server);
- }
- }
-#endif
-
return 1;
}
Modified: branches/PR-6424345/launchd/src/libvproc.c
===================================================================
--- branches/PR-6424345/launchd/src/libvproc.c 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/libvproc.c 2009-01-20 03:58:30 UTC (rev 23784)
@@ -34,6 +34,8 @@
#include <pthread.h>
#include <signal.h>
#include <assert.h>
+#include <libkern/OSAtomic.h>
+
#if HAVE_QUARANTINE
#include <quarantine.h>
#endif
@@ -59,6 +61,55 @@
static pthread_once_t shmem_inited = PTHREAD_ONCE_INIT;
static uint64_t s_cached_transactions_enabled = 0;
+struct vproc_s {
+ int32_t refcount;
+ mach_port_t j_port;
+};
+
+vproc_t vprocmgr_lookup_vproc(const char *label)
+{
+ struct vproc_s *vp = NULL;
+
+ mach_port_t mp = MACH_PORT_NULL;
+ kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp);
+ if( kr == BOOTSTRAP_SUCCESS ) {
+ vp = (struct vproc_s *)calloc(1, sizeof(struct vproc_s));
+ if( vp ) {
+ vp->refcount = 1;
+ mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
+ vp->j_port = mp;
+ }
+ mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, -1);
+ }
+
+ return vp;
+}
+
+vproc_t vproc_retain(vproc_t vp)
+{
+ int32_t orig = OSAtomicAdd32(1, &vp->refcount) - 1;
+ if( orig <= 0 ) {
+ /* We've gone from 0 to 1, which means that this object was due to be freed. */
+ __crashreporter_info__ = "Under-retain / over-release of vproc_t.";
+ abort();
+ }
+
+ return vp;
+}
+
+void vproc_release(vproc_t vp)
+{
+ int32_t newval = OSAtomicAdd32(-1, &vp->refcount);
+ if( newval < 0 ) {
+ /* We're in negative numbers, which is bad. */
+ __crashreporter_info__ = "Over-release of vproc_t.";
+ abort();
+ } else if( newval == 0 ) {
+ mach_port_deallocate(mach_task_self(), vp->j_port);
+ free(vp);
+ }
+}
+
static void
vproc_shmem_init(void)
{
@@ -69,18 +120,31 @@
kr = vproc_mig_setup_shmem(bootstrap_port, &shmem_port);
//assert(kr == 0);
- if (kr) return;
+ if (kr) {
+ /* rdar://problem/6416724
+ * If we fail to set up a shared memory page, just allocate a local chunk
+ * of memory. This way, processes can still introspect their own transaction
+ * counts if they're being run under a debugger. Moral of the story: Debug
+ * from the environment you intend to run in.
+ */
+ void *_vm_addr = calloc(1, sizeof(struct vproc_shmem_s));
+ if( !_vm_addr ) {
+ return;
+ }
- kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false,
- VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
+ vm_addr = (vm_address_t)_vm_addr;
+ } else {
+ kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false,
+ VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
+
+ //assert(kr == 0);
+ if (kr) return;
+
+ kr = mach_port_deallocate(mach_task_self(), shmem_port);
+
+ //assert(kr == 0);
+ }
- //assert(kr == 0);
- if (kr) return;
-
- kr = mach_port_deallocate(mach_task_self(), shmem_port);
-
- //assert(kr == 0);
-
vproc_shmem = (struct vproc_shmem_s *)vm_addr;
}
@@ -101,8 +165,9 @@
vproc_transaction_begin(vproc_t vp __attribute__((unused)))
{
vproc_transaction_t vpt = (vproc_transaction_t)vproc_shmem_init; /* we need a "random" variable that is testable */
-
+#if !TARGET_OS_EMBEDDED
_vproc_transaction_begin();
+#endif
return vpt;
}
@@ -110,6 +175,7 @@
void
_vproc_transaction_begin(void)
{
+#if !TARGET_OS_EMBEDDED
if (unlikely(vproc_shmem == NULL)) {
int po_r = pthread_once(&shmem_inited, vproc_client_init);
if (po_r != 0 || vproc_shmem == NULL) {
@@ -133,6 +199,7 @@
} while( !__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, old, old + 1) );
runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT, old + 1, 0, 0);
+#endif
}
size_t
@@ -170,10 +237,16 @@
_vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
{
boolean_t _condemned = false;
- return vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, (boolean_t *)condemned ? : &_condemned);
+ kern_return_t kr = vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, &_condemned);
+ if( kr == KERN_SUCCESS && condemned ) {
+ *condemned = _condemned ? true : false;
+ }
+
+ return kr;
}
void
+#if !TARGET_OS_EMBEDDED
_vproc_transaction_try_exit(int status)
{
if (unlikely(vproc_shmem == NULL)) {
@@ -185,6 +258,12 @@
_exit(status);
}
}
+#else
+_vproc_transaction_try_exit(int status __attribute__((unused)))
+{
+
+}
+#endif
void
vproc_transaction_end(vproc_t vp __attribute__((unused)), vproc_transaction_t vpt)
@@ -194,12 +273,15 @@
abort();
}
+#if !TARGET_OS_EMBEDDED
_vproc_transaction_end();
+#endif
}
void
_vproc_transaction_end(void)
{
+#if !TARGET_OS_EMBEDDED
typeof(vproc_shmem->vp_shmem_transaction_cnt) newval;
if (unlikely(vproc_shmem == NULL)) {
@@ -218,6 +300,7 @@
}
abort();
}
+#endif
}
vproc_standby_t
@@ -414,12 +497,14 @@
return !issetugid() ? _vproc_post_fork_ping() : NULL;
}
-vproc_err_t
-_vprocmgr_detach_from_console(uint32_t flags __attribute__((unused)))
+vproc_err_t
+_vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __attribute__((unused)))
{
mach_port_t new_bsport = MACH_PORT_NULL;
- if( vproc_mig_detach_from_console(bootstrap_port, &new_bsport) != KERN_SUCCESS ) {
- return (vproc_err_t)_vprocmgr_detach_from_console;
+ kern_return_t kr = KERN_FAILURE;
+ if( (kr = vproc_mig_switch_to_session(bootstrap_port, mach_task_self(), (char *)target_session, &new_bsport)) != KERN_SUCCESS ) {
+ _vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr);
+ return (vproc_err_t)_vprocmgr_switch_to_session;
}
task_set_bootstrap_port(mach_task_self(), new_bsport);
@@ -429,6 +514,12 @@
return !issetugid() ? _vproc_post_fork_ping() : NULL;
}
+vproc_err_t
+_vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused)))
+{
+ return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0);
+}
+
pid_t
_spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version)
{
@@ -703,7 +794,7 @@
}
vproc_err_t
-vproc_swap_integer(vproc_t vp __attribute__((unused)), vproc_gsk_t key, int64_t *inval, int64_t *outval)
+vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval)
{
static int64_t cached_is_managed = -1;
int64_t dummyval = 0;
@@ -742,7 +833,8 @@
break;
}
- if (vproc_mig_swap_integer(bootstrap_port, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval) == 0) {
+ mach_port_t mp = vp ? vp->j_port : bootstrap_port;
+ if (vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval) == 0) {
switch (key) {
case VPROC_GSK_MGR_PID:
cached_pid = outval ? *outval : dummyval;
@@ -789,7 +881,7 @@
}
vproc_err_t
-vproc_swap_complex(vproc_t vp __attribute__((unused)), vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
+vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
{
size_t data_offset = 0, good_enough_size = 10*1024*1024;
mach_msg_type_number_t indata_cnt = 0, outdata_cnt;
@@ -810,7 +902,8 @@
indata = (vm_offset_t)buf;
}
- if (vproc_mig_swap_complex(bootstrap_port, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
+ mach_port_t mp = vp ? vp->j_port : bootstrap_port;
+ if (vproc_mig_swap_complex(mp, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
goto out;
}
Modified: branches/PR-6424345/launchd/src/protocol_vproc.defs
===================================================================
--- branches/PR-6424345/launchd/src/protocol_vproc.defs 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/protocol_vproc.defs 2009-01-20 03:58:30 UTC (rev 23784)
@@ -182,8 +182,10 @@
out __child_names : name_array_t, dealloc;
out __child_properties : bootstrap_property_array_t, dealloc);
-routine detach_from_console(
+routine switch_to_session(
__bs_port : job_t;
+ __req_port : mach_port_t;
+ __session_name : name_t;
out __new_bs_port : mach_port_make_send_t);
routine transaction_count_for_pid(
@@ -197,3 +199,7 @@
__pid : pid_t;
out __managed : boolean_t);
+routine port_for_label(
+ __bs_port : job_t;
+ __label : name_t;
+out __mp : mach_port_make_send_t);
Modified: branches/PR-6424345/launchd/src/vproc.h
===================================================================
--- branches/PR-6424345/launchd/src/vproc.h 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/vproc.h 2009-01-20 03:58:30 UTC (rev 23784)
@@ -34,7 +34,7 @@
typedef void * vproc_err_t;
-typedef void * vproc_t;
+typedef struct vproc_s * vproc_t;
typedef void * vprocmgr_t;
const char *vproc_strerror(vproc_err_t r);
Modified: branches/PR-6424345/launchd/src/vproc_priv.h
===================================================================
--- branches/PR-6424345/launchd/src/vproc_priv.h 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd/src/vproc_priv.h 2009-01-20 03:58:30 UTC (rev 23784)
@@ -59,12 +59,17 @@
VPROC_GSK_ABANDON_PROCESS_GROUP,
VPROC_GSK_TRANSACTIONS_ENABLED,
VPROC_GSK_WEIRD_BOOTSTRAP,
+ VPROC_GSK_WAITFORDEBUGGER,
} vproc_gsk_t;
typedef unsigned int vproc_flags_t;
/* For _vproc_kickstart_by_label() -- instructs launchd to kickstart the job to stall before exec(2). */
#define VPROCFLAG_STALL_JOB_EXEC 1 << 1
+vproc_t vprocmgr_lookup_vproc(const char *label);
+vproc_t vproc_retain(vproc_t vp);
+void vproc_release(vproc_t vp);
+
vproc_err_t vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval);
vproc_err_t vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval);
@@ -90,6 +95,7 @@
#define VPROCMGR_SESSION_SYSTEM "System"
vproc_err_t _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type);
+vproc_err_t _vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags);
vproc_err_t _vprocmgr_detach_from_console(vproc_flags_t flags);
void _vproc_standby_begin(void) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
Modified: branches/PR-6424345/launchd.xcodeproj/project.pbxproj
===================================================================
--- branches/PR-6424345/launchd.xcodeproj/project.pbxproj 2009-01-13 23:11:13 UTC (rev 23783)
+++ branches/PR-6424345/launchd.xcodeproj/project.pbxproj 2009-01-20 03:58:30 UTC (rev 23784)
@@ -1005,6 +1005,7 @@
PREBINDING = NO;
PRODUCT_NAME = launchd;
STRIP_STYLE = debugging;
+ VALID_ARCHS = "armv5 armv6 armv7 i386 ppc ppc64 ppc7400 ppc970 x86_64";
ZERO_LINK = NO;
};
name = Release;
@@ -1031,6 +1032,7 @@
);
PRODUCT_NAME = launch;
PUBLIC_HEADERS_FOLDER_PATH = /usr/include;
+ VALID_ARCHS = "armv5 armv6 armv7 i386 ppc ppc64 ppc7400 ppc970 x86_64";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = __;
};
@@ -1048,6 +1050,7 @@
PREBINDING = NO;
PRODUCT_NAME = launchctl;
STRIP_STYLE = debugging;
+ VALID_ARCHS = "armv5 armv6 armv7 i386 ppc ppc64 ppc7400 ppc970 x86_64";
ZERO_LINK = NO;
};
name = Release;
@@ -1085,6 +1088,7 @@
PREBINDING = NO;
PRODUCT_NAME = SystemStarter;
STRIP_STYLE = debugging;
+ VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64";
ZERO_LINK = NO;
};
name = Release;
@@ -1100,6 +1104,7 @@
PREBINDING = NO;
PRODUCT_NAME = launchproxy;
STRIP_STYLE = debugging;
+ VALID_ARCHS = "armv5 armv6 armv7 i386 ppc ppc64 ppc7400 ppc970 x86_64";
ZERO_LINK = NO;
};
name = Release;
@@ -1115,6 +1120,7 @@
PREBINDING = NO;
PRODUCT_NAME = wait4path;
STRIP_STYLE = debugging;
+ VALID_ARCHS = "armv5 armv6 armv7 i386 ppc ppc64 ppc7400 ppc970 x86_64";
ZERO_LINK = NO;
};
name = Release;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-changes/attachments/20090119/002e4925/attachment-0001.html>
More information about the launchd-changes
mailing list