/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2017 Bernhard Trinnes * * Copyright (C) 2018 Simon Stürz * * * * This file is part of nymea. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 library; If not, see * * . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "devicepluginremotessh.h" #include "plugininfo.h" #include #include DevicePluginRemoteSsh::DevicePluginRemoteSsh() { } void DevicePluginRemoteSsh::init() { m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginRemoteSsh::onPluginTimeout); } Device::DeviceSetupStatus DevicePluginRemoteSsh::setupDevice(Device *device) { qCDebug(dcRemoteSsh()) << "Setup" << device->name() << device->params(); if (device->deviceClassId() == reverseSshDeviceClassId) { m_identityFilePath = QString("%1/.ssh/id_rsa_guh").arg(QDir::homePath()); return Device::DeviceSetupStatusSuccess; } return Device::DeviceSetupStatusFailure; } Device::DeviceError DevicePluginRemoteSsh::executeAction(Device *device, const Action &action) { if (device->deviceClassId() == reverseSshDeviceClassId ) { if (action.actionTypeId() == reverseSshConnectedActionTypeId) { if (action.param(reverseSshConnectedActionConnectedParamTypeId).value().toBool() == true) { QProcess *process = startReverseSSHProcess(device); m_reverseSSHProcess.insert(process, device); m_startingProcess.insert(process, action.id()); return Device::DeviceErrorAsync; } else { QProcess *process = m_reverseSSHProcess.key(device); // Check if the application is running... if (!process) return Device::DeviceErrorNoError; if (process->state() == QProcess::NotRunning) return Device::DeviceErrorNoError; process->kill(); m_killingProcess.insert(process, action.id()); return Device::DeviceErrorAsync; } return Device::DeviceErrorNoError; } return Device::DeviceErrorActionTypeNotFound; } return Device::DeviceErrorDeviceClassNotFound; } void DevicePluginRemoteSsh::deviceRemoved(Device *device) { if (device->deviceClassId() == reverseSshDeviceClassId) { QProcess *process = m_reverseSSHProcess.key(device); if (!process) return; m_reverseSSHProcess.remove(process); if (process->state() != QProcess::NotRunning) { process->kill(); } process->deleteLater(); } } void DevicePluginRemoteSsh::processReadyRead() { QByteArray data = static_cast(sender())->readAll(); qCWarning(dcRemoteSsh()) << "process read" << data; } QProcess * DevicePluginRemoteSsh::startReverseSSHProcess(Device *device) { 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, &DevicePluginRemoteSsh::processStateChanged); connect(process, &QProcess::readyRead, this, &DevicePluginRemoteSsh::processReadyRead); QStringList arguments; int localPort = device->paramValue(reverseSshDeviceLocalPortParamTypeId).toInt(); int remotePort = device->paramValue(reverseSshDeviceRemotePortParamTypeId).toInt(); QString user = device->paramValue(reverseSshDeviceUserParamTypeId).toString(); QString password = device->paramValue(reverseSshDevicePasswordParamTypeId).toString(); QString address = device->paramValue(reverseSshDeviceAddressParamTypeId).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 DevicePluginRemoteSsh::onPluginTimeout() { foreach(QProcess *process, m_reverseSSHProcess.keys()) { if (process->state() == QProcess::NotRunning) qCDebug(dcRemoteSsh()) << "SSH Process not running"; } } void DevicePluginRemoteSsh::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"; Device *device = m_reverseSSHProcess.value(process); device->setStateValue(reverseSshConnectedStateTypeId, false); m_reverseSSHProcess.remove(process); } else if (m_sshKeyGenProcess.contains(process)) { qCDebug(dcRemoteSsh()) << "SSH Key generation process finished" << process->readAll(); Device *device = 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(); device->setStateValue(reverseSshSshKeyStateTypeId, sshKey); process->kill(); m_sshKeyGenProcess.remove(process); file.close(); } } void DevicePluginRemoteSsh::processStateChanged(QProcess::ProcessState state) { QProcess *process = static_cast(sender()); Device *device = m_reverseSSHProcess.value(process); switch (state) { case QProcess::Running: device->setStateValue(reverseSshConnectedStateTypeId, true); if (m_startingProcess.contains(process)) { emit actionExecutionFinished(m_startingProcess.value(process), Device::DeviceErrorNoError); m_startingProcess.remove(process); } break; case QProcess::NotRunning: if (device) device->setStateValue(reverseSshConnectedStateTypeId, false); if (m_startingProcess.contains(process)) { emit actionExecutionFinished(m_startingProcess.value(process), Device::DeviceErrorInvalidParameter); m_startingProcess.remove(process); } if (m_killingProcess.contains(process)) { emit actionExecutionFinished(m_killingProcess.value(process), Device::DeviceErrorNoError); m_reverseSSHProcess.remove(process); m_killingProcess.remove(process); } break; default: break; } }