]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | #include "SubProcess.h" |
2 | ||
3 | #if defined(__FreeBSD__) || defined(__APPLE__) | |
4 | #include <sys/types.h> | |
5 | #include <signal.h> | |
6 | #endif | |
7 | #include <stdarg.h> | |
8 | #include <fcntl.h> | |
9 | #include <unistd.h> | |
10 | #include <iostream> | |
11 | ||
12 | #include "common/errno.h" | |
13 | #include "include/ceph_assert.h" | |
14 | #include "include/compat.h" | |
15 | ||
16 | SubProcess::SubProcess(const char *cmd_, std_fd_op stdin_op_, std_fd_op stdout_op_, std_fd_op stderr_op_) : | |
17 | cmd(cmd_), | |
18 | cmd_args(), | |
19 | stdin_op(stdin_op_), | |
20 | stdout_op(stdout_op_), | |
21 | stderr_op(stderr_op_), | |
22 | stdin_pipe_out_fd(-1), | |
23 | stdout_pipe_in_fd(-1), | |
24 | stderr_pipe_in_fd(-1), | |
25 | pid(-1), | |
26 | errstr() { | |
27 | } | |
28 | ||
29 | SubProcess::~SubProcess() { | |
30 | ceph_assert(!is_spawned()); | |
31 | ceph_assert(stdin_pipe_out_fd == -1); | |
32 | ceph_assert(stdout_pipe_in_fd == -1); | |
33 | ceph_assert(stderr_pipe_in_fd == -1); | |
34 | } | |
35 | ||
36 | void SubProcess::add_cmd_args(const char *arg, ...) { | |
37 | ceph_assert(!is_spawned()); | |
38 | ||
39 | va_list ap; | |
40 | va_start(ap, arg); | |
41 | const char *p = arg; | |
42 | do { | |
43 | add_cmd_arg(p); | |
44 | p = va_arg(ap, const char*); | |
45 | } while (p != NULL); | |
46 | va_end(ap); | |
47 | } | |
48 | ||
49 | void SubProcess::add_cmd_arg(const char *arg) { | |
50 | ceph_assert(!is_spawned()); | |
51 | ||
52 | cmd_args.push_back(arg); | |
53 | } | |
54 | ||
55 | int SubProcess::get_stdin() const { | |
56 | ceph_assert(is_spawned()); | |
57 | ceph_assert(stdin_op == PIPE); | |
58 | ||
59 | return stdin_pipe_out_fd; | |
60 | } | |
61 | ||
62 | int SubProcess::get_stdout() const { | |
63 | ceph_assert(is_spawned()); | |
64 | ceph_assert(stdout_op == PIPE); | |
65 | ||
66 | return stdout_pipe_in_fd; | |
67 | } | |
68 | ||
69 | int SubProcess::get_stderr() const { | |
70 | ceph_assert(is_spawned()); | |
71 | ceph_assert(stderr_op == PIPE); | |
72 | ||
73 | return stderr_pipe_in_fd; | |
74 | } | |
75 | ||
76 | void SubProcess::close(int &fd) { | |
77 | if (fd == -1) | |
78 | return; | |
79 | ||
80 | ::close(fd); | |
81 | fd = -1; | |
82 | } | |
83 | ||
84 | void SubProcess::close_stdin() { | |
85 | ceph_assert(is_spawned()); | |
86 | ceph_assert(stdin_op == PIPE); | |
87 | ||
88 | close(stdin_pipe_out_fd); | |
89 | } | |
90 | ||
91 | void SubProcess::close_stdout() { | |
92 | ceph_assert(is_spawned()); | |
93 | ceph_assert(stdout_op == PIPE); | |
94 | ||
95 | close(stdout_pipe_in_fd); | |
96 | } | |
97 | ||
98 | void SubProcess::close_stderr() { | |
99 | ceph_assert(is_spawned()); | |
100 | ceph_assert(stderr_op == PIPE); | |
101 | ||
102 | close(stderr_pipe_in_fd); | |
103 | } | |
104 | ||
105 | void SubProcess::kill(int signo) const { | |
106 | ceph_assert(is_spawned()); | |
107 | ||
108 | int ret = ::kill(pid, signo); | |
109 | ceph_assert(ret == 0); | |
110 | } | |
111 | ||
112 | const std::string SubProcess::err() const { | |
113 | return errstr.str(); | |
114 | } | |
115 | ||
116 | class fd_buf : public std::streambuf { | |
117 | int fd; | |
118 | public: | |
119 | fd_buf (int fd) : fd(fd) | |
120 | {} | |
121 | protected: | |
122 | int_type overflow (int_type c) override { | |
123 | if (c == EOF) return EOF; | |
124 | char buf = c; | |
125 | if (write (fd, &buf, 1) != 1) { | |
126 | return EOF; | |
127 | } | |
128 | return c; | |
129 | } | |
130 | std::streamsize xsputn (const char* s, std::streamsize count) override { | |
131 | return write(fd, s, count); | |
132 | } | |
133 | }; | |
134 | ||
135 | int SubProcess::spawn() { | |
136 | ceph_assert(!is_spawned()); | |
137 | ceph_assert(stdin_pipe_out_fd == -1); | |
138 | ceph_assert(stdout_pipe_in_fd == -1); | |
139 | ceph_assert(stderr_pipe_in_fd == -1); | |
140 | ||
141 | enum { IN = 0, OUT = 1 }; | |
142 | ||
143 | int ipipe[2], opipe[2], epipe[2]; | |
144 | ||
145 | ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = epipe[0] = epipe[1] = -1; | |
146 | ||
147 | int ret = 0; | |
148 | ||
9f95a23c TL |
149 | if ((stdin_op == PIPE && pipe_cloexec(ipipe, 0) == -1) || |
150 | (stdout_op == PIPE && pipe_cloexec(opipe, 0) == -1) || | |
151 | (stderr_op == PIPE && pipe_cloexec(epipe, 0) == -1)) { | |
11fdf7f2 TL |
152 | ret = -errno; |
153 | errstr << "pipe failed: " << cpp_strerror(errno); | |
154 | goto fail; | |
155 | } | |
156 | ||
157 | pid = fork(); | |
158 | ||
159 | if (pid > 0) { // Parent | |
160 | stdin_pipe_out_fd = ipipe[OUT]; close(ipipe[IN ]); | |
161 | stdout_pipe_in_fd = opipe[IN ]; close(opipe[OUT]); | |
162 | stderr_pipe_in_fd = epipe[IN ]; close(epipe[OUT]); | |
163 | return 0; | |
164 | } | |
165 | ||
166 | if (pid == 0) { // Child | |
167 | close(ipipe[OUT]); | |
168 | close(opipe[IN ]); | |
169 | close(epipe[IN ]); | |
170 | ||
171 | if (ipipe[IN] >= 0) { | |
172 | if (ipipe[IN] == STDIN_FILENO) { | |
173 | ::fcntl(STDIN_FILENO, F_SETFD, 0); /* clear FD_CLOEXEC */ | |
174 | } else { | |
175 | ::dup2(ipipe[IN], STDIN_FILENO); | |
176 | ::close(ipipe[IN]); | |
177 | } | |
178 | } | |
179 | if (opipe[OUT] >= 0) { | |
180 | if (opipe[OUT] == STDOUT_FILENO) { | |
181 | ::fcntl(STDOUT_FILENO, F_SETFD, 0); /* clear FD_CLOEXEC */ | |
182 | } else { | |
183 | ::dup2(opipe[OUT], STDOUT_FILENO); | |
184 | ::close(opipe[OUT]); | |
185 | static fd_buf buf(STDOUT_FILENO); | |
186 | std::cout.rdbuf(&buf); | |
187 | } | |
188 | } | |
189 | if (epipe[OUT] >= 0) { | |
190 | if (epipe[OUT] == STDERR_FILENO) { | |
191 | ::fcntl(STDERR_FILENO, F_SETFD, 0); /* clear FD_CLOEXEC */ | |
192 | } else { | |
193 | ::dup2(epipe[OUT], STDERR_FILENO); | |
194 | ::close(epipe[OUT]); | |
195 | static fd_buf buf(STDERR_FILENO); | |
196 | std::cerr.rdbuf(&buf); | |
197 | } | |
198 | } | |
199 | ||
200 | int maxfd = sysconf(_SC_OPEN_MAX); | |
201 | if (maxfd == -1) | |
202 | maxfd = 16384; | |
203 | for (int fd = 0; fd <= maxfd; fd++) { | |
204 | if (fd == STDIN_FILENO && stdin_op != CLOSE) | |
205 | continue; | |
206 | if (fd == STDOUT_FILENO && stdout_op != CLOSE) | |
207 | continue; | |
208 | if (fd == STDERR_FILENO && stderr_op != CLOSE) | |
209 | continue; | |
210 | ::close(fd); | |
211 | } | |
212 | ||
213 | exec(); | |
214 | ceph_abort(); // Never reached | |
215 | } | |
216 | ||
217 | ret = -errno; | |
218 | errstr << "fork failed: " << cpp_strerror(errno); | |
219 | ||
220 | fail: | |
221 | close(ipipe[0]); | |
222 | close(ipipe[1]); | |
223 | close(opipe[0]); | |
224 | close(opipe[1]); | |
225 | close(epipe[0]); | |
226 | close(epipe[1]); | |
227 | ||
228 | return ret; | |
229 | } | |
230 | ||
231 | void SubProcess::exec() { | |
232 | ceph_assert(is_child()); | |
233 | ||
234 | std::vector<const char *> args; | |
235 | args.push_back(cmd.c_str()); | |
236 | for (std::vector<std::string>::iterator i = cmd_args.begin(); | |
237 | i != cmd_args.end(); | |
238 | i++) { | |
239 | args.push_back(i->c_str()); | |
240 | } | |
241 | args.push_back(NULL); | |
242 | ||
243 | int ret = execvp(cmd.c_str(), (char * const *)&args[0]); | |
244 | ceph_assert(ret == -1); | |
245 | ||
246 | std::cerr << cmd << ": exec failed: " << cpp_strerror(errno) << "\n"; | |
247 | _exit(EXIT_FAILURE); | |
248 | } | |
249 | ||
250 | int SubProcess::join() { | |
251 | ceph_assert(is_spawned()); | |
252 | ||
253 | close(stdin_pipe_out_fd); | |
254 | close(stdout_pipe_in_fd); | |
255 | close(stderr_pipe_in_fd); | |
256 | ||
257 | int status; | |
258 | ||
259 | while (waitpid(pid, &status, 0) == -1) | |
260 | ceph_assert(errno == EINTR); | |
261 | ||
262 | pid = -1; | |
263 | ||
264 | if (WIFEXITED(status)) { | |
265 | if (WEXITSTATUS(status) != EXIT_SUCCESS) | |
266 | errstr << cmd << ": exit status: " << WEXITSTATUS(status); | |
267 | return WEXITSTATUS(status); | |
268 | } | |
269 | if (WIFSIGNALED(status)) { | |
270 | errstr << cmd << ": got signal: " << WTERMSIG(status); | |
271 | return 128 + WTERMSIG(status); | |
272 | } | |
273 | errstr << cmd << ": waitpid: unknown status returned\n"; | |
274 | return EXIT_FAILURE; | |
275 | } | |
276 | ||
277 | SubProcessTimed::SubProcessTimed(const char *cmd, std_fd_op stdin_op, | |
278 | std_fd_op stdout_op, std_fd_op stderr_op, | |
279 | int timeout_, int sigkill_) : | |
280 | SubProcess(cmd, stdin_op, stdout_op, stderr_op), | |
281 | timeout(timeout_), | |
282 | sigkill(sigkill_) { | |
283 | } | |
284 | ||
285 | static bool timedout = false; // only used after fork | |
286 | void timeout_sighandler(int sig) { | |
287 | timedout = true; | |
288 | } | |
289 | static void dummy_sighandler(int sig) {} | |
290 | ||
291 | void SubProcessTimed::exec() { | |
292 | ceph_assert(is_child()); | |
293 | ||
294 | if (timeout <= 0) { | |
295 | SubProcess::exec(); | |
296 | ceph_abort(); // Never reached | |
297 | } | |
298 | ||
299 | sigset_t mask, oldmask; | |
300 | int pid; | |
301 | ||
302 | // Restore default action for SIGTERM in case the parent process decided | |
303 | // to ignore it. | |
304 | if (signal(SIGTERM, SIG_DFL) == SIG_ERR) { | |
305 | std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n"; | |
306 | goto fail_exit; | |
307 | } | |
308 | // Because SIGCHLD is ignored by default, setup dummy handler for it, | |
309 | // so we can mask it. | |
310 | if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) { | |
311 | std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n"; | |
312 | goto fail_exit; | |
313 | } | |
314 | // Setup timeout handler. | |
315 | if (signal(SIGALRM, timeout_sighandler) == SIG_ERR) { | |
316 | std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n"; | |
317 | goto fail_exit; | |
318 | } | |
319 | // Block interesting signals. | |
320 | sigemptyset(&mask); | |
321 | sigaddset(&mask, SIGINT); | |
322 | sigaddset(&mask, SIGTERM); | |
323 | sigaddset(&mask, SIGCHLD); | |
324 | sigaddset(&mask, SIGALRM); | |
325 | if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) { | |
326 | std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n"; | |
327 | goto fail_exit; | |
328 | } | |
329 | ||
330 | pid = fork(); | |
331 | ||
332 | if (pid == -1) { | |
333 | std::cerr << cmd << ": fork failed: " << cpp_strerror(errno) << "\n"; | |
334 | goto fail_exit; | |
335 | } | |
336 | ||
337 | if (pid == 0) { // Child | |
338 | // Restore old sigmask. | |
339 | if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) { | |
340 | std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n"; | |
341 | goto fail_exit; | |
342 | } | |
343 | (void)setpgid(0, 0); // Become process group leader. | |
344 | SubProcess::exec(); | |
345 | ceph_abort(); // Never reached | |
346 | } | |
347 | ||
348 | // Parent | |
349 | (void)alarm(timeout); | |
350 | ||
351 | for (;;) { | |
352 | int signo; | |
353 | if (sigwait(&mask, &signo) == -1) { | |
354 | std::cerr << cmd << ": sigwait failed: " << cpp_strerror(errno) << "\n"; | |
355 | goto fail_exit; | |
356 | } | |
357 | switch (signo) { | |
358 | case SIGCHLD: | |
359 | int status; | |
360 | if (waitpid(pid, &status, WNOHANG) == -1) { | |
361 | std::cerr << cmd << ": waitpid failed: " << cpp_strerror(errno) << "\n"; | |
362 | goto fail_exit; | |
363 | } | |
364 | if (WIFEXITED(status)) | |
365 | _exit(WEXITSTATUS(status)); | |
366 | if (WIFSIGNALED(status)) | |
367 | _exit(128 + WTERMSIG(status)); | |
368 | std::cerr << cmd << ": unknown status returned\n"; | |
369 | goto fail_exit; | |
370 | case SIGINT: | |
371 | case SIGTERM: | |
372 | // Pass SIGINT and SIGTERM, which are usually used to terminate | |
373 | // a process, to the child. | |
374 | if (::kill(pid, signo) == -1) { | |
375 | std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n"; | |
376 | goto fail_exit; | |
377 | } | |
378 | continue; | |
379 | case SIGALRM: | |
380 | std::cerr << cmd << ": timed out (" << timeout << " sec)\n"; | |
381 | if (::killpg(pid, sigkill) == -1) { | |
382 | std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n"; | |
383 | goto fail_exit; | |
384 | } | |
385 | continue; | |
386 | default: | |
387 | std::cerr << cmd << ": sigwait: invalid signal: " << signo << "\n"; | |
388 | goto fail_exit; | |
389 | } | |
390 | } | |
391 | ||
392 | fail_exit: | |
393 | _exit(EXIT_FAILURE); | |
394 | } |