[launchd-changes] [23760] trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Dec 10 20:42:32 PST 2008


Revision: 23760
          http://trac.macosforge.org/projects/launchd/changeset/23760
Author:   dsorresso at apple.com
Date:     2008-12-10 20:42:32 -0800 (Wed, 10 Dec 2008)
Log Message:
-----------
<rdar://problem/6263051> launchd should remove the BezelUI hack
<rdar://problem/6271234> QFA: Need to be able to install software at shutdown time to eliminate double reboot
<rdar://problem/6399100> launchd is killing stray processes at shutdown, but only when running Apple-internal?
<rdar://problem/6416724> launchd_libs should go to the heap if it can't allocate shared memory for Instant Off (for debugging purposes)
<rdar://problem/6423066> launchctl should have an option to output XML so that it can be integrated easily with scripts
<rdar://problem/6423070> launchctl should have an option to talk to the root launchd
<rdar://problem/6426061> launchctl should have "managerpid" and "manageruid" subcommands
<rdar://problem/6427956> SnowLeopard10A226: hang on attempt to restart
<rdar://problem/6010023> Repair critical directories, /private/tmp, /var/folders/, et al. at boot?

Embedded build fixes.

Modified Paths:
--------------
    trunk/launchd/src/launchctl.c
    trunk/launchd/src/launchd.c
    trunk/launchd/src/launchd_core_logic.c
    trunk/launchd/src/launchd_runtime.c
    trunk/launchd/src/libbootstrap.c
    trunk/launchd/src/liblaunch.c
    trunk/launchd/src/libvproc.c
    trunk/launchd/src/protocol_vproc.defs
    trunk/launchd/src/vproc_priv.h
    trunk/launchd.xcodeproj/project.pbxproj

Modified: trunk/launchd/src/launchctl.c
===================================================================
--- trunk/launchd/src/launchctl.c	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/launchctl.c	2008-12-11 04:42:32 UTC (rev 23760)
@@ -105,6 +105,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);
@@ -158,6 +161,7 @@
 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,
@@ -189,7 +193,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[]);
 
@@ -198,39 +203,43 @@
 	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" },
+	{ "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[])
@@ -241,18 +250,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;
 
@@ -261,6 +262,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;
@@ -269,6 +286,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);
@@ -312,7 +361,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);
@@ -1221,6 +1270,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)
 {
@@ -1484,6 +1676,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);
@@ -2094,39 +2306,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;
 }
 
@@ -2473,6 +2714,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[])
 {
@@ -2699,7 +2958,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]);
@@ -2775,6 +3034,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)
 {
@@ -2929,7 +3216,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;
 		}
@@ -2939,7 +3226,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);
@@ -2958,7 +3245,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
 	{
@@ -2977,10 +3264,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;
@@ -2992,7 +3281,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: trunk/launchd/src/launchd.c
===================================================================
--- trunk/launchd/src/launchd.c	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/launchd.c	2008-12-11 04:42:32 UTC (rev 23760)
@@ -200,9 +200,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 ) {
@@ -213,7 +215,7 @@
 	}
 
 	jobmgr_init(sflag);
-
+	
 	launchd_runtime_init2();
 
 	launchd_runtime();

Modified: trunk/launchd/src/launchd_core_logic.c
===================================================================
--- trunk/launchd/src/launchd_core_logic.c	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/launchd_core_logic.c	2008-12-11 04:42:32 UTC (rev 23760)
@@ -569,6 +569,7 @@
 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);
 
 /* file local globals */
 static size_t total_children;
@@ -652,7 +653,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 +670,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();
 
@@ -1007,13 +1010,14 @@
 		runtime_del_weak_ref();
 		SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle);
 	} else if (pid1_magic) {
+		eliminate_double_reboot();
 		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();
 		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 {
@@ -2567,7 +2571,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);
@@ -3097,6 +3101,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
@@ -4351,7 +4358,7 @@
 			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);
@@ -5364,11 +5371,15 @@
 			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);
+			
+			/* <rdar://problem/6399100> Let the kernel clean up the stragglers. */
+			if( 0 ) {
+				jobmgr_kill_stray_child(jm, p_i);
+			}
 		}
 	}
 
@@ -5536,8 +5547,13 @@
 		envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, 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 = _PATH_CONSOLE;
+		}
 	}
-
+	
 	jm->session_initialized = true;
 
 	return bootstrapper;
@@ -5708,7 +5724,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 +5734,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);
@@ -6350,14 +6366,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);
@@ -6643,7 +6659,7 @@
 		}
 	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);
 			
@@ -7506,19 +7522,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 +7560,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 +7755,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;
 }
 
@@ -8103,3 +8132,57 @@
 	
 	return max;
 }
+
+/* See rdar://problem/6271234 */
+void
+eliminate_double_reboot(void)
+{
+	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;
+		}
+		
+		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!");
+		}
+	}
+}

Modified: trunk/launchd/src/launchd_runtime.c
===================================================================
--- trunk/launchd/src/launchd_runtime.c	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/launchd_runtime.c	2008-12-11 04:42:32 UTC (rev 23760)
@@ -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--;
 }

Modified: trunk/launchd/src/libbootstrap.c
===================================================================
--- trunk/launchd/src/libbootstrap.c	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/libbootstrap.c	2008-12-11 04:42:32 UTC (rev 23760)
@@ -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: trunk/launchd/src/liblaunch.c
===================================================================
--- trunk/launchd/src/liblaunch.c	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/liblaunch.c	2008-12-11 04:42:32 UTC (rev 23760)
@@ -208,16 +208,26 @@
 		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( (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 if( _vprocmgr_getsocket(spath) == 0 ) {
+				/* 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) {
@@ -1265,26 +1275,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: trunk/launchd/src/libvproc.c
===================================================================
--- trunk/launchd/src/libvproc.c	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/libvproc.c	2008-12-11 04:42:32 UTC (rev 23760)
@@ -69,18 +69,39 @@
 	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 = malloc(sizeof(struct vproc_shmem_s));
+		if( !_vm_addr ) {
+			return;
+		}
+		
+		_vproc_log(LOG_WARNING,
+					"Using private memory for transactions. You are likely running under a launchd agent or daemon under a debugger.\n"
+					"Please keep the following considerations in mind.\n"
+					"0. This process is not actually participating in Instant Off. It will only be able to keep track of its transaction count.\n"
+					"1. This process will not die after cleaning up its last transaction after it has received SIGTERM.\n"
+					"2. You are debugging your program in an environment that is very different from the one it will run under.\n"
+					"3. You can use the WaitForDebugger key to stall execution of your daemon or agent so that you can attach to it. See launchd.plist(5). "
+					"This only applies if you are debugging a launchd daemon or agent. If you are debugging a GUI application under Xcode, this consideration does not apply.\n");
+		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);
+	}
 
-	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);
-
 	vproc_shmem = (struct vproc_shmem_s *)vm_addr;
 }
 
@@ -101,8 +122,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 +132,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 +156,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
@@ -174,6 +198,7 @@
 }
 
 void
+#if !TARGET_OS_EMBEDDED
 _vproc_transaction_try_exit(int status)
 {
 	if (unlikely(vproc_shmem == NULL)) {
@@ -185,6 +210,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 +225,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 +252,7 @@
 		}
 		abort();
 	}
+#endif
 }
 
 vproc_standby_t
@@ -414,12 +449,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 +466,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)
 {

Modified: trunk/launchd/src/protocol_vproc.defs
===================================================================
--- trunk/launchd/src/protocol_vproc.defs	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/protocol_vproc.defs	2008-12-11 04:42:32 UTC (rev 23760)
@@ -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(

Modified: trunk/launchd/src/vproc_priv.h
===================================================================
--- trunk/launchd/src/vproc_priv.h	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd/src/vproc_priv.h	2008-12-11 04:42:32 UTC (rev 23760)
@@ -90,6 +90,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: trunk/launchd.xcodeproj/project.pbxproj
===================================================================
--- trunk/launchd.xcodeproj/project.pbxproj	2008-11-26 06:56:06 UTC (rev 23759)
+++ trunk/launchd.xcodeproj/project.pbxproj	2008-12-11 04:42:32 UTC (rev 23760)
@@ -1079,6 +1079,7 @@
 				PREBINDING = NO;
 				PRODUCT_NAME = SystemStarter;
 				STRIP_STYLE = debugging;
+				VALID_ARCHS = "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/20081210/bff1f74d/attachment-0001.html>


More information about the launchd-changes mailing list