254 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 12815 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 FDs37 * 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_stdout51 && !cmd->stdout_to_stderr52 && 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 else120 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 168static 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 218int check_if_command_finished(struct child_process *cmd)219{220 wait_or_whine(cmd, /*block=*/false);221 return cmd->finished;222}223 224int finish_command(struct child_process *cmd)225{226 return wait_or_whine(cmd, /*block=*/true);227}228 229int 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 237static 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 248int 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