diff options
Diffstat (limited to 'src/proc.c')
-rw-r--r-- | src/proc.c | 269 |
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 */ |