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->no_exec_cmd) 126 exit(cmd->no_exec_cmd(cmd)); 127 if (cmd->exec_cmd) { 128 execv_cmd(cmd->argv); 129 } else { 130 execvp(cmd->argv[0], (char *const*) cmd->argv); 131 } 132 exit(127); 133 } 134 135 if (cmd->pid < 0) { 136 int err = errno; 137 if (need_in) 138 close_pair(fdin); 139 else if (cmd->in) 140 close(cmd->in); 141 if (need_out) 142 close_pair(fdout); 143 else if (cmd->out) 144 close(cmd->out); 145 if (need_err) 146 close_pair(fderr); 147 return err == ENOENT ? 148 -ERR_RUN_COMMAND_EXEC : 149 -ERR_RUN_COMMAND_FORK; 150 } 151 152 if (need_in) 153 close(fdin[0]); 154 else if (cmd->in) 155 close(cmd->in); 156 157 if (need_out) 158 close(fdout[1]); 159 else if (cmd->out) 160 close(cmd->out); 161 162 if (need_err) 163 close(fderr[1]); 164 165 return 0; 166 } 167 168 static int wait_or_whine(struct child_process *cmd, bool block) 169 { 170 bool finished = cmd->finished; 171 int result = cmd->finish_result; 172 173 while (!finished) { 174 int status, code; 175 pid_t waiting = waitpid(cmd->pid, &status, block ? 0 : WNOHANG); 176 177 if (!block && waiting == 0) 178 break; 179 180 if (waiting < 0 && errno == EINTR) 181 continue; 182 183 finished = true; 184 if (waiting < 0) { 185 char sbuf[STRERR_BUFSIZE]; 186 187 fprintf(stderr, " Error: waitpid failed (%s)", 188 str_error_r(errno, sbuf, sizeof(sbuf))); 189 result = -ERR_RUN_COMMAND_WAITPID; 190 } else if (waiting != cmd->pid) { 191 result = -ERR_RUN_COMMAND_WAITPID_WRONG_PID; 192 } else if (WIFSIGNALED(status)) { 193 result = -ERR_RUN_COMMAND_WAITPID_SIGNAL; 194 } else if (!WIFEXITED(status)) { 195 result = -ERR_RUN_COMMAND_WAITPID_NOEXIT; 196 } else { 197 code = WEXITSTATUS(status); 198 switch (code) { 199 case 127: 200 result = -ERR_RUN_COMMAND_EXEC; 201 break; 202 case 0: 203 result = 0; 204 break; 205 default: 206 result = -code; 207 break; 208 } 209 } 210 } 211 if (finished) { 212 cmd->finished = 1; 213 cmd->finish_result = result; 214 } 215 return result; 216 } 217 218 int check_if_command_finished(struct child_process *cmd) 219 { 220 wait_or_whine(cmd, /*block=*/false); 221 return cmd->finished; 222 } 223 224 int finish_command(struct child_process *cmd) 225 { 226 return wait_or_whine(cmd, /*block=*/true); 227 } 228 229 int run_command(struct child_process *cmd) 230 { 231 int code = start_command(cmd); 232 if (code) 233 return code; 234 return finish_command(cmd); 235 } 236 237 static void prepare_run_command_v_opt(struct child_process *cmd, 238 const char **argv, 239 int opt) 240 { 241 memset(cmd, 0, sizeof(*cmd)); 242 cmd->argv = argv; 243 cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; 244 cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; 245 cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; 246 } 247 248 int run_command_v_opt(const char **argv, int opt) 249 { 250 struct child_process cmd; 251 prepare_run_command_v_opt(&cmd, argv, opt); 252 return run_command(&cmd); 253 } 254
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.