/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. * This project including source code and documentation is protected by * copyright law, and remains the property of nymea GmbH. All rights, including * reproduction, publication, editing and translation, are reserved. The use of * this project is subject to the terms of a license agreement to be concluded * with nymea GmbH in accordance with the terms of use of nymea GmbH, available * under https://nymea.io/license * * GNU Lesser General Public License Usage * Alternatively, this project may be redistributed and/or modified under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation; version 3. This project is distributed in the hope that * it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this project. If not, see . * * For any further details and any questions please contact us under * contact@nymea.io or see our FAQ/Licensing Information on * https://nymea.io/license/faq * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "integrationpluginremotessh.h" #include "plugininfo.h" #include #include IntegrationPluginRemoteSsh::IntegrationPluginRemoteSsh() { } void IntegrationPluginRemoteSsh::init() { m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginRemoteSsh::onPluginTimeout); } void IntegrationPluginRemoteSsh::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); qCDebug(dcRemoteSsh()) << "Setup" << thing->name() << thing->params(); if (thing->thingClassId() == reverseSshThingClassId) { m_identityFilePath = QString("%1/.ssh/id_rsa_guh").arg(QDir::homePath()); return info->finish(Thing::ThingErrorNoError); } } void IntegrationPluginRemoteSsh::executeAction(ThingActionInfo *info) { Thing *thing = info->thing(); Action action = info->action(); if (thing->thingClassId() == reverseSshThingClassId ) { if (action.actionTypeId() == reverseSshConnectedActionTypeId) { if (action.param(reverseSshConnectedActionConnectedParamTypeId).value().toBool() == true) { QProcess *process = startReverseSSHProcess(thing); m_reverseSSHProcess.insert(process, thing); m_startingProcess.insert(process, info); // in case action call is cancelled, detach result reporting connect(info, &ThingActionInfo::destroyed, process, [this, process]{ m_startingProcess.remove(process); }); return; } else { QProcess *process = m_reverseSSHProcess.key(thing); // Check if the application is running... if (!process) return info->finish(Thing::ThingErrorNoError); if (process->state() == QProcess::NotRunning) return info->finish(Thing::ThingErrorNoError); process->kill(); m_killingProcess.insert(process, info); // in case action call is cancelled, detach result reporting connect(info, &ThingActionInfo::destroyed, process, [this, process]{ m_killingProcess.remove(process); }); return; } } } } void IntegrationPluginRemoteSsh::thingRemoved(Thing *thing) { if (thing->thingClassId() == reverseSshThingClassId) { QProcess *process = m_reverseSSHProcess.key(thing); if (!process) return; m_reverseSSHProcess.remove(process); if (process->state() != QProcess::NotRunning) { process->kill(); } process->deleteLater(); } } void IntegrationPluginRemoteSsh::processReadyRead() { QByteArray data = static_cast(sender())->readAll(); qCWarning(dcRemoteSsh()) << "process read" << data; } QProcess * IntegrationPluginRemoteSsh::startReverseSSHProcess(Thing *thing) { qCDebug(dcRemoteSsh()) << "Start reverse SSH"; QProcess *process = new QProcess(this); process->setProcessChannelMode(QProcess::MergedChannels); connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); connect(process, &QProcess::stateChanged, this, &IntegrationPluginRemoteSsh::processStateChanged); connect(process, &QProcess::readyRead, this, &IntegrationPluginRemoteSsh::processReadyRead); QStringList arguments; int localPort = thing->paramValue(reverseSshThingLocalPortParamTypeId).toInt(); int remotePort = thing->paramValue(reverseSshThingRemotePortParamTypeId).toInt(); QString user = thing->paramValue(reverseSshThingUserParamTypeId).toString(); QString password = thing->paramValue(reverseSshThingPasswordParamTypeId).toString(); QString address = thing->paramValue(reverseSshThingAddressParamTypeId).toString(); arguments << "-p" << password; arguments << "ssh" << "-o StrictHostKeyChecking=no" << "-oUserKnownHostsFile=/dev/null"; arguments << "-TN" << "-R" << QString("%1:localhost:%2").arg(remotePort).arg(localPort) << QString("%1@%2").arg(user, address); process->start(QStringLiteral("sshpass"), arguments); qCDebug(dcRemoteSsh()) << "Command:" << process->program() << process->arguments(); return process; } void IntegrationPluginRemoteSsh::onPluginTimeout() { foreach(QProcess *process, m_reverseSSHProcess.keys()) { if (process->state() == QProcess::NotRunning) qCDebug(dcRemoteSsh()) << "SSH Process not running"; } } void IntegrationPluginRemoteSsh::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitCode); QProcess *process = static_cast(sender()); if(exitStatus != QProcess::NormalExit || exitCode != 0) { qCWarning(dcRemoteSsh()) << "Error:" << process->readAllStandardError(); } if (m_reverseSSHProcess.contains(process)) { qCDebug(dcRemoteSsh()) << "SSH process finished"; Thing *thing = m_reverseSSHProcess.value(process); thing->setStateValue(reverseSshConnectedStateTypeId, false); m_reverseSSHProcess.remove(process); } else if (m_sshKeyGenProcess.contains(process)) { qCDebug(dcRemoteSsh()) << "SSH Key generation process finished" << process->readAll(); Thing *thing = m_sshKeyGenProcess.value(process); QFile file(QString(m_identityFilePath + ".pub")); if(!file.open(QIODevice::ReadOnly)) qCWarning(dcRemoteSsh()) << "error" << file.errorString(); QTextStream in(&file); QString sshKey = in.readLine(); thing->setStateValue(reverseSshSshKeyStateTypeId, sshKey); process->kill(); m_sshKeyGenProcess.remove(process); file.close(); } } void IntegrationPluginRemoteSsh::processStateChanged(QProcess::ProcessState state) { QProcess *process = static_cast(sender()); Thing *thing = m_reverseSSHProcess.value(process); switch (state) { case QProcess::Running: thing->setStateValue(reverseSshConnectedStateTypeId, true); if (m_startingProcess.contains(process)) { m_startingProcess.take(process)->finish(Thing::ThingErrorNoError); } break; case QProcess::NotRunning: if (thing) thing->setStateValue(reverseSshConnectedStateTypeId, false); if (m_startingProcess.contains(process)) { m_startingProcess.take(process)->finish(Thing::ThingErrorInvalidParameter); } if (m_killingProcess.contains(process)) { m_killingProcess.take(process)->finish(Thing::ThingErrorNoError); m_reverseSSHProcess.remove(process); } break; default: break; } }