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:
parent
fb40737b0d
commit
48b6c8503a
@ -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: ");
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user