aboutsummaryrefslogtreecommitdiffstats
path: root/libexec_spawn.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexec_spawn.c')
-rw-r--r--libexec_spawn.c89
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