forked from qt-creator/qt-creator
SSH: Support different read channels in SshRemoteProcess.
This is part of the effort to support more QProcess concepts. Change-Id: Idb888e733570a58d3810f371409b657b30bbd929 Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
@@ -96,28 +96,35 @@ SshRemoteProcess::~SshRemoteProcess()
|
|||||||
|
|
||||||
bool SshRemoteProcess::atEnd() const
|
bool SshRemoteProcess::atEnd() const
|
||||||
{
|
{
|
||||||
return QIODevice::atEnd() && d->m_stdout.isEmpty();
|
return QIODevice::atEnd() && d->data().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 SshRemoteProcess::bytesAvailable() const
|
qint64 SshRemoteProcess::bytesAvailable() const
|
||||||
{
|
{
|
||||||
return QIODevice::bytesAvailable() + d->m_stdout.count();
|
return QIODevice::bytesAvailable() + d->data().count();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SshRemoteProcess::canReadLine() const
|
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()
|
QByteArray SshRemoteProcess::readAllStandardOutput()
|
||||||
{
|
{
|
||||||
return readAll();
|
return readAllFromChannel(QProcess::StandardOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SshRemoteProcess::readAllStandardError()
|
QByteArray SshRemoteProcess::readAllStandardError()
|
||||||
{
|
{
|
||||||
const QByteArray data = d->m_stderr;
|
return readAllFromChannel(QProcess::StandardError);
|
||||||
d->m_stderr.clear();
|
}
|
||||||
|
|
||||||
|
QByteArray SshRemoteProcess::readAllFromChannel(QProcess::ProcessChannel channel)
|
||||||
|
{
|
||||||
|
const QProcess::ProcessChannel currentReadChannel = readChannel();
|
||||||
|
setReadChannel(channel);
|
||||||
|
const QByteArray &data = readAll();
|
||||||
|
setReadChannel(currentReadChannel);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,9 +136,9 @@ void SshRemoteProcess::close()
|
|||||||
|
|
||||||
qint64 SshRemoteProcess::readData(char *data, qint64 maxlen)
|
qint64 SshRemoteProcess::readData(char *data, qint64 maxlen)
|
||||||
{
|
{
|
||||||
const qint64 bytesRead = qMin(qint64(d->m_stdout.count()), maxlen);
|
const qint64 bytesRead = qMin(qint64(d->data().count()), maxlen);
|
||||||
memcpy(data, d->m_stdout.constData(), bytesRead);
|
memcpy(data, d->data().constData(), bytesRead);
|
||||||
d->m_stdout.remove(0, bytesRead);
|
d->data().remove(0, bytesRead);
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,13 +151,23 @@ qint64 SshRemoteProcess::writeData(const char *data, qint64 len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QProcess::ProcessChannel SshRemoteProcess::readChannel() const
|
||||||
|
{
|
||||||
|
return d->m_readChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SshRemoteProcess::setReadChannel(QProcess::ProcessChannel channel)
|
||||||
|
{
|
||||||
|
d->m_readChannel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
void SshRemoteProcess::init()
|
void SshRemoteProcess::init()
|
||||||
{
|
{
|
||||||
connect(d, SIGNAL(started()), this, SIGNAL(started()),
|
connect(d, SIGNAL(started()), this, SIGNAL(started()),
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyReadStandardOutput()),
|
connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyReadStandardOutput()),
|
||||||
Qt::QueuedConnection);
|
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,
|
connect(d, SIGNAL(readyReadStandardError()), this,
|
||||||
SIGNAL(readyReadStandardError()), Qt::QueuedConnection);
|
SIGNAL(readyReadStandardError()), Qt::QueuedConnection);
|
||||||
connect(d, SIGNAL(closed(int)), this, SIGNAL(closed(int)), Qt::QueuedConnection);
|
connect(d, SIGNAL(closed(int)), this, SIGNAL(closed(int)), Qt::QueuedConnection);
|
||||||
@@ -228,6 +245,7 @@ void SshRemoteProcessPrivate::init()
|
|||||||
m_procState = NotYetStarted;
|
m_procState = NotYetStarted;
|
||||||
m_wasRunning = false;
|
m_wasRunning = false;
|
||||||
m_exitCode = 0;
|
m_exitCode = 0;
|
||||||
|
m_readChannel = QProcess::StandardOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SshRemoteProcessPrivate::setProcState(ProcessState newState)
|
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()
|
void SshRemoteProcessPrivate::closeHook()
|
||||||
{
|
{
|
||||||
if (m_wasRunning) {
|
if (m_wasRunning) {
|
||||||
@@ -303,6 +326,8 @@ void SshRemoteProcessPrivate::handleChannelDataInternal(const QByteArray &data)
|
|||||||
{
|
{
|
||||||
m_stdout += data;
|
m_stdout += data;
|
||||||
emit readyReadStandardOutput();
|
emit readyReadStandardOutput();
|
||||||
|
if (m_readChannel == QProcess::StandardOutput)
|
||||||
|
emit readyRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type,
|
void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type,
|
||||||
@@ -313,6 +338,8 @@ void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type,
|
|||||||
} else {
|
} else {
|
||||||
m_stderr += data;
|
m_stderr += data;
|
||||||
emit readyReadStandardError();
|
emit readyReadStandardError();
|
||||||
|
if (m_readChannel == QProcess::StandardError)
|
||||||
|
emit readyRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ public:
|
|||||||
void close();
|
void close();
|
||||||
bool isSequential() const { return true; }
|
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
|
* Note that this is of limited value in practice, because servers are
|
||||||
* usually configured to ignore such requests for security reasons.
|
* usually configured to ignore such requests for security reasons.
|
||||||
@@ -127,6 +130,7 @@ private:
|
|||||||
qint64 writeData(const char *data, qint64 len);
|
qint64 writeData(const char *data, qint64 len);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
QByteArray readAllFromChannel(QProcess::ProcessChannel channel);
|
||||||
|
|
||||||
Internal::SshRemoteProcessPrivate *d;
|
Internal::SshRemoteProcessPrivate *d;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#include <QtCore/QList>
|
#include <QtCore/QList>
|
||||||
#include <QtCore/QPair>
|
#include <QtCore/QPair>
|
||||||
|
#include <QtCore/QProcess>
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
class SshRemoteProcess;
|
class SshRemoteProcess;
|
||||||
@@ -60,8 +61,11 @@ public:
|
|||||||
|
|
||||||
virtual void closeHook();
|
virtual void closeHook();
|
||||||
|
|
||||||
|
QByteArray &data();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void started();
|
void started();
|
||||||
|
void readyRead();
|
||||||
void readyReadStandardOutput();
|
void readyReadStandardOutput();
|
||||||
void readyReadStandardError();
|
void readyReadStandardError();
|
||||||
void closed(int exitStatus);
|
void closed(int exitStatus);
|
||||||
@@ -83,6 +87,8 @@ private:
|
|||||||
void init();
|
void init();
|
||||||
void setProcState(ProcessState newState);
|
void setProcState(ProcessState newState);
|
||||||
|
|
||||||
|
QProcess::ProcessChannel m_readChannel;
|
||||||
|
|
||||||
ProcessState m_procState;
|
ProcessState m_procState;
|
||||||
bool m_wasRunning;
|
bool m_wasRunning;
|
||||||
QByteArray m_signal;
|
QByteArray m_signal;
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
|
const QByteArray StderrOutput("ChannelTest");
|
||||||
|
|
||||||
RemoteProcessTest::RemoteProcessTest(const SshConnectionParameters ¶ms)
|
RemoteProcessTest::RemoteProcessTest(const SshConnectionParameters ¶ms)
|
||||||
: m_sshParams(params),
|
: m_sshParams(params),
|
||||||
m_timeoutTimer(new QTimer(this)),
|
m_timeoutTimer(new QTimer(this)),
|
||||||
@@ -76,7 +78,7 @@ void RemoteProcessTest::run()
|
|||||||
|
|
||||||
void RemoteProcessTest::handleConnectionError()
|
void RemoteProcessTest::handleConnectionError()
|
||||||
{
|
{
|
||||||
const QString error = m_state == TestingIoDevice
|
const QString error = m_state == TestingIoDevice || m_state == TestingProcessChannels
|
||||||
? m_sshConnection->errorString() : m_remoteRunner->lastConnectionErrorString();
|
? m_sshConnection->errorString() : m_remoteRunner->lastConnectionErrorString();
|
||||||
|
|
||||||
std::cerr << "Error: Connection failure (" << qPrintable(error) << ")." << std::endl;
|
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)),
|
connect(m_sshConnection.data(), SIGNAL(error(Utils::SshError)),
|
||||||
SLOT(handleConnectionError()));
|
SLOT(handleConnectionError()));
|
||||||
m_sshConnection->connectToHost();
|
m_sshConnection->connectToHost();
|
||||||
|
m_timeoutTimer->start();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TestingIoDevice:
|
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:
|
case Inactive:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
}
|
}
|
||||||
@@ -238,8 +260,19 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
|
|||||||
m_remoteRunner->runInTerminal("top -n 1", SshPseudoTerminal(), m_sshParams);
|
m_remoteRunner->runInTerminal("top -n 1", SshPseudoTerminal(), m_sshParams);
|
||||||
break;
|
break;
|
||||||
case TestingIoDevice:
|
case TestingIoDevice:
|
||||||
std::cout << "Ok.\nAll tests succeeded." << std::endl;
|
std::cout << "Ok\nTesting process channels... " << std::flush;
|
||||||
qApp->quit();
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
std::cerr << "Error: Unexpected crash." << std::endl;
|
std::cerr << "Error: Unexpected crash." << std::endl;
|
||||||
@@ -274,15 +307,38 @@ QString RemoteProcessTest::testString() const
|
|||||||
|
|
||||||
void RemoteProcessTest::handleReadyRead()
|
void RemoteProcessTest::handleReadyRead()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_state == TestingIoDevice);
|
switch (m_state) {
|
||||||
|
case TestingIoDevice: {
|
||||||
const QString &data = QString::fromUtf8(m_catProcess->readAll());
|
const QString &data = QString::fromUtf8(m_catProcess->readAll());
|
||||||
if (data != testString()) {
|
if (data != testString()) {
|
||||||
std::cerr << "Testing of QIODevice functionality failed: Expected '"
|
std::cerr << "Testing of QIODevice functionality failed: Expected '"
|
||||||
<< qPrintable(testString()) << "', got '" << qPrintable(data) << "'." << std::endl;
|
<< qPrintable(testString()) << "', got '" << qPrintable(data) << "'." << std::endl;
|
||||||
qApp->exit(1);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::SshRemoteProcessRunner * const killer = new Utils::SshRemoteProcessRunner(this);
|
}
|
||||||
killer->run("pkill -9 cat", m_sshParams);
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
m_remoteStderr = "dummy";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,14 @@ private slots:
|
|||||||
void handleProcessClosed(int exitStatus);
|
void handleProcessClosed(int exitStatus);
|
||||||
void handleTimeout();
|
void handleTimeout();
|
||||||
void handleReadyRead();
|
void handleReadyRead();
|
||||||
|
void handleReadyReadStdout();
|
||||||
|
void handleReadyReadStderr();
|
||||||
void handleConnected();
|
void handleConnected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum State {
|
enum State {
|
||||||
Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal, TestingIoDevice
|
Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal, TestingIoDevice,
|
||||||
|
TestingProcessChannels
|
||||||
};
|
};
|
||||||
|
|
||||||
QString testString() const;
|
QString testString() const;
|
||||||
@@ -70,9 +73,11 @@ private:
|
|||||||
QTextStream *m_textStream;
|
QTextStream *m_textStream;
|
||||||
Utils::SshRemoteProcessRunner * const m_remoteRunner;
|
Utils::SshRemoteProcessRunner * const m_remoteRunner;
|
||||||
Utils::SshRemoteProcess::Ptr m_catProcess;
|
Utils::SshRemoteProcess::Ptr m_catProcess;
|
||||||
|
Utils::SshRemoteProcess::Ptr m_echoProcess;
|
||||||
Utils::SshConnection::Ptr m_sshConnection;
|
Utils::SshConnection::Ptr m_sshConnection;
|
||||||
QByteArray m_remoteStdout;
|
QByteArray m_remoteStdout;
|
||||||
QByteArray m_remoteStderr;
|
QByteArray m_remoteStderr;
|
||||||
|
QByteArray m_remoteData;
|
||||||
State m_state;
|
State m_state;
|
||||||
bool m_started;
|
bool m_started;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user