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