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