QProcess/Unix: update the start error messages to be all similar

The user doesn't care if it happened in the parent process or in the
child.

Windows says "Process failed to start: %1" which is a bit nicer, but I'm
not going to update to that.

Change-Id: If974ba44f84ffc236e48fffd4e48457fac21b248
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
This commit is contained in:
Thiago Macieira 2025-01-29 18:05:37 -08:00
parent 675d6fd558
commit 030c93add8
2 changed files with 38 additions and 32 deletions

View File

@ -189,6 +189,36 @@ static_assert(std::is_trivial_v<ChildError>);
static_assert(PIPE_BUF >= sizeof(ChildError)); // PIPE_BUF may be bigger
#endif
// we need an errno value to use to indicate the child process modifier threw,
// something the regular operations shouldn't set.
static constexpr int FakeErrnoForThrow = std::numeric_limits<int>::max();
static QString errorMessageForSyscall(QUtf8StringView fnName, int errnoCode = -1)
{
QString msg = qt_error_string(errnoCode);
return QProcess::tr("Child process set up failed: %1: %2").arg(fnName, std::move(msg));
}
static QString startFailureErrorMessage(ChildError &err, [[maybe_unused]] ssize_t bytesRead)
{
// ChildError is less than the POSIX pipe buffer atomic size, so the read
// must not have been truncated
Q_ASSERT(bytesRead == sizeof(err));
qsizetype len = qstrnlen(err.function, sizeof(err.function));
const QUtf8StringView complement(err.function, len);
if (err.code == FakeErrnoForThrow)
return QProcess::tr("Child process modifier threw an exception: %1")
.arg(complement);
if (err.code == 0)
return QProcess::tr("Child process modifier reported error: %1")
.arg(complement);
if (err.code < 0)
return QProcess::tr("Child process modifier reported error: %1: %2")
.arg(complement, qt_error_string(-err.code));
return errorMessageForSyscall(complement, err.code);
}
struct QProcessPoller
{
QProcessPoller(const QProcessPrivate &proc);
@ -262,7 +292,7 @@ struct QChildProcess
if (!d->workingDirectory.isEmpty()) {
workingDirectory = opendirfd(QFile::encodeName(d->workingDirectory));
if (workingDirectory < 0) {
d->setErrorAndEmit(QProcess::FailedToStart, "chdir: "_L1 + qt_error_string());
d->setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("chdir"));
d->cleanup();
// make sure our destructor does nothing
@ -491,7 +521,7 @@ bool QProcessPrivate::openChannel(Channel &channel)
if (channel.type == Channel::Normal) {
// we're piping this channel to our own process
if (qt_create_pipe(channel.pipe) != 0) {
setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
return false;
}
@ -573,7 +603,7 @@ bool QProcessPrivate::openChannel(Channel &channel)
Q_PIPE pipe[2] = { -1, -1 };
if (qt_create_pipe(pipe) != 0) {
setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
return false;
}
sink->pipe[0] = pipe[0];
@ -708,7 +738,7 @@ void QProcessPrivate::startProcess()
return;
}
if (qt_create_pipe(childStartedPipe) != 0) {
setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
cleanup();
return;
}
@ -778,31 +808,6 @@ void QProcessPrivate::startProcess()
::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
}
// we need an errno number to use to indicate the child process modifier threw,
// something the regular operations shouldn't set.
static constexpr int FakeErrnoForThrow = std::numeric_limits<int>::max();
static QString startFailureErrorMessage(ChildError &err, [[maybe_unused]] ssize_t bytesRead)
{
// ChildError is less than the POSIX pipe buffer atomic size, so the read
// must not have been truncated
Q_ASSERT(bytesRead == sizeof(err));
qsizetype len = qstrnlen(err.function, sizeof(err.function));
const QUtf8StringView complement(err.function, len);
if (err.code == FakeErrnoForThrow)
return QProcess::tr("Child process modifier threw an exception: %1")
.arg(complement);
if (err.code == 0)
return QProcess::tr("Child process modifier reported error: %1")
.arg(complement);
if (err.code < 0)
return QProcess::tr("Child process modifier reported error: %1: %2")
.arg(complement, qt_error_string(-err.code));
return QProcess::tr("Child process set up failed: %1: %2")
.arg(complement, qt_error_string(err.code));
}
Q_NORETURN void
failChildProcess(const QProcessPrivate *d, const char *description, int code) noexcept
{
@ -1320,7 +1325,7 @@ bool QProcessPrivate::startDetached(qint64 *pid)
{
AutoPipe startedPipe, pidPipe;
if (!startedPipe || !pidPipe) {
setErrorAndEmit(QProcess::FailedToStart, "pipe: "_L1 + qt_error_string(errno));
setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
return false;
}
@ -1359,7 +1364,7 @@ bool QProcessPrivate::startDetached(qint64 *pid)
if (childPid == -1) {
childProcess.cleanup();
setErrorAndEmit(QProcess::FailedToStart, "fork: "_L1 + qt_error_string(savedErrno));
setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("fork", savedErrno));
return false;
}

View File

@ -2884,7 +2884,8 @@ void tst_QProcess::detachedSetNonExistentWorkingDirectory()
QCOMPARE(process.error(), QProcess::FailedToStart);
#ifdef Q_OS_UNIX
QVERIFY2(process.errorString().startsWith("chdir:"), process.errorString().toLocal8Bit());
QVERIFY2(process.errorString().contains(": chdir: "), process.errorString().toLocal8Bit());
QVERIFY2(process.errorString().contains(qt_error_string(ENOENT)), process.errorString().toLocal8Bit());
#endif
}