nbos

nbos

https://git.tonybtw.com/nbos.git git://git.tonybtw.com/nbos.git
2,600 bytes raw
1
#define _GNU_SOURCE
2
#include "run.h"
3
4
#include <errno.h>
5
#include <fcntl.h>
6
#include <stdio.h>
7
#include <string.h>
8
#include <sys/stat.h>
9
#include <sys/types.h>
10
#include <sys/wait.h>
11
#include <unistd.h>
12
13
/**
14
 * run() - Fork+exec a command with stdout/stderr captured to a log.
15
 * @cmd: Absolute path to the executable.
16
 * @argv: NULL-terminated argv. argv[0] is conventionally @cmd.
17
 * @envp: NULL-terminated envp. Pass {NULL} for an empty environment.
18
 * @cwd: Working directory for the child, or NULL to inherit.
19
 * @log_path: File path that captures the child's stdout and stderr.
20
 *
21
 * Stdin is redirected to /dev/null. The child is reaped before return.
22
 *
23
 * Return: RUN_OK_VAL on exit code 0; RUN_E_NONZERO with @exit_code and
24
 * @log_path filled; RUN_E_SPAWN or RUN_E_IO with @errno_val filled.
25
 */
26
run_error run(
27
    const char  *cmd,
28
    char *const  argv[],
29
    char *const  envp[],
30
    const char  *cwd,
31
    const char  *log_path)
32
{
33
    run_error err = RUN_OK_VAL;
34
35
    int log_fd = open(log_path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
36
    if (log_fd < 0) {
37
        err.kind = RUN_E_IO;
38
        err.errno_val = errno;
39
        return err;
40
    }
41
42
    pid_t pid = fork();
43
    if (pid < 0) {
44
        err.kind = RUN_E_SPAWN;
45
        err.errno_val = errno;
46
        close(log_fd);
47
        return err;
48
    }
49
50
    if (pid == 0) {
51
        if (cwd != nullptr && chdir(cwd) < 0) {
52
            dprintf(log_fd, "run: chdir(%s) failed: %s\n", cwd, strerror(errno));
53
            _exit(127);
54
        }
55
56
        if (dup2(log_fd, STDOUT_FILENO) < 0 || dup2(log_fd, STDERR_FILENO) < 0) {
57
            _exit(127);
58
        }
59
        close(log_fd);
60
61
        int devnull = open("/dev/null", O_RDONLY | O_CLOEXEC);
62
        if (devnull >= 0) {
63
            dup2(devnull, STDIN_FILENO);
64
            close(devnull);
65
        }
66
67
        execve(cmd, argv, envp);
68
        dprintf(STDERR_FILENO, "run: execve(%s) failed: %s\n", cmd, strerror(errno));
69
        _exit(127);
70
    }
71
72
    close(log_fd);
73
74
    int status = 0;
75
    while (waitpid(pid, &status, 0) < 0) {
76
        if (errno == EINTR) continue;
77
        err.kind = RUN_E_IO;
78
        err.errno_val = errno;
79
        return err;
80
    }
81
82
    int code;
83
    if (WIFEXITED(status)) {
84
        code = WEXITSTATUS(status);
85
    } else if (WIFSIGNALED(status)) {
86
        code = 128 + WTERMSIG(status);
87
    } else {
88
        err.kind = RUN_E_IO;
89
        err.errno_val = EINVAL;
90
        return err;
91
    }
92
93
    if (code != 0) {
94
        err.kind = RUN_E_NONZERO;
95
        err.exit_code = code;
96
        err.log_path = log_path;
97
        return err;
98
    }
99
100
    return RUN_OK_VAL;
101
}