/* * Copyright (C) 2003-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file exec.c Helper functions for spawning new processes. */ #include #include "para.h" #include "close_on_fork.h" #include "error.h" #include "string.h" #include "fd.h" /** * Spawn a new process and redirect fd 0, 1, and 2. * * \param pid Will hold the pid of the created process upon return. * \param file Path of the executable to execute. * \param args The argument array for the command. * \param fds a Pointer to a value-result array. * * \return Standard. * * \sa null(4), pipe(2), dup2(2), fork(2), exec(3). */ static int para_exec(pid_t *pid, const char *file, char *const *const args, int *fds) { int ret, in[2] = {-1, -1}, out[2] = {-1, -1}, err[2] = {-1, -1}, null = -1; /* ;) */ ret = -E_DUP_PIPE; if (fds[0] > 0 && pipe(in) < 0) goto err_out; if (fds[1] > 0 && pipe(out) < 0) goto err_out; if (fds[2] > 0 && pipe(err) < 0) goto err_out; if (!fds[0] || !fds[1] || !fds[2]) { ret = para_open("/dev/null", O_RDWR, 42); if (ret < 0) goto err_out; null = ret; } ret = fork(); if (ret < 0) { ret = -ERRNO_TO_PARA_ERROR(errno); goto err_out; } *pid = ret; if (!(*pid)) { /* child */ if (fds[0] >= 0) { if (fds[0]) { close(in[1]); if (in[0] != STDIN_FILENO) dup2(in[0], STDIN_FILENO); } else dup2(null, STDIN_FILENO); } if (fds[1] >= 0) { if (fds[1]) { close(out[0]); if (out[1] != STDOUT_FILENO) dup2(out[1], STDOUT_FILENO); } else dup2(null, STDOUT_FILENO); } if (fds[2] >= 0) { if (fds[2]) { close(err[0]); if (err[1] != STDERR_FILENO) dup2(err[1], STDERR_FILENO); } else dup2(null, STDERR_FILENO); } if (null >= 0) close(null); execvp(file, args); _exit(EXIT_FAILURE); } if (fds[0] > 0) { close(in[0]); *fds = in[1]; } if (fds[1] > 0) { close(out[1]); *(fds + 1) = out[0]; } if (fds[2] > 0) { close(err[1]); *(fds + 2) = err[0]; } if (null >= 0) close(null); return 1; err_out: if (err[0] >= 0) close(err[0]); if (err[1] >= 0) close(err[1]); if (out[0] >= 0) close(out[0]); if (out[1] >= 0) close(out[1]); if (in[0] >= 0) close(in[0]); if (in[1] >= 0) close(in[1]); if (null >= 0) close(null); PARA_ERROR_LOG("%s\n", para_strerror(-ret)); return ret; } /** * Exec the given command. * * \param pid Will hold the pid of the created process upon return. * \param cmdline Holds the command and its arguments, seperated by spaces. * \param fds A pointer to a value-result array. * * This function uses fork/exec to create a new process. \a fds must be a * pointer to three integers, corresponding to stdin, stdout and stderr * respectively. It specifies how to deal with fd 0, 1, 2 in the child. The * contents of \a fds are interpreted as follows: * * - fd[i] < 0: leave fd \a i alone. * - fd[i] = 0: dup fd \a i to \p /dev/null. * - fd[i] > 0: create a pipe and dup i to one end of that pipe. * Upon return, fd[i] contains the file descriptor of the pipe. * * In any case, all unneeded filedescriptors are closed. * * \return Standard. */ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds) { int ret; char **argv; char *tmp = para_strdup(cmdline); split_args(tmp, &argv, " \t"); ret = para_exec(pid, argv[0], argv, fds); free(argv); free(tmp); return ret; }