1 /*****************************************************************************
2 * Copyright 2014 - 2015 Yichao Yu <yyc1992@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU Lesser General Public License as *
6 * published by the Free Software Foundation; either version 2.1 of the *
7 * License, or (at your option) version 3, or any later version accepted *
8 * by the membership of KDE e.V. (or its successor approved by the *
9 * membership of KDE e.V.), which shall act as a proxy defined in *
10 * Section 6 of version 3 of the license. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
15 * Lesser General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with this library. If not, *
19 * see <http://www.gnu.org/licenses/>. *
20 *****************************************************************************/
21
22 #include "process.h"
23 #include "fd_utils.h"
24 #include "timer.h"
25 #include <unistd.h>
26 #include <sys/wait.h>
27 #include <signal.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <poll.h>
33
34 static bool
qtcSignalHandlerSet(int sig)35 qtcSignalHandlerSet(int sig)
36 {
37 struct sigaction oact;
38 QTC_RET_IF_FAIL(sigaction(sig, nullptr, &oact) == 0, false);
39 void *handler = ((oact.sa_flags & SA_SIGINFO) ? (void*)oact.sa_handler :
40 (void*)oact.sa_sigaction);
41 return QtCurve::noneOf(handler, SIG_DFL, SIG_IGN);
42 }
43
44 QTC_EXPORT bool
qtcForkBackground(QtcCallback cb,void * data,QtcCallback fail_cb)45 qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb)
46 {
47 QTC_RET_IF_FAIL(cb, false);
48 // On linux, waitpid will not accept (discard) SIGCHLD therefore if there is
49 // a signal handler registered for SIGCHLD and the child process exit
50 // inside waitpid()/wait(), it will be run after the process state is
51 // cleared and would therefore block if it call wait() (or waitpid(-1))
52 // and if there are other child processes. As a workaround we only call
53 // waitpid() if the main program did not set up any signal handlers for
54 // SIGCHLD. See (the RATIONALE section of) wait(3P) for more detail.
55 pid_t child = fork();
56 if (child < 0) {
57 return false;
58 } else if (child == 0) {
59 pid_t grandchild = fork();
60 if (grandchild < 0) {
61 qtcCall(fail_cb, data);
62 _exit(1);
63 } else if (grandchild == 0) {
64 /* grandchild */
65 cb(data);
66 _exit(0);
67 } else {
68 _exit(0);
69 }
70 return true;
71 } else {
72 /* parent */
73 if (qtcSignalHandlerSet(SIGCHLD)) {
74 // If we create a child process, the signal handler will receive
75 // the signal anyway (and there is no way to only block SIGCHLD
76 // only for our child process). Since the signal handler may
77 // hang and should already take care of getting rid of
78 // zombie processes, we do not call waitpid in this case....
79 return true;
80 }
81 // If SIGCHLD is ignored, waitpid will return -1 with errno
82 // set to ECHILD, treat this as success (good enough for our purpose
83 // and not likely to fail anyway...)
84 int status = 0;
85 return ((waitpid(child, &status, 0) > 0 && status == 0) ||
86 errno == ECHILD);
87 }
88 }
89
90 typedef struct {
91 const char *file;
92 char *const *argv;
93 QtcCallback cb;
94 void *cb_data;
95 QtcCallback fail_cb;
96 } QtcSpawnData;
97
98 static void
qtcSpawnCb(void * _data)99 qtcSpawnCb(void *_data)
100 {
101 const QtcSpawnData *data = (const QtcSpawnData*)_data;
102 qtcCall(data->cb, data->cb_data);
103 execvp(data->file, data->argv);
104 }
105
106 static void
qtcSpawnFailCb(void * _data)107 qtcSpawnFailCb(void *_data)
108 {
109 const QtcSpawnData *data = (const QtcSpawnData*)_data;
110 qtcCall(data->fail_cb, data->cb_data);
111 }
112
113 QTC_EXPORT bool
qtcSpawn(const char * file,const char * const * argv,QtcCallback cb,void * cb_data,QtcCallback fail_cb)114 qtcSpawn(const char *file, const char *const *argv, QtcCallback cb,
115 void *cb_data, QtcCallback fail_cb)
116 {
117 QtcSpawnData data = {file, (char *const*)argv, cb, cb_data, fail_cb};
118 return qtcForkBackground(qtcSpawnCb, &data, qtcSpawnFailCb);
119 }
120
121 typedef struct {
122 int ctrl_fd;
123 unsigned fd_num;
124 QtcPopenFD *fds;
125 } QtcPopenData;
126
127 static void
qtcPopenCb(void * _data)128 qtcPopenCb(void *_data)
129 {
130 QtcPopenData *data = (QtcPopenData*)_data;
131 for (unsigned i = 0;i < data->fd_num;i++) {
132 int mode = data->fds[i].mode & QTC_POPEN_RDWR;
133 int ret_fd = -1;
134 int replace_fd = -1;
135 if (!mode) {
136 replace_fd = open("/dev/null", O_RDWR);
137 // Make sure a valid fd is sent.
138 ret_fd = replace_fd;
139 } else {
140 // Open socket pairs in the child process and send it back to
141 // parent with a unix domain socket so that the write end of the
142 // pair is always under control.
143 // For writing to sub process, the parent will shutdown the write
144 // end when it is done (therefore the client will receive EOF even
145 // if the parent forks other subprocesses which keeps the pipes
146 // open).
147 // For reading from sub process, the write end of the pipe is not
148 // shared with any other process so the parent will receive EOF
149 // whenever the client closes the pipe or exit.
150 // See http://stackoverflow.com/questions/1583005/is-there-any-difference-between-socketpair-and-pair-of-unnamed-pipes
151 int socket_fds[2];
152 socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
153 ret_fd = socket_fds[0];
154 replace_fd = socket_fds[1];
155 if (!(mode & QTC_POPEN_READ)) {
156 shutdown(ret_fd, SHUT_RD);
157 shutdown(replace_fd, SHUT_WR);
158 } else if (!(mode & QTC_POPEN_WRITE)) {
159 shutdown(ret_fd, SHUT_WR);
160 shutdown(replace_fd, SHUT_RD);
161 }
162 }
163 dup2(replace_fd, data->fds[i].orig);
164 close(replace_fd);
165 qtcSendFD(data->ctrl_fd, ret_fd);
166 close(ret_fd);
167 }
168 shutdown(data->ctrl_fd, SHUT_RDWR);
169 close(data->ctrl_fd);
170 }
171
172 static void
qtcPopenFailCb(void * _data)173 qtcPopenFailCb(void *_data)
174 {
175 QtcPopenData *data = (QtcPopenData*)_data;
176 // Notify the parent that sth goes wrong.
177 shutdown(data->ctrl_fd, SHUT_RDWR);
178 close(data->ctrl_fd);
179 }
180
181 QTC_EXPORT bool
qtcPopen(const char * file,const char * const * argv,unsigned fd_num,QtcPopenFD * fds)182 qtcPopen(const char *file, const char *const *argv,
183 unsigned fd_num, QtcPopenFD *fds)
184 {
185 if (qtcUnlikely(!fds || !fd_num)) {
186 return qtcSpawn(file, argv, nullptr, nullptr);
187 }
188 for (unsigned i = 0;i < fd_num;i++) {
189 QTC_RET_IF_FAIL(fds[i].orig >= 0, false);
190 }
191 int socket_fds[2];
192 QTC_RET_IF_FAIL(socketpair(AF_UNIX, SOCK_STREAM, 0,
193 socket_fds) == 0, false);
194 qtcFDSetCloexec(socket_fds[0], true);
195 qtcFDSetCloexec(socket_fds[1], true);
196 QtcPopenData cbdata = {socket_fds[0], fd_num, fds};
197 bool res = qtcSpawn(file, argv, qtcPopenCb, &cbdata, qtcPopenFailCb);
198 if (!res) {
199 shutdown(socket_fds[0], SHUT_RDWR);
200 close(socket_fds[0]);
201 shutdown(socket_fds[1], SHUT_RDWR);
202 close(socket_fds[1]);
203 return false;
204 }
205 close(socket_fds[0]);
206 for (unsigned i = 0;i < fd_num;i++) {
207 if ((fds[i].replace = qtcRecvFD(socket_fds[1])) < 0) {
208 res = false;
209 for (unsigned j = 0;j < i;j++) {
210 if (fds[i].replace) {
211 shutdown(fds[i].replace, SHUT_RDWR);
212 close(fds[i].replace);
213 }
214 }
215 break;
216 }
217 if (!(fds[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) {
218 close(fds[i].replace);
219 fds[i].replace = -1;
220 continue;
221 }
222 }
223 shutdown(socket_fds[1], SHUT_RDWR);
224 close(socket_fds[1]);
225 return res;
226 }
227
228 static bool
qtcPopenReadBuff(QtcPopenBuff * buffs)229 qtcPopenReadBuff(QtcPopenBuff *buffs)
230 {
231 buffs->buff = (char*)realloc(buffs->buff, buffs->len + 1024 + 1);
232 ssize_t len = read(buffs->orig, buffs->buff + buffs->len, 1024);
233 if (len == 0 || (len == -1 && QtCurve::noneOf(errno, EAGAIN, EINTR,
234 EWOULDBLOCK))) {
235 return false;
236 } else if (len > 0) {
237 buffs->len += len;
238 }
239 return true;
240 }
241
242 static bool
qtcPopenWriteBuff(QtcPopenBuff * buffs)243 qtcPopenWriteBuff(QtcPopenBuff *buffs)
244 {
245 ssize_t len = write(buffs->orig, buffs->buff, buffs->len);
246 if (len == 0 || (len == -1 && QtCurve::noneOf(errno, EAGAIN, EINTR,
247 EWOULDBLOCK))) {
248 return false;
249 } else if (len > 0) {
250 buffs->buff += len;
251 buffs->len -= len;
252 }
253 return true;
254 }
255
256 static bool
qtcPopenPollCheckTimeout(uint64_t start,int timeout,int * new_timeout)257 qtcPopenPollCheckTimeout(uint64_t start, int timeout, int *new_timeout)
258 {
259 if (timeout < 0) {
260 *new_timeout = -1;
261 return true;
262 }
263 int elapse = QtCurve::getElapse(start) / 1000000;
264 if (elapse > timeout) {
265 return false;
266 }
267 *new_timeout = timeout - elapse;
268 return true;
269 }
270
271 QTC_EXPORT bool
qtcPopenBuff(const char * file,const char * const argv[],unsigned buff_num,QtcPopenBuff * buffs,int timeout)272 qtcPopenBuff(const char *file, const char *const argv[],
273 unsigned buff_num, QtcPopenBuff *buffs, int timeout)
274 {
275 if (qtcUnlikely(!buffs || !buff_num)) {
276 return qtcSpawn(file, argv, nullptr, nullptr);
277 }
278 bool need_poll = false;
279 for (unsigned i = 0;i < buff_num;i++) {
280 QTC_RET_IF_FAIL(buffs[i].orig >= 0, false);
281 QTC_RET_IF_FAIL(!(buffs[i].mode & QTC_POPEN_READ &&
282 buffs[i].mode & QTC_POPEN_WRITE), false);
283 if (buffs[i].mode & QTC_POPEN_READ ||
284 buffs[i].mode & QTC_POPEN_WRITE) {
285 need_poll = true;
286 }
287 }
288 QtCurve::LocalBuff<QtcPopenFD, 16> fds(buff_num);
289 for (unsigned i = 0;i < buff_num;i++) {
290 fds[i].orig = buffs[i].orig;
291 fds[i].replace = -1;
292 fds[i].mode = buffs[i].mode;
293 }
294 bool res = qtcPopen(file, argv, buff_num, fds.get());
295 if (!res) {
296 return false;
297 }
298 for (unsigned i = 0;i < buff_num;i++) {
299 buffs[i].orig = fds[i].replace;
300 if (fds[i].replace >= 0) {
301 qtcFDSetNonBlock(fds[i].replace, true);
302 qtcFDSetCloexec(fds[i].replace, true);
303 }
304 }
305 if (!need_poll) {
306 return true;
307 }
308 QtCurve::LocalBuff<pollfd, 16> poll_fds(buff_num);
309 QtCurve::LocalBuff<int, 16> indexes(buff_num);
310 unsigned poll_fd_num = 0;
311 for (unsigned i = 0;i < buff_num;i++) {
312 if (!(buffs[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) {
313 close(buffs[i].orig);
314 continue;
315 }
316 indexes[poll_fd_num] = i;
317 pollfd *cur_fd = &poll_fds[poll_fd_num];
318 cur_fd->fd = buffs[i].orig;
319 cur_fd->events = (buffs[i].mode & QTC_POPEN_READ) ? POLLIN : POLLOUT;
320 poll_fd_num++;
321 }
322 uint64_t start_time = QtCurve::getTime();
323 int poll_timeout = timeout;
324 while (true) {
325 int ret = poll(poll_fds.get(), poll_fd_num, poll_timeout);
326 if (ret == -1) {
327 if (errno == EINTR) {
328 if (!qtcPopenPollCheckTimeout(start_time, timeout,
329 &poll_timeout)) {
330 break;
331 }
332 continue;
333 }
334 break;
335 } else if (ret == 0) {
336 break;
337 }
338 for (unsigned i = 0;i < poll_fd_num;i++) {
339 pollfd *cur_fd = &poll_fds[i];
340 if (cur_fd->revents & POLLIN) {
341 if (!qtcPopenReadBuff(&buffs[indexes[i]])) {
342 cur_fd->events &= ~POLLIN;
343 }
344 } else if (cur_fd->revents & POLLOUT) {
345 if (!qtcPopenWriteBuff(&buffs[indexes[i]])) {
346 cur_fd->events &= ~POLLOUT;
347 }
348 }
349 if (cur_fd->revents & (POLLERR | POLLHUP | POLLNVAL) ||
350 !(cur_fd->events & (POLLIN | POLLOUT))) {
351 shutdown(cur_fd->fd, SHUT_RDWR);
352 close(cur_fd->fd);
353 poll_fd_num--;
354 memmove(cur_fd, cur_fd + 1,
355 (poll_fd_num - i) * sizeof(pollfd));
356 memmove(indexes.get() + i, indexes.get() + i + 1,
357 (poll_fd_num - i) * sizeof(int));
358 i--;
359 }
360 }
361 if (poll_fd_num <= 0 || !qtcPopenPollCheckTimeout(start_time, timeout,
362 &poll_timeout)) {
363 break;
364 }
365 }
366 for (unsigned i = 0;i < poll_fd_num;i++) {
367 pollfd *cur_fd = &poll_fds[i];
368 shutdown(cur_fd->fd, SHUT_RDWR);
369 close(cur_fd->fd);
370 }
371 return true;
372 }
373