Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <unistd.h>
0003 #include <sys/types.h>
0004 #include <sys/stat.h>
0005 #include <fcntl.h>
0006 #include <string.h>
0007 #include <linux/string.h>
0008 #include <errno.h>
0009 #include <sys/wait.h>
0010 #include "subcmd-util.h"
0011 #include "run-command.h"
0012 #include "exec-cmd.h"
0013 
0014 #define STRERR_BUFSIZE 128
0015 
0016 static inline void close_pair(int fd[2])
0017 {
0018     close(fd[0]);
0019     close(fd[1]);
0020 }
0021 
0022 static inline void dup_devnull(int to)
0023 {
0024     int fd = open("/dev/null", O_RDWR);
0025     dup2(fd, to);
0026     close(fd);
0027 }
0028 
0029 int start_command(struct child_process *cmd)
0030 {
0031     int need_in, need_out, need_err;
0032     int fdin[2], fdout[2], fderr[2];
0033     char sbuf[STRERR_BUFSIZE];
0034 
0035     /*
0036      * In case of errors we must keep the promise to close FDs
0037      * that have been passed in via ->in and ->out.
0038      */
0039 
0040     need_in = !cmd->no_stdin && cmd->in < 0;
0041     if (need_in) {
0042         if (pipe(fdin) < 0) {
0043             if (cmd->out > 0)
0044                 close(cmd->out);
0045             return -ERR_RUN_COMMAND_PIPE;
0046         }
0047         cmd->in = fdin[1];
0048     }
0049 
0050     need_out = !cmd->no_stdout
0051         && !cmd->stdout_to_stderr
0052         && cmd->out < 0;
0053     if (need_out) {
0054         if (pipe(fdout) < 0) {
0055             if (need_in)
0056                 close_pair(fdin);
0057             else if (cmd->in)
0058                 close(cmd->in);
0059             return -ERR_RUN_COMMAND_PIPE;
0060         }
0061         cmd->out = fdout[0];
0062     }
0063 
0064     need_err = !cmd->no_stderr && cmd->err < 0;
0065     if (need_err) {
0066         if (pipe(fderr) < 0) {
0067             if (need_in)
0068                 close_pair(fdin);
0069             else if (cmd->in)
0070                 close(cmd->in);
0071             if (need_out)
0072                 close_pair(fdout);
0073             else if (cmd->out)
0074                 close(cmd->out);
0075             return -ERR_RUN_COMMAND_PIPE;
0076         }
0077         cmd->err = fderr[0];
0078     }
0079 
0080     fflush(NULL);
0081     cmd->pid = fork();
0082     if (!cmd->pid) {
0083         if (cmd->no_stdin)
0084             dup_devnull(0);
0085         else if (need_in) {
0086             dup2(fdin[0], 0);
0087             close_pair(fdin);
0088         } else if (cmd->in) {
0089             dup2(cmd->in, 0);
0090             close(cmd->in);
0091         }
0092 
0093         if (cmd->no_stderr)
0094             dup_devnull(2);
0095         else if (need_err) {
0096             dup2(fderr[1], 2);
0097             close_pair(fderr);
0098         }
0099 
0100         if (cmd->no_stdout)
0101             dup_devnull(1);
0102         else if (cmd->stdout_to_stderr)
0103             dup2(2, 1);
0104         else if (need_out) {
0105             dup2(fdout[1], 1);
0106             close_pair(fdout);
0107         } else if (cmd->out > 1) {
0108             dup2(cmd->out, 1);
0109             close(cmd->out);
0110         }
0111 
0112         if (cmd->dir && chdir(cmd->dir))
0113             die("exec %s: cd to %s failed (%s)", cmd->argv[0],
0114                 cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
0115         if (cmd->env) {
0116             for (; *cmd->env; cmd->env++) {
0117                 if (strchr(*cmd->env, '='))
0118                     putenv((char*)*cmd->env);
0119                 else
0120                     unsetenv(*cmd->env);
0121             }
0122         }
0123         if (cmd->preexec_cb)
0124             cmd->preexec_cb();
0125         if (cmd->exec_cmd) {
0126             execv_cmd(cmd->argv);
0127         } else {
0128             execvp(cmd->argv[0], (char *const*) cmd->argv);
0129         }
0130         exit(127);
0131     }
0132 
0133     if (cmd->pid < 0) {
0134         int err = errno;
0135         if (need_in)
0136             close_pair(fdin);
0137         else if (cmd->in)
0138             close(cmd->in);
0139         if (need_out)
0140             close_pair(fdout);
0141         else if (cmd->out)
0142             close(cmd->out);
0143         if (need_err)
0144             close_pair(fderr);
0145         return err == ENOENT ?
0146             -ERR_RUN_COMMAND_EXEC :
0147             -ERR_RUN_COMMAND_FORK;
0148     }
0149 
0150     if (need_in)
0151         close(fdin[0]);
0152     else if (cmd->in)
0153         close(cmd->in);
0154 
0155     if (need_out)
0156         close(fdout[1]);
0157     else if (cmd->out)
0158         close(cmd->out);
0159 
0160     if (need_err)
0161         close(fderr[1]);
0162 
0163     return 0;
0164 }
0165 
0166 static int wait_or_whine(pid_t pid)
0167 {
0168     char sbuf[STRERR_BUFSIZE];
0169 
0170     for (;;) {
0171         int status, code;
0172         pid_t waiting = waitpid(pid, &status, 0);
0173 
0174         if (waiting < 0) {
0175             if (errno == EINTR)
0176                 continue;
0177             fprintf(stderr, " Error: waitpid failed (%s)",
0178                 str_error_r(errno, sbuf, sizeof(sbuf)));
0179             return -ERR_RUN_COMMAND_WAITPID;
0180         }
0181         if (waiting != pid)
0182             return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
0183         if (WIFSIGNALED(status))
0184             return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
0185 
0186         if (!WIFEXITED(status))
0187             return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
0188         code = WEXITSTATUS(status);
0189         switch (code) {
0190         case 127:
0191             return -ERR_RUN_COMMAND_EXEC;
0192         case 0:
0193             return 0;
0194         default:
0195             return -code;
0196         }
0197     }
0198 }
0199 
0200 int finish_command(struct child_process *cmd)
0201 {
0202     return wait_or_whine(cmd->pid);
0203 }
0204 
0205 int run_command(struct child_process *cmd)
0206 {
0207     int code = start_command(cmd);
0208     if (code)
0209         return code;
0210     return finish_command(cmd);
0211 }
0212 
0213 static void prepare_run_command_v_opt(struct child_process *cmd,
0214                       const char **argv,
0215                       int opt)
0216 {
0217     memset(cmd, 0, sizeof(*cmd));
0218     cmd->argv = argv;
0219     cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
0220     cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
0221     cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
0222 }
0223 
0224 int run_command_v_opt(const char **argv, int opt)
0225 {
0226     struct child_process cmd;
0227     prepare_run_command_v_opt(&cmd, argv, opt);
0228     return run_command(&cmd);
0229 }