+
+kern_return_t
+job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, mach_port_t *server_portp)
+{
+ 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);
+
+#define LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
+ /* XXX - This code should go away once the per session launchd is integrated with the rest of the system */
+#ifdef LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
+ if (getpid() == 1) {
+ if (ldc.euid && server_uid && (ldc.euid != server_uid)) {
+ job_log(j, LOG_WARNING, "Server create: \"%s\": Will run as UID %d, not UID %d as they told us to",
+ server_cmd, ldc.euid, server_uid);
+ server_uid = ldc.euid;
+ }
+ } else
+#endif
+ if (getuid()) {
+ if (server_uid != getuid()) {
+ job_log(j, LOG_WARNING, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d",
+ server_cmd, getuid(), server_uid);
+ }
+ server_uid = 0; /* zero means "do nothing" */
+ }
+
+ js = job_new_via_mach_init(j, server_cmd, server_uid, on_demand);
+
+ if (js == NULL) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ *server_portp = js->j_port;
+ return BOOTSTRAP_SUCCESS;
+}
+
+kern_return_t
+job_mig_send_signal(job_t j, mach_port_t srp, name_t targetlabel, int sig)
+{
+ struct ldcred ldc;
+ job_t otherj;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+ if (ldc.euid != 0 && ldc.euid != getuid()) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ if (!(otherj = job_find(targetlabel))) {
+ return BOOTSTRAP_UNKNOWN_SERVICE;
+ }
+
+ if (sig == VPROC_MAGIC_UNLOAD_SIGNAL) {
+ bool do_block = otherj->p;
+
+ if (otherj->anonymous) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ job_remove(otherj);
+
+ if (do_block) {
+ job_log(j, LOG_DEBUG, "Blocking MIG return of job_remove(): %s", otherj->label);
+ /* this is messy. We shouldn't access 'otherj' after job_remove(), but we check otherj->p first... */
+ job_assumes(otherj, waiting4removal_new(otherj, srp));
+ return MIG_NO_REPLY;
+ } else {
+ return 0;
+ }
+ } else if (otherj->p) {
+ job_assumes(j, runtime_kill(otherj->p, sig) != -1);
+ }
+
+ return 0;
+}
+
+kern_return_t
+job_mig_log_forward(job_t j, vm_offset_t inval, mach_msg_type_number_t invalCnt)
+{
+ struct ldcred ldc;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (!job_assumes(j, j->per_user)) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+ return runtime_log_forward(ldc.euid, ldc.egid, inval, invalCnt);
+}
+
+kern_return_t
+job_mig_log_drain(job_t j, mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
+{
+ struct ldcred ldc;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+ if (ldc.euid) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ return runtime_log_drain(srp, outval, outvalCnt);
+}
+
+kern_return_t
+job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey,
+ vm_offset_t inval, mach_msg_type_number_t invalCnt,
+ vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
+{
+ const char *action;
+ launch_data_t input_obj, output_obj;
+ size_t data_offset = 0;
+ size_t packed_size;
+ struct ldcred ldc;
+
+ runtime_get_caller_creds(&ldc);
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (inkey && ldc.euid && ldc.euid != getuid()) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ if (inkey && outkey && !job_assumes(j, inkey == outkey)) {
+ return 1;
+ }
+
+ if (inkey && outkey) {
+ action = "Swapping";
+ } else if (inkey) {
+ action = "Setting";
+ } else {
+ action = "Getting";
+ }
+
+ job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
+
+ *outvalCnt = 20 * 1024 * 1024;
+ mig_allocate(outval, *outvalCnt);
+ if (!job_assumes(j, *outval != 0)) {
+ return 1;
+ }
+
+ if (invalCnt && !job_assumes(j, (input_obj = launch_data_unpack((void *)inval, invalCnt, NULL, 0, &data_offset, NULL)) != NULL)) {
+ goto out_bad;
+ }
+
+ switch (outkey) {
+ case VPROC_GSK_ENVIRONMENT:
+ if (!job_assumes(j, (output_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
+ goto out_bad;
+ }
+ jobmgr_export_env_from_other_jobs(j->mgr, output_obj);
+ if (!job_assumes(j, launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL) != 0)) {
+ goto out_bad;
+ }
+ launch_data_free(output_obj);
+ break;
+ case VPROC_GSK_ALLJOBS:
+ if (!job_assumes(j, (output_obj = job_export_all()) != NULL)) {
+ goto out_bad;
+ }
+ ipc_revoke_fds(output_obj);
+ 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;
+ *outvalCnt = 0;
+ break;
+ default:
+ goto out_bad;
+ }
+
+ if (invalCnt) switch (inkey) {
+ case VPROC_GSK_ENVIRONMENT:
+ job_assumes(j, false);
+ break;
+ case 0:
+ break;
+ default:
+ goto out_bad;
+ }
+
+ mig_deallocate(inval, invalCnt);
+
+ return 0;
+
+out_bad:
+ if (*outval) {
+ mig_deallocate(*outval, *outvalCnt);
+ }
+ return 1;
+}
+
+kern_return_t
+job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inval, int64_t *outval)
+{
+ const char *action;
+ kern_return_t kr = 0;
+ struct ldcred ldc;
+ int oldmask;
+
+ runtime_get_caller_creds(&ldc);
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (inkey && ldc.euid && ldc.euid != getuid()) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ if (inkey && outkey && !job_assumes(j, inkey == outkey)) {
+ return 1;
+ }
+
+ if (inkey && outkey) {
+ action = "Swapping";
+ } else if (inkey) {
+ action = "Setting";
+ } else {
+ action = "Getting";
+ }
+
+ job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
+
+ switch (outkey) {
+ case VPROC_GSK_LAST_EXIT_STATUS:
+ *outval = j->last_exit_status;
+ break;
+ case VPROC_GSK_MGR_UID:
+ *outval = getuid();
+ break;
+ case VPROC_GSK_MGR_PID:
+ *outval = getpid();
+ break;
+ case VPROC_GSK_IS_MANAGED:
+ *outval = j->anonymous ? 0 : 1;
+ break;
+ case VPROC_GSK_BASIC_KEEPALIVE:
+ *outval = !j->ondemand;
+ break;
+ case VPROC_GSK_START_INTERVAL:
+ *outval = j->start_interval;
+ break;
+ case VPROC_GSK_IDLE_TIMEOUT:
+ *outval = j->timeout;
+ break;
+ case VPROC_GSK_EXIT_TIMEOUT:
+ *outval = j->exit_timeout;
+ break;
+ case VPROC_GSK_GLOBAL_LOG_MASK:
+ oldmask = runtime_setlogmask(LOG_UPTO(LOG_DEBUG));
+ *outval = oldmask;
+ runtime_setlogmask(oldmask);
+ break;
+ case VPROC_GSK_GLOBAL_UMASK:
+ oldmask = umask(0);
+ *outval = oldmask;
+ umask(oldmask);
+ break;
+ case 0:
+ *outval = 0;
+ break;
+ default:
+ kr = 1;
+ break;
+ }
+
+ switch (inkey) {
+ case VPROC_GSK_GLOBAL_ON_DEMAND:
+ kr = job_set_global_on_demand(j, (bool)inval) ? 0 : 1;
+ break;
+ case VPROC_GSK_BASIC_KEEPALIVE:
+ j->ondemand = !inval;
+ break;
+ case VPROC_GSK_START_INTERVAL:
+ if ((uint64_t)inval > UINT32_MAX) {
+ kr = 1;
+ } else if (inval) {
+ if (j->start_interval == 0) {
+ runtime_add_ref();
+ } else {
+ /* Workaround 5225889 */
+ job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1);
+ }
+ j->start_interval = inval;
+ job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j) != -1);
+ } else if (j->start_interval) {
+ job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1);
+ if (j->start_interval != 0) {
+ runtime_del_ref();
+ }
+ j->start_interval = 0;
+ }
+ break;
+ case VPROC_GSK_IDLE_TIMEOUT:
+ if ((unsigned int)inval > 0) {
+ j->timeout = inval;
+ }
+ break;
+ case VPROC_GSK_EXIT_TIMEOUT:
+ if ((unsigned int)inval > 0) {
+ j->exit_timeout = inval;
+ }
+ break;
+ case VPROC_GSK_GLOBAL_LOG_MASK:
+ runtime_setlogmask(inval);
+ break;
+ case VPROC_GSK_GLOBAL_UMASK:
+ umask(inval);
+ break;
+ case 0:
+ break;
+ default:
+ kr = 1;
+ break;
+ }
+
+ return kr;
+}
+
+kern_return_t
+job_mig_post_fork_ping(job_t j, task_t child_task)
+{
+ struct machservice *ms;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ job_log(j, LOG_DEBUG, "Post fork ping.");
+
+ job_setup_exception_port(j, child_task);
+
+ SLIST_FOREACH(ms, &special_ports, special_port_sle) {
+ if (j->per_user && (ms->special_port_num != TASK_ACCESS_PORT)) {
+ /* The TASK_ACCESS_PORT funny business is to workaround 5325399. */
+ continue;
+ }
+
+ errno = task_set_special_port(child_task, ms->special_port_num, ms->port);
+
+ if (errno) {
+ int desired_log_level = LOG_ERR;
+
+ if (j->anonymous) {
+ /* 5338127 */
+
+ desired_log_level = LOG_WARNING;
+
+ if (ms->special_port_num == TASK_SEATBELT_PORT) {
+ desired_log_level = LOG_DEBUG;
+ }
+ }
+
+ job_log(j, desired_log_level, "Could not setup Mach task special port %u: %s", ms->special_port_num, mach_error_string(errno));
+ }
+ }
+
+ job_assumes(j, launchd_mport_deallocate(child_task) == KERN_SUCCESS);
+
+ return 0;
+}
+
+kern_return_t
+job_mig_reboot2(job_t j, uint64_t flags)
+{
+ char who_started_the_reboot[2048] = "";
+ struct kinfo_proc kp;
+ struct ldcred ldc;
+ pid_t pid_to_log;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (getpid() != 1) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+ if (ldc.euid) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ for (pid_to_log = ldc.pid; pid_to_log; pid_to_log = kp.kp_eproc.e_ppid) {
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid_to_log };
+ size_t who_offset, len = sizeof(kp);
+
+ if (!job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1)) {
+ return 1;
+ }
+
+ who_offset = strlen(who_started_the_reboot);
+ snprintf(who_started_the_reboot + who_offset, sizeof(who_started_the_reboot) - who_offset,
+ " %s[%u]%s", kp.kp_proc.p_comm, pid_to_log, kp.kp_eproc.e_ppid ? " ->" : "");
+ }
+
+ root_jobmgr->reboot_flags = (int)flags;
+
+ launchd_shutdown();
+
+ job_log(j, LOG_DEBUG, "reboot2() initiated by:%s", who_started_the_reboot);
+
+ return 0;
+}
+
+kern_return_t
+job_mig_getsocket(job_t j, name_t spr)
+{
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ ipc_server_init();
+
+ if (!sockpath) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ strncpy(spr, sockpath, sizeof(name_t));
+
+ return BOOTSTRAP_SUCCESS;
+}
+
+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 {
+ job_log(j, pri, "%s", msg);
+ }
+
+ return 0;
+}
+
+void
+ensure_root_bkgd_setup(void)
+{
+ if (background_jobmgr || getpid() != 1) {
+ return;
+ }
+
+ if (!jobmgr_assumes(root_jobmgr, (background_jobmgr = jobmgr_new(root_jobmgr, mach_task_self(), MACH_PORT_NULL, false, VPROCMGR_SESSION_BACKGROUND)) != NULL)) {
+ return;
+ }
+
+ background_jobmgr->req_port = 0;
+ jobmgr_assumes(root_jobmgr, launchd_mport_make_send(background_jobmgr->jm_port) == KERN_SUCCESS);
+}
+
+kern_return_t
+job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont)
+{
+ struct ldcred ldc;
+ job_t ji;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ job_log(j, LOG_DEBUG, "Looking up per user launchd for UID: %u", which_user);
+
+ runtime_get_caller_creds(&ldc);
+
+ if (getpid() != 1) {
+ job_log(j, LOG_ERR, "Only PID 1 supports per user launchd lookups.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ if (ldc.euid || ldc.uid) {
+ which_user = ldc.euid ? ldc.euid : ldc.uid;
+ }
+
+ *up_cont = MACH_PORT_NULL;
+
+ if (which_user == 0) {
+ ensure_root_bkgd_setup();
+
+ *up_cont = background_jobmgr->jm_port;
+
+ return 0;
+ }
+
+ LIST_FOREACH(ji, &root_jobmgr->jobs, sle) {
+ if (!ji->per_user) {
+ continue;
+ }
+ if (ji->mach_uid != which_user) {
+ continue;
+ }
+ if (SLIST_EMPTY(&ji->machservices)) {
+ continue;
+ }
+ if (!SLIST_FIRST(&ji->machservices)->per_user_hack) {
+ continue;
+ }
+ break;
+ }
+
+ if (ji == NULL) {
+ struct machservice *ms;
+ char lbuf[1024];
+
+ job_log(j, LOG_DEBUG, "Creating per user launchd job for UID: %u", which_user);
+
+ sprintf(lbuf, "com.apple.launchd.peruser.%u", which_user);
+
+ ji = job_new(root_jobmgr, lbuf, "/sbin/launchd", NULL);
+
+ if (ji == NULL) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ ji->mach_uid = which_user;
+ ji->per_user = true;
+
+ if ((ms = machservice_new(ji, lbuf, up_cont, false)) == NULL) {
+ job_remove(ji);
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ ms->per_user_hack = true;
+ ms->hide = true;
+
+ ji = job_dispatch(ji, false);
+ } else {
+ job_log(j, LOG_DEBUG, "Per user launchd job found for UID: %u", which_user);
+ }
+
+ if (job_assumes(j, ji != NULL)) {
+ *up_cont = machservice_port(SLIST_FIRST(&ji->machservices));
+ }
+
+ return 0;
+}
+
+kern_return_t
+job_mig_check_in(job_t j, name_t servicename, mach_port_t *serviceportp)
+{
+ static pid_t last_warned_pid = 0;
+ struct machservice *ms;
+ struct ldcred ldc;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+ ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
+
+ if (ms == NULL) {
+ job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Unknown: %s", servicename);
+ return BOOTSTRAP_UNKNOWN_SERVICE;
+ }
+ if (machservice_job(ms) != j) {
+ if (last_warned_pid != ldc.pid) {
+ job_log(j, LOG_NOTICE, "Check-in of Mach service failed. PID %d is not privileged: %s",
+ ldc.pid, servicename);
+ last_warned_pid = ldc.pid;
+ }
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+ if (machservice_active(ms)) {
+ job_log(j, LOG_WARNING, "Check-in of Mach service failed. Already active: %s", servicename);
+ return BOOTSTRAP_SERVICE_ACTIVE;
+ }
+
+ machservice_request_notifications(ms);
+
+ job_log(j, LOG_INFO, "Check-in of service: %s", servicename);
+
+ *serviceportp = machservice_port(ms);
+ return BOOTSTRAP_SUCCESS;
+}
+
+kern_return_t
+job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t flags)
+{
+ struct machservice *ms;
+ struct ldcred ldc;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+#if 0
+ job_log(j, LOG_APPLEONLY, "bootstrap_register() is deprecated. Service: %s", servicename);
+#endif
+
+ job_log(j, LOG_DEBUG, "%sMach service registration attempt: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
+
+ /*
+ * From a per-user/session launchd's perspective, SecurityAgent (UID
+ * 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)->parentmgr == NULL && ldc.uid != 0 && ldc.uid != getuid() && ldc.uid != 92) {
+ if (getpid() == 1) {
+ return VPROC_ERR_TRY_PER_USER;
+ } else {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+ }
+
+ ms = jobmgr_lookup_service(j->mgr, servicename, false, flags & BOOTSTRAP_PER_PID_SERVICE ? ldc.pid : 0);
+
+ if (ms) {
+ if (machservice_job(ms) != j) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+ if (machservice_active(ms)) {
+ job_log(j, LOG_DEBUG, "Mach service registration failed. Already active: %s", servicename);
+ return BOOTSTRAP_SERVICE_ACTIVE;
+ }
+ job_checkin(j);
+ machservice_delete(j, ms, false);
+ }
+
+ if (serviceport != MACH_PORT_NULL) {
+ if ((ms = machservice_new(j, servicename, &serviceport, flags & BOOTSTRAP_PER_PID_SERVICE ? true : false))) {
+ machservice_request_notifications(ms);
+ } else {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+ }
+
+ return BOOTSTRAP_SUCCESS;
+}
+
+kern_return_t
+job_mig_look_up2(job_t j, name_t servicename, mach_port_t *serviceportp, mach_msg_type_name_t *ptype, pid_t target_pid, uint64_t flags)
+{
+ struct machservice *ms;
+ struct ldcred ldc;
+ kern_return_t kr;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+ if (getpid() == 1 && j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.euid != 0) {
+ return VPROC_ERR_TRY_PER_USER;
+ }
+
+ if (!mspolicy_check(j, servicename, flags & BOOTSTRAP_PER_PID_SERVICE)) {
+ job_log(j, LOG_NOTICE, "Policy denied Mach service lookup: %s", servicename);
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ if (flags & BOOTSTRAP_PER_PID_SERVICE) {
+ ms = jobmgr_lookup_service(j->mgr, servicename, false, target_pid);
+ } else {
+ ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
+ }
+
+ if (ms && machservice_hidden(ms) && !job_active(machservice_job(ms))) {
+ ms = NULL;
+ } else if (ms && ms->per_user_hack) {
+ ms = NULL;
+ }
+
+ if (ms) {
+ launchd_assumes(machservice_port(ms) != MACH_PORT_NULL);
+ job_log(j, LOG_DEBUG, "%sMach service lookup: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
+#if 0
+ /* After Leopard ships, we should enable this */
+ if (j->lastlookup == ms && j->lastlookup_gennum == ms->gen_num && !j->per_user) {
+ ms->bad_perf_cnt++;
+ job_log(j, LOG_APPLEONLY, "Performance opportunity: Number of bootstrap_lookup(... \"%s\" ...) calls that should have been cached: %llu",
+ servicename, ms->bad_perf_cnt);
+ }
+ j->lastlookup = ms;
+ j->lastlookup_gennum = ms->gen_num;
+#endif
+ *serviceportp = machservice_port(ms);
+ *ptype = MACH_MSG_TYPE_COPY_SEND;
+ kr = BOOTSTRAP_SUCCESS;
+ } else if (!(flags & BOOTSTRAP_PER_PID_SERVICE) && (inherited_bootstrap_port != MACH_PORT_NULL)) {
+ job_log(j, LOG_DEBUG, "Mach service lookup forwarded: %s", servicename);
+ *ptype = MACH_MSG_TYPE_MOVE_SEND;
+ kr = bootstrap_look_up(inherited_bootstrap_port, servicename, serviceportp);
+ } else if (getpid() == 1 && j->anonymous && ldc.euid >= 500 && strcasecmp(job_get_bs(j)->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
+ /*
+ * 5240036 Should start background session when a lookup of CCacheServer occurs
+ *
+ * This is a total hack. We sniff out loginwindow session, and attempt to guess what it is up to.
+ * If we find a EUID that isn't root, we force it over to the per-user context.
+ */
+ return VPROC_ERR_TRY_PER_USER;
+ } else {
+ job_log(j, LOG_DEBUG, "%sMach service lookup failed: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
+ kr = BOOTSTRAP_UNKNOWN_SERVICE;
+ }
+
+ return kr;
+}
+
+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;
+
+ *pptype = MACH_MSG_TYPE_MAKE_SEND;
+
+ if (jobmgr_parent(jm)) {
+ *parentport = jobmgr_parent(jm)->jm_port;
+ } else if (MACH_PORT_NULL == inherited_bootstrap_port) {
+ *parentport = jm->jm_port;
+ } else {
+ *pptype = MACH_MSG_TYPE_COPY_SEND;
+ *parentport = inherited_bootstrap_port;
+ }
+ return BOOTSTRAP_SUCCESS;
+}
+
+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)
+{
+ 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;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ jm = j->mgr;
+
+ LIST_FOREACH(ji, &jm->jobs, sle) {
+ SLIST_FOREACH(ms, &ji->machservices, sle) {
+ if (!ms->per_pid) {
+ cnt++;
+ }
+ }
+ }
+
+ if (cnt == 0) {
+ goto out;
+ }
+
+ 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 *)&service_actives, cnt * sizeof(service_actives[0]));
+ if (!launchd_assumes(service_actives != NULL)) {
+ goto out_bad;
+ }
+
+ LIST_FOREACH(ji, &jm->jobs, sle) {
+ SLIST_FOREACH(ms, &ji->machservices, sle) {
+ if (!ms->per_pid) {
+ strlcpy(service_names[cnt2], machservice_name(ms), sizeof(service_names[0]));
+ service_actives[cnt2] = machservice_status(ms);
+ cnt2++;
+ }
+ }
+ }
+
+ launchd_assumes(cnt == cnt2);
+
+out:
+ *servicenamesp = service_names;
+ *serviceactivesp = service_actives;
+ *servicenames_cnt = *serviceactives_cnt = cnt;
+
+ return BOOTSTRAP_SUCCESS;
+
+out_bad:
+ if (service_names) {
+ mig_deallocate((vm_address_t)service_names, cnt * sizeof(service_names[0]));
+ }
+ if (service_actives) {
+ mig_deallocate((vm_address_t)service_actives, cnt * sizeof(service_actives[0]));
+ }
+
+ return BOOTSTRAP_NO_MEMORY;
+}
+
+void
+job_reparent_hack(job_t j, const char *where)
+{
+ jobmgr_t jmi, jmi2;
+
+ ensure_root_bkgd_setup();
+
+ /* NULL is only passed for our custom API for LaunchServices. If that is the case, we do magic. */
+ if (where == NULL) {
+ if (strcasecmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
+ where = VPROCMGR_SESSION_LOGINWINDOW;
+ } else {
+ where = VPROCMGR_SESSION_AQUA;
+ }
+ }
+
+ if (strcasecmp(j->mgr->name, where) == 0) {
+ return;
+ }
+
+ SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
+ if (jmi->shutting_down) {
+ continue;
+ } else if (strcasecmp(jmi->name, where) == 0) {
+ goto jm_found;
+ } else if (strcasecmp(jmi->name, VPROCMGR_SESSION_BACKGROUND) == 0 && getpid() == 1) {
+ SLIST_FOREACH(jmi2, &jmi->submgrs, sle) {
+ if (strcasecmp(jmi2->name, where) == 0) {
+ jmi = jmi2;
+ goto jm_found;
+ }
+ }
+ }
+ }
+
+jm_found:
+ if (job_assumes(j, jmi != NULL)) {
+ struct machservice *msi;
+
+ SLIST_FOREACH(msi, &j->machservices, sle) {
+ LIST_REMOVE(msi, name_hash_sle);
+ }
+
+ LIST_REMOVE(j, sle);
+ LIST_INSERT_HEAD(&jmi->jobs, j, sle);
+ j->mgr = jmi;
+
+ SLIST_FOREACH(msi, &j->machservices, sle) {
+ LIST_INSERT_HEAD(&j->mgr->ms_hash[hash_ms(msi->name)], msi, name_hash_sle);
+ }
+ }
+}
+
+kern_return_t
+job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type)
+{
+ mach_msg_type_number_t l2l_i, l2l_port_cnt = 0;
+ mach_port_array_t l2l_ports = NULL;
+ mach_port_t reqport, rcvright;
+ kern_return_t kr = 1;
+ launch_data_t out_obj_array = NULL;
+ struct ldcred ldc;
+ jobmgr_t jmr = NULL;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ runtime_get_caller_creds(&ldc);
+
+ if (target_subset == MACH_PORT_NULL) {
+ job_t j2;
+
+ if (j->mgr->session_initialized) {
+ if (ldc.uid == 0 && getpid() == 1) {
+ if (strcmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
+ job_t ji, jn;
+
+ LIST_FOREACH_SAFE(ji, &j->mgr->jobs, sle, jn) {
+ if (!ji->anonymous) {
+ job_remove(ji);
+ }
+ }
+
+ ensure_root_bkgd_setup();
+
+ SLIST_REMOVE(&j->mgr->parentmgr->submgrs, j->mgr, jobmgr_s, sle);
+ j->mgr->parentmgr = background_jobmgr;
+ SLIST_INSERT_HEAD(&j->mgr->parentmgr->submgrs, j->mgr, sle);
+
+ /*
+ * We really should wait for all the jobs to die before proceeding. See 5351245 for more info.
+ *
+ * We have hacked around this in job_find() by ignoring jobs that are pending removal.
+ */
+
+ } else if (strcmp(j->mgr->name, VPROCMGR_SESSION_AQUA) == 0) {
+ job_log(j, LOG_DEBUG, "Tried to move the Aqua session.");
+ return 0;
+ } else if (strcmp(j->mgr->name, VPROCMGR_SESSION_BACKGROUND) == 0) {
+ job_log(j, LOG_DEBUG, "Tried to move the background session.");
+ return 0;
+ } else {
+ job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
+ kr = BOOTSTRAP_NOT_PRIVILEGED;
+ goto out;
+ }
+ } else {
+ job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
+ kr = BOOTSTRAP_NOT_PRIVILEGED;
+ goto out;
+ }
+ } else if (ldc.uid == 0 && getpid() == 1 && strcmp(session_type, VPROCMGR_SESSION_STANDARDIO) == 0) {
+ ensure_root_bkgd_setup();
+
+ SLIST_REMOVE(&j->mgr->parentmgr->submgrs, j->mgr, jobmgr_s, sle);
+ j->mgr->parentmgr = background_jobmgr;
+ SLIST_INSERT_HEAD(&j->mgr->parentmgr->submgrs, j->mgr, sle);
+ } 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 (jmi->shutting_down) {
+ continue;
+ } else if (strcasecmp(jmi->name, session_type) == 0) {
+ jobmgr_shutdown(jmi);
+ break;
+ }
+ }
+ }
+
+ jobmgr_log(j->mgr, LOG_DEBUG, "Renaming to: %s", session_type);
+ strcpy(j->mgr->name, session_type);
+
+ if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) {
+ job_assumes(j, job_dispatch(j2, true));
+ }
+
+ kr = 0;
+ goto out;
+ } else if (job_mig_intran2(root_jobmgr, target_subset, ldc.pid)) {
+ job_log(j, LOG_ERR, "Moving a session to ourself is bogus.");
+
+ kr = BOOTSTRAP_NOT_PRIVILEGED;
+ goto out;
+ }
+
+ job_log(j, LOG_DEBUG, "Move subset attempt: 0x%x", target_subset);
+
+ kr = _vproc_grab_subset(target_subset, &reqport, &rcvright, &out_obj_array, &l2l_ports, &l2l_port_cnt);
+
+ if (!job_assumes(j, kr == 0)) {
+ goto out;
+ }
+
+ launchd_assert(launch_data_array_get_count(out_obj_array) == l2l_port_cnt);
+
+ if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, reqport, rcvright, false, session_type)) != NULL)) {
+ kr = BOOTSTRAP_NO_MEMORY;
+ goto out;
+ }
+
+ for (l2l_i = 0; l2l_i < l2l_port_cnt; l2l_i++) {
+ launch_data_t tmp, obj_at_idx;
+ struct machservice *ms;
+ job_t j_for_service;
+ const char *serv_name;
+ pid_t target_pid;
+ bool serv_perpid;
+
+ job_assumes(j, obj_at_idx = launch_data_array_get_index(out_obj_array, l2l_i));
+ job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PID));
+ target_pid = (pid_t)launch_data_get_integer(tmp);
+ job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PERPID));
+ serv_perpid = launch_data_get_bool(tmp);
+ job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_NAME));
+ serv_name = launch_data_get_string(tmp);
+
+ j_for_service = jobmgr_find_by_pid(jmr, target_pid, true);
+
+ if (!j_for_service) {
+ /* The PID probably exited */
+ job_assumes(j, launchd_mport_deallocate(l2l_ports[l2l_i]) == KERN_SUCCESS);
+ continue;
+ }
+
+ if ((ms = machservice_new(j_for_service, serv_name, &l2l_ports[l2l_i], serv_perpid))) {
+ machservice_request_notifications(ms);
+ }
+ }
+
+ kr = 0;
+
+out:
+ if (out_obj_array) {
+ launch_data_free(out_obj_array);
+ }
+
+ if (l2l_ports) {
+ mig_deallocate((vm_address_t)l2l_ports, l2l_port_cnt * sizeof(l2l_ports[0]));
+ }
+
+ if (kr == 0) {
+ if (target_subset) {
+ job_assumes(j, launchd_mport_deallocate(target_subset) == KERN_SUCCESS);
+ }
+ } else if (jmr) {
+ jobmgr_shutdown(jmr);
+ }
+
+ return kr;
+}
+
+kern_return_t
+job_mig_take_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright,
+ vm_offset_t *outdata, mach_msg_type_number_t *outdataCnt,
+ mach_port_array_t *portsp, unsigned int *ports_cnt)
+{
+ launch_data_t tmp_obj, tmp_dict, outdata_obj_array = NULL;
+ mach_port_array_t ports = NULL;
+ unsigned int cnt = 0, cnt2 = 0;
+ size_t packed_size;
+ struct machservice *ms;
+ jobmgr_t jm;
+ job_t ji;
+
+ 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 (jobmgr_parent(jm) == NULL) {
+ job_log(j, LOG_ERR, "Root Mach bootstrap cannot be transferred.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ } else if (strcasecmp(jm->name, VPROCMGR_SESSION_AQUA) == 0) {
+ job_log(j, LOG_ERR, "Cannot transfer a setup GUI session.");
+ 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.");
+
+ outdata_obj_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
+ if (!job_assumes(j, outdata_obj_array)) {
+ goto out_bad;
+ }
+
+ *outdataCnt = 20 * 1024 * 1024;
+ mig_allocate(outdata, *outdataCnt);
+ if (!job_assumes(j, *outdata != 0)) {
+ return 1;
+ }
+
+ LIST_FOREACH(ji, &j->mgr->jobs, sle) {
+ if (!ji->anonymous) {
+ continue;
+ }
+ SLIST_FOREACH(ms, &ji->machservices, sle) {
+ cnt++;
+ }
+ }
+
+ mig_allocate((vm_address_t *)&ports, cnt * sizeof(ports[0]));
+ if (!launchd_assumes(ports != NULL)) {
+ goto out_bad;
+ }
+
+ LIST_FOREACH(ji, &j->mgr->jobs, sle) {
+ if (!ji->anonymous) {
+ continue;
+ }
+
+ SLIST_FOREACH(ms, &ji->machservices, sle) {
+ if (job_assumes(j, (tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
+ job_assumes(j, launch_data_array_set_index(outdata_obj_array, tmp_dict, cnt2));
+ } else {
+ goto out_bad;
+ }
+
+ if (job_assumes(j, (tmp_obj = launch_data_new_string(machservice_name(ms))))) {
+ job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_NAME));
+ } else {
+ goto out_bad;
+ }
+
+ if (job_assumes(j, (tmp_obj = launch_data_new_integer((ms->job->p))))) {
+ job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PID));
+ } else {
+ goto out_bad;
+ }
+
+ if (job_assumes(j, (tmp_obj = launch_data_new_bool((ms->per_pid))))) {
+ job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PERPID));
+ } else {
+ goto out_bad;
+ }
+
+ ports[cnt2] = machservice_port(ms);
+
+ /* Increment the send right by one so we can shutdown the jobmgr cleanly */
+ jobmgr_assumes(jm, (errno = mach_port_mod_refs(mach_task_self(), ports[cnt2], MACH_PORT_RIGHT_SEND, 1)) == 0);
+ cnt2++;
+ }
+ }
+
+ launchd_assumes(cnt == cnt2);
+
+ packed_size = launch_data_pack(outdata_obj_array, (void *)*outdata, *outdataCnt, NULL, NULL);
+ if (!job_assumes(j, packed_size != 0)) {
+ goto out_bad;
+ }
+
+ launch_data_free(outdata_obj_array);
+
+ *portsp = ports;
+ *ports_cnt = cnt;
+
+ *reqport = jm->req_port;
+ *rcvright = jm->jm_port;
+
+ jm->req_port = 0;
+ jm->jm_port = 0;
+
+ workaround_5477111 = j;
+
+ jobmgr_shutdown(jm);
+
+ return BOOTSTRAP_SUCCESS;
+
+out_bad:
+ if (outdata_obj_array) {
+ launch_data_free(outdata_obj_array);
+ }
+ if (*outdata) {
+ mig_deallocate(*outdata, *outdataCnt);
+ }
+ if (ports) {
+ mig_deallocate((vm_address_t)ports, cnt * sizeof(ports[0]));
+ }
+
+ return BOOTSTRAP_NO_MEMORY;
+}
+
+kern_return_t
+job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp)
+{
+ int bsdepth = 0;
+ jobmgr_t jmr;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ jmr = j->mgr;
+
+ while ((jmr = jobmgr_parent(jmr)) != NULL) {
+ bsdepth++;
+ }
+
+ /* Since we use recursion, we need an artificial depth for subsets */
+ if (bsdepth > 100) {
+ job_log(j, LOG_ERR, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth);
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if ((jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL, false, NULL)) == NULL) {
+ if (requestorport == MACH_PORT_NULL) {
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ *subsetportp = jmr->jm_port;
+ return BOOTSTRAP_SUCCESS;
+}
+
+kern_return_t
+job_mig_create_service(job_t j, name_t servicename, mach_port_t *serviceportp)
+{
+ 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;
+ }
+
+ if (!j->legacy_mach_job) {
+ job_log(j, LOG_ERR, "bootstrap_create_service() is only allowed against legacy Mach jobs: %s", servicename);
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ ms = jobmgr_lookup_service(j->mgr, servicename, false, 0);
+ if (ms) {
+ job_log(j, LOG_DEBUG, "Mach service creation attempt for failed. Already exists: %s", servicename);
+ return BOOTSTRAP_NAME_IN_USE;
+ }
+
+ job_checkin(j);
+
+ *serviceportp = MACH_PORT_NULL;
+ ms = machservice_new(j, servicename, serviceportp, false);
+
+ if (!launchd_assumes(ms != NULL)) {
+ goto out_bad;
+ }
+
+ return BOOTSTRAP_SUCCESS;
+
+out_bad:
+ launchd_assumes(launchd_mport_close_recv(*serviceportp) == KERN_SUCCESS);
+ return BOOTSTRAP_NO_MEMORY;
+}
+
+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);
+#endif
+ return job_handle_mpm_wait(j, srp, waitstatus);
+}
+
+kern_return_t
+job_mig_uncork_fork(job_t j)
+{
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (!j->stall_before_exec) {
+ job_log(j, LOG_WARNING, "Attempt to uncork a job that isn't in the middle of a fork().");
+ return 1;
+ }
+
+ job_uncork_fork(j);
+ j->stall_before_exec = false;
+ return 0;
+}
+
+kern_return_t
+job_mig_set_service_policy(job_t j, pid_t target_pid, uint64_t flags, name_t target_service)
+{
+ job_t target_j;
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (!job_assumes(j, (target_j = jobmgr_find_by_pid(j->mgr, target_pid, true)) != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (SLIST_EMPTY(&j->mspolicies)) {
+ job_log(j, LOG_DEBUG, "Setting policy on job \"%s\" for Mach service: %s", target_j->label, target_service);
+ if (target_service[0]) {
+ job_assumes(j, mspolicy_new(target_j, target_service, flags & BOOTSTRAP_ALLOW_LOOKUP, flags & BOOTSTRAP_PER_PID_SERVICE, false));
+ } else {
+ target_j->deny_unknown_mslookups = !(flags & BOOTSTRAP_ALLOW_LOOKUP);
+ }
+ } else {
+ job_log(j, LOG_WARNING, "Jobs that have policies assigned to them may not set policies.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ return 0;
+}
+
+kern_return_t
+job_mig_spawn(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, pid_t *child_pid, mach_port_t *obsvr_port)
+{
+ launch_data_t input_obj = NULL;
+ size_t data_offset = 0;
+ struct ldcred ldc;
+ job_t jr;
+
+ runtime_get_caller_creds(&ldc);
+
+ if (!launchd_assumes(j != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ if (getpid() == 1 && ldc.euid && ldc.uid) {
+ job_log(j, LOG_DEBUG, "Punting spawn to per-user-context");
+ return VPROC_ERR_TRY_PER_USER;
+ }
+
+ if (!job_assumes(j, indataCnt != 0)) {
+ return 1;
+ }
+
+ if (!job_assumes(j, (input_obj = launch_data_unpack((void *)indata, indataCnt, NULL, 0, &data_offset, NULL)) != NULL)) {
+ return 1;
+ }
+
+ jr = jobmgr_import2(j->mgr, input_obj);
+
+ if (!job_assumes(j, jr != NULL)) {
+ switch (errno) {
+ case EEXIST:
+ return BOOTSTRAP_NAME_IN_USE;
+ default:
+ return BOOTSTRAP_NO_MEMORY;
+ }
+ }
+
+ job_reparent_hack(jr, NULL);
+
+ if (getpid() == 1) {
+ jr->mach_uid = ldc.uid;
+ }
+
+ jr->unload_at_exit = true;
+ jr->wait4pipe_eof = true;
+ jr->abandon_pg = true;
+ jr->stall_before_exec = jr->wait4debugger;
+ jr->wait4debugger = false;
+
+ jr = job_dispatch(jr, true);
+
+ if (!job_assumes(j, jr != NULL)) {
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ job_assumes(jr, jr->p);
+
+ if (!job_setup_machport(jr)) {
+ job_remove(jr);
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ job_log(j, LOG_INFO, "Spawned");
+
+ *child_pid = jr->p;
+ *obsvr_port = jr->j_port;
+
+ mig_deallocate(indata, indataCnt);
+
+ return BOOTSTRAP_SUCCESS;
+}
+
+void
+jobmgr_init(bool sflag)
+{
+ const char *root_session_type = getpid() == 1 ? VPROCMGR_SESSION_SYSTEM : VPROCMGR_SESSION_BACKGROUND;
+
+ launchd_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, root_session_type)) != NULL);
+}
+
+size_t
+our_strhash(const char *s)
+{
+ size_t c, r = 5381;
+
+ /* djb2
+ * This algorithm was first reported by Dan Bernstein many years ago in comp.lang.c
+ */
+
+ while ((c = *s++)) {
+ r = ((r << 5) + r) + c; /* hash*33 + c */
+ }
+
+ return r;
+}
+
+size_t
+hash_label(const char *label)
+{
+ return our_strhash(label) % LABEL_HASH_SIZE;
+}
+
+size_t
+hash_ms(const char *msstr)
+{
+ return our_strhash(msstr) % MACHSERVICE_HASH_SIZE;
+}
+
+bool
+mspolicy_copy(job_t j_to, job_t j_from)
+{
+ struct mspolicy *msp;
+
+ SLIST_FOREACH(msp, &j_from->mspolicies, sle) {
+ if (!mspolicy_new(j_to, msp->name, msp->allow, msp->per_pid, true)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+mspolicy_new(job_t j, const char *name, bool allow, bool pid_local, bool skip_check)
+{
+ struct mspolicy *msp;
+
+ if (!skip_check) SLIST_FOREACH(msp, &j->mspolicies, sle) {
+ if (msp->per_pid != pid_local) {
+ continue;
+ } else if (strcmp(msp->name, name) == 0) {
+ return false;
+ }
+ }
+
+ if ((msp = calloc(1, sizeof(struct mspolicy) + strlen(name) + 1)) == NULL) {
+ return false;
+ }
+
+ strcpy((char *)msp->name, name);
+ msp->per_pid = pid_local;
+ msp->allow = allow;
+
+ SLIST_INSERT_HEAD(&j->mspolicies, msp, sle);
+
+ return true;
+}
+
+void
+mspolicy_setup(launch_data_t obj, const char *key, void *context)
+{
+ job_t j = context;
+
+ if (launch_data_get_type(obj) != LAUNCH_DATA_BOOL) {
+ job_log(j, LOG_WARNING, "Invalid object type for Mach service policy key: %s", key);
+ return;
+ }
+
+ job_assumes(j, mspolicy_new(j, key, launch_data_get_bool(obj), false, false));
+}
+
+bool
+mspolicy_check(job_t j, const char *name, bool pid_local)
+{
+ struct mspolicy *mspi;
+
+ SLIST_FOREACH(mspi, &j->mspolicies, sle) {
+ if (mspi->per_pid != pid_local) {
+ continue;
+ } else if (strcmp(mspi->name, name) != 0) {
+ continue;
+ }
+ return mspi->allow;
+ }
+
+ return !j->deny_unknown_mslookups;
+}
+
+void
+mspolicy_delete(job_t j, struct mspolicy *msp)
+{
+ SLIST_REMOVE(&j->mspolicies, msp, mspolicy, sle);
+
+ free(msp);
+}
+
+bool
+waiting4removal_new(job_t j, mach_port_t rp)
+{
+ struct waiting_for_removal *w4r;
+
+ if (!job_assumes(j, (w4r = malloc(sizeof(struct waiting_for_removal))) != NULL)) {
+ return false;
+ }
+
+ w4r->reply_port = rp;
+
+ SLIST_INSERT_HEAD(&j->removal_watchers, w4r, sle);
+
+ return true;
+}
+
+void
+waiting4removal_delete(job_t j, struct waiting_for_removal *w4r)
+{
+ job_assumes(j, job_mig_send_signal_reply(w4r->reply_port, 0) == 0);
+
+ SLIST_REMOVE(&j->removal_watchers, w4r, waiting_for_removal, sle);
+
+ free(w4r);
+}
+
+void
+do_file_init(void)
+{
+ launchd_assert(mach_timebase_info(&tbi) == 0);
+
+}