X-Git-Url: https://git.saurik.com/apple/launchd.git/blobdiff_plain/ef3989319e2fdaf6ddb7590bc634ee693aa02a3b..ddbbfbc16a73ae4ed2ebe2c11ac6fb84b944c0dd:/launchd/src/launchctl.c diff --git a/launchd/src/launchctl.c b/launchd/src/launchctl.c index 4c943db..fd1381e 100644 --- a/launchd/src/launchctl.c +++ b/launchd/src/launchctl.c @@ -18,17 +18,21 @@ * @APPLE_APACHE_LICENSE_HEADER_END@ */ -static const char *const __rcs_file_version__ = "$Revision: 23792 $"; +static const char *const __rcs_file_version__ = "$Revision: 23930 $"; +#include "config.h" #include "launch.h" #include "launch_priv.h" #include "bootstrap.h" #include "vproc.h" #include "vproc_priv.h" #include "vproc_internal.h" +#include "bootstrap_priv.h" +#include "launch_internal.h" #include #include +#include #include #if HAVE_SECURITY #include @@ -43,6 +47,10 @@ static const char *const __rcs_file_version__ = "$Revision: 23792 $"; #include #include #include +#ifndef SO_EXECPATH +/* This is just so it's easy for me to compile launchctl without buildit. */ + #define SO_EXECPATH 0x1085 +#endif #include #include #include @@ -73,9 +81,20 @@ static const char *const __rcs_file_version__ = "$Revision: 23792 $"; #include #include #include +#include +#include + +#if HAVE_LIBAUDITD +#include +#ifndef AUDITD_PLIST_FILE +#define AUDITD_PLIST_FILE "/System/Library/LaunchDaemons/com.apple.auditd.plist" +#endif +#endif +extern char **environ; -#define LAUNCH_SECDIR "/tmp/launch-XXXXXX" + +#define LAUNCH_SECDIR _PATH_TMP "launch-XXXXXX" #define MACHINIT_JOBKEY_ONDEMAND "OnDemand" #define MACHINIT_JOBKEY_SERVICENAME "ServiceName" @@ -86,16 +105,21 @@ static const char *const __rcs_file_version__ = "$Revision: 23792 $"; #define assumes(e) \ (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true) +#define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID()) struct load_unload_state { launch_data_t pass0; launch_data_t pass1; launch_data_t pass2; char *session_type; - unsigned int editondisk:1, load:1, forceload:1, __pad:29; + bool editondisk:1, load:1, forceload:1; }; static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context); +static void job_override(CFTypeRef key, CFTypeRef val, CFMutableDictionaryRef job); +static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj); +static CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr); +static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict); static bool launch_data_array_append(launch_data_t a, launch_data_t o); static void distill_jobs(launch_data_t); static void distill_config_file(launch_data_t); @@ -104,6 +128,7 @@ static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data static launch_data_t CF2launch_data(CFTypeRef); static launch_data_t read_plist_file(const char *file, bool editondisk, bool load); static CFPropertyListRef CreateMyPropertyListFromFile(const char *); +static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL); static void WriteMyPropertyListToFile(CFPropertyListRef, const char *); static bool path_goodness_check(const char *path, bool forceload); static void readpath(const char *, struct load_unload_state *); @@ -131,7 +156,7 @@ static void print_launchd_env(launch_data_t obj, const char *key, void *context) static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test); static void loopback_setup_ipv4(void); static void loopback_setup_ipv6(void); -static pid_t fwexec(const char *const *argv, bool _wait); +static pid_t fwexec(const char *const *argv, int *wstatus); static void do_potential_fsck(void); static bool path_check(const char *path); static bool is_safeboot(void); @@ -145,9 +170,16 @@ static void preheat_page_cache_hack(void); static void do_bootroot_magic(void); static void do_single_user_mode(bool); static bool do_single_user_mode2(void); +static void do_crash_debug_mode(void); +static bool do_crash_debug_mode2(void); static void read_launchd_conf(void); +static void read_environment_dot_plist(void); static bool job_disabled_logic(launch_data_t obj); static void fix_bogus_file_metadata(void); +static void do_file_init(void) __attribute__((constructor)); +static void setup_system_context(void); +static void handle_system_bootstrapper_crashes_separately(void); +static void fatal_signal_handler(int sig, siginfo_t *si, void *uap); typedef enum { BOOTCACHE_START = 1, @@ -167,6 +199,7 @@ static int list_cmd(int argc, char *const argv[]); static int setenv_cmd(int argc, char *const argv[]); static int unsetenv_cmd(int argc, char *const argv[]); static int getenv_and_export_cmd(int argc, char *const argv[]); +static int wait4debugger_cmd(int argc, char *const argv[]); static int limit_cmd(int argc, char *const argv[]); static int stdio_cmd(int argc, char *const argv[]); @@ -175,8 +208,13 @@ static int logupdate_cmd(int argc, char *const argv[]); 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, bool show_job, bool local_only); static int bslist_cmd(int argc, char *const argv[]); - +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[]); @@ -185,37 +223,58 @@ static const struct { int (*func)(int argc, char *const argv[]); const char *desc; } cmds[] = { - { "load", load_and_unload_cmd, "Load configuration files and/or directories" }, - { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" }, -// { "reload", reload_cmd, "Reload configuration files and/or directories" }, - { "start", start_stop_remove_cmd, "Start specified job" }, - { "stop", start_stop_remove_cmd, "Stop specified job" }, - { "submit", submit_cmd, "Submit a job from the command line" }, - { "remove", start_stop_remove_cmd, "Remove specified job" }, - { "bootstrap", bootstrap_cmd, "Bootstrap launchd" }, - { "list", list_cmd, "List jobs and information about jobs" }, - { "setenv", setenv_cmd, "Set an environmental variable in launchd" }, - { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" }, - { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" }, - { "export", getenv_and_export_cmd, "Export shell settings from launchd" }, - { "limit", limit_cmd, "View and adjust launchd resource limits" }, - { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" }, - { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" }, - { "shutdown", fyi_cmd, "Prepare for system shutdown" }, - { "singleuser", fyi_cmd, "Switch to single-user mode" }, - { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" }, - { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" }, - { "umask", umask_cmd, "Change launchd's umask" }, - { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" }, - { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" }, - { "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; static bool verbose; static bool is_managed; +static bool do_apple_internal_magic; +static bool system_context; +static bool rootuser_context; +static bool bootstrapping_system; +static bool bootstrapping_peruser; +static bool g_verbose_boot = false; + +static bool g_job_overrides_db_has_changed = false; +static CFMutableDictionaryRef g_job_overrides_db = NULL; +static char g_job_overrides_db_path[PATH_MAX]; + +#if 0 +static bool g_job_cache_db_has_changed = false; +static launch_data_t g_job_cache_db = NULL; +static char g_job_cache_db_path[PATH_MAX]; +#endif int main(int argc, char *const argv[]) @@ -226,18 +285,10 @@ main(int argc, char *const argv[]) if (vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed_val) == NULL && is_managed_val) { is_managed = true; } - - if (getuid() == 0 && !is_managed) { - mach_port_t root_bs = str2bsport("/"); - task_set_bootstrap_port(mach_task_self(), root_bs); - mach_port_deallocate(mach_task_self(), bootstrap_port); - bootstrap_port = root_bs; - } - + istty = isatty(STDIN_FILENO); - argc--, argv++; - + if (argc > 0 && argv[0][0] == '-') { char *flago; @@ -246,6 +297,22 @@ main(int argc, char *const argv[]) case 'v': verbose = true; break; + case 'u': + if( argc > 1 ) { + if( strncmp(argv[1], "root", sizeof("root")) == 0 ) { + rootuser_context = true; + } else { + fprintf(stderr, "Unknown user: %s\n", argv[1]); + exit(EXIT_FAILURE); + } + argc--, argv++; + } else { + fprintf(stderr, "-u option requires an argument. Currently, only \"root\" is supported.\n"); + } + break; + case '1': + system_context = true; + break; default: fprintf(stderr, "Unknown argument: '-%c'\n", *flago); break; @@ -254,6 +321,38 @@ main(int argc, char *const argv[]) argc--, argv++; } + /* Running in the context of the root user's per-user launchd is only supported ... well + * in the root user's per-user context. I know it's confusing. I'm genuinely sorry. + */ + if( rootuser_context ) { + int64_t manager_uid = -1, manager_pid = -1; + if( vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid) == NULL ) { + if( vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid) == NULL ) { + if( manager_uid || manager_pid == 1 ) { + fprintf(stderr, "Running in the root user's per-user context is not supported outside of the root user's bootstrap.\n"); + exit(EXIT_FAILURE); + } + } + } + } else if( !(system_context || rootuser_context) ) { + /* Running in the system context is implied when we're running as root and not running as a bootstrapper. */ + system_context = ( !is_managed && getuid() == 0 ); + } + + if( system_context ) { + if( getuid() == 0 ) { + setup_system_context(); + } else { + fprintf(stderr, "You must be root to run in the system context.\n"); + exit(EXIT_FAILURE); + } + } else if( rootuser_context ) { + if( getuid() != 0 ) { + fprintf(stderr, "You must be root to run in the root user context.\n"); + exit(EXIT_FAILURE); + } + } + if (NULL == readline) { fprintf(stderr, "missing library: readline\n"); exit(EXIT_FAILURE); @@ -297,7 +396,7 @@ demux_cmd(int argc, char *const argv[]) optind = 1; optreset = 1; - + for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { if (!strcmp(cmds[i].name, argv[0])) { return cmds[i].func(argc, argv); @@ -313,7 +412,8 @@ read_launchd_conf(void) { char s[1000], *c, *av[100]; const char *file; - size_t len, i; + size_t len; + int i; FILE *f; if (getppid() == 1) { @@ -326,7 +426,7 @@ read_launchd_conf(void) return; } - while ((c = fgets(s, sizeof(s), f))) { + while ((c = fgets(s, (int) sizeof s, f))) { len = strlen(c); if (len && c[len - 1] == '\n') { c[len - 1] = '\0'; @@ -348,6 +448,105 @@ read_launchd_conf(void) fclose(f); } +CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL) +{ + CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL); + + CFErrorRef streamErr = NULL; + if( !CFReadStreamOpen(plistReadStream) ) { + streamErr = CFReadStreamCopyError(plistReadStream); + CFStringRef errString = CFErrorCopyDescription(streamErr); + + CFShow(errString); + + CFRelease(errString); + CFRelease(streamErr); + } + + CFPropertyListRef plist = NULL; + if( plistReadStream ) { + CFStringRef errString = NULL; + CFPropertyListFormat plistFormat = 0; + plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString); + if( !plist ) { + CFShow(errString); + CFRelease(errString); + } + } + + CFReadStreamClose(plistReadStream); + CFRelease(plistReadStream); + + return plist; +} + +#define CFReleaseIfNotNULL(cf) if( cf ) CFRelease(cf); +void +read_environment_dot_plist(void) +{ + CFStringRef plistPath = NULL; + CFURLRef plistURL = NULL; + CFDictionaryRef envPlist = NULL; + launch_data_t req = NULL, launch_env_dict = NULL, resp = NULL; + + 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; + } + + plistURL = CFURLCreateWithFileSystemPath(NULL, plistPath, kCFURLPOSIXPathStyle, false); + if( !assumes(plistURL != NULL) ) { + goto out; + } + + envPlist = (CFDictionaryRef)CFPropertyListCreateFromFile(plistURL); + if( !assumes(envPlist != NULL) ) { + goto out; + } + + launch_env_dict = CF2launch_data(envPlist); + if( !assumes(launch_env_dict != NULL) ) { + goto out; + } + + req = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + if( !assumes(req != NULL) ) { + goto out; + } + + launch_data_dict_insert(req, launch_env_dict, LAUNCH_KEY_SETUSERENVIRONMENT); + resp = launch_msg(req); + if( !assumes(resp != NULL) ) { + goto out; + } + + if( !assumes(launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) ) { + goto out; + } + + assumes(launch_data_get_errno(resp) == 0); +out: + CFReleaseIfNotNULL(plistPath); + CFReleaseIfNotNULL(plistURL); + CFReleaseIfNotNULL(envPlist); + if( req ) { + launch_data_free(req); + } + + if( resp ) { + launch_data_free(resp); + } +} + int unsetenv_cmd(int argc, char *const argv[]) { @@ -462,6 +661,37 @@ getenv_and_export_cmd(int argc, char *const argv[]) return 0; } +int +wait4debugger_cmd(int argc, char * const argv[]) +{ + if( argc != 3 ) { + fprintf(stderr, "%s usage: debug