add QProcess::InputChannelMode

this enables forwarding standard input from the parent process.

Change-Id: I7ee72b9842acc96320d4da693b95dd15d9a7b4d4
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Oliver Wolff <oliver.wolff@digia.com>
This commit is contained in:
Oswald Buddenhagen 2013-09-03 22:06:26 +02:00 committed by The Qt Project
parent fba0a30791
commit 50a8a5e795
7 changed files with 94 additions and 17 deletions

View File

@ -608,8 +608,8 @@ void QProcessPrivate::Channel::clear()
/*!
\enum QProcess::ProcessChannelMode
This enum describes the process channel modes of QProcess. Pass
one of these values to setProcessChannelMode() to set the
This enum describes the process output channel modes of QProcess.
Pass one of these values to setProcessChannelMode() to set the
current read channel mode.
\value SeparateChannels QProcess manages the output of the
@ -651,6 +651,26 @@ void QProcessPrivate::Channel::clear()
\sa setProcessChannelMode()
*/
/*!
\enum QProcess::InputChannelMode
\since 5.2
This enum describes the process input channel modes of QProcess.
Pass one of these values to setInputChannelMode() to set the
current write channel mode.
\value ManagedInputChannel QProcess manages the input of the running
process. This is the default input channel mode of QProcess.
\value ForwardedInputChannel QProcess forwards the input of the main
process onto the running process. The child process reads its standard
input from the same source as the main process.
Note that the main process must not try to read its standard input
while the child process is running.
\sa setInputChannelMode()
*/
/*!
\enum QProcess::ProcessError
@ -779,6 +799,7 @@ QProcessPrivate::QProcessPrivate()
{
processChannel = QProcess::StandardOutput;
processChannelMode = QProcess::SeparateChannels;
inputChannelMode = QProcess::ManagedInputChannel;
processError = QProcess::UnknownError;
processState = QProcess::NotRunning;
pid = 0;
@ -1243,6 +1264,34 @@ void QProcess::setProcessChannelMode(ProcessChannelMode mode)
d->processChannelMode = mode;
}
/*!
\since 5.2
Returns the channel mode of the QProcess standard input channel.
\sa setInputChannelMode(), InputChannelMode
*/
QProcess::InputChannelMode QProcess::inputChannelMode() const
{
Q_D(const QProcess);
return d->inputChannelMode;
}
/*!
\since 5.2
Sets the channel mode of the QProcess standard intput
channel to the \a mode specified.
This mode will be used the next time start() is called.
\sa inputChannelMode(), InputChannelMode
*/
void QProcess::setInputChannelMode(InputChannelMode mode)
{
Q_D(QProcess);
d->inputChannelMode = mode;
}
/*!
Returns the current read channel of the QProcess.

View File

@ -128,6 +128,10 @@ public:
ForwardedOutputChannel,
ForwardedErrorChannel
};
enum InputChannelMode {
ManagedInputChannel,
ForwardedInputChannel
};
enum ExitStatus {
NormalExit,
CrashExit
@ -151,6 +155,8 @@ public:
void setReadChannelMode(ProcessChannelMode mode);
ProcessChannelMode processChannelMode() const;
void setProcessChannelMode(ProcessChannelMode mode);
InputChannelMode inputChannelMode() const;
void setInputChannelMode(InputChannelMode mode);
ProcessChannel readChannel() const;
void setReadChannel(ProcessChannel channel);

View File

@ -302,6 +302,7 @@ public:
QProcess::ProcessChannel processChannel;
QProcess::ProcessChannelMode processChannelMode;
QProcess::InputChannelMode inputChannelMode;
QProcess::ProcessError processError;
QProcess::ProcessState processState;
QString workingDirectory;

View File

@ -807,29 +807,29 @@ pid_t QProcessPrivate::spawnChild(const char *workingDir, char **argv, char **en
? i : SPAWN_FDCLOSED;
}
if (inputChannelMode == QProcess::ManagedInputChannel)
fd_map[0] = stdinChannel.pipe[0];
else
fd_map[0] = QT_FILENO(stdin);
switch (processChannelMode) {
case QProcess::ForwardedChannels:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = QT_FILENO(stdout);
fd_map[2] = QT_FILENO(stderr);
break;
case QProcess::ForwardedOutputChannel:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = QT_FILENO(stdout);
fd_map[2] = stderrChannel.pipe[1];
break;
case QProcess::ForwardedErrorChannel:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1];
fd_map[2] = QT_FILENO(stderr);
break;
case QProcess::MergedChannels:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1];
fd_map[2] = stdoutChannel.pipe[1];
break;
case QProcess::SeparateChannels:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1];
fd_map[2] = stderrChannel.pipe[1];
break;
@ -855,8 +855,9 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
Q_Q(QProcess);
// copy the stdin socket (without closing on exec)
qt_safe_dup2(stdinChannel.pipe[0], fileno(stdin), 0);
// copy the stdin socket if asked to (without closing on exec)
if (inputChannelMode != QProcess::ForwardedInputChannel)
qt_safe_dup2(stdinChannel.pipe[0], fileno(stdin), 0);
// copy the stdout and stderr if asked to
if (processChannelMode != QProcess::ForwardedChannels) {

View File

@ -156,7 +156,15 @@ bool QProcessPrivate::createChannel(Channel &channel)
if (channel.type == Channel::Normal) {
// we're piping this channel to our own process
if (&channel == &stdinChannel) {
qt_create_pipe(channel.pipe, true);
if (inputChannelMode != QProcess::ForwardedInputChannel) {
qt_create_pipe(channel.pipe, true);
} else {
channel.pipe[1] = INVALID_Q_PIPE;
HANDLE hStdReadChannel = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hCurrentProcess = GetCurrentProcess();
DuplicateHandle(hCurrentProcess, hStdReadChannel, hCurrentProcess,
&channel.pipe[0], 0, TRUE, DUPLICATE_SAME_ACCESS);
}
} else {
QWindowsPipeReader *pipeReader = 0;
if (&channel == &stdoutChannel) {

View File

@ -48,7 +48,7 @@ int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if (argc < 2)
if (argc < 3)
return 13;
#ifndef QT_NO_PROCESS
@ -59,12 +59,17 @@ int main(int argc, char **argv)
if (process.processChannelMode() != mode)
return 1;
QProcess::InputChannelMode inmode = (QProcess::InputChannelMode)atoi(argv[2]);
process.setInputChannelMode(inmode);
if (process.inputChannelMode() != inmode)
return 11;
process.start("testProcessEcho2/testProcessEcho2");
if (!process.waitForStarted(5000))
return 2;
if (process.write("forwarded") != 9)
if (inmode == QProcess::ManagedInputChannel && process.write("forwarded") != 9)
return 3;
process.closeWriteChannel();

View File

@ -1094,33 +1094,40 @@ void tst_QProcess::mergedChannels()
void tst_QProcess::forwardedChannels_data()
{
QTest::addColumn<int>("mode");
QTest::addColumn<int>("inmode");
QTest::addColumn<QByteArray>("outdata");
QTest::addColumn<QByteArray>("errdata");
QTest::newRow("separate") << int(QProcess::SeparateChannels)
QTest::newRow("separate") << int(QProcess::SeparateChannels) << int(QProcess::ManagedInputChannel)
<< QByteArray() << QByteArray();
QTest::newRow("forwarded") << int(QProcess::ForwardedChannels)
QTest::newRow("forwarded") << int(QProcess::ForwardedChannels) << int(QProcess::ManagedInputChannel)
<< QByteArray("forwarded") << QByteArray("forwarded");
QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel)
QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel) << int(QProcess::ManagedInputChannel)
<< QByteArray("forwarded") << QByteArray();
QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel)
QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ManagedInputChannel)
<< QByteArray() << QByteArray("forwarded");
QTest::newRow("fwdinput") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ForwardedInputChannel)
<< QByteArray() << QByteArray("input");
}
void tst_QProcess::forwardedChannels()
{
QFETCH(int, mode);
QFETCH(int, inmode);
QFETCH(QByteArray, outdata);
QFETCH(QByteArray, errdata);
QProcess process;
process.start("testForwarding/testForwarding", QStringList() << QString::number(mode));
process.start("testForwarding/testForwarding", QStringList() << QString::number(mode) << QString::number(inmode));
QVERIFY(process.waitForStarted(5000));
QCOMPARE(process.write("input"), 5);
process.closeWriteChannel();
QVERIFY(process.waitForFinished(5000));
const char *err;
switch (process.exitCode()) {
case 0: err = "ok"; break;
case 1: err = "processChannelMode is wrong"; break;
case 11: err = "inputChannelMode is wrong"; break;
case 2: err = "failed to start"; break;
case 3: err = "failed to write"; break;
case 4: err = "did not finish"; break;