~ubuntu-branches/debian/experimental/qtcurve/experimental

« back to all changes in this revision

Viewing changes to lib/utils/process.cpp

  • Committer: Package Import Robot
  • Author(s): Boris Pek, Thanks to Scarlett Clark
  • Date: 2015-07-26 04:17:05 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20150726041705-yuxbierbpely1fvp
Tags: 1.8.18+git20150711-a3fff13-1
* Upstream Git snapshot (1.8.18-242-ga3fff13) is taken from:
  http://quickgit.kde.org/?p=qtcurve.git
* Localization files are taken from last stable release (1.8.18).
* Fixed in upstream:
  - gtk2-engines-qtcurve: inkscape 0.91 crashes after palette mouse-over
    (Closes: #786831)
  - gtk2-engines-qtcurve: massive memory leak in mysql-workbench
    (Closes: #682162)
* Update debian/patches:
  - delete qt53-build-fix.diff (fixed in upstream)
  - add enable-translations.patch
* Update debian/control:
  - bump Standards-Version to 3.9.6 (was 3.9.5): no changes required
  - update Build-Depends for transition from KDE4 to KF5:
    + delete dependencies from: kdebase-workspace-dev and qtdeclarative5-dev
    + add dependencies from: extra-cmake-modules, kio-dev,
      libkf5archive-dev, libkf5config-dev, libkf5configwidgets-dev,
      libkf5i18n-dev, libkf5kdelibs4support-dev, libkf5widgetsaddons-dev,
      libkf5xmlgui-dev, libqt5x11extras5-dev, libxcb1-dev, pkg-config
      [Thanks to Scarlett Clark]
  - delete package kwin-style-qtcurve: it is not available for KF5 yet
    (LP: #1452218)
  - package kde-style-qtcurve now provides package kde-style-qtcurve-qt4
  - package kde-style-qtcurve is "Multi-Arch: same" now
  - metapackage qtcurve now depends on kde-style-qtcurve-qt5 and recommends
    kwin-decoration-oxygen, oxygen-icon-theme and oxygencursors
* Update debian/rules:
  - build using kf5 libraries instead of kde4
  - update configure flags
  - use xz compression in packages
* Update debian/kde-style-qtcurve.install:
  there are no KDE4 related files anymore.
* Update debian/kde-style-qtcurve-qt5.install.
* Update debian/copyright.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#if !defined(__APPLE__) && !defined(__MACH__)
 
27
#  include <wait.h>
 
28
#endif
 
29
#include <signal.h>
 
30
#include <sys/stat.h>
 
31
#include <sys/socket.h>
 
32
#include <fcntl.h>
 
33
#include <errno.h>
 
34
#include <poll.h>
 
35
 
 
36
static bool
 
37
qtcSignalHandlerSet(int sig)
 
38
{
 
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);
 
44
}
 
45
 
 
46
QTC_EXPORT bool
 
47
qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb)
 
48
{
 
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.
 
57
    pid_t child = fork();
 
58
    if (child < 0) {
 
59
        return false;
 
60
    } else if (child == 0) {
 
61
        pid_t grandchild = fork();
 
62
        if (grandchild < 0) {
 
63
            qtcCall(fail_cb, data);
 
64
            _exit(1);
 
65
        } else if (grandchild == 0) {
 
66
            /* grandchild */
 
67
            cb(data);
 
68
            _exit(0);
 
69
        } else {
 
70
            _exit(0);
 
71
        }
 
72
        return true;
 
73
    } else {
 
74
        /* parent */
 
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....
 
81
            return true;
 
82
        }
 
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...)
 
86
        int status = 0;
 
87
        return ((waitpid(child, &status, 0) > 0 && status == 0) ||
 
88
                errno == ECHILD);
 
89
    }
 
90
}
 
91
 
 
92
typedef struct {
 
93
    const char *file;
 
94
    char *const *argv;
 
95
    QtcCallback cb;
 
96
    void *cb_data;
 
97
    QtcCallback fail_cb;
 
98
} QtcSpawnData;
 
99
 
 
100
static void
 
101
qtcSpawnCb(void *_data)
 
102
{
 
103
    const QtcSpawnData *data = (const QtcSpawnData*)_data;
 
104
    qtcCall(data->cb, data->cb_data);
 
105
    execvp(data->file, data->argv);
 
106
}
 
107
 
 
108
static void
 
109
qtcSpawnFailCb(void *_data)
 
110
{
 
111
    const QtcSpawnData *data = (const QtcSpawnData*)_data;
 
112
    qtcCall(data->fail_cb, data->cb_data);
 
113
}
 
114
 
 
115
QTC_EXPORT bool
 
116
qtcSpawn(const char *file, const char *const *argv, QtcCallback cb,
 
117
         void *cb_data, QtcCallback fail_cb)
 
118
{
 
119
    QtcSpawnData data = {file, (char *const*)argv, cb, cb_data, fail_cb};
 
120
    return qtcForkBackground(qtcSpawnCb, &data, qtcSpawnFailCb);
 
121
}
 
122
 
 
123
typedef struct {
 
124
    int ctrl_fd;
 
125
    unsigned fd_num;
 
126
    QtcPopenFD *fds;
 
127
} QtcPopenData;
 
128
 
 
129
static void
 
130
qtcPopenCb(void *_data)
 
131
{
 
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;
 
135
        int ret_fd = -1;
 
136
        int replace_fd = -1;
 
137
        if (!mode) {
 
138
            replace_fd = open("/dev/null", O_RDWR);
 
139
            // Make sure a valid fd is sent.
 
140
            ret_fd = replace_fd;
 
141
        } else {
 
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
 
148
            // open).
 
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
 
153
            int socket_fds[2];
 
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);
 
163
            }
 
164
        }
 
165
        dup2(replace_fd, data->fds[i].orig);
 
166
        close(replace_fd);
 
167
        qtcSendFD(data->ctrl_fd, ret_fd);
 
168
        close(ret_fd);
 
169
    }
 
170
    shutdown(data->ctrl_fd, SHUT_RDWR);
 
171
    close(data->ctrl_fd);
 
172
}
 
173
 
 
174
static void
 
175
qtcPopenFailCb(void *_data)
 
176
{
 
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);
 
181
}
 
182
 
 
183
QTC_EXPORT bool
 
184
qtcPopen(const char *file, const char *const *argv,
 
185
         unsigned fd_num, QtcPopenFD *fds)
 
186
{
 
187
    if (qtcUnlikely(!fds || !fd_num)) {
 
188
        return qtcSpawn(file, argv, nullptr, nullptr);
 
189
    }
 
190
    for (unsigned i = 0;i < fd_num;i++) {
 
191
        QTC_RET_IF_FAIL(fds[i].orig >= 0, false);
 
192
    }
 
193
    int socket_fds[2];
 
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);
 
200
    if (!res) {
 
201
        shutdown(socket_fds[0], SHUT_RDWR);
 
202
        close(socket_fds[0]);
 
203
        shutdown(socket_fds[1], SHUT_RDWR);
 
204
        close(socket_fds[1]);
 
205
        return false;
 
206
    }
 
207
    close(socket_fds[0]);
 
208
    for (unsigned i = 0;i < fd_num;i++) {
 
209
        if ((fds[i].replace = qtcRecvFD(socket_fds[1])) < 0) {
 
210
            res = false;
 
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);
 
215
                }
 
216
            }
 
217
            break;
 
218
        }
 
219
        if (!(fds[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) {
 
220
            close(fds[i].replace);
 
221
            fds[i].replace = -1;
 
222
            continue;
 
223
        }
 
224
    }
 
225
    shutdown(socket_fds[1], SHUT_RDWR);
 
226
    close(socket_fds[1]);
 
227
    return res;
 
228
}
 
229
 
 
230
static bool
 
231
qtcPopenReadBuff(QtcPopenBuff *buffs)
 
232
{
 
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,
 
236
                                                  EWOULDBLOCK))) {
 
237
        return false;
 
238
    } else if (len > 0) {
 
239
        buffs->len += len;
 
240
    }
 
241
    return true;
 
242
}
 
243
 
 
244
static bool
 
245
qtcPopenWriteBuff(QtcPopenBuff *buffs)
 
246
{
 
247
    ssize_t len = write(buffs->orig, buffs->buff, buffs->len);
 
248
    if (len == 0 || (len == -1 && QtCurve::noneOf(errno, EAGAIN, EINTR,
 
249
                                                  EWOULDBLOCK))) {
 
250
        return false;
 
251
    } else if (len > 0) {
 
252
        buffs->buff += len;
 
253
        buffs->len -= len;
 
254
    }
 
255
    return true;
 
256
}
 
257
 
 
258
static bool
 
259
qtcPopenPollCheckTimeout(uint64_t start, int timeout, int *new_timeout)
 
260
{
 
261
    if (timeout < 0) {
 
262
        *new_timeout = -1;
 
263
        return true;
 
264
    }
 
265
    int elapse = QtCurve::getElapse(start) / 1000000;
 
266
    if (elapse > timeout) {
 
267
        return false;
 
268
    }
 
269
    *new_timeout = timeout - elapse;
 
270
    return true;
 
271
}
 
272
 
 
273
QTC_EXPORT bool
 
274
qtcPopenBuff(const char *file, const char *const argv[],
 
275
             unsigned buff_num, QtcPopenBuff *buffs, int timeout)
 
276
{
 
277
    if (qtcUnlikely(!buffs || !buff_num)) {
 
278
        return qtcSpawn(file, argv, nullptr, nullptr);
 
279
    }
 
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) {
 
287
            need_poll = true;
 
288
        }
 
289
    }
 
290
    QtCurve::LocalBuff<QtcPopenFD, 16> fds(buff_num);
 
291
    for (unsigned i = 0;i < buff_num;i++) {
 
292
        fds[i].orig = buffs[i].orig;
 
293
        fds[i].replace = -1;
 
294
        fds[i].mode = buffs[i].mode;
 
295
    }
 
296
    bool res = qtcPopen(file, argv, buff_num, fds.get());
 
297
    if (!res) {
 
298
        return false;
 
299
    }
 
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);
 
305
        }
 
306
    }
 
307
    if (!need_poll) {
 
308
        return true;
 
309
    }
 
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);
 
316
            continue;
 
317
        }
 
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;
 
322
        poll_fd_num++;
 
323
    }
 
324
    uint64_t start_time = QtCurve::getTime();
 
325
    int poll_timeout = timeout;
 
326
    while (true) {
 
327
        int ret = poll(poll_fds.get(), poll_fd_num, poll_timeout);
 
328
        if (ret == -1) {
 
329
            if (errno == EINTR) {
 
330
                if (!qtcPopenPollCheckTimeout(start_time, timeout,
 
331
                                              &poll_timeout)) {
 
332
                    break;
 
333
                }
 
334
                continue;
 
335
            }
 
336
            break;
 
337
        } else if (ret == 0) {
 
338
            break;
 
339
        }
 
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;
 
345
                }
 
346
            } else if (cur_fd->revents & POLLOUT) {
 
347
                if (!qtcPopenWriteBuff(&buffs[indexes[i]])) {
 
348
                    cur_fd->events &= ~POLLOUT;
 
349
                }
 
350
            }
 
351
            if (cur_fd->revents & (POLLERR | POLLHUP | POLLNVAL) ||
 
352
                !(cur_fd->events & (POLLIN | POLLOUT))) {
 
353
                shutdown(cur_fd->fd, SHUT_RDWR);
 
354
                close(cur_fd->fd);
 
355
                poll_fd_num--;
 
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));
 
360
                i--;
 
361
            }
 
362
        }
 
363
        if (poll_fd_num <= 0 || !qtcPopenPollCheckTimeout(start_time, timeout,
 
364
                                                          &poll_timeout)) {
 
365
            break;
 
366
        }
 
367
    }
 
368
    for (unsigned i = 0;i < poll_fd_num;i++) {
 
369
        pollfd *cur_fd = &poll_fds[i];
 
370
        shutdown(cur_fd->fd, SHUT_RDWR);
 
371
        close(cur_fd->fd);
 
372
    }
 
373
    return true;
 
374
}