summaryrefslogtreecommitdiff
path: root/src/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/proc.c')
-rw-r--r--src/proc.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/src/proc.c b/src/proc.c
new file mode 100644
index 0000000..a7b47aa
--- /dev/null
+++ b/src/proc.c
@@ -0,0 +1,269 @@
+/*
+ * Process management
+ * Copyright Jan Engelhardt, 2008-2009
+ *
+ * This file is part of libHX. libHX is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser
+ * General Public License as published by the Free Software Foundation;
+ * either version 2.1 or (at your option) any later version.
+ */
+#include "config.h"
+#include "internal.h"
+
+#if !defined(HAVE_FORK) || !defined(HAVE_PIPE) || !defined(HAVE_EXECV) || \
+ !defined(HAVE_EXECVP)
+#include <errno.h>
+#include <libHX/proc.h>
+
+struct HXproc;
+
+EXPORT_SYMBOL int HXproc_run_async(const char *const *argv, struct HXproc *p)
+{
+ return -ENOSYS;
+}
+
+EXPORT_SYMBOL int HXproc_run_sync(const char *const *argv, unsigned int flags)
+{
+ /* Might use system() here... */
+ return -ENOSYS;
+}
+
+EXPORT_SYMBOL int HXproc_wait(struct HXproc *p)
+{
+ return -ENOSYS;
+}
+
+#else /* HAVE_FORK, HAVE_PIPE, HAVE_EXECVE */
+
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libHX/defs.h>
+#include <libHX/proc.h>
+#include "internal.h"
+
+#ifdef _WIN32
+# define NULL_DEVICE "nul"
+#else
+# define NULL_DEVICE "/dev/null"
+#endif
+
+/**
+ * HXproc_build_pipes -
+ * @fd_request: user array to tell us which pipe sets to create
+ * @p: result array
+ *
+ * Create some pipes.
+ * Explicitly initialize the @p array with -1 so that we can call close()
+ * on all of them without any side effects.
+ */
+static __inline__ int
+HXproc_build_pipes(const struct HXproc *proc, int (*p)[2])
+{
+ unsigned int x, y;
+
+ for (x = 0; x < 3; ++x)
+ for (y = 0; y < 2; ++y)
+ p[x][y] = -1;
+
+ if ((proc->p_flags & HXPROC_STDIN) && pipe(p[0]) < 0)
+ return -errno;
+ if ((proc->p_flags & HXPROC_STDOUT) && pipe(p[1]) < 0)
+ return -errno;
+ if ((proc->p_flags & HXPROC_STDERR) && pipe(p[2]) < 0)
+ return -errno;
+
+ return 1;
+}
+
+/**
+ * HXproc_close_pipes -
+ * @p: pipe fds to close
+ *
+ * In @p, there might be some fds that are -1 (due to the p[x][y] = -1 in
+ * HXproc_build_pipes()). That is ok, as closing -1 does not do anything.
+ */
+static void HXproc_close_pipes(int (*p)[2])
+{
+ if (p[0][0] >= 0)
+ close(p[0][0]);
+ if (p[0][1] >= 0)
+ close(p[0][1]);
+ if (p[1][0] >= 0)
+ close(p[1][0]);
+ if (p[1][1] >= 0)
+ close(p[1][1]);
+ if (p[2][0] >= 0)
+ close(p[2][0]);
+ if (p[2][1] >= 0)
+ close(p[2][1]);
+}
+
+/**
+ * HXproc_run_async -
+ * @argv: program and arguments
+ * @proc: control block with flags, also used to return info like fds
+ *
+ * Sets up pipes and runs the specified program.
+ */
+EXPORT_SYMBOL int
+HXproc_run_async(const char *const *argv, struct HXproc *proc)
+{
+ int pipes[3][2], nullfd = -1, ret, saved_errno;
+ unsigned int t;
+
+ if (argv == NULL || *argv == NULL)
+ return -EFAULT;
+
+ proc->p_stdin = proc->p_stdout = proc->p_stderr = -1;
+
+ t = (proc->p_flags & (HXPROC_STDIN | HXPROC_NULL_STDIN)) ==
+ (HXPROC_STDIN | HXPROC_NULL_STDIN);
+ t |= (proc->p_flags & (HXPROC_STDOUT | HXPROC_NULL_STDOUT)) ==
+ (HXPROC_STDOUT | HXPROC_NULL_STDOUT);
+ t |= (proc->p_flags & (HXPROC_STDERR | HXPROC_NULL_STDERR)) ==
+ (HXPROC_STDERR | HXPROC_NULL_STDERR);
+ if (t > 0)
+ return -EINVAL;
+
+ if (proc->p_flags & (HXPROC_NULL_STDIN | HXPROC_NULL_STDOUT |
+ HXPROC_NULL_STDERR)) {
+ if ((nullfd = open(NULL_DEVICE, O_RDWR)) < 0)
+ return -errno;
+ }
+ if ((ret = HXproc_build_pipes(proc, pipes)) <= 0) {
+ saved_errno = errno;
+ if (nullfd >= 0)
+ close(nullfd);
+ errno = saved_errno;
+ return ret;
+ }
+
+ if (proc->p_ops != NULL && proc->p_ops->p_prefork != NULL)
+ proc->p_ops->p_prefork(proc->p_data);
+ if ((proc->p_pid = fork()) < 0) {
+ saved_errno = errno;
+ if (proc->p_ops != NULL && proc->p_ops->p_complete != NULL)
+ proc->p_ops->p_complete(proc->p_data);
+ HXproc_close_pipes(pipes);
+ if (nullfd >= 0)
+ close(nullfd);
+ return -(errno = saved_errno);
+ } else if (proc->p_pid == 0) {
+ const char *prog = *argv;
+
+ /*
+ * Put file descriptors in place... and do so before postfork,
+ * as someone could have used proc.p_data = &proc; already.
+ *
+ * Take a dup of the pipe ends, so that close_pipes does not
+ * accidentally close them.
+ */
+ if (proc->p_flags & HXPROC_STDIN)
+ proc->p_stdin = dup(pipes[0][0]);
+ else if (proc->p_flags & HXPROC_NULL_STDIN)
+ proc->p_stdin = dup(nullfd);
+ if (proc->p_flags & HXPROC_STDOUT)
+ proc->p_stdout = dup(pipes[1][1]);
+ else if (proc->p_flags & HXPROC_NULL_STDOUT)
+ proc->p_stdout = dup(nullfd);
+ if (proc->p_flags & HXPROC_STDERR)
+ proc->p_stderr = dup(pipes[2][1]);
+ else if (proc->p_flags & HXPROC_NULL_STDERR)
+ proc->p_stderr = dup(nullfd);
+ if (proc->p_ops != NULL && proc->p_ops->p_postfork != NULL)
+ proc->p_ops->p_postfork(proc->p_data);
+
+ /*
+ * The rest of housekeeping. Now move the pipe ends onto
+ * their final fds.
+ */
+ HXproc_close_pipes(pipes);
+ if ((proc->p_flags & (HXPROC_STDIN | HXPROC_NULL_STDIN)) &&
+ proc->p_stdin != STDIN_FILENO) {
+ dup2(proc->p_stdin, STDIN_FILENO);
+ close(proc->p_stdin);
+ }
+ if ((proc->p_flags & (HXPROC_STDOUT | HXPROC_NULL_STDOUT)) &&
+ proc->p_stdout != STDOUT_FILENO) {
+ dup2(proc->p_stdout, STDOUT_FILENO);
+ close(proc->p_stdout);
+ }
+ if ((proc->p_flags & (HXPROC_STDERR | HXPROC_NULL_STDERR)) &&
+ proc->p_stderr != STDERR_FILENO) {
+ dup2(proc->p_stderr, STDERR_FILENO);
+ close(proc->p_stderr);
+ }
+ if (nullfd >= 0)
+ close(nullfd);
+ if (proc->p_flags & HXPROC_A0)
+ ++argv;
+ if (proc->p_flags & HXPROC_EXECV)
+ execv(prog, const_cast2(char * const *, argv));
+ else
+ execvp(prog, const_cast2(char * const *, argv));
+ if (proc->p_flags & HXPROC_VERBOSE)
+ fprintf(stderr, "%s: %s: %s\n", __func__,
+ prog, strerror(errno));
+ _exit(-1);
+ }
+
+ if (proc->p_flags & HXPROC_STDIN) {
+ close(pipes[0][0]);
+ proc->p_stdin = pipes[0][1];
+ }
+ if (proc->p_flags & HXPROC_STDOUT) {
+ close(pipes[1][1]);
+ proc->p_stdout = pipes[1][0];
+ }
+ if (proc->p_flags & HXPROC_STDERR) {
+ close(pipes[2][1]);
+ proc->p_stderr = pipes[2][0];
+ }
+
+ return 1;
+}
+
+EXPORT_SYMBOL int HXproc_run_sync(const char *const *argv, unsigned int flags)
+{
+ struct HXproc proc;
+ int ret;
+
+ memset(&proc, 0, sizeof(proc));
+ /*
+ * Assigning file descriptors makes no sense because they would not
+ * be read from. %HXPROC_NULL_* is ok of course.
+ */
+ if (flags & (HXPROC_STDIN | HXPROC_STDOUT | HXPROC_STDERR))
+ return -EINVAL;
+ proc.p_flags = flags;
+ if ((ret = HXproc_run_async(argv, &proc)) <= 0)
+ return ret;
+ return HXproc_wait(&proc);
+}
+
+EXPORT_SYMBOL int HXproc_wait(struct HXproc *proc)
+{
+ int status;
+
+ /* User has to close the pipes. Do not do it here. */
+
+ if (waitpid(proc->p_pid, &status, 0) < 0)
+ return -errno;
+ if (proc->p_ops != NULL && proc->p_ops->p_complete != NULL)
+ proc->p_ops->p_complete(proc->p_data);
+
+ if ((proc->p_exited = WIFEXITED(status)))
+ proc->p_status = WEXITSTATUS(status);
+ if ((proc->p_terminated = WIFSIGNALED(status)))
+ proc->p_status = WTERMSIG(status);
+ if (proc->p_terminated)
+ return static_cast(unsigned int,
+ static_cast(unsigned char, proc->p_status)) << 16;
+ return static_cast(unsigned char, proc->p_status);
+}
+
+#endif /* HAVE_lots */