+ job_t j2;
+
+ kern_return_t kr = BOOTSTRAP_NO_MEMORY;
+ if (j->mgr->session_initialized) {
+ job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
+ kr = BOOTSTRAP_NOT_PRIVILEGED;
+ } else if (strcmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
+ jobmgr_t jmi;
+
+ /*
+ * 5330262
+ *
+ * We're working around LoginWindow and the WindowServer.
+ *
+ * In practice, there is only one LoginWindow session. Unfortunately, for certain
+ * scenarios, the WindowServer spawns loginwindow, and in those cases, it frequently
+ * spawns a replacement loginwindow session before cleaning up the previous one.
+ *
+ * We're going to use the creation of a new LoginWindow context as a clue that the
+ * previous LoginWindow context is on the way out and therefore we should just
+ * kick-start the shutdown of it.
+ */
+
+ SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
+ if (unlikely(jmi->shutting_down)) {
+ continue;
+ } else if (strcasecmp(jmi->name, session_type) == 0) {
+ jobmgr_shutdown(jmi);
+ break;
+ }
+ }
+ }
+
+ jobmgr_log(j->mgr, LOG_DEBUG, "Initializing as %s", session_type);
+ strcpy(j->mgr->name_init, session_type);
+
+ if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) {
+ j2->audit_session = audit_session;
+ job_assumes(j, job_dispatch(j2, true));
+ kr = BOOTSTRAP_SUCCESS;
+ }
+
+ return kr;
+}
+
+kern_return_t
+job_mig_switch_to_session(job_t j, mach_port_t requestor_port, name_t session_name, mach_port_t audit_session, mach_port_t *new_bsport)
+{
+ struct ldcred *ldc = runtime_get_caller_creds();
+ if (!jobmgr_assumes(root_jobmgr, j != NULL)) {
+ jobmgr_log(root_jobmgr, LOG_ERR, "%s() called with NULL job: PID %d", __func__, ldc->pid);
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ job_log(j, LOG_DEBUG, "Job wants to move to %s session.", session_name);
+
+ if( !job_assumes(j, pid1_magic == false) ) {
+ job_log(j, LOG_WARNING, "Switching sessions is not allowed in the system Mach bootstrap.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ if( !j->anonymous ) {
+ job_log(j, LOG_NOTICE, "Non-anonymous job tried to switch sessions. Please use LimitLoadToSessionType instead.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+
+ jobmgr_t target_jm = jobmgr_find_by_name(root_jobmgr, session_name);
+ if( target_jm == j->mgr ) {
+ job_log(j, LOG_DEBUG, "Job is already in its desired session (%s).", session_name);
+ *new_bsport = target_jm->jm_port;
+ return BOOTSTRAP_SUCCESS;
+ }
+
+ if( !target_jm ) {
+ target_jm = jobmgr_new(j->mgr, requestor_port, MACH_PORT_NULL, false, session_name, false, audit_session);
+ if( !target_jm ) {
+ mach_port_deallocate(mach_task_self(), audit_session);
+ } else {
+ target_jm->properties |= BOOTSTRAP_PROPERTY_IMPLICITSUBSET;
+ }
+ }
+
+ if( !job_assumes(j, target_jm != NULL) ) {
+ job_log(j, LOG_WARNING, "Could not find %s session!", session_name);
+ return BOOTSTRAP_NO_MEMORY;
+ }
+
+ /* Remove the job from it's current job manager. */
+ LIST_REMOVE(j, sle);
+ LIST_REMOVE(j, pid_hash_sle);
+
+ job_t ji = NULL, jit = NULL;
+ LIST_FOREACH_SAFE( ji, &j->mgr->global_env_jobs, global_env_sle, jit ) {
+ if( ji == j ) {
+ LIST_REMOVE(ji, global_env_sle);
+ break;
+ }
+ }
+
+ /* Put the job into the target job manager. */
+ LIST_INSERT_HEAD(&target_jm->jobs, j, sle);
+ LIST_INSERT_HEAD(&target_jm->active_jobs[ACTIVE_JOB_HASH(j->p)], j, pid_hash_sle);
+
+ if( ji ) {
+ LIST_INSERT_HEAD(&target_jm->global_env_jobs, j, global_env_sle);
+ }
+
+ /* Move our Mach services over if we're not in a flat namespace. */
+ if( !g_flat_mach_namespace && !SLIST_EMPTY(&j->machservices) ) {
+ struct machservice *msi = NULL, *msit = NULL;
+ SLIST_FOREACH_SAFE( msi, &j->machservices, sle, msit ) {
+ LIST_REMOVE(msi, name_hash_sle);
+ LIST_INSERT_HEAD(&target_jm->ms_hash[hash_ms(msi->name)], msi, name_hash_sle);
+ }
+ }
+
+ j->mgr = target_jm;
+
+ if( !j->holds_ref ) {
+ /* Anonymous jobs which move around are particularly interesting to us, so we want to
+ * stick around while they're still around.
+ * For example, login calls into the PAM launchd module, which moves the process into
+ * the StandardIO session by default. So we'll hold a reference on that job to prevent
+ * ourselves from going away.
+ */
+ j->holds_ref = true;
+ runtime_add_ref();
+ }
+
+ *new_bsport = target_jm->jm_port;
+
+ return KERN_SUCCESS;
+}
+
+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 (unlikely(!pid1_magic)) {
+ job_log(j, LOG_ERR, "Only the system launchd will transfer Mach sub-bootstraps.");
+ return BOOTSTRAP_NOT_PRIVILEGED;
+ }
+ if (unlikely(jobmgr_parent(jm) == NULL)) {