skip to content
==> src/spawn.c <==
1/* native.rs's pipe, spawn, describe and exit_word. */
2#include "mesh/spawn.h"
3#include "mesh/text.h"
4
5#include <errno.h>
6#include <fcntl.h>
7#include <signal.h>
8#include <spawn.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/wait.h>
12#include <unistd.h>
13
14extern char **environ;
15
16static char *os_error(int e) { return mesh_fmt("%s (os error %d)", strerror(e), e); }
17
18bool mesh_pipe_cloexec(int fds[2], char **err) {
19    if (pipe(fds) != 0) {
20        char *e = os_error(errno);
21        mesh_errf(err, "native: pipe: %s", e);
22        free(e);
23        return false;
24    }
25    fcntl(fds[0], F_SETFD, FD_CLOEXEC);
26    fcntl(fds[1], F_SETFD, FD_CLOEXEC);
27    return true;
28}
29
30static bool overridden(char *const *env, const char *kv) {
31    const char *eq = strchr(kv, '=');
32    size_t kn = eq ? (size_t)(eq - kv) : strlen(kv);
33    for (size_t i = 0; env && env[i]; i++)
34        if (strncmp(env[i], kv, kn) == 0 && env[i][kn] == '=') return true;
35    return false;
36}
37
38bool mesh_spawn(char *const *argv, char *const *env, const mesh_spawn_fd *fds, size_t nfds, pid_t *pid, char **err) {
39    size_t n = 0, m = 0;
40    while (environ[n]) n++;
41    while (env && env[m]) m++;
42    char **envp = mesh_alloc((n + m + 1) * sizeof *envp);
43    size_t k = 0;
44    for (size_t i = 0; i < n; i++)
45        if (!overridden(env, environ[i])) envp[k++] = environ[i];
46    for (size_t i = 0; i < m; i++) envp[k++] = env[i];
47    envp[k] = NULL;
48    /* Every source above the targets first, so no dup2 overwrites a later source. */
49    int *high = mesh_alloc((nfds ? nfds : 1) * sizeof *high);
50    bool ok = true;
51    for (size_t i = 0; i < nfds && ok; i++) {
52        high[i] = fcntl(fds[i].parent, F_DUPFD_CLOEXEC, 10);
53        if (high[i] < 0) {
54            char *e = os_error(errno);
55            mesh_errf(err, "dup: %s", e);
56            free(e);
57            for (size_t j = 0; j < i; j++) close(high[j]);
58            ok = false;
59        }
60    }
61    if (ok) {
62        posix_spawn_file_actions_t fa;
63        posix_spawnattr_t at;
64        posix_spawn_file_actions_init(&fa);
65        posix_spawnattr_init(&at);
66        for (size_t i = 0; i < nfds; i++) posix_spawn_file_actions_adddup2(&fa, high[i], fds[i].child);
67        posix_spawnattr_setflags(&at, POSIX_SPAWN_SETPGROUP);
68        posix_spawnattr_setpgroup(&at, 0);
69        int r = posix_spawnp(pid, argv[0], &fa, &at, argv, envp);
70        posix_spawn_file_actions_destroy(&fa);
71        posix_spawnattr_destroy(&at);
72        for (size_t i = 0; i < nfds; i++) close(high[i]);
73        if (r != 0) {
74            char *e = os_error(r);
75            mesh_errf(err, "posix_spawn %s: %s", argv[0], e);
76            free(e);
77            ok = false;
78        }
79    }
80    free(high);
81    free(envp);
82    return ok;
83}
84
85char *mesh_describe_status(int st) {
86    if (WIFEXITED(st)) return mesh_fmt("exit %d", WEXITSTATUS(st));
87    if (WIFSIGNALED(st)) {
88        int sig = WTERMSIG(st);
89        return mesh_fmt("signal %d (%s)%s", sig, strsignal(sig), WCOREDUMP(st) ? ", core dumped" : "");
90    }
91    return mesh_fmt("wait status %d", st);
92}
93
94uint32_t mesh_exit_word(int st) {
95    uint32_t code = WIFEXITED(st) ? (uint32_t)WEXITSTATUS(st) : 0, sig = WIFSIGNALED(st) ? (uint32_t)WTERMSIG(st) : 0;
96    return (code & 0xffffffu) << 8 | (sig & 0xffu);
97}
98
99bool mesh_status_success(int st) { return WIFEXITED(st) && WEXITSTATUS(st) == 0; }