5af789f65caa57b725d7c19146c44f64a94bebb7
[m6w6/libmemcached] / test / lib / ForkAndExec.cpp
1 #include "ForkAndExec.hpp"
2
3 #include <cstdio>
4
5 #include <fcntl.h>
6 #include <sys/poll.h>
7 #include <unistd.h>
8 #include <spawn.h>
9
10 #if !HAVE_PIPE2
11 static inline int setfl(int fd, int newflags) {
12 auto oldflags = fcntl(fd, F_GETFL);
13 return fcntl(fd, F_SETFL, oldflags | newflags);
14 }
15 static inline int pipe2(int pipefd[2], int flags) {
16 int rc;
17
18 rc = pipe(pipefd);
19 if (0 > rc) {
20 return rc;
21 }
22 rc = setfl(pipefd[0], flags);
23 if (0 > rc) {
24 return rc;
25 }
26 return setfl(pipefd[1], flags);
27 }
28 #endif
29
30 ForkAndExec::ForkAndExec(const char *binary_, char **argv_)
31 : ready{-1, -1}
32 , pipes{-1, -1}
33 , binary{binary_}
34 , argv{argv_}
35 {
36 }
37
38 ForkAndExec::~ForkAndExec() {
39 for (auto &pipe : {ready, pipes}) {
40 for (auto m : {READ, WRITE}) {
41 closePipe(pipe[m]);
42 }
43 }
44 }
45
46 int ForkAndExec::createPipe() {
47 if (pipes[mode::READ] == -1) {
48 if(pipe2(pipes, O_NONBLOCK)) {
49 perror("ForkAndExec pipe()");
50 return -1;
51 }
52 }
53
54 return pipes[mode::READ];
55 }
56
57 pid_t ForkAndExec::operator()() {
58 if (!prepareExecReadyPipe()) {
59 return 0;
60 }
61
62 switch (pid_t pid = fork()) {
63 case 0:
64 closePipe(pipes[mode::READ]);
65 prepareOutputPipe();
66 execvp(binary, argv);
67 perror("ForkAndExec exec()");
68 _exit(EXIT_FAILURE);
69
70 case -1:
71 perror("ForkAndExec fork()");
72 return 0;
73
74 default:
75 pipes[mode::READ] = -1;
76 closePipe(pipes[mode::WRITE]);
77 pollExecReadyPipe();
78 return pid;
79 }
80 }
81
82 bool ForkAndExec::prepareExecReadyPipe() {
83 if (ready[mode::READ] == -1) {
84 if (pipe2(ready, O_CLOEXEC | O_NONBLOCK)) {
85 perror("ForkAndExec pipe()");
86 return false;
87 }
88 closePipe(ready[mode::WRITE]);
89 }
90
91 return true;
92 }
93
94 void ForkAndExec::prepareOutputPipe() {
95 if (pipes[mode::WRITE] != -1) {
96 if (0 > dup2(pipes[mode::WRITE], STDERR_FILENO) ||
97 0 > dup2(pipes[mode::WRITE], STDOUT_FILENO)) {
98 perror("ForkAndExec dup()");
99 }
100 }
101 }
102
103 void ForkAndExec::closePipe(int &fd) {
104 if (fd != -1) {
105 close(fd);
106 fd = -1;
107 }
108 }
109
110 void ForkAndExec::pollExecReadyPipe() {
111 #if __APPLE__
112 char c, n = 50;
113 do {
114 if (0 == read(ready[mode::READ], &c, 1)) {
115 return;
116 }
117 this_thread::sleep_for(100ms);
118 } while (errno == EAGAIN && n--);
119 #else
120 pollfd fd{ready[mode::READ], 0, 0};
121 if (1 > poll(&fd, 1, 5000)) {
122 cerr << "exec() timed out" << endl;
123 }
124 #endif
125 }