linux/tools/lib/subcmd/run-command.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <unistd.h>
   3#include <sys/types.h>
   4#include <sys/stat.h>
   5#include <fcntl.h>
   6#include <string.h>
   7#include <linux/string.h>
   8#include <errno.h>
   9#include <sys/wait.h>
  10#include "subcmd-util.h"
  11#include "run-command.h"
  12#include "exec-cmd.h"
  13
  14#define STRERR_BUFSIZE 128
  15
  16static inline void close_pair(int fd[2])
  17{
  18        close(fd[0]);
  19        close(fd[1]);
  20}
  21
  22static inline void dup_devnull(int to)
  23{
  24        int fd = open("/dev/null", O_RDWR);
  25        dup2(fd, to);
  26        close(fd);
  27}
  28
  29int start_command(struct child_process *cmd)
  30{
  31        int need_in, need_out, need_err;
  32        int fdin[2], fdout[2], fderr[2];
  33        char sbuf[STRERR_BUFSIZE];
  34
  35        /*
  36         * In case of errors we must keep the promise to close FDs
  37         * that have been passed in via ->in and ->out.
  38         */
  39
  40        need_in = !cmd->no_stdin && cmd->in < 0;
  41        if (need_in) {
  42                if (pipe(fdin) < 0) {
  43                        if (cmd->out > 0)
  44                                close(cmd->out);
  45                        return -ERR_RUN_COMMAND_PIPE;
  46                }
  47                cmd->in = fdin[1];
  48        }
  49
  50        need_out = !cmd->no_stdout
  51                && !cmd->stdout_to_stderr
  52                && cmd->out < 0;
  53        if (need_out) {
  54                if (pipe(fdout) < 0) {
  55                        if (need_in)
  56                                close_pair(fdin);
  57                        else if (cmd->in)
  58                                close(cmd->in);
  59                        return -ERR_RUN_COMMAND_PIPE;
  60                }
  61                cmd->out = fdout[0];
  62        }
  63
  64        need_err = !cmd->no_stderr && cmd->err < 0;
  65        if (need_err) {
  66                if (pipe(fderr) < 0) {
  67                        if (need_in)
  68                                close_pair(fdin);
  69                        else if (cmd->in)
  70                                close(cmd->in);
  71                        if (need_out)
  72                                close_pair(fdout);
  73                        else if (cmd->out)
  74                                close(cmd->out);
  75                        return -ERR_RUN_COMMAND_PIPE;
  76                }
  77                cmd->err = fderr[0];
  78        }
  79
  80        fflush(NULL);
  81        cmd->pid = fork();
  82        if (!cmd->pid) {
  83                if (cmd->no_stdin)
  84                        dup_devnull(0);
  85                else if (need_in) {
  86                        dup2(fdin[0], 0);
  87                        close_pair(fdin);
  88                } else if (cmd->in) {
  89                        dup2(cmd->in, 0);
  90                        close(cmd->in);
  91                }
  92
  93                if (cmd->no_stderr)
  94                        dup_devnull(2);
  95                else if (need_err) {
  96                        dup2(fderr[1], 2);
  97                        close_pair(fderr);
  98                }
  99
 100                if (cmd->no_stdout)
 101                        dup_devnull(1);
 102                else if (cmd->stdout_to_stderr)
 103                        dup2(2, 1);
 104                else if (need_out) {
 105                        dup2(fdout[1], 1);
 106                        close_pair(fdout);
 107                } else if (cmd->out > 1) {
 108                        dup2(cmd->out, 1);
 109                        close(cmd->out);
 110                }
 111
 112                if (cmd->dir && chdir(cmd->dir))
 113                        die("exec %s: cd to %s failed (%s)", cmd->argv[0],
 114                            cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
 115                if (cmd->env) {
 116                        for (; *cmd->env; cmd->env++) {
 117                                if (strchr(*cmd->env, '='))
 118                                        putenv((char*)*cmd->env);
 119                                else
 120                                        unsetenv(*cmd->env);
 121                        }
 122                }
 123                if (cmd->preexec_cb)
 124                        cmd->preexec_cb();
 125                if (cmd->exec_cmd) {
 126                        execv_cmd(cmd->argv);
 127                } else {
 128                        execvp(cmd->argv[0], (char *const*) cmd->argv);
 129                }
 130                exit(127);
 131        }
 132
 133        if (cmd->pid < 0) {
 134                int err = errno;
 135                if (need_in)
 136                        close_pair(fdin);
 137                else if (cmd->in)
 138                        close(cmd->in);
 139                if (need_out)
 140                        close_pair(fdout);
 141                else if (cmd->out)
 142                        close(cmd->out);
 143                if (need_err)
 144                        close_pair(fderr);
 145                return err == ENOENT ?
 146                        -ERR_RUN_COMMAND_EXEC :
 147                        -ERR_RUN_COMMAND_FORK;
 148        }
 149
 150        if (need_in)
 151                close(fdin[0]);
 152        else if (cmd->in)
 153                close(cmd->in);
 154
 155        if (need_out)
 156                close(fdout[1]);
 157        else if (cmd->out)
 158                close(cmd->out);
 159
 160        if (need_err)
 161                close(fderr[1]);
 162
 163        return 0;
 164}
 165
 166static int wait_or_whine(pid_t pid)
 167{
 168        char sbuf[STRERR_BUFSIZE];
 169
 170        for (;;) {
 171                int status, code;
 172                pid_t waiting = waitpid(pid, &status, 0);
 173
 174                if (waiting < 0) {
 175                        if (errno == EINTR)
 176                                continue;
 177                        fprintf(stderr, " Error: waitpid failed (%s)",
 178                                str_error_r(errno, sbuf, sizeof(sbuf)));
 179                        return -ERR_RUN_COMMAND_WAITPID;
 180                }
 181                if (waiting != pid)
 182                        return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
 183                if (WIFSIGNALED(status))
 184                        return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
 185
 186                if (!WIFEXITED(status))
 187                        return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
 188                code = WEXITSTATUS(status);
 189                switch (code) {
 190                case 127:
 191                        return -ERR_RUN_COMMAND_EXEC;
 192                case 0:
 193                        return 0;
 194                default:
 195                        return -code;
 196                }
 197        }
 198}
 199
 200int finish_command(struct child_process *cmd)
 201{
 202        return wait_or_whine(cmd->pid);
 203}
 204
 205int run_command(struct child_process *cmd)
 206{
 207        int code = start_command(cmd);
 208        if (code)
 209                return code;
 210        return finish_command(cmd);
 211}
 212
 213static void prepare_run_command_v_opt(struct child_process *cmd,
 214                                      const char **argv,
 215                                      int opt)
 216{
 217        memset(cmd, 0, sizeof(*cmd));
 218        cmd->argv = argv;
 219        cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
 220        cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
 221        cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
 222}
 223
 224int run_command_v_opt(const char **argv, int opt)
 225{
 226        struct child_process cmd;
 227        prepare_run_command_v_opt(&cmd, argv, opt);
 228        return run_command(&cmd);
 229}
 230