Revision: 22943 http://trac.macosforge.org/projects/launchd/changeset/22943 Author: zarzycki@apple.com Date: 2006-11-13 12:53:23 -0800 (Mon, 13 Nov 2006) Log Message: ----------- First pass at the jobmgr struct. Next step is to have a per jobmgr socket setup... Modified Paths: -------------- trunk/launchd/src/launchctl.c trunk/launchd/src/launchd.c trunk/launchd/src/launchd_core_logic.c trunk/launchd/src/launchd_core_logic.h trunk/launchd/src/launchd_runtime.c trunk/launchd/src/launchd_unix_ipc.c trunk/launchd/src/liblaunch_public.h Modified: trunk/launchd/src/launchctl.c =================================================================== --- trunk/launchd/src/launchctl.c 2006-11-13 17:54:12 UTC (rev 22942) +++ trunk/launchd/src/launchctl.c 2006-11-13 20:53:23 UTC (rev 22943) @@ -108,7 +108,7 @@ static void let_go_of_mach_jobs(launch_data_t jobs); static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup); static mach_port_t str2bsport(const char *s); -static void print_jobs(launch_data_t j); +static void print_jobs(launch_data_t j, const char *key, void *context); static void print_obj(launch_data_t obj, const char *key, void *context); static bool is_legacy_mach_job(launch_data_t obj); static bool delay_to_second_pass(launch_data_t o); @@ -1568,15 +1568,14 @@ } void -print_jobs(launch_data_t j) +print_jobs(launch_data_t j, const char *key __attribute__((unused)), void *context __attribute__((unused))) { static size_t depth = 0; launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL); launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID); launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS); - launch_data_t sjobs = launch_data_dict_lookup(j, LAUNCH_JOBKEY_SUBJOBS); const char *label = launch_data_get_string(lo); - size_t i, c; + size_t i; if (pido) { fprintf(stdout, "%lld\t-\t", launch_data_get_integer(pido)); @@ -1596,19 +1595,6 @@ fprintf(stdout, "\t"); fprintf(stdout, "%s\n", label); - - if (sjobs) { - launch_data_t oai; - - c = launch_data_array_get_count(sjobs); - - depth++; - for (i = 0; i < c; i++) { - oai = launch_data_array_get_index(sjobs, i); - print_jobs(oai); - } - depth--; - } } void @@ -1671,16 +1657,17 @@ int list_cmd(int argc, char *const argv[]) { - launch_data_t resp, msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); + launch_data_t resp, msg; int r = 0; if (argc > 2) { fprintf(stderr, "usage: %s list [label]\n", getprogname()); return 1; } else if (argc == 2) { + msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB); } else { - launch_data_dict_insert(msg, launch_data_new_string(""), LAUNCH_KEY_GETJOB); + msg = launch_data_new_string(LAUNCH_KEY_GETJOBS); } resp = launch_msg(msg); @@ -1692,7 +1679,7 @@ } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) { if (argc == 1) { fprintf(stdout, "PID\tStatus\tLabel\n"); - print_jobs(resp); + launch_data_dict_iterate(resp, print_jobs, NULL); } else { print_obj(resp, NULL, NULL); } Modified: trunk/launchd/src/launchd.c =================================================================== --- trunk/launchd/src/launchd.c 2006-11-13 17:54:12 UTC (rev 22942) +++ trunk/launchd/src/launchd.c 2006-11-13 20:53:23 UTC (rev 22943) @@ -275,11 +275,11 @@ snprintf(ldconf, sizeof(ldconf), "%s/%s", h, LAUNCHD_CONF); } - rlcj = job_new(root_job, READCONF_LABEL, LAUNCHCTL_PATH, NULL, ldconf, MACH_PORT_NULL); + rlcj = job_new(root_jobmgr, READCONF_LABEL, LAUNCHCTL_PATH, NULL, ldconf); launchd_assert(rlcj != NULL); if (argv[0]) { - fbj = job_new(root_job, FIRSTBORN_LABEL, NULL, (const char *const *)argv, NULL, MACH_PORT_NULL); + fbj = job_new(root_jobmgr, FIRSTBORN_LABEL, NULL, (const char *const *)argv, NULL); } if (NULL == getenv("PATH")) { @@ -432,14 +432,16 @@ shutdown_in_progress = true; if (stat("/var/db/debugShutdownHangs", &sb) != -1) { - // When this changes to a more sustainable API, update this: - // http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown + /* + * When this changes to a more sustainable API, update this: + * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown + */ debug_shutdown_hangs = true; } rlcj = NULL; - job_remove_all_inactive(root_job); + jobmgr_remove_all_inactive(root_jobmgr); if (getpid() == 1) { catatonia(); @@ -650,7 +652,7 @@ if (new_networking_state != network_up) { network_up = new_networking_state; - job_dispatch_all_other_semaphores(root_job, NULL); + jobmgr_dispatch_all_other_semaphores(root_jobmgr, NULL); } } Modified: trunk/launchd/src/launchd_core_logic.c =================================================================== --- trunk/launchd/src/launchd_core_logic.c 2006-11-13 17:54:12 UTC (rev 22942) +++ trunk/launchd/src/launchd_core_logic.c 2006-11-13 20:53:23 UTC (rev 22943) @@ -192,7 +192,36 @@ static void semaphoreitem_setup(launch_data_t obj, const char *key, void *context); static void semaphoreitem_setup_paths(launch_data_t obj, const char *key, void *context); +struct jobmgr_s { + SLIST_ENTRY(jobmgr_s) sle; + SLIST_HEAD(, jobmgr_s) submgrs; + SLIST_HEAD(, job_s) jobs; + mach_port_t jm_port; + mach_port_t req_port; + jobmgr_t parentmgr; + job_t anonj; + unsigned int transfer_bstrap:1; + char name[0]; +}; +#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 checkin_port); +static jobmgr_t jobmgr_parent(jobmgr_t jm); +static void jobmgr_dispatch_all(jobmgr_t jm); +static job_t jobmgr_new_anonymous(jobmgr_t jm); +static job_t job_mig_intran2(jobmgr_t jm, mach_port_t p); +static mach_port_t jobmgr_get_reqport(jobmgr_t jm); +static void job_export_all2(jobmgr_t jm, launch_data_t where); +static pid_t jobmgr_fork(jobmgr_t jm); +static void jobmgr_setup_env_from_other_jobs(jobmgr_t jm); +static struct machservice *jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent); +static void jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap); +static void jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); +/* static void jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); */ +static void jobmgr_log_bug(jobmgr_t jm, const char *rcs_rev, const char *path, unsigned int line, const char *test); + struct job_s { kq_callback kqjob_callback; SLIST_ENTRY(job_s) sle; @@ -204,13 +233,11 @@ SLIST_HEAD(, limititem) limits; SLIST_HEAD(, machservice) machservices; SLIST_HEAD(, semaphoreitem) semaphores; - SLIST_HEAD(, job_s) jobs; struct rusage ru; - job_t parent; - mach_port_t bs_port; - mach_port_t req_port; + mach_port_t j_port; mach_port_t wait_reply_port; uid_t mach_uid; + jobmgr_t mgr; char **argv; char *prog; char *rootdir; @@ -234,8 +261,8 @@ importing_global_env:1, importing_hard_limits:1, setmask:1, legacy_mach_job:1, runatload:1, anonymous:1; mode_t mask; - unsigned int globargv:1, wait4debugger:1, transfer_bstrap:1, unload_at_exit:1, force_ppc:1, - stall_before_exec:1, only_once:1, currently_ignored:1, forced_peers_to_demand_mode:1; + unsigned int globargv:1, wait4debugger:1, unload_at_exit:1, force_ppc:1, stall_before_exec:1, + only_once:1, currently_ignored:1, forced_peers_to_demand_mode:1; char label[0]; }; @@ -249,7 +276,6 @@ static void job_import_integer(job_t j, const char *key, long long value); static void job_import_dictionary(job_t j, const char *key, launch_data_t value); static void job_import_array(job_t j, const char *key, launch_data_t value); -static void job_dispatch_all(job_t j); static bool job_set_global_on_demand(job_t j, bool val); static void job_watch(job_t j); static void job_ignore(job_t j); @@ -263,26 +289,13 @@ static void job_postfork_become_user(job_t j); static void job_force_sampletool(job_t j); static void job_callback(void *obj, struct kevent *kev); -static pid_t job_fork(job_t j); -static size_t job_prep_log_preface(job_t j, char *buf); -static void job_setup_env_from_other_jobs(job_t j); -static void job_export_all2(job_t j, launch_data_t where); static launch_data_t job_export2(job_t j, bool subjobs); -static job_t job_find_by_pid(job_t j, pid_t p, bool recurse); static job_t job_new_spawn(job_t j, const char *label, const char *path, const char *workingdir, const char *const *argv, const char *const *env, mode_t *u_mask, bool w4d, bool fppc); -static job_t job_new_via_mach_init(job_t jbs, const char *cmd, uid_t uid, bool ond); -static job_t job_new_bootstrap(job_t p, mach_port_t requestorport, mach_port_t checkin_port); -static bool job_new_anonymous(job_t p); -static job_t job_find_anonymous(job_t p); +static job_t job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond); static const char *job_prog(job_t j); static pid_t job_get_pid(job_t j); -static mach_port_t job_get_bsport(job_t j); -static mach_port_t job_get_reqport(job_t j); -static job_t job_get_bs(job_t j); -static job_t job_parent(job_t j); +static jobmgr_t job_get_bs(job_t j); static void job_uncork_fork(job_t j); -static struct machservice *job_lookup_service(job_t jbs, const char *name, bool check_parent); -static void job_foreach_service(job_t jbs, void (*bs_iter)(struct machservice *, void *), void *context, bool only_anonymous); static void job_logv(job_t j, int pri, int err, const char *msg, va_list ap); static void job_log(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); static void job_log_error(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); @@ -320,8 +333,8 @@ static int dir_has_files(job_t j, const char *path); static char **mach_cmd2argv(const char *string); static size_t global_on_demand_cnt; -job_t root_job; -job_t gc_this_job; +jobmgr_t root_jobmgr; +jobmgr_t gc_this_jobmgr; size_t total_children; void @@ -481,6 +494,8 @@ launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_MACHSERVICES); } +#if 0 + /* jobs don't have subjobs anymore... */ if (subjobs && !SLIST_EMPTY(&j->jobs) && (tmp = launch_data_alloc(LAUNCH_DATA_ARRAY))) { job_t ji; size_t i = 0; @@ -493,35 +508,71 @@ launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_SUBJOBS); } +#endif return r; } void -job_remove_all_inactive(job_t j) +jobmgr_remove_all_inactive(jobmgr_t jm) { + jobmgr_t jmi, jmn; job_t ji, jn; - SLIST_FOREACH_SAFE(ji, &j->jobs, sle, jn) { - job_remove_all_inactive(ji); + SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) { + jobmgr_remove_all_inactive(jmi); } - if (!job_active(j)) { - job_remove(j); - } else { - if (debug_shutdown_hangs) { - job_assumes(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 8, j) != -1); + SLIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) { + if (!job_active(ji)) { + job_remove(ji); + } else { + if (debug_shutdown_hangs) { + job_assumes(ji, kevent_mod((uintptr_t)ji, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 8, ji) != -1); + } + if (getpid() != 1) { + job_stop(ji); + } } - if (getpid() != 1) { - job_stop(j); + } +} + +void +jobmgr_remove(jobmgr_t jm) +{ + jobmgr_t jmi; + job_t ji; + + while ((jmi = SLIST_FIRST(&jm->submgrs))) { + jobmgr_remove(jmi); + } + + while ((ji = SLIST_FIRST(&jm->jobs))) { + job_remove(ji); + } + + if (jm->parentmgr) { + SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle); + } + + if (jm->req_port) { + jobmgr_assumes(jm, launchd_mport_deallocate(jm->req_port) == KERN_SUCCESS); + } + + if (jm->jm_port) { + if (jm->transfer_bstrap) { + jobmgr_assumes(jm, launchd_mport_deallocate(jm->jm_port) == KERN_SUCCESS); + } else { + jobmgr_assumes(jm, launchd_mport_close_recv(jm->jm_port) == KERN_SUCCESS); } } + + free(jm); } void job_remove(job_t j) { - job_t ji; struct calendarinterval *ci; struct socketgroup *sg; struct watchpath *wp; @@ -542,34 +593,24 @@ } } - if (j->parent) { - SLIST_REMOVE(&j->parent->jobs, j, job_s, sle); + if (job_assumes(j, j->mgr)) { + SLIST_REMOVE(&j->mgr->jobs, j, job_s, sle); } if (j->execfd) { job_assumes(j, close(j->execfd) == 0); } - if (j->bs_port) { - if (j->transfer_bstrap) { - job_assumes(j, launchd_mport_deallocate(j->bs_port) == KERN_SUCCESS); - } else { - job_assumes(j, launchd_mport_close_recv(j->bs_port) == KERN_SUCCESS); - } + if (j->j_port) { + job_assumes(j, launchd_mport_deallocate(j->j_port) == KERN_SUCCESS); + job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS); } - if (j->req_port) { - job_assumes(j, launchd_mport_deallocate(j->req_port) == KERN_SUCCESS); - } - #if 0 if (j->wait_reply_port) { } #endif - while ((ji = SLIST_FIRST(&j->jobs))) { - job_remove(ji); - } while ((sg = SLIST_FIRST(&j->sockets))) { socketgroup_delete(j, sg); } @@ -675,7 +716,7 @@ } if (global_on_demand_cnt == 0) { - job_dispatch_all(root_job); + jobmgr_dispatch_all(root_jobmgr); } return true; @@ -686,7 +727,7 @@ { mach_msg_size_t mxmsgsz; - if (!job_assumes(j, launchd_mport_create_recv(&j->bs_port) == KERN_SUCCESS)) { + if (!job_assumes(j, launchd_mport_create_recv(&j->j_port) == KERN_SUCCESS)) { goto out_bad; } @@ -696,62 +737,62 @@ mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize; } - if (!job_assumes(j, runtime_add_mport(j->bs_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) { + if (!job_assumes(j, runtime_add_mport(j->j_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) { goto out_bad2; } return true; out_bad2: - job_assumes(j, launchd_mport_close_recv(j->bs_port) == KERN_SUCCESS); + job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS); out_bad: return false; } job_t -job_new_via_mach_init(job_t jbs, const char *cmd, uid_t uid, bool ond) +job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond) { const char **argv = (const char **)mach_cmd2argv(cmd); - job_t j = NULL; + job_t jr = NULL; char buf[1000]; - if (!job_assumes(jbs, argv != NULL)) { + if (!job_assumes(j, argv != NULL)) { goto out_bad; } /* preflight the string so we know how big it is */ snprintf(buf, sizeof(buf), "%s.%s", sizeof(void *) == 8 ? "0xdeadbeeffeedface" : "0xbabecafe", basename((char *)argv[0])); - j = job_new(jbs, buf, NULL, argv, NULL, MACH_PORT_NULL); + jr = job_new(j->mgr, buf, NULL, argv, NULL); - snprintf(j->label, strlen(j->label) + 1, "%p.%s", j, basename(j->argv[0])); - free(argv); - if (!job_assumes(jbs, j != NULL)) { + if (!job_assumes(j, jr != NULL)) { goto out_bad; } - j->mach_uid = uid; - j->ondemand = ond; - j->legacy_mach_job = true; - j->priv_port_has_senders = true; /* the IPC that called us will make-send on this port */ + snprintf(jr->label, strlen(jr->label) + 1, "%p.%s", jr, basename(jr->argv[0])); - if (!job_setup_machport(j)) { + jr->mach_uid = uid; + jr->ondemand = ond; + jr->legacy_mach_job = true; + jr->priv_port_has_senders = true; /* the IPC that called us will make-send on this port */ + + if (!job_setup_machport(jr)) { goto out_bad; } - if (!job_assumes(j, launchd_mport_notify_req(j->bs_port, MACH_NOTIFY_NO_SENDERS) == KERN_SUCCESS)) { - job_assumes(j, launchd_mport_close_recv(j->bs_port) == KERN_SUCCESS); + if (!job_assumes(jr, launchd_mport_notify_req(jr->j_port, MACH_NOTIFY_NO_SENDERS) == KERN_SUCCESS)) { + job_assumes(jr, launchd_mport_close_recv(jr->j_port) == KERN_SUCCESS); goto out_bad; } - job_log(j, LOG_INFO, "Legacy%s server created in bootstrap: %x", ond ? " on-demand" : "", jbs->bs_port); + job_log(jr, LOG_INFO, "Legacy%s server created", ond ? " on-demand" : ""); - return j; + return jr; out_bad: - if (j) { - job_remove(j); + if (jr) { + job_remove(jr); } return NULL; } @@ -774,12 +815,12 @@ { job_t jr; - if ((jr = job_find(j, label)) != NULL) { + if ((jr = jobmgr_find(j->mgr, label)) != NULL) { errno = EEXIST; return NULL; } - jr = job_new(j, label, path, argv, NULL, MACH_PORT_NULL); + jr = job_new(j->mgr, label, path, argv, NULL); if (!jr) { return NULL; @@ -822,56 +863,42 @@ } job_t -job_find_anonymous(job_t p) +jobmgr_new_anonymous(jobmgr_t jm) { - job_t ji = NULL; - - SLIST_FOREACH(ji, &p->jobs, sle) { - if (ji->anonymous) { - break; - } - } - - return ji; -} - -bool -job_new_anonymous(job_t p) -{ char newlabel[1000], *procname = "unknown"; job_t jr; - snprintf(newlabel, sizeof(newlabel), "%u.anonymous", MACH_PORT_INDEX(p->bs_port)); + snprintf(newlabel, sizeof(newlabel), "%u.anonymous", MACH_PORT_INDEX(jm->jm_port)); - if ((jr = job_new(p, newlabel, procname, NULL, NULL, MACH_PORT_NULL))) { + if ((jr = job_new(jm, newlabel, procname, NULL, NULL))) { jr->anonymous = true; } - return jr ? true : false; + return jr; } job_t -job_new(job_t p, const char *label, const char *prog, const char *const *argv, const char *stdinpath, mach_port_t reqport) +job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *argv, const char *stdinpath) { const char *const *argv_tmp = argv; char *co; int i, cc = 0; job_t j; - if (reqport == MACH_PORT_NULL && prog == NULL && argv == NULL) { + if (prog == NULL && argv == NULL) { errno = EINVAL; return NULL; } j = calloc(1, sizeof(struct job_s) + strlen(label) + 1); - if (!job_assumes(p, j != NULL)) { + if (!jobmgr_assumes(jm, j != NULL)) { return NULL; } strcpy(j->label, label); j->kqjob_callback = job_callback; - j->parent = p ? job_get_bs(p) : NULL; + j->mgr = jm; j->min_run_time = LAUNCHD_MIN_JOB_RUN_TIME; j->timeout = LAUNCHD_ADVISABLE_IDLE_TIMEOUT; j->currently_ignored = true; @@ -879,13 +906,6 @@ j->checkedin = true; j->firstborn = (strcmp(label, FIRSTBORN_LABEL) == 0); - if (reqport != MACH_PORT_NULL) { - j->req_port = reqport; - if (!job_assumes(j, launchd_mport_notify_req(reqport, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS)) { - goto out_bad; - } - } - if (prog) { j->prog = strdup(prog); if (!job_assumes(j, j->prog != NULL)) { @@ -923,11 +943,10 @@ j->argv[i] = NULL; } - if (j->parent) { - SLIST_INSERT_HEAD(&j->parent->jobs, j, sle); - job_log(j->parent, LOG_DEBUG, "Conceived"); - } + SLIST_INSERT_HEAD(&jm->jobs, j, sle); + job_log(j, LOG_DEBUG, "Conceived"); + return j; out_bad: @@ -1373,7 +1392,7 @@ if (label == NULL) { errno = EINVAL; return NULL; - } else if ((j = job_find(root_job, label)) != NULL) { + } else if ((j = jobmgr_find(root_jobmgr, label)) != NULL) { errno = EEXIST; return NULL; } else if (label[0] == '\0' || (strncasecmp(label, "", strlen("com.apple.launchd")) == 0) || @@ -1394,7 +1413,7 @@ argv[i] = NULL; } - if ((j = job_new(root_job, label, prog, argv, NULL, MACH_PORT_NULL))) { + if ((j = job_new(root_jobmgr, label, prog, argv, NULL))) { launch_data_dict_iterate(pload, job_import_keys, j); } @@ -1402,21 +1421,20 @@ } job_t -job_find(job_t j, const char *label) +jobmgr_find(jobmgr_t jm, const char *label) { - job_t jr, ji; + jobmgr_t jmi; + job_t ji; - if (label[0] == '\0') { - return root_job; + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + if ((ji = jobmgr_find(jmi, label))) { + return ji; + } } - if (strcmp(j->label, label) == 0) { - return j; - } - - SLIST_FOREACH(ji, &j->jobs, sle) { - if ((jr = job_find(ji, label))) { - return jr; + SLIST_FOREACH(ji, &jm->jobs, sle) { + if (strcmp(ji->label, label) == 0) { + return ji; } } @@ -1425,72 +1443,77 @@ } job_t -job_find_by_pid(job_t j, pid_t p, bool recurse) +job_mig_intran2(jobmgr_t jm, mach_port_t p) { - job_t jr, ji; + job_t ji; + jobmgr_t jmi; - if (j->p == p) { - return j; - } + if (jm->jm_port == p) { + struct ldcred ldc; - SLIST_FOREACH(ji, &j->jobs, sle) { - if (ji->p == p) { - return ji; - } else if (recurse && (jr = job_find_by_pid(ji, p, recurse))) { - return jr; + runtime_get_caller_creds(&ldc); + + SLIST_FOREACH(ji, &jm->jobs, sle) { + if (ji->p == ldc.pid) { + /* This is just a MRU perfomance hack */ + SLIST_REMOVE(&jm->jobs, ji, job_s, sle); + SLIST_INSERT_HEAD(&jm->jobs, ji, sle); + return ji; + } } + + return jm->anonj; } - errno = ESRCH; - return NULL; -} + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + job_t jr; -static job_t -job_find_by_port2(job_t j, mach_port_t p) -{ - struct machservice *ms; - job_t jr, ji; - - if (j->bs_port == p) { - return j; - } - - SLIST_FOREACH(ms, &j->machservices, sle) { - if (ms->port == p) { - return j; + if ((jr = job_mig_intran2(jmi, p))) { + return jr; } } - SLIST_FOREACH(ji, &j->jobs, sle) { - if ((jr = job_find_by_port2(ji, p))) { - return jr; + SLIST_FOREACH(ji, &jm->jobs, sle) { + if (ji->j_port == p) { + return ji; } } - errno = ESRCH; return NULL; } job_t job_mig_intran(mach_port_t p) { - struct ldcred ldc; - job_t jp, jr = NULL; + job_t jr = job_mig_intran2(root_jobmgr, p); - runtime_get_caller_creds(&ldc); + launchd_assumes(jr != NULL); - if (launchd_assumes((jp = job_find_by_port2(root_job, p)) != NULL)) { - if (jp->req_port) { - if (!(jr = job_find_by_pid(jp, ldc.pid, false))) { - jr = job_find_anonymous(jp); + return jr; +} + +job_t +jobmgr_find_by_service_port(jobmgr_t jm, mach_port_t p) +{ + struct machservice *ms; + jobmgr_t jmi; + job_t ji, jr; + + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + if ((jr = jobmgr_find_by_service_port(jmi, p))) { + return jr; + } + } + + SLIST_FOREACH(ji, &jm->jobs, sle) { + SLIST_FOREACH(ms, &ji->machservices, sle) { + if (ms->port == p) { + return ji; } - } else { - jr = jp; } - job_assumes(jp, jr != NULL); } - return jr; + return NULL; } void @@ -1499,17 +1522,22 @@ } void -job_export_all2(job_t j, launch_data_t where) +job_export_all2(jobmgr_t jm, launch_data_t where) { - launch_data_t tmp; + jobmgr_t jmi; job_t ji; - if (job_assumes(j, (tmp = job_export2(j, false)) != NULL)) { - launch_data_dict_insert(where, tmp, j->label); + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + job_export_all2(jmi, where); } - SLIST_FOREACH(ji, &j->jobs, sle) - job_export_all2(ji, where); + SLIST_FOREACH(ji, &jm->jobs, sle) { + launch_data_t tmp; + + if (jobmgr_assumes(jm, (tmp = job_export2(ji, false)) != NULL)) { + launch_data_dict_insert(where, tmp, ji->label); + } + } } launch_data_t @@ -1517,7 +1545,9 @@ { launch_data_t resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); - job_export_all2(root_job, resp); + if (launchd_assumes(resp != NULL)) { + job_export_all2(root_jobmgr, resp); + } return resp; } @@ -1581,15 +1611,18 @@ } void -job_dispatch_all(job_t j) +jobmgr_dispatch_all(jobmgr_t jm) { + jobmgr_t jmi; job_t ji; - SLIST_FOREACH(ji, &j->jobs, sle) { - job_dispatch_all(ji); + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + jobmgr_dispatch_all(jmi); } - job_dispatch(j, false); + SLIST_FOREACH(ji, &jm->jobs, sle) { + job_dispatch(ji, false); + } } void @@ -1688,14 +1721,10 @@ bool sipc = false; time_t td; - if (!job_assumes(j, j->req_port == MACH_PORT_NULL)) { + if (!job_assumes(j, j->mgr != NULL)) { return; } - if (!job_assumes(j, j->parent != NULL)) { - return; - } - if (job_active(j)) { job_log(j, LOG_DEBUG, "Already started"); return; @@ -1732,7 +1761,7 @@ time(&j->start_time); - switch (c = job_fork(j->parent)) { + switch (c = jobmgr_fork(j->mgr)) { case -1: job_log_error(j, LOG_ERR, "fork() failed, will try again in one second"); job_assumes(j, close(execspair[0]) == 0); @@ -1858,16 +1887,20 @@ exit(EXIT_FAILURE); } -void job_setup_env_from_other_jobs(job_t j) +void jobmgr_setup_env_from_other_jobs(jobmgr_t jm) { struct envitem *ei; job_t ji; - SLIST_FOREACH(ji, &j->jobs, sle) - job_setup_env_from_other_jobs(ji); + if (jm->parentmgr) { + jobmgr_setup_env_from_other_jobs(jm->parentmgr); + } - SLIST_FOREACH(ei, &j->global_env, sle) - setenv(ei->key, ei->value, 1); + SLIST_FOREACH(ji, &jm->jobs, sle) { + SLIST_FOREACH(ei, &ji->global_env, sle) { + setenv(ei->key, ei->value, 1); + } + } } void @@ -2019,7 +2052,7 @@ } } - job_setup_env_from_other_jobs(root_job); + jobmgr_setup_env_from_other_jobs(j->mgr); SLIST_FOREACH(ei, &j->env, sle) setenv(ei->key, ei->value, 1); @@ -2073,55 +2106,55 @@ } } -size_t -job_prep_log_preface(job_t j, char *buf) +static void +extract_rcsid_substr(const char *i, char *o, size_t osz) { - size_t lsz = strlen(j->label); - char newlabel[lsz * 2 + 1]; - size_t i, o, r = 0; + char *rcs_rev_tmp = strchr(i, ' '); - for (i = 0, o = 0; i < lsz; i++, o++) { - if (j->label[i] == '%') { - newlabel[o] = '%'; - o++; - newlabel[o] = '%'; - } else { - newlabel[o] = j->label[i]; + if (!rcs_rev_tmp) { + strlcpy(o, i, osz); + } else { + strlcpy(o, rcs_rev_tmp + 1, osz); + rcs_rev_tmp = strchr(o, ' '); + if (rcs_rev_tmp) { + *rcs_rev_tmp = '\0'; } } - newlabel[o] = '\0'; +} - if (j->parent) { - r = job_prep_log_preface(j->parent, buf); +void +jobmgr_log_bug(jobmgr_t jm, const char *rcs_rev, const char *path, unsigned int line, const char *test) +{ + int saved_errno = errno; + const char *file = strrchr(path, '/'); + char buf[100]; + + extract_rcsid_substr(rcs_rev, buf, sizeof(buf)); + + if (!file) { + file = path; + } else { + file += 1; } - return r + sprintf(buf + r, "%s%s", j->parent ? "/" : "", newlabel); + jobmgr_log(jm, LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test); } void job_log_bug(job_t j, const char *rcs_rev, const char *path, unsigned int line, const char *test) { int saved_errno = errno; + const char *file = strrchr(path, '/'); char buf[100]; - const char *file = strrchr(path, '/'); - char *rcs_rev_tmp = strchr(rcs_rev, ' '); + extract_rcsid_substr(rcs_rev, buf, sizeof(buf)); + if (!file) { file = path; } else { file += 1; } - if (!rcs_rev_tmp) { - strlcpy(buf, rcs_rev, sizeof(buf)); - } else { - strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf)); - rcs_rev_tmp = strchr(buf, ' '); - if (rcs_rev_tmp) { - *rcs_rev_tmp = '\0'; - } - } - job_log(j, LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test); } @@ -2130,7 +2163,6 @@ { char newmsg[10000]; int oldmask = 0; - size_t o; /* * Hack: If bootstrap_port is set, we must be on the child side of a @@ -2141,12 +2173,10 @@ return _vproc_logv(pri, err, msg, ap); } - o = job_prep_log_preface(j, newmsg); - if (err) { - snprintf(newmsg + o, sizeof(newmsg) - o, ": %s: %s", msg, strerror(err)); + snprintf(newmsg, sizeof(newmsg), "%s: %s: %s", j->label, msg, strerror(err)); } else { - snprintf(newmsg + o, sizeof(newmsg) - o, ": %s", msg); + snprintf(newmsg, sizeof(newmsg), "%s: %s", j->label, msg); } if (j->debug) { @@ -2180,6 +2210,36 @@ va_end(ap); } +#if 0 +void +jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + jobmgr_logv(jm, pri, errno, msg, ap); + va_end(ap); +} +#endif + +void +jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + jobmgr_logv(jm, pri, 0, msg, ap); + va_end(ap); +} + +void +jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap) +{ + if (launchd_assumes(jm->anonj)) { + job_logv(jm->anonj, pri, err, msg, ap); + } +} + bool watchpath_new(job_t j, const char *name, bool qdir) { @@ -2657,7 +2717,7 @@ /* Maybe another job has the inverse path based semaphore as this job */ if (dispatch_others) { - job_dispatch_all_other_semaphores(root_job, j); + jobmgr_dispatch_all_other_semaphores(root_jobmgr, j); } return false; @@ -2684,10 +2744,6 @@ return true; } - if (j->req_port) { - return true; - } - if (j->p) { return true; } @@ -2708,25 +2764,25 @@ pid_t launchd_fork(void) { - return job_fork(root_job); + return jobmgr_fork(root_jobmgr); } pid_t -job_fork(job_t j) +jobmgr_fork(jobmgr_t jm) { - mach_port_t p = j->bs_port; + mach_port_t p = jm->jm_port; pid_t r = -1; sigprocmask(SIG_BLOCK, &blocked_signals, NULL); - job_assumes(j, launchd_mport_make_send(p) == KERN_SUCCESS); - job_assumes(j, launchd_set_bport(p) == KERN_SUCCESS); - job_assumes(j, launchd_mport_deallocate(p) == KERN_SUCCESS); + jobmgr_assumes(jm, launchd_mport_make_send(p) == KERN_SUCCESS); + jobmgr_assumes(jm, launchd_set_bport(p) == KERN_SUCCESS); + jobmgr_assumes(jm, launchd_mport_deallocate(p) == KERN_SUCCESS); r = fork(); if (r != 0) { - job_assumes(j, launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS); + jobmgr_assumes(jm, launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS); } else if (r == 0) { size_t i; @@ -2881,7 +2937,7 @@ struct machservice *ms; mach_port_t p = MACH_PORT_NULL; - if ((ms = job_lookup_service(j->parent, key, false))) { + if ((ms = jobmgr_lookup_service(j->mgr, key, false))) { job_log(j, LOG_WARNING, "Conflict with job: %s over Mach service: %s", ms->job->label, key); return; } @@ -2898,10 +2954,10 @@ } } -job_t -job_parent(job_t j) +jobmgr_t +jobmgr_parent(jobmgr_t jm) { - return j->parent; + return jm->parentmgr; } void @@ -2920,60 +2976,42 @@ } } -void -job_foreach_service(job_t j, void (*bs_iter)(struct machservice *, void *), void *context, bool only_anonymous) +jobmgr_t +jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t checkin_port) { - struct machservice *ms; - job_t ji; - - j = job_get_bs(j); - - SLIST_FOREACH(ji, &j->jobs, sle) { - if (ji->req_port) { - continue; - } else if (only_anonymous && !ji->anonymous) { - continue; - } - - SLIST_FOREACH(ms, &ji->machservices, sle) { - bs_iter(ms, context); - } - } - - if (!job_assumes(j, SLIST_EMPTY(&j->machservices))) { - SLIST_FOREACH(ms, &j->machservices, sle) { - bs_iter(ms, context); - } - } -} - -job_t -job_new_bootstrap(job_t p, mach_port_t requestorport, mach_port_t checkin_port) -{ - char bslabel[1024] = "100000"; mach_msg_size_t mxmsgsz; - job_t j; + jobmgr_t jmr; if (requestorport == MACH_PORT_NULL) { - if (p) { - job_log(p, LOG_ERR, "Mach sub-bootstrap create request requires a requester port"); + if (jm) { + jobmgr_log(jm, LOG_ERR, "Mach sub-bootstrap create request requires a requester port"); } return NULL; } - j = job_new(p, bslabel, NULL, NULL, NULL, requestorport); + jmr = calloc(1, sizeof(struct jobmgr_s) + strlen("100000") + 1); - if (j == NULL) { + if (jmr == NULL) { return NULL; } + jmr->req_port = requestorport; + + if ((jmr->parentmgr = jm)) { + SLIST_INSERT_HEAD(&jm->submgrs, jmr, sle); + } + + if (!jobmgr_assumes(jmr, launchd_mport_notify_req(jmr->req_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS)) { + goto out_bad; + } + if (checkin_port != MACH_PORT_NULL) { - j->bs_port = checkin_port; - } else if (!job_assumes(j, launchd_mport_create_recv(&j->bs_port) == KERN_SUCCESS)) { + jmr->jm_port = checkin_port; + } else if (!jobmgr_assumes(jmr, launchd_mport_create_recv(&jmr->jm_port) == KERN_SUCCESS)) { goto out_bad; } - snprintf(j->label, strlen(j->label) + 1, "%d", MACH_PORT_INDEX(j->bs_port)); + snprintf(jmr->name, strlen(jmr->name) + 1, "%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); @@ -2981,29 +3019,32 @@ mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize; } - if (!job_assumes(j, runtime_add_mport(j->bs_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) { + if (!jobmgr_assumes(jmr, runtime_add_mport(jmr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) { goto out_bad; } - if (p) { - job_log(p, LOG_DEBUG, "Mach sub-bootstrap created: %s", j->label); + if (jm) { + jobmgr_log(jm, LOG_DEBUG, "Mach sub-bootstrap created: %s", jmr->name); } - job_assumes(j, job_new_anonymous(j)); + jmr->anonj = jobmgr_new_anonymous(jmr); - return j; + jobmgr_assumes(jmr, jmr->anonj != NULL); + return jmr; + out_bad: - if (j) { - job_remove(j); + if (jmr) { + jobmgr_remove(jmr); } return NULL; } void -job_delete_anything_with_port(job_t j, mach_port_t port) +jobmgr_delete_anything_with_port(jobmgr_t jm, mach_port_t port) { struct machservice *ms, *next_ms; + jobmgr_t jmi, jmn; job_t ji, jn; /* Mach ports, unlike Unix descriptors, are reference counted. In other @@ -3016,56 +3057,42 @@ * to use. */ - SLIST_FOREACH_SAFE(ji, &j->jobs, sle, jn) { - job_delete_anything_with_port(ji, port); + if (jm->req_port == port) { + if (jm == root_jobmgr) { + launchd_shutdown(); + } else { + return jobmgr_remove(jm); + } } - SLIST_FOREACH_SAFE(ms, &j->machservices, sle, next_ms) { - if (ms->port == port) { - machservice_delete(ms); - } + SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) { + jobmgr_delete_anything_with_port(jmi, port); } - if (j->req_port == port) { - if (j == root_job) { - launchd_shutdown(); - } else { - return job_remove(j); + SLIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) { + SLIST_FOREACH_SAFE(ms, &ji->machservices, sle, next_ms) { + if (ms->port == port) { + machservice_delete(ms); + } } } } struct machservice * -job_lookup_service(job_t j, const char *name, bool check_parent) +jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent) { struct machservice *ms; job_t ji; - j = job_get_bs(j); - - SLIST_FOREACH(ji, &j->jobs, sle) { - if (ji->req_port) { - continue; - } - + SLIST_FOREACH(ji, &jm->jobs, sle) { SLIST_FOREACH(ms, &ji->machservices, sle) { if (strcmp(name, ms->name) == 0) { - if (ji->parent) { - SLIST_REMOVE(&ji->parent->jobs, ji, job_s, sle); - SLIST_INSERT_HEAD(&ji->parent->jobs, ji, sle); - } return ms; } } } - SLIST_FOREACH(ms, &j->machservices, sle) { - if (strcmp(name, ms->name) == 0) { - return ms; - } - } - - if (j->parent == NULL) { + if (jm->parentmgr == NULL) { return NULL; } @@ -3073,7 +3100,7 @@ return NULL; } - return job_lookup_service(j->parent, name, true); + return jobmgr_lookup_service(jm->parentmgr, name, true); } mach_port_t @@ -3202,38 +3229,37 @@ } bool -job_ack_port_destruction(job_t j, mach_port_t p) +jobmgr_ack_port_destruction(jobmgr_t jm, mach_port_t p) { + struct machservice *ms = NULL; + jobmgr_t jmi; job_t ji; - struct machservice *ms; - SLIST_FOREACH(ji, &j->jobs, sle) { - if (job_ack_port_destruction(ji, p)) { + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + if (jobmgr_ack_port_destruction(jmi, p)) { return true; } } - SLIST_FOREACH(ms, &j->machservices, sle) { - if (ms->port == p) { - break; - } - } + SLIST_FOREACH(ji, &jm->jobs, sle) { + SLIST_FOREACH(ms, &ji->machservices, sle) { + if (ms->port != p) { + continue; + } - if (ms == NULL) { - return false; - } + ms->isActive = false; - ms->isActive = false; + if (ms->reset) { + machservice_resetport(ji, ms); + } - if (ms->reset) { - machservice_resetport(j, ms); + job_log(ji, LOG_DEBUG, "Receive right returned to us: %s", ms->name); + job_dispatch(ji, false); + return true; + } } - job_log(j, LOG_DEBUG, "Receive right returned to us: %s", ms->name); - - job_dispatch(j, false); - - return true; + return false; } void @@ -3241,8 +3267,8 @@ { j->priv_port_has_senders = false; - job_assumes(j, launchd_mport_close_recv(j->bs_port) == KERN_SUCCESS); - j->bs_port = 0; + job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS); + j->j_port = 0; job_log(j, LOG_DEBUG, "No more senders on privileged Mach bootstrap port"); @@ -3250,31 +3276,21 @@ } mach_port_t -job_get_reqport(job_t j) +jobmgr_get_reqport(jobmgr_t jm) { - j->transfer_bstrap = true; - gc_this_job = j; + jm->transfer_bstrap = true; + gc_this_jobmgr = jm; - return j->req_port; + return jm->req_port; } -mach_port_t -job_get_bsport(job_t j) -{ - return j->bs_port; -} - -job_t +jobmgr_t job_get_bs(job_t j) { - if (j->req_port) { - return j; + if (job_assumes(j, j->mgr != NULL)) { + return j->mgr; } - if (job_assumes(j, j->parent != NULL)) { - return j->parent; - } - return NULL; } @@ -3369,20 +3385,20 @@ } void -job_dispatch_all_other_semaphores(job_t j, job_t nj) +jobmgr_dispatch_all_other_semaphores(jobmgr_t jm, job_t nj) { + jobmgr_t jmi; job_t ji, jn; - if (j == nj) { - return; - } - SLIST_FOREACH_SAFE(ji, &j->jobs, sle, jn) { - job_dispatch_all_other_semaphores(ji, nj); + SLIST_FOREACH(jmi, &jm->submgrs, sle) { + jobmgr_dispatch_all_other_semaphores(jmi, nj); } - if (!SLIST_EMPTY(&j->semaphores)) { - job_dispatch(j, false); + SLIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) { + if (ji != nj && !SLIST_EMPTY(&ji->semaphores)) { + job_dispatch(ji, false); + } } } @@ -3564,6 +3580,10 @@ struct ldcred ldc; job_t js; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + runtime_get_caller_creds(&ldc); job_log(j, LOG_DEBUG, "Server create attempt: %s", server_cmd); @@ -3593,7 +3613,7 @@ return BOOTSTRAP_NO_MEMORY; } - *server_portp = job_get_bsport(js); + *server_portp = js->j_port; return BOOTSTRAP_SUCCESS; } @@ -3602,6 +3622,10 @@ { kern_return_t kr = 0; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + switch (key) { case LAST_EXIT_STATUS: *val = j->last_exit_status; @@ -3619,6 +3643,10 @@ { kern_return_t kr = 0; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + switch (key) { case GLOBAL_ON_DEMAND: kr = job_set_global_on_demand(j, (bool)val) ? 0 : 1; @@ -3634,6 +3662,10 @@ kern_return_t job_mig_getsocket(job_t j, name_t spr) { + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + if (!sockpath) { return BOOTSTRAP_NO_MEMORY; } else if (getpid() == 1) { @@ -3648,6 +3680,10 @@ kern_return_t job_mig_log(job_t j, int pri, int err, logmsg_t msg) { + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + if ((errno = err)) { job_log_error(j, pri, "%s", msg); } else { @@ -3661,11 +3697,11 @@ job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont) { struct ldcred ldc; - job_t ji, jbs = root_job; + job_t ji; -#if 0 - jbs = job_get_bs(j); -#endif + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } runtime_get_caller_creds(&ldc); @@ -3679,7 +3715,7 @@ *up_cont = MACH_PORT_NULL; - SLIST_FOREACH(ji, &jbs->jobs, sle) { + SLIST_FOREACH(ji, &root_jobmgr->jobs, sle) { if (ji->mach_uid != which_user) { continue; } @@ -3698,7 +3734,7 @@ sprintf(lbuf, "com.apple.launchd.peruser.%u", which_user); - ji = job_new(jbs, lbuf, "/sbin/launchd", NULL, NULL, 0); + ji = job_new(root_jobmgr, lbuf, "/sbin/launchd", NULL, NULL); if (ji == NULL) { return BOOTSTRAP_NO_MEMORY; @@ -3729,9 +3765,13 @@ struct machservice *ms; struct ldcred ldc; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + runtime_get_caller_creds(&ldc); - ms = job_lookup_service(j, servicename, true); + ms = jobmgr_lookup_service(j->mgr, servicename, true); if (ms == NULL) { job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Unknown: %s", servicename); @@ -3764,6 +3804,10 @@ struct machservice *ms; struct ldcred ldc; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + runtime_get_caller_creds(&ldc); #if 0 @@ -3777,7 +3821,7 @@ * 92) is a rogue application (not our UID, not root and not a child of * us). We'll have to reconcile this design friction at a later date. */ - if (j->anonymous && job_get_bs(j)->parent == NULL && ldc.uid != 0 && ldc.uid != getuid() && ldc.uid != 92) { + if (j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.uid != getuid() && ldc.uid != 92) { if (getpid() == 1) { return VPROC_ERR_TRY_PER_USER; } else { @@ -3785,7 +3829,7 @@ } } - ms = job_lookup_service(j, servicename, false); + ms = jobmgr_lookup_service(j->mgr, servicename, false); if (ms) { if (machservice_job(ms) != j) { @@ -3816,13 +3860,17 @@ struct machservice *ms; struct ldcred ldc; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + runtime_get_caller_creds(&ldc); - if (getpid() == 1 && j->anonymous && job_get_bs(j)->parent == NULL && ldc.uid != 0 && ldc.euid != 0) { + if (getpid() == 1 && j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.euid != 0) { return VPROC_ERR_TRY_PER_USER; } - ms = job_lookup_service(j, servicename, true); + ms = jobmgr_lookup_service(j->mgr, servicename, true); if (ms && machservice_hidden(ms) && !job_active(machservice_job(ms))) { ms = NULL; @@ -3849,16 +3897,19 @@ kern_return_t job_mig_parent(job_t j, mach_port_t *parentport, mach_msg_type_name_t *pptype) { + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + job_log(j, LOG_DEBUG, "Requested parent bootstrap port"); + jobmgr_t jm = j->mgr; - j = job_get_bs(j); - *pptype = MACH_MSG_TYPE_MAKE_SEND; - if (job_parent(j)) { - *parentport = job_get_bsport(job_parent(j)); + if (jobmgr_parent(jm)) { + *parentport = jobmgr_parent(jm)->jm_port; } else if (MACH_PORT_NULL == inherited_bootstrap_port) { - *parentport = job_get_bsport(j); + *parentport = jm->jm_port; } else { *pptype = MACH_MSG_TYPE_COPY_SEND; *parentport = inherited_bootstrap_port; @@ -3866,76 +3917,61 @@ return BOOTSTRAP_SUCCESS; } -static void -job_mig_info_countservices(struct machservice *ms, void *context) -{ - unsigned int *cnt = context; - - (*cnt)++; -} - -struct x_bootstrap_info_copyservices_cb { - name_array_t service_names; - bootstrap_status_array_t service_actives; - mach_port_array_t ports; - unsigned int i; -}; - -static void -job_mig_info_copyservices(struct machservice *ms, void *context) -{ - struct x_bootstrap_info_copyservices_cb *info_resp = context; - - strlcpy(info_resp->service_names[info_resp->i], machservice_name(ms), sizeof(info_resp->service_names[0])); - - launchd_assumes(info_resp->service_actives || info_resp->ports); - - if (info_resp->service_actives) { - info_resp->service_actives[info_resp->i] = machservice_status(ms); - } else { - info_resp->ports[info_resp->i] = machservice_port(ms); - } - info_resp->i++; -} - 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) { - struct x_bootstrap_info_copyservices_cb info_resp = { NULL, NULL, NULL, 0 }; - unsigned int cnt = 0; + name_array_t service_names = NULL; + bootstrap_status_array_t service_actives = NULL; + unsigned int cnt = 0, cnt2 = 0; + struct machservice *ms; + jobmgr_t jm; job_t ji; - for (ji = j; ji; ji = job_parent(ji)) - job_foreach_service(ji, job_mig_info_countservices, &cnt, false); + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } - mig_allocate((vm_address_t *)&info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); - if (!launchd_assumes(info_resp.service_names != NULL)) { + jm = j->mgr; + + SLIST_FOREACH(ji, &jm->jobs, sle) { + SLIST_FOREACH(ms, &ji->machservices, sle) { + cnt++; + } + } + + mig_allocate((vm_address_t *)&service_names, cnt * sizeof(service_names[0])); + if (!launchd_assumes(service_names != NULL)) { goto out_bad; } - mig_allocate((vm_address_t *)&info_resp.service_actives, cnt * sizeof(info_resp.service_actives[0])); - if (!launchd_assumes(info_resp.service_actives != NULL)) { + mig_allocate((vm_address_t *)&service_actives, cnt * sizeof(service_actives[0])); + if (!launchd_assumes(service_actives != NULL)) { goto out_bad; } - for (ji = j; ji; ji = job_parent(ji)) - job_foreach_service(ji, job_mig_info_copyservices, &info_resp, false); + SLIST_FOREACH(ji, &jm->jobs, sle) { + SLIST_FOREACH(ms, &ji->machservices, sle) { + strlcpy(service_names[cnt2], machservice_name(ms), sizeof(service_names[0])); + service_actives[cnt2] = machservice_status(ms); + cnt2++; + } + } - launchd_assumes(info_resp.i == cnt); + launchd_assumes(cnt == cnt2); - *servicenamesp = info_resp.service_names; - *serviceactivesp = info_resp.service_actives; + *servicenamesp = service_names; + *serviceactivesp = service_actives; *servicenames_cnt = *serviceactives_cnt = cnt; return BOOTSTRAP_SUCCESS; out_bad: - if (info_resp.service_names) { - mig_deallocate((vm_address_t)info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); + if (service_names) { + mig_deallocate((vm_address_t)service_names, cnt * sizeof(service_names[0])); } - if (info_resp.service_actives) { - mig_deallocate((vm_address_t)info_resp.service_actives, cnt * sizeof(info_resp.service_actives[0])); + if (service_actives) { + mig_deallocate((vm_address_t)service_actives, cnt * sizeof(service_actives[0])); } return BOOTSTRAP_NO_MEMORY; @@ -3944,47 +3980,61 @@ kern_return_t job_mig_transfer_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright, name_array_t *servicenamesp, unsigned int *servicenames_cnt, - mach_port_array_t *ports, unsigned int *ports_cnt) + mach_port_array_t *portsp, unsigned int *ports_cnt) { - struct x_bootstrap_info_copyservices_cb info_resp = { NULL, NULL, NULL, 0 }; - unsigned int cnt = 0; + name_array_t service_names = NULL; + mach_port_array_t ports = NULL; + unsigned int cnt = 0, cnt2 = 0; + struct machservice *ms; + jobmgr_t jm; - if (j->anonymous) { - j = j->parent; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; } + jm = j->mgr; + if (getpid() != 1) { job_log(j, LOG_ERR, "Only the system launchd will transfer Mach sub-bootstraps."); return BOOTSTRAP_NOT_PRIVILEGED; - } else if (!job_parent(j)) { + } else if (jobmgr_parent(jm) == NULL) { job_log(j, LOG_ERR, "Root Mach bootstrap cannot be transferred."); return BOOTSTRAP_NOT_PRIVILEGED; + } else if (!j->anonymous) { + job_log(j, LOG_ERR, "Only the anonymous job can transfer Mach sub-bootstraps."); + return BOOTSTRAP_NOT_PRIVILEGED; } job_log(j, LOG_DEBUG, "Transferring sub-bootstrap to the per session launchd."); - job_foreach_service(j, job_mig_info_countservices, &cnt, true); + SLIST_FOREACH(ms, &j->machservices, sle) { + cnt++; + } - mig_allocate((vm_address_t *)&info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); - if (!launchd_assumes(info_resp.service_names != NULL)) { + mig_allocate((vm_address_t *)&service_names, cnt * sizeof(service_names[0])); + if (!launchd_assumes(service_names != NULL)) { goto out_bad; } - mig_allocate((vm_address_t *)&info_resp.ports, cnt * sizeof(info_resp.ports[0])); - if (!launchd_assumes(info_resp.ports != NULL)) { + mig_allocate((vm_address_t *)&ports, cnt * sizeof(ports[0])); + if (!launchd_assumes(ports != NULL)) { goto out_bad; } - job_foreach_service(j, job_mig_info_copyservices, &info_resp, true); + SLIST_FOREACH(ms, &j->machservices, sle) { + strlcpy(service_names[cnt2], machservice_name(ms), sizeof(service_names[0])); + ports[cnt2] = machservice_port(ms); + cnt2++; + } - launchd_assumes(info_resp.i == cnt); + launchd_assumes(cnt == cnt2); - *servicenamesp = info_resp.service_names; - *ports = info_resp.ports; + *servicenamesp = service_names; + *portsp = ports; *servicenames_cnt = *ports_cnt = cnt; - *reqport = job_get_reqport(j); - *rcvright = job_get_bsport(j); + *reqport = jobmgr_get_reqport(jm); + *rcvright = jm->jm_port; launchd_assumes(runtime_remove_mport(*rcvright) == KERN_SUCCESS); @@ -3993,11 +4043,11 @@ return BOOTSTRAP_SUCCESS; out_bad: - if (info_resp.service_names) { - mig_deallocate((vm_address_t)info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); + if (service_names) { + mig_deallocate((vm_address_t)service_names, cnt * sizeof(service_names[0])); } - if (info_resp.ports) { - mig_deallocate((vm_address_t)info_resp.ports, cnt * sizeof(info_resp.ports[0])); + if (ports) { + mig_deallocate((vm_address_t)ports, cnt * sizeof(ports[0])); } return BOOTSTRAP_NO_MEMORY; @@ -4007,9 +4057,15 @@ job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp) { int bsdepth = 0; - job_t js = j; + jobmgr_t jmr; - while ((js = job_parent(js)) != NULL) { + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + + jmr = j->mgr; + + while ((jmr = jobmgr_parent(jmr)) != NULL) { bsdepth++; } @@ -4019,14 +4075,14 @@ return BOOTSTRAP_NO_MEMORY; } - if ((js = job_new_bootstrap(j, requestorport, MACH_PORT_NULL)) == NULL) { + if ((jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL)) == NULL) { if (requestorport == MACH_PORT_NULL) { return BOOTSTRAP_NOT_PRIVILEGED; } return BOOTSTRAP_NO_MEMORY; } - *subsetportp = job_get_bsport(js); + *subsetportp = jmr->jm_port; return BOOTSTRAP_SUCCESS; } @@ -4035,6 +4091,10 @@ { struct machservice *ms; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + if (job_prog(j)[0] == '\0') { job_log(j, LOG_ERR, "Mach service creation requires a target server: %s", servicename); return BOOTSTRAP_NOT_PRIVILEGED; @@ -4045,7 +4105,7 @@ return BOOTSTRAP_NOT_PRIVILEGED; } - ms = job_lookup_service(j, servicename, false); + ms = jobmgr_lookup_service(j->mgr, servicename, false); if (ms) { job_log(j, LOG_DEBUG, "Mach service creation attempt for failed. Already exists: %s", servicename); return BOOTSTRAP_NAME_IN_USE; @@ -4070,6 +4130,9 @@ kern_return_t job_mig_wait(job_t j, mach_port_t srp, integer_t *waitstatus) { + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } #if 0 struct ldcred ldc; runtime_get_caller_creds(&ldc); @@ -4080,8 +4143,8 @@ kern_return_t job_mig_uncork_fork(job_t j) { - if (!j) { - return BOOTSTRAP_NOT_PRIVILEGED; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; } job_uncork_fork(j); @@ -4103,6 +4166,10 @@ const char *workingdir = NULL; size_t argv_i = 0, env_i = 0; + if (!launchd_assumes(j != NULL)) { + return BOOTSTRAP_NO_MEMORY; + } + #if 0 if (ldc.asid != inherited_asid) { job_log(j, LOG_ERR, "Security: PID %d (ASID %d) was denied a request to spawn a process in this session (ASID %d)", @@ -4141,7 +4208,7 @@ } } - jr = job_new_spawn(job_get_bs(j), label, path, workingdir, argv, env, flags & SPAWN_HAS_UMASK ? &mig_umask : NULL, + jr = job_new_spawn(j, label, path, workingdir, argv, env, flags & SPAWN_HAS_UMASK ? &mig_umask : NULL, flags & SPAWN_WANTS_WAIT4DEBUGGER, flags & SPAWN_WANTS_FORCE_PPC); if (jr == NULL) switch (errno) { @@ -4161,7 +4228,7 @@ flags & SPAWN_WANTS_WAIT4DEBUGGER ? " stopped": ""); *child_pid = job_get_pid(jr); - *obsvr_port = job_get_bsport(jr); + *obsvr_port = jr->j_port; return BOOTSTRAP_SUCCESS; } @@ -4208,15 +4275,22 @@ { mach_msg_type_number_t l2l_i; auditinfo_t inherited_audit; - job_t anon_job; + job_t ji, anon_job = NULL; getaudit(&inherited_audit); inherited_asid = inherited_audit.ai_asid; - launchd_assert((root_job = job_new_bootstrap(NULL, req_port ? req_port : mach_task_self(), checkin_port)) != NULL); + launchd_assert((root_jobmgr = jobmgr_new(NULL, req_port ? req_port : mach_task_self(), checkin_port)) != NULL); - launchd_assert((anon_job = job_find_anonymous(root_job)) != NULL); + SLIST_FOREACH(ji, &root_jobmgr->jobs, sle) { + if (ji->anonymous) { + anon_job = ji; + break; + } + } + launchd_assert(anon_job != NULL); + launchd_assert(launchd_get_bport(&inherited_bootstrap_port) == KERN_SUCCESS); if (getpid() != 1) { Modified: trunk/launchd/src/launchd_core_logic.h =================================================================== --- trunk/launchd/src/launchd_core_logic.h 2006-11-13 17:54:12 UTC (rev 22942) +++ trunk/launchd/src/launchd_core_logic.h 2006-11-13 20:53:23 UTC (rev 22943) @@ -24,28 +24,33 @@ #include "liblaunch_public.h" typedef struct job_s *job_t; +typedef struct jobmgr_s *jobmgr_t; -extern job_t root_job; -extern job_t gc_this_job; +extern jobmgr_t root_jobmgr; +extern jobmgr_t gc_this_jobmgr; extern size_t total_children; -job_t job_new(job_t p, const char *label, const char *prog, const char *const *argv, const char *stdinpath, mach_port_t); +void jobmgr_remove_all_inactive(jobmgr_t jm); +void jobmgr_dispatch_all_other_semaphores(jobmgr_t jm, job_t nj); +job_t jobmgr_find(jobmgr_t jm, const char *label); +void jobmgr_delete_anything_with_port(jobmgr_t jm, mach_port_t port); +bool jobmgr_ack_port_destruction(jobmgr_t jm, mach_port_t p); +void jobmgr_remove(jobmgr_t jm); +job_t jobmgr_find_by_service_port(jobmgr_t jm, mach_port_t p); + +launch_data_t job_export_all(void); + +job_t job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *argv, const char *stdinpath); void job_dispatch(job_t j, bool kickstart); bool job_active(job_t j); -void job_remove_all_inactive(job_t j); -void job_dispatch_all_other_semaphores(job_t j, job_t nj); launch_data_t job_export(job_t j); -launch_data_t job_export_all(void); void job_stop(job_t j); void job_checkin(job_t j); void job_remove(job_t j); -job_t job_find(job_t j, const char *label); job_t job_import(launch_data_t pload); launch_data_t job_import_bulk(launch_data_t pload); job_t job_mig_intran(mach_port_t mp); void job_mig_destructor(job_t j); -bool job_ack_port_destruction(job_t j, mach_port_t p); void job_ack_no_senders(job_t j); -void job_delete_anything_with_port(job_t jbs, mach_port_t port); #endif Modified: trunk/launchd/src/launchd_runtime.c =================================================================== --- trunk/launchd/src/launchd_runtime.c 2006-11-13 17:54:12 UTC (rev 22942) +++ trunk/launchd/src/launchd_runtime.c 2006-11-13 20:53:23 UTC (rev 22943) @@ -160,7 +160,7 @@ continue; } if (status.mps_msgcount) { - EV_SET(&kev, members[i], EVFILT_MACHPORT, 0, 0, 0, job_mig_intran(members[i])); + EV_SET(&kev, members[i], EVFILT_MACHPORT, 0, 0, 0, jobmgr_find_by_service_port(root_jobmgr, members[i])); (*((kq_callback *)kev.udata))(kev.udata, &kev); /* the callback may have tainted our ability to continue this for loop */ break; @@ -407,7 +407,7 @@ { /* This message is sent to us when a receive right is returned to us. */ - if (!job_ack_port_destruction(root_job, rights)) { + if (!jobmgr_ack_port_destruction(root_jobmgr, rights)) { launchd_assumes(launchd_mport_close_recv(rights) == KERN_SUCCESS); } @@ -466,7 +466,7 @@ inherited_bootstrap_port = MACH_PORT_NULL; } - job_delete_anything_with_port(root_job, name); + jobmgr_delete_anything_with_port(root_jobmgr, name); /* A dead-name notification about a port appears to increment the * rights on said port. Let's deallocate it so that we don't leak @@ -568,9 +568,9 @@ bufReply = bufTemp; /* XXX - So very gross */ - if (gc_this_job) { - job_remove(gc_this_job); - gc_this_job = NULL; + if (gc_this_jobmgr) { + jobmgr_remove(gc_this_jobmgr); + gc_this_jobmgr = NULL; } if (!(tmp_options & MACH_RCV_MSG)) { Modified: trunk/launchd/src/launchd_unix_ipc.c =================================================================== --- trunk/launchd/src/launchd_unix_ipc.c 2006-11-13 17:54:12 UTC (rev 22942) +++ trunk/launchd/src/launchd_unix_ipc.c 2006-11-13 20:53:23 UTC (rev 22943) @@ -398,19 +398,19 @@ launch_data_set_bool(resp, batch_disabler_count == 0); } } else if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) { - if ((j = job_find(root_job, launch_data_get_string(data))) != NULL) { + if ((j = jobmgr_find(root_jobmgr, launch_data_get_string(data))) != NULL) { job_dispatch(j, true); errno = 0; } resp = launch_data_new_errno(errno); } else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) { - if ((j = job_find(root_job, launch_data_get_string(data))) != NULL) { + if ((j = jobmgr_find(root_jobmgr, launch_data_get_string(data))) != NULL) { job_stop(j); errno = 0; } resp = launch_data_new_errno(errno); } else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) { - if ((j = job_find(root_job, launch_data_get_string(data))) != NULL) { + if ((j = jobmgr_find(root_jobmgr, launch_data_get_string(data))) != NULL) { job_remove(j); errno = 0; } @@ -433,14 +433,14 @@ } else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) { resp = adjust_rlimits(data); } else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) { - if ((j = job_find(root_job, launch_data_get_string(data))) == NULL) { + if ((j = jobmgr_find(root_jobmgr, launch_data_get_string(data))) == NULL) { resp = launch_data_new_errno(errno); } else { resp = job_export(j); ipc_revoke_fds(resp); } } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBWITHHANDLES)) { - if ((j = job_find(root_job, launch_data_get_string(data))) == NULL) { + if ((j = jobmgr_find(root_jobmgr, launch_data_get_string(data))) == NULL) { resp = launch_data_new_errno(errno); } else { resp = job_export(j); Modified: trunk/launchd/src/liblaunch_public.h =================================================================== --- trunk/launchd/src/liblaunch_public.h 2006-11-13 17:54:12 UTC (rev 22942) +++ trunk/launchd/src/liblaunch_public.h 2006-11-13 20:53:23 UTC (rev 22943) @@ -93,7 +93,6 @@ #define LAUNCH_JOBKEY_BONJOURFDS "BonjourFDs" #define LAUNCH_JOBKEY_LASTEXITSTATUS "LastExitStatus" #define LAUNCH_JOBKEY_PID "PID" -#define LAUNCH_JOBKEY_SUBJOBS "SubJobs" #define LAUNCH_JOBKEY_THROTTLEINTERVAL "ThrottleInterval" #define LAUNCH_JOBKEY_LAUNCHONLYONCE "LaunchOnlyOnce"