+ /* This is down here to mitigate the effects of rdar://problem/7264615, in which a process
+ * attaches to its own parent. We need to make sure that the anonymous job has been added
+ * to the process list so that, if it's used ptrace(3) to cause a cycle in the process
+ * tree (thereby making it not a tree anymore), we'll find the tracing parent PID of the
+ * parent process, which is the child, when we go looking for it in jobmgr_find_by_pid().
+ */
+ switch (proc.pbsi_ppid) {
+ case 0:
+ /* the kernel */
+ break;
+ case 1:
+ if (!pid1_magic) {
+ /* we cannot possibly find a parent job_t that is useful in this function */
+ break;
+ }
+ /* fall through */
+ default:
+ jp = jobmgr_find_by_pid(jm, proc.pbsi_ppid, true);
+ if (jobmgr_assumes(jm, jp != NULL)) {
+ if (jp && !jp->anonymous && unlikely(!(proc.pbsi_flags & P_EXEC))) {
+ job_log(jp, LOG_DEBUG, "Called *fork(). Please switch to posix_spawn*(), pthreads or launchd. Child PID %u", proc.pbsi_pid);
+ }
+ }
+ break;
+ }
+
+ return jr;
+}
+
+job_t
+job_new_subjob(job_t j, uuid_t identifier)
+{
+ char label[0];
+ uuid_string_t idstr;
+ uuid_unparse(identifier, idstr);
+ size_t label_sz = snprintf(label, 0, "%s.%s", j->label, idstr);
+
+ job_t nj = (struct job_s *)calloc(1, sizeof(struct job_s) + label_sz + 1);
+ if (launchd_assumes(nj != NULL)) {
+ nj->kqjob_callback = job_callback;
+ nj->mgr = j->mgr;
+ nj->min_run_time = j->min_run_time;
+ nj->timeout = j->timeout;
+ nj->exit_timeout = j->exit_timeout;
+
+ snprintf((char *)nj->label, label_sz + 1, "%s.%s", j->label, idstr);
+
+ /* Set all our simple Booleans that are applicable. */
+ nj->debug = j->debug;
+ nj->ondemand = j->ondemand;
+ nj->checkedin = true;
+ nj->low_pri_io = j->low_pri_io;
+ nj->setmask = j->setmask;
+ nj->wait4debugger = j->wait4debugger;
+ nj->internal_exc_handler = j->internal_exc_handler;
+ nj->setnice = j->setnice;
+ nj->abandon_pg = j->abandon_pg;
+ nj->ignore_pg_at_shutdown = j->ignore_pg_at_shutdown;
+ nj->deny_job_creation = j->deny_job_creation;
+ nj->kill_via_shmem = j->kill_via_shmem;
+ nj->needs_kickoff = j->needs_kickoff;
+ nj->currently_ignored = true;
+ nj->dedicated_instance = true;
+ nj->xpc_service = j->xpc_service;
+
+ nj->mask = j->mask;
+ uuid_copy(nj->instance_id, identifier);
+
+ /* These jobs are purely on-demand Mach jobs. */
+
+ /* {Hard | Soft}ResourceLimits are not supported. */
+
+ struct machservice *msi = NULL;
+ SLIST_FOREACH(msi, &j->machservices, sle) {
+ /* Only copy MachServices that were actually declared in the plist.
+ * So skip over per-PID ones and ones that were created via
+ * bootstrap_register().
+ */
+ if (msi->upfront) {
+ mach_port_t mp = MACH_PORT_NULL;
+ struct machservice *msj = machservice_new(nj, msi->name, &mp, msi->per_pid);
+ if (job_assumes(nj, msj != NULL)) {
+ msj->reset = msi->reset;
+ msj->delete_on_destruction = msi->delete_on_destruction;
+ msj->drain_one_on_crash = msi->drain_one_on_crash;
+ msj->drain_all_on_crash = msi->drain_all_on_crash;
+ }
+ }
+ }
+
+ if (j->prog) {
+ nj->prog = strdup(j->prog);
+ }
+ if (j->argv) {
+ size_t sz = malloc_size(j->argv);
+ nj->argv = (char **)malloc(sz);
+ if (job_assumes(nj, nj->argv != NULL)) {
+ /* This is the start of our strings. */
+ char *p = ((char *)nj->argv) + ((j->argc + 1) * sizeof(char *));
+
+ size_t i = 0;
+ for (i = 0; i < j->argc; i++) {
+ (void)strcpy(p, j->argv[i]);
+ nj->argv[i] = p;
+ p += (strlen(j->argv[i]) + 1);
+ }
+ nj->argv[i] = NULL;
+ }
+
+ nj->argc = j->argc;
+ }
+
+ /* We ignore global environment variables. */
+ struct envitem *ei = NULL;
+ SLIST_FOREACH(ei, &j->env, sle) {
+ (void)job_assumes(nj, envitem_new(nj, ei->key, ei->value, false, false));
+ }
+ uuid_string_t val;
+ uuid_unparse(identifier, val);
+ (void)job_assumes(nj, envitem_new(nj, LAUNCH_ENV_INSTANCEID, val, false, false));
+
+ if (j->rootdir) {
+ nj->rootdir = strdup(j->rootdir);
+ }
+ if (j->workingdir) {
+ nj->workingdir = strdup(j->workingdir);
+ }
+ if (j->username) {
+ nj->username = strdup(j->username);
+ }
+ if (j->groupname) {
+ nj->groupname = strdup(j->groupname);
+ }
+ /* FIXME: We shouldn't redirect all the output from these jobs to the same
+ * file. We should uniquify the file names.
+ */
+ if (j->stdinpath) {
+ nj->stdinpath = strdup(j->stdinpath);
+ }
+ if (j->stdoutpath) {
+ nj->stdoutpath = strdup(j->stdinpath);
+ }
+ if (j->stderrpath) {
+ nj->stderrpath = strdup(j->stderrpath);
+ }
+ if (j->alt_exc_handler) {
+ nj->alt_exc_handler = strdup(j->alt_exc_handler);
+ }
+ #if HAVE_SANDBOX
+ if (j->seatbelt_profile) {
+ nj->seatbelt_profile = strdup(j->seatbelt_profile);
+ }
+ #endif
+
+ #if HAVE_QUARANTINE
+ if (j->quarantine_data) {
+ nj->quarantine_data = strdup(j->quarantine_data);
+ }
+ nj->quarantine_data_sz = j->quarantine_data_sz;
+ #endif
+ if (j->j_binpref) {
+ size_t sz = malloc_size(j->j_binpref);
+ nj->j_binpref = (cpu_type_t *)malloc(sz);
+ if (job_assumes(nj, nj->j_binpref)) {
+ memcpy(&nj->j_binpref, &j->j_binpref, sz);
+ }
+ }
+
+ /* JetsamPriority is unsupported. */
+
+ if (j->asport != MACH_PORT_NULL) {
+ (void)job_assumes(nj, launchd_mport_copy_send(j->asport) == KERN_SUCCESS);
+ nj->asport = j->asport;
+ }
+
+ LIST_INSERT_HEAD(&nj->mgr->jobs, nj, sle);
+
+ jobmgr_t where2put = root_jobmgr;
+ if (j->mgr->properties & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
+ where2put = j->mgr;
+ }
+ LIST_INSERT_HEAD(&where2put->label_hash[hash_label(nj->label)], nj, label_hash_sle);
+ LIST_INSERT_HEAD(&j->subjobs, nj, subjob_sle);
+ }
+
+ return nj;