Revision: 23130 http://trac.macosforge.org/projects/launchd/changeset/23130 Author: zarzycki@apple.com Date: 2007-03-04 15:38:21 -0800 (Sun, 04 Mar 2007) Log Message: ----------- <rdar://problem/4465956> launchd should be able to bootstrap itself Modified Paths: -------------- trunk/launchd/src/launchctl.c trunk/launchd/src/launchd_core_logic.c trunk/launchd/src/liblaunch.c trunk/launchd/src/libvproc.c trunk/launchd/src/libvproc_internal.h trunk/launchd/src/protocol_job.defs Modified: trunk/launchd/src/launchctl.c =================================================================== --- trunk/launchd/src/launchctl.c 2007-03-04 17:34:05 UTC (rev 23129) +++ trunk/launchd/src/launchctl.c 2007-03-04 23:38:21 UTC (rev 23130) @@ -250,8 +250,9 @@ } } - if (i > 0) + if (i > 0) { demux_cmd(i, argv2); + } free(l); } @@ -1243,7 +1244,7 @@ } static void -very_pid2_specific_bootstrap(bool sflag) +system_specific_bootstrap(bool sflag) { int hnmib[] = { CTL_KERN, KERN_HOSTNAME }; struct group *tfp_gr; @@ -1340,7 +1341,7 @@ _vproc_set_global_on_demand(true); - char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", NULL, NULL }; + char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", NULL }; if (is_safeboot()) { load_launchd_items[2] = "system"; @@ -1387,18 +1388,61 @@ } int -bootstrap_cmd(int argc, char *const argv[] __attribute__((unused))) +bootstrap_cmd(int argc, char *const argv[]) { - if (getuid() == 0) { - very_pid2_specific_bootstrap(argc == 2); + char *session_type = NULL; + bool sflag = false; + int ch; + + while ((ch = getopt(argc, argv, "sS:")) != -1) { + switch (ch) { + case 's': + sflag = true; + break; + case 'S': + session_type = optarg; + break; + case '?': + default: + break; + } + } + + optind = 1; + optreset = 1; + + if (!session_type) { + fprintf(stderr, "usage: %s bootstrap [-s] -S <session-type>\n", getprogname()); + return 1; + } + + if (strcasecmp(session_type, "System") == 0) { + system_specific_bootstrap(sflag); } else { - char *load_launchd_items[] = { "load", "-D", "all", "-S", "Background", NULL }; + char *load_launchd_items[] = { "load", "-S", session_type, "-D", "all", NULL, NULL, NULL, NULL }; + int the_argc = 5; if (is_safeboot()) { - load_launchd_items[2] = "system"; + load_launchd_items[4] = "system"; } - assumes(load_and_unload_cmd(5, load_launchd_items) == 0); + if (strcasecmp(session_type, "Background") == 0 || strcasecmp(session_type, "LoginWindow") == 0) { + load_launchd_items[4] = "system"; + if (!is_safeboot()) { + load_launchd_items[5] = "-D"; + load_launchd_items[6] = "local"; + the_argc += 2; + } + if (strcasecmp(session_type, "LoginWindow") == 0) { + load_launchd_items[the_argc] = "/etc/mach_init_per_login_session.d"; + the_argc += 1; + } + } else if (strcasecmp(session_type, "Aqua") == 0) { + load_launchd_items[5] = "/etc/mach_init_per_user.d"; + the_argc += 1; + } + + assumes(load_and_unload_cmd(the_argc, load_launchd_items) == 0); } return 0; @@ -1416,8 +1460,9 @@ memset(&lus, 0, sizeof(lus)); - if (!strcmp(argv[0], "load")) + if (strcmp(argv[0], "load") == 0) { lus.load = true; + } while ((ch = getopt(argc, argv, "wFS:D:")) != -1) { switch (ch) { @@ -1431,20 +1476,20 @@ lus.session_type = optarg; break; case 'D': - if (strcasecmp(optarg, "all") == 0) { - es |= NSAllDomainsMask; - } else if (strcasecmp(optarg, "user") == 0) { - es |= NSUserDomainMask; - } else if (strcasecmp(optarg, "local") == 0) { - es |= NSLocalDomainMask; - } else if (strcasecmp(optarg, "network") == 0) { - es |= NSNetworkDomainMask; - } else if (strcasecmp(optarg, "system") == 0) { - es |= NSSystemDomainMask; - } else { + if (strcasecmp(optarg, "all") == 0) { + es |= NSAllDomainsMask; + } else if (strcasecmp(optarg, "user") == 0) { + es |= NSUserDomainMask; + } else if (strcasecmp(optarg, "local") == 0) { + es |= NSLocalDomainMask; + } else if (strcasecmp(optarg, "network") == 0) { + es |= NSNetworkDomainMask; + } else if (strcasecmp(optarg, "system") == 0) { + es |= NSSystemDomainMask; + } else { badopts = true; - } - break; + } + break; case '?': default: badopts = true; @@ -1454,11 +1499,13 @@ argc -= optind; argv += optind; - if (lus.session_type == NULL) + if (lus.session_type == NULL) { es &= ~NSUserDomainMask; + } - if (argc == 0 && es == 0) + if (argc == 0 && es == 0) { badopts = true; + } if (badopts) { fprintf(stderr, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname()); @@ -1497,8 +1544,9 @@ } } - for (i = 0; i < (size_t)argc; i++) + for (i = 0; i < (size_t)argc; i++) { readpath(argv[i], &lus); + } if (launch_data_array_get_count(lus.pass0) == 0 && launch_data_array_get_count(lus.pass1) == 0 && Modified: trunk/launchd/src/launchd_core_logic.c =================================================================== --- trunk/launchd/src/launchd_core_logic.c 2007-03-04 17:34:05 UTC (rev 23129) +++ trunk/launchd/src/launchd_core_logic.c 2007-03-04 23:38:21 UTC (rev 23130) @@ -211,7 +211,7 @@ #define jobmgr_assumes(jm, e) \ (__builtin_expect(!(e), 0) ? jobmgr_log_bug(jm, __rcs_file_version__, __FILE__, __LINE__, #e), false : true) -static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag); +static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name); static jobmgr_t jobmgr_parent(jobmgr_t jm); static jobmgr_t jobmgr_do_garbage_collection(jobmgr_t jm); static bool jobmgr_is_idle(jobmgr_t jm); @@ -272,7 +272,7 @@ mode_t mask; unsigned int globargv:1, wait4debugger:1, unload_at_exit:1, stall_before_exec:1, only_once:1, currently_ignored:1, forced_peers_to_demand_mode:1, setnice:1, hopefully_exits_last:1, removal_pending:1, - wait4pipe_eof:1, sent_sigkill:1, debug_before_kill:1, weird_per_user_bootstrap:1; + wait4pipe_eof:1, sent_sigkill:1, debug_before_kill:1, weird_bootstrap:1; char label[0]; }; @@ -1649,7 +1649,7 @@ job_log(j, LOG_DEBUG, "Reaping"); - if (j->weird_per_user_bootstrap) { + if (j->weird_bootstrap) { mach_msg_size_t mxmsgsz = sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem); if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) { @@ -1657,7 +1657,7 @@ } job_assumes(j, runtime_add_mport(j->mgr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS); - j->weird_per_user_bootstrap = false; + j->weird_bootstrap = false; } if (j->log_redirect_fd && (!j->wait4pipe_eof || j->mgr->shutting_down)) { @@ -1996,7 +1996,7 @@ time(&j->start_time); - switch (c = runtime_fork(j->weird_per_user_bootstrap ? j->j_port : j->mgr->jm_port)) { + switch (c = runtime_fork(j->weird_bootstrap ? j->j_port : j->mgr->jm_port)) { case -1: job_log_error(j, LOG_ERR, "fork() failed, will try again in one second"); job_assumes(j, close(execspair[0]) == 0); @@ -3397,15 +3397,15 @@ } jobmgr_t -jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag) +jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name) { - const char *bootstrap_tool[] = { "/bin/launchctl", "bootstrap", NULL, NULL }; + const char *bootstrap_tool[] = { "/bin/launchctl", "bootstrap", "-S", name, NULL, NULL }; mach_msg_size_t mxmsgsz; job_t bootstrapper = NULL; jobmgr_t jmr; if (sflag) { - bootstrap_tool[2] = "-s"; + bootstrap_tool[4] = "-s"; } launchd_assert(offsetof(struct jobmgr_s, kqjobmgr_callback) == 0); @@ -3415,7 +3415,7 @@ return NULL; } - jmr = calloc(1, sizeof(struct jobmgr_s) + 30); + jmr = calloc(1, sizeof(struct jobmgr_s) + (name ? (strlen(name) + 1) : 128)); if (jmr == NULL) { return NULL; @@ -3423,7 +3423,7 @@ TAILQ_INIT(&jmr->jobs); jmr->kqjobmgr_callback = jobmgr_callback; - strcpy(jmr->name, "In-utero"); + strcpy(jmr->name, name ? name : "Under construction"); jmr->req_port = requestorport; @@ -3468,7 +3468,9 @@ goto out_bad; } - sprintf(jmr->name, "%u", MACH_PORT_INDEX(jmr->jm_port)); + if (!name) { + sprintf(jmr->name, "%u", MACH_PORT_INDEX(jmr->jm_port)); + } /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */ mxmsgsz = sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem); @@ -3478,17 +3480,20 @@ jobmgr_assumes(jmr, (jmr->anonj = jobmgr_get_anonymous(jmr)) != NULL); - bootstrapper = job_new(jmr, "com.apple.launchctld", NULL, bootstrap_tool); - if (!jm) { jobmgr_assumes(jmr, kevent_mod(SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1); } - if (!jm && getuid() != 0) { - /* per-user bootstrap context is messy */ - bootstrapper->weird_per_user_bootstrap = true; - jobmgr_assumes(jmr, job_setup_machport(bootstrapper)); - } else { + if (name) { + /* no name implies: bootstrap_subset() where creating a "bootstrapper" makes no sense */ + bootstrapper = job_new(jmr, "com.apple.launchctld", NULL, bootstrap_tool); + if (jm || getuid()) { + bootstrapper->weird_bootstrap = true; + jobmgr_assumes(jmr, job_setup_machport(bootstrapper)); + } + } + + if (!bootstrapper || !bootstrapper->weird_bootstrap) { if (!jobmgr_assumes(jmr, runtime_add_mport(jmr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) { goto out_bad; } @@ -3496,7 +3501,7 @@ jobmgr_log(jmr, LOG_DEBUG, "Created job manager%s%s", jm ? " with parent: " : ".", jm ? jm->name : ""); - if (!jm && jobmgr_assumes(jmr, bootstrapper != NULL)) { + if (bootstrapper) { jobmgr_assumes(jmr, job_dispatch(bootstrapper, true) != NULL); } @@ -4586,7 +4591,7 @@ } kern_return_t -job_mig_move_subset_to_user(job_t j, mach_port_t target_subset) +job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type) { mach_msg_type_number_t l2l_i, l2l_name_cnt = 0, l2l_port_cnt = 0; name_array_t l2l_names = NULL; @@ -4596,9 +4601,17 @@ jobmgr_t jmr; if (getuid() == 0) { + const char *bootstrap_tool[] = { "/bin/launchctl", "bootstrap", "-S", session_type, NULL }; + job_t bootstrapper; + j = job_mig_intran2(root_jobmgr, target_subset); + strcpy(j->mgr->name, session_type); job_assumes(j, launchd_mport_deallocate(target_subset) == KERN_SUCCESS); - strcpy(j->mgr->name, "Aqua"); + + bootstrapper = job_new(j->mgr, "com.apple.launchctld", NULL, bootstrap_tool); + if (job_assumes(j, bootstrapper != NULL)) { + job_dispatch(bootstrapper, true); + } return 0; } @@ -4613,13 +4626,11 @@ launchd_assert(l2l_name_cnt == l2l_port_cnt); - if ((jmr = jobmgr_new(j->mgr, reqport, rcvright, false)) == NULL) { + if ((jmr = jobmgr_new(j->mgr, reqport, rcvright, false, session_type)) == NULL) { kr = BOOTSTRAP_NO_MEMORY; goto out; } - strcpy(jmr->name, "Aqua"); - for (l2l_i = 0; l2l_i < l2l_name_cnt; l2l_i++) { struct machservice *ms; @@ -4742,7 +4753,7 @@ return BOOTSTRAP_NO_MEMORY; } - if ((jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL, false)) == NULL) { + if ((jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL, false, NULL)) == NULL) { if (requestorport == MACH_PORT_NULL) { return BOOTSTRAP_NOT_PRIVILEGED; } @@ -4917,5 +4928,5 @@ void jobmgr_init(bool sflag) { - launchd_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag)) != NULL); + launchd_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, getpid() == 1 ? "System" : "Background")) != NULL); } Modified: trunk/launchd/src/liblaunch.c =================================================================== --- trunk/launchd/src/liblaunch.c 2007-03-04 17:34:05 UTC (rev 23129) +++ trunk/launchd/src/liblaunch.c 2007-03-04 23:38:21 UTC (rev 23130) @@ -1160,101 +1160,28 @@ return r; } -static pid_t -fexecv_as_user(const char *login, uid_t u, gid_t g, char *const argv[]) -{ - int i, dtsz; - pid_t p; - - if ((p = fork()) != 0) - return p; - - chdir("/"); - - seteuid(0); - setegid(0); - setgid(g); - initgroups(login, g); - setuid(u); - - dtsz = getdtablesize(); - - for (i = STDERR_FILENO + 1; i < dtsz; i++) - close(i); - - execv(argv[0], argv); - _exit(EXIT_FAILURE); -} - void -load_launchd_jobs_at_loginwindow_prompt(int flags, ...) +load_launchd_jobs_at_loginwindow_prompt(int flags __attribute__((unused)), ...) { - char *largv[] = { "/bin/launchctl", "load", "-S", "LoginWindow", - "-D", "system", "-D", "local", "/etc/mach_init_per_login_session.d", NULL }; - int wstatus; - pid_t p; - - if (flags & LOAD_ONLY_SAFEMODE_LAUNCHAGENTS) { - largv[5] = "system"; - } - - if (__vproc_tag_loginwindow_context()) { - return; - } - - if ((p = fexecv_as_user("root", 0, 0, largv)) == -1) { - return; - } - - if (waitpid(p, &wstatus, 0) != p) { - return; - } + _vproc_move_subset_to_user("LoginWindow"); } pid_t -create_and_switch_to_per_session_launchd(const char *login, int flags, ...) +create_and_switch_to_per_session_launchd(const char *login __attribute__((unused)), int flags __attribute__((unused)), ...) { - char *largv[] = { "/bin/launchctl", "load", "-S", "Aqua", "-D", "all", "/etc/mach_init_per_user.d", NULL }; mach_port_t bezel_ui_server; - struct passwd *pwe; struct stat sb; - int wstatus; - pid_t p; - uid_t u; - gid_t g; - if (_vproc_move_subset_to_user()) { + if (_vproc_move_subset_to_user("Aqua")) { return -1; } - if ((pwe = getpwnam(login)) == NULL) - return -1; - - u = pwe->pw_uid; - g = pwe->pw_gid; - - if (flags & LOAD_ONLY_SAFEMODE_LAUNCHAGENTS) { - largv[5] = "system"; - } - - if ((p = fexecv_as_user(login, u, g, largv)) == -1) { - return -1; - } - - if (waitpid(p, &wstatus, 0) != p) { - return -1; - } - - if (!(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)) { - return -1; - } - #define BEZEL_UI_PATH "/System/Library/LoginPlugins/BezelServices.loginPlugin/Contents/Resources/BezelUI/BezelUIServer" #define BEZEL_UI_PLIST "/System/Library/LaunchAgents/com.apple.BezelUIServer.plist" #define BEZEL_UI_SERVICE "BezelUI" if (!(stat(BEZEL_UI_PLIST, &sb) == 0 && S_ISREG(sb.st_mode))) { - if (bootstrap_create_server(bootstrap_port, BEZEL_UI_PATH, u, true, &bezel_ui_server) == BOOTSTRAP_SUCCESS) { + if (bootstrap_create_server(bootstrap_port, BEZEL_UI_PATH, 0, true, &bezel_ui_server) == BOOTSTRAP_SUCCESS) { mach_port_t srv; if (bootstrap_create_service(bezel_ui_server, BEZEL_UI_SERVICE, &srv) == BOOTSTRAP_SUCCESS) { Modified: trunk/launchd/src/libvproc.c =================================================================== --- trunk/launchd/src/libvproc.c 2007-03-04 17:34:05 UTC (rev 23129) +++ trunk/launchd/src/libvproc.c 2007-03-04 23:38:21 UTC (rev 23130) @@ -47,7 +47,7 @@ } vproc_err_t -_vproc_move_subset_to_user(void) +_vproc_move_subset_to_user(char *session_type) { kern_return_t kr = 1; mach_port_t puc = 0, which_port = bootstrap_port; @@ -56,7 +56,7 @@ which_port = puc; } - kr = vproc_mig_move_subset_to_user(which_port, bootstrap_port); + kr = vproc_mig_move_subset(which_port, bootstrap_port, session_type); if (puc) { mach_port_deallocate(mach_task_self(), puc); Modified: trunk/launchd/src/libvproc_internal.h =================================================================== --- trunk/launchd/src/libvproc_internal.h 2007-03-04 17:34:05 UTC (rev 23129) +++ trunk/launchd/src/libvproc_internal.h 2007-03-04 23:38:21 UTC (rev 23130) @@ -57,7 +57,7 @@ kern_return_t _vprocmgr_getsocket(name_t); -vproc_err_t _vproc_move_subset_to_user(void); +vproc_err_t _vproc_move_subset_to_user(char *session_type); void _vproc_logv(int pri, int err, const char *msg, va_list ap); Modified: trunk/launchd/src/protocol_job.defs =================================================================== --- trunk/launchd/src/protocol_job.defs 2007-03-04 17:34:05 UTC (rev 23129) +++ trunk/launchd/src/protocol_job.defs 2007-03-04 23:38:21 UTC (rev 23130) @@ -142,6 +142,7 @@ __wu : uid_t; out __u_cont : mach_port_t); -routine move_subset_to_user( +routine move_subset( __bs_port : job_t; - __target_port : mach_port_t); + __target_port : mach_port_t; + __sessiontype : name_t);