+pid_t
+basic_spawn(job_t j, void (*what_to_do)(job_t))
+{
+ pid_t p = 0;
+ thread_state_flavor_t f = 0;
+#if defined (__ppc__) || defined(__ppc64__)
+ f = PPC_THREAD_STATE64;
+#elif defined(__i386__) || defined(__x86_64__)
+ f = x86_THREAD_STATE;
+#elif defined(__arm__)
+ f = ARM_THREAD_STATE;
+#else
+ #error "unknown architecture"
+#endif
+
+ int execpair[2] = { 0, 0 };
+ job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, execpair) != -1);
+
+ switch( (p = fork()) ) {
+ case 0 :
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ /* Wait for the parent to attach a kevent. */
+ read(_fd(execpair[1]), &p, sizeof(p));
+ what_to_do(j);
+ _exit(EXIT_FAILURE);
+ case -1 :
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ job_assumes(j, runtime_close(execpair[1]) != -1);
+ execpair[0] = -1;
+ execpair[1] = -1;
+ job_log(j, LOG_NOTICE | LOG_CONSOLE, "fork(2) failed: %d", errno);
+ break;
+ default :
+ job_assumes(j, runtime_close(execpair[1]) != -1);
+ execpair[1] = -1;
+ break;
+ }
+
+ int r = -1;
+ if( p != -1 ) {
+ /* Let us know when sample is done. ONESHOT is implicit if we're just interested in NOTE_EXIT. */
+ if( job_assumes(j, (r = kevent_mod(p, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, j)) != -1) ) {
+ if( !job_assumes(j, write(execpair[0], &p, sizeof(p)) == sizeof(p)) ) {
+ job_assumes(j, kevent_mod(p, EVFILT_PROC, EV_DELETE, 0, 0, NULL) != -1);
+ job_assumes(j, runtime_kill(p, SIGKILL) != -1);
+ r = -1;
+ p = -1;
+ }
+ } else {
+ job_assumes(j, runtime_kill(p, SIGKILL) != -1);
+ }
+
+ int status = 0;
+ if( r == -1 ) {
+ job_assumes(j, waitpid(p, &status, WNOHANG) != -1);
+ }
+ }
+
+ if( execpair[0] != -1 ) {
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ }
+
+ if( execpair[1] != -1 ) {
+ job_assumes(j, runtime_close(execpair[0]) != -1);
+ }
+
+ return p;
+}
+
+void
+take_sample(job_t j)
+{
+ char pidstr[32];
+ snprintf(pidstr, sizeof(pidstr), "%u", j->p);
+#if !TARGET_OS_EMBEDDED
+ /* -nodsyms so sample doesn't try to use Spotlight to find dsym files after mds has gone away. */
+ char *sample_args[] = { "/usr/bin/sample", pidstr, "1", "-unsupportedShowArch", "-mayDie", "-nodsyms", "-file", j->mgr->sample_log_file, NULL };
+#else
+ char *sample_args[] = { "/usr/bin/sample", pidstr, "1", "-unsupportedShowArch", "-mayDie", "-file", j->mgr->sample_log_file, NULL };
+#endif
+
+ execve(sample_args[0], sample_args, environ);
+ _exit(EXIT_FAILURE);
+}
+
+void
+jobmgr_dequeue_next_sample(jobmgr_t jm)
+{
+ if( STAILQ_EMPTY(&jm->pending_samples) ) {
+ jobmgr_log(jm, LOG_DEBUG | LOG_CONSOLE, "Sample queue is empty.");
+ return;
+ }
+
+ /* Dequeue the next in line. */
+ job_t j = STAILQ_FIRST(&jm->pending_samples);
+ if( j->is_being_sampled ) {
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sampling is in progress. Not dequeuing next job.");
+ return;
+ }
+
+ if( !job_assumes(j, !j->sampling_complete) ) {
+ return;
+ }
+
+ if (!job_assumes(j, do_apple_internal_logging)) {
+ return;
+ }
+
+ if (!job_assumes(j, mkdir(SHUTDOWN_LOG_DIR, S_IRWXU) != -1 || errno == EEXIST)) {
+ return;
+ }
+
+ char pidstr[32];
+ snprintf(pidstr, sizeof(pidstr), "%u", j->p);
+ snprintf(j->mgr->sample_log_file, sizeof(j->mgr->sample_log_file), SHUTDOWN_LOG_DIR "/%s-%u.sample.txt", j->label, j->p);
+
+ if (job_assumes(j, unlink(jm->sample_log_file) != -1 || errno == ENOENT)) {
+ pid_t sp = basic_spawn(j, take_sample);
+
+ if( sp == -1 ) {
+ job_log(j, LOG_ERR | LOG_CONSOLE, "Sampling for job failed!");
+ STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
+ j->sampling_complete = true;
+ jobmgr_dequeue_next_sample(jm);
+ } else {
+ j->tracing_pid = sp;
+ j->is_being_sampled = true;
+ job_log(j, LOG_DEBUG | LOG_CONSOLE, "Sampling job (sample PID: %i, file: %s).", sp, j->mgr->sample_log_file);
+ }
+ } else {
+ STAILQ_REMOVE(&jm->pending_samples, j, job_s, pending_samples_sle);
+ j->sampling_complete = true;
+ }
+
+ j->pending_sample = false;
+}
+
+void
+job_dispatch_curious_jobs(job_t j)
+{
+ job_t ji = NULL, jt = NULL;
+ SLIST_FOREACH_SAFE( ji, &s_curious_jobs, curious_jobs_sle, jt ) {
+ struct semaphoreitem *si = NULL;
+ SLIST_FOREACH( si, &ji->semaphores, sle ) {
+ if( !(si->why == OTHER_JOB_ENABLED || si->why == OTHER_JOB_DISABLED) ) {
+ continue;
+ }
+
+ if( strncmp(si->what, j->label, strlen(j->label)) == 0 ) {
+ job_log(ji, LOG_DEBUG, "Dispatching out of interest in \"%s\".", j->label);
+
+ job_dispatch(ji, false);
+ /* ji could be removed here, so don't do anything with it or its semaphores
+ * after this point.
+ */
+ break;
+ }
+ }
+ }
+}
+