[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