QProcess: allow merged channels forwarding for detached processes

[ChangeLog][QtCore][QProcess] Added support for QProcess::MergedChannels
mode with startDetached().

Change-Id: I953ad2063322015332269522a297f8e2842e438c
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
This commit is contained in:
Alex Trotsenko 2020-12-28 19:43:52 +02:00 committed by Oswald Buddenhagen
parent 1fb9c5348e
commit 04c34eb799
6 changed files with 37 additions and 38 deletions

View File

@ -616,7 +616,8 @@ void QProcessPrivate::Channel::clear()
process into the standard output channel (\c stdout). The
standard error channel (\c stderr) will not receive any data. The
standard output and standard error data of the running process
are interleaved.
are interleaved. For detached processes, the merged output of the
running process is forwarded onto the main process.
\value ForwardedChannels QProcess forwards the output of the
running process onto the main process. Anything the child process
@ -1000,10 +1001,11 @@ bool QProcessPrivate::openChannelsForDetached()
return false;
// stderr channel.
if (processChannelMode == QProcess::MergedChannels || (stderrChannel.type != Channel::Normal
if (stderrChannel.type != Channel::Normal
&& (stderrChannel.type != Channel::Redirect
|| processChannelMode == QProcess::ForwardedChannels
|| processChannelMode == QProcess::ForwardedErrorChannel))) {
|| processChannelMode == QProcess::ForwardedErrorChannel
|| processChannelMode == QProcess::MergedChannels)) {
qWarning("QProcess::openChannelsForDetached: Inconsistent stderr channel configuration");
}
if (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))
@ -2172,6 +2174,7 @@ void QProcess::startCommand(const QString &command, OpenMode mode)
\li setStandardErrorFile()
\li setStandardInputFile()
\li setStandardOutputFile()
\li setProcessChannelMode(QProcess::MergedChannels)
\li setWorkingDirectory()
\endlist
All other properties of the QProcess object are ignored.

View File

@ -319,6 +319,9 @@ public:
bool openChannels();
bool openChannelsForDetached();
bool openChannel(Channel &channel);
#if defined(Q_OS_UNIX)
void commitChannels();
#endif
void closeChannel(Channel *channel);
void closeWriteChannel();
bool tryReadFromChannel(Channel *channel); // obviously, only stdout and stderr

View File

@ -319,6 +319,24 @@ bool QProcessPrivate::openChannel(Channel &channel)
}
}
void QProcessPrivate::commitChannels()
{
// copy the stdin socket if asked to (without closing on exec)
if (stdinChannel.pipe[0] != INVALID_Q_PIPE)
qt_safe_dup2(stdinChannel.pipe[0], STDIN_FILENO, 0);
// copy the stdout and stderr if asked to
if (stdoutChannel.pipe[1] != INVALID_Q_PIPE)
qt_safe_dup2(stdoutChannel.pipe[1], STDOUT_FILENO, 0);
if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
qt_safe_dup2(stderrChannel.pipe[1], STDERR_FILENO, 0);
} else {
// merge stdout and stderr if asked to
if (processChannelMode == QProcess::MergedChannels)
qt_safe_dup2(STDOUT_FILENO, STDERR_FILENO, 0);
}
}
static char **_q_dupEnvironment(const QProcessEnvironmentPrivate::Map &environment, int *envc)
{
*envc = 0;
@ -525,20 +543,8 @@ void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp
ChildError error = { 0, {} }; // force zeroing of function[8]
// copy the stdin socket if asked to (without closing on exec)
if (stdinChannel.pipe[0] != INVALID_Q_PIPE)
qt_safe_dup2(stdinChannel.pipe[0], STDIN_FILENO, 0);
// copy the stdout and stderr if asked to
if (stdoutChannel.pipe[1] != INVALID_Q_PIPE)
qt_safe_dup2(stdoutChannel.pipe[1], STDOUT_FILENO, 0);
if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
qt_safe_dup2(stderrChannel.pipe[1], STDERR_FILENO, 0);
} else {
// merge stdout and stderr if asked to
if (processChannelMode == QProcess::MergedChannels)
qt_safe_dup2(STDOUT_FILENO, STDERR_FILENO, 0);
}
// Render channels configuration.
commitChannels();
// make sure this fd is closed if execv() succeeds
qt_safe_close(childStartedPipe[0]);
@ -901,15 +907,8 @@ bool QProcessPrivate::startDetached(qint64 *pid)
if (doubleForkPid == 0) {
qt_safe_close(pidPipe[1]);
// copy the stdin socket if asked to (without closing on exec)
if (stdinChannel.type == Channel::Redirect)
qt_safe_dup2(stdinChannel.pipe[0], STDIN_FILENO, 0);
// copy the stdout and stderr if asked to
if (stdoutChannel.type == Channel::Redirect)
qt_safe_dup2(stdoutChannel.pipe[1], STDOUT_FILENO, 0);
if (stderrChannel.type == Channel::Redirect)
qt_safe_dup2(stderrChannel.pipe[1], STDERR_FILENO, 0);
// Render channels configuration.
commitChannels();
if (!encodedWorkingDirectory.isEmpty()) {
if (QT_CHDIR(encodedWorkingDirectory.constData()) == -1)

View File

@ -914,17 +914,7 @@ bool QProcessPrivate::startDetached(qint64 *pid)
DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
0, 0, 0,
STARTF_USESTDHANDLES,
0, 0, 0,
pipeOrStdHandle(stdinChannel.pipe[0], STD_INPUT_HANDLE),
pipeOrStdHandle(stdoutChannel.pipe[1], STD_OUTPUT_HANDLE),
pipeOrStdHandle(stderrChannel.pipe[1], STD_ERROR_HANDLE)
};
STARTUPINFOW startupInfo = createStartupInfo();
QProcess::CreateProcessArguments cpargs = {
nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
nullptr, nullptr, true, dwCreationFlags, envPtr,

View File

@ -36,8 +36,8 @@ int main(int argc, char *argv[])
return 1;
}
fputs("out data", stdout);
fputs("err data", stderr);
fflush(stdout);
fputs("err data", stderr);
fflush(stderr);
std::ofstream out(argv[1]);
out << "That's all folks!";

View File

@ -1125,6 +1125,10 @@ void tst_QProcess::forwardedChannels_data()
<< true
<< int(QProcess::SeparateChannels) << int(QProcess::ManagedInputChannel)
<< QByteArray("out data") << QByteArray("err data");
QTest::newRow("detached-merged-forwarding")
<< true
<< int(QProcess::MergedChannels) << int(QProcess::ManagedInputChannel)
<< QByteArray("out data" "err data") << QByteArray();
}
void tst_QProcess::forwardedChannels()