[launchd-changes] [23790] trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Jan 29 14:46:14 PST 2009


Revision: 23790
          http://trac.macosforge.org/projects/launchd/changeset/23790
Author:   dsorresso at apple.com
Date:     2009-01-29 14:46:14 -0800 (Thu, 29 Jan 2009)
Log Message:
-----------
<rdar://problem/6317143> Kill stray processes before proceeding to HopefullyExitsLast bucket
<rdar://problem/6443367> CrashTracer: 15 crashes in quicklookd at com.apple.QuickLookDaemon ?\195?\162?\194?\128?\194?\162 -[QLDiskCache _cleanupDirtyLock]
<rdar://problem/6476050> Add support for RunAtLoad=false when KeepAlive=true
<rdar://problem/6485062> 10A245: launchd logging errors about Bug: launchd_core_logic.c:2902
<rdar://problem/6519318> SnowLeopard: Sporadic Failure of XServe Netboot
<rdar://problem/6520584> PID 1 crashes occasionally when logging messages on behalf of per-user launchd
<rdar://problem/6524729> SSH sessions are having their bootstraps taken away
<rdar://problem/6530122> Workaround rdar://problem/5020256
<rdar://problem/6535070> CrashTracer: 3 users reported crashes in launchd at launchd ?\195?\162?\194?\128?\194?\162 ipc_readmsg2 + 652

Modified Paths:
--------------
    trunk/launchd/src/bootstrap_priv.h
    trunk/launchd/src/launch.h
    trunk/launchd/src/launch_priv.h
    trunk/launchd/src/launchctl.c
    trunk/launchd/src/launchd.c
    trunk/launchd/src/launchd.h
    trunk/launchd/src/launchd_core_logic.c
    trunk/launchd/src/launchd_runtime.c
    trunk/launchd/src/launchd_runtime.h
    trunk/launchd/src/launchd_unix_ipc.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_internal.h
    trunk/launchd/src/vproc_priv.h
    trunk/launchd.xcodeproj/project.pbxproj

Modified: trunk/launchd/src/bootstrap_priv.h
===================================================================
--- trunk/launchd/src/bootstrap_priv.h	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/bootstrap_priv.h	2009-01-29 22:46:14 UTC (rev 23790)
@@ -27,13 +27,13 @@
 
 #pragma GCC visibility push(default)
 
-#define BOOTSTRAP_PER_PID_SERVICE	0x1
-#define BOOTSTRAP_ALLOW_LOOKUP		0x2
-#define BOOTSTRAP_DENY_JOB_CREATION	0x4
-#define BOOTSTRAP_PRIVILEGED_SERVER	0x8
+#define BOOTSTRAP_PER_PID_SERVICE		1 << 0
+#define BOOTSTRAP_ALLOW_LOOKUP			1 << 1
+#define BOOTSTRAP_DENY_JOB_CREATION		1 << 2
+#define BOOTSTRAP_PRIVILEGED_SERVER		1 << 3
 
-#define BOOTSTRAP_PROPERTY_SUBSET		1 << 1
-#define BOOTSTRAP_PROPERTY_PERUSER		1 << 2
+#define BOOTSTRAP_PROPERTY_SUBSET		1 << 0
+#define BOOTSTRAP_PROPERTY_PERUSER		1 << 1
 
 kern_return_t bootstrap_register2(mach_port_t bp, name_t service_name, mach_port_t sp, uint64_t flags);
 

Modified: trunk/launchd/src/launch.h
===================================================================
--- trunk/launchd/src/launch.h	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launch.h	2009-01-29 22:46:14 UTC (rev 23790)
@@ -44,108 +44,109 @@
 #endif
 
 
-#define LAUNCH_KEY_SUBMITJOB					"SubmitJob"
-#define LAUNCH_KEY_REMOVEJOB					"RemoveJob"
-#define LAUNCH_KEY_STARTJOB						"StartJob"
-#define LAUNCH_KEY_STOPJOB						"StopJob"
-#define LAUNCH_KEY_GETJOB						"GetJob"
-#define LAUNCH_KEY_GETJOBS						"GetJobs"
-#define LAUNCH_KEY_CHECKIN						"CheckIn"
+#define LAUNCH_KEY_SUBMITJOB						"SubmitJob"
+#define LAUNCH_KEY_REMOVEJOB						"RemoveJob"
+#define LAUNCH_KEY_STARTJOB							"StartJob"
+#define LAUNCH_KEY_STOPJOB							"StopJob"
+#define LAUNCH_KEY_GETJOB							"GetJob"
+#define LAUNCH_KEY_GETJOBS							"GetJobs"
+#define LAUNCH_KEY_CHECKIN							"CheckIn"
 
-#define LAUNCH_JOBKEY_LABEL						"Label"
-#define LAUNCH_JOBKEY_DISABLED					"Disabled"
-#define LAUNCH_JOBKEY_USERNAME					"UserName"
-#define LAUNCH_JOBKEY_GROUPNAME					"GroupName"
-#define LAUNCH_JOBKEY_TIMEOUT					"TimeOut"
-#define LAUNCH_JOBKEY_EXITTIMEOUT				"ExitTimeOut"
-#define LAUNCH_JOBKEY_INITGROUPS				"InitGroups"
-#define LAUNCH_JOBKEY_SOCKETS					"Sockets"
-#define LAUNCH_JOBKEY_MACHSERVICES				"MachServices"
-#define LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES	"MachServiceLookupPolicies"
-#define LAUNCH_JOBKEY_INETDCOMPATIBILITY		"inetdCompatibility"
-#define LAUNCH_JOBKEY_ENABLEGLOBBING			"EnableGlobbing"
-#define LAUNCH_JOBKEY_PROGRAMARGUMENTS			"ProgramArguments"
-#define LAUNCH_JOBKEY_PROGRAM					"Program"
-#define LAUNCH_JOBKEY_ONDEMAND					"OnDemand"
-#define LAUNCH_JOBKEY_KEEPALIVE					"KeepAlive"
-#define LAUNCH_JOBKEY_LIMITLOADTOHOSTS			"LimitLoadToHosts"
-#define LAUNCH_JOBKEY_LIMITLOADFROMHOSTS		"LimitLoadFromHosts"
-#define LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE	"LimitLoadToSessionType"
-#define LAUNCH_JOBKEY_RUNATLOAD					"RunAtLoad"
-#define LAUNCH_JOBKEY_ROOTDIRECTORY				"RootDirectory"
-#define LAUNCH_JOBKEY_WORKINGDIRECTORY			"WorkingDirectory"
-#define LAUNCH_JOBKEY_ENVIRONMENTVARIABLES		"EnvironmentVariables"
-#define LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES	"UserEnvironmentVariables"
-#define LAUNCH_JOBKEY_UMASK						"Umask"
-#define LAUNCH_JOBKEY_NICE						"Nice"
-#define LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST  		"HopefullyExitsFirst"
-#define LAUNCH_JOBKEY_HOPEFULLYEXITSLAST   		"HopefullyExitsLast"
-#define LAUNCH_JOBKEY_LOWPRIORITYIO				"LowPriorityIO"
-#define LAUNCH_JOBKEY_SESSIONCREATE				"SessionCreate"
-#define LAUNCH_JOBKEY_STARTONMOUNT				"StartOnMount"
-#define LAUNCH_JOBKEY_SOFTRESOURCELIMITS		"SoftResourceLimits"
-#define LAUNCH_JOBKEY_HARDRESOURCELIMITS		"HardResourceLimits"
-#define LAUNCH_JOBKEY_STANDARDINPATH			"StandardInPath"
-#define LAUNCH_JOBKEY_STANDARDOUTPATH			"StandardOutPath"
-#define LAUNCH_JOBKEY_STANDARDERRORPATH			"StandardErrorPath"
-#define LAUNCH_JOBKEY_DEBUG						"Debug"
-#define LAUNCH_JOBKEY_WAITFORDEBUGGER			"WaitForDebugger"
-#define LAUNCH_JOBKEY_QUEUEDIRECTORIES			"QueueDirectories"
-#define LAUNCH_JOBKEY_WATCHPATHS				"WatchPaths"
-#define LAUNCH_JOBKEY_STARTINTERVAL				"StartInterval"
-#define LAUNCH_JOBKEY_STARTCALENDARINTERVAL		"StartCalendarInterval"
-#define LAUNCH_JOBKEY_BONJOURFDS				"BonjourFDs"
-#define LAUNCH_JOBKEY_LASTEXITSTATUS			"LastExitStatus"
-#define LAUNCH_JOBKEY_PID						"PID"
-#define LAUNCH_JOBKEY_THROTTLEINTERVAL			"ThrottleInterval"
-#define LAUNCH_JOBKEY_LAUNCHONLYONCE			"LaunchOnlyOnce"
-#define LAUNCH_JOBKEY_ABANDONPROCESSGROUP		"AbandonProcessGroup"
-#define LAUNCH_JOBKEY_POLICIES					"Policies"
-#define LAUNCH_JOBKEY_ENABLETRANSACTIONS		"EnableTransactions"
+#define LAUNCH_JOBKEY_LABEL							"Label"
+#define LAUNCH_JOBKEY_DISABLED						"Disabled"
+#define LAUNCH_JOBKEY_USERNAME						"UserName"
+#define LAUNCH_JOBKEY_GROUPNAME						"GroupName"
+#define LAUNCH_JOBKEY_TIMEOUT						"TimeOut"
+#define LAUNCH_JOBKEY_EXITTIMEOUT					"ExitTimeOut"
+#define LAUNCH_JOBKEY_INITGROUPS					"InitGroups"
+#define LAUNCH_JOBKEY_SOCKETS						"Sockets"
+#define LAUNCH_JOBKEY_MACHSERVICES					"MachServices"
+#define LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES		"MachServiceLookupPolicies"
+#define LAUNCH_JOBKEY_INETDCOMPATIBILITY			"inetdCompatibility"
+#define LAUNCH_JOBKEY_ENABLEGLOBBING				"EnableGlobbing"
+#define LAUNCH_JOBKEY_PROGRAMARGUMENTS				"ProgramArguments"
+#define LAUNCH_JOBKEY_PROGRAM						"Program"
+#define LAUNCH_JOBKEY_ONDEMAND						"OnDemand"
+#define LAUNCH_JOBKEY_KEEPALIVE						"KeepAlive"
+#define LAUNCH_JOBKEY_LIMITLOADTOHOSTS				"LimitLoadToHosts"
+#define LAUNCH_JOBKEY_LIMITLOADFROMHOSTS			"LimitLoadFromHosts"
+#define LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE		"LimitLoadToSessionType"
+#define LAUNCH_JOBKEY_RUNATLOAD						"RunAtLoad"
+#define LAUNCH_JOBKEY_ROOTDIRECTORY					"RootDirectory"
+#define LAUNCH_JOBKEY_WORKINGDIRECTORY				"WorkingDirectory"
+#define LAUNCH_JOBKEY_ENVIRONMENTVARIABLES			"EnvironmentVariables"
+#define LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES		"UserEnvironmentVariables"
+#define LAUNCH_JOBKEY_UMASK							"Umask"
+#define LAUNCH_JOBKEY_NICE							"Nice"
+#define LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST	  		"HopefullyExitsFirst"
+#define LAUNCH_JOBKEY_HOPEFULLYEXITSLAST   			"HopefullyExitsLast"
+#define LAUNCH_JOBKEY_LOWPRIORITYIO					"LowPriorityIO"
+#define LAUNCH_JOBKEY_SESSIONCREATE					"SessionCreate"
+#define LAUNCH_JOBKEY_STARTONMOUNT					"StartOnMount"
+#define LAUNCH_JOBKEY_SOFTRESOURCELIMITS			"SoftResourceLimits"
+#define LAUNCH_JOBKEY_HARDRESOURCELIMITS			"HardResourceLimits"
+#define LAUNCH_JOBKEY_STANDARDINPATH				"StandardInPath"
+#define LAUNCH_JOBKEY_STANDARDOUTPATH				"StandardOutPath"
+#define LAUNCH_JOBKEY_STANDARDERRORPATH				"StandardErrorPath"
+#define LAUNCH_JOBKEY_DEBUG							"Debug"
+#define LAUNCH_JOBKEY_WAITFORDEBUGGER				"WaitForDebugger"
+#define LAUNCH_JOBKEY_QUEUEDIRECTORIES				"QueueDirectories"
+#define LAUNCH_JOBKEY_WATCHPATHS					"WatchPaths"
+#define LAUNCH_JOBKEY_STARTINTERVAL					"StartInterval"
+#define LAUNCH_JOBKEY_STARTCALENDARINTERVAL			"StartCalendarInterval"
+#define LAUNCH_JOBKEY_BONJOURFDS					"BonjourFDs"
+#define LAUNCH_JOBKEY_LASTEXITSTATUS				"LastExitStatus"
+#define LAUNCH_JOBKEY_PID							"PID"
+#define LAUNCH_JOBKEY_THROTTLEINTERVAL				"ThrottleInterval"
+#define LAUNCH_JOBKEY_LAUNCHONLYONCE				"LaunchOnlyOnce"
+#define LAUNCH_JOBKEY_ABANDONPROCESSGROUP			"AbandonProcessGroup"
+#define LAUNCH_JOBKEY_POLICIES						"Policies"
+#define LAUNCH_JOBKEY_ENABLETRANSACTIONS			"EnableTransactions"
 
-#define LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS	"DenyCreatingOtherJobs"
+#define LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS		"DenyCreatingOtherJobs"
 
-#define LAUNCH_JOBINETDCOMPATIBILITY_WAIT		"Wait"
+#define LAUNCH_JOBINETDCOMPATIBILITY_WAIT			"Wait"
 
-#define LAUNCH_JOBKEY_MACH_RESETATCLOSE			"ResetAtClose"
-#define LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN		"HideUntilCheckIn"
+#define LAUNCH_JOBKEY_MACH_RESETATCLOSE				"ResetAtClose"
+#define LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN			"HideUntilCheckIn"
 
-#define LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT	"SuccessfulExit"
-#define LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE	"NetworkState"
-#define LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE		"PathState"
-#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE	"OtherJobActive"
-#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED	"OtherJobEnabled"
+#define LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT		"SuccessfulExit"
+#define LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE		"NetworkState"
+#define LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE			"PathState"
+#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE		"OtherJobActive"
+#define LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED		"OtherJobEnabled"
+#define LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND	"AfterInitialDemand"
 
-#define LAUNCH_JOBKEY_CAL_MINUTE				"Minute"
-#define LAUNCH_JOBKEY_CAL_HOUR					"Hour"
-#define LAUNCH_JOBKEY_CAL_DAY					"Day"
-#define LAUNCH_JOBKEY_CAL_WEEKDAY				"Weekday"
-#define LAUNCH_JOBKEY_CAL_MONTH					"Month"
+#define LAUNCH_JOBKEY_CAL_MINUTE					"Minute"
+#define LAUNCH_JOBKEY_CAL_HOUR						"Hour"
+#define LAUNCH_JOBKEY_CAL_DAY						"Day"
+#define LAUNCH_JOBKEY_CAL_WEEKDAY					"Weekday"
+#define LAUNCH_JOBKEY_CAL_MONTH						"Month"
                                             
-#define LAUNCH_JOBKEY_RESOURCELIMIT_CORE		"Core"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_CPU			"CPU"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_DATA		"Data"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE		"FileSize"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK		"MemoryLock"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE		"NumberOfFiles"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_NPROC		"NumberOfProcesses"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_RSS			"ResidentSetSize"
-#define LAUNCH_JOBKEY_RESOURCELIMIT_STACK		"Stack"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_CORE			"Core"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_CPU				"CPU"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_DATA			"Data"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE			"FileSize"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK			"MemoryLock"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE			"NumberOfFiles"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_NPROC			"NumberOfProcesses"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_RSS				"ResidentSetSize"
+#define LAUNCH_JOBKEY_RESOURCELIMIT_STACK			"Stack"
 
-#define LAUNCH_JOBKEY_DISABLED_MACHINETYPE		"MachineType"
-#define LAUNCH_JOBKEY_DISABLED_MODELNAME		"ModelName"
+#define LAUNCH_JOBKEY_DISABLED_MACHINETYPE			"MachineType"
+#define LAUNCH_JOBKEY_DISABLED_MODELNAME			"ModelName"
 
-#define LAUNCH_JOBSOCKETKEY_TYPE				"SockType"
-#define LAUNCH_JOBSOCKETKEY_PASSIVE				"SockPassive"
-#define LAUNCH_JOBSOCKETKEY_BONJOUR				"Bonjour"
-#define LAUNCH_JOBSOCKETKEY_SECUREWITHKEY		"SecureSocketWithKey"
-#define LAUNCH_JOBSOCKETKEY_PATHNAME			"SockPathName"
-#define LAUNCH_JOBSOCKETKEY_PATHMODE			"SockPathMode"
-#define LAUNCH_JOBSOCKETKEY_NODENAME			"SockNodeName"
-#define LAUNCH_JOBSOCKETKEY_SERVICENAME			"SockServiceName"
-#define LAUNCH_JOBSOCKETKEY_FAMILY				"SockFamily"
-#define LAUNCH_JOBSOCKETKEY_PROTOCOL			"SockProtocol"
-#define LAUNCH_JOBSOCKETKEY_MULTICASTGROUP		"MulticastGroup"
+#define LAUNCH_JOBSOCKETKEY_TYPE					"SockType"
+#define LAUNCH_JOBSOCKETKEY_PASSIVE					"SockPassive"
+#define LAUNCH_JOBSOCKETKEY_BONJOUR					"Bonjour"
+#define LAUNCH_JOBSOCKETKEY_SECUREWITHKEY			"SecureSocketWithKey"
+#define LAUNCH_JOBSOCKETKEY_PATHNAME				"SockPathName"
+#define LAUNCH_JOBSOCKETKEY_PATHMODE				"SockPathMode"
+#define LAUNCH_JOBSOCKETKEY_NODENAME				"SockNodeName"
+#define LAUNCH_JOBSOCKETKEY_SERVICENAME				"SockServiceName"
+#define LAUNCH_JOBSOCKETKEY_FAMILY					"SockFamily"
+#define LAUNCH_JOBSOCKETKEY_PROTOCOL				"SockProtocol"
+#define LAUNCH_JOBSOCKETKEY_MULTICASTGROUP			"MulticastGroup"
 
 typedef struct _launch_data *launch_data_t;
 

Modified: trunk/launchd/src/launch_priv.h
===================================================================
--- trunk/launchd/src/launch_priv.h	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launch_priv.h	2009-01-29 22:46:14 UTC (rev 23790)
@@ -90,7 +90,8 @@
  *
  * This returns 1 on success (it used to return otherwise), and -1 on failure.
  */
-#define	LOAD_ONLY_SAFEMODE_LAUNCHAGENTS	1
+#define	LOAD_ONLY_SAFEMODE_LAUNCHAGENTS	1 << 0
+#define LAUNCH_GLOBAL_ON_DEMAND			1 << 1
 pid_t create_and_switch_to_per_session_launchd(const char * /* loginname */, int flags, ...);
 
 /* Also for LoginWindow.

Modified: trunk/launchd/src/launchctl.c
===================================================================
--- trunk/launchd/src/launchctl.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launchctl.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -164,6 +164,7 @@
 static void fix_bogus_file_metadata(void);
 static void do_file_init(void) __attribute__((constructor));
 static void setup_system_context(void);
+static void tell_launchd_about_boot_args(void);
 
 typedef enum {
 	BOOTCACHE_START = 1,
@@ -192,12 +193,13 @@
 static int umask_cmd(int argc, char *const argv[]);
 static int getrusage_cmd(int argc, char *const argv[]);
 static int bsexec_cmd(int argc, char *const argv[]);
-static int _bslist_cmd(mach_port_t bport, unsigned int depth);
+static int _bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job);
 static int bslist_cmd(int argc, char *const argv[]);
-static int _bstree_cmd(mach_port_t bsport, unsigned int depth);
+static int _bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs);
 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 managername_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,36 +208,37 @@
 	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" },
-	{ "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" },
+	{ "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." },
+	{ "managername",	managername_cmd,		"Print the name of 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;
@@ -244,6 +247,7 @@
 static bool do_apple_internal_magic;
 static bool system_context;
 static bool rootuser_context;
+static bool g_shutdown_debugging = false;
 
 int
 main(int argc, char *const argv[])
@@ -458,7 +462,16 @@
 	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"));
+	char plist_path_str[PATH_MAX];
+	plist_path_str[PATH_MAX - 1] = 0;
+	snprintf(plist_path_str, sizeof(plist_path_str), "%s/.MacOSX/environment.plist", getenv("HOME"));
+	
+	struct stat sb;
+	if( stat(plist_path_str, &sb) == -1 ) {
+		goto out;
+	}
+	
+	plistPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), plist_path_str);
 	if( !assumes(plistPath != NULL) ) {
 		goto out;
 	}
@@ -1367,10 +1380,18 @@
 	if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
 		fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
 	}
+	
 	propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString);
 	if (!propertyList) {
 		fprintf(stderr, "%s: propertyList is NULL\n", getprogname());
 	}
+	if( fileURL ) {
+		CFRelease(fileURL);
+	}
+	
+	if( resourceData ) {
+		CFRelease(resourceData);
+	}
 
 	return propertyList;
 }
@@ -1393,6 +1414,10 @@
 	if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode)) {
 		fprintf(stderr, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
 	}
+	
+	if( resourceData ) {
+		CFRelease(resourceData);
+	}
 }
 
 static inline Boolean __is_launch_data_t(launch_data_t obj) 
@@ -1514,10 +1539,11 @@
 		}
 		
 		result = CFArrayCreateCopy(NULL, mutResult);
-		
+	}
+	
+	if( mutResult ) {
 		CFRelease(mutResult);
 	}
-	
 	return result;
 }
 
@@ -1783,6 +1809,7 @@
 		assumes(fwexec(rcserver_tool, NULL) != -1);
 	}
 
+	tell_launchd_about_boot_args();
 	read_launchd_conf();
 
 	if (path_check("/var/account/acct")) {
@@ -2010,6 +2037,8 @@
 				 * directory.
 				 */
 				if( getppid() != 1 ) {
+					the_argc_user = 5;
+				}
 			#else
 				/* This deadlocks against mount_url when logging in with a network home
 				 * directory. For now, we'll just load user Background agents when
@@ -2018,9 +2047,9 @@
 				 * but it satisfies the user expectation in 99% of cases.
 				 */
 				if( 0 ) {
-			#endif
 					the_argc_user = 5;
 				}
+			#endif
 			}
 		} else if (strcasecmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
 			load_launchd_items[5] = "/etc/mach_init_per_user.d";
@@ -2490,18 +2519,19 @@
 				
 				if( plistStr ) {
 					CFShow(plistStr);
+					CFRelease(plistStr);
 					r = 0;
 				}
 			} else {
 				print_obj(resp, NULL, NULL);
 				r = 0;
 			}
+			launch_data_free(resp);
 		} else {
 			fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
 			r = 1;
+			launch_data_free(resp);
 		}
-		
-		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);
@@ -2874,6 +2904,43 @@
 	bootstrap_port = rootbs;
 }
 
+static void
+tell_launchd_about_boot_args(void)
+{
+	if( !g_shutdown_debugging ) {
+		return;
+	}
+	
+	CFTypeRef value = NULL;	
+	do {
+		io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options");
+		if( assumes(entry == IO_OBJECT_NULL) ) {
+			break;
+		}
+		
+		value = IORegistryEntryCreateCFProperty(entry, CFSTR("boot-args"), kCFAllocatorDefault, 0);
+		if( !assumes(value != NULL) ) {
+			break;
+		}
+		
+		IOObjectRelease(entry);
+	} while( 0 );
+	
+	Boolean is_verbose = false;
+	if( value ) {
+		/* Normally I'd just use CFStringFind(), but the compiler whines about it returning a 
+		 * struct with -Wall.
+		 */
+		CFRange range = { 0, CFStringGetLength(value) };
+		CFRange found_range = { 0, 0 };
+		
+		is_verbose = CFStringFindWithOptions(value, CFSTR("-v"), range, 0, &found_range);
+		CFRelease(value);
+				
+		assumes(vproc_swap_integer(NULL, VPROC_GSK_SHUTDOWN_DEBUGGING, (int64_t *)&is_verbose, NULL) == KERN_SUCCESS);
+	}
+}
+
 int
 submit_cmd(int argc, char *const argv[])
 {
@@ -3081,11 +3148,12 @@
 }
 
 int
-_bslist_cmd(mach_port_t bport, unsigned int depth)
+_bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job)
 {
 	kern_return_t result;
 	name_array_t service_names;
-	mach_msg_type_number_t service_cnt, service_active_cnt;
+	name_array_t service_jobs;
+	mach_msg_type_number_t service_cnt, service_jobs_cnt, service_active_cnt;
 	bootstrap_status_array_t service_actives;
 	unsigned int i;
 	
@@ -3094,7 +3162,7 @@
 		return 1;
 	}
 	
-	result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt);
+	result = bootstrap_info(bport, &service_names, &service_cnt, &service_jobs, &service_jobs_cnt, &service_actives, &service_active_cnt);
 	if (result != BOOTSTRAP_SUCCESS) {
 		fprintf(stderr, "bootstrap_info(): %d\n", result);
 		return 1;
@@ -3103,7 +3171,11 @@
 #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]);
+		fprintf(stdout, "%*s%-3s%s", depth, "", bport_state((service_actives[i])), service_names[i]);
+		if( show_job ) {
+			fprintf(stdout, " (%s)", service_jobs[i]);
+		}
+		fprintf(stdout, "\n");
 	}
 	
 	return 0;
@@ -3113,20 +3185,29 @@
 bslist_cmd(int argc, char *const argv[])
 {
 	mach_port_t bport = bootstrap_port;
-	if( argc == 2 ) {
-		bport = str2bsport(argv[1]);
+	bool show_jobs = false;
+	if( argc > 2 && strcmp(argv[2], "-j") == 0 ) {
+		show_jobs = true;
 	}
 	
+	if( argc > 1 ) {
+		if( show_jobs ) {
+			bport = str2bsport(argv[1]);
+		} else if( strcmp(argv[1], "-j") == 0 ) {
+			show_jobs = true;
+		}
+	}
+	
 	if( bport == MACH_PORT_NULL ) {
 		fprintf(stderr, "Invalid bootstrap port\n");
 		return 1;
 	}
 	
-	return _bslist_cmd(bport, 0);
+	return _bslist_cmd(bport, 0, show_jobs);
 }
 
 int
-_bstree_cmd(mach_port_t bsport, unsigned int depth)
+_bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs)
 {
 	if( bsport == MACH_PORT_NULL ) {
 		fprintf(stderr, "No root port!\n");
@@ -3150,13 +3231,13 @@
 	}
 	
 	unsigned int i = 0;
-	_bslist_cmd(bsport, depth);
+	_bslist_cmd(bsport, depth, show_jobs);
 	
 	for( i = 0; i < cnt; i++ ) {
 		char *type = ( child_props[i] & BOOTSTRAP_PROPERTY_PERUSER ) ? "Per-user" : "Subset";
 		fprintf(stdout, "%*s%s (%s)/\n", depth, "", child_names[i], type);
 		if( child_ports[i] != MACH_PORT_NULL ) {
-			_bstree_cmd(child_ports[i], depth + 4);
+			_bstree_cmd(child_ports[i], depth + 4, show_jobs);
 		}
 	}
 	
@@ -3164,16 +3245,20 @@
 }
 
 int
-bstree_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
+bstree_cmd(int argc, char * const argv[])
 {
+	bool show_jobs = false;
 	if( geteuid() != 0 ) {
 		fprintf(stderr, "You must be root to perform this operation.\n");
 		return 1;
 	} else {
+		if( argc == 2 && strcmp(argv[1], "-j") == 0 ) {
+			show_jobs = true;
+		}
 		fprintf(stdout, "System/\n");
 	}
 	
-	return _bstree_cmd(str2bsport("/"), 4);
+	return _bstree_cmd(str2bsport("/"), 4, show_jobs);
 }
 
 int
@@ -3204,6 +3289,22 @@
 	return 0;
 }
 
+int
+managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
+{
+	char *manager_name = NULL;
+	vproc_err_t verr = vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &manager_name);
+	if( verr ) {
+		fprintf(stdout, "Unknown job manager!\n");
+		return 1;
+	}
+	
+	fprintf(stdout, "%s\n", manager_name);
+	free(manager_name);
+	
+	return 0;
+}
+
 bool
 is_legacy_mach_job(launch_data_t obj)
 {
@@ -3316,10 +3417,13 @@
 	int wstatus2;
 	pid_t p;
 
-	errno = posix_spawnp(&p, argv[0], NULL, NULL, (char **)argv, environ);
+	/* We'd use posix_spawnp(), but we want to workaround: 6288899 */
 
-	if (errno) {
+	if ((p = vfork()) == -1) {
 		return -1;
+	} else if (p == 0) {
+		execvp(argv[0], (char *const *)argv);
+		_exit(EXIT_FAILURE);
 	}
 
 	if (waitpid(p, wstatus ? wstatus : &wstatus2, 0) == -1) {
@@ -3798,4 +3902,8 @@
 	if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) {
 		do_apple_internal_magic = true;
 	}
+	
+	if( stat("/var/db/.launchd_shutdown_debugging", &sb) == 0 ) {
+		g_shutdown_debugging = true;
+	}
 }

Modified: trunk/launchd/src/launchd.c
===================================================================
--- trunk/launchd/src/launchd.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launchd.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -69,6 +69,7 @@
 #include <spawn.h>
 #include <sched.h>
 #include <pthread.h>
+#include <util.h>
 
 #include "bootstrap.h"
 #include "vproc.h"
@@ -96,6 +97,9 @@
 static void monitor_networking_state(void);
 static void fatal_signal_handler(int sig, siginfo_t *si, void *uap);
 static void handle_pid1_crashes_separately(void);
+static void do_pid1_crash_diagnosis_mode(void);
+static int basic_fork(void);
+static bool do_pid1_crash_diagnosis_mode2(void);
 
 static void *update_thread(void *nothing);
 
@@ -107,7 +111,7 @@
 bool shutdown_in_progress;
 bool fake_shutdown_in_progress;
 bool network_up;
-char g_username[128] = "__UnknownUserToLaunchd_DontPanic_NotImportant__";
+char g_username[128] = "__Uninitialized__";
 FILE *g_console = NULL;
 
 int
@@ -121,8 +125,7 @@
 	testfd_or_openfd(STDOUT_FILENO, stdouterr_path, O_WRONLY);
 	testfd_or_openfd(STDERR_FILENO, stdouterr_path, O_WRONLY);
 
-#if 0
-	if (pid1_magic) {
+	if (pid1_magic && g_use_gmalloc) {
 		if (!getenv("DYLD_INSERT_LIBRARIES")) {
 			setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1);
 			setenv("MALLOC_STRICT_SIZE", "1", 1);
@@ -132,7 +135,6 @@
 			unsetenv("MALLOC_STRICT_SIZE");
 		}
 	}
-#endif
 
 	while ((ch = getopt(argc, argv, "s")) != -1) {
 		switch (ch) {
@@ -157,9 +159,9 @@
 		} else {
 			if( !launchd_assumes((g_console = fopen(_PATH_CONSOLE, "w")) != NULL) ) {
 				g_console = stdout;
+			} else {
+				_fd(fileno(g_console));
 			}
-			
-			_fd(fileno(g_console));
 		}
 	} else {
 		g_console = stdout;
@@ -186,6 +188,9 @@
 
 	if( pid1_magic ) {
 		runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[1] has started up. ***");
+		if( g_use_gmalloc ) {
+			runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Using libgmalloc ***");
+		}
 		
 		struct stat sb;
 		if( stat("/var/db/.launchd_flat_per_user_namespace", &sb) == 0 ) {
@@ -254,6 +259,85 @@
 static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot;
 
 void
+do_pid1_crash_diagnosis_mode(void)
+{
+	while( g_shutdown_debugging && !do_pid1_crash_diagnosis_mode2() ) {
+		sleep(1);
+	}
+}
+
+int
+basic_fork(void)
+{
+	int wstatus;
+	pid_t p;
+	
+	switch ((p = fork())) {
+		case -1:
+			runtime_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m");
+			return p;
+		case 0:
+			return p;
+		default:
+			waitpid(p, &wstatus, 0);
+			if (WIFEXITED(wstatus)) {
+				if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
+					return 1;
+				} else {
+					fprintf(stdout, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus));
+				}
+			} else {
+				fprintf(stdout, "PID 1 copy: %s\n", strsignal(WTERMSIG(wstatus)));
+			}
+			return 1;
+	}
+	
+	return -1;
+}
+
+bool
+do_pid1_crash_diagnosis_mode2(void)
+{
+	if( basic_fork() == 0 ) {
+		/* Neuter our bootstrap port so that the shell doesn't try talking to us while
+		 * we're blocked waiting on it.
+		 */
+		fflush(g_console);
+		task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL);
+		if( basic_fork() != 0 ) {
+			fflush(g_console);
+			return true;
+		}
+	} else {
+		return true;
+	}
+	
+	int fd;
+	revoke(_PATH_CONSOLE);
+	if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) {
+		_exit(2);
+	}
+	if (login_tty(fd) == -1) {
+		_exit(3);
+	}
+	setenv("TERM", "vt100", 1);
+	fprintf(stdout, "\n");
+	fprintf(stdout, "Entering launchd PID 1 debugging mode...\n");
+	fprintf(stdout, "The PID 1 launchd has crashed. It has fork(2)ed itself for debugging.\n");
+	fprintf(stdout, "To debug the main thread of PID 1:\n");
+	fprintf(stdout, "    gdb attach %d\n", getppid());
+	fprintf(stdout, "To exit this shell, capture a sample of launchd and shut down:\n");
+	fprintf(stdout, "    exit\n");
+	fprintf(stdout, "A sample of PID 1 will be written to %s\n", PID1_CRASH_LOGFILE);
+	fprintf(stdout, "\n");
+	fflush(stdout);
+	
+	execl(_PATH_BSHELL, "-sh", NULL);
+	syslog(LOG_ERR, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL);
+	_exit(EXIT_FAILURE);
+}
+
+void
 fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
 {
 	const char *doom_why = "at instruction";
@@ -264,6 +348,8 @@
 	crash_addr = si->si_addr;
 	crash_pid = si->si_pid;
 
+	do_pid1_crash_diagnosis_mode();
+	
 	unlink(PID1_CRASH_LOGFILE);
 
 	switch ((sample_p = vfork())) {
@@ -339,7 +425,7 @@
 	now = runtime_get_wall_time();
 
 	char *term_who = pid1_magic ? "System shutdown" : "Per-user launchd termination for ";
-	runtime_syslog(LOG_NOTICE, "%s%s began at: %lld.%06llu", term_who, pid1_magic ? "" : g_username, now / USEC_PER_SEC, now % USEC_PER_SEC);
+	runtime_syslog(LOG_NOTICE, "%s%s began", term_who, pid1_magic ? "" : g_username);
 
 	launchd_assert(jobmgr_shutdown(root_jobmgr) != NULL);
 }

Modified: trunk/launchd/src/launchd.h
===================================================================
--- trunk/launchd/src/launchd.h	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launchd.h	2009-01-29 22:46:14 UTC (rev 23790)
@@ -33,6 +33,7 @@
 extern bool fake_shutdown_in_progress;
 extern bool network_up;
 extern bool g_force_old_kill_path;
+extern bool g_simulate_pid1_crash;
 extern FILE *g_console;
 
 bool init_check_pid(pid_t);

Modified: trunk/launchd/src/launchd_core_logic.c
===================================================================
--- trunk/launchd/src/launchd_core_logic.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launchd_core_logic.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -481,7 +481,7 @@
 		    hopefully_exits_last		:1,	/* man launchd.plist --> HopefullyExitsLast */
 		    removal_pending				:1,	/* a job was asked to be unloaded/removed while running, we'll remove it after it exits */
 		    sent_sigkill				:1,	/* job_kill() was called */
-		    sampled						:1,	/* job_force_sampletool() was called (or is disabled) */
+		    sampling_complete			:1,	/* job_force_sampletool() was called (or is disabled) */
 		    debug_before_kill			:1,	/* enter the kernel debugger before killing a job */
 		    weird_bootstrap				:1,	/* a hack that launchd+launchctl use during jobmgr_t creation */
 		    start_on_mount				:1,	/* man launchd.plist --> StartOnMount */
@@ -496,14 +496,18 @@
 	     	sent_kill_via_shmem			:1,	/* We need to 'kill_via_shmem' once-and-only-once */
 			pending_sample				:1, /* This job needs to be sampled for some reason. */
 			kill_after_sample			:1, /* The job is to be killed after sampling. */
-			reap_after_sample			:1,	/* The job exited before sample did, so we should reap it after sample is done. */
+			is_being_sampled			:1, /* We've spawned a sample tool to sample the job. */
+			reap_after_trace			: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. */
 			stopped						:1, /* job_stop() was called. */
-			jetsam_frontmost			:1; /* The job is considered "frontmost" by Jetsam. */
+			jetsam_frontmost			:1, /* The job is considered "frontmost" by Jetsam. */
+			needs_kickoff				:1, /* The job is to be kept alive continuously, but it must be initially kicked off. */
+			is_bootstrapper				:1, /* The job is a bootstrapper. */
+			migratory					:1; /* The (anonymous) job called vprocmgr_switch_to_session(). */
 	mode_t mask;
-	pid_t sample_pid;
+	pid_t tracing_pid;
 	const char label[0];
 };
 
@@ -528,7 +532,7 @@
 static const char *job_active(job_t j);
 static void job_watch(job_t j);
 static void job_ignore(job_t j);
-static void job_reap_sample(job_t j);
+static void job_cleanup_after_tracer(job_t j);
 static void job_reap(job_t j);
 static bool job_useless(job_t j);
 static bool job_keepalive(job_t j);
@@ -593,6 +597,8 @@
 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 simulate_pid1_crash(void);
+
 void eliminate_double_reboot(void);
 
 /* For Jetsam. */
@@ -611,6 +617,7 @@
 /* process wide globals */
 mach_port_t inherited_bootstrap_port;
 jobmgr_t root_jobmgr;
+bool g_shutdown_debugging = false;
 
 void
 job_ignore(job_t j)
@@ -721,7 +728,7 @@
 		job_assumes(j, runtime_kill(j->p, SIGTERM) != -1);
 
 		if (timeout) {
-			j->sampled = !do_sample;
+			j->sampling_complete = !do_sample;
 			job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER,
 						EV_ADD|EV_ONESHOT, NOTE_SECONDS, timeout, j) != -1);
 		}
@@ -2504,6 +2511,9 @@
 
 	if (j->anonymous) {
 		total_anon_children--;
+		if( j->migratory ) {
+			runtime_del_ref();
+		}
 	} else {
 		runtime_del_ref();
 		total_children--;
@@ -2517,7 +2527,7 @@
 	}
 
 	if( j->pending_sample ) {
-		job_log(j, LOG_NOTICE | LOG_CONSOLE, "Job exited before we could sample it.");
+		job_log(j, LOG_DEBUG | LOG_CONSOLE, "Job exited before we could sample it.");
 		STAILQ_REMOVE(&j->mgr->pending_samples, j, job_s, pending_samples_sle);
 		j->pending_sample = false;
 	}
@@ -2605,7 +2615,7 @@
 	j->last_exit_status = status;
 	j->sent_signal_time = 0;
 	j->sent_sigkill = false;
-	j->sampled = false;
+	j->sampling_complete = false;
 	j->sent_kill_via_shmem = false;
 	j->lastlookup = NULL;
 	j->lastlookup_gennum = 0;
@@ -2655,12 +2665,12 @@
 	
 	/* Dequeue the next in line. */
 	job_t j = STAILQ_FIRST(&jm->pending_samples);
-	if( j->sample_pid ) {
+	if( j->is_being_sampled ) {
 		job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sampling is in progress. Not dequeuing next job.");
 		return;
 	}
 
-	if (j->sampled || j->per_user) {
+	if (j->sampling_complete || j->per_user) {
 		return;
 	}
 
@@ -2700,7 +2710,7 @@
 			STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
 			jobmgr_dequeue_next_sample(jm);
 		} else {
-			j->sample_pid = sp;
+			j->tracing_pid = sp;
 
 			/* Let us know when sample is done. ONESHOT is implicit if we're just interested in NOTE_EXIT. */
 			job_assumes(j, kevent_mod(sp, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j) != -1);
@@ -2740,7 +2750,8 @@
 			/* Let us know when sample is done. ONESHOT is implicit if we're just interested in NOTE_EXIT. */
 			if( job_assumes(j, (r = kevent_mod(sp, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j)) != -1) ) {
 				if( job_assumes(j, write(execpair[0], &sp, sizeof(sp)) == sizeof(sp)) ) {
-					j->sample_pid = sp;
+					j->tracing_pid = sp;
+					j->is_being_sampled = true;
 				} else {
 					job_assumes(j, kevent_mod(sp, EVFILT_PROC, EV_DELETE, 0, 0, NULL) != -1);
 					job_assumes(j, runtime_kill(sp, SIGKILL) != -1);
@@ -2767,7 +2778,7 @@
 		if( r == -1 ) {
 			job_log(j, LOG_ERR | LOG_CONSOLE, "Sampling for job failed!");
 			STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
-			j->sampled = true;
+			j->sampling_complete = true;
 			jobmgr_dequeue_next_sample(jm);
 		} else {
 			job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sampling job (sample PID: %i, file: %s).", sp, j->mgr->sample_log_file);
@@ -2775,7 +2786,7 @@
 	#endif
 	} else {
 		STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
-		j->sampled = true;
+		j->sampling_complete = true;
 	}
 	
 	j->pending_sample = false;
@@ -2814,7 +2825,8 @@
 		if (job_useless(j)) {
 			job_remove(j, false);
 			return NULL;
-		} else if (kickstart || job_keepalive(j)) {
+		} 
+		if (kickstart || job_keepalive(j)) {
 			job_log(j, LOG_DEBUG, "Starting job (kickstart = %s)", kickstart ? "true" : "false");
 			job_start(j);
 		} else {
@@ -2867,8 +2879,10 @@
 	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 && errno != EAGAIN)) {
-		close_log_redir = true;
+	} else if (rsz == -1) {
+		if( !job_assumes(j, errno == EAGAIN) ) {
+			close_log_redir = true;
+		}
 	} else {
 		buf[rsz] = '\0';
 
@@ -2950,42 +2964,44 @@
 }
 
 void
-job_reap_sample(job_t j)
+job_cleanup_after_tracer(job_t j)
 {
-	int wstatus = 0;
-	
-	if (!job_assumes(j, waitpid(j->sample_pid, &wstatus, 0) != -1)) {
-		goto out;
-	}
-
-	job_assumes(j, WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0);
-	
-out:
-	if( j->kill_after_sample ) {
-		if (unlikely(j->debug_before_kill)) {
-			job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger");
-			job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
+	jobmgr_t jm = NULL;
+	if( j->is_being_sampled ) {
+		int wstatus = 0;
+		job_log(j, LOG_DEBUG | LOG_CONSOLE, "sample[%i] finished with job.", j->tracing_pid);
+		if( job_assumes(j, waitpid(j->tracing_pid, &wstatus, 0) != -1) ) {
+			job_assumes(j, WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0);
 		}
+		STAILQ_REMOVE(&j->mgr->pending_samples, j, job_s, pending_samples_sle);
 		
-		job_log(j, LOG_NOTICE, "Killing...");
-		job_kill(j);
+		if( j->kill_after_sample ) {
+			if (unlikely(j->debug_before_kill)) {
+				job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger");
+				job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
+			}
+			
+			job_log(j, LOG_NOTICE, "Killing...");
+			job_kill(j);
+		}
+		j->sampling_complete = true;
+		j->is_being_sampled = false;
+		jm = j->mgr;
 	}
 
-	job_log(j, LOG_DEBUG | LOG_CONSOLE, "sample[%i] finished with job.", j->sample_pid);
-	j->sample_pid = 0;
-	j->sampled = true;
-	STAILQ_REMOVE(&j->mgr->pending_samples, j, job_s, pending_samples_sle);
-	
-	if( j->reap_after_sample ) {
-		job_log(j, LOG_DEBUG | LOG_CONSOLE, "Reaping job now that sample is done.");
+	j->tracing_pid = 0;
+	if( j->reap_after_trace ) {
+		job_log(j, LOG_DEBUG | LOG_CONSOLE, "Reaping job now that attached tracer is gone.");
 		struct kevent kev;
-		EV_SET(&kev, 1, 0, 0, NOTE_EXIT, 0, 0);
+		EV_SET(&kev, j->p, 0, 0, NOTE_EXIT, 0, 0);
 		
 		/* Fake a kevent to keep our logic consistent. */
 		job_callback_proc(j, &kev);
 	}
 	
-	jobmgr_dequeue_next_sample(j->mgr);
+	if( jm ) {
+		jobmgr_dequeue_next_sample(jm);
+	}
 }
 
 void
@@ -2994,19 +3010,46 @@
 	bool program_changed = false;
 	int fflags = kev->fflags;
 	
-	if( j->sample_pid == (pid_t)kev->ident ) {
-		job_assumes(j, (fflags & NOTE_EXIT) != 0);
+	if( fflags & NOTE_EXIT ) {
+		if( j->p == (pid_t)kev->ident && !j->anonymous && !j->is_being_sampled ) {
+			int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, j->p };
+			struct kinfo_proc kp;
+			size_t 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) ) {
+				if( !job_assumes(j, kp.kp_eproc.e_ppid == getpid()) ) {
+					/* Someone has attached to the process with ptrace(). There's a race here. 
+					 * If we determine that we are not the parent process and then fail to attach 
+					 * a kevent to the parent PID (who is probably using ptrace()), we can take that as an
+					 * indication that the parent exited between sysctl(3) and kevent_mod(). The
+					 * reparenting of the PID should be atomic to us, so in that case, we reap the
+					 * job as normal.
+					 *
+					 * Otherwise, we wait for the death of the parent tracer and then reap, just as we
+					 * would if a job died while we were sampling it at shutdown.
+					 */
+					if( job_assumes(j, kevent_mod(kp.kp_eproc.e_ppid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j) != -1) ) {
+						j->tracing_pid = kp.kp_eproc.e_ppid;
+						j->reap_after_trace = true;
+						return;
+					}
+				}
+			}
+		} else if( !j->anonymous ) {
+			if( j->tracing_pid == (pid_t)kev->ident ) {
+				job_cleanup_after_tracer(j);
+				
+				return;
+			} else if( j->tracing_pid && !j->reap_after_trace ) {
+				/* The job exited before our sample completed. */
+				job_log(j, LOG_NOTICE | LOG_CONSOLE, "Job has exited. Will reap after tracing PID %i exits.", j->tracing_pid);
+				j->reap_after_trace = true;
+				return;
+			}
+		}
+	}
 		
-		job_reap_sample(j);
-		
-		return;
-	} else if( j->sample_pid && !j->reap_after_sample ) {
-		/* The job exited before our sample completed. */
-		job_log(j, LOG_NOTICE | LOG_CONSOLE, "Job has exited. Will reap after sample[%i] is complete.", j->sample_pid);
-		j->reap_after_sample = true;
-		return;
-	}
-	
 	if (fflags & NOTE_EXEC) {
 		program_changed = true;
 
@@ -3082,7 +3125,7 @@
 		 *    with the long SIGKILL
 		 */
 		
-		bool was_is_or_will_be_sampled = ( j->sampled || j->sample_pid || j->pending_sample );
+		bool was_is_or_will_be_sampled = ( j->sampling_complete || j->is_being_sampled || j->pending_sample );
 		bool should_enqueue = ( !was_is_or_will_be_sampled && do_apple_internal_logging );
 		
 		if (j->sent_sigkill) {
@@ -3106,12 +3149,12 @@
 			j->pending_sample = true;
 			jobmgr_dequeue_next_sample(j->mgr);
 		} else {
-			if( do_apple_internal_logging && !j->sampled ) {
-				if( j->sample_pid || j->pending_sample ) {
+			if( do_apple_internal_logging && !j->sampling_complete ) {
+				if( j->is_being_sampled || j->pending_sample ) {
 					char pidstr[24] = { 0 };
-					snprintf(pidstr, sizeof(pidstr), "[%i] ", j->sample_pid);
+					snprintf(pidstr, sizeof(pidstr), "[%i] ", j->tracing_pid);
 					
-					job_log(j, LOG_DEBUG | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Will kill after sample%shas completed.", j->exit_timeout, j->sample_pid ? pidstr : " ");
+					job_log(j, LOG_DEBUG | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Will kill after sample%shas completed.", j->exit_timeout, j->tracing_pid ? pidstr : " ");
 					j->kill_after_sample = true;
 				} else {
 					job_log(j, LOG_DEBUG | LOG_CONSOLE, "Exit timeout elapsed (%u seconds). Will sample and then kill.", j->exit_timeout);
@@ -3347,11 +3390,19 @@
 
 		job_log(j, LOG_DEBUG, "Started as PID: %u", c);
 
+		j->checkedin = false;
 		j->start_pending = false;
 		j->reaped = false;
 		j->crashed = false;
 		j->stopped = false;
+		if( j->needs_kickoff ) {
+			j->needs_kickoff = false;
 			
+			if( SLIST_EMPTY(&j->semaphores) ) {
+				j->ondemand = false;
+			}
+		}
+		
 		runtime_add_ref();
 		total_children++;
 		LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
@@ -4233,8 +4284,27 @@
 	/* 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(si->what, O_EVTONLY|O_NOCTTY))) == -1) {
-				si->watching_parent = job_assumes(j, (si->fd = _fd(open(parentdir, O_EVTONLY|O_NOCTTY))) != -1);
+			struct stat sb;
+			if( stat(si->what, &sb) == 0 ) {
+				/* If we're watching a character or block device, only watch the parent directory. 
+				 * See rdar://problem/6489900 for the gory details. Basically, holding an open file
+				 * descriptor to a devnode could end up (a) blocking us on open(2) until someone else
+				 * open(2)s the file (like a character device that waits for a carrier signal) or 
+				 * (b) preventing other processes from obtaining an exclusive lock on the file, even
+				 * though we're opening it with O_EVTONLY.
+				 *
+				 * The main point of contention is that O_EVTONLY doesn't actually mean "event only".
+				 * It means "Don't prevent unmounts of this descriptor's volume". We work around this
+				 * for dev nodes by only watching the parent directory and stat(2)ing our desired file
+				 * each time the parent changes to see if it appeared or disappeared.
+				 */
+				if( S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode) ) {
+					si->fd = _fd(open(si->what, O_EVTONLY | O_NOCTTY | O_NONBLOCK));
+				}
+			}
+			
+			if( si->fd == -1 ) {
+				si->watching_parent = job_assumes(j, (si->fd = _fd(open(parentdir, O_EVTONLY | O_NOCTTY | O_NONBLOCK))) != -1);
 			} else {
 				si->watching_parent = false;
 			}
@@ -4656,7 +4726,7 @@
 	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, false);
 	} else {
-		job_log(j, LOG_WARNING, "Ignoring reserved environmental variable: %s", key);
+		job_log(j, LOG_DEBUG, "Ignoring reserved environmental variable: %s", key);
 	}
 }
 
@@ -4823,6 +4893,11 @@
 		return false;
 	}
 
+	if( unlikely(j->needs_kickoff) ) {
+		job_log(j, LOG_DEBUG, "KeepAlive check: Job needs to be kicked off on-demand before KeepAlive sets in.");
+		return false;
+	}
+
 	if (j->start_pending) {
 		job_log(j, LOG_DEBUG, "KeepAlive check: Pent-up non-IPC launch criteria.");
 		return true;
@@ -5256,17 +5331,24 @@
 				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);
+			if( !(ji->hopefully_exits_first || ji->hopefully_exits_last) ) {
+				if( !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);
+					}
+				} else if( ji->migratory && jm->parentmgr ) {
+					/* If a job has migrated into a sub-bootstrap, we want to 
+					 * keep the job manager around as long as the job is there.
+					 */
+					unkilled_cnt++;
 				}
 			}
 		}
@@ -5292,8 +5374,12 @@
 		return _jm;
 	}
 	
+	if( jm == root_jobmgr ) {
+		simulate_pid1_crash();
+	}
+	
 	static bool killed_stray_jobs = false;
-	if( !killed_stray_jobs ) {
+	if( !killed_stray_jobs && pid1_magic && jm == root_jobmgr ) {
 		jobmgr_log_stray_children(jm, true);
 		killed_stray_jobs = true;
 	}
@@ -5359,7 +5445,11 @@
 		
 		job_t ji = NULL;
 		LIST_FOREACH( ji, &jm->jobs, sle ) {
-			if( !job_assumes(ji, (ji->anonymous || ji->sent_sigkill) && ji->p) ) {
+			if( pid1_magic && !jm->parentmgr ) {
+				if( (ji->anonymous || ji->sent_sigkill) && ji->p ) {
+					job_log(ji, LOG_NOTICE | LOG_CONSOLE, "Job should be gone but is not.");
+				}
+			} else if( !pid1_magic && !ji->anonymous ) {
 				job_log(ji, LOG_NOTICE | LOG_CONSOLE, "Job should be gone but is not.");
 			}
 		}
@@ -5586,9 +5676,6 @@
 		}
 	}
 
-	jmr->killed_hopefully_first_jobs	= false;
-	jmr->killed_normal_jobs 			= false;
-	jmr->killed_hopefully_last_jobs 	= false;
 	STAILQ_INIT(&jmr->pending_samples);
 
 	jobmgr_log(jmr, LOG_DEBUG, "Created job manager%s%s", jm ? " with parent: " : ".", jm ? jm->name : "");
@@ -5621,6 +5708,7 @@
 	bootstrapper = job_new(jm, thelabel, NULL, bootstrap_tool);
 	
 	if (jobmgr_assumes(jm, bootstrapper != NULL) && (jm->parentmgr || getuid())) {
+		bootstrapper->is_bootstrapper = true;
 		char buf[100];
 
 		/* <rdar://problem/5042202> launchd-201: can't ssh in with AFP OD account (hangs) */
@@ -5629,6 +5717,7 @@
 		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 ) {
+		bootstrapper->is_bootstrapper = true;
 		if( jobmgr_assumes(jm, pid1_magic) ) {
 			/* Have our system bootstrapper print out to the console. */
 			bootstrapper->stdoutpath = strdup(_PATH_CONSOLE);
@@ -6064,8 +6153,6 @@
 	if (si->fd != -1) {
 		job_assumes(j, runtime_close(si->fd) != -1);
 	}
-
-	job_log(j, LOG_DEBUG, "Removing semaphore item... (what = %s, why = %u)", si->what, si->why);
 	
 	/* We'll need to rethink this if it ever becomes possible to dynamically add or remove semaphores. */
 	if( (si->why == OTHER_JOB_ENABLED || si->why == OTHER_JOB_DISABLED) && j->nosy ) {
@@ -6103,6 +6190,8 @@
 			why = launch_data_get_bool(obj) ? SUCCESSFUL_EXIT : FAILED_EXIT;
 			semaphoreitem_new(j, why, NULL);
 			j->start_pending = true;
+		} else if( strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND) == 0 ) {
+			j->needs_kickoff = launch_data_get_bool(obj);
 		} else {
 			job_assumes(j, false);
 		}
@@ -6577,6 +6666,17 @@
 		}
 		launch_data_free(output_obj);
 		break;
+	case VPROC_GSK_MGR_NAME:
+		if( !job_assumes(j, (output_obj = launch_data_new_string(j->mgr->name)) != NULL) ) {
+			goto out_bad;
+		}
+		packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL);
+		if (!job_assumes(j, packed_size != 0)) {
+			goto out_bad;
+		}
+			
+		launch_data_free(output_obj);
+		break;
 	case 0:
 		mig_deallocate(*outval, *outvalCnt);
 		*outval = 0;
@@ -6700,6 +6800,7 @@
 		j->abandon_pg = (bool)inval;
 		break;
 	case VPROC_GSK_GLOBAL_ON_DEMAND:
+		job_log(j, LOG_NOTICE, "Job is setting global on-demand mode to %s (j->forced_peers_to_demand_mode = %s)", (bool)inval ? "true" : "false", j->forced_peers_to_demand_mode ? "true" : "false");
 		kr = job_set_global_on_demand(j, (bool)inval) ? 0 : 1;
 		break;
 	case VPROC_GSK_BASIC_KEEPALIVE:
@@ -6778,6 +6879,12 @@
 	case VPROC_GSK_WAITFORDEBUGGER:
 		j->wait4debugger_oneshot = inval;
 		break;
+	case VPROC_GSK_SHUTDOWN_DEBUGGING:
+		if( pid1_magic && j->is_bootstrapper ) {
+			runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***");
+			g_shutdown_debugging = true;
+		}
+		break;
 	case 0:
 		break;
 	default:
@@ -7010,10 +7117,9 @@
 			ms->isActive = true;
 			ms->recv = false;
 		}
-		job_checkin(j);
 
 		if (!(j->anonymous || j->legacy_LS_job || j->legacy_mach_job)) {
-			job_log(j, LOG_NOTICE, "Please add the following service to the configuration file for this job: %s", servicename);
+			job_log(j, LOG_APPLEONLY, "Please add the following service to the configuration file for this job: %s", servicename);
 		}
 	} else {
 		if (unlikely((jo = machservice_job(ms)) != j)) {
@@ -7033,6 +7139,7 @@
 		
 	}
 
+	job_checkin(j);
 	machservice_request_notifications(ms);
 
 	job_log(j, LOG_INFO, "Check-in of service: %s", servicename);
@@ -7206,10 +7313,12 @@
 }
 
 kern_return_t
-job_mig_info(job_t j, name_array_t *servicenamesp, unsigned int *servicenames_cnt,
-		bootstrap_status_array_t *serviceactivesp, unsigned int *serviceactives_cnt)
+job_mig_info(job_t j,	name_array_t *servicenamesp, unsigned int *servicenames_cnt,
+						name_array_t *servicejobsp, unsigned int *servicejobs_cnt,
+						bootstrap_status_array_t *serviceactivesp, unsigned int *serviceactives_cnt)
 {
 	name_array_t service_names = NULL;
+	name_array_t service_jobs = NULL;
 	bootstrap_status_array_t service_actives = NULL;
 	unsigned int cnt = 0, cnt2 = 0;
 	jobmgr_t jm;
@@ -7237,6 +7346,11 @@
 		goto out_bad;
 	}
 
+	mig_allocate((vm_address_t *)&service_jobs, cnt * sizeof(service_jobs[0]));
+	if (!job_assumes(j, service_jobs != NULL)) {
+		goto out_bad;
+	}
+
 	mig_allocate((vm_address_t *)&service_actives, cnt * sizeof(service_actives[0]));
 	if (!job_assumes(j, service_actives != NULL)) {
 		goto out_bad;
@@ -7246,6 +7360,7 @@
 		LIST_FOREACH( msi, &jm->ms_hash[i], name_hash_sle ) {
 			if( !msi->per_pid ) {
 				strlcpy(service_names[cnt2], machservice_name(msi), sizeof(service_names[0]));
+				strlcpy(service_jobs[cnt2], msi->job->label, sizeof(service_jobs[0]));
 				service_actives[cnt2] = machservice_status(msi);
 				cnt2++;
 			}
@@ -7256,8 +7371,9 @@
 
 out:
 	*servicenamesp = service_names;
+	*servicejobsp = service_jobs;
 	*serviceactivesp = service_actives;
-	*servicenames_cnt = *serviceactives_cnt = cnt;
+	*servicenames_cnt = *servicejobs_cnt = *serviceactives_cnt = cnt;
 
 	return BOOTSTRAP_SUCCESS;
 
@@ -7265,6 +7381,9 @@
 	if (service_names) {
 		mig_deallocate((vm_address_t)service_names, cnt * sizeof(service_names[0]));
 	}
+	if (service_jobs) {
+		mig_deallocate((vm_address_t)service_jobs, cnt * sizeof(service_jobs[0]));
+	}
 	if (service_actives) {
 		mig_deallocate((vm_address_t)service_actives, cnt * sizeof(service_actives[0]));
 	}
@@ -7522,7 +7641,7 @@
 }
 
 kern_return_t
-job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type)
+job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type, uint64_t flags)
 {
 	mach_msg_type_number_t l2l_i, l2l_port_cnt = 0;
 	mach_port_array_t l2l_ports = NULL;
@@ -7601,6 +7720,24 @@
 		goto out;
 	}
 
+	/* This is a hack. We should be doing this in jobmgr_new(), but since we're in the middle of
+	 * processing an IPC request, we'll do this action before the new job manager can get any IPC
+	 * requests. This serialization is guaranteed since we are single-threaded in that respect.
+	 */
+	if( flags & LAUNCH_GLOBAL_ON_DEMAND ) {
+		/* This is so awful. */
+		mach_port_t mp = MACH_PORT_NULL;
+		name_t target_name;
+		strlcpy(target_name, jmr->name, sizeof(target_name));
+		
+		kr = job_mig_switch_to_session(j, MACH_PORT_NULL, target_name, &mp);
+		if( job_assumes(j, kr == KERN_SUCCESS) ) {
+			job_set_global_on_demand(j, true);
+		} else {
+			job_log(j, LOG_WARNING, "Can't set global on-demand mode, unable to preemptively switch to %s job manager.", jmr->name);
+		}
+	}
+	
 	for (l2l_i = 0; l2l_i < l2l_port_cnt; l2l_i++) {
 		launch_data_t tmp, obj_at_idx;
 		struct machservice *ms;
@@ -7653,6 +7790,53 @@
 }
 
 kern_return_t
+job_mig_init_session(job_t j, name_t session_type)
+{
+	job_t j2;
+	
+	kern_return_t kr = BOOTSTRAP_NO_MEMORY;
+	if (j->mgr->session_initialized) {
+		job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
+		kr = BOOTSTRAP_NOT_PRIVILEGED;
+	} else if (strcmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
+		jobmgr_t jmi;
+		
+		/*
+		 * 5330262
+		 *
+		 * We're working around LoginWindow and the WindowServer.
+		 *
+		 * In practice, there is only one LoginWindow session. Unfortunately, for certain
+		 * scenarios, the WindowServer spawns loginwindow, and in those cases, it frequently
+		 * spawns a replacement loginwindow session before cleaning up the previous one.
+		 *
+		 * We're going to use the creation of a new LoginWindow context as a clue that the
+		 * previous LoginWindow context is on the way out and therefore we should just
+		 * kick-start the shutdown of it.
+		 */
+		
+		SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
+			if (unlikely(jmi->shutting_down)) {
+				continue;
+			} else if (strcasecmp(jmi->name, session_type) == 0) {
+				jobmgr_shutdown(jmi);
+				break;
+			}
+		}
+	}
+	
+	jobmgr_log(j->mgr, LOG_DEBUG, "Initializing: %s", session_type);
+	strcpy(j->mgr->name_init, session_type);
+	
+	if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) {
+		job_assumes(j, job_dispatch(j2, true));
+		kr = BOOTSTRAP_SUCCESS;
+	}
+	
+	return kr;
+}
+
+kern_return_t
 job_mig_switch_to_session(job_t j, mach_port_t requestor_port, name_t session_name, mach_port_t *new_bsport)
 {
 	job_log(j, LOG_NOTICE, "Job wants to move to %s session.", session_name);
@@ -7709,8 +7893,17 @@
 	}
 	
 	j->mgr = target_jm;
+	j->migratory = true;
 	*new_bsport = target_jm->jm_port;
 	
+	/* Anonymous jobs which move around are particularly interesting to us, so we want to
+	 * stick around while they're still around.
+	 * For example, login calls into the PAM launchd module, which moves the process into
+	 * the StandardIO session by default. So we'll hold a reference on that job to prevent
+	 * ourselves from going away.
+	 */
+	runtime_add_ref();
+	
 	return KERN_SUCCESS;
 }
 
@@ -8286,6 +8479,15 @@
 }
 
 static void
+simulate_pid1_crash(void)
+{
+	if( pid1_magic && g_simulate_pid1_crash ) {
+		runtime_syslog(LOG_EMERG | LOG_CONSOLE, "About to simulate a crash.");
+		raise(SIGSEGV);
+	}
+}
+
+static void
 jetsam_priority_from_job(job_t j, bool front, jetsam_priority_entry_t *jp)
 {
 	jp->pid = j->p;

Modified: trunk/launchd/src/launchd_runtime.c
===================================================================
--- trunk/launchd/src/launchd_runtime.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launchd_runtime.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -132,6 +132,8 @@
 bool low_level_debug;
 bool g_force_old_kill_path = false;
 bool g_flat_mach_namespace = true;
+bool g_simulate_pid1_crash = false;
+bool g_use_gmalloc = false;
 
 mach_port_t
 runtime_get_kernel_port(void)
@@ -1099,6 +1101,7 @@
 		case MACH_RCV_TIMED_OUT:
 			if (to != MACH_MSG_TIMEOUT_NONE) {
 				if (busy_cnt == 0) {
+					runtime_syslog(LOG_NOTICE, "Idle exiting. (This message will be removed before shipping.)");
 					launchd_shutdown();
 				} else if (runtime_idle_callback) {
 					runtime_idle_callback();
@@ -1175,7 +1178,6 @@
 				tmp_options |= MACH_SEND_TIMEOUT;
 			}
 		}
-
 	}
 }
 
@@ -1458,6 +1460,14 @@
 	}
 
 	for (lm_walk = (struct logmsg_s *)inval; (data_left > 0) && (lm_walk->obj_sz <= data_left); lm_walk = ((void *)lm_walk + lm_walk->obj_sz)) {
+		/* malloc() does not return NULL if you ask it for an allocation of size 0.
+		 * It will return a valid pointer that can be passed to free(). If we don't
+		 * do this check, we'll wind up corrupting our heap in the subsequent 
+		 * assignments.
+		 */
+		if( !launchd_assumes(lm_walk->obj_sz > 0) ) {
+			continue;
+		}
 		if (!launchd_assumes(lm = malloc(lm_walk->obj_sz))) {
 			continue;
 		}
@@ -1746,4 +1756,12 @@
 	if( !pid1_magic && stat("/var/db/.launchd_no_flat_per_user_namespace", &sb) == 0 ) {
 		g_flat_mach_namespace = false;
 	}
+	
+	if( pid1_magic && stat("/var/db/.launchd_simulate_pid1_crash", &sb) == 0 ) {
+		g_simulate_pid1_crash = true;
+	}
+	
+	if( pid1_magic && stat("/var/db/.launchd_use_gmalloc", &sb) == 0 ) {
+		g_use_gmalloc = true;
+	}
 }

Modified: trunk/launchd/src/launchd_runtime.h
===================================================================
--- trunk/launchd/src/launchd_runtime.h	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launchd_runtime.h	2009-01-29 22:46:14 UTC (rev 23790)
@@ -101,6 +101,8 @@
 extern bool pid1_magic;
 extern bool low_level_debug;
 extern char g_username[128];
+extern bool g_shutdown_debugging;
+extern bool g_use_gmalloc;
 
 mach_port_t runtime_get_kernel_port(void);
 extern boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply);

Modified: trunk/launchd/src/launchd_unix_ipc.c
===================================================================
--- trunk/launchd/src/launchd_unix_ipc.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/launchd_unix_ipc.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -235,9 +235,15 @@
 	}
 }
 
-static void set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
+static void 
+set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
 {
-	setenv(key, launch_data_get_string(obj), 1);
+	const char *v = launch_data_get_string(obj);
+	if( v ) {
+		setenv(key, v, 1);
+	} else {
+		runtime_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj));
+	}
 }
 
 void

Modified: trunk/launchd/src/libbootstrap.c
===================================================================
--- trunk/launchd/src/libbootstrap.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/libbootstrap.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -274,11 +274,11 @@
 
 kern_return_t
 bootstrap_info(mach_port_t bp,
-		name_array_t *service_names, mach_msg_type_number_t *service_namesCnt,
-		bootstrap_status_array_t *service_active, mach_msg_type_number_t *service_activeCnt)
+			   name_array_t *service_names, mach_msg_type_number_t *service_namesCnt,
+			   name_array_t *service_jobs, mach_msg_type_number_t *service_jobsCnt,
+			   bootstrap_status_array_t *service_active, mach_msg_type_number_t *service_activeCnt)
 {
-	return vproc_mig_info(bp, service_names, service_namesCnt,
-			service_active, service_activeCnt);
+	return vproc_mig_info(bp, service_names, service_namesCnt, service_jobs, service_jobsCnt, service_active, service_activeCnt);
 }
 
 const char *

Modified: trunk/launchd/src/liblaunch.c
===================================================================
--- trunk/launchd/src/liblaunch.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/liblaunch.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -1267,17 +1267,18 @@
 void
 load_launchd_jobs_at_loginwindow_prompt(int flags __attribute__((unused)), ...)
 {
-	_vprocmgr_init("LoginWindow");
+	_vprocmgr_init(VPROCMGR_SESSION_LOGINWINDOW);
 }
 
 pid_t
-create_and_switch_to_per_session_launchd(const char *login __attribute__((unused)), int flags __attribute__((unused)), ...)
+create_and_switch_to_per_session_launchd(const char *login __attribute__((unused)), int flags, ...)
 {
 	uid_t target_user = geteuid() ? geteuid() : getuid();
-
-	if (_vprocmgr_move_subset_to_user(target_user, "Aqua")) {
+	if (_vprocmgr_move_subset_to_user(target_user, VPROCMGR_SESSION_AQUA, flags)) {
 		return -1;
 	}
 
 	return 1;
 }
+
+

Modified: trunk/launchd/src/libvproc.c
===================================================================
--- trunk/launchd/src/libvproc.c	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/libvproc.c	2009-01-29 22:46:14 UTC (rev 23790)
@@ -189,8 +189,7 @@
 		
 		if (unlikely(old < 0)) {
 			if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
-				raise(SIGKILL);
-				__crashreporter_info__ = "raise(SIGKILL) failed";
+				_exit(0);
 			} else {
 				__crashreporter_info__ = "Unbalanced: vproc_transaction_begin()";
 			}
@@ -293,8 +292,7 @@
 	runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT, newval, 0, 0);
 	if (unlikely(newval < 0)) {
 		if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
-			raise(SIGKILL);
-			__crashreporter_info__ = "raise(SIGKILL) failed";
+			_exit(0);
 		} else {
 			__crashreporter_info__ = "Unbalanced: vproc_transaction_end()";
 		}
@@ -412,16 +410,10 @@
 	return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self()) == 0 ? NULL : _vproc_post_fork_ping;
 }
 
-static void
-setup_env_hack(const launch_data_t obj, const char *key, void *context __attribute__((unused)))
-{
-	setenv(key, launch_data_get_string(obj), 1);
-}
-
 vproc_err_t
 _vprocmgr_init(const char *session_type)
 {
-	if (vproc_mig_move_subset(bootstrap_port, MACH_PORT_NULL, (char *)session_type) == 0) {
+	if (vproc_mig_init_session(bootstrap_port, (char *)session_type) == 0) {
 		return NULL;
 	}
 
@@ -429,9 +421,8 @@
 }
 
 vproc_err_t
-_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type)
+_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags)
 {
-	launch_data_t output_obj;
 	kern_return_t kr = 0;
 	bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0);
 	int64_t ldpid, lduid;
@@ -466,7 +457,7 @@
 		mach_port_deallocate(mach_task_self(), bootstrap_port);
 		bootstrap_port = puc;
 	} else {
-		kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type);
+		kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, flags);
 		mach_port_deallocate(mach_task_self(), puc);
 	}
 	
@@ -476,16 +467,6 @@
 		return (vproc_err_t)_vprocmgr_move_subset_to_user;
 	}
 
-	/* XXX We need to give 'nohup' a better API after Leopard ships */
-	if (getprogname() && strcmp(getprogname(), "nohup") != 0) {
-		if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &output_obj) == NULL) {
-			if (launch_data_get_type(output_obj) == LAUNCH_DATA_DICTIONARY) {
-				launch_data_dict_iterate(output_obj, setup_env_hack, NULL);
-				launch_data_free(output_obj);
-			}
-		}
-	}
-
 	/* Don't do things like setting up the exception port if we're tainted. This is primarily for
 	 * loginwindow, which setuid(2)s to the logged-in user but still runs under the system Mach
 	 * bootstrap. So we want the system launchd to spawn a crash reporter to report loginwindow
@@ -502,7 +483,11 @@
 {
 	mach_port_t new_bsport = MACH_PORT_NULL;
 	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 ) {
+	
+	mach_port_t tnp = MACH_PORT_NULL;
+	task_name_for_pid(mach_task_self(), getpid(), &tnp);
+	
+	if( (kr = vproc_mig_switch_to_session(bootstrap_port, tnp, (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;
 	}
@@ -930,6 +915,28 @@
 	return rval;
 }
 
+vproc_err_t
+vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr)
+{
+	launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL;
+	launch_data_t outstr_data = NULL;
+	
+	vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data);
+	if( !verr && outstr ) {
+		if( launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING ) {
+			*outstr = strdup(launch_data_get_string(outstr_data));
+		} else {
+			verr = (vproc_err_t)vproc_swap_string;
+		}
+		launch_data_free(outstr_data);
+	}
+	if( instr_data ) {
+		launch_data_free(instr_data);
+	}
+	
+	return verr;
+}
+
 void *
 reboot2(uint64_t flags)
 {

Modified: trunk/launchd/src/protocol_vproc.defs
===================================================================
--- trunk/launchd/src/protocol_vproc.defs	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/protocol_vproc.defs	2009-01-29 22:46:14 UTC (rev 23790)
@@ -88,6 +88,7 @@
 routine info(
 				__bs_port			: job_t;
 out				__service_names		: name_array_t, dealloc;
+out				__service_jobs		: name_array_t, dealloc;
 out				__service_active	: bootstrap_status_array_t, dealloc);
 
 routine subset(
@@ -145,7 +146,8 @@
 routine move_subset(
 				__bs_port			: job_t;
 				__target_port		: mach_port_t;
-				__sessiontype		: name_t);
+				__sessiontype		: name_t;
+				__sessionflags		: uint64_t);
 
 routine swap_complex(
 				__bs_port			: job_t;
@@ -203,3 +205,7 @@
 				__bs_port			: job_t;
 				__label				: name_t;
 out				__mp				: mach_port_make_send_t);
+
+routine init_session(
+				__bs_port			: job_t;
+				__session_name		: name_t);

Modified: trunk/launchd/src/vproc_internal.h
===================================================================
--- trunk/launchd/src/vproc_internal.h	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/vproc_internal.h	2009-01-29 22:46:14 UTC (rev 23790)
@@ -103,12 +103,13 @@
 
 
 kern_return_t
-bootstrap_info(
-		mach_port_t bp,
-		name_array_t *service_names,
-		mach_msg_type_number_t *service_namesCnt,
-		bootstrap_status_array_t *service_active,
-		mach_msg_type_number_t *service_activeCnt);
+bootstrap_info(mach_port_t bp,
+			   name_array_t *service_names,
+			   mach_msg_type_number_t *service_namesCnt,
+			   name_array_t *service_jobs,
+			   mach_msg_type_number_t *service_jobsCnt,
+			   bootstrap_status_array_t *service_active,
+			   mach_msg_type_number_t *service_activeCnt);
 
 #pragma GCC visibility pop
 

Modified: trunk/launchd/src/vproc_priv.h
===================================================================
--- trunk/launchd/src/vproc_priv.h	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd/src/vproc_priv.h	2009-01-29 22:46:14 UTC (rev 23790)
@@ -48,6 +48,7 @@
 	VPROC_GSK_MGR_UID,
 	VPROC_GSK_MGR_PID,
 	VPROC_GSK_IS_MANAGED,
+	VPROC_GSK_MGR_NAME,
 	VPROC_GSK_BASIC_KEEPALIVE,
 	VPROC_GSK_START_INTERVAL,
 	VPROC_GSK_IDLE_TIMEOUT,
@@ -60,6 +61,7 @@
 	VPROC_GSK_TRANSACTIONS_ENABLED,
 	VPROC_GSK_WEIRD_BOOTSTRAP,
 	VPROC_GSK_WAITFORDEBUGGER,
+	VPROC_GSK_SHUTDOWN_DEBUGGING,
 } vproc_gsk_t;
 
 typedef unsigned int vproc_flags_t;
@@ -72,6 +74,7 @@
 
 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);
+vproc_err_t vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr);
 
 vproc_err_t _vproc_get_last_exit_status(int *wstatus);
 vproc_err_t _vproc_set_global_on_demand(bool val);
@@ -94,7 +97,7 @@
 #define VPROCMGR_SESSION_STANDARDIO		"StandardIO"
 #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_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags);
 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);
 

Modified: trunk/launchd.xcodeproj/project.pbxproj
===================================================================
--- trunk/launchd.xcodeproj/project.pbxproj	2009-01-21 05:14:00 UTC (rev 23789)
+++ trunk/launchd.xcodeproj/project.pbxproj	2009-01-29 22:46:14 UTC (rev 23790)
@@ -547,8 +547,8 @@
 		FC59A0C80E8C8A4E00D41150 /* launchproxy */ = {
 			isa = PBXGroup;
 			children = (
+				FC59A0DB0E8C8A6900D41150 /* launchproxy.8 */,
 				FC59A0DA0E8C8A6900D41150 /* launchproxy.c */,
-				FC59A0DB0E8C8A6900D41150 /* launchproxy.8 */,
 			);
 			name = launchproxy;
 			sourceTree = "<group>";
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-changes/attachments/20090129/b848c021/attachment-0001.html>


More information about the launchd-changes mailing list