QProcess/Unix: enable setChildProcessModifier for startDetached

Do this by making the actual child-execution code common between
startProcess() and startDetached(). It does mean we've moved the chdir()
operation from the child to the grandchild process, though.

[ChangeLog][QtCore][QProcess] The modifier function set with
setChildProcessModifier() will now also be executed when the process is
started with startDetached().

Change-Id: Icfe44ecf285a480fafe4fffd174d9aa57dd7dfff
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Thiago Macieira 2023-03-18 12:37:51 -07:00 committed by Volker Hilsheimer
parent fb40737b0d
commit 48b6c8503a
2 changed files with 50 additions and 46 deletions

View File

@ -576,18 +576,40 @@ void QProcessPrivate::startProcess()
::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK); ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
} }
static bool callChildProcessModifier(const QProcessPrivate::UnixExtras *unixExtras) noexcept static const char *callChildProcessModifier(const QProcessPrivate::UnixExtras *unixExtras) noexcept
{ {
QT_TRY { QT_TRY {
if (unixExtras->childProcessModifier) if (unixExtras->childProcessModifier)
unixExtras->childProcessModifier(); unixExtras->childProcessModifier();
} QT_CATCH (...) { } QT_CATCH (...) {
errno = FakeErrnoForThrow; errno = FakeErrnoForThrow;
return false; return "throw";
} }
return true; return nullptr;
} }
// this function doesn't return if the execution succeeds
static const char *doExecChild(char **argv, char **envp, int workingDirFd,
const QProcessPrivate::UnixExtras *unixExtras) noexcept
{
// enter the working directory
if (workingDirFd != -1 && fchdir(workingDirFd) == -1)
return "fchdir";
if (unixExtras) {
if (const char *what = callChildProcessModifier(unixExtras))
return what;
}
// execute the process
if (!envp)
qt_safe_execv(argv[0], argv);
else
qt_safe_execve(argv[0], argv, envp);
return "execve";
}
// IMPORTANT: // IMPORTANT:
// //
// This function is called in a vfork() context on some OSes (notably, Linux // This function is called in a vfork() context on some OSes (notably, Linux
@ -605,36 +627,12 @@ void QProcessPrivate::execChild(int workingDir, char **argv, char **envp) const
// make sure this fd is closed if execv() succeeds // make sure this fd is closed if execv() succeeds
qt_safe_close(childStartedPipe[0]); qt_safe_close(childStartedPipe[0]);
// enter the working directory const char *what = doExecChild(argv, envp, workingDir, unixExtras.get());
if (workingDir != -1 && fchdir(workingDir) == -1) { strcpy(error.function, what);
// failed, stop the process
strcpy(error.function, "fchdir");
goto report_errno;
}
if (unixExtras) {
if (!callChildProcessModifier(unixExtras.get())) {
std::strcpy(error.function, "throw");
goto report_errno;
}
}
// execute the process
if (!envp) {
qt_safe_execv(argv[0], argv);
strcpy(error.function, "execvp");
} else {
#if defined (QPROCESS_DEBUG)
fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);
#endif
qt_safe_execve(argv[0], argv, envp);
strcpy(error.function, "execve");
}
// notify failure // notify failure
// don't use strerror or any other routines that may allocate memory, since // don't use strerror or any other routines that may allocate memory, since
// some buggy libc versions can deadlock on locked mutexes. // some buggy libc versions can deadlock on locked mutexes.
report_errno:
error.code = errno; error.code = errno;
qt_safe_write(childStartedPipe[1], &error, sizeof(error)); qt_safe_write(childStartedPipe[1], &error, sizeof(error));
} }
@ -1038,20 +1036,13 @@ bool QProcessPrivate::startDetached(qint64 *pid)
::_exit(1); ::_exit(1);
}; };
if (workingDirFd != -1 && fchdir(workingDirFd) == -1)
reportFailed("fchdir: ");
pid_t doubleForkPid = fork(); pid_t doubleForkPid = fork();
if (doubleForkPid == 0) { if (doubleForkPid == 0) {
// Render channels configuration. // Render channels configuration.
commitChannels(); commitChannels();
if (envp.pointers) reportFailed(doExecChild(argv.pointers.get(), envp.pointers.get(), workingDirFd,
qt_safe_execve(argv.pointers[0], argv.pointers.get(), envp.pointers.get()); unixExtras.get()));
else
qt_safe_execv(argv.pointers[0], argv.pointers.get());
reportFailed("execv: ");
} else if (doubleForkPid == -1) { } else if (doubleForkPid == -1) {
reportFailed("fork: "); reportFailed("fork: ");
} }

View File

@ -111,6 +111,7 @@ private slots:
void createProcessArgumentsModifier(); void createProcessArgumentsModifier();
#endif // Q_OS_WIN #endif // Q_OS_WIN
#if defined(Q_OS_UNIX) #if defined(Q_OS_UNIX)
void setChildProcessModifier_data();
void setChildProcessModifier(); void setChildProcessModifier();
void throwInChildProcessModifier(); void throwInChildProcessModifier();
#endif #endif
@ -1446,8 +1447,16 @@ static void childProcessModifier(int fd)
QT_CLOSE(fd); QT_CLOSE(fd);
} }
void tst_QProcess::setChildProcessModifier_data()
{
QTest::addColumn<bool>("detached");
QTest::newRow("normal") << false;
QTest::newRow("detached") << true;
}
void tst_QProcess::setChildProcessModifier() void tst_QProcess::setChildProcessModifier()
{ {
QFETCH(bool, detached);
int pipes[2] = { -1 , -1 }; int pipes[2] = { -1 , -1 };
QVERIFY(qt_safe_pipe(pipes) == 0); QVERIFY(qt_safe_pipe(pipes) == 0);
@ -1455,20 +1464,24 @@ void tst_QProcess::setChildProcessModifier()
process.setChildProcessModifier([pipes]() { process.setChildProcessModifier([pipes]() {
::childProcessModifier(pipes[1]); ::childProcessModifier(pipes[1]);
}); });
process.setProgram("testProcessNormal/testProcessNormal");
if (detached) {
process.startDetached();
} else {
process.start("testProcessNormal/testProcessNormal"); process.start("testProcessNormal/testProcessNormal");
if (process.state() != QProcess::Starting) if (process.state() != QProcess::Starting)
QCOMPARE(process.state(), QProcess::Running); QCOMPARE(process.state(), QProcess::Running);
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString()));
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
QCOMPARE(process.exitCode(), 0);
}
char buf[sizeof messageFromChildProcess] = {}; char buf[sizeof messageFromChildProcess] = {};
qt_safe_close(pipes[1]); qt_safe_close(pipes[1]);
QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(messageFromChildProcess)) - 1); QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(messageFromChildProcess)) - 1);
QCOMPARE(buf, messageFromChildProcess); QCOMPARE(buf, messageFromChildProcess);
qt_safe_close(pipes[0]); qt_safe_close(pipes[0]);
QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString()));
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
QCOMPARE(process.exitCode(), 0);
} }
void tst_QProcess::throwInChildProcessModifier() void tst_QProcess::throwInChildProcessModifier()