1
/*****************************************************************************
2
* Copyright 2014 - 2015 Yichao Yu <yyc1992@gmail.com> *
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. *
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. *
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
*****************************************************************************/
26
#if !defined(__APPLE__) && !defined(__MACH__)
31
#include <sys/socket.h>
37
qtcSignalHandlerSet(int sig)
39
struct sigaction oact;
40
QTC_RET_IF_FAIL(sigaction(sig, nullptr, &oact) == 0, false);
41
void *handler = ((oact.sa_flags & SA_SIGINFO) ? (void*)oact.sa_handler :
42
(void*)oact.sa_sigaction);
43
return QtCurve::noneOf(handler, SIG_DFL, SIG_IGN);
47
qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb)
49
QTC_RET_IF_FAIL(cb, false);
50
// On linux, waitpid will not accept (discard) SIGCHLD therefore if there is
51
// a signal handler registered for SIGCHLD and the child process exit
52
// inside waitpid()/wait(), it will be run after the process state is
53
// cleared and would therefore block if it call wait() (or waitpid(-1))
54
// and if there are other child processes. As a workaround we only call
55
// waitpid() if the main program did not set up any signal handlers for
56
// SIGCHLD. See (the RATIONALE section of) wait(3P) for more detail.
60
} else if (child == 0) {
61
pid_t grandchild = fork();
63
qtcCall(fail_cb, data);
65
} else if (grandchild == 0) {
75
if (qtcSignalHandlerSet(SIGCHLD)) {
76
// If we create a child process, the signal handler will receive
77
// the signal anyway (and there is no way to only block SIGCHLD
78
// only for our child process). Since the signal handler may
79
// hang and should already take care of getting rid of
80
// zombie processes, we do not call waitpid in this case....
83
// If SIGCHLD is ignored, waitpid will return -1 with errno
84
// set to ECHILD, treat this as success (good enough for our purpose
85
// and not likely to fail anyway...)
87
return ((waitpid(child, &status, 0) > 0 && status == 0) ||
101
qtcSpawnCb(void *_data)
103
const QtcSpawnData *data = (const QtcSpawnData*)_data;
104
qtcCall(data->cb, data->cb_data);
105
execvp(data->file, data->argv);
109
qtcSpawnFailCb(void *_data)
111
const QtcSpawnData *data = (const QtcSpawnData*)_data;
112
qtcCall(data->fail_cb, data->cb_data);
116
qtcSpawn(const char *file, const char *const *argv, QtcCallback cb,
117
void *cb_data, QtcCallback fail_cb)
119
QtcSpawnData data = {file, (char *const*)argv, cb, cb_data, fail_cb};
120
return qtcForkBackground(qtcSpawnCb, &data, qtcSpawnFailCb);
130
qtcPopenCb(void *_data)
132
QtcPopenData *data = (QtcPopenData*)_data;
133
for (unsigned i = 0;i < data->fd_num;i++) {
134
int mode = data->fds[i].mode & QTC_POPEN_RDWR;
138
replace_fd = open("/dev/null", O_RDWR);
139
// Make sure a valid fd is sent.
142
// Open socket pairs in the child process and send it back to
143
// parent with a unix domain socket so that the write end of the
144
// pair is always under control.
145
// For writing to sub process, the parent will shutdown the write
146
// end when it is done (therefore the client will receive EOF even
147
// if the parent forks other subprocesses which keeps the pipes
149
// For reading from sub process, the write end of the pipe is not
150
// shared with any other process so the parent will receive EOF
151
// whenever the client closes the pipe or exit.
152
// See http://stackoverflow.com/questions/1583005/is-there-any-difference-between-socketpair-and-pair-of-unnamed-pipes
154
socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
155
ret_fd = socket_fds[0];
156
replace_fd = socket_fds[1];
157
if (!(mode & QTC_POPEN_READ)) {
158
shutdown(ret_fd, SHUT_RD);
159
shutdown(replace_fd, SHUT_WR);
160
} else if (!(mode & QTC_POPEN_WRITE)) {
161
shutdown(ret_fd, SHUT_WR);
162
shutdown(replace_fd, SHUT_RD);
165
dup2(replace_fd, data->fds[i].orig);
167
qtcSendFD(data->ctrl_fd, ret_fd);
170
shutdown(data->ctrl_fd, SHUT_RDWR);
171
close(data->ctrl_fd);
175
qtcPopenFailCb(void *_data)
177
QtcPopenData *data = (QtcPopenData*)_data;
178
// Notify the parent that sth goes wrong.
179
shutdown(data->ctrl_fd, SHUT_RDWR);
180
close(data->ctrl_fd);
184
qtcPopen(const char *file, const char *const *argv,
185
unsigned fd_num, QtcPopenFD *fds)
187
if (qtcUnlikely(!fds || !fd_num)) {
188
return qtcSpawn(file, argv, nullptr, nullptr);
190
for (unsigned i = 0;i < fd_num;i++) {
191
QTC_RET_IF_FAIL(fds[i].orig >= 0, false);
194
QTC_RET_IF_FAIL(socketpair(AF_UNIX, SOCK_STREAM, 0,
195
socket_fds) == 0, false);
196
qtcFDSetCloexec(socket_fds[0], true);
197
qtcFDSetCloexec(socket_fds[1], true);
198
QtcPopenData cbdata = {socket_fds[0], fd_num, fds};
199
bool res = qtcSpawn(file, argv, qtcPopenCb, &cbdata, qtcPopenFailCb);
201
shutdown(socket_fds[0], SHUT_RDWR);
202
close(socket_fds[0]);
203
shutdown(socket_fds[1], SHUT_RDWR);
204
close(socket_fds[1]);
207
close(socket_fds[0]);
208
for (unsigned i = 0;i < fd_num;i++) {
209
if ((fds[i].replace = qtcRecvFD(socket_fds[1])) < 0) {
211
for (unsigned j = 0;j < i;j++) {
212
if (fds[i].replace) {
213
shutdown(fds[i].replace, SHUT_RDWR);
214
close(fds[i].replace);
219
if (!(fds[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) {
220
close(fds[i].replace);
225
shutdown(socket_fds[1], SHUT_RDWR);
226
close(socket_fds[1]);
231
qtcPopenReadBuff(QtcPopenBuff *buffs)
233
buffs->buff = (char*)realloc(buffs->buff, buffs->len + 1024 + 1);
234
ssize_t len = read(buffs->orig, buffs->buff + buffs->len, 1024);
235
if (len == 0 || (len == -1 && QtCurve::noneOf(errno, EAGAIN, EINTR,
238
} else if (len > 0) {
245
qtcPopenWriteBuff(QtcPopenBuff *buffs)
247
ssize_t len = write(buffs->orig, buffs->buff, buffs->len);
248
if (len == 0 || (len == -1 && QtCurve::noneOf(errno, EAGAIN, EINTR,
251
} else if (len > 0) {
259
qtcPopenPollCheckTimeout(uint64_t start, int timeout, int *new_timeout)
265
int elapse = QtCurve::getElapse(start) / 1000000;
266
if (elapse > timeout) {
269
*new_timeout = timeout - elapse;
274
qtcPopenBuff(const char *file, const char *const argv[],
275
unsigned buff_num, QtcPopenBuff *buffs, int timeout)
277
if (qtcUnlikely(!buffs || !buff_num)) {
278
return qtcSpawn(file, argv, nullptr, nullptr);
280
bool need_poll = false;
281
for (unsigned i = 0;i < buff_num;i++) {
282
QTC_RET_IF_FAIL(buffs[i].orig >= 0, false);
283
QTC_RET_IF_FAIL(!(buffs[i].mode & QTC_POPEN_READ &&
284
buffs[i].mode & QTC_POPEN_WRITE), false);
285
if (buffs[i].mode & QTC_POPEN_READ ||
286
buffs[i].mode & QTC_POPEN_WRITE) {
290
QtCurve::LocalBuff<QtcPopenFD, 16> fds(buff_num);
291
for (unsigned i = 0;i < buff_num;i++) {
292
fds[i].orig = buffs[i].orig;
294
fds[i].mode = buffs[i].mode;
296
bool res = qtcPopen(file, argv, buff_num, fds.get());
300
for (unsigned i = 0;i < buff_num;i++) {
301
buffs[i].orig = fds[i].replace;
302
if (fds[i].replace >= 0) {
303
qtcFDSetNonBlock(fds[i].replace, true);
304
qtcFDSetCloexec(fds[i].replace, true);
310
QtCurve::LocalBuff<pollfd, 16> poll_fds(buff_num);
311
QtCurve::LocalBuff<int, 16> indexes(buff_num);
312
unsigned poll_fd_num = 0;
313
for (unsigned i = 0;i < buff_num;i++) {
314
if (!(buffs[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) {
315
close(buffs[i].orig);
318
indexes[poll_fd_num] = i;
319
pollfd *cur_fd = &poll_fds[poll_fd_num];
320
cur_fd->fd = buffs[i].orig;
321
cur_fd->events = (buffs[i].mode & QTC_POPEN_READ) ? POLLIN : POLLOUT;
324
uint64_t start_time = QtCurve::getTime();
325
int poll_timeout = timeout;
327
int ret = poll(poll_fds.get(), poll_fd_num, poll_timeout);
329
if (errno == EINTR) {
330
if (!qtcPopenPollCheckTimeout(start_time, timeout,
337
} else if (ret == 0) {
340
for (unsigned i = 0;i < poll_fd_num;i++) {
341
pollfd *cur_fd = &poll_fds[i];
342
if (cur_fd->revents & POLLIN) {
343
if (!qtcPopenReadBuff(&buffs[indexes[i]])) {
344
cur_fd->events &= ~POLLIN;
346
} else if (cur_fd->revents & POLLOUT) {
347
if (!qtcPopenWriteBuff(&buffs[indexes[i]])) {
348
cur_fd->events &= ~POLLOUT;
351
if (cur_fd->revents & (POLLERR | POLLHUP | POLLNVAL) ||
352
!(cur_fd->events & (POLLIN | POLLOUT))) {
353
shutdown(cur_fd->fd, SHUT_RDWR);
356
memmove(cur_fd, cur_fd + 1,
357
(poll_fd_num - i) * sizeof(pollfd));
358
memmove(indexes.get() + i, indexes.get() + i + 1,
359
(poll_fd_num - i) * sizeof(int));
363
if (poll_fd_num <= 0 || !qtcPopenPollCheckTimeout(start_time, timeout,
368
for (unsigned i = 0;i < poll_fd_num;i++) {
369
pollfd *cur_fd = &poll_fds[i];
370
shutdown(cur_fd->fd, SHUT_RDWR);