QProcess::startDetached/Unix: report which function failed
Like QProcess::start(). Change-Id: Ic90d8429a0eb4837971dfffd1664ef1293a6523d Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de> (cherry picked from commit 5d977b0fd2fa4611571e50378647c09febd49141)
This commit is contained in:
parent
4ffc03d9fd
commit
0ea39a7c42
@ -168,6 +168,12 @@ struct AutoPipe
|
|||||||
int operator[](int idx) const { return pipe[idx]; }
|
int operator[](int idx) const { return pipe[idx]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ChildError
|
||||||
|
{
|
||||||
|
qint64 code;
|
||||||
|
char function[8];
|
||||||
|
};
|
||||||
|
|
||||||
struct QProcessPoller
|
struct QProcessPoller
|
||||||
{
|
{
|
||||||
QProcessPoller(const QProcessPrivate &proc);
|
QProcessPoller(const QProcessPrivate &proc);
|
||||||
@ -552,12 +558,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChildError
|
|
||||||
{
|
|
||||||
int code;
|
|
||||||
char function[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp)
|
void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp)
|
||||||
{
|
{
|
||||||
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
|
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
|
||||||
@ -902,7 +902,9 @@ bool QProcessPrivate::startDetached(qint64 *pid)
|
|||||||
{
|
{
|
||||||
QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory);
|
QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory);
|
||||||
|
|
||||||
// To catch the startup of the child and communicate its pid
|
static_assert(PIPE_BUF >= sizeof(ChildError));
|
||||||
|
ChildError childStatus = { 0, {} };
|
||||||
|
|
||||||
AutoPipe startedPipe, pidPipe;
|
AutoPipe startedPipe, pidPipe;
|
||||||
if (!startedPipe || !pidPipe) {
|
if (!startedPipe || !pidPipe) {
|
||||||
setErrorAndEmit(QProcess::FailedToStart, QLatin1String("pipe: ") + qt_error_string(errno));
|
setErrorAndEmit(QProcess::FailedToStart, QLatin1String("pipe: ") + qt_error_string(errno));
|
||||||
@ -925,15 +927,20 @@ bool QProcessPrivate::startDetached(qint64 *pid)
|
|||||||
qt_safe_close(startedPipe[0]);
|
qt_safe_close(startedPipe[0]);
|
||||||
qt_safe_close(pidPipe[0]);
|
qt_safe_close(pidPipe[0]);
|
||||||
|
|
||||||
pid_t doubleForkPid = 0;
|
auto reportFailed = [&](const char *function) {
|
||||||
if (!encodedWorkingDirectory.isEmpty())
|
childStatus.code = errno;
|
||||||
doubleForkPid = QT_CHDIR(encodedWorkingDirectory.constData());
|
strcpy(childStatus.function, function);
|
||||||
|
qt_safe_write(startedPipe[1], &childStatus, sizeof(childStatus));
|
||||||
|
::_exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
if (doubleForkPid == 0)
|
if (!encodedWorkingDirectory.isEmpty()) {
|
||||||
doubleForkPid = fork();
|
if (QT_CHDIR(encodedWorkingDirectory.constData()) < 0)
|
||||||
|
reportFailed("chdir: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t doubleForkPid = fork();
|
||||||
if (doubleForkPid == 0) {
|
if (doubleForkPid == 0) {
|
||||||
qt_safe_close(pidPipe[1]);
|
|
||||||
|
|
||||||
// Render channels configuration.
|
// Render channels configuration.
|
||||||
commitChannels();
|
commitChannels();
|
||||||
|
|
||||||
@ -964,19 +971,13 @@ bool QProcessPrivate::startDetached(qint64 *pid)
|
|||||||
else
|
else
|
||||||
qt_safe_execv(argv[0], argv);
|
qt_safe_execv(argv[0], argv);
|
||||||
|
|
||||||
// '\1' means execv failed
|
reportFailed("execv: ");
|
||||||
char c = '\1';
|
|
||||||
qt_safe_write(startedPipe[1], &c, 1);
|
|
||||||
qt_safe_close(startedPipe[1]);
|
|
||||||
::_exit(1);
|
|
||||||
} else if (doubleForkPid == -1) {
|
} else if (doubleForkPid == -1) {
|
||||||
// '\2' means internal error
|
reportFailed("fork: ");
|
||||||
char c = '\2';
|
|
||||||
qt_safe_write(startedPipe[1], &c, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qt_safe_close(startedPipe[1]);
|
// success
|
||||||
qt_safe_write(pidPipe[1], (const char *)&doubleForkPid, sizeof(pid_t));
|
qt_safe_write(pidPipe[1], &doubleForkPid, sizeof(pid_t));
|
||||||
::_exit(1);
|
::_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,24 +991,35 @@ bool QProcessPrivate::startDetached(qint64 *pid)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close the writing ends of the pipes so we can properly get EOFs
|
||||||
|
qt_safe_close(pidPipe[1]);
|
||||||
qt_safe_close(startedPipe[1]);
|
qt_safe_close(startedPipe[1]);
|
||||||
startedPipe[1] = -1;
|
pidPipe[1] = startedPipe[1] = -1;
|
||||||
|
|
||||||
char reply = '\0';
|
// This read() will block until we're cleared to proceed. If it returns 0
|
||||||
int startResult = qt_safe_read(startedPipe[0], &reply, 1);
|
// (EOF), it means the direct child has exited and the grandchild
|
||||||
|
// successfully execve()'d the target process. If it returns any positive
|
||||||
|
// result, it means one of the two children wrote an error result. Negative
|
||||||
|
// values should not happen.
|
||||||
|
ssize_t startResult = qt_safe_read(startedPipe[0], &childStatus, sizeof(childStatus));
|
||||||
|
|
||||||
|
// reap the intermediate child
|
||||||
int result;
|
int result;
|
||||||
qt_safe_waitpid(childPid, &result, 0);
|
qt_safe_waitpid(childPid, &result, 0);
|
||||||
|
|
||||||
bool success = (startResult != -1 && reply == '\0');
|
bool success = (startResult == 0); // nothing written -> no error
|
||||||
if (success && pid) {
|
if (success && pid) {
|
||||||
pid_t actualPid;
|
pid_t actualPid;
|
||||||
if (qt_safe_read(pidPipe[0], &actualPid, sizeof(pid_t)) != sizeof(pid_t))
|
if (qt_safe_read(pidPipe[0], &actualPid, sizeof(pid_t)) != sizeof(pid_t))
|
||||||
actualPid = 0;
|
actualPid = 0; // this shouldn't happen!
|
||||||
*pid = actualPid;
|
*pid = actualPid;
|
||||||
} else if (!success) {
|
} else if (!success) {
|
||||||
if (pid)
|
if (pid)
|
||||||
*pid = -1;
|
*pid = -1;
|
||||||
setErrorAndEmit(QProcess::FailedToStart);
|
QString msg;
|
||||||
|
if (startResult == sizeof(childStatus))
|
||||||
|
msg = QLatin1String(childStatus.function) + qt_error_string(childStatus.code);
|
||||||
|
setErrorAndEmit(QProcess::FailedToStart, msg);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -2281,6 +2281,10 @@ void tst_QProcess::detachedSetNonExistentWorkingDirectory()
|
|||||||
QCOMPARE(pid, -1);
|
QCOMPARE(pid, -1);
|
||||||
QCOMPARE(process.error(), QProcess::FailedToStart);
|
QCOMPARE(process.error(), QProcess::FailedToStart);
|
||||||
QVERIFY(process.errorString() != "Unknown error");
|
QVERIFY(process.errorString() != "Unknown error");
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
QVERIFY2(process.errorString().startsWith("chdir:"), process.errorString().toLocal8Bit());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QProcess::startFinishStartFinish()
|
void tst_QProcess::startFinishStartFinish()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user