Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / SubProcess.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph distributed storage system
5  *
6  * Copyright (C) 2015 Mirantis Inc
7  *
8  * Author: Mykola Golub <mgolub@mirantis.com>
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  */
16
17 #ifndef SUB_PROCESS_H
18 #define SUB_PROCESS_H
19
20 #include <sys/wait.h>
21 #include <stdarg.h>
22 #include <sstream>
23 #include <vector>
24 #include <iostream>
25 #include <include/assert.h>
26 #include <common/errno.h>
27 #if defined(__FreeBSD__)
28 #include <sys/types.h>
29 #include <signal.h>
30 #endif
31
32 /**
33  * SubProcess:
34  * A helper class to spawn a subprocess.
35  *
36  * Example:
37  *
38  *   SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE);
39  *   if (cat.spawn() != 0) {
40  *     std::cerr << "cat failed: " << cat.err() << std::endl;
41  *     return false;
42  *   }
43  *   write_to_fd(cat.get_stdout(), "hello world!\n");
44  *   cat.close_stdout();
45  *   read_from_fd(cat.get_stdin(), buf);
46  *   if (cat.join() != 0) {
47  *     std::cerr << cat.err() << std::endl;
48  *     return false;
49  *   }
50  */
51
52 class SubProcess {
53 public:
54   enum std_fd_op{
55     KEEP,
56     CLOSE,
57     PIPE
58   };
59 public:
60   SubProcess(const char *cmd,
61              std_fd_op stdin_op = CLOSE,
62              std_fd_op stdout_op = CLOSE,
63              std_fd_op stderr_op = CLOSE);
64   virtual ~SubProcess();
65
66   void add_cmd_args(const char *arg, ...);
67   void add_cmd_arg(const char *arg);
68
69   virtual int spawn(); // Returns 0 on success or -errno on failure.
70   virtual int join();  // Returns exit code (0 on success).
71
72   bool is_spawned() const { return pid > 0; }
73
74   int get_stdin() const;
75   int get_stdout() const;
76   int get_stderr() const;
77
78   void close_stdin();
79   void close_stdout();
80   void close_stderr();
81
82   void kill(int signo = SIGTERM) const;
83
84   const std::string err() const;
85
86 protected:
87   bool is_child() const { return pid == 0; }
88   virtual void exec();
89
90 private:
91   void close(int &fd);
92
93 protected:
94   std::string cmd;
95   std::vector<std::string> cmd_args;
96   std_fd_op stdin_op;
97   std_fd_op stdout_op;
98   std_fd_op stderr_op;
99   int stdin_pipe_out_fd;
100   int stdout_pipe_in_fd;
101   int stderr_pipe_in_fd;
102   int pid;
103   std::ostringstream errstr;
104 };
105
106 class SubProcessTimed : public SubProcess {
107 public:
108   SubProcessTimed(const char *cmd, std_fd_op stdin_op = CLOSE,
109                   std_fd_op stdout_op = CLOSE, std_fd_op stderr_op = CLOSE,
110                   int timeout = 0, int sigkill = SIGKILL);
111
112 protected:
113   void exec() override;
114
115 private:
116   int timeout;
117   int sigkill;
118 };
119
120 inline SubProcess::SubProcess(const char *cmd_, std_fd_op stdin_op_, std_fd_op stdout_op_, std_fd_op stderr_op_) :
121   cmd(cmd_),
122   cmd_args(),
123   stdin_op(stdin_op_),
124   stdout_op(stdout_op_),
125   stderr_op(stderr_op_),
126   stdin_pipe_out_fd(-1),
127   stdout_pipe_in_fd(-1),
128   stderr_pipe_in_fd(-1),
129   pid(-1),
130   errstr() {
131 }
132
133 inline SubProcess::~SubProcess() {
134   assert(!is_spawned());
135   assert(stdin_pipe_out_fd == -1);
136   assert(stdout_pipe_in_fd == -1);
137   assert(stderr_pipe_in_fd == -1);
138 }
139
140 inline void SubProcess::add_cmd_args(const char *arg, ...) {
141   assert(!is_spawned());
142
143   va_list ap;
144   va_start(ap, arg);
145   const char *p = arg;
146   do {
147     add_cmd_arg(p);
148     p = va_arg(ap, const char*);
149   } while (p != NULL);
150   va_end(ap);
151 }
152
153 inline void SubProcess::add_cmd_arg(const char *arg) {
154   assert(!is_spawned());
155
156   cmd_args.push_back(arg);
157 }
158
159 inline int SubProcess::get_stdin() const {
160   assert(is_spawned());
161   assert(stdin_op == PIPE);
162
163   return stdin_pipe_out_fd;
164 }
165
166 inline int SubProcess::get_stdout() const {
167   assert(is_spawned());
168   assert(stdout_op == PIPE);
169
170   return stdout_pipe_in_fd;
171 }
172
173 inline int SubProcess::get_stderr() const {
174   assert(is_spawned());
175   assert(stderr_op == PIPE);
176
177   return stderr_pipe_in_fd;
178 }
179
180 inline void SubProcess::close(int &fd) {
181   if (fd == -1)
182     return;
183
184   ::close(fd);
185   fd = -1;
186 }
187
188 inline void SubProcess::close_stdin() {
189   assert(is_spawned());
190   assert(stdin_op == PIPE);
191
192   close(stdin_pipe_out_fd);
193 }
194
195 inline void SubProcess::close_stdout() {
196   assert(is_spawned());
197   assert(stdout_op == PIPE);
198
199   close(stdout_pipe_in_fd);
200 }
201
202 inline void SubProcess::close_stderr() {
203   assert(is_spawned());
204   assert(stderr_op == PIPE);
205
206   close(stderr_pipe_in_fd);
207 }
208
209 inline void SubProcess::kill(int signo) const {
210   assert(is_spawned());
211
212   int ret = ::kill(pid, signo);
213   assert(ret == 0);
214 }
215
216 inline const std::string SubProcess::err() const {
217   return errstr.str();
218 }
219
220 class fd_buf : public std::streambuf {
221   int fd;
222 public:
223   fd_buf (int fd) : fd(fd)
224   {}
225 protected:
226   int_type overflow (int_type c) override {
227     if (c == EOF) return EOF;
228     char buf = c;
229     if (write (fd, &buf, 1) != 1) {
230       return EOF;
231     }
232     return c;
233   }
234   std::streamsize xsputn (const char* s, std::streamsize count) override {
235     return write(fd, s, count);
236   }
237 };
238
239 inline int SubProcess::spawn() {
240   assert(!is_spawned());
241   assert(stdin_pipe_out_fd == -1);
242   assert(stdout_pipe_in_fd == -1);
243   assert(stderr_pipe_in_fd == -1);
244
245   enum { IN = 0, OUT = 1 };
246
247   int ipipe[2], opipe[2], epipe[2];
248
249   ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = epipe[0] = epipe[1] = -1;
250
251   int ret = 0;
252
253   if ((stdin_op == PIPE  && ::pipe(ipipe) == -1) ||
254       (stdout_op == PIPE && ::pipe(opipe) == -1) ||
255       (stderr_op == PIPE && ::pipe(epipe) == -1)) {
256     ret = -errno;
257     errstr << "pipe failed: " << cpp_strerror(errno);
258     goto fail;
259   }
260
261   pid = fork();
262
263   if (pid > 0) { // Parent
264     stdin_pipe_out_fd = ipipe[OUT]; close(ipipe[IN ]);
265     stdout_pipe_in_fd = opipe[IN ]; close(opipe[OUT]);
266     stderr_pipe_in_fd = epipe[IN ]; close(epipe[OUT]);
267     return 0;
268   }
269
270   if (pid == 0) { // Child
271     close(ipipe[OUT]);
272     close(opipe[IN ]);
273     close(epipe[IN ]);
274
275     if (ipipe[IN] != -1 && ipipe[IN] != STDIN_FILENO) {
276       ::dup2(ipipe[IN], STDIN_FILENO);
277       close(ipipe[IN]);
278     }
279     if (opipe[OUT] != -1 && opipe[OUT] != STDOUT_FILENO) {
280       ::dup2(opipe[OUT], STDOUT_FILENO);
281       close(opipe[OUT]);
282       static fd_buf buf(STDOUT_FILENO);
283       std::cout.rdbuf(&buf);
284     }
285     if (epipe[OUT] != -1 && epipe[OUT] != STDERR_FILENO) {
286       ::dup2(epipe[OUT], STDERR_FILENO);
287       close(epipe[OUT]);
288       static fd_buf buf(STDERR_FILENO);
289       std::cerr.rdbuf(&buf);
290     }
291
292     int maxfd = sysconf(_SC_OPEN_MAX);
293     if (maxfd == -1)
294       maxfd = 16384;
295     for (int fd = 0; fd <= maxfd; fd++) {
296       if (fd == STDIN_FILENO && stdin_op != CLOSE)
297         continue;
298       if (fd == STDOUT_FILENO && stdout_op != CLOSE)
299         continue;
300       if (fd == STDERR_FILENO && stderr_op != CLOSE)
301         continue;
302       ::close(fd);
303     }
304
305     exec();
306     ceph_abort(); // Never reached
307   }
308
309   ret = -errno;
310   errstr << "fork failed: " << cpp_strerror(errno);
311
312 fail:
313   close(ipipe[0]);
314   close(ipipe[1]);
315   close(opipe[0]);
316   close(opipe[1]);
317   close(epipe[0]);
318   close(epipe[1]);
319
320   return ret;
321 }
322
323 inline void SubProcess::exec() {
324   assert(is_child());
325
326   std::vector<const char *> args;
327   args.push_back(cmd.c_str());
328   for (std::vector<std::string>::iterator i = cmd_args.begin();
329        i != cmd_args.end();
330        i++) {
331     args.push_back(i->c_str());
332   }
333   args.push_back(NULL);
334
335   int ret = execvp(cmd.c_str(), (char * const *)&args[0]);
336   assert(ret == -1);
337
338   std::cerr << cmd << ": exec failed: " << cpp_strerror(errno) << "\n";
339   _exit(EXIT_FAILURE);
340 }
341
342 inline int SubProcess::join() {
343   assert(is_spawned());
344
345   close(stdin_pipe_out_fd);
346   close(stdout_pipe_in_fd);
347   close(stderr_pipe_in_fd);
348
349   int status;
350
351   while (waitpid(pid, &status, 0) == -1)
352     assert(errno == EINTR);
353
354   pid = -1;
355
356   if (WIFEXITED(status)) {
357     if (WEXITSTATUS(status) != EXIT_SUCCESS)
358       errstr << cmd << ": exit status: " << WEXITSTATUS(status);
359     return WEXITSTATUS(status);
360   }
361   if (WIFSIGNALED(status)) {
362     errstr << cmd << ": got signal: " << WTERMSIG(status);
363     return 128 + WTERMSIG(status);
364   }
365   errstr << cmd << ": waitpid: unknown status returned\n";
366   return EXIT_FAILURE;
367 }
368
369 inline SubProcessTimed::SubProcessTimed(const char *cmd, std_fd_op stdin_op,
370                                  std_fd_op stdout_op, std_fd_op stderr_op,
371                                  int timeout_, int sigkill_) :
372   SubProcess(cmd, stdin_op, stdout_op, stderr_op),
373   timeout(timeout_),
374   sigkill(sigkill_) {
375 }
376
377 static bool timedout = false; // only used after fork
378 static void timeout_sighandler(int sig) {
379   timedout = true;
380 }
381 static void dummy_sighandler(int sig) {}
382
383 inline void SubProcessTimed::exec() {
384   assert(is_child());
385
386   if (timeout <= 0) {
387     SubProcess::exec();
388     ceph_abort(); // Never reached
389   }
390
391   sigset_t mask, oldmask;
392   int pid;
393
394   // Restore default action for SIGTERM in case the parent process decided
395   // to ignore it.
396   if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
397     std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n";
398     goto fail_exit;
399   }
400   // Because SIGCHLD is ignored by default, setup dummy handler for it,
401   // so we can mask it.
402   if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) {
403     std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n";
404     goto fail_exit;
405   }
406   // Setup timeout handler.
407   if (signal(SIGALRM, timeout_sighandler) == SIG_ERR) {
408     std::cerr << cmd << ": signal failed: " << cpp_strerror(errno) << "\n";
409     goto fail_exit;
410   }
411   // Block interesting signals.
412   sigemptyset(&mask);
413   sigaddset(&mask, SIGINT);
414   sigaddset(&mask, SIGTERM);
415   sigaddset(&mask, SIGCHLD);
416   sigaddset(&mask, SIGALRM);
417   if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) {
418     std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n";
419     goto fail_exit;
420   }
421
422   pid = fork();
423
424   if (pid == -1) {
425     std::cerr << cmd << ": fork failed: " << cpp_strerror(errno) << "\n";
426     goto fail_exit;
427   }
428
429   if (pid == 0) { // Child
430     // Restore old sigmask.
431     if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {
432       std::cerr << cmd << ": sigprocmask failed: " << cpp_strerror(errno) << "\n";
433       goto fail_exit;
434     }
435     (void)setpgid(0, 0); // Become process group leader.
436     SubProcess::exec();
437     ceph_abort(); // Never reached
438   }
439
440   // Parent
441   (void)alarm(timeout);
442
443   for (;;) {
444     int signo;
445     if (sigwait(&mask, &signo) == -1) {
446       std::cerr << cmd << ": sigwait failed: " << cpp_strerror(errno) << "\n";
447       goto fail_exit;
448     }
449     switch (signo) {
450     case SIGCHLD:
451       int status;
452       if (waitpid(pid, &status, WNOHANG) == -1) {
453         std::cerr << cmd << ": waitpid failed: " << cpp_strerror(errno) << "\n";
454         goto fail_exit;
455       }
456       if (WIFEXITED(status))
457         _exit(WEXITSTATUS(status));
458       if (WIFSIGNALED(status))
459         _exit(128 + WTERMSIG(status));
460       std::cerr << cmd << ": unknown status returned\n";
461       goto fail_exit;
462     case SIGINT:
463     case SIGTERM:
464       // Pass SIGINT and SIGTERM, which are usually used to terminate
465       // a process, to the child.
466       if (::kill(pid, signo) == -1) {
467         std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n";
468         goto fail_exit;
469       }
470       continue;
471     case SIGALRM:
472       std::cerr << cmd << ": timed out (" << timeout << " sec)\n";
473       if (::killpg(pid, sigkill) == -1) {
474         std::cerr << cmd << ": kill failed: " << cpp_strerror(errno) << "\n";
475         goto fail_exit;
476       }
477       continue;
478     default:
479       std::cerr << cmd << ": sigwait: invalid signal: " << signo << "\n";
480       goto fail_exit;
481     }
482   }
483
484 fail_exit:
485   _exit(EXIT_FAILURE);
486 }
487
488 #endif