diff options
Diffstat (limited to 'libexec_spawn.c')
-rw-r--r-- | libexec_spawn.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/libexec_spawn.c b/libexec_spawn.c new file mode 100644 index 0000000..f0b46e6 --- /dev/null +++ b/libexec_spawn.c @@ -0,0 +1,89 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_spawn(pid_t *out, struct libexec_command *cmd, + int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user), + void *user, struct libexec_document **docsp, size_t *ndocsp, int doc_fd_flags) +{ + int fds[2], error, new_fd; + size_t i; + ssize_t r; + + if (!docsp || !ndocsp || !out || !cmd) { + errno = EINVAL; + return -1; + } + + *out = -1; + + if (pipe2(fds, O_CLOEXEC)) + return -1; + + new_fd = fds[1]; + for (i = 0; i < cmd->nplumings; i++) + if (cmd->plumings[i].fd >= new_fd) + new_fd = cmd->plumings[i].fd + 1; + + if (new_fd != fds[1]) { + new_fd = fcntl(fds[1], F_DUPFD_CLOEXEC, new_fd); + if (new_fd == -1) + goto fail; + fds[1] = new_fd; + } + + if (libexec_get_documents(cmd, docsp, ndocsp, doc_fd_flags)) + goto fail; + + *out = fork(); + switch (*out) { + case -1: + goto fail; + + case 0: + close(fds[0]); + if (after_fork) + if ((*after_fork)(cmd, fds[1], user)) + goto child_fail; + libexec_exec(cmd); + child_fail: + error = errno; + write_again: + r = write(fds[1], &error, sizeof(error)); + if (r <= 0) { + if (errno == EINTR) + goto write_again; + abort(); + } + _exit(1); + + default: + close(fds[1]); + read_again: + r = read(fds[0], &error, sizeof(error)); + if (r < 0) { + if (errno == EINTR) + goto read_again; + } else if (r == 0) { + return 0; + } else if ((size_t)r >= sizeof(error)) { + errno = error; + } + + return -1; + } + +fail: + error = errno; + close(fds[0]); + close(fds[1]); + errno = error; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif |