/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "sshremoteprocess.h" #include "sshremoteprocess_p.h" #include "sshdelayedsignal_p.h" #include "sshincomingpacket_p.h" #include "sshsendfacility_p.h" #include namespace Core { const QByteArray SshRemoteProcess::AbrtSignal("ABRT"); const QByteArray SshRemoteProcess::AlrmSignal("ALRM"); const QByteArray SshRemoteProcess::FpeSignal("FPE"); const QByteArray SshRemoteProcess::HupSignal("HUP"); const QByteArray SshRemoteProcess::IllSignal("ILL"); const QByteArray SshRemoteProcess::IntSignal("INT"); const QByteArray SshRemoteProcess::KillSignal("KILL"); const QByteArray SshRemoteProcess::PipeSignal("PIPE"); const QByteArray SshRemoteProcess::QuitSignal("QUIT"); const QByteArray SshRemoteProcess::SegvSignal("SEGV"); const QByteArray SshRemoteProcess::TermSignal("TERM"); const QByteArray SshRemoteProcess::Usr1Signal("USR1"); const QByteArray SshRemoteProcess::Usr2Signal("USR2"); SshRemoteProcess::SshRemoteProcess(const QByteArray &command, quint32 channelId, Internal::SshSendFacility &sendFacility) : d(new Internal::SshRemoteProcessPrivate(command, channelId, sendFacility, this)) { } SshRemoteProcess::~SshRemoteProcess() { Q_ASSERT(d->channelState() == Internal::SshRemoteProcessPrivate::Inactive || d->channelState() == Internal::SshRemoteProcessPrivate::CloseRequested || d->channelState() == Internal::SshRemoteProcessPrivate::Closed); delete d; } void SshRemoteProcess::addToEnvironment(const QByteArray &var, const QByteArray &value) { if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive) d->m_env << qMakePair(var, value); // Cached locally and sent on start() } void SshRemoteProcess::start() { if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive) { #ifdef CREATOR_SSH_DEBUG qDebug("process start requested, channel id = %u", d->localChannelId()); #endif d->requestSessionStart(); } } void SshRemoteProcess::sendSignal(const QByteArray &signal) { try { if (isRunning()) d->m_sendFacility.sendChannelSignalPacket(d->remoteChannel(), signal); } catch (Botan::Exception &e) { d->setError(QString::fromAscii(e.what())); d->closeChannel(); } } void SshRemoteProcess::closeChannel() { d->closeChannel(); } void SshRemoteProcess::sendInput(const QByteArray &data) { if (isRunning()) d->sendData(data); } bool SshRemoteProcess::isRunning() const { return d->m_procState == Internal::SshRemoteProcessPrivate::Running; } QString SshRemoteProcess::errorString() const { return d->errorString(); } int SshRemoteProcess::exitCode() const { return d->m_exitCode; } QByteArray SshRemoteProcess::exitSignal() const { return d->m_signal; } namespace Internal { SshRemoteProcessPrivate::SshRemoteProcessPrivate(const QByteArray &command, quint32 channelId, SshSendFacility &sendFacility, SshRemoteProcess *proc) : AbstractSshChannel(channelId, sendFacility), m_procState(NotYetStarted), m_wasRunning(false), m_exitCode(0), m_command(command), m_proc(proc) { } void SshRemoteProcessPrivate::setProcState(ProcessState newState) { #ifdef CREATOR_SSH_DEBUG qDebug("channel: old state = %d,new state = %d", m_procState, newState); #endif m_procState = newState; if (newState == StartFailed) { createClosedSignal(SshRemoteProcess::FailedToStart); } else if (newState == Running) { m_wasRunning = true; createStartedSignal(); } } void SshRemoteProcessPrivate::closeHook() { if (m_wasRunning) { if (!m_signal.isEmpty()) createClosedSignal(SshRemoteProcess::KilledBySignal); else createClosedSignal(SshRemoteProcess::ExitedNormally); } } void SshRemoteProcessPrivate::handleOpenSuccessInternal() { foreach (const EnvVar &envVar, m_env) { m_sendFacility.sendEnvPacket(remoteChannel(), envVar.first, envVar.second); } m_sendFacility.sendExecPacket(remoteChannel(), m_command); setProcState(ExecRequested); } void SshRemoteProcessPrivate::handleOpenFailureInternal() { setProcState(StartFailed); } void SshRemoteProcessPrivate::handleChannelSuccess() { if (m_procState != ExecRequested) { throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, "Unexpected SSH_MSG_CHANNEL_SUCCESS message."); } setProcState(Running); } void SshRemoteProcessPrivate::handleChannelFailure() { if (m_procState != ExecRequested) { throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, "Unexpected SSH_MSG_CHANNEL_FAILURE message."); } setProcState(StartFailed); closeChannel(); } void SshRemoteProcessPrivate::handleChannelDataInternal(const QByteArray &data) { createOutputAvailableSignal(data); } void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type, const QByteArray &data) { if (type != SSH_EXTENDED_DATA_STDERR) qWarning("Unknown extended data type %u", type); else createErrorOutputAvailableSignal(data); } void SshRemoteProcessPrivate::handleChannelRequest(const SshIncomingPacket &packet) { checkChannelActive(); const QByteArray &requestType = packet.extractChannelRequestType(); if (requestType == SshIncomingPacket::ExitStatusType) { const SshChannelExitStatus status = packet.extractChannelExitStatus(); #ifdef CREATOR_SSH_DEBUG qDebug("Process exiting with exit code %d", status.exitStatus); #endif m_exitCode = status.exitStatus; m_procState = Exited; } else if (requestType == SshIncomingPacket::ExitSignalType) { const SshChannelExitSignal &signal = packet.extractChannelExitSignal(); #ifdef CREATOR_SSH_DEBUG qDebug("Exit due to signal %s", signal.signal.data()); #endif setError(signal.error); m_signal = signal.signal; m_procState = Exited; } else { qWarning("Ignoring unknown request type '%s'", requestType.data()); } } void SshRemoteProcessPrivate::createStartedSignal() { new SshRemoteProcessStartedSignal(this, QWeakPointer(m_proc)); } void SshRemoteProcessPrivate::emitStartedSignal() { emit m_proc->started(); } void SshRemoteProcessPrivate::createOutputAvailableSignal(const QByteArray &output) { new SshRemoteProcessOutputAvailableSignal(this, QWeakPointer(m_proc), output); } void SshRemoteProcessPrivate::emitOutputAvailableSignal(const QByteArray &output) { emit m_proc->outputAvailable(output); } void SshRemoteProcessPrivate::createErrorOutputAvailableSignal(const QByteArray &output) { new SshRemoteProcessErrorOutputAvailableSignal(this, QWeakPointer(m_proc), output); } void SshRemoteProcessPrivate::emitErrorOutputAvailableSignal(const QByteArray &output) { emit m_proc->errorOutputAvailable(output); } void SshRemoteProcessPrivate::createClosedSignal(int exitStatus) { new SshRemoteProcessClosedSignal(this, QWeakPointer(m_proc), exitStatus); } void SshRemoteProcessPrivate::emitClosedSignal(int exitStatus) { emit m_proc->closed(exitStatus); } } // namespace Internal } // namespace Core