Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/lib/subcmd/run-command.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <unistd.h>
3
#include <sys/types.h>
4
#include <sys/stat.h>
5
#include <ctype.h>
6
#include <fcntl.h>
7
#include <string.h>
8
#include <linux/compiler.h>
9
#include <linux/string.h>
10
#include <errno.h>
11
#include <sys/wait.h>
12
#include "subcmd-util.h"
13
#include "run-command.h"
14
#include "exec-cmd.h"
15
16
#define STRERR_BUFSIZE 128
17
18
static inline void close_pair(int fd[2])
19
{
20
close(fd[0]);
21
close(fd[1]);
22
}
23
24
static inline void dup_devnull(int to)
25
{
26
int fd = open("/dev/null", O_RDWR);
27
dup2(fd, to);
28
close(fd);
29
}
30
31
int start_command(struct child_process *cmd)
32
{
33
int need_in, need_out, need_err;
34
int fdin[2], fdout[2], fderr[2];
35
char sbuf[STRERR_BUFSIZE];
36
37
/*
38
* In case of errors we must keep the promise to close FDs
39
* that have been passed in via ->in and ->out.
40
*/
41
42
need_in = !cmd->no_stdin && cmd->in < 0;
43
if (need_in) {
44
if (pipe(fdin) < 0) {
45
if (cmd->out > 0)
46
close(cmd->out);
47
return -ERR_RUN_COMMAND_PIPE;
48
}
49
cmd->in = fdin[1];
50
}
51
52
need_out = !cmd->no_stdout
53
&& !cmd->stdout_to_stderr
54
&& cmd->out < 0;
55
if (need_out) {
56
if (pipe(fdout) < 0) {
57
if (need_in)
58
close_pair(fdin);
59
else if (cmd->in)
60
close(cmd->in);
61
return -ERR_RUN_COMMAND_PIPE;
62
}
63
cmd->out = fdout[0];
64
}
65
66
need_err = !cmd->no_stderr && cmd->err < 0;
67
if (need_err) {
68
if (pipe(fderr) < 0) {
69
if (need_in)
70
close_pair(fdin);
71
else if (cmd->in)
72
close(cmd->in);
73
if (need_out)
74
close_pair(fdout);
75
else if (cmd->out)
76
close(cmd->out);
77
return -ERR_RUN_COMMAND_PIPE;
78
}
79
cmd->err = fderr[0];
80
}
81
82
fflush(NULL);
83
cmd->pid = fork();
84
if (!cmd->pid) {
85
if (cmd->no_stdin)
86
dup_devnull(0);
87
else if (need_in) {
88
dup2(fdin[0], 0);
89
close_pair(fdin);
90
} else if (cmd->in) {
91
dup2(cmd->in, 0);
92
close(cmd->in);
93
}
94
95
if (cmd->no_stderr)
96
dup_devnull(2);
97
else if (need_err) {
98
dup2(fderr[1], 2);
99
close_pair(fderr);
100
}
101
102
if (cmd->no_stdout)
103
dup_devnull(1);
104
else if (cmd->stdout_to_stderr)
105
dup2(2, 1);
106
else if (need_out) {
107
dup2(fdout[1], 1);
108
close_pair(fdout);
109
} else if (cmd->out > 1) {
110
dup2(cmd->out, 1);
111
close(cmd->out);
112
}
113
114
if (cmd->dir && chdir(cmd->dir))
115
die("exec %s: cd to %s failed (%s)", cmd->argv[0],
116
cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
117
if (cmd->env) {
118
for (; *cmd->env; cmd->env++) {
119
if (strchr(*cmd->env, '='))
120
putenv((char*)*cmd->env);
121
else
122
unsetenv(*cmd->env);
123
}
124
}
125
if (cmd->preexec_cb)
126
cmd->preexec_cb();
127
if (cmd->no_exec_cmd)
128
exit(cmd->no_exec_cmd(cmd));
129
if (cmd->exec_cmd) {
130
execv_cmd(cmd->argv);
131
} else {
132
execvp(cmd->argv[0], (char *const*) cmd->argv);
133
}
134
exit(127);
135
}
136
137
if (cmd->pid < 0) {
138
int err = errno;
139
if (need_in)
140
close_pair(fdin);
141
else if (cmd->in)
142
close(cmd->in);
143
if (need_out)
144
close_pair(fdout);
145
else if (cmd->out)
146
close(cmd->out);
147
if (need_err)
148
close_pair(fderr);
149
return err == ENOENT ?
150
-ERR_RUN_COMMAND_EXEC :
151
-ERR_RUN_COMMAND_FORK;
152
}
153
154
if (need_in)
155
close(fdin[0]);
156
else if (cmd->in)
157
close(cmd->in);
158
159
if (need_out)
160
close(fdout[1]);
161
else if (cmd->out)
162
close(cmd->out);
163
164
if (need_err)
165
close(fderr[1]);
166
167
return 0;
168
}
169
170
static int wait_or_whine(struct child_process *cmd, bool block)
171
{
172
bool finished = cmd->finished;
173
int result = cmd->finish_result;
174
175
while (!finished) {
176
int status, code;
177
pid_t waiting = waitpid(cmd->pid, &status, block ? 0 : WNOHANG);
178
179
if (!block && waiting == 0)
180
break;
181
182
if (waiting < 0 && errno == EINTR)
183
continue;
184
185
finished = true;
186
if (waiting < 0) {
187
char sbuf[STRERR_BUFSIZE];
188
189
fprintf(stderr, " Error: waitpid failed (%s)",
190
str_error_r(errno, sbuf, sizeof(sbuf)));
191
result = -ERR_RUN_COMMAND_WAITPID;
192
} else if (waiting != cmd->pid) {
193
result = -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
194
} else if (WIFSIGNALED(status)) {
195
result = -ERR_RUN_COMMAND_WAITPID_SIGNAL;
196
} else if (!WIFEXITED(status)) {
197
result = -ERR_RUN_COMMAND_WAITPID_NOEXIT;
198
} else {
199
code = WEXITSTATUS(status);
200
switch (code) {
201
case 127:
202
result = -ERR_RUN_COMMAND_EXEC;
203
break;
204
case 0:
205
result = 0;
206
break;
207
default:
208
result = -code;
209
break;
210
}
211
}
212
}
213
if (finished) {
214
cmd->finished = 1;
215
cmd->finish_result = result;
216
}
217
return result;
218
}
219
220
/*
221
* Conservative estimate of number of characaters needed to hold an a decoded
222
* integer, assume each 3 bits needs a character byte and plus a possible sign
223
* character.
224
*/
225
#ifndef is_signed_type
226
#define is_signed_type(type) (((type)(-1)) < (type)1)
227
#endif
228
#define MAX_STRLEN_TYPE(type) (sizeof(type) * 8 / 3 + (is_signed_type(type) ? 1 : 0))
229
230
int check_if_command_finished(struct child_process *cmd)
231
{
232
#ifdef __linux__
233
char filename[6 + MAX_STRLEN_TYPE(typeof(cmd->pid)) + 7 + 1];
234
char status_line[256];
235
FILE *status_file;
236
237
/*
238
* Check by reading /proc/<pid>/status as calling waitpid causes
239
* stdout/stderr to be closed and data lost.
240
*/
241
sprintf(filename, "/proc/%u/status", cmd->pid);
242
status_file = fopen(filename, "r");
243
if (status_file == NULL) {
244
/* Open failed assume finish_command was called. */
245
return true;
246
}
247
while (fgets(status_line, sizeof(status_line), status_file) != NULL) {
248
char *p;
249
250
if (strncmp(status_line, "State:", 6))
251
continue;
252
253
fclose(status_file);
254
p = status_line + 6;
255
while (isspace(*p))
256
p++;
257
return *p == 'Z' ? 1 : 0;
258
}
259
/* Read failed assume finish_command was called. */
260
fclose(status_file);
261
return 1;
262
#else
263
wait_or_whine(cmd, /*block=*/false);
264
return cmd->finished;
265
#endif
266
}
267
268
int finish_command(struct child_process *cmd)
269
{
270
return wait_or_whine(cmd, /*block=*/true);
271
}
272
273
int run_command(struct child_process *cmd)
274
{
275
int code = start_command(cmd);
276
if (code)
277
return code;
278
return finish_command(cmd);
279
}
280
281
static void prepare_run_command_v_opt(struct child_process *cmd,
282
const char **argv,
283
int opt)
284
{
285
memset(cmd, 0, sizeof(*cmd));
286
cmd->argv = argv;
287
cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
288
cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
289
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
290
}
291
292
int run_command_v_opt(const char **argv, int opt)
293
{
294
struct child_process cmd;
295
prepare_run_command_v_opt(&cmd, argv, opt);
296
return run_command(&cmd);
297
}
298
299