SSH: Implement terminal support and corresponding tests.

Task-number: QTCREATORBUG-3891
This commit is contained in:
Christian Kandeler
2011-04-06 10:26:45 +02:00
parent dc6874acb8
commit 288b25bf40
8 changed files with 79 additions and 19 deletions

View File

@@ -160,7 +160,7 @@ void SshOutgoingPacket::generatePtyRequestPacket(quint32 remoteChannel,
init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel) init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
.appendString("pty-req").appendBool(false) .appendString("pty-req").appendBool(false)
.appendString(terminal.termType).appendInt(terminal.columnCount) .appendString(terminal.termType).appendInt(terminal.columnCount)
.appendInt(terminal.rowCount); .appendInt(terminal.rowCount).appendInt(0).appendInt(0);
QByteArray modeString; QByteArray modeString;
for (SshPseudoTerminal::ModeMap::ConstIterator it = terminal.modes.constBegin(); for (SshPseudoTerminal::ModeMap::ConstIterator it = terminal.modes.constBegin();
it != terminal.modes.constEnd(); ++it) { it != terminal.modes.constEnd(); ++it) {

View File

@@ -199,9 +199,8 @@ void SshRemoteProcessPrivate::handleOpenSuccessInternal()
envVar.second); envVar.second);
} }
if (m_useTerminal) { if (m_useTerminal)
// TODO: Encode m_terminal m_sendFacility.sendPtyRequestPacket(remoteChannel(), m_terminal);
}
m_sendFacility.sendExecPacket(remoteChannel(), m_command); m_sendFacility.sendExecPacket(remoteChannel(), m_command);
setProcState(ExecRequested); setProcState(ExecRequested);

View File

@@ -34,6 +34,7 @@
#include "sshremoteprocessrunner.h" #include "sshremoteprocessrunner.h"
#include "sshconnectionmanager.h" #include "sshconnectionmanager.h"
#include "sshpseudoterminal.h"
#define ASSERT_STATE(states) assertState(states, Q_FUNC_INFO) #define ASSERT_STATE(states) assertState(states, Q_FUNC_INFO)
@@ -54,7 +55,9 @@ public:
SshRemoteProcessRunnerPrivate(const SshConnection::Ptr &connection, SshRemoteProcessRunnerPrivate(const SshConnection::Ptr &connection,
QObject *parent); QObject *parent);
~SshRemoteProcessRunnerPrivate(); ~SshRemoteProcessRunnerPrivate();
void run(const QByteArray &command); void runWithoutTerminal(const QByteArray &command);
void runInTerminal(const QByteArray &command,
const SshPseudoTerminal &terminal);
QByteArray command() const { return m_command; } QByteArray command() const { return m_command; }
const SshConnection::Ptr m_connection; const SshConnection::Ptr m_connection;
@@ -77,12 +80,15 @@ private slots:
private: private:
enum State { Inactive, Connecting, Connected, ProcessRunning }; enum State { Inactive, Connecting, Connected, ProcessRunning };
void run(const QByteArray &command);
void setState(State state); void setState(State state);
void assertState(const QList<State> &allowedStates, const char *func); void assertState(const QList<State> &allowedStates, const char *func);
void assertState(State allowedState, const char *func); void assertState(State allowedState, const char *func);
State m_state; State m_state;
bool m_needsRelease; bool m_needsRelease;
bool m_runInTerminal;
SshPseudoTerminal m_terminal;
QByteArray m_command; QByteArray m_command;
}; };
@@ -110,6 +116,20 @@ SshRemoteProcessRunnerPrivate::~SshRemoteProcessRunnerPrivate()
setState(Inactive); setState(Inactive);
} }
void SshRemoteProcessRunnerPrivate::runWithoutTerminal(const QByteArray &command)
{
m_runInTerminal = false;
run(command);
}
void SshRemoteProcessRunnerPrivate::runInTerminal(const QByteArray &command,
const SshPseudoTerminal &terminal)
{
m_terminal = terminal;
m_runInTerminal = true;
run(command);
}
void SshRemoteProcessRunnerPrivate::run(const QByteArray &command) void SshRemoteProcessRunnerPrivate::run(const QByteArray &command)
{ {
ASSERT_STATE(Inactive); ASSERT_STATE(Inactive);
@@ -142,6 +162,8 @@ void SshRemoteProcessRunnerPrivate::handleConnected()
SIGNAL(processOutputAvailable(QByteArray))); SIGNAL(processOutputAvailable(QByteArray)));
connect(m_process.data(), SIGNAL(errorOutputAvailable(QByteArray)), connect(m_process.data(), SIGNAL(errorOutputAvailable(QByteArray)),
SIGNAL(processErrorOutputAvailable(QByteArray))); SIGNAL(processErrorOutputAvailable(QByteArray)));
if (m_runInTerminal)
m_process->requestTerminal(m_terminal);
m_process->start(); m_process->start();
} }
@@ -246,7 +268,13 @@ void SshRemoteProcessRunner::init()
void SshRemoteProcessRunner::run(const QByteArray &command) void SshRemoteProcessRunner::run(const QByteArray &command)
{ {
d->run(command); d->runWithoutTerminal(command);
}
void SshRemoteProcessRunner::runInTerminal(const QByteArray &command,
const SshPseudoTerminal &terminal)
{
d->runInTerminal(command, terminal);
} }
QByteArray SshRemoteProcessRunner::command() const { return d->command(); } QByteArray SshRemoteProcessRunner::command() const { return d->command(); }

View File

@@ -51,6 +51,8 @@ public:
static Ptr create(const SshConnection::Ptr &connection); static Ptr create(const SshConnection::Ptr &connection);
void run(const QByteArray &command); void run(const QByteArray &command);
void runInTerminal(const QByteArray &command,
const SshPseudoTerminal &terminal);
QByteArray command() const; QByteArray command() const;
SshConnection::Ptr connection() const; SshConnection::Ptr connection() const;

View File

@@ -153,6 +153,13 @@ void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize,
sendPacket(); sendPacket();
} }
void SshSendFacility::sendPtyRequestPacket(quint32 remoteChannel,
const SshPseudoTerminal &terminal)
{
m_outgoingPacket.generatePtyRequestPacket(remoteChannel, terminal);
sendPacket();
}
void SshSendFacility::sendEnvPacket(quint32 remoteChannel, void SshSendFacility::sendEnvPacket(quint32 remoteChannel,
const QByteArray &var, const QByteArray &value) const QByteArray &var, const QByteArray &value)
{ {

View File

@@ -43,6 +43,8 @@ QT_END_NAMESPACE
namespace Utils { namespace Utils {
class SshPseudoTerminal;
namespace Internal { namespace Internal {
class SshKeyExchange; class SshKeyExchange;
@@ -70,6 +72,8 @@ public:
void sendInvalidPacket(); void sendInvalidPacket();
void sendSessionPacket(quint32 channelId, quint32 windowSize, void sendSessionPacket(quint32 channelId, quint32 windowSize,
quint32 maxPacketSize); quint32 maxPacketSize);
void sendPtyRequestPacket(quint32 remoteChannel,
const SshPseudoTerminal &terminal);
void sendEnvPacket(quint32 remoteChannel, const QByteArray &var, void sendEnvPacket(quint32 remoteChannel, const QByteArray &var,
const QByteArray &value); const QByteArray &value);
void sendExecPacket(quint32 remoteChannel, const QByteArray &command); void sendExecPacket(quint32 remoteChannel, const QByteArray &command);

View File

@@ -33,6 +33,8 @@
#include "remoteprocesstest.h" #include "remoteprocesstest.h"
#include <utils/ssh/sshpseudoterminal.h>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QTimer> #include <QtCore/QTimer>
@@ -65,7 +67,7 @@ void RemoteProcessTest::run()
connect(m_remoteRunner.data(), SIGNAL(processClosed(int)), connect(m_remoteRunner.data(), SIGNAL(processClosed(int)),
SLOT(handleProcessClosed(int))); SLOT(handleProcessClosed(int)));
std::cout << "Testing successful remote process..." << std::endl; std::cout << "Testing successful remote process... " << std::flush;
m_state = TestingSuccess; m_state = TestingSuccess;
m_started = false; m_started = false;
m_timeoutTimer->start(); m_timeoutTimer->start();
@@ -101,7 +103,7 @@ void RemoteProcessTest::handleProcessStdout(const QByteArray &output)
std::cerr << "Error: Remote output from non-started process." std::cerr << "Error: Remote output from non-started process."
<< std::endl; << std::endl;
qApp->quit(); qApp->quit();
} else if (m_state != TestingSuccess) { } else if (m_state != TestingSuccess && m_state != TestingTerminal) {
std::cerr << "Error: Got remote standard output in state " << m_state std::cerr << "Error: Got remote standard output in state " << m_state
<< "." << std::endl; << "." << std::endl;
qApp->quit(); qApp->quit();
@@ -150,12 +152,11 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
return; return;
} }
std::cout << "Ok. Testing unsuccessful remote process..." std::cout << "Ok.\nTesting unsuccessful remote process... " << std::flush;
<< std::endl;
m_state = TestingFailure; m_state = TestingFailure;
m_started = false; m_started = false;
m_timeoutTimer->start(); m_timeoutTimer->start();
m_remoteRunner->run("ls /wedontexepectsuchafiletoexist"); m_remoteRunner->run("top -n 1"); // Does not succeed without terminal.
break; break;
} }
case TestingFailure: { case TestingFailure: {
@@ -167,14 +168,12 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
return; return;
} }
if (m_remoteStderr.isEmpty()) { if (m_remoteStderr.isEmpty()) {
std::cerr << "Error: Command did not produce error output." std::cerr << "Error: Command did not produce error output." << std::flush;
<< std::endl;
qApp->quit(); qApp->quit();
return; return;
} }
std::cout << "Ok. Testing crashing remote process..." std::cout << "Ok.\nTesting crashing remote process... " << std::flush;
<< std::endl;
m_state = TestingCrash; m_state = TestingCrash;
m_started = false; m_started = false;
m_timeoutTimer->start(); m_timeoutTimer->start();
@@ -185,7 +184,25 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
std::cerr << "Error: Successful exit from process that was " std::cerr << "Error: Successful exit from process that was "
"supposed to crash." << std::endl; "supposed to crash." << std::endl;
qApp->quit(); qApp->quit();
return; break;
case TestingTerminal: {
const int exitCode = m_remoteRunner->process()->exitCode();
if (exitCode != 0) {
std::cerr << "Error: exit code is " << exitCode
<< ", expected zero." << std::endl;
qApp->quit();
return;
}
if (m_remoteStdout.isEmpty()) {
std::cerr << "Error: Command did not produce output."
<< std::endl;
qApp->quit();
return;
}
std::cout << "Ok.\nAll tests succeeded." << std::endl;
qApp->quit();
break;
}
case Inactive: case Inactive:
Q_ASSERT(false); Q_ASSERT(false);
} }
@@ -205,8 +222,11 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
qApp->quit(); qApp->quit();
return; return;
} }
std::cout << "Ok. All tests succeeded." << std::endl; std::cout << "Ok.\nTesting remote process with terminal... " << std::flush;
qApp->quit(); m_state = TestingTerminal;
m_started = false;
m_timeoutTimer->start();
m_remoteRunner->runInTerminal("top -n 1", SshPseudoTerminal());
} }
} }

View File

@@ -56,7 +56,7 @@ private slots:
void handleTimeout(); void handleTimeout();
private: private:
enum State { Inactive, TestingSuccess, TestingFailure, TestingCrash }; enum State { Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal };
QTimer * const m_timeoutTimer; QTimer * const m_timeoutTimer;
const Utils::SshRemoteProcessRunner::Ptr m_remoteRunner; const Utils::SshRemoteProcessRunner::Ptr m_remoteRunner;