==> 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; }