From 57c0979012c43f329e61547c638d7935a1444439 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 29 Nov 2011 10:18:10 +0100 Subject: SSH: Support different read channels in SshRemoteProcess. This is part of the effort to support more QProcess concepts. Change-Id: Idb888e733570a58d3810f371409b657b30bbd929 Reviewed-by: hjk --- src/libs/utils/ssh/sshremoteprocess.cpp | 47 ++++++++++--- src/libs/utils/ssh/sshremoteprocess.h | 4 ++ src/libs/utils/ssh/sshremoteprocess_p.h | 6 ++ .../manual/ssh/remoteprocess/remoteprocesstest.cpp | 80 ++++++++++++++++++---- tests/manual/ssh/remoteprocess/remoteprocesstest.h | 7 +- 5 files changed, 121 insertions(+), 23 deletions(-) diff --git a/src/libs/utils/ssh/sshremoteprocess.cpp b/src/libs/utils/ssh/sshremoteprocess.cpp index 52068ba869..875325a158 100644 --- a/src/libs/utils/ssh/sshremoteprocess.cpp +++ b/src/libs/utils/ssh/sshremoteprocess.cpp @@ -96,28 +96,35 @@ SshRemoteProcess::~SshRemoteProcess() bool SshRemoteProcess::atEnd() const { - return QIODevice::atEnd() && d->m_stdout.isEmpty(); + return QIODevice::atEnd() && d->data().isEmpty(); } qint64 SshRemoteProcess::bytesAvailable() const { - return QIODevice::bytesAvailable() + d->m_stdout.count(); + return QIODevice::bytesAvailable() + d->data().count(); } bool SshRemoteProcess::canReadLine() const { - return QIODevice::canReadLine() || d->m_stdout.contains('\n'); // TODO: Not cross-platform? + return QIODevice::canReadLine() || d->data().contains('\n'); } QByteArray SshRemoteProcess::readAllStandardOutput() { - return readAll(); + return readAllFromChannel(QProcess::StandardOutput); } QByteArray SshRemoteProcess::readAllStandardError() { - const QByteArray data = d->m_stderr; - d->m_stderr.clear(); + return readAllFromChannel(QProcess::StandardError); +} + +QByteArray SshRemoteProcess::readAllFromChannel(QProcess::ProcessChannel channel) +{ + const QProcess::ProcessChannel currentReadChannel = readChannel(); + setReadChannel(channel); + const QByteArray &data = readAll(); + setReadChannel(currentReadChannel); return data; } @@ -129,9 +136,9 @@ void SshRemoteProcess::close() qint64 SshRemoteProcess::readData(char *data, qint64 maxlen) { - const qint64 bytesRead = qMin(qint64(d->m_stdout.count()), maxlen); - memcpy(data, d->m_stdout.constData(), bytesRead); - d->m_stdout.remove(0, bytesRead); + const qint64 bytesRead = qMin(qint64(d->data().count()), maxlen); + memcpy(data, d->data().constData(), bytesRead); + d->data().remove(0, bytesRead); return bytesRead; } @@ -144,13 +151,23 @@ qint64 SshRemoteProcess::writeData(const char *data, qint64 len) return 0; } +QProcess::ProcessChannel SshRemoteProcess::readChannel() const +{ + return d->m_readChannel; +} + +void SshRemoteProcess::setReadChannel(QProcess::ProcessChannel channel) +{ + d->m_readChannel = channel; +} + void SshRemoteProcess::init() { connect(d, SIGNAL(started()), this, SIGNAL(started()), Qt::QueuedConnection); connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyReadStandardOutput()), Qt::QueuedConnection); - connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyRead()), Qt::QueuedConnection); + connect(d, SIGNAL(readyRead()), this, SIGNAL(readyRead()), Qt::QueuedConnection); connect(d, SIGNAL(readyReadStandardError()), this, SIGNAL(readyReadStandardError()), Qt::QueuedConnection); connect(d, SIGNAL(closed(int)), this, SIGNAL(closed(int)), Qt::QueuedConnection); @@ -228,6 +245,7 @@ void SshRemoteProcessPrivate::init() m_procState = NotYetStarted; m_wasRunning = false; m_exitCode = 0; + m_readChannel = QProcess::StandardOutput; } void SshRemoteProcessPrivate::setProcState(ProcessState newState) @@ -244,6 +262,11 @@ void SshRemoteProcessPrivate::setProcState(ProcessState newState) } } +QByteArray &SshRemoteProcessPrivate::data() +{ + return m_readChannel == QProcess::StandardOutput ? m_stdout : m_stderr; +} + void SshRemoteProcessPrivate::closeHook() { if (m_wasRunning) { @@ -303,6 +326,8 @@ void SshRemoteProcessPrivate::handleChannelDataInternal(const QByteArray &data) { m_stdout += data; emit readyReadStandardOutput(); + if (m_readChannel == QProcess::StandardOutput) + emit readyRead(); } void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type, @@ -313,6 +338,8 @@ void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type, } else { m_stderr += data; emit readyReadStandardError(); + if (m_readChannel == QProcess::StandardError) + emit readyRead(); } } diff --git a/src/libs/utils/ssh/sshremoteprocess.h b/src/libs/utils/ssh/sshremoteprocess.h index fcd3f0edd5..d696228b01 100644 --- a/src/libs/utils/ssh/sshremoteprocess.h +++ b/src/libs/utils/ssh/sshremoteprocess.h @@ -85,6 +85,9 @@ public: void close(); bool isSequential() const { return true; } + QProcess::ProcessChannel readChannel() const; + void setReadChannel(QProcess::ProcessChannel channel); + /* * Note that this is of limited value in practice, because servers are * usually configured to ignore such requests for security reasons. @@ -127,6 +130,7 @@ private: qint64 writeData(const char *data, qint64 len); void init(); + QByteArray readAllFromChannel(QProcess::ProcessChannel channel); Internal::SshRemoteProcessPrivate *d; }; diff --git a/src/libs/utils/ssh/sshremoteprocess_p.h b/src/libs/utils/ssh/sshremoteprocess_p.h index 32564ef9cb..300050d5eb 100644 --- a/src/libs/utils/ssh/sshremoteprocess_p.h +++ b/src/libs/utils/ssh/sshremoteprocess_p.h @@ -39,6 +39,7 @@ #include #include +#include namespace Utils { class SshRemoteProcess; @@ -60,8 +61,11 @@ public: virtual void closeHook(); + QByteArray &data(); + signals: void started(); + void readyRead(); void readyReadStandardOutput(); void readyReadStandardError(); void closed(int exitStatus); @@ -83,6 +87,8 @@ private: void init(); void setProcState(ProcessState newState); + QProcess::ProcessChannel m_readChannel; + ProcessState m_procState; bool m_wasRunning; QByteArray m_signal; diff --git a/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp b/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp index dd8cfafdef..7f5fcd63cd 100644 --- a/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp +++ b/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp @@ -42,6 +42,8 @@ using namespace Utils; +const QByteArray StderrOutput("ChannelTest"); + RemoteProcessTest::RemoteProcessTest(const SshConnectionParameters ¶ms) : m_sshParams(params), m_timeoutTimer(new QTimer(this)), @@ -76,7 +78,7 @@ void RemoteProcessTest::run() void RemoteProcessTest::handleConnectionError() { - const QString error = m_state == TestingIoDevice + const QString error = m_state == TestingIoDevice || m_state == TestingProcessChannels ? m_sshConnection->errorString() : m_remoteRunner->lastConnectionErrorString(); std::cerr << "Error: Connection failure (" << qPrintable(error) << ")." << std::endl; @@ -212,9 +214,29 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus) connect(m_sshConnection.data(), SIGNAL(error(Utils::SshError)), SLOT(handleConnectionError())); m_sshConnection->connectToHost(); + m_timeoutTimer->start(); break; } case TestingIoDevice: + std::cerr << "Error: Successful exit from process that was supposed to crash." + << std::endl; + qApp->exit(EXIT_FAILURE); + break; + case TestingProcessChannels: + if (m_remoteStderr.isEmpty()) { + std::cerr << "Error: Did not receive readyReadStderr()." << std::endl; + qApp->exit(EXIT_FAILURE); + return; + } + if (m_remoteData != StderrOutput) { + std::cerr << "Error: Expected output '" << StderrOutput.data() << "', received '" + << m_remoteData.data() << "'." << std::endl; + qApp->exit(EXIT_FAILURE); + return; + } + std::cout << "Ok.\nAll tests succeeded." << std::endl; + qApp->quit(); + break; case Inactive: Q_ASSERT(false); } @@ -238,8 +260,19 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus) m_remoteRunner->runInTerminal("top -n 1", SshPseudoTerminal(), m_sshParams); break; case TestingIoDevice: - std::cout << "Ok.\nAll tests succeeded." << std::endl; - qApp->quit(); + std::cout << "Ok\nTesting process channels... " << std::flush; + m_state = TestingProcessChannels; + m_started = false; + m_remoteStderr.clear(); + m_echoProcess = m_sshConnection->createRemoteProcess("printf " + StderrOutput + " >&2"); + m_echoProcess->setReadChannel(QProcess::StandardError); + connect(m_echoProcess.data(), SIGNAL(started()), SLOT(handleProcessStarted())); + connect(m_echoProcess.data(), SIGNAL(closed(int)), SLOT(handleProcessClosed(int))); + connect(m_echoProcess.data(), SIGNAL(readyRead()), SLOT(handleReadyRead())); + connect(m_echoProcess.data(), SIGNAL(readyReadStandardError()), + SLOT(handleReadyReadStderr())); + m_echoProcess->start(); + m_timeoutTimer->start(); break; default: std::cerr << "Error: Unexpected crash." << std::endl; @@ -274,15 +307,38 @@ QString RemoteProcessTest::testString() const void RemoteProcessTest::handleReadyRead() { - Q_ASSERT(m_state == TestingIoDevice); - - const QString &data = QString::fromUtf8(m_catProcess->readAll()); - if (data != testString()) { - std::cerr << "Testing of QIODevice functionality failed: Expected '" - << qPrintable(testString()) << "', got '" << qPrintable(data) << "'." << std::endl; - qApp->exit(1); + switch (m_state) { + case TestingIoDevice: { + const QString &data = QString::fromUtf8(m_catProcess->readAll()); + if (data != testString()) { + std::cerr << "Testing of QIODevice functionality failed: Expected '" + << qPrintable(testString()) << "', got '" << qPrintable(data) << "'." << std::endl; + qApp->exit(1); + } + Utils::SshRemoteProcessRunner * const killer = new Utils::SshRemoteProcessRunner(this); + killer->run("pkill -9 cat", m_sshParams); + break; } + case TestingProcessChannels: + m_remoteData += m_echoProcess->readAll(); + break; + default: + qFatal("%s: Unexpected state %d.", Q_FUNC_INFO, m_state); + } + +} + +void RemoteProcessTest::handleReadyReadStdout() +{ + Q_ASSERT(m_state == TestingProcessChannels); + + std::cerr << "Error: Received unexpected stdout data." << std::endl; + qApp->exit(EXIT_FAILURE); +} + +void RemoteProcessTest::handleReadyReadStderr() +{ + Q_ASSERT(m_state == TestingProcessChannels); - Utils::SshRemoteProcessRunner * const killer = new Utils::SshRemoteProcessRunner(this); - killer->run("pkill -9 cat", m_sshParams); + m_remoteStderr = "dummy"; } diff --git a/tests/manual/ssh/remoteprocess/remoteprocesstest.h b/tests/manual/ssh/remoteprocess/remoteprocesstest.h index bc1a381999..9a90f6a4b9 100644 --- a/tests/manual/ssh/remoteprocess/remoteprocesstest.h +++ b/tests/manual/ssh/remoteprocess/remoteprocesstest.h @@ -56,11 +56,14 @@ private slots: void handleProcessClosed(int exitStatus); void handleTimeout(); void handleReadyRead(); + void handleReadyReadStdout(); + void handleReadyReadStderr(); void handleConnected(); private: enum State { - Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal, TestingIoDevice + Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal, TestingIoDevice, + TestingProcessChannels }; QString testString() const; @@ -70,9 +73,11 @@ private: QTextStream *m_textStream; Utils::SshRemoteProcessRunner * const m_remoteRunner; Utils::SshRemoteProcess::Ptr m_catProcess; + Utils::SshRemoteProcess::Ptr m_echoProcess; Utils::SshConnection::Ptr m_sshConnection; QByteArray m_remoteStdout; QByteArray m_remoteStderr; + QByteArray m_remoteData; State m_state; bool m_started; }; -- cgit v1.2.1