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(fd: 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(fd: fdin); |
69 | else if (cmd->in) |
70 | close(cmd->in); |
71 | if (need_out) |
72 | close_pair(fd: 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(to: 0); |
85 | else if (need_in) { |
86 | dup2(fdin[0], 0); |
87 | close_pair(fd: 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(to: 2); |
95 | else if (need_err) { |
96 | dup2(fderr[1], 2); |
97 | close_pair(fd: fderr); |
98 | } |
99 | |
100 | if (cmd->no_stdout) |
101 | dup_devnull(to: 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(fd: 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(err: "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(argv: 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(fd: fdin); |
139 | else if (cmd->in) |
140 | close(cmd->in); |
141 | if (need_out) |
142 | close_pair(fd: fdout); |
143 | else if (cmd->out) |
144 | close(cmd->out); |
145 | if (need_err) |
146 | close_pair(fd: 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(pid_t pid) |
169 | { |
170 | char sbuf[STRERR_BUFSIZE]; |
171 | |
172 | for (;;) { |
173 | int status, code; |
174 | pid_t waiting = waitpid(pid, &status, 0); |
175 | |
176 | if (waiting < 0) { |
177 | if (errno == EINTR) |
178 | continue; |
179 | fprintf(stderr, " Error: waitpid failed (%s)" , |
180 | str_error_r(errno, sbuf, sizeof(sbuf))); |
181 | return -ERR_RUN_COMMAND_WAITPID; |
182 | } |
183 | if (waiting != pid) |
184 | return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; |
185 | if (WIFSIGNALED(status)) |
186 | return -ERR_RUN_COMMAND_WAITPID_SIGNAL; |
187 | |
188 | if (!WIFEXITED(status)) |
189 | return -ERR_RUN_COMMAND_WAITPID_NOEXIT; |
190 | code = WEXITSTATUS(status); |
191 | switch (code) { |
192 | case 127: |
193 | return -ERR_RUN_COMMAND_EXEC; |
194 | case 0: |
195 | return 0; |
196 | default: |
197 | return -code; |
198 | } |
199 | } |
200 | } |
201 | |
202 | int finish_command(struct child_process *cmd) |
203 | { |
204 | return wait_or_whine(pid: cmd->pid); |
205 | } |
206 | |
207 | int run_command(struct child_process *cmd) |
208 | { |
209 | int code = start_command(cmd); |
210 | if (code) |
211 | return code; |
212 | return finish_command(cmd); |
213 | } |
214 | |
215 | static void prepare_run_command_v_opt(struct child_process *cmd, |
216 | const char **argv, |
217 | int opt) |
218 | { |
219 | memset(cmd, 0, sizeof(*cmd)); |
220 | cmd->argv = argv; |
221 | cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; |
222 | cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; |
223 | cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; |
224 | } |
225 | |
226 | int run_command_v_opt(const char **argv, int opt) |
227 | { |
228 | struct child_process cmd; |
229 | prepare_run_command_v_opt(cmd: &cmd, argv, opt); |
230 | return run_command(cmd: &cmd); |
231 | } |
232 | |