diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2015-12-15 10:39:39 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-12-17 12:27:14 -0500 |
commit | 4b6ab94eabe4f55371cff4569750bb3996c55db6 (patch) | |
tree | db5b95ed4647e3455bb1b1d4bb24137708a97829 /tools/lib/subcmd/run-command.c | |
parent | 2f4ce5ec1d447beb42143a9653716a2ab025161e (diff) |
perf subcmd: Create subcmd library
Move the subcommand-related files from perf to a new library named
libsubcmd.a.
Since we're moving files anyway, go ahead and rename 'exec_cmd.*' to
'exec-cmd.*' to be consistent with the naming of all the other files.
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/c0a838d4c878ab17fee50998811612b2281355c1.1450193761.git.jpoimboe@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/lib/subcmd/run-command.c')
-rw-r--r-- | tools/lib/subcmd/run-command.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/tools/lib/subcmd/run-command.c b/tools/lib/subcmd/run-command.c new file mode 100644 index 000000000000..f4f6c9eb8e59 --- /dev/null +++ b/tools/lib/subcmd/run-command.c | |||
@@ -0,0 +1,227 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <sys/types.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <string.h> | ||
6 | #include <errno.h> | ||
7 | #include <sys/wait.h> | ||
8 | #include "subcmd-util.h" | ||
9 | #include "run-command.h" | ||
10 | #include "exec-cmd.h" | ||
11 | |||
12 | #define STRERR_BUFSIZE 128 | ||
13 | |||
14 | static inline void close_pair(int fd[2]) | ||
15 | { | ||
16 | close(fd[0]); | ||
17 | close(fd[1]); | ||
18 | } | ||
19 | |||
20 | static inline void dup_devnull(int to) | ||
21 | { | ||
22 | int fd = open("/dev/null", O_RDWR); | ||
23 | dup2(fd, to); | ||
24 | close(fd); | ||
25 | } | ||
26 | |||
27 | int start_command(struct child_process *cmd) | ||
28 | { | ||
29 | int need_in, need_out, need_err; | ||
30 | int fdin[2], fdout[2], fderr[2]; | ||
31 | char sbuf[STRERR_BUFSIZE]; | ||
32 | |||
33 | /* | ||
34 | * In case of errors we must keep the promise to close FDs | ||
35 | * that have been passed in via ->in and ->out. | ||
36 | */ | ||
37 | |||
38 | need_in = !cmd->no_stdin && cmd->in < 0; | ||
39 | if (need_in) { | ||
40 | if (pipe(fdin) < 0) { | ||
41 | if (cmd->out > 0) | ||
42 | close(cmd->out); | ||
43 | return -ERR_RUN_COMMAND_PIPE; | ||
44 | } | ||
45 | cmd->in = fdin[1]; | ||
46 | } | ||
47 | |||
48 | need_out = !cmd->no_stdout | ||
49 | && !cmd->stdout_to_stderr | ||
50 | && cmd->out < 0; | ||
51 | if (need_out) { | ||
52 | if (pipe(fdout) < 0) { | ||
53 | if (need_in) | ||
54 | close_pair(fdin); | ||
55 | else if (cmd->in) | ||
56 | close(cmd->in); | ||
57 | return -ERR_RUN_COMMAND_PIPE; | ||
58 | } | ||
59 | cmd->out = fdout[0]; | ||
60 | } | ||
61 | |||
62 | need_err = !cmd->no_stderr && cmd->err < 0; | ||
63 | if (need_err) { | ||
64 | if (pipe(fderr) < 0) { | ||
65 | if (need_in) | ||
66 | close_pair(fdin); | ||
67 | else if (cmd->in) | ||
68 | close(cmd->in); | ||
69 | if (need_out) | ||
70 | close_pair(fdout); | ||
71 | else if (cmd->out) | ||
72 | close(cmd->out); | ||
73 | return -ERR_RUN_COMMAND_PIPE; | ||
74 | } | ||
75 | cmd->err = fderr[0]; | ||
76 | } | ||
77 | |||
78 | fflush(NULL); | ||
79 | cmd->pid = fork(); | ||
80 | if (!cmd->pid) { | ||
81 | if (cmd->no_stdin) | ||
82 | dup_devnull(0); | ||
83 | else if (need_in) { | ||
84 | dup2(fdin[0], 0); | ||
85 | close_pair(fdin); | ||
86 | } else if (cmd->in) { | ||
87 | dup2(cmd->in, 0); | ||
88 | close(cmd->in); | ||
89 | } | ||
90 | |||
91 | if (cmd->no_stderr) | ||
92 | dup_devnull(2); | ||
93 | else if (need_err) { | ||
94 | dup2(fderr[1], 2); | ||
95 | close_pair(fderr); | ||
96 | } | ||
97 | |||
98 | if (cmd->no_stdout) | ||
99 | dup_devnull(1); | ||
100 | else if (cmd->stdout_to_stderr) | ||
101 | dup2(2, 1); | ||
102 | else if (need_out) { | ||
103 | dup2(fdout[1], 1); | ||
104 | close_pair(fdout); | ||
105 | } else if (cmd->out > 1) { | ||
106 | dup2(cmd->out, 1); | ||
107 | close(cmd->out); | ||
108 | } | ||
109 | |||
110 | if (cmd->dir && chdir(cmd->dir)) | ||
111 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], | ||
112 | cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); | ||
113 | if (cmd->env) { | ||
114 | for (; *cmd->env; cmd->env++) { | ||
115 | if (strchr(*cmd->env, '=')) | ||
116 | putenv((char*)*cmd->env); | ||
117 | else | ||
118 | unsetenv(*cmd->env); | ||
119 | } | ||
120 | } | ||
121 | if (cmd->preexec_cb) | ||
122 | cmd->preexec_cb(); | ||
123 | if (cmd->exec_cmd) { | ||
124 | execv_cmd(cmd->argv); | ||
125 | } else { | ||
126 | execvp(cmd->argv[0], (char *const*) cmd->argv); | ||
127 | } | ||
128 | exit(127); | ||
129 | } | ||
130 | |||
131 | if (cmd->pid < 0) { | ||
132 | int err = errno; | ||
133 | if (need_in) | ||
134 | close_pair(fdin); | ||
135 | else if (cmd->in) | ||
136 | close(cmd->in); | ||
137 | if (need_out) | ||
138 | close_pair(fdout); | ||
139 | else if (cmd->out) | ||
140 | close(cmd->out); | ||
141 | if (need_err) | ||
142 | close_pair(fderr); | ||
143 | return err == ENOENT ? | ||
144 | -ERR_RUN_COMMAND_EXEC : | ||
145 | -ERR_RUN_COMMAND_FORK; | ||
146 | } | ||
147 | |||
148 | if (need_in) | ||
149 | close(fdin[0]); | ||
150 | else if (cmd->in) | ||
151 | close(cmd->in); | ||
152 | |||
153 | if (need_out) | ||
154 | close(fdout[1]); | ||
155 | else if (cmd->out) | ||
156 | close(cmd->out); | ||
157 | |||
158 | if (need_err) | ||
159 | close(fderr[1]); | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int wait_or_whine(pid_t pid) | ||
165 | { | ||
166 | char sbuf[STRERR_BUFSIZE]; | ||
167 | |||
168 | for (;;) { | ||
169 | int status, code; | ||
170 | pid_t waiting = waitpid(pid, &status, 0); | ||
171 | |||
172 | if (waiting < 0) { | ||
173 | if (errno == EINTR) | ||
174 | continue; | ||
175 | fprintf(stderr, " Error: waitpid failed (%s)", | ||
176 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
177 | return -ERR_RUN_COMMAND_WAITPID; | ||
178 | } | ||
179 | if (waiting != pid) | ||
180 | return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; | ||
181 | if (WIFSIGNALED(status)) | ||
182 | return -ERR_RUN_COMMAND_WAITPID_SIGNAL; | ||
183 | |||
184 | if (!WIFEXITED(status)) | ||
185 | return -ERR_RUN_COMMAND_WAITPID_NOEXIT; | ||
186 | code = WEXITSTATUS(status); | ||
187 | switch (code) { | ||
188 | case 127: | ||
189 | return -ERR_RUN_COMMAND_EXEC; | ||
190 | case 0: | ||
191 | return 0; | ||
192 | default: | ||
193 | return -code; | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | |||
198 | int finish_command(struct child_process *cmd) | ||
199 | { | ||
200 | return wait_or_whine(cmd->pid); | ||
201 | } | ||
202 | |||
203 | int run_command(struct child_process *cmd) | ||
204 | { | ||
205 | int code = start_command(cmd); | ||
206 | if (code) | ||
207 | return code; | ||
208 | return finish_command(cmd); | ||
209 | } | ||
210 | |||
211 | static void prepare_run_command_v_opt(struct child_process *cmd, | ||
212 | const char **argv, | ||
213 | int opt) | ||
214 | { | ||
215 | memset(cmd, 0, sizeof(*cmd)); | ||
216 | cmd->argv = argv; | ||
217 | cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; | ||
218 | cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; | ||
219 | cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; | ||
220 | } | ||
221 | |||
222 | int run_command_v_opt(const char **argv, int opt) | ||
223 | { | ||
224 | struct child_process cmd; | ||
225 | prepare_run_command_v_opt(&cmd, argv, opt); | ||
226 | return run_command(&cmd); | ||
227 | } | ||