diff options
Diffstat (limited to 'src/corelib/io/qprocess_unix.cpp')
-rw-r--r-- | src/corelib/io/qprocess_unix.cpp | 268 |
1 files changed, 116 insertions, 152 deletions
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 8eb5ac9564..f9d14c8e5a 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -1,31 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Intel Corporation. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -115,11 +122,52 @@ QT_BEGIN_NAMESPACE // so we will use 512 static const int errorBufferMax = 512; -static inline void add_fd(int &nfds, int fd, fd_set *fdset) +namespace { +struct QProcessPoller { - FD_SET(fd, fdset); - if ((fd) > nfds) - nfds = fd; + QProcessPoller(const QProcessPrivate &proc); + + int poll(int timeout); + + pollfd &stdinPipe() { return pfds[0]; } + pollfd &stdoutPipe() { return pfds[1]; } + pollfd &stderrPipe() { return pfds[2]; } + pollfd &forkfd() { return pfds[3]; } + pollfd &childStartedPipe() { return pfds[4]; } + + enum { n_pfds = 5 }; + pollfd pfds[n_pfds]; +}; + +QProcessPoller::QProcessPoller(const QProcessPrivate &proc) +{ + for (int i = 0; i < n_pfds; i++) + pfds[i] = qt_make_pollfd(-1, POLLIN); + + stdoutPipe().fd = proc.stdoutChannel.pipe[0]; + stderrPipe().fd = proc.stderrChannel.pipe[0]; + + if (!proc.writeBuffer.isEmpty()) { + stdinPipe().fd = proc.stdinChannel.pipe[1]; + stdinPipe().events = POLLOUT; + } + + forkfd().fd = proc.forkfd; + + if (proc.processState == QProcess::Starting) + childStartedPipe().fd = proc.childStartedPipe[0]; +} + +int QProcessPoller::poll(int timeout) +{ + const nfds_t nfds = (childStartedPipe().fd == -1) ? 4 : 5; + return qt_poll_msecs(pfds, nfds, timeout); +} +} // anonymous namespace + +static bool qt_pollfd_check(const pollfd &pfd, short revents) +{ + return pfd.fd >= 0 && (pfd.revents & (revents | POLLHUP | POLLERR | POLLNVAL)) != 0; } static int qt_create_pipe(int *pipe) @@ -564,7 +612,7 @@ static int doSpawn(pid_t *ppid, const posix_spawn_file_actions_t *file_actions, qWarning("ThreadCtl(): failed to chdir to %s", oldWorkingDir); # ifdef Q_OS_QNX - if (ThreadCtl(_NTO_TCTL_THREADS_CONT, 0) == -1) + if (Q_UNLIKELY(ThreadCtl(_NTO_TCTL_THREADS_CONT, 0) == -1)) qFatal("ThreadCtl(): cannot resume threads: %s", qPrintable(qt_error_string(errno))); # endif } @@ -676,6 +724,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv qt_safe_close(childStartedPipe[0]); // enter the working directory + const char *callthatfailed = "chdir: "; if (workingDir && QT_CHDIR(workingDir) == -1) { // failed, stop the process goto report_errno; @@ -687,6 +736,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv // execute the process if (!envp) { qt_safe_execvp(argv[0], argv); + callthatfailed = "execvp: "; } else { if (path) { char **arg = path; @@ -704,15 +754,19 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv #endif qt_safe_execve(argv[0], argv, envp); } + callthatfailed = "execve: "; } // notify failure + // we're running in the child process, so we don't need to be thread-safe; + // we can use strerror report_errno: - QString error = qt_error_string(errno); + const char *msg = strerror(errno); #if defined (QPROCESS_DEBUG) - fprintf(stderr, "QProcessPrivate::execChild() failed (%s), notifying parent process\n", qPrintable(error)); + fprintf(stderr, "QProcessPrivate::execChild() failed (%s), notifying parent process\n", msg); #endif - qt_safe_write(childStartedPipe[1], error.data(), error.length() * sizeof(QChar)); + qt_safe_write(childStartedPipe[1], callthatfailed, strlen(callthatfailed)); + qt_safe_write(childStartedPipe[1], msg, strlen(msg)); qt_safe_close(childStartedPipe[1]); childStartedPipe[1] = -1; } @@ -720,8 +774,15 @@ report_errno: bool QProcessPrivate::processStarted(QString *errorMessage) { - ushort buf[errorBufferMax]; - int i = qt_safe_read(childStartedPipe[0], &buf, sizeof buf); + char buf[errorBufferMax]; + int i = 0; + int ret; + do { + ret = qt_safe_read(childStartedPipe[0], buf + i, sizeof buf - i); + if (ret > 0) + i += ret; + } while (ret > 0 && i < int(sizeof buf)); + if (startupSocketNotifier) { startupSocketNotifier->setEnabled(false); startupSocketNotifier->deleteLater(); @@ -736,7 +797,7 @@ bool QProcessPrivate::processStarted(QString *errorMessage) // did we read an error message? if ((i > 0) && errorMessage) - *errorMessage = QString((const QChar *)buf, i / sizeof(QChar)); + *errorMessage = QString::fromLocal8Bit(buf, i); return i <= 0; } @@ -812,10 +873,9 @@ bool QProcessPrivate::waitForStarted(int msecs) childStartedPipe[0]); #endif - fd_set fds; - FD_ZERO(&fds); - FD_SET(childStartedPipe[0], &fds); - if (qt_select_msecs(childStartedPipe[0] + 1, &fds, 0, msecs) == 0) { + pollfd pfd = qt_make_pollfd(childStartedPipe[0], POLLIN); + + if (qt_poll_msecs(&pfd, 1, msecs) == 0) { setError(QProcess::Timedout); #if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::waitForStarted(%d) == false (timed out)", msecs); @@ -830,17 +890,6 @@ bool QProcessPrivate::waitForStarted(int msecs) return startedEmitted; } -#ifdef Q_OS_BLACKBERRY -QList<QSocketNotifier *> QProcessPrivate::defaultNotifiers() const -{ - QList<QSocketNotifier *> notifiers; - notifiers << stdoutChannel.notifier - << stderrChannel.notifier - << stdinChannel.notifier; - return notifiers; -} -#endif // Q_OS_BLACKBERRY - bool QProcessPrivate::waitForReadyRead(int msecs) { #if defined (QPROCESS_DEBUG) @@ -850,37 +899,12 @@ bool QProcessPrivate::waitForReadyRead(int msecs) QElapsedTimer stopWatch; stopWatch.start(); -#ifdef Q_OS_BLACKBERRY - QList<QSocketNotifier *> notifiers = defaultNotifiers(); -#endif - forever { - fd_set fdread; - fd_set fdwrite; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - int nfds = forkfd; - FD_SET(forkfd, &fdread); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); + QProcessPoller poller(*this); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); -#ifdef Q_OS_BLACKBERRY - int ret = bb_select(notifiers, nfds + 1, &fdread, &fdwrite, timeout); -#else - int ret = qt_select_msecs(nfds + 1, &fdread, &fdwrite, timeout); -#endif + int ret = poller.poll(timeout); + if (ret < 0) { break; } @@ -889,29 +913,29 @@ bool QProcessPrivate::waitForReadyRead(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_pollfd_check(poller.childStartedPipe(), POLLIN)) { if (!_q_startupNotification()) return false; } bool readyReadEmitted = false; - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) { + if (qt_pollfd_check(poller.stdoutPipe(), POLLIN)) { bool canRead = _q_canReadStandardOutput(); - if (processChannel == QProcess::StandardOutput && canRead) + if (currentReadChannel == QProcess::StandardOutput && canRead) readyReadEmitted = true; } - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) { + if (qt_pollfd_check(poller.stderrPipe(), POLLIN)) { bool canRead = _q_canReadStandardError(); - if (processChannel == QProcess::StandardError && canRead) + if (currentReadChannel == QProcess::StandardError && canRead) readyReadEmitted = true; } if (readyReadEmitted) return true; - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_pollfd_check(poller.stdinPipe(), POLLOUT)) _q_canWrite(); - if (forkfd == -1 || FD_ISSET(forkfd, &fdread)) { + if (qt_pollfd_check(poller.forkfd(), POLLIN)) { if (_q_processDied()) return false; } @@ -928,38 +952,12 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) QElapsedTimer stopWatch; stopWatch.start(); -#ifdef Q_OS_BLACKBERRY - QList<QSocketNotifier *> notifiers = defaultNotifiers(); -#endif - - while (!stdinChannel.buffer.isEmpty()) { - fd_set fdread; - fd_set fdwrite; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - int nfds = forkfd; - FD_SET(forkfd, &fdread); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - - if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); + while (!writeBuffer.isEmpty()) { + QProcessPoller poller(*this); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); -#ifdef Q_OS_BLACKBERRY - int ret = bb_select(notifiers, nfds + 1, &fdread, &fdwrite, timeout); -#else - int ret = qt_select_msecs(nfds + 1, &fdread, &fdwrite, timeout); -#endif + int ret = poller.poll(timeout); + if (ret < 0) { break; } @@ -969,21 +967,21 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_pollfd_check(poller.childStartedPipe(), POLLIN)) { if (!_q_startupNotification()) return false; } - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_pollfd_check(poller.stdinPipe(), POLLOUT)) return _q_canWrite(); - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stdoutPipe(), POLLIN)) _q_canReadStandardOutput(); - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stderrPipe(), POLLIN)) _q_canReadStandardError(); - if (forkfd == -1 || FD_ISSET(forkfd, &fdread)) { + if (qt_pollfd_check(poller.forkfd(), POLLIN)) { if (_q_processDied()) return false; } @@ -1001,38 +999,12 @@ bool QProcessPrivate::waitForFinished(int msecs) QElapsedTimer stopWatch; stopWatch.start(); -#ifdef Q_OS_BLACKBERRY - QList<QSocketNotifier *> notifiers = defaultNotifiers(); -#endif - forever { - fd_set fdread; - fd_set fdwrite; - int nfds = -1; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - if (processState == QProcess::Running && forkfd != -1) - add_fd(nfds, forkfd, &fdread); - - if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); + QProcessPoller poller(*this); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); -#ifdef Q_OS_BLACKBERRY - int ret = bb_select(notifiers, nfds + 1, &fdread, &fdwrite, timeout); -#else - int ret = qt_select_msecs(nfds + 1, &fdread, &fdwrite, timeout); -#endif + int ret = poller.poll(timeout); + if (ret < 0) { break; } @@ -1041,20 +1013,20 @@ bool QProcessPrivate::waitForFinished(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_pollfd_check(poller.childStartedPipe(), POLLIN)) { if (!_q_startupNotification()) return false; } - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_pollfd_check(poller.stdinPipe(), POLLOUT)) _q_canWrite(); - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stdoutPipe(), POLLIN)) _q_canReadStandardOutput(); - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stderrPipe(), POLLIN)) _q_canReadStandardError(); - if (forkfd == -1 || FD_ISSET(forkfd, &fdread)) { + if (qt_pollfd_check(poller.forkfd(), POLLIN)) { if (_q_processDied()) return true; } @@ -1062,14 +1034,6 @@ bool QProcessPrivate::waitForFinished(int msecs) return false; } -bool QProcessPrivate::waitForWrite(int msecs) -{ - fd_set fdwrite; - FD_ZERO(&fdwrite); - FD_SET(stdinChannel.pipe[1], &fdwrite); - return qt_select_msecs(stdinChannel.pipe[1] + 1, 0, &fdwrite, msecs < 0 ? 0 : msecs) == 1; -} - void QProcessPrivate::findExitCode() { } |