1/*
2 *  fork_program.c
3 *  kext_tools
4 *
5 *  Created by nik on 5/11/08.
6 *  Copyright 2008 __MyCompanyName__. All rights reserved.
7 *
8 */
9
10#include "fork_program.h"
11#include "kext_tools_util.h"
12#include <spawn.h>
13#include <sys/wait.h>
14#include <libc.h>
15#include <crt_externs.h>
16
17/*******************************************************************************
18* Fork a process after a specified delay, and either wait on it to exit or
19* leave it to run in the background.
20*
21* Returns -2 on spawn() failure, -1 on other failure, and depending on wait:
22* wait: true - exit status of forked program
23* wait: false - pid of background process
24*******************************************************************************/
25int fork_program(const char * argv0, char * const argv[], Boolean wait)
26{
27    int            result;
28    int            spawn_result;
29    pid_t          child_pid;
30    int            child_status;
31    int            normal_iopolicy = getiopolicy_np(IOPOL_TYPE_DISK,
32                                                    IOPOL_SCOPE_PROCESS);
33    char ** environ = *(_NSGetEnviron());
34
35    if (!wait) {
36        setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
37    }
38
39    spawn_result = posix_spawn(&child_pid, argv0, /* file_actions */ NULL,
40        /* spawnattrs */ NULL, argv, environ);
41
42    // If we couldn't spawn the process, return -2 with errno for detail
43    if (spawn_result != 0) {
44        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel,
45            "posix_spawn failed for %s.", argv0);
46        errno = spawn_result;
47        result = -2;
48        goto finish;
49    }
50
51    OSKextLog(/* kext */ NULL, kOSKextLogDetailLevel,
52              "started child process %s[%d] (%ssynchronous).",
53              argv0, child_pid, wait ? "" : "a");
54
55    if (wait) {
56        OSKextLogSpec logSpec = kOSKextLogDetailLevel;
57        if (waitpid(child_pid, &child_status, 0) == -1) {
58            result = -1;
59            goto finish;
60        }
61        if (WIFEXITED(child_status)) {
62            result = WEXITSTATUS(child_status);
63            if (result) {
64                logSpec = kOSKextLogErrorLevel;
65            }
66            OSKextLog(/* kext */ NULL, logSpec,
67                "Child process %s[%d] exited with status %d.",
68                argv0, child_pid, result);
69        } else if (WIFSIGNALED(child_status)) {
70            result = WTERMSIG(child_status);
71            logSpec = kOSKextLogErrorLevel;
72            OSKextLog(/* kext */ NULL, logSpec,
73                "Child process %s[%d] exited due to signal %d.",
74                argv0, child_pid, result);
75        } else {
76            // shouldn't be any other types of exit
77            result = -1;
78        }
79    } else {
80        result = child_pid;
81    }
82
83finish:
84    setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, normal_iopolicy);
85
86    return result;
87}
88