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 } 230