diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index d5c54c3908d946704c7a0ab9e6ecf14831edf3b9..be7bee06ea7f6b80c446604a57c40538acf7d1d5 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -105,6 +105,7 @@ SOURCES += mainwindow.cpp \ ssh/sftpincomingpacket.cpp \ ssh/sftpdefs.cpp \ ssh/sftpchannel.cpp \ + ssh/sshremoteprocessrunner.cpp \ outputpanemanager.cpp \ navigationsubwidget.cpp \ sidebarwidget.cpp \ @@ -217,6 +218,7 @@ HEADERS += mainwindow.h \ ssh/sftpdefs.h \ ssh/sftpchannel.h \ ssh/sftpchannel_p.h \ + ssh/sshremoteprocessrunner.h \ outputpanemanager.h \ navigationsubwidget.h \ sidebarwidget.h \ diff --git a/src/plugins/coreplugin/ssh/sshremoteprocessrunner.cpp b/src/plugins/coreplugin/ssh/sshremoteprocessrunner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8ded6a2c16a1a408683fee8ba80bec09d05fde5 --- /dev/null +++ b/src/plugins/coreplugin/ssh/sshremoteprocessrunner.cpp @@ -0,0 +1,210 @@ +#include "sshremoteprocessrunner.h" + +#define ASSERT_STATE(states) assertState(states, Q_FUNC_INFO) + +namespace Core { + +class SshRemoteProcessRunnerPrivate : public QObject +{ + Q_OBJECT +public: + SshRemoteProcessRunnerPrivate(const SshConnectionParameters ¶ms, + QObject *parent); + SshRemoteProcessRunnerPrivate(const SshConnection::Ptr &connection, + QObject *parent); + void run(const QByteArray &command); + QByteArray command() const { return m_command; } + + const SshConnection::Ptr m_connection; + SshRemoteProcess::Ptr m_process; + +signals: + void connectionError(Core::SshError); + void processStarted(); + void processOutputAvailable(const QByteArray &output); + void processErrorOutputAvailable(const QByteArray &output); + void processClosed(int exitStatus); + +private slots: + void handleConnected(); + void handleConnectionError(Core::SshError error); + void handleDisconnected(); + void handleProcessStarted(); + void handleProcessFinished(int exitStatus); + +private: + enum State { Inactive, Connecting, Connected, ProcessRunning }; + + void setState(State state); + void assertState(const QList<State> &allowedStates, const char *func); + void assertState(State allowedState, const char *func); + + State m_state; + QByteArray m_command; + const SshConnectionParameters m_params; +}; + + +SshRemoteProcessRunnerPrivate::SshRemoteProcessRunnerPrivate(const SshConnectionParameters ¶ms, + QObject *parent) + : QObject(parent), + m_connection(SshConnection::create()), + m_state(Inactive), + m_params(params) +{ +} + +SshRemoteProcessRunnerPrivate::SshRemoteProcessRunnerPrivate(const SshConnection::Ptr &connection, + QObject *parent) + : QObject(parent), + m_connection(connection), + m_state(Inactive), + m_params(connection->connectionParameters()) +{ +} + +void SshRemoteProcessRunnerPrivate::run(const QByteArray &command) +{ + ASSERT_STATE(Inactive); + setState(Connecting); + + m_command = command; + connect(m_connection.data(), SIGNAL(error(Core::SshError)), + SLOT(handleConnectionError(Core::SshError))); + connect(m_connection.data(), SIGNAL(disconnected()), + SLOT(handleDisconnected())); + if (m_connection->state() == SshConnection::Connected) { + handleConnected(); + } else { + connect(m_connection.data(), SIGNAL(connected()), + SLOT(handleConnected())); + m_connection->connectToHost(m_params); + } +} + +void SshRemoteProcessRunnerPrivate::handleConnected() +{ + ASSERT_STATE(Connecting); + setState(Connected); + + m_process = m_connection->createRemoteProcess(m_command); + connect(m_process.data(), SIGNAL(started()), SLOT(handleProcessStarted())); + connect(m_process.data(), SIGNAL(closed(int)), + SLOT(handleProcessFinished(int))); + connect(m_process.data(), SIGNAL(outputAvailable(QByteArray)), + SIGNAL(processOutputAvailable(QByteArray))); + connect(m_process.data(), SIGNAL(errorOutputAvailable(QByteArray)), + SIGNAL(processErrorOutputAvailable(QByteArray))); + m_process->start(); +} + +void SshRemoteProcessRunnerPrivate::handleConnectionError(Core::SshError error) +{ + handleDisconnected(); + emit connectionError(error); +} + +void SshRemoteProcessRunnerPrivate::handleDisconnected() +{ + ASSERT_STATE(QList<State>() << Connecting << Connected << ProcessRunning); + setState(Inactive); +} + +void SshRemoteProcessRunnerPrivate::handleProcessStarted() +{ + ASSERT_STATE(Connected); + setState(ProcessRunning); + + emit processStarted(); +} + +void SshRemoteProcessRunnerPrivate::handleProcessFinished(int exitStatus) +{ + switch (exitStatus) { + case SshRemoteProcess::FailedToStart: + ASSERT_STATE(Connected); + break; + case SshRemoteProcess::KilledBySignal: + case SshRemoteProcess::ExitedNormally: + ASSERT_STATE(ProcessRunning); + break; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Impossible exit status."); + } + setState(Inactive); + emit processClosed(exitStatus); +} + +void SshRemoteProcessRunnerPrivate::setState(State state) +{ + if (m_state != state) { + m_state = state; + if (m_state == Inactive) { + if (m_process) + disconnect(m_process.data(), 0, this, 0); + disconnect(m_connection.data(), 0, this, 0); + } + } +} + +void SshRemoteProcessRunnerPrivate::assertState(const QList<State> &allowedStates, + const char *func) +{ + if (!allowedStates.contains(m_state)) + qWarning("Unexpected state %d in function %s", m_state, func); +} + +void SshRemoteProcessRunnerPrivate::assertState(State allowedState, + const char *func) +{ + assertState(QList<State>() << allowedState, func); +} + + +SshRemoteProcessRunner::Ptr SshRemoteProcessRunner::create(const SshConnectionParameters ¶ms) +{ + return SshRemoteProcessRunner::Ptr(new SshRemoteProcessRunner(params)); +} + +SshRemoteProcessRunner::Ptr SshRemoteProcessRunner::create(const SshConnection::Ptr &connection) +{ + return SshRemoteProcessRunner::Ptr(new SshRemoteProcessRunner(connection)); +} + +SshRemoteProcessRunner::SshRemoteProcessRunner(const SshConnectionParameters ¶ms) + : d(new SshRemoteProcessRunnerPrivate(params, this)) +{ + init(); +} + +SshRemoteProcessRunner::SshRemoteProcessRunner(const SshConnection::Ptr &connection) + : d(new SshRemoteProcessRunnerPrivate(connection, this)) +{ + init(); +} + +void SshRemoteProcessRunner::init() +{ + connect(d, SIGNAL(connectionError(Core::SshError)), + SIGNAL(connectionError(Core::SshError))); + connect(d, SIGNAL(processStarted()), SIGNAL(processStarted())); + connect(d, SIGNAL(processClosed(int)), SIGNAL(processClosed(int))); + connect(d, SIGNAL(processOutputAvailable(QByteArray)), + SIGNAL(processOutputAvailable(QByteArray))); + connect(d, SIGNAL(processErrorOutputAvailable(QByteArray)), + SIGNAL(processErrorOutputAvailable(QByteArray))); +} + +void SshRemoteProcessRunner::run(const QByteArray &command) +{ + d->run(command); +} + +QByteArray SshRemoteProcessRunner::command() const { return d->command(); } +SshConnection::Ptr SshRemoteProcessRunner::connection() const { return d->m_connection; } +SshRemoteProcess::Ptr SshRemoteProcessRunner::process() const { return d->m_process; } + +} // namespace Core + + +#include "sshremoteprocessrunner.moc" diff --git a/src/plugins/coreplugin/ssh/sshremoteprocessrunner.h b/src/plugins/coreplugin/ssh/sshremoteprocessrunner.h new file mode 100644 index 0000000000000000000000000000000000000000..c2545971fe6286094ae651b905a9fec15bf8ab77 --- /dev/null +++ b/src/plugins/coreplugin/ssh/sshremoteprocessrunner.h @@ -0,0 +1,44 @@ +#ifndef SSHREMOTEPROCESSRUNNER_H +#define SSHREMOTEPROCESSRUNNER_H + +#include "sshconnection.h" +#include "sshremoteprocess.h" + +namespace Core { +class SshRemoteProcessRunnerPrivate; + +// Convenience class for running a remote process over an SSH connection. +class CORE_EXPORT SshRemoteProcessRunner : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(SshRemoteProcessRunner) +public: + typedef QSharedPointer<SshRemoteProcessRunner> Ptr; + + Ptr create(const SshConnectionParameters ¶ms); + Ptr create(const SshConnection::Ptr &connection); + + void run(const QByteArray &command); + QByteArray command() const; + + SshConnection::Ptr connection() const; + SshRemoteProcess::Ptr process() const; + +signals: + void connectionError(Core::SshError); + void processStarted(); + void processOutputAvailable(const QByteArray &output); + void processErrorOutputAvailable(const QByteArray &output); + void processClosed(int exitStatus); // values are of type SshRemoteProcess::ExitStatus + +private: + SshRemoteProcessRunner(const SshConnectionParameters ¶ms); + SshRemoteProcessRunner(const SshConnection::Ptr &connection); + void init(); + + SshRemoteProcessRunnerPrivate *d; +}; + +} // namespace Core + +#endif // SSHREMOTEPROCESSRUNNER_H