QProcess/Unix: capture the child process modifier exception's what()

Change-Id: I5f7f427ded124479baa6fffd175ffb017b6cd13c
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Thiago Macieira 2023-05-17 09:49:34 -07:00
parent 90bc0ad41f
commit 921bf4a11a
2 changed files with 19 additions and 17 deletions

View File

@ -641,13 +641,7 @@ void QProcessPrivate::startProcess()
// we need an errno number to use to indicate the child process modifier threw, // we need an errno number to use to indicate the child process modifier threw,
// something the regular operations shouldn't set. // something the regular operations shouldn't set.
static constexpr int FakeErrnoForThrow = static constexpr int FakeErrnoForThrow = std::numeric_limits<int>::max();
#ifdef ECANCELED
ECANCELED
#else
ESHUTDOWN
#endif
;
static QString startFailureErrorMessage(ChildError &err, [[maybe_unused]] ssize_t bytesRead) static QString startFailureErrorMessage(ChildError &err, [[maybe_unused]] ssize_t bytesRead)
{ {
@ -658,7 +652,8 @@ static QString startFailureErrorMessage(ChildError &err, [[maybe_unused]] ssize_
qsizetype len = qstrnlen(err.function, sizeof(err.function)); qsizetype len = qstrnlen(err.function, sizeof(err.function));
QString complement = QString::fromUtf8(err.function, len); QString complement = QString::fromUtf8(err.function, len);
if (err.code == FakeErrnoForThrow) if (err.code == FakeErrnoForThrow)
return QProcess::tr("Child process modifier threw an exception"); return QProcess::tr("Child process modifier threw an exception: %1")
.arg(std::move(complement));
if (err.code == 0) if (err.code == 0)
return QProcess::tr("Child process modifier reported error: %1") return QProcess::tr("Child process modifier reported error: %1")
.arg(std::move(complement)); .arg(std::move(complement));
@ -732,16 +727,16 @@ static void applyProcessParameters(const QProcess::UnixProcessParameters &params
} }
// the noexcept here adds an extra layer of protection // the noexcept here adds an extra layer of protection
static const char *callChildProcessModifier(const QProcessPrivate::UnixExtras *unixExtras) noexcept static void callChildProcessModifier(const QProcessPrivate *d) noexcept
{ {
QT_TRY { QT_TRY {
if (unixExtras->childProcessModifier) if (d->unixExtras->childProcessModifier)
unixExtras->childProcessModifier(); d->unixExtras->childProcessModifier();
} QT_CATCH (std::exception &e) {
failChildProcess(d, e.what(), FakeErrnoForThrow);
} QT_CATCH (...) { } QT_CATCH (...) {
errno = FakeErrnoForThrow; failChildProcess(d, "throw", FakeErrnoForThrow);
return "throw";
} }
return nullptr;
} }
Q_NORETURN static void Q_NORETURN static void
@ -754,8 +749,7 @@ doExecChild(char **argv, char **envp, int workingDirFd, const QProcessPrivate *d
if (d->unixExtras) { if (d->unixExtras) {
// FIRST we call the user modifier function, before we dropping // FIRST we call the user modifier function, before we dropping
// privileges or closing non-standard file descriptors // privileges or closing non-standard file descriptors
if (const char *what = callChildProcessModifier(d->unixExtras.get())) callChildProcessModifier(d);
failChildProcess(d, what, FakeErrnoForThrow);
// then we apply our other user-provided parameters // then we apply our other user-provided parameters
applyProcessParameters(d->unixExtras->processParameters); applyProcessParameters(d->unixExtras->processParameters);

View File

@ -1560,9 +1560,13 @@ void tst_QProcess::throwInChildProcessModifier()
#ifndef __cpp_exceptions #ifndef __cpp_exceptions
Q_SKIP("Exceptions disabled."); Q_SKIP("Exceptions disabled.");
#else #else
static constexpr char What[] = "tst_QProcess::throwInChildProcessModifier()::MyException";
struct MyException : std::exception {
const char *what() const noexcept override { return What; }
};
QProcess process; QProcess process;
process.setChildProcessModifier([]() { process.setChildProcessModifier([]() {
throw 42; throw MyException();
}); });
process.setProgram("testProcessNormal/testProcessNormal"); process.setProgram("testProcessNormal/testProcessNormal");
@ -1572,6 +1576,8 @@ void tst_QProcess::throwInChildProcessModifier()
QCOMPARE(process.error(), QProcess::FailedToStart); QCOMPARE(process.error(), QProcess::FailedToStart);
QVERIFY2(process.errorString().contains("Child process modifier threw an exception"), QVERIFY2(process.errorString().contains("Child process modifier threw an exception"),
qPrintable(process.errorString())); qPrintable(process.errorString()));
QVERIFY2(process.errorString().contains(What),
qPrintable(process.errorString()));
// try again, to ensure QProcess internal state wasn't corrupted // try again, to ensure QProcess internal state wasn't corrupted
process.start(); process.start();
@ -1580,6 +1586,8 @@ void tst_QProcess::throwInChildProcessModifier()
QCOMPARE(process.error(), QProcess::FailedToStart); QCOMPARE(process.error(), QProcess::FailedToStart);
QVERIFY2(process.errorString().contains("Child process modifier threw an exception"), QVERIFY2(process.errorString().contains("Child process modifier threw an exception"),
qPrintable(process.errorString())); qPrintable(process.errorString()));
QVERIFY2(process.errorString().contains(What),
qPrintable(process.errorString()));
#endif #endif
} }