QProcess: report the syscall that failed in the Unix child process

Prefix the error string message with either "chdir" or the "execvXX"-
family function that failed. In order to simplify the process, I also
made it transmit local 8-bit data instead of UTF-16 (this also avoids
memory allocation with QString).

Since there are now two write(2) calls, it's possible for the parent
process to be woken up and read(2) only the first. The parent process
now needs to wait for EOF.

Change-Id: Ib306f8f647014b399b87ffff13f1956199a5aee0
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
Thiago Macieira 2015-07-16 17:46:42 -07:00
parent 16cd053331
commit 6d78b7a0c4
3 changed files with 27 additions and 7 deletions

View File

@ -1194,7 +1194,7 @@ bool QProcessPrivate::_q_startupNotification()
}
q->setProcessState(QProcess::NotRunning);
setErrorAndEmit(QProcess::FailedToStart);
setErrorAndEmit(QProcess::FailedToStart, errorString); // the error string was already set
#ifdef Q_OS_UNIX
// make sure the process manager removes this entry
waitForDeadChild();

View File

@ -672,6 +672,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
qt_safe_close(childStartedPipe[0]);
// enter the working directory
const char *callthatfailed = "chdir: ";
if (workingDir && QT_CHDIR(workingDir) == -1) {
// failed, stop the process
goto report_errno;
@ -683,6 +684,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
// execute the process
if (!envp) {
qt_safe_execvp(argv[0], argv);
callthatfailed = "execvp: ";
} else {
if (path) {
char **arg = path;
@ -700,15 +702,19 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
#endif
qt_safe_execve(argv[0], argv, envp);
}
callthatfailed = "execve: ";
}
// notify failure
// we're running in the child process, so we don't need to be thread-safe;
// we can use strerror
report_errno:
QString error = qt_error_string(errno);
const char *msg = strerror(errno);
#if defined (QPROCESS_DEBUG)
fprintf(stderr, "QProcessPrivate::execChild() failed (%s), notifying parent process\n", qPrintable(error));
fprintf(stderr, "QProcessPrivate::execChild() failed (%s), notifying parent process\n", msg);
#endif
qt_safe_write(childStartedPipe[1], error.data(), error.length() * sizeof(QChar));
qt_safe_write(childStartedPipe[1], callthatfailed, strlen(callthatfailed));
qt_safe_write(childStartedPipe[1], msg, strlen(msg));
qt_safe_close(childStartedPipe[1]);
childStartedPipe[1] = -1;
}
@ -716,8 +722,15 @@ report_errno:
bool QProcessPrivate::processStarted()
{
ushort buf[errorBufferMax];
int i = qt_safe_read(childStartedPipe[0], &buf, sizeof buf);
char buf[errorBufferMax];
int i = 0;
int ret;
do {
ret = qt_safe_read(childStartedPipe[0], buf + i, sizeof buf - i);
if (ret > 0)
i += ret;
} while (ret > 0 && i < int(sizeof buf));
if (startupSocketNotifier) {
startupSocketNotifier->setEnabled(false);
startupSocketNotifier->deleteLater();
@ -732,7 +745,7 @@ bool QProcessPrivate::processStarted()
// did we read an error message?
if (i > 0)
q_func()->setErrorString(QString((const QChar *)buf, i / sizeof(QChar)));
q_func()->setErrorString(QString::fromLocal8Bit(buf, i));
return i <= 0;
}

View File

@ -2285,6 +2285,13 @@ void tst_QProcess::setNonExistentWorkingDirectory()
#endif
QCOMPARE(int(process->error()), int(QProcess::FailedToStart));
#ifdef Q_OS_UNIX
# ifdef QPROCESS_USE_SPAWN
QEXPECT_FAIL("", "QProcess cannot detect failure to start when using posix_spawn()", Continue);
# endif
QVERIFY2(process->errorString().startsWith("chdir:"), process->errorString().toLocal8Bit());
#endif
delete process;
process = 0;
}