fork_program.c   [plain text]


/*
 *  fork_program.c
 *  kext_tools
 *
 *  Created by nik on 5/11/08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

#include "fork_program.h"
#include "kext_tools_util.h"
#include <spawn.h>
#include <sys/wait.h>
#include <libc.h>
#include <crt_externs.h>

/*******************************************************************************
* Fork a process after a specified delay, and either wait on it to exit or
* leave it to run in the background.
*
* Returns -2 on spawn() failure, -1 on other failure, and depending on wait:
* wait: true - exit status of forked program
* wait: false - pid of background process
*******************************************************************************/
int fork_program(const char * argv0, char * const argv[], Boolean wait)
{
    int            result;
    int            spawn_result;
    pid_t          child_pid;
    int            child_status;
    int            normal_iopolicy = getiopolicy_np(IOPOL_TYPE_DISK,
                                                    IOPOL_SCOPE_PROCESS);
    char ** environ = *(_NSGetEnviron());

    if (!wait) {
        setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
    }

    spawn_result = posix_spawn(&child_pid, argv0, /* file_actions */ NULL,
        /* spawnattrs */ NULL, argv, environ);

    // If we couldn't spawn the process, return -2 with errno for detail
    if (spawn_result != 0) {
        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel,
            "posix_spawn failed for %s.", argv0);
        errno = spawn_result;
        result = -2;
        goto finish;
    }

    OSKextLog(/* kext */ NULL, kOSKextLogDetailLevel,
              "started child process %s[%d] (%ssynchronous).",
              argv0, child_pid, wait ? "" : "a");

    if (wait) {
        OSKextLogSpec logSpec = kOSKextLogDetailLevel;
        if (waitpid(child_pid, &child_status, 0) == -1) {
            result = -1;
            goto finish;
        }
        if (WIFEXITED(child_status)) {
            result = WEXITSTATUS(child_status);
            if (result) {
                logSpec = kOSKextLogErrorLevel;
            }
            OSKextLog(/* kext */ NULL, logSpec,
                "Child process %s[%d] exited with status %d.",
                argv0, child_pid, result);
        } else if (WIFSIGNALED(child_status)) {
            result = WTERMSIG(child_status);
            logSpec = kOSKextLogErrorLevel;
            OSKextLog(/* kext */ NULL, logSpec,
                "Child process %s[%d] exited due to signal %d.",
                argv0, child_pid, result);
        } else {
            // shouldn't be any other types of exit
            result = -1;
        }
    } else {
        result = child_pid;
    }

finish:
    setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, normal_iopolicy);
    
    return result;
}