Add remote ssh plugin
parent
6218d39a73
commit
f58b0c9573
|
|
@ -557,6 +557,21 @@ Description: nymea.io plugin for keba
|
|||
This package will install the nymea.io plugin for keba
|
||||
|
||||
|
||||
Package: nymea-plugin-remotessh
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
nymea-plugins-translations,
|
||||
Description: nymea.io plugin for remote ssh connection
|
||||
The nymea daemon is a plugin based IoT (Internet of Things) server. The
|
||||
server works like a translator for devices, things and services and
|
||||
allows them to interact.
|
||||
With the powerful rule engine you are able to connect any device available
|
||||
in the system and create individual scenes and behaviors for your environment.
|
||||
.
|
||||
This package will install the nymea.io plugin for remote ssh connection
|
||||
|
||||
|
||||
Package: nymea-plugins-translations
|
||||
Section: misc
|
||||
Architecture: all
|
||||
|
|
@ -611,6 +626,7 @@ Depends: nymea-plugin-commandlauncher,
|
|||
nymea-plugin-genericelements,
|
||||
nymea-plugin-avahimonitor,
|
||||
nymea-plugin-gpio,
|
||||
nymea-plugin-remotessh,
|
||||
Replaces: guh-plugins-maker
|
||||
Description: Plugins for nymea IoT server - Meta package for makers, tinkers and hackers
|
||||
The nymea daemon is a plugin based IoT (Internet of Things) server. The
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginremotessh.so
|
||||
|
|
@ -34,7 +34,8 @@ PLUGIN_DIRS = \
|
|||
gpio \
|
||||
snapd \
|
||||
simulation \
|
||||
keba \
|
||||
keba \
|
||||
remotessh \
|
||||
|
||||
|
||||
CONFIG+=all
|
||||
|
|
|
|||
|
|
@ -0,0 +1,205 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2017 Bernhard Trinnes <bernhard.trinnes@guh.io> *
|
||||
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* *
|
||||
* 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 *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "devicepluginremotessh.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
DevicePluginRemoteSsh::DevicePluginRemoteSsh()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DevicePluginRemoteSsh::init()
|
||||
{
|
||||
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
|
||||
connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginRemoteSsh::onPluginTimeout);
|
||||
}
|
||||
|
||||
DeviceManager::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 DeviceManager::DeviceSetupStatusSuccess;
|
||||
}
|
||||
return DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
DeviceManager::DeviceError DevicePluginRemoteSsh::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
if (device->deviceClassId() == reverseSshDeviceClassId ) {
|
||||
|
||||
if (action.actionTypeId() == reverseSshConnectedActionTypeId) {
|
||||
|
||||
if (action.param(reverseSshConnectedStateParamTypeId).value().toBool() == true) {
|
||||
QProcess *process = startReverseSSHProcess(device);
|
||||
m_reverseSSHProcess.insert(process, device);
|
||||
m_startingProcess.insert(process, action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
} else {
|
||||
QProcess *process = m_reverseSSHProcess.key(device);
|
||||
|
||||
// Check if the application is running...
|
||||
if (!process)
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
|
||||
if (process->state() == QProcess::NotRunning)
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
|
||||
process->kill();
|
||||
m_killingProcess.insert(process, action.id());
|
||||
return DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
return DeviceManager::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
return DeviceManager::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<QProcess*>(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(reverseSshLocalPortParamTypeId).toInt();
|
||||
int remotePort = device->paramValue(reverseSshRemotePortParamTypeId).toInt();
|
||||
QString user = device->paramValue(reverseSshUserParamTypeId).toString();
|
||||
QString password = device->paramValue(reverseSshPasswordParamTypeId).toString();
|
||||
QString address = device->paramValue(reverseSshAddressParamTypeId).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<QProcess*>(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<QProcess*>(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), DeviceManager::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), DeviceManager::DeviceErrorInvalidParameter);
|
||||
m_startingProcess.remove(process);
|
||||
}
|
||||
|
||||
if (m_killingProcess.contains(process)) {
|
||||
emit actionExecutionFinished(m_killingProcess.value(process), DeviceManager::DeviceErrorNoError);
|
||||
m_reverseSSHProcess.remove(process);
|
||||
m_killingProcess.remove(process);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2017 Bernhard Trinnes <bernhard.trinnes@guh.io> *
|
||||
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* *
|
||||
* 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 *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef DEVICEPLUGINREMOTESSH_H
|
||||
#define DEVICEPLUGINREMOTESSH_H
|
||||
|
||||
#include "plugintimer.h"
|
||||
#include "plugin/deviceplugin.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
class DevicePluginRemoteSsh : public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginremotessh.json")
|
||||
Q_INTERFACES(DevicePlugin)
|
||||
|
||||
public:
|
||||
explicit DevicePluginRemoteSsh();
|
||||
|
||||
void init() override;
|
||||
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
|
||||
private:
|
||||
QHash<QProcess *, Device *> m_reverseSSHProcess;
|
||||
QHash<QProcess *, Device *> m_sshKeyGenProcess;
|
||||
|
||||
QHash<QProcess *, ActionId> m_startingProcess;
|
||||
QHash<QProcess *, ActionId> m_killingProcess;
|
||||
|
||||
PluginTimer *m_pluginTimer = nullptr;
|
||||
|
||||
bool m_aboutToQuit = false;
|
||||
QString m_identityFilePath;
|
||||
|
||||
QProcess *startReverseSSHProcess(Device *device);
|
||||
|
||||
private slots:
|
||||
void onPluginTimeout();
|
||||
void processReadyRead();
|
||||
void processStateChanged(QProcess::ProcessState state);
|
||||
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINREMOTESSH_H
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"id": "cd75d899-3f53-43fa-9ee8-f6b36646a27d",
|
||||
"name": "RemoteSsh",
|
||||
"displayName": "Remote SSH",
|
||||
"vendors": [
|
||||
{
|
||||
"id": "e87ad7b1-1705-46b1-a962-282126646b4d",
|
||||
"name": "remoteAccess",
|
||||
"displayName": "Remote Access",
|
||||
"deviceClasses": [
|
||||
{
|
||||
"id": "a4f12741-4f30-40ca-a319-7f15e9c0c43a",
|
||||
"name": "reverseSsh",
|
||||
"displayName": "Reverse SSH",
|
||||
"deviceIcon": "Network",
|
||||
"createMethods": ["user"],
|
||||
"basicTags": ["Service"],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "92747d75-d18a-4915-bd48-0edd5cc5f19a",
|
||||
"name": "address",
|
||||
"displayName": "Address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"id": "7f7aa198-c719-415e-b31c-7a676b9d8e01",
|
||||
"name": "localPort",
|
||||
"displayName": "Local Port",
|
||||
"type": "int",
|
||||
"defaultValue": 22
|
||||
},
|
||||
{
|
||||
"id": "988aec42-1026-4aef-85d1-329ee1a34208",
|
||||
"name": "remotePort",
|
||||
"displayName": "Remote Port",
|
||||
"type": "int",
|
||||
"defaultValue": 2022
|
||||
},
|
||||
{
|
||||
"id": "c675f7ea-f94a-46e9-bf0f-92682182d6dd",
|
||||
"name": "user",
|
||||
"displayName": "User Name",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": "Enter your user"
|
||||
},
|
||||
{
|
||||
"id": "d8cc7177-bf35-4394-ab7b-881184bd8c8b",
|
||||
"name": "password",
|
||||
"displayName": "Password",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": "Enter your password"
|
||||
}
|
||||
],
|
||||
"stateTypes":[
|
||||
{
|
||||
"id": "19f079f0-1654-44c3-ab10-e7d7f9742e09",
|
||||
"name": "reachable",
|
||||
"displayName": "Server Reachable",
|
||||
"displayNameEvent": "reachable status changed",
|
||||
"type": "bool",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"id": "1ae425b2-d642-42ca-be41-4d06dff5c5cd",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"displayNameEvent": "Connected changed",
|
||||
"displayNameAction": "Connect to Server",
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "d8bb619e-6602-4c89-8654-85e111520561",
|
||||
"name": "sshKey",
|
||||
"displayName": "SSH public key",
|
||||
"displayNameEvent": "SSH key changed",
|
||||
"type": "QString",
|
||||
"defaultValue": "-"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
include(../plugins.pri)
|
||||
|
||||
TARGET = $$qtLibraryTarget(nymea_devicepluginremotessh)
|
||||
|
||||
SOURCES += \
|
||||
devicepluginremotessh.cpp \
|
||||
|
||||
HEADERS += \
|
||||
devicepluginremotessh.h \
|
||||
|
||||
Loading…
Reference in New Issue