forked from qt-creator/qt-creator
SSH: Implement terminal support and corresponding tests.
Task-number: QTCREATORBUG-3891
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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(); }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user