[launchd-changes] [23808] branches/PR-6424345
source_changes at macosforge.org
source_changes at macosforge.org
Sat Feb 14 16:33:24 PST 2009
Revision: 23808
http://trac.macosforge.org/projects/launchd/changeset/23808
Author: nectar at apple.com
Date: 2009-02-14 16:33:23 -0800 (Sat, 14 Feb 2009)
Log Message:
-----------
merge from trunk r23790
Modified Paths:
--------------
branches/PR-6424345/launchd/src/bootstrap_priv.h
branches/PR-6424345/launchd/src/launch.h
branches/PR-6424345/launchd/src/launch_priv.h
branches/PR-6424345/launchd/src/launchctl.c
branches/PR-6424345/launchd/src/launchd.c
branches/PR-6424345/launchd/src/launchd.h
branches/PR-6424345/launchd/src/launchd_core_logic.c
branches/PR-6424345/launchd/src/launchd_runtime.c
branches/PR-6424345/launchd/src/launchd_runtime.h
branches/PR-6424345/launchd/src/launchd_unix_ipc.c
branches/PR-6424345/launchd/src/libbootstrap.c
branches/PR-6424345/launchd/src/liblaunch.c
branches/PR-6424345/launchd/src/libvproc.c
branches/PR-6424345/launchd/src/protocol_vproc.defs
branches/PR-6424345/launchd/src/vproc_internal.h
branches/PR-6424345/launchd/src/vproc_priv.h
branches/PR-6424345/launchd.xcodeproj/project.pbxproj
Property Changed:
----------------
branches/PR-6424345/
Property changes on: branches/PR-6424345
___________________________________________________________________
Modified: svn:mergeinfo
- /branches/PR-5092682:23731-23742
/branches/PR-5898404:23681-23700
/branches/PR-5978442:23651-23701
/branches/PR-6132016:23719-23738
/trunk:23759-23783
+ /branches/PR-5092682:23731-23742
/branches/PR-5898404:23681-23700
/branches/PR-5978442:23651-23701
/branches/PR-6132016:23719-23738
/trunk:23759-23790
Modified: branches/PR-6424345/launchd/src/bootstrap_priv.h
===================================================================
--- branches/PR-6424345/launchd/src/bootstrap_priv.h 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/bootstrap_priv.h 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/launch.h
===================================================================
--- branches/PR-6424345/launchd/src/launch.h 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launch.h 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/launch_priv.h
===================================================================
--- branches/PR-6424345/launchd/src/launch_priv.h 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launch_priv.h 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/launchctl.c
===================================================================
--- branches/PR-6424345/launchd/src/launchctl.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launchctl.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -172,6 +172,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,
@@ -200,12 +201,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[]);
@@ -214,36 +216,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;
@@ -252,6 +255,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[])
@@ -466,7 +470,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;
}
@@ -1375,10 +1388,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;
}
@@ -1401,6 +1422,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)
@@ -1522,10 +1547,11 @@
}
result = CFArrayCreateCopy(NULL, mutResult);
-
+ }
+
+ if( mutResult ) {
CFRelease(mutResult);
}
-
return result;
}
@@ -1794,6 +1820,7 @@
assumes(fwexec(rcserver_tool, NULL) != -1);
}
+ tell_launchd_about_boot_args();
read_launchd_conf();
if (path_check("/var/account/acct")) {
@@ -2031,6 +2058,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
@@ -2039,9 +2068,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";
@@ -2511,18 +2540,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);
@@ -2895,6 +2925,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[])
{
@@ -3102,11 +3169,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;
@@ -3115,7 +3183,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;
@@ -3124,7 +3192,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;
@@ -3134,20 +3206,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");
@@ -3171,13 +3252,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);
}
}
@@ -3185,16 +3266,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
@@ -3225,6 +3310,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)
{
@@ -3337,10 +3438,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) {
@@ -3819,4 +3923,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: branches/PR-6424345/launchd/src/launchd.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launchd.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -69,6 +69,7 @@
#include <spawn.h>
#include <sched.h>
#include <pthread.h>
+#include <util.h>
#if HAVE_LIBAUDITD
#include <bsm/auditd_lib.h>
@@ -100,6 +101,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);
@@ -111,7 +115,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
@@ -125,8 +129,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);
@@ -136,7 +139,6 @@
unsetenv("MALLOC_STRICT_SIZE");
}
}
-#endif
while ((ch = getopt(argc, argv, "s")) != -1) {
switch (ch) {
@@ -161,9 +163,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;
@@ -190,6 +192,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 ) {
@@ -258,6 +263,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";
@@ -268,6 +352,8 @@
crash_addr = si->si_addr;
crash_pid = si->si_pid;
+ do_pid1_crash_diagnosis_mode();
+
unlink(PID1_CRASH_LOGFILE);
switch ((sample_p = vfork())) {
@@ -343,7 +429,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: branches/PR-6424345/launchd/src/launchd.h
===================================================================
--- branches/PR-6424345/launchd/src/launchd.h 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launchd.h 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/launchd_core_logic.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd_core_logic.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launchd_core_logic.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/launchd_runtime.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd_runtime.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launchd_runtime.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/launchd_runtime.h
===================================================================
--- branches/PR-6424345/launchd/src/launchd_runtime.h 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launchd_runtime.h 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/launchd_unix_ipc.c
===================================================================
--- branches/PR-6424345/launchd/src/launchd_unix_ipc.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/launchd_unix_ipc.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/libbootstrap.c
===================================================================
--- branches/PR-6424345/launchd/src/libbootstrap.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/libbootstrap.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/liblaunch.c
===================================================================
--- branches/PR-6424345/launchd/src/liblaunch.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/liblaunch.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/libvproc.c
===================================================================
--- branches/PR-6424345/launchd/src/libvproc.c 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/libvproc.c 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/protocol_vproc.defs
===================================================================
--- branches/PR-6424345/launchd/src/protocol_vproc.defs 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/protocol_vproc.defs 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/vproc_internal.h
===================================================================
--- branches/PR-6424345/launchd/src/vproc_internal.h 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/vproc_internal.h 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd/src/vproc_priv.h
===================================================================
--- branches/PR-6424345/launchd/src/vproc_priv.h 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd/src/vproc_priv.h 2009-02-15 00:33:23 UTC (rev 23808)
@@ -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: branches/PR-6424345/launchd.xcodeproj/project.pbxproj
===================================================================
--- branches/PR-6424345/launchd.xcodeproj/project.pbxproj 2009-02-14 01:13:33 UTC (rev 23807)
+++ branches/PR-6424345/launchd.xcodeproj/project.pbxproj 2009-02-15 00:33:23 UTC (rev 23808)
@@ -553,8 +553,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/20090214/ac447877/attachment-0001.html>
More information about the launchd-changes
mailing list