File indexing completed on 2024-05-19 16:50:19
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 }