File indexing completed on 2024-04-28 05:46:50

0001 /*****************************************************************************
0002  *   Copyright 2014 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
0003  *                                                                           *
0004  *   This program is free software; you can redistribute it and/or modify    *
0005  *   it under the terms of the GNU Lesser General Public License as          *
0006  *   published by the Free Software Foundation; either version 2.1 of the    *
0007  *   License, or (at your option) version 3, or any later version accepted   *
0008  *   by the membership of KDE e.V. (or its successor approved by the         *
0009  *   membership of KDE e.V.), which shall act as a proxy defined in          *
0010  *   Section 6 of version 3 of the license.                                  *
0011  *                                                                           *
0012  *   This program is distributed in the hope that it will be useful,         *
0013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
0014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
0015  *   Lesser General Public License for more details.                         *
0016  *                                                                           *
0017  *   You should have received a copy of the GNU Lesser General Public        *
0018  *   License along with this library. If not,                                *
0019  *   see <http://www.gnu.org/licenses/>.                                     *
0020  *****************************************************************************/
0021 
0022 #include "process.h"
0023 #include "fd_utils.h"
0024 #include "timer.h"
0025 #include <unistd.h>
0026 #include <sys/wait.h>
0027 #include <signal.h>
0028 #include <sys/stat.h>
0029 #include <sys/socket.h>
0030 #include <fcntl.h>
0031 #include <errno.h>
0032 #include <poll.h>
0033 
0034 static bool
0035 qtcSignalHandlerSet(int sig)
0036 {
0037     struct sigaction oact;
0038     QTC_RET_IF_FAIL(sigaction(sig, nullptr, &oact) == 0, false);
0039     void *handler = ((oact.sa_flags & SA_SIGINFO) ? (void*)oact.sa_handler :
0040                      (void*)oact.sa_sigaction);
0041     return QtCurve::noneOf(handler, SIG_DFL, SIG_IGN);
0042 }
0043 
0044 QTC_EXPORT bool
0045 qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb)
0046 {
0047     QTC_RET_IF_FAIL(cb, false);
0048     // On linux, waitpid will not accept (discard) SIGCHLD therefore if there is
0049     // a signal handler registered for SIGCHLD and the child process exit
0050     // inside waitpid()/wait(), it will be run after the process state is
0051     // cleared and would therefore block if it call wait() (or waitpid(-1))
0052     // and if there are other child processes. As a workaround we only call
0053     // waitpid() if the main program did not set up any signal handlers for
0054     // SIGCHLD. See (the RATIONALE section of) wait(3P) for more detail.
0055     pid_t child = fork();
0056     if (child < 0) {
0057         return false;
0058     } else if (child == 0) {
0059         pid_t grandchild = fork();
0060         if (grandchild < 0) {
0061             qtcCall(fail_cb, data);
0062             _exit(1);
0063         } else if (grandchild == 0) {
0064             /* grandchild */
0065             cb(data);
0066             _exit(0);
0067         } else {
0068             _exit(0);
0069         }
0070         return true;
0071     } else {
0072         /* parent */
0073         if (qtcSignalHandlerSet(SIGCHLD)) {
0074             // If we create a child process, the signal handler will receive
0075             // the signal anyway (and there is no way to only block SIGCHLD
0076             // only for our child process). Since the signal handler may
0077             // hang and should already take care of getting rid of
0078             // zombie processes, we do not call waitpid in this case....
0079             return true;
0080         }
0081         // If SIGCHLD is ignored, waitpid will return -1 with errno
0082         // set to ECHILD, treat this as success (good enough for our purpose
0083         // and not likely to fail anyway...)
0084         int status = 0;
0085         return ((waitpid(child, &status, 0) > 0 && status == 0) ||
0086                 errno == ECHILD);
0087     }
0088 }
0089 
0090 typedef struct {
0091     const char *file;
0092     char *const *argv;
0093     QtcCallback cb;
0094     void *cb_data;
0095     QtcCallback fail_cb;
0096 } QtcSpawnData;
0097 
0098 static void
0099 qtcSpawnCb(void *_data)
0100 {
0101     const QtcSpawnData *data = (const QtcSpawnData*)_data;
0102     qtcCall(data->cb, data->cb_data);
0103     execvp(data->file, data->argv);
0104 }
0105 
0106 static void
0107 qtcSpawnFailCb(void *_data)
0108 {
0109     const QtcSpawnData *data = (const QtcSpawnData*)_data;
0110     qtcCall(data->fail_cb, data->cb_data);
0111 }
0112 
0113 QTC_EXPORT bool
0114 qtcSpawn(const char *file, const char *const *argv, QtcCallback cb,
0115          void *cb_data, QtcCallback fail_cb)
0116 {
0117     QtcSpawnData data = {file, (char *const*)argv, cb, cb_data, fail_cb};
0118     return qtcForkBackground(qtcSpawnCb, &data, qtcSpawnFailCb);
0119 }
0120 
0121 typedef struct {
0122     int ctrl_fd;
0123     unsigned fd_num;
0124     QtcPopenFD *fds;
0125 } QtcPopenData;
0126 
0127 static void
0128 qtcPopenCb(void *_data)
0129 {
0130     QtcPopenData *data = (QtcPopenData*)_data;
0131     for (unsigned i = 0;i < data->fd_num;i++) {
0132         int mode = data->fds[i].mode & QTC_POPEN_RDWR;
0133         int ret_fd = -1;
0134         int replace_fd = -1;
0135         if (!mode) {
0136             replace_fd = open("/dev/null", O_RDWR);
0137             // Make sure a valid fd is sent.
0138             ret_fd = replace_fd;
0139         } else {
0140             // Open socket pairs in the child process and send it back to
0141             // parent with a unix domain socket so that the write end of the
0142             // pair is always under control.
0143             // For writing to sub process, the parent will shutdown the write
0144             // end when it is done (therefore the client will receive EOF even
0145             // if the parent forks other subprocesses which keeps the pipes
0146             // open).
0147             // For reading from sub process, the write end of the pipe is not
0148             // shared with any other process so the parent will receive EOF
0149             // whenever the client closes the pipe or exit.
0150             // See http://stackoverflow.com/questions/1583005/is-there-any-difference-between-socketpair-and-pair-of-unnamed-pipes
0151             int socket_fds[2];
0152             socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
0153             ret_fd = socket_fds[0];
0154             replace_fd = socket_fds[1];
0155             if (!(mode & QTC_POPEN_READ)) {
0156                 shutdown(ret_fd, SHUT_RD);
0157                 shutdown(replace_fd, SHUT_WR);
0158             } else if (!(mode & QTC_POPEN_WRITE)) {
0159                 shutdown(ret_fd, SHUT_WR);
0160                 shutdown(replace_fd, SHUT_RD);
0161             }
0162         }
0163         dup2(replace_fd, data->fds[i].orig);
0164         close(replace_fd);
0165         qtcSendFD(data->ctrl_fd, ret_fd);
0166         close(ret_fd);
0167     }
0168     shutdown(data->ctrl_fd, SHUT_RDWR);
0169     close(data->ctrl_fd);
0170 }
0171 
0172 static void
0173 qtcPopenFailCb(void *_data)
0174 {
0175     QtcPopenData *data = (QtcPopenData*)_data;
0176     // Notify the parent that sth goes wrong.
0177     shutdown(data->ctrl_fd, SHUT_RDWR);
0178     close(data->ctrl_fd);
0179 }
0180 
0181 QTC_EXPORT bool
0182 qtcPopen(const char *file, const char *const *argv,
0183          unsigned fd_num, QtcPopenFD *fds)
0184 {
0185     if (qtcUnlikely(!fds || !fd_num)) {
0186         return qtcSpawn(file, argv, nullptr, nullptr);
0187     }
0188     for (unsigned i = 0;i < fd_num;i++) {
0189         QTC_RET_IF_FAIL(fds[i].orig >= 0, false);
0190     }
0191     int socket_fds[2];
0192     QTC_RET_IF_FAIL(socketpair(AF_UNIX, SOCK_STREAM, 0,
0193                                socket_fds) == 0, false);
0194     qtcFDSetCloexec(socket_fds[0], true);
0195     qtcFDSetCloexec(socket_fds[1], true);
0196     QtcPopenData cbdata = {socket_fds[0], fd_num, fds};
0197     bool res = qtcSpawn(file, argv, qtcPopenCb, &cbdata, qtcPopenFailCb);
0198     if (!res) {
0199         shutdown(socket_fds[0], SHUT_RDWR);
0200         close(socket_fds[0]);
0201         shutdown(socket_fds[1], SHUT_RDWR);
0202         close(socket_fds[1]);
0203         return false;
0204     }
0205     close(socket_fds[0]);
0206     for (unsigned i = 0;i < fd_num;i++) {
0207         if ((fds[i].replace = qtcRecvFD(socket_fds[1])) < 0) {
0208             res = false;
0209             for (unsigned j = 0;j < i;j++) {
0210                 if (fds[i].replace) {
0211                     shutdown(fds[i].replace, SHUT_RDWR);
0212                     close(fds[i].replace);
0213                 }
0214             }
0215             break;
0216         }
0217         if (!(fds[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) {
0218             close(fds[i].replace);
0219             fds[i].replace = -1;
0220             continue;
0221         }
0222     }
0223     shutdown(socket_fds[1], SHUT_RDWR);
0224     close(socket_fds[1]);
0225     return res;
0226 }
0227 
0228 static bool
0229 qtcPopenReadBuff(QtcPopenBuff *buffs)
0230 {
0231     buffs->buff = (char*)realloc(buffs->buff, buffs->len + 1024 + 1);
0232     ssize_t len = read(buffs->orig, buffs->buff + buffs->len, 1024);
0233     if (len == 0 || (len == -1 && QtCurve::noneOf(errno, EAGAIN, EINTR,
0234                                                   EWOULDBLOCK))) {
0235         return false;
0236     } else if (len > 0) {
0237         buffs->len += len;
0238     }
0239     return true;
0240 }
0241 
0242 static bool
0243 qtcPopenWriteBuff(QtcPopenBuff *buffs)
0244 {
0245     ssize_t len = write(buffs->orig, buffs->buff, buffs->len);
0246     if (len == 0 || (len == -1 && QtCurve::noneOf(errno, EAGAIN, EINTR,
0247                                                   EWOULDBLOCK))) {
0248         return false;
0249     } else if (len > 0) {
0250         buffs->buff += len;
0251         buffs->len -= len;
0252     }
0253     return true;
0254 }
0255 
0256 static bool
0257 qtcPopenPollCheckTimeout(uint64_t start, int timeout, int *new_timeout)
0258 {
0259     if (timeout < 0) {
0260         *new_timeout = -1;
0261         return true;
0262     }
0263     int elapse = QtCurve::getElapse(start) / 1000000;
0264     if (elapse > timeout) {
0265         return false;
0266     }
0267     *new_timeout = timeout - elapse;
0268     return true;
0269 }
0270 
0271 QTC_EXPORT bool
0272 qtcPopenBuff(const char *file, const char *const argv[],
0273              unsigned buff_num, QtcPopenBuff *buffs, int timeout)
0274 {
0275     if (qtcUnlikely(!buffs || !buff_num)) {
0276         return qtcSpawn(file, argv, nullptr, nullptr);
0277     }
0278     bool need_poll = false;
0279     for (unsigned i = 0;i < buff_num;i++) {
0280         QTC_RET_IF_FAIL(buffs[i].orig >= 0, false);
0281         QTC_RET_IF_FAIL(!(buffs[i].mode & QTC_POPEN_READ &&
0282                           buffs[i].mode & QTC_POPEN_WRITE), false);
0283         if (buffs[i].mode & QTC_POPEN_READ ||
0284             buffs[i].mode & QTC_POPEN_WRITE) {
0285             need_poll = true;
0286         }
0287     }
0288     QtCurve::LocalBuff<QtcPopenFD, 16> fds(buff_num);
0289     for (unsigned i = 0;i < buff_num;i++) {
0290         fds[i].orig = buffs[i].orig;
0291         fds[i].replace = -1;
0292         fds[i].mode = buffs[i].mode;
0293     }
0294     bool res = qtcPopen(file, argv, buff_num, fds.get());
0295     if (!res) {
0296         return false;
0297     }
0298     for (unsigned i = 0;i < buff_num;i++) {
0299         buffs[i].orig = fds[i].replace;
0300         if (fds[i].replace >= 0) {
0301             qtcFDSetNonBlock(fds[i].replace, true);
0302             qtcFDSetCloexec(fds[i].replace, true);
0303         }
0304     }
0305     if (!need_poll) {
0306         return true;
0307     }
0308     QtCurve::LocalBuff<pollfd, 16> poll_fds(buff_num);
0309     QtCurve::LocalBuff<int, 16> indexes(buff_num);
0310     unsigned poll_fd_num = 0;
0311     for (unsigned i = 0;i < buff_num;i++) {
0312         if (!(buffs[i].mode & (QTC_POPEN_READ | QTC_POPEN_WRITE))) {
0313             close(buffs[i].orig);
0314             continue;
0315         }
0316         indexes[poll_fd_num] = i;
0317         pollfd *cur_fd = &poll_fds[poll_fd_num];
0318         cur_fd->fd = buffs[i].orig;
0319         cur_fd->events = (buffs[i].mode & QTC_POPEN_READ) ? POLLIN : POLLOUT;
0320         poll_fd_num++;
0321     }
0322     uint64_t start_time = QtCurve::getTime();
0323     int poll_timeout = timeout;
0324     while (true) {
0325         int ret = poll(poll_fds.get(), poll_fd_num, poll_timeout);
0326         if (ret == -1) {
0327             if (errno == EINTR) {
0328                 if (!qtcPopenPollCheckTimeout(start_time, timeout,
0329                                               &poll_timeout)) {
0330                     break;
0331                 }
0332                 continue;
0333             }
0334             break;
0335         } else if (ret == 0) {
0336             break;
0337         }
0338         for (unsigned i = 0;i < poll_fd_num;i++) {
0339             pollfd *cur_fd = &poll_fds[i];
0340             if (cur_fd->revents & POLLIN) {
0341                 if (!qtcPopenReadBuff(&buffs[indexes[i]])) {
0342                     cur_fd->events &= ~POLLIN;
0343                 }
0344             } else if (cur_fd->revents & POLLOUT) {
0345                 if (!qtcPopenWriteBuff(&buffs[indexes[i]])) {
0346                     cur_fd->events &= ~POLLOUT;
0347                 }
0348             }
0349             if (cur_fd->revents & (POLLERR | POLLHUP | POLLNVAL) ||
0350                 !(cur_fd->events & (POLLIN | POLLOUT))) {
0351                 shutdown(cur_fd->fd, SHUT_RDWR);
0352                 close(cur_fd->fd);
0353                 poll_fd_num--;
0354                 memmove(cur_fd, cur_fd + 1,
0355                         (poll_fd_num - i) * sizeof(pollfd));
0356                 memmove(indexes.get() + i, indexes.get() + i + 1,
0357                         (poll_fd_num - i) * sizeof(int));
0358                 i--;
0359             }
0360         }
0361         if (poll_fd_num <= 0 || !qtcPopenPollCheckTimeout(start_time, timeout,
0362                                                           &poll_timeout)) {
0363             break;
0364         }
0365     }
0366     for (unsigned i = 0;i < poll_fd_num;i++) {
0367         pollfd *cur_fd = &poll_fds[i];
0368         shutdown(cur_fd->fd, SHUT_RDWR);
0369         close(cur_fd->fd);
0370     }
0371     return true;
0372 }