123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- #include <linux/string.h>
- #include <errno.h>
- #include <sys/wait.h>
- #include "subcmd-util.h"
- #include "run-command.h"
- #include "exec-cmd.h"
- #define STRERR_BUFSIZE 128
- static inline void close_pair(int fd[2])
- {
- close(fd[0]);
- close(fd[1]);
- }
- static inline void dup_devnull(int to)
- {
- int fd = open("/dev/null", O_RDWR);
- dup2(fd, to);
- close(fd);
- }
- int start_command(struct child_process *cmd)
- {
- int need_in, need_out, need_err;
- int fdin[2], fdout[2], fderr[2];
- char sbuf[STRERR_BUFSIZE];
- /*
- * In case of errors we must keep the promise to close FDs
- * that have been passed in via ->in and ->out.
- */
- need_in = !cmd->no_stdin && cmd->in < 0;
- if (need_in) {
- if (pipe(fdin) < 0) {
- if (cmd->out > 0)
- close(cmd->out);
- return -ERR_RUN_COMMAND_PIPE;
- }
- cmd->in = fdin[1];
- }
- need_out = !cmd->no_stdout
- && !cmd->stdout_to_stderr
- && cmd->out < 0;
- if (need_out) {
- if (pipe(fdout) < 0) {
- if (need_in)
- close_pair(fdin);
- else if (cmd->in)
- close(cmd->in);
- return -ERR_RUN_COMMAND_PIPE;
- }
- cmd->out = fdout[0];
- }
- need_err = !cmd->no_stderr && cmd->err < 0;
- if (need_err) {
- if (pipe(fderr) < 0) {
- if (need_in)
- close_pair(fdin);
- else if (cmd->in)
- close(cmd->in);
- if (need_out)
- close_pair(fdout);
- else if (cmd->out)
- close(cmd->out);
- return -ERR_RUN_COMMAND_PIPE;
- }
- cmd->err = fderr[0];
- }
- fflush(NULL);
- cmd->pid = fork();
- if (!cmd->pid) {
- if (cmd->no_stdin)
- dup_devnull(0);
- else if (need_in) {
- dup2(fdin[0], 0);
- close_pair(fdin);
- } else if (cmd->in) {
- dup2(cmd->in, 0);
- close(cmd->in);
- }
- if (cmd->no_stderr)
- dup_devnull(2);
- else if (need_err) {
- dup2(fderr[1], 2);
- close_pair(fderr);
- }
- if (cmd->no_stdout)
- dup_devnull(1);
- else if (cmd->stdout_to_stderr)
- dup2(2, 1);
- else if (need_out) {
- dup2(fdout[1], 1);
- close_pair(fdout);
- } else if (cmd->out > 1) {
- dup2(cmd->out, 1);
- close(cmd->out);
- }
- if (cmd->dir && chdir(cmd->dir))
- die("exec %s: cd to %s failed (%s)", cmd->argv[0],
- cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
- if (cmd->env) {
- for (; *cmd->env; cmd->env++) {
- if (strchr(*cmd->env, '='))
- putenv((char*)*cmd->env);
- else
- unsetenv(*cmd->env);
- }
- }
- if (cmd->preexec_cb)
- cmd->preexec_cb();
- if (cmd->exec_cmd) {
- execv_cmd(cmd->argv);
- } else {
- execvp(cmd->argv[0], (char *const*) cmd->argv);
- }
- exit(127);
- }
- if (cmd->pid < 0) {
- int err = errno;
- if (need_in)
- close_pair(fdin);
- else if (cmd->in)
- close(cmd->in);
- if (need_out)
- close_pair(fdout);
- else if (cmd->out)
- close(cmd->out);
- if (need_err)
- close_pair(fderr);
- return err == ENOENT ?
- -ERR_RUN_COMMAND_EXEC :
- -ERR_RUN_COMMAND_FORK;
- }
- if (need_in)
- close(fdin[0]);
- else if (cmd->in)
- close(cmd->in);
- if (need_out)
- close(fdout[1]);
- else if (cmd->out)
- close(cmd->out);
- if (need_err)
- close(fderr[1]);
- return 0;
- }
- static int wait_or_whine(pid_t pid)
- {
- char sbuf[STRERR_BUFSIZE];
- for (;;) {
- int status, code;
- pid_t waiting = waitpid(pid, &status, 0);
- if (waiting < 0) {
- if (errno == EINTR)
- continue;
- fprintf(stderr, " Error: waitpid failed (%s)",
- str_error_r(errno, sbuf, sizeof(sbuf)));
- return -ERR_RUN_COMMAND_WAITPID;
- }
- if (waiting != pid)
- return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
- if (WIFSIGNALED(status))
- return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
- if (!WIFEXITED(status))
- return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
- code = WEXITSTATUS(status);
- switch (code) {
- case 127:
- return -ERR_RUN_COMMAND_EXEC;
- case 0:
- return 0;
- default:
- return -code;
- }
- }
- }
- int finish_command(struct child_process *cmd)
- {
- return wait_or_whine(cmd->pid);
- }
- int run_command(struct child_process *cmd)
- {
- int code = start_command(cmd);
- if (code)
- return code;
- return finish_command(cmd);
- }
- static void prepare_run_command_v_opt(struct child_process *cmd,
- const char **argv,
- int opt)
- {
- memset(cmd, 0, sizeof(*cmd));
- cmd->argv = argv;
- cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
- cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
- cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
- }
- int run_command_v_opt(const char **argv, int opt)
- {
- struct child_process cmd;
- prepare_run_command_v_opt(&cmd, argv, opt);
- return run_command(&cmd);
- }
|