summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_io_qprocess.cpp34
-rw-r--r--src/corelib/io/qprocess.cpp76
-rw-r--r--src/corelib/io/qprocess.h15
-rw-r--r--src/corelib/io/qprocess_p.h2
-rw-r--r--src/corelib/io/qprocess_unix.cpp7
-rw-r--r--tests/auto/corelib/io/qprocess/tst_qprocess.cpp47
-rw-r--r--tests/auto/corelib/tools/qsharedpointer/externaltests.cpp32
7 files changed, 128 insertions, 85 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_io_qprocess.cpp b/src/corelib/doc/snippets/code/src_corelib_io_qprocess.cpp
index 8a2825cd10..5bbbd1b2cd 100644
--- a/src/corelib/doc/snippets/code/src_corelib_io_qprocess.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_io_qprocess.cpp
@@ -90,28 +90,22 @@ process2.start("command2");
//! [4]
-class SandboxProcess : public QProcess
+void runSandboxed(const QString &name, const QStringList &arguments)
{
- ...
- protected:
- void setupChildProcess() override;
- ...
-};
-
-void SandboxProcess::setupChildProcess()
-{
- // Drop all privileges in the child process, and enter
- // a chroot jail.
-#if defined Q_OS_UNIX
- ::setgroups(0, 0);
- ::chroot("/etc/safe");
- ::chdir("/");
- ::setgid(safeGid);
- ::setuid(safeUid);
- ::umask(0);
-#endif
+ QProcess proc;
+ proc.setChildProcessModifier([] {
+ // Drop all privileges in the child process, and enter
+ // a chroot jail.
+ ::setgroups(0, 0);
+ ::chroot("/etc/safe");
+ ::chdir("/");
+ ::setgid(safeGid);
+ ::setuid(safeUid);
+ ::umask(0);
+ });
+ proc.start(name, arguments);
+ proc.waitForFinished();
}
-
//! [4]
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 8e8664fda4..483c99ed5f 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -1529,7 +1529,7 @@ QProcess::CreateProcessArgumentModifier QProcess::createProcessArgumentsModifier
\note This function is available only on the Windows platform and requires
C++11.
- \sa QProcess::CreateProcessArgumentModifier
+ \sa QProcess::CreateProcessArgumentModifier, setChildProcessModifier()
*/
void QProcess::setCreateProcessArgumentsModifier(CreateProcessArgumentModifier modifier)
{
@@ -1539,6 +1539,59 @@ void QProcess::setCreateProcessArgumentsModifier(CreateProcessArgumentModifier m
#endif
+#if defined(Q_OS_UNIX) || defined(Q_QDOC)
+/*!
+ \since 6.0
+
+ Returns the modifier function previously set by calling
+ setChildProcessModifier().
+
+ \note This function is only available on Unix platforms.
+
+ \sa setChildProcessModifier()
+*/
+std::function<void(void)> QProcess::childProcessModifier() const
+{
+ Q_D(const QProcess);
+ return d->childProcessModifier;
+}
+
+/*!
+ \since 6.0
+
+ 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.
+
+ 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()}.
+
+ \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
+ other threads (in general, using only functions defined by POSIX as
+ "async-signal-safe" is advised). Most of the Qt API is unsafe inside this
+ callback, including qDebug(), and may lead to deadlocks.
+
+ \sa childProcessModifier()
+*/
+void QProcess::setChildProcessModifier(const std::function<void(void)> &modifier)
+{
+ Q_D(QProcess);
+ d->childProcessModifier = modifier;
+}
+#endif
+
/*!
If QProcess has been assigned a working directory, this function returns
the working directory that the QProcess will enter before the program has
@@ -1843,25 +1896,16 @@ void QProcess::setProcessState(ProcessState state)
emit stateChanged(state, QPrivateSignal());
}
+#if QT_VERSION < QT_VERSION_CHECK(7,0,0)
/*!
- This function is called in the child process context just before the
- program is executed on Unix or \macos (i.e., after \c fork(), but before
- \c execve()). Reimplement this function to do last minute initialization
- of the child process. Example:
-
- \snippet code/src_corelib_io_qprocess.cpp 4
-
- You cannot exit the process (by calling exit(), for instance) from
- this function. If you need to stop the program before it starts
- execution, your workaround is to emit finished() and then call
- exit().
-
- \warning This function is called by QProcess on Unix and \macos
- only. On Windows and QNX, it is not called.
+ \internal
*/
-void QProcess::setupChildProcess()
+auto QProcess::setupChildProcess() -> Use_setChildProcessModifier_Instead
{
+ Q_UNREACHABLE();
+ return {};
}
+#endif
/*! \reimp
*/
diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h
index 9e497c3871..77980e012c 100644
--- a/src/corelib/io/qprocess.h
+++ b/src/corelib/io/qprocess.h
@@ -205,6 +205,10 @@ public:
CreateProcessArgumentModifier createProcessArgumentsModifier() const;
void setCreateProcessArgumentsModifier(CreateProcessArgumentModifier modifier);
#endif // Q_OS_WIN || Q_CLANG_QDOC
+#if defined(Q_OS_UNIX) || defined(Q_CLANG_QDOC)
+ std::function<void(void)> childProcessModifier() const;
+ void setChildProcessModifier(const std::function<void(void)> &modifier);
+#endif
QString workingDirectory() const;
void setWorkingDirectory(const QString &dir);
@@ -263,8 +267,6 @@ Q_SIGNALS:
protected:
void setProcessState(ProcessState state);
- virtual void setupChildProcess();
-
// QIODevice
qint64 readData(char *data, qint64 maxlen) override;
qint64 writeData(const char *data, qint64 len) override;
@@ -273,6 +275,15 @@ private:
Q_DECLARE_PRIVATE(QProcess)
Q_DISABLE_COPY(QProcess)
+#if QT_VERSION < QT_VERSION_CHECK(7,0,0)
+ // ### Qt7: Remove this struct and the virtual function; they're here only
+ // to cause build errors in Qt 5 code that wasn't updated to Qt 6's
+ // setChildProcessModifier()
+ struct Use_setChildProcessModifier_Instead {};
+ QT_DEPRECATED_X("Use setChildProcessModifier() instead")
+ virtual Use_setChildProcessModifier_Instead setupChildProcess();
+#endif
+
Q_PRIVATE_SLOT(d_func(), bool _q_canReadStandardOutput())
Q_PRIVATE_SLOT(d_func(), bool _q_canReadStandardError())
Q_PRIVATE_SLOT(d_func(), bool _q_canWrite())
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index 2587530c09..7cb70cc95c 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -322,6 +322,8 @@ public:
#if defined(Q_OS_WIN)
QString nativeArguments;
QProcess::CreateProcessArgumentModifier modifyCreateProcessArgs;
+#else
+ std::function<void(void)> childProcessModifier;
#endif
QProcessEnvironment environment;
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
index 50390e57f5..f14e78f94e 100644
--- a/src/corelib/io/qprocess_unix.cpp
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -459,7 +459,7 @@ void QProcessPrivate::startProcess()
// safe with vfork semantics: suspend the parent execution until the child
// either execve()s or _exit()s.
int ffdflags = FFD_CLOEXEC;
- if (typeid(*q) != typeid(QProcess))
+ if (childProcessModifier)
ffdflags |= FFD_USE_FORK;
pid_t childPid;
forkfd = ::forkfd(ffdflags , &childPid);
@@ -544,7 +544,6 @@ void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp
{
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
- Q_Q(QProcess);
ChildError error = { 0, {} }; // force zeroing of function[8]
// copy the stdin socket if asked to (without closing on exec)
@@ -574,8 +573,8 @@ void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp
goto report_errno;
}
- // this is a virtual call, and it base behavior is to do nothing.
- q->setupChildProcess();
+ if (childProcessModifier)
+ childProcessModifier();
// execute the process
if (!envp) {
diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
index a7ecf6cae4..317db0574c 100644
--- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
+++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
@@ -63,7 +63,7 @@ private slots:
void getSetCheck();
void constructing();
void simpleStart();
- void setupChildProcess();
+ void setChildProcessModifier();
void startWithOpen();
void startWithOldOpen();
void execute();
@@ -267,49 +267,40 @@ void tst_QProcess::simpleStart()
QCOMPARE(qvariant_cast<QProcess::ProcessState>(spy.at(2).at(0)), QProcess::NotRunning);
}
-void tst_QProcess::setupChildProcess()
+static const char messageFromChildProcess[] = "Message from the child process";
+static void childProcessModifier(int fd)
{
- /* This test exists because in Qt 5.15, the Unix version of QProcess has
- * some code that depends on whether it's an actual QProcess or a
- * derived class */
- static const char setupChildMessage[] = "Called from setupChildProcess()";
- class DerivedProcessClass : public QProcess {
- public:
- int fd;
- DerivedProcessClass(int fd) : fd(fd)
- {
- }
-
- protected:
- void setupChildProcess() override
- {
- QT_WRITE(fd, setupChildMessage, sizeof(setupChildMessage) - 1);
- QT_CLOSE(fd);
- }
- };
+ QT_WRITE(fd, messageFromChildProcess, sizeof(messageFromChildProcess) - 1);
+ QT_CLOSE(fd);
+}
- int pipes[2] = { -1 , -1 };
+void tst_QProcess::setChildProcessModifier()
+{
#ifdef Q_OS_UNIX
+ int pipes[2] = { -1 , -1 };
QVERIFY(qt_safe_pipe(pipes) == 0);
-#endif
- DerivedProcessClass process(pipes[1]);
+ QProcess process;
+ process.setChildProcessModifier([pipes]() {
+ ::childProcessModifier(pipes[1]);
+ });
process.start("testProcessNormal/testProcessNormal");
if (process.state() != QProcess::Starting)
QCOMPARE(process.state(), QProcess::Running);
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
-#ifdef Q_OS_UNIX
- char buf[sizeof setupChildMessage] = {};
+ char buf[sizeof messageFromChildProcess] = {};
qt_safe_close(pipes[1]);
- QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(setupChildMessage) - 1));
- QCOMPARE(buf, setupChildMessage);
+ QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(messageFromChildProcess)) - 1);
+ QCOMPARE(buf, messageFromChildProcess);
qt_safe_close(pipes[0]);
-#endif
QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString()));
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
QCOMPARE(process.exitCode(), 0);
+#else
+ QSKIP("Unix-only test");
+#endif
}
void tst_QProcess::startWithOpen()
diff --git a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp
index d1bb89f549..21ef6e8e35 100644
--- a/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp
+++ b/tests/auto/corelib/tools/qsharedpointer/externaltests.cpp
@@ -86,26 +86,28 @@ namespace QTest {
}
}
+# ifdef Q_OS_UNIX
class QExternalProcess: public QProcess
{
- protected:
-#ifdef Q_OS_UNIX
- void setupChildProcess()
+ public:
+ QExternalProcess()
{
- // run in user code
- QProcess::setupChildProcess();
-
- if (processChannelMode() == ForwardedChannels) {
- // reopen /dev/tty into stdin
- int fd = ::open("/dev/tty", O_RDONLY);
- if (fd == -1)
- return;
- ::dup2(fd, 0);
- ::close(fd);
- }
+ setChildProcessModifier([this]() {
+ // run in user code
+ if (processChannelMode() == ForwardedChannels) {
+ // reopen /dev/tty into stdin
+ int fd = ::open("/dev/tty", O_RDONLY);
+ if (fd == -1)
+ return;
+ ::dup2(fd, 0);
+ ::close(fd);
+ }
+ });
}
-#endif
};
+# else
+ using QExternalProcess = QProcess;
+# endif
#endif // QT_CONFIG(process)
class QExternalTestPrivate