root/tools/lib/subcmd/run-command.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. close_pair
  2. dup_devnull
  3. start_command
  4. wait_or_whine
  5. finish_command
  6. run_command
  7. prepare_run_command_v_opt
  8. run_command_v_opt

   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 
  16 static inline void close_pair(int fd[2])
  17 {
  18         close(fd[0]);
  19         close(fd[1]);
  20 }
  21 
  22 static inline void dup_devnull(int to)
  23 {
  24         int fd = open("/dev/null", O_RDWR);
  25         dup2(fd, to);
  26         close(fd);
  27 }
  28 
  29 int 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 
 166 static 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 
 200 int finish_command(struct child_process *cmd)
 201 {
 202         return wait_or_whine(cmd->pid);
 203 }
 204 
 205 int 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 
 213 static 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 
 224 int 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 }

/* [<][>][^][v][top][bottom][index][help] */