/* * 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 #include 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 #include #include #include #include #include #include #include #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 */