summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qprocess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qprocess.cpp')
-rw-r--r--src/corelib/io/qprocess.cpp775
1 files changed, 465 insertions, 310 deletions
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 745c88e726..f5e9708365 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -1,89 +1,12 @@
-/****************************************************************************
-**
-** 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: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 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 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.
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QPROCESS_DEBUG
#include <qdebug.h>
#include <qdir.h>
#include <qscopedvaluerollback.h>
-#if defined(Q_OS_WIN)
-#include <qtimer.h>
-#endif
-#if defined QPROCESS_DEBUG
-#include <qstring.h>
-#include <ctype.h>
-
-QT_BEGIN_NAMESPACE
-/*
- Returns a human readable representation of the first \a len
- characters in \a data.
-*/
-static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
-{
- if (!data) return "(null)";
- QByteArray out;
- for (int i = 0; i < len && i < maxSize; ++i) {
- char c = data[i];
- if (isprint(c)) {
- out += c;
- } else switch (c) {
- case '\n': out += "\\n"; break;
- case '\r': out += "\\r"; break;
- case '\t': out += "\\t"; break;
- default:
- char buf[5];
- qsnprintf(buf, sizeof(buf), "\\%3o", c);
- buf[4] = '\0';
- out += QByteArray(buf);
- }
- }
-
- if (len < maxSize)
- out += "...";
-
- return out;
-}
-
-QT_END_NAMESPACE
-
-#endif
#include "qprocess.h"
#include "qprocess_p.h"
@@ -91,15 +14,8 @@ QT_END_NAMESPACE
#include <qbytearray.h>
#include <qdeadlinetimer.h>
#include <qcoreapplication.h>
-#include <qsocketnotifier.h>
#include <qtimer.h>
-#ifdef Q_OS_WIN
-#include <qwineventnotifier.h>
-#else
-#include <private/qcore_unix_p.h>
-#endif
-
#if __has_include(<paths.h>)
#include <paths.h>
#endif
@@ -119,6 +35,8 @@ QT_BEGIN_NAMESPACE
\reentrant
\since 4.6
+ \compares equality
+
A process's environment is composed of a set of key=value pairs known as
environment variables. The QProcessEnvironment class wraps that concept
and allows easy manipulation of those variables. It's meant to be used
@@ -146,7 +64,7 @@ QStringList QProcessEnvironmentPrivate::toList() const
QStringList result;
result.reserve(vars.size());
for (auto it = vars.cbegin(), end = vars.cend(); it != end; ++it)
- result << nameToString(it.key()) + QLatin1Char('=') + valueToString(it.value());
+ result << nameToString(it.key()) + u'=' + valueToString(it.value());
return result;
}
@@ -156,7 +74,7 @@ QProcessEnvironment QProcessEnvironmentPrivate::fromList(const QStringList &list
QStringList::ConstIterator it = list.constBegin(),
end = list.constEnd();
for ( ; it != end; ++it) {
- int pos = it->indexOf(QLatin1Char('='), 1);
+ const qsizetype pos = it->indexOf(u'=', 1);
if (pos < 1)
continue;
@@ -196,14 +114,43 @@ void QProcessEnvironmentPrivate::insert(const QProcessEnvironmentPrivate &other)
}
/*!
+ \enum QProcessEnvironment::Initialization
+
+ This enum contains a token that is used to disambiguate constructors.
+
+ \value InheritFromParent A QProcessEnvironment will be created that, when
+ set on a QProcess, causes it to inherit variables from its parent.
+
+ \since 6.3
+*/
+
+/*!
Creates a new QProcessEnvironment object. This constructor creates an
empty environment. If set on a QProcess, this will cause the current
- environment variables to be removed.
+ environment variables to be removed (except for PATH and SystemRoot
+ on Windows).
*/
-QProcessEnvironment::QProcessEnvironment()
- : d(nullptr)
-{
-}
+QProcessEnvironment::QProcessEnvironment() : d(new QProcessEnvironmentPrivate) { }
+
+/*!
+ Creates an object that when set on QProcess will cause it to be executed with
+ environment variables inherited from its parent process.
+
+ \note The created object does not store any environment variables by itself,
+ it just indicates to QProcess to arrange for inheriting the environment at the
+ time when the new process is started. Adding any environment variables to
+ the created object will disable inheritance of the environment and result in
+ an environment containing only the added environment variables.
+
+ If a modified version of the parent environment is wanted, start with the
+ return value of \c systemEnvironment() and modify that (but note that changes to
+ the parent process's environment after that is created won't be reflected
+ in the modified environment).
+
+ \sa inheritsFromParent(), systemEnvironment()
+ \since 6.3
+*/
+QProcessEnvironment::QProcessEnvironment(QProcessEnvironment::Initialization) noexcept { }
/*!
Frees the resources associated with this QProcessEnvironment object.
@@ -239,15 +186,17 @@ QProcessEnvironment &QProcessEnvironment::operator=(const QProcessEnvironment &o
*/
/*!
- \fn bool QProcessEnvironment::operator !=(const QProcessEnvironment &other) const
+ \fn bool QProcessEnvironment::operator!=(const QProcessEnvironment &lhs, const QProcessEnvironment &rhs)
- Returns \c true if this and the \a other QProcessEnvironment objects are different.
+ Returns \c true if the process environment objects \a lhs and \a rhs are different.
\sa operator==()
*/
/*!
- Returns \c true if this and the \a other QProcessEnvironment objects are equal.
+ \fn bool QProcessEnvironment::operator==(const QProcessEnvironment &lhs, const QProcessEnvironment &rhs)
+
+ Returns \c true if the process environment objects \a lhs and \a rhs are equal.
Two QProcessEnvironment objects are considered equal if they have the same
set of key=value pairs. The comparison of keys is done case-sensitive on
@@ -255,26 +204,22 @@ QProcessEnvironment &QProcessEnvironment::operator=(const QProcessEnvironment &o
\sa operator!=(), contains()
*/
-bool QProcessEnvironment::operator==(const QProcessEnvironment &other) const
+bool comparesEqual(const QProcessEnvironment &lhs, const QProcessEnvironment &rhs)
{
- if (d == other.d)
+ if (lhs.d == rhs.d)
return true;
- if (d) {
- if (other.d) {
- return d->vars == other.d->vars;
- } else {
- return isEmpty();
- }
- } else {
- return other.isEmpty();
- }
+
+ return lhs.d && rhs.d && lhs.d->vars == rhs.d->vars;
}
/*!
Returns \c true if this QProcessEnvironment object is empty: that is
there are no key=value pairs set.
- \sa clear(), systemEnvironment(), insert()
+ This method also returns \c true for objects that were constructed using
+ \c{QProcessEnvironment::InheritFromParent}.
+
+ \sa clear(), systemEnvironment(), insert(), inheritsFromParent()
*/
bool QProcessEnvironment::isEmpty() const
{
@@ -283,9 +228,24 @@ bool QProcessEnvironment::isEmpty() const
}
/*!
+ Returns \c true if this QProcessEnvironment was constructed using
+ \c{QProcessEnvironment::InheritFromParent}.
+
+ \since 6.3
+ \sa isEmpty()
+*/
+bool QProcessEnvironment::inheritsFromParent() const
+{
+ return !d;
+}
+
+/*!
Removes all key=value pairs from this QProcessEnvironment object, making
it empty.
+ If the environment was constructed using \c{QProcessEnvironment::InheritFromParent}
+ it remains unchanged.
+
\sa isEmpty(), systemEnvironment()
*/
void QProcessEnvironment::clear()
@@ -389,6 +349,9 @@ QStringList QProcessEnvironment::toStringList() const
Returns a list containing all the variable names in this QProcessEnvironment
object.
+
+ The returned list is empty for objects constructed using
+ \c{QProcessEnvironment::InheritFromParent}.
*/
QStringList QProcessEnvironment::keys() const
{
@@ -428,6 +391,8 @@ void QProcessPrivate::Channel::clear()
process->stdoutChannel.type = Normal;
process->stdoutChannel.process = nullptr;
break;
+ default:
+ break;
}
type = Normal;
@@ -491,6 +456,97 @@ void QProcessPrivate::Channel::clear()
\note QProcess is not supported on VxWorks, iOS, tvOS, or watchOS.
+ \section1 Finding the Executable
+
+ The program to be run can be set either by calling setProgram() or directly
+ in the start() call. The effect of calling start() with the program name
+ and arguments is equivalent to calling setProgram() and setArguments()
+ before that function and then calling the overload without those
+ parameters.
+
+ QProcess interprets the program name in one of three different ways,
+ similar to how Unix shells and the Windows command interpreter operate in
+ their own command-lines:
+
+ \list
+ \li If the program name is an absolute path, then that is the exact
+ executable that will be launched and QProcess performs no searching.
+
+ \li If the program name is a relative path with more than one path
+ component (that is, it contains at least one slash), the starting
+ directory where that relative path is searched is OS-dependent: on
+ Windows, it's the parent process' current working dir, while on Unix it's
+ the one set with setWorkingDirectory().
+
+ \li If the program name is a plain file name with no slashes, the
+ behavior is operating-system dependent. On Unix systems, QProcess will
+ search the \c PATH environment variable; on Windows, the search is
+ performed by the OS and will first the parent process' current directory
+ before the \c PATH environment variable (see the documentation for
+ \l{CreateProcess} for the full list).
+ \endlist
+
+ To avoid platform-dependent behavior or any issues with how the current
+ application was launched, it is advisable to always pass an absolute path
+ to the executable to be launched. For auxiliary binaries shipped with the
+ application, one can construct such a path starting with
+ QCoreApplication::applicationDirPath(). Similarly, to explicitly run an
+ executable that is to be found relative to the directory set with
+ setWorkingDirectory(), use a program path starting with "./" or "../" as
+ the case may be.
+
+ On Windows, the ".exe" suffix is not required for most uses, except those
+ outlined in the \l{CreateProcess} documentation. Additionally, QProcess
+ will convert the Unix-style forward slashes to Windows path backslashes for
+ the program name. This allows code using QProcess to be written in a
+ cross-platform manner, as shown in the examples above.
+
+ QProcess does not support directly executing Unix shell or Windows command
+ interpreter built-in functions, such as \c{cmd.exe}'s \c dir command or the
+ Bourne shell's \c export. On Unix, even though many shell built-ins are
+ also provided as separate executables, their behavior may differ from those
+ implemented as built-ins. To run those commands, one should explicitly
+ execute the interpreter with suitable options. For Unix systems, launch
+ "/bin/sh" with two arguments: "-c" and a string with the command-line to be
+ run. For Windows, due to the non-standard way \c{cmd.exe} parses its
+ command-line, use setNativeArguments() (for example, "/c dir d:").
+
+ \section1 Environment variables
+
+ The QProcess API offers methods to manipulate the environment variables
+ that the child process will see. By default, the child process will have a
+ copy of the current process environment variables that exist at the time
+ the start() function is called. This means that any modifications performed
+ using qputenv() prior to that call will be reflected in the child process'
+ environment. Note that QProcess makes no attempt to prevent race conditions
+ with qputenv() happening in other threads, so it is recommended to avoid
+ qputenv() after the application's initial start up.
+
+ The environment for a specific child can be modified using the
+ processEnvironment() and setProcessEnvironment() functions, which use the
+ \l QProcessEnvironment class. By default, processEnvironment() will return
+ an object for which QProcessEnvironment::inheritsFromParent() is true.
+ Setting an environment that does not inherit from the parent will cause
+ QProcess to use exactly that environment for the child when it is started.
+
+ The normal scenario starts from the current environment by calling
+ QProcessEnvironment::systemEnvironment() and then proceeds to adding,
+ changing, or removing specific variables. The resulting variable roster can
+ then be applied to a QProcess with setProcessEnvironment().
+
+ It is possible to remove all variables from the environment or to start
+ from an empty environment, using the QProcessEnvironment() default
+ constructor. This is not advisable outside of controlled and
+ system-specific conditions, as there may be system variables that are set
+ in the current process environment and are required for proper execution
+ of the child process.
+
+ On Windows, QProcess will copy the current process' \c "PATH" and \c
+ "SystemRoot" environment variables if they were unset. It is not possible
+ to unset them completely, but it is possible to set them to empty values.
+ Setting \c "PATH" to empty on Windows will likely cause the child process
+ to fail to start.
+
\section1 Communicating via Channels
Processes have two predefined output channels: The standard
@@ -539,11 +595,6 @@ void QProcessPrivate::Channel::clear()
command line option; X11 applications generally accept a
\c{-geometry} command line option.
- \note On QNX, setting the working directory may cause all
- application threads, with the exception of the QProcess caller
- thread, to temporarily freeze during the spawning process,
- owing to a limitation in the operating system.
-
\section1 Synchronous Process API
QProcess provides a set of functions which allow it to be used
@@ -571,15 +622,6 @@ void QProcessPrivate::Channel::clear()
\snippet process/process.cpp 0
- \section1 Notes for Windows Users
-
- Some Windows commands (for example, \c dir) are not provided by
- separate applications, but by the command interpreter itself.
- If you attempt to use QProcess to execute these commands directly,
- it won't work. One possible solution is to execute the command
- interpreter itself (\c{cmd.exe} on some Windows systems), and ask
- the interpreter to execute the desired command.
-
\sa QBuffer, QFile, QTcpSocket
*/
@@ -767,6 +809,98 @@ void QProcessPrivate::Channel::clear()
*/
/*!
+ \class QProcess::UnixProcessParameters
+ \inmodule QtCore
+ \note This struct is only available on Unix platforms
+ \since 6.6
+
+ This struct can be used to pass extra, Unix-specific configuration for the
+ child process using QProcess::setUnixProcessParameters().
+
+ Its members are:
+ \list
+ \li UnixProcessParameters::flags Flags, see QProcess::UnixProcessFlags
+ \li UnixProcessParameters::lowestFileDescriptorToClose The lowest file
+ descriptor to close.
+ \endlist
+
+ When the QProcess::UnixProcessFlags::CloseFileDescriptors flag is set in
+ the \c flags field, QProcess closes the application's open file descriptors
+ before executing the child process. The descriptors 0, 1, and 2 (that is,
+ \c stdin, \c stdout, and \c stderr) are left alone, along with the ones
+ numbered lower than the value of the \c lowestFileDescriptorToClose field.
+
+ All of the settings above can also be manually achieved by calling the
+ respective POSIX function from a handler set with
+ QProcess::setChildProcessModifier(). This structure allows QProcess to deal
+ with any platform-specific differences, benefit from certain optimizations,
+ and reduces code duplication. Moreover, if any of those functions fail,
+ QProcess will enter QProcess::FailedToStart state, while the child process
+ modifier callback is not allowed to fail.
+
+ \sa QProcess::setUnixProcessParameters(), QProcess::setChildProcessModifier()
+*/
+
+/*!
+ \enum QProcess::UnixProcessFlag
+ \since 6.6
+
+ These flags can be used in the \c flags field of \l UnixProcessParameters.
+
+ \value CloseFileDescriptors Close all file descriptors above the threshold
+ defined by \c lowestFileDescriptorToClose, preventing any currently
+ open descriptor in the parent process from accidentally leaking to the
+ child. The \c stdin, \c stdout, and \c stderr file descriptors are
+ never closed.
+
+ \value [since 6.7] CreateNewSession Starts a new process session, by calling
+ \c{setsid(2)}. This allows the child process to outlive the session
+ the current process is in. This is one of the steps that
+ startDetached() takes to allow the process to detach, and is also one
+ of the steps to daemonize a process.
+
+ \value [since 6.7] DisconnectControllingTerminal Requests that the process
+ disconnect from its controlling terminal, if it has one. If it has
+ none, nothing happens. Processes still connected to a controlling
+ terminal may get a Hang Up (\c SIGHUP) signal if the terminal
+ closes, or one of the other terminal-control signals (\c SIGTSTP, \c
+ SIGTTIN, \c SIGTTOU). Note that on some operating systems, a process
+ may only disconnect from the controlling terminal if it is the
+ session leader, meaning the \c CreateNewSession flag may be
+ required. Like it, this is one of the steps to daemonize a process.
+
+ \value IgnoreSigPipe Always sets the \c SIGPIPE signal to ignored
+ (\c SIG_IGN), even if the \c ResetSignalHandlers flag was set. By
+ default, if the child attempts to write to its standard output or
+ standard error after the respective channel was closed with
+ QProcess::closeReadChannel(), it would get the \c SIGPIPE signal and
+ terminate immediately; with this flag, the write operation fails
+ without a signal and the child may continue executing.
+
+ \value [since 6.7] ResetIds Drops any retained, effective user or group
+ ID the current process may still have (see \c{setuid(2)} and
+ \c{setgid(2)}, plus QCoreApplication::setSetuidAllowed()). This is
+ useful if the current process was setuid or setgid and does not wish
+ the child process to retain the elevated privileges.
+
+ \value ResetSignalHandlers Resets all Unix signal handlers back to their
+ default state (that is, pass \c SIG_DFL to \c{signal(2)}). This flag
+ is useful to ensure any ignored (\c SIG_IGN) signal does not affect
+ the child's behavior.
+
+ \value UseVFork Requests that QProcess use \c{vfork(2)} to start the child
+ process. Use this flag to indicate that the callback function set
+ with setChildProcessModifier() is safe to execute in the child side of
+ a \c{vfork(2)}; that is, the callback does not modify any non-local
+ variables (directly or through any function it calls), nor attempts
+ to communicate with the parent process. It is implementation-defined
+ if QProcess will actually use \c{vfork(2)} and if \c{vfork(2)} is
+ different from standard \c{fork(2)}.
+
+ \sa setUnixProcessParameters(), unixProcessParameters()
+*/
+
+/*!
\fn void QProcess::errorOccurred(QProcess::ProcessError error)
\since 5.6
@@ -828,7 +962,9 @@ void QProcessPrivate::Channel::clear()
QProcessPrivate::QProcessPrivate()
{
readBufferChunkSize = QRINGBUFFER_CHUNKSIZE;
+#ifndef Q_OS_WIN
writeBufferChunkSize = QRINGBUFFER_CHUNKSIZE;
+#endif
}
/*!
@@ -845,46 +981,6 @@ QProcessPrivate::~QProcessPrivate()
/*!
\internal
*/
-void QProcessPrivate::cleanup()
-{
- q_func()->setProcessState(QProcess::NotRunning);
-#ifdef Q_OS_WIN
- if (stdinWriteTrigger) {
- delete stdinWriteTrigger;
- stdinWriteTrigger = 0;
- }
- if (processFinishedNotifier) {
- delete processFinishedNotifier;
- processFinishedNotifier = 0;
- }
- if (pid) {
- CloseHandle(pid->hThread);
- CloseHandle(pid->hProcess);
- delete pid;
- pid = nullptr;
- }
-#else
- pid = 0;
-#endif
-
- if (stateNotifier) {
- delete stateNotifier;
- stateNotifier = nullptr;
- }
- closeChannel(&stdoutChannel);
- closeChannel(&stderrChannel);
- closeChannel(&stdinChannel);
- destroyPipe(childStartedPipe);
-#ifdef Q_OS_UNIX
- if (forkfd != -1)
- qt_safe_close(forkfd);
- forkfd = -1;
-#endif
-}
-
-/*!
- \internal
-*/
void QProcessPrivate::setError(QProcess::ProcessError error, const QString &description)
{
processError = error;
@@ -922,7 +1018,7 @@ void QProcessPrivate::setErrorAndEmit(QProcess::ProcessError error, const QStrin
Q_Q(QProcess);
Q_ASSERT(error != QProcess::UnknownError);
setError(error, description);
- emit q->errorOccurred(processError);
+ emit q->errorOccurred(QProcess::ProcessError(processError));
}
/*!
@@ -963,6 +1059,16 @@ bool QProcessPrivate::openChannels()
/*!
\internal
*/
+void QProcessPrivate::closeChannels()
+{
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
+ closeChannel(&stdinChannel);
+}
+
+/*!
+ \internal
+*/
bool QProcessPrivate::openChannelsForDetached()
{
// stdin channel.
@@ -1094,36 +1200,6 @@ bool QProcessPrivate::_q_canReadStandardError()
/*!
\internal
*/
-bool QProcessPrivate::_q_canWrite()
-{
- if (writeBuffer.isEmpty()) {
-#ifdef Q_OS_WIN
- if (stdinChannel.closed && pipeWriterBytesToWrite() == 0)
- closeWriteChannel();
-#else
- if (stdinChannel.notifier)
- stdinChannel.notifier->setEnabled(false);
-#endif
-#if defined QPROCESS_DEBUG
- qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer).");
-#endif
- return false;
- }
-
- const bool writeSucceeded = writeToStdin();
-
-#ifdef Q_OS_UNIX
- if (writeBuffer.isEmpty() && stdinChannel.closed)
- closeWriteChannel();
- else if (stdinChannel.notifier)
- stdinChannel.notifier->setEnabled(!writeBuffer.isEmpty());
-#endif
- return writeSucceeded;
-}
-
-/*!
- \internal
-*/
void QProcessPrivate::_q_processDied()
{
#if defined QPROCESS_DEBUG
@@ -1165,10 +1241,8 @@ void QProcessPrivate::processFinished()
cleanup();
- if (crashed) {
- exitStatus = QProcess::CrashExit;
+ if (exitStatus == QProcess::CrashExit)
setErrorAndEmit(QProcess::Crashed);
- }
// we received EOF now:
emit q->readChannelFinished();
@@ -1176,7 +1250,7 @@ void QProcessPrivate::processFinished()
//emit q->standardOutputClosed();
//emit q->standardErrorClosed();
- emit q->finished(exitCode, exitStatus);
+ emit q->finished(exitCode, QProcess::ExitStatus(exitStatus));
#if defined QPROCESS_DEBUG
qDebug("QProcessPrivate::processFinished(): process is dead");
@@ -1261,7 +1335,7 @@ QProcess::~QProcess()
QProcess::ProcessChannelMode QProcess::processChannelMode() const
{
Q_D(const QProcess);
- return d->processChannelMode;
+ return ProcessChannelMode(d->processChannelMode);
}
/*!
@@ -1291,7 +1365,7 @@ void QProcess::setProcessChannelMode(ProcessChannelMode mode)
QProcess::InputChannelMode QProcess::inputChannelMode() const
{
Q_D(const QProcess);
- return d->inputChannelMode;
+ return InputChannelMode(d->inputChannelMode);
}
/*!
@@ -1429,6 +1503,9 @@ void QProcess::setStandardInputFile(const QString &fileName)
Calling setStandardOutputFile() after the process has started has
no effect.
+ If \a fileName is an empty string, it stops redirecting the standard
+ output. This is useful for restoring the standard output after redirection.
+
\sa setStandardInputFile(), setStandardErrorFile(),
setStandardOutputProcess()
*/
@@ -1488,7 +1565,7 @@ void QProcess::setStandardOutputProcess(QProcess *destination)
dto->stdinChannel.pipeFrom(dfrom);
}
-#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
/*!
\since 4.7
@@ -1572,12 +1649,12 @@ void QProcess::setCreateProcessArgumentsModifier(CreateProcessArgumentModifier m
\note This function is only available on Unix platforms.
- \sa setChildProcessModifier()
+ \sa setChildProcessModifier(), unixProcessParameters()
*/
std::function<void(void)> QProcess::childProcessModifier() const
{
Q_D(const QProcess);
- return d->childProcessModifier;
+ return d->unixExtras ? d->unixExtras->childProcessModifier : std::function<void(void)>();
}
/*!
@@ -1586,20 +1663,28 @@ std::function<void(void)> QProcess::childProcessModifier() const
Sets the \a modifier function for the child process, for Unix systems
(including \macos; for Windows, see setCreateProcessArgumentsModifier()).
The function contained by the \a modifier argument will be invoked in the
- child process after \c{fork()} is completed and QProcess has set up the
- standard file descriptors for the child process, but before \c{execve()},
- inside start(). The modifier is useful to change certain properties of the
- child process, such as setting up additional file descriptors or closing
- others, changing the nice level, disconnecting from the controlling TTY,
- etc.
+ child process after \c{fork()} or \c{vfork()} is completed and QProcess has
+ set up the standard file descriptors for the child process, but before
+ \c{execve()}, inside start().
The following shows an example of setting up a child process to run without
privileges:
\snippet code/src_corelib_io_qprocess.cpp 4
- If the modifier function needs to exit the process, remember to use
- \c{_exit()}, not \c{exit()}.
+ If the modifier function experiences a failure condition, it can use
+ failChildProcessModifier() to report the situation to the QProcess caller.
+ Alternatively, it may use other methods of stopping the process, like
+ \c{_exit()}, or \c{abort()}.
+
+ Certain properties of the child process, such as closing all extraneous
+ file descriptors or disconnecting from the controlling TTY, can be more
+ readily achieved by using setUnixProcessParameters(), which can detect
+ failure and report a \l{QProcess::}{FailedToStart} condition. The modifier
+ is useful to change certain uncommon properties of the child process, such
+ as setting up additional file descriptors. If both a child process modifier
+ and Unix process parameters are set, the modifier is run before these
+ parameters are applied.
\note In multithreaded applications, this function must be careful not to
call any functions that may lock mutexes that may have been in use in
@@ -1607,12 +1692,125 @@ std::function<void(void)> QProcess::childProcessModifier() const
"async-signal-safe" is advised). Most of the Qt API is unsafe inside this
callback, including qDebug(), and may lead to deadlocks.
- \sa childProcessModifier()
+ \note If the UnixProcessParameters::UseVFork flag is set via
+ setUnixProcessParameters(), QProcess may use \c{vfork()} semantics to
+ start the child process, so this function must obey even stricter
+ constraints. First, because it is still sharing memory with the parent
+ process, it must not write to any non-local variable and must obey proper
+ ordering semantics when reading from them, to avoid data races. Second,
+ even more library functions may misbehave; therefore, this function should
+ only make use of low-level system calls, such as \c{read()},
+ \c{write()}, \c{setsid()}, \c{nice()}, and similar.
+
+ \sa childProcessModifier(), failChildProcessModifier(), setUnixProcessParameters()
*/
void QProcess::setChildProcessModifier(const std::function<void(void)> &modifier)
{
Q_D(QProcess);
- d->childProcessModifier = modifier;
+ if (!d->unixExtras)
+ d->unixExtras.reset(new QProcessPrivate::UnixExtras);
+ d->unixExtras->childProcessModifier = modifier;
+}
+
+/*!
+ \fn void QProcess::failChildProcessModifier(const char *description, int error) noexcept
+ \since 6.7
+
+ This functions can be used inside the modifier set with
+ setChildProcessModifier() to indicate an error condition was encountered.
+ When the modifier calls these functions, QProcess will emit errorOccurred()
+ with code QProcess::FailedToStart in the parent process. The \a description
+ can be used to include some information in errorString() to help diagnose
+ the problem, usually the name of the call that failed, similar to the C
+ Library function \c{perror()}. Additionally, the \a error parameter can be
+ an \c{<errno.h>} error code whose text form will also be included.
+
+ For example, a child modifier could prepare an extra file descriptor for
+ the child process this way:
+
+ \code
+ process.setChildProcessModifier([fd, &process]() {
+ if (dup2(fd, TargetFileDescriptor) < 0)
+ process.failChildProcessModifier(errno, "aux comm channel");
+ });
+ process.start();
+ \endcode
+
+ Where \c{fd} is a file descriptor currently open in the parent process. If
+ the \c{dup2()} system call resulted in an \c EBADF condition, the process
+ errorString() could be "Child process modifier reported error: aux comm
+ channel: Bad file descriptor".
+
+ This function does not return to the caller. Using it anywhere except in
+ the child modifier and with the correct QProcess object is undefined
+ behavior.
+
+ \note The implementation imposes a length limit to the \a description
+ parameter to about 500 characters. This does not include the text from the
+ \a error code.
+
+ \sa setChildProcessModifier(), setUnixProcessParameters()
+*/
+
+/*!
+ \since 6.6
+ Returns the \l UnixProcessParameters object describing extra flags and
+ settings that will be applied to the child process on Unix systems. The
+ default settings correspond to a default-constructed UnixProcessParameters.
+
+ \note This function is only available on Unix platforms.
+
+ \sa childProcessModifier()
+*/
+auto QProcess::unixProcessParameters() const noexcept -> UnixProcessParameters
+{
+ Q_D(const QProcess);
+ return d->unixExtras ? d->unixExtras->processParameters : UnixProcessParameters{};
+}
+
+/*!
+ \since 6.6
+ Sets the extra settings and parameters for the child process on Unix
+ systems to be \a params. This function can be used to ask QProcess to
+ modify the child process before launching the target executable.
+
+ This function can be used to change certain properties of the child
+ process, such as closing all extraneous file descriptors, changing the nice
+ level of the child, or disconnecting from the controlling TTY. For more
+ fine-grained control of the child process or to modify it in other ways,
+ use the setChildProcessModifier() function. If both a child process
+ modifier and Unix process parameters are set, the modifier is run before
+ these parameters are applied.
+
+ \note This function is only available on Unix platforms.
+
+ \sa unixProcessParameters(), setChildProcessModifier()
+*/
+void QProcess::setUnixProcessParameters(const UnixProcessParameters &params)
+{
+ Q_D(QProcess);
+ if (!d->unixExtras)
+ d->unixExtras.reset(new QProcessPrivate::UnixExtras);
+ d->unixExtras->processParameters = params;
+}
+
+/*!
+ \since 6.6
+ \overload
+
+ Sets the extra settings for the child process on Unix systems to \a
+ flagsOnly. This is the same as the overload with just the \c flags field
+ set.
+ \note This function is only available on Unix platforms.
+
+ \sa unixProcessParameters(), setChildProcessModifier()
+*/
+void QProcess::setUnixProcessParameters(UnixProcessFlags flagsOnly)
+{
+ Q_D(QProcess);
+ if (!d->unixExtras)
+ d->unixExtras.reset(new QProcessPrivate::UnixExtras);
+ d->unixExtras->processParameters = { flagsOnly };
}
#endif
@@ -1636,9 +1834,6 @@ QString QProcess::workingDirectory() const
process in this directory. The default behavior is to start the
process in the working directory of the calling process.
- \note On QNX, this may cause all application threads to
- temporarily freeze.
-
\sa workingDirectory(), start()
*/
void QProcess::setWorkingDirectory(const QString &dir)
@@ -1691,11 +1886,11 @@ bool QProcess::isSequential() const
*/
qint64 QProcess::bytesToWrite() const
{
- qint64 size = QIODevice::bytesToWrite();
#ifdef Q_OS_WIN
- size += d_func()->pipeWriterBytesToWrite();
+ return d_func()->pipeWriterBytesToWrite();
+#else
+ return QIODevice::bytesToWrite();
#endif
- return size;
}
/*!
@@ -1706,7 +1901,7 @@ qint64 QProcess::bytesToWrite() const
QProcess::ProcessError QProcess::error() const
{
Q_D(const QProcess);
- return d->processError;
+ return ProcessError(d->processError);
}
/*!
@@ -1717,7 +1912,7 @@ QProcess::ProcessError QProcess::error() const
QProcess::ProcessState QProcess::state() const
{
Q_D(const QProcess);
- return d->processState;
+ return ProcessState(d->processState);
}
/*!
@@ -1764,7 +1959,8 @@ QStringList QProcess::environment() const
Note how, on Windows, environment variable names are case-insensitive.
- \sa processEnvironment(), QProcessEnvironment::systemEnvironment(), setEnvironment()
+ \sa processEnvironment(), QProcessEnvironment::systemEnvironment(),
+ {Environment variables}
*/
void QProcess::setProcessEnvironment(const QProcessEnvironment &environment)
{
@@ -1774,12 +1970,12 @@ void QProcess::setProcessEnvironment(const QProcessEnvironment &environment)
/*!
\since 4.6
- Returns the environment that QProcess will pass to its child
- process, or an empty object if no environment has been set using
- setEnvironment() or setProcessEnvironment(). If no environment has
- been set, the environment of the calling process will be used.
+ Returns the environment that QProcess will pass to its child process. If no
+ environment has been set using setProcessEnvironment(), this method returns
+ an object indicating the environment will be inherited from the parent.
- \sa setProcessEnvironment(), setEnvironment(), QProcessEnvironment::isEmpty()
+ \sa setProcessEnvironment(), QProcessEnvironment::inheritsFromParent(),
+ {Environment variables}
*/
QProcessEnvironment QProcess::processEnvironment() const
{
@@ -1793,7 +1989,8 @@ QProcessEnvironment QProcess::processEnvironment() const
Returns \c true if the process was started successfully; otherwise
returns \c false (if the operation timed out or if an error
- occurred).
+ occurred). If the process had already started successfully before this
+ function, it returns immediately.
This function can operate without an event loop. It is
useful when writing non-GUI applications and when performing
@@ -1804,9 +2001,6 @@ QProcessEnvironment QProcess::processEnvironment() const
If msecs is -1, this function will not time out.
- \note On some UNIX operating systems, this function may return true but
- the process may later report a QProcess::FailedToStart error.
-
\sa started(), waitForReadyRead(), waitForBytesWritten(), waitForFinished()
*/
bool QProcess::waitForStarted(int msecs)
@@ -1914,8 +2108,7 @@ void QProcess::setProcessState(ProcessState state)
*/
auto QProcess::setupChildProcess() -> Use_setChildProcessModifier_Instead
{
- Q_UNREACHABLE();
- return {};
+ Q_UNREACHABLE_RETURN({});
}
#endif
@@ -1932,44 +2125,6 @@ qint64 QProcess::readData(char *data, qint64 maxlen)
return 0;
}
-/*! \reimp
-*/
-qint64 QProcess::writeData(const char *data, qint64 len)
-{
- Q_D(QProcess);
-
- if (d->stdinChannel.closed) {
-#if defined QPROCESS_DEBUG
- qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)",
- data, qt_prettyDebug(data, len, 16).constData(), len);
-#endif
- return 0;
- }
-
-#if defined(Q_OS_WIN)
- if (!d->stdinWriteTrigger) {
- d->stdinWriteTrigger = new QTimer;
- d->stdinWriteTrigger->setSingleShot(true);
- QObjectPrivate::connect(d->stdinWriteTrigger, &QTimer::timeout,
- d, &QProcessPrivate::_q_canWrite);
- }
-#endif
-
- d->write(data, len);
-#ifdef Q_OS_WIN
- if (!d->stdinWriteTrigger->isActive())
- d->stdinWriteTrigger->start();
-#else
- if (d->stdinChannel.notifier)
- d->stdinChannel.notifier->setEnabled(true);
-#endif
-#if defined QPROCESS_DEBUG
- qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)",
- data, qt_prettyDebug(data, len, 16).constData(), len, len);
-#endif
- return len;
-}
-
/*!
Regardless of the current read channel, this function returns all
data available from the standard output of the process as a
@@ -1995,27 +2150,38 @@ QByteArray QProcess::readAllStandardOutput()
*/
QByteArray QProcess::readAllStandardError()
{
- ProcessChannel tmp = readChannel();
- setReadChannel(StandardError);
- QByteArray data = readAll();
- setReadChannel(tmp);
+ Q_D(QProcess);
+ QByteArray data;
+ if (d->processChannelMode == MergedChannels) {
+ qWarning("QProcess::readAllStandardError: Called with MergedChannels");
+ } else {
+ ProcessChannel tmp = readChannel();
+ setReadChannel(StandardError);
+ data = readAll();
+ setReadChannel(tmp);
+ }
return data;
}
/*!
Starts the given \a program in a new process, passing the command line
- arguments in \a arguments.
+ arguments in \a arguments. See setProgram() for information about how
+ QProcess searches for the executable to be run. The OpenMode is set to \a
+ mode. No further splitting of the arguments is performed.
The QProcess object will immediately enter the Starting state. If the
process starts successfully, QProcess will emit started(); otherwise,
- errorOccurred() will be emitted.
-
- \note Processes are started asynchronously, which means the started()
- and errorOccurred() signals may be delayed. Call waitForStarted() to make
- sure the process has started (or has failed to start) and those signals
- have been emitted.
-
- \note No further splitting of the arguments is performed.
+ errorOccurred() will be emitted. Do note that on platforms that are able to
+ start child processes synchronously (notably Windows), those signals will
+ be emitted before this function returns and this QProcess object will
+ transition to either QProcess::Running or QProcess::NotRunning state,
+ respectively. On others paltforms, the started() and errorOccurred()
+ signals will be delayed.
+
+ Call waitForStarted() to make sure the process has started (or has failed
+ to start) and those signals have been emitted. It is safe to call that
+ function even if the process starting state is already known, though the
+ signal will not be emitted again.
\b{Windows:} The arguments are quoted and joined into a command line
that is compatible with the \c CommandLineToArgvW() Windows function.
@@ -2024,12 +2190,16 @@ QByteArray QProcess::readAllStandardError()
not follow the \c CommandLineToArgvW() rules is cmd.exe and, by
consequence, all batch scripts.
- The OpenMode is set to \a mode.
-
If the QProcess object is already running a process, a warning may be
printed at the console, and the existing process will continue running
unaffected.
+ \note Success at starting the child process only implies the operating
+ system has successfully created the process and assigned the resources
+ every process has, such as its process ID. The child process may crash or
+ otherwise fail very early and thus not produce its expected output. On most
+ operating systems, this may include dynamic linking errors.
+
\sa processId(), started(), waitForStarted(), setNativeArguments()
*/
void QProcess::start(const QString &program, const QStringList &arguments, OpenMode mode)
@@ -2130,9 +2300,6 @@ void QProcess::startCommand(const QString &command, OpenMode mode)
If workingDirectory() is empty, the working directory is inherited
from the calling process.
- \note On QNX, this may cause all application threads to
- temporarily freeze.
-
If the function is successful then *\a pid is set to the process identifier
of the started process; otherwise, it's set to -1. Note that the child
process may exit and the PID may become invalid without notice.
@@ -2265,7 +2432,7 @@ QStringList QProcess::splitCommand(QStringView command)
// "hello world". three consecutive double quotes represent
// the quote character itself.
for (int i = 0; i < command.size(); ++i) {
- if (command.at(i) == QLatin1Char('"')) {
+ if (command.at(i) == u'"') {
++quoteCount;
if (quoteCount == 3) {
// third consecutive quote
@@ -2313,7 +2480,12 @@ QString QProcess::program() const
Set the \a program to use when starting the process.
This function must be called before start().
- \sa start(), setArguments(), program()
+ If \a program is an absolute path, it specifies the exact executable that
+ will be launched. Relative paths will be resolved in a platform-specific
+ manner, which includes searching the \c PATH environment variable (see
+ \l{Finding the Executable} for details).
+
+ \sa start(), setArguments(), program(), QStandardPaths::findExecutable()
*/
void QProcess::setProgram(const QString &program)
{
@@ -2415,7 +2587,7 @@ int QProcess::exitCode() const
QProcess::ExitStatus QProcess::exitStatus() const
{
Q_D(const QProcess);
- return d->exitStatus;
+ return ExitStatus(d->exitStatus);
}
/*!
@@ -2476,18 +2648,6 @@ bool QProcess::startDetached(const QString &program,
return process.startDetached(pid);
}
-QT_BEGIN_INCLUDE_NAMESPACE
-#if defined(Q_OS_MACOS)
-# include <crt_externs.h>
-# define environ (*_NSGetEnviron())
-#elif defined(QT_PLATFORM_UIKIT)
- static char *qt_empty_environ[] = { 0 };
-#define environ qt_empty_environ
-#elif !defined(Q_OS_WIN)
- extern char **environ;
-#endif
-QT_END_INCLUDE_NAMESPACE
-
/*!
\since 4.1
@@ -2509,12 +2669,7 @@ QT_END_INCLUDE_NAMESPACE
*/
QStringList QProcess::systemEnvironment()
{
- QStringList tmp;
- char *entry = nullptr;
- int count = 0;
- while ((entry = environ[count++]))
- tmp << QString::fromLocal8Bit(entry);
- return tmp;
+ return QProcessEnvironment::systemEnvironment().toStringList();
}
/*!