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
33
34IOPOL_SCOPE_PROCESS);
35    char ** environ = *(_NSGetEnviron());
36
37#if 0 // spew program and arguments we are forking...
38    if (argv0) {
39        int i;
40        int commandLen = 0;
41
42        OSKextLog(NULL,
43                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
44                  "Forking: %s",
45                  argv0);
46        for (i = 0; argv[i] != NULL; i++) {
47            commandLen += strlen(argv[i]);
48            commandLen++;
49        }
50        if (commandLen > 0) {
51            char * myCmd = NULL;
52            myCmd = (char *) malloc(commandLen);
53            if (myCmd) {
54                for (i = 0; argv[i] != NULL; i++) {
55                    strcat(myCmd, argv[i]);
56                    strcat(myCmd, " ");
57                }
58                OSKextLog(NULL,
59                          kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
60                          "%s ",
61                          myCmd);
62                free(myCmd);
63            }
64        }
65    }
66#endif
67
68    if (!wait) {
69        setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
70    }
71
72    spawn_result = posix_spawn(&child_pid, argv0, /* file_actions */ NULL,
73        /* spawnattrs */ NULL, argv, environ);
74
75    // If we couldn't spawn the process, return -2 with errno for detail
76    if (spawn_result != 0) {
77        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel,
78            "posix_spawn failed for %s.", argv0);
79        errno = spawn_result;
80        result = -2;
81        goto finish;
82    }
83
84    OSKextLog(/* kext */ NULL, kOSKextLogDetailLevel,
85              "started child process %s[%d] (%ssynchronous).",
86              argv0, child_pid, wait ? "" : "a");
87
88    if (wait) {
89        OSKextLogSpec logSpec = kOSKextLogDetailLevel;
90        if (waitpid(child_pid, &child_status, 0) == -1) {
91            result = -1;
92            goto finish;
93        }
94        if (WIFEXITED(child_status)) {
95            result = WEXITSTATUS(child_status);
96            if (result) {
97                logSpec = kOSKextLogErrorLevel;
98            }
99            OSKextLog(/* kext */ NULL, logSpec,
100                "Child process %s[%d] exited with status %d.",
101                argv0, child_pid, result);
102        } else if (WIFSIGNALED(child_status)) {
103            result = WTERMSIG(child_status);
104            logSpec = kOSKextLogErrorLevel;
105            OSKextLog(/* kext */ NULL, logSpec,
106                "Child process %s[%d] exited due to signal %d.",
107                argv0, child_pid, result);
108        } else {
109            // shouldn't be any other types of exit
110            result = -1;
111        }
112    } else {
113        result = child_pid;
114    }
115
116finish:
117    setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, normal_iopolicy);
118
119    return result;
120}
121