Socket
Socket
Sign inDemoInstall

@mdslab/iotronic-lightning-rod

Package Overview
Dependencies
Maintainers
3
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mdslab/iotronic-lightning-rod - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0

device/Device.js

340

board-management.js

@@ -1,9 +0,18 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Andrea Rocco Lotronto, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################

@@ -16,2 +25,3 @@ //service logging configuration: "board-management"

var board_session = null;

@@ -22,23 +32,58 @@ var spawn = require('child_process').spawn;

exports.execute = function (command, label) {
cmd = command.split(' ');
logger.debug(label + ' COMMAND: ' + command);
var result = spawn(cmd[0], cmd.slice(1));
var exec = require('child_process').exec;
var Q = require("q");
result.stdout.on('data', function (data) {
logger.debug(label + ' stdout: ' + data);
});
result.stderr.on('data', function (data) {
if (command.indexOf('socat') > -1)
logger.info(label + ' stderr: ' + data);
else
logger.error(label + ' stderr: ' + data);
});
// This function contains the logic that has to be performed if I'm connected to the WAMP server
function manage_WAMP_connection(session) {
return result;
logger.info('[CONFIGURATION] - Board configuration starting...');
};
//EXPORTING NETWORK COMMANDS
var manageNetworks = require(LIGHTNINGROD_HOME + '/modules/vnets-manager/manage-networks');
manageNetworks.exportNetworkCommands(session);
//Topic on which the board can send a message to be registered
var connectionTopic = 'board.connection';
session.subscribe(connectionTopic, onTopicConnection);
//Registering the board to the Cloud by sending a message to the connection topic
logger.info("[WAMP] - Sending board ID '" + boardCode + "' to topic " + connectionTopic + " to register the board");
session.publish(connectionTopic, [boardCode, 'connection', session._id]);
}
// This function manages the messages published in "board.connection" topic
function onTopicConnection(args) {
var message = args[0];
if (message == 'IoTronic-connected')
logger.info("Message on board.connection: " + args[0])
}
// This function loads the Lightning-rod modules
function moduleLoader (session, device) {
// MODULES LOADING--------------------------------------------------------------------------------------------------
var manageGpio = require(LIGHTNINGROD_HOME + '/modules/gpio-manager/manage-gpio');
manageGpio.exportPins(session, lyt_device);
var managePlugins = require(LIGHTNINGROD_HOME + '/modules/plugins-manager/manage-plugins');
managePlugins.exportPluginCommands(session);
var driversManager = require(LIGHTNINGROD_HOME + "/modules/drivers-manager/manage-drivers");
driversManager.exportDriverCommands(session);
driversManager.restartDrivers();
var fsManager = require(LIGHTNINGROD_HOME + "/modules/vfs-manager/manage-fs");
fsManager.exportFSCommands(session);
var fsLibsManager = require(LIGHTNINGROD_HOME + "/modules/vfs-manager/manage-fs-libs");
fsLibsManager.exportFSLibs(session);
var manageServices = require(LIGHTNINGROD_HOME + '/modules/services-manager/manage-services');
manageServices.exportServiceCommands(session);
//------------------------------------------------------------------------------------------------------------------
}
// Init() LR function in order to control the correct LR configuration:

@@ -132,2 +177,3 @@ // - logging setup

// This function checks the settings correctness

@@ -161,10 +207,10 @@ exports.checkSettings = function (callback) {

port_reverse = nconf.get('config:reverse:server:port_reverse');
wstt_lib = nconf.get('config:reverse:lib:bin');
wstun_lib = nconf.get('config:reverse:lib:bin');
if ((url_reverse == undefined || url_reverse == "") || (port_reverse == undefined || port_reverse == "") || (wstt_lib == undefined || wstt_lib == "")) {
if ((url_reverse == undefined || url_reverse == "") || (port_reverse == undefined || port_reverse == "") || (wstun_lib == undefined || wstun_lib == "")) {
logger.warn('[SYSTEM] - WSTT configuration is wrong or not specified!');
logger.warn('[SYSTEM] - WSTUN configuration is wrong or not specified!');
logger.debug(' - url_reverse value: ' + url_reverse);
logger.debug(' - port_reverse value: ' + port_reverse);
logger.debug(' - wstt_lib value: ' + wstt_lib);
logger.debug(' - wstun_lib value: ' + wstun_lib);

@@ -179,2 +225,3 @@ process.exit();

device = nconf.get('config:device');
if (device == undefined || device == "") {

@@ -210,3 +257,3 @@ logger.warn('[SYSTEM] - Device "' + device + '" not supported!');

if (board_position == undefined || Object.keys(board_position).length === 0) {
if ( (board_position == undefined || Object.keys(board_position).length === 0 ) && (reg_status == "registered") ) {
logger.warn('[SYSTEM] - Wrong board coordinates! Set status to "new" to retrive these information.');

@@ -248,77 +295,45 @@ logger.debug('- Coordinates: ' + JSON.stringify(board_position));

function onTopicConnection(args) {
var message = args[0];
if (message == 'Iotronic-connected')
logger.info("Message on board.connection: " + args[0])
// This function sets the coordinates of the device: called by Iotronic via RPC
exports.setBoardPosition = function (args) {
}
var board_position = args[0];
logger.info("[SYSTEM] - Set board position: " + JSON.stringify(board_position));
//This function contains the logic that has to be performed if I'm connected to the WAMP server
exports.manage_WAMP_connection = function (session, details) {
var configFile = JSON.parse(fs.readFileSync(SETTINGS, 'utf8'));
var board_config = configFile.config["board"];
logger.info("[SYSTEM] --> BOARD CONFIGURATION " + JSON.stringify(board_config));
logger.info('[CONFIGURATION] - Registered board configuration starting...');
//EXPORTING NETWORK COMMANDS
var manageNetworks = require(LIGHTNINGROD_HOME + '/modules/vnets-manager/manage-networks');
manageNetworks.exportNetworkCommands(session);
board_config["position"] = board_position;
logger.info("[SYSTEM] --> BOARD POSITION UPDATED: " + JSON.stringify(board_config["position"]));
//Topic on which the board can send a message to be registered
var connectionTopic = 'board.connection';
session.subscribe(connectionTopic, onTopicConnection);
//Registering the board to the Cloud by sending a message to the connection topic
logger.info('[WAMP] - Sending board ID ' + boardCode + ' to topic ' + connectionTopic + ' to register the board');
session.publish(connectionTopic, [boardCode, 'connection', session._id]);
//Updates the settings.json file
fs.writeFile(SETTINGS, JSON.stringify(configFile, null, 4), function (err) {
if (err) {
logger.error('[SYSTEM] --> Error writing settings.json file: ' + err);
} else {
logger.debug("[SYSTEM] --> settings.json configuration file saved to " + SETTINGS);
}
});
return "Board configuration file updated!";
/*
//Topic on which the board can listen for commands
var commandTopic = 'board.command';
//Subscribing to the command topic to receive messages for asyncronous operation to be performed
//Maybe everything can be implemented as RPCs
//Right now the onCommand method of the manageCommands object is invoked as soon as a message is received on the topic
logger.info('[WAMP] - Registering to command topic ' + commandTopic);
var manageCommands = require(LIGHTNINGROD_HOME + '/modules/services-manager/manage-commands');
session.subscribe(commandTopic, manageCommands.onCommand);
*/
//If I'm connected to the WAMP server I can export my pins on the Cloud as RPCs
var managePins = require(LIGHTNINGROD_HOME + '/modules/gpio-manager/manage-pins');
managePins.exportPins(session);
//If I'm connected to the WAMP server I can receive plugins to be scheduled as RPCs
var managePlugins = require(LIGHTNINGROD_HOME + '/modules/plugins-manager/manage-plugins');
managePlugins.exportPluginCommands(session);
//If I'm connected to the WAMP server I can receive RPC command requests to manage drivers
var driversManager = require(LIGHTNINGROD_HOME + "/modules/drivers-manager/manage-drivers");
driversManager.exportDriverCommands(session);
driversManager.restartDrivers();
//If I'm connected to the WAMP server I can receive RPC command requests to manage FUSE filesystem
var fsManager = require(LIGHTNINGROD_HOME + "/modules/vfs-manager/manage-fs");
fsManager.exportFSCommands(session);
var fsLibsManager = require(LIGHTNINGROD_HOME + "/modules/vfs-manager/manage-fs-libs");
fsLibsManager.exportFSLibs(session);
var manageServices = require(LIGHTNINGROD_HOME + '/modules/services-manager/manage-services');
manageServices.exportServiceCommands(session);
};
// This function sets the coordinates of the device: called by Iotronic via RPC
exports.setBoardPosition = function (args) {
/*
// This function sets the coordinates of the device: called by IoTronic via RPC
exports.updateConf = function (args) {
var board_position = args[0];
logger.info("[SYSTEM] - Set board position: " + JSON.stringify(board_position));
var remote_conf = args[0];
var board_position = args[1];
logger.info("[SYSTEM] - Set board configuration: " + JSON.stringify(args));
var configFile = JSON.parse(fs.readFileSync(SETTINGS, 'utf8'));
var board_config = configFile.config["board"];
logger.info("[SYSTEM] --> BOARD CONFIGURATION " + JSON.stringify(board_config));
board_config["remote_conf"] = remote_conf;
board_config["position"] = board_position;
logger.info("[SYSTEM] --> BOARD POSITION UPDATED: " + JSON.stringify(board_config["position"]));
logger.info("[SYSTEM] --> BOARD CONF UPDATED: " + JSON.stringify(board_config));

@@ -338,4 +353,5 @@ //Updates the settings.json file

};
*/
// This function manages the wrong/unregistered status of the board
// This function manages the registration status of the board
exports.checkRegistrationStatus = function(args){

@@ -345,15 +361,98 @@

logger.error("[SYSTEM] - Connection to Iotronic "+response.result+" - "+response.message);
logger.info("[SYSTEM] - Bye");
if(response.result == "SUCCESS"){
process.exit();
logger.info("[SYSTEM] - Connection to Iotronic "+response.result+" - "+response.message);
// CONNECTION TO IoTronic after access granted.
var configFile = JSON.parse(fs.readFileSync(SETTINGS, 'utf8'));
var board_config = configFile.config["board"];
logger.info("[CONFIGURATION] - Board configuration parameters: " + JSON.stringify(board_config));
//PROVISIONING: Iotronic sends coordinates to this device when its status is "new"
if(reg_status === "new"){
logger.info('[CONFIGURATION] - NEW BOARD CONFIGURATION STARTED... ');
board_session.call("s4t.board.provisioning", [boardCode]).then(
function(result){
logger.info("\n\nPROVISIONING "+boardCode+" RECEIVED: " + JSON.stringify(result) + "\n\n");
board_position = result.message.info.coordinates;
board_config["position"] = board_position;
board_config["status"] = "registered";
logger.info("\n[CONFIGURATION] - BOARD POSITION UPDATED: " + JSON.stringify(board_config["position"]));
//Updates the settings.json file
fs.writeFile(SETTINGS, JSON.stringify(configFile, null, 4), function(err) {
if(err) {
logger.error('Error writing settings.json file: ' + err);
} else {
logger.info("settings.json configuration file saved to " + SETTINGS);
//Calling the manage_WAMP_connection function that contains the logic that has to be performed if the device is connected to the WAMP server
//exports.manage_WAMP_connection(board_session, details);
moduleLoader(board_session, lyt_device);
}
});
//Create a backup file of settings.json
fs.writeFile(SETTINGS + ".BKP", JSON.stringify(configFile, null, 4), function(err) {
if(err) {
logger.error('Error writing settings.json.BKP file: ' + err);
} else {
logger.info("settings.json.BKP configuration file saved to " + SETTINGS + ".BKP");
}
});
},
function (error){
logger.error('[WAMP] - Error board provisioning: ' + JSON.stringify(error));
logger.info("Bye!");
process.exit(1);
}
);
} else if(reg_status === "registered"){
moduleLoader(board_session, lyt_device);
} else{
logger.error('[CONFIGURATION] - WRONG BOARD STATUS: status allowed "new" or "registerd"!');
process.exit();
}
}else {
// IF access to IoTronic was rejected
logger.error("[SYSTEM] - Connection to Iotronic "+response.result+" - "+response.message);
logger.info("[SYSTEM] - Bye");
process.exit();
}
};
var exec = require('child_process').exec;
var Q = require("q");
// Reboot LR
// To execute pre-defined commands in the board shell
exports.execAction = function(args){

@@ -375,2 +474,7 @@

logger.info('[SYSTEM] - Rebooting...');
response.message = "Rebooting";
response.result = "SUCCESS";
d.resolve(response);
exec('reboot', function(error, stdout, stderr){

@@ -402,4 +506,35 @@

});
break;
case 'hostname':
exec('echo `hostname`', function(error, stdout, stderr){
if(error) {
logger.error('[SYSTEM] - Echo result: ' + error);
response.message = error;
response.result = "ERROR";
d.resolve(response);
}
else if (stderr){
if (stderr == "")
stderr = "Doing echo...";
logger.info('[SYSTEM] - Echo result: ' + stderr);
response.message = stderr;
response.result = "WARNING";
d.resolve(response);
}
else{
stdout = stdout.replace(/\n$/, '');
logger.info('[SYSTEM] - Echo result: ' + stdout);
response.message = stdout;
response.result = "SUCCESS";
d.resolve(response);
}
});
break;
default:

@@ -425,10 +560,17 @@

exports.exportManagementCommands = function (session) {
board_session = session;
//Register all the module functions as WAMP RPCs
logger.info('[WAMP-EXPORTS] Management commands exported to the cloud!');
session.register(boardCode + '.command.setBoardPosition', exports.setBoardPosition);
session.register(boardCode + '.command.checkRegistrationStatus', exports.checkRegistrationStatus);
session.register(boardCode + '.command.execAction', exports.execAction);
session.register('s4t.' + boardCode + '.board.setBoardPosition', exports.setBoardPosition);
session.register('s4t.' + boardCode + '.board.checkRegistrationStatus', exports.checkRegistrationStatus);
session.register('s4t.' + boardCode + '.board.execAction', exports.execAction);
//session.register('s4t.' + boardCode + '.board.updateConf', exports.updateConf);
manage_WAMP_connection(session)
};
};

@@ -14,6 +14,6 @@ # Arduino YUN/Linino ONE installation guide

##### Install dependencies via opkg
```
opkg update
opkg install logrotate nano git unzip socat ip dsniff fuse-utils node-autobahn node-jsonfile node-nconf node-reverse-wstunnel node-tty.js node-ideino-linino-lib node-fuse-bindings node-mknod node-statvfs
opkg install logrotate ntpdate nano git unzip socat ip dsniff fuse-utils node-autobahn node-jsonfile node-nconf node-ideino-linino-lib node-fuse-bindings node-mknod node-statvfs wstun
opkg install kmod-gre kmod-ip6-tunnel kmod-iptunnel4 kmod-iptunnel6 kmod-ipv6 kmod-tun
```

@@ -23,24 +23,8 @@

```
npm install -g --skip-installed --unsafe iotronic-lightning-rod
npm install -g --unsafe @mdslab/iotronic-lightning-rod
```
If you get some problems during npm dependencies installation phase we suggest you to follow the "Install from source-code" procedure.
If you have some problems during npm dependencies installation phase we suggest you to follow the "Install from source-code" procedure.
##### Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
$NODE_PATH/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: [ "arduino_yun" | "server" | "raspberry_pi"]
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server IP
```
## Install from source-code

@@ -50,3 +34,3 @@

```
npm install -g requestify is-running connection-tester log4js q fs-access util
npm install -g requestify is-running connection-tester@0.1.2 log4js@1.1.1 q fs-access util
```

@@ -57,11 +41,14 @@

mkdir /var/lib/iotronic/ && cd /var/lib/iotronic/
mkdir plugins && mkdir drivers
cd /usr/lib/node_modules/@mdslab/
mkdir /usr/lib/node_modules/@mdslab/
git clone git://github.com/MDSLab/s4t-lightning-rod.git
mv s4t-lightning-rod iotronic-lightning-rod
mkdir plugins && mkdir drivers
cp /var/lib/iotronic/iotronic-lightning-rod/etc/init.d/s4t-lightning-rod_yun /etc/init.d/lightning-rod
sed -i "s/<LIGHTNINGROD_HOME>/export LIGHTNINGROD_HOME=\/var\/lib\/iotronic\/iotronic-lightning-rod/g" /etc/init.d/lightning-rod
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/etc/init.d/s4t-lightning-rod_yun /etc/init.d/lightning-rod
sed -i "s/<LIGHTNINGROD_HOME>/export LIGHTNINGROD_HOME=\/usr\/lib\/node_modules\/@mdslab\/iotronic-lightning-rod/g" /etc/init.d/lightning-rod
chmod +x /etc/init.d/lightning-rod
cp /var/lib/iotronic/iotronic-lightning-rod/etc/logrotate.d/lightning-rod.log /etc/logrotate.d/lightning-rod.log
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/etc/logrotate.d/lightning-rod.log /etc/logrotate.d/lightning-rod.log

@@ -72,19 +59,24 @@ mkdir /var/log/iotronic/

echo "export IOTRONIC_HOME=/var/lib/iotronic" >> /etc/profile
echo "export LIGHTNINGROD_HOME=/var/lib/iotronic/iotronic-lightning-rod" >> /etc/profile
echo "export LIGHTNINGROD_HOME=/usr/lib/node_modules/@mdslab/iotronic-lightning-rod" >> /etc/profile
source /etc/profile
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/plugins-manager/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/drivers-manager/drivers.example.json /var/lib/iotronic/drivers/drivers.json
```
##### Configure Lightning-rod
Note that you need the NODE_ID that is the code returned by the IoTronic service after node registration.
## Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
cp /var/lib/iotronic/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /var/lib/iotronic/iotronic-lightning-rod/modules/plugins-manager/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /var/lib/iotronic/iotronic-lightning-rod/modules/drivers-manager/drivers.example.json /var/lib/iotronic/drivers/drivers.json
$NODE_PATH/@mdslab/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: "arduino_yun"
sed -i "s/\"device\":.*\"\"/\"device\": \"arduino_yun\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"code\":.*\"\"/\"code\": \"<NODE_ID>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"bin\":.*\"\"/\"bin\": \"\/usr\/lib\/node_modules\/node-reverse-wstunnel\/bin\/wstt.js\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_wamp\":.*\"\"/\"url_wamp\": \"ws:\/\/<IOTRONIC-SERVER-IP>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_reverse\":.*\"\"/\"url_reverse\": \"ws:\/\/<IOTRONIC-SERVER-IP>\"/g" /var/lib/iotronic/settings.json
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server URL
```

@@ -95,3 +87,3 @@

/etc/init.d/cron stop
cp /var/lib/iotronic/iotronic-lightning-rod/etc/cron.d/root_yun /etc/crontabs/root
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/etc/cron.d/root_yun /etc/crontabs/root
/etc/init.d/cron start

@@ -107,3 +99,1 @@ ```

```

@@ -20,3 +20,3 @@ # Bundled distribution

- `nconf` - loads stores from a filesystem
- `node-reverse-wstunnel` - is used by LR as a program, not as a CommonJS module.
- `wstun` - is used by LR as a program, not as a CommonJS module.
- `ideino-linino-lib` - Arduino Yun specific - not needed on other boards. Must be installed

@@ -23,0 +23,0 @@ manually.

@@ -7,9 +7,8 @@ # Raspberry Pi 2 installation guide

#### Install dependencies via apt-get
##### Install dependencies via apt-get
```
apt-get install python unzip socat dsniff fuse libfuse-dev pkg-config
apt -y install unzip socat dsniff fuse libfuse-dev pkg-config python git ntpdate
```
#### Install NodeJS 7.x
##### Install latest NodeJS 7.x release
```

@@ -23,9 +22,5 @@ curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -

npm -v
```
#### Configure npm NODE_PATH variable
```
echo "export NODE_PATH=/usr/lib/node_modules" | tee -a /etc/profile
source /etc/profile > /dev/null
echo "NODE_PATH=/usr/lib/node_modules" | tee -a /etc/environment
source /etc/environment > /dev/null
echo $NODE_PATH

@@ -37,69 +32,68 @@ ```

```
npm install -g --unsafe iotronic-lightning-rod
npm install -g --unsafe @mdslab/iotronic-lightning-rod
npm install -g --unsafe @mdslab/wstun
```
##### Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
$NODE_PATH/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: [ "arduino_yun" | "server" | "raspberry_pi"]
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server IP
```
## Install from source-code
##### Install required NodeJS modules via npm
##### Install required NodeJS modules via npm:
```
npm install -g npm
npm install -g gyp autobahn jsonfile nconf node-reverse-wstunnel tty.js fuse-bindings requestify is-running connection-tester log4js q secure-keys fs-access mknod util path
# Install statvfs node module compliant with NodeJS 7.x:
npm install -g https://github.com/PlayNetwork/node-statvfs/tarball/v3.0.0
npm install -g --unsafe gyp autobahn nconf @mdslab/wstun fuse-bindings requestify is-running connection-tester log4js@1.1.1 q fs-access mknod jsonfile
npm install -g --unsafe https://github.com/PlayNetwork/node-statvfs/tarball/v3.0.0
```
##### Install Lightning-rod
##### Install the Lightning-rod
```
mkdir /var/lib/iotronic/ && cd /var/lib/iotronic/
mkdir plugins && mkdir drivers
mkdir drivers/mountpoints/
cd /usr/lib/node_modules/
git clone git://github.com/MDSLab/s4t-lightning-rod.git
mv s4t-lightning-rod iotronic-lightning-rod
mkdir plugins && mkdir drivers
cp /var/lib/iotronic/iotronic-lightning-rod/etc/systemd/system/s4t-lightning-rod.service /etc/systemd/system/lightning-rod.service
sed -i "s/Environment=\"LIGHTNINGROD_HOME=\"/Environment=\"LIGHTNINGROD_HOME=\/var\/lib\/iotronic\/iotronic-lightning-rod\"/g" /etc/systemd/system/lightning-rod.service
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/etc/systemd/system/s4t-lightning-rod.service /etc/systemd/system/lightning-rod.service
sed -i "s/Environment=\"LIGHTNINGROD_HOME=\"/Environment=\"LIGHTNINGROD_HOME=\/usr\/lib\/node_modules\/@mdslab\/iotronic-lightning-rod\"/g" /etc/systemd/system/lightning-rod.service
chmod +x /etc/systemd/system/lightning-rod.service
systemctl daemon-reload
mkdir /var/log/iotronic/
touch /var/log/iotronic/lightning-rod.log
echo "export IOTRONIC_HOME=/var/lib/iotronic >> /etc/profile
echo "export LIGHTNINGROD_HOME=/var/lib/iotronic/iotronic-lightning-rod >> /etc/profile
source /etc/profile
echo "IOTRONIC_HOME=/var/lib/iotronic" | tee -a /etc/environment
echo "LIGHTNINGROD_HOME=/usr/lib/node_modules/@mdslab/iotronic-lightning-rod" | tee -a /etc/environment
source /etc/environment > /dev/null
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/plugins-manager/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/drivers-manager/drivers.example.json /var/lib/iotronic/drivers/drivers.json
```
##### Configure and start the Lightning-rod
Note that you need the <NODE_ID> that is the code returned by the IoTronic service after node registration:
## Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
cp /var/lib/iotronic/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/iotronic-lightning-rod/settings.json
cp /var/lib/iotronic/iotronic-lightning-rod/plugins.example.json /var/lib/iotronic/iotronic-lightning-rod/plugins/plugins.json
cp /var/lib/iotronic/iotronic-lightning-rod/drivers.example.json /var/lib/iotronic/iotronic-lightning-rod/drivers/drivers.json
$NODE_PATH/@mdslab/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: "raspberry_pi"
sed -i "s/\"device\":.*\"\"/\"device\": \"raspberry_pi\"/g" /var/lib/iotronic/iotronic-lightning-rod/settings.json
sed -i "s/\"code\":.*\"\"/\"code\": \"<NODE_ID>\"/g" /var/lib/iotronic/iotronic-lightning-rod/settings.json
sed -i "s/\"bin\":.*\"\"/\"bin\": \"\/usr\/lib\/node_modules\/node-reverse-wstunnel\/bin\/wstt.js\"/g" /var/lib/iotronic/iotronic-lightning-rod/settings.json
sed -i "s/\"url_wamp\":.*\"\"/\"url_wamp\": \"ws:\/\/<IOTRONIC-SERVER-IP>\"/g" /var/lib/iotronic/iotronic-lightning-rod/settings.json
sed -i "s/\"url_reverse\":.*\"\"/\"url_reverse\": \"ws:\/\/<IOTRONIC-SERVER-IP>\"/g" /var/lib/iotronic/iotronic-lightning-rod/settings.json
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server URL
```
Add ENV variables
```
echo "IOTRONIC_HOME=/var/lib/iotronic" | tee -a /etc/environment
echo "LIGHTNINGROD_HOME=/usr/lib/node_modules/@mdslab/iotronic-lightning-rod" | tee -a /etc/environment
##### Configure logrotate
source /etc/environment > /dev/null
```
## Configure logrotate
nano /etc/logrotate.d/lightning-rod.log

@@ -116,2 +110,3 @@ ```

## Start Lightning-rod

@@ -118,0 +113,0 @@ ```

# Raspberry Pi 3 installation guide
We tested this procedure on a ubuntu-16.04-preinstalled-server. Everything needs to be run as root.
## Install OS distribution "ubuntu-16.04-preinstalled-server"

@@ -15,9 +16,6 @@ ```

```
sudo apt update
sudo apt upgrade
sudo reboot
apt -y install unzip socat dsniff fuse libfuse-dev pkg-config
apt -y install unzip socat dsniff fuse libfuse-dev pkg-config python git ntpdate
```
##### Install NodeJS 7.x
##### Install latest NodeJS 7.x release
```

@@ -27,84 +25,81 @@ curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -

node -v
```
##### Configure npm NODE_PATH variable
```
echo "export NODE_PATH=/usr/lib/node_modules" | tee -a
source /etc/profile > /dev/null
npm install -g npm
npm config set python `which python2.7`
npm -v
echo "NODE_PATH=/usr/lib/node_modules" | tee -a /etc/environment
source /etc/environment > /dev/null
echo $NODE_PATH
```
## Install from NPM
```
npm install -g --skip-installed --unsafe iotronic-lightning-rod
npm install -g --unsafe @mdslab/iotronic-lightning-rod
npm install -g --unsafe @mdslab/wstun
```
##### Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
$NODE_PATH/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: [ "arduino_yun" | "server" | "raspberry_pi"]
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server IP
```
## Install from source-code
##### Install required NodeJS modules via npm
##### Install required NodeJS modules via npm:
```
npm install -g npm
npm install -g gyp autobahn jsonfile nconf node-reverse-wstunnel tty.js fuse-bindings requestify is-running connection-tester log4js q secure-keys fs-access mknod util path
# Install statvfs node module compliant with NodeJS 7.x:
npm install -g https://github.com/PlayNetwork/node-statvfs/tarball/v3.0.0
npm install -g --unsafe gyp autobahn nconf @mdslab/wstun fuse-bindings requestify is-running connection-tester log4js@1.1.1 q fs-access mknod jsonfile
npm install -g --unsafe https://github.com/PlayNetwork/node-statvfs/tarball/v3.0.0
```
##### Install the Lightning-rod
```
mkdir /var/lib/iotronic/ && cd /var/lib/iotronic/
mkdir plugins && mkdir drivers
mkdir drivers/mountpoints/
cd /usr/lib/node_modules/
git clone git://github.com/MDSLab/s4t-lightning-rod.git
mv s4t-lightning-rod iotronic-lightning-rod
mkdir plugins && mkdir drivers
cp /var/lib/iotronic/iotronic-lightning-rod/etc/systemd/system/s4t-lightning-rod.service /etc/systemd/system/lightning-rod.service
sed -i "s/Environment=\"LIGHTNINGROD_HOME=\"/Environment=\"LIGHTNINGROD_HOME=\/var\/lib\/iotronic\/iotronic-lightning-rod\"/g" /etc/systemd/system/lightning-rod.service
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/etc/systemd/system/s4t-lightning-rod.service /etc/systemd/system/lightning-rod.service
sed -i "s/Environment=\"LIGHTNINGROD_HOME=\"/Environment=\"LIGHTNINGROD_HOME=\/usr\/lib\/node_modules\/@mdslab\/iotronic-lightning-rod\"/g" /etc/systemd/system/lightning-rod.service
chmod +x /etc/systemd/system/lightning-rod.service
systemctl daemon-reload
mkdir /var/log/iotronic/
touch /var/log/iotronic/lightning-rod.log
echo "export IOTRONIC_HOME=/var/lib/iotronic/iotronic-lightning-rod" >> /etc/profile
echo "export LIGHTNINGROD_HOME=$IOTRONIC_HOME/lightning-rod" >> /etc/profile
source /etc/profile
echo "IOTRONIC_HOME=/var/lib/iotronic" | tee -a /etc/environment
echo "LIGHTNINGROD_HOME=/usr/lib/node_modules/@mdslab/iotronic-lightning-rod" | tee -a /etc/environment
source /etc/environment > /dev/null
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/plugins-manager/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/drivers-manager/drivers.example.json /var/lib/iotronic/drivers/drivers.json
```
##### Configure and start the Lightning-rod
Note that you need the NODE_ID that is the code returned by the IoTronic service after node registration.
## Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
cp /var/lib/iotronic/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /var/lib/iotronic/iotronic-lightning-rod/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /var/lib/iotronic/iotronic-lightning-rod/drivers.example.json /var/lib/iotronic/drivers/drivers.json
$NODE_PATH/@mdslab/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: "raspberry_pi"
sed -i "s/\"device\":.*\"\"/\"device\": \"raspberry_pi\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"code\":.*\"\"/\"code\": \"<NODE_ID>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"bin\":.*\"\"/\"bin\": \"\/usr\/lib\/node_modules\/node-reverse-wstunnel\/bin\/wstt.js\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_wamp\":.*\"\"/\"url_wamp\": \"ws:\/\/<IOTRONIC-SERVER-IP>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_reverse\":.*\"\"/\"url_reverse\": \"ws:\/\/<IOTRONIC-SERVER-IP>\"/g" /var/lib/iotronic/settings.json
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server URL
```
Add ENV variables
```
echo "IOTRONIC_HOME=/var/lib/iotronic" | tee -a /etc/environment
echo "LIGHTNINGROD_HOME=/usr/lib/node_modules/@mdslab/iotronic-lightning-rod" | tee -a /etc/environment
##### Configure logrotate
source /etc/environment > /dev/null
```
## Configure logrotate
nano /etc/logrotate.d/lightning-rod.log

@@ -121,2 +116,3 @@ ```

## Start Lightning-rod

@@ -129,3 +125,3 @@ ```

tail -f /var/log/iotronic/lightning-rod.log
```
```

@@ -8,14 +8,16 @@ # IoTronic Lightning-rod installation guide for Ubuntu 14.04

```
apt -y install nodejs npm nodejs-legacy unzip socat dsniff fuse libfuse-dev pkg-config
apt -y install unzip socat dsniff fuse libfuse-dev pkg-config python git ntpdate
```
##### Install required NodeJS modules via npm
##### Install latest NodeJS 7.x release
```
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
apt-get install -y nodejs
node -v
npm install -g npm
npm install -g gyp autobahn jsonfile nconf node-reverse-wstunnel tty.js fuse-bindings requestify is-running connection-tester log4js q secure-keys
```
npm config set python `which python2.7`
npm -v
##### Configure npm NODE_PATH variable
```
echo "NODE_PATH=/usr/local/lib/node_modules" | sudo tee -a /etc/environment
echo "NODE_PATH=/usr/lib/node_modules" | tee -a /etc/environment
source /etc/environment > /dev/null

@@ -25,30 +27,19 @@ echo $NODE_PATH

## Install from NPM
```
npm install -g --skip-installed --unsafe iotronic-lightning-rod
```
npm install -g --unsafe @mdslab/wstun
npm install -g --unsafe @mdslab/iotronic-lightning-rod
##### Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
$NODE_PATH/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: [ "arduino_yun" | "server" | "raspberry_pi"]
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
## Install from source-code
* WAMP server IP
##### Install required NodeJS modules via npm:
```
npm install -g --unsafe gyp autobahn nconf @mdslab/wstun fuse-bindings requestify is-running connection-tester log4js@1.1.1 q fs-access mknod jsonfile
npm install -g --unsafe https://github.com/PlayNetwork/node-statvfs/tarball/v3.0.0
```
## Install from source-code
##### Install Lightning-rod

@@ -71,18 +62,26 @@ ```

source /etc/environment > /dev/null
```
##### Configure Lightning-rod
Note that you will need the IP address of a working instance of a WAMP router (<WAMP_IP>), the IP address of a working instance of a Websocket reverse tunnel server (<WS_IP>), and the UUID of the node that you need to have previously registered on the IoTronic (<NODE_UUID>). Also, note that if while installing the IoTronic service, you configured a custom port and realm name for the WAMP router or a custom port for the Websocket reverse tunnel server, you will need to manually change the setting.json, accordingly.
```
cp /var/lib/iotronic/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /var/lib/iotronic/iotronic-lightning-rod/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /var/lib/iotronic/iotronic-lightning-rod/drivers.example.json /var/lib/iotronic/drivers/drivers.json
```
sed -i "s/\"device\":.*\"\"/\"device\": \"laptop\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"code\":.*\"\"/\"code\": \"<NODE_UUID>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"bin\":.*\"\"/\"bin\": \"\/usr\/local\/lib\/node_modules\/node-reverse-wstunnel\/bin\/wstt.js\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_wamp\":.*\"\"/\"url_wamp\": \"ws:\/\/<WAMP_IP>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_reverse\":.*\"\"/\"url_reverse\": \"ws:\/\/<WS_IP>\"/g" /var/lib/iotronic/settings.json
## Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
$NODE_PATH/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: "server"
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server IP
```
##### Configure logrotate

@@ -89,0 +88,0 @@ nano /etc/logrotate.d/lightning-rod.log

@@ -10,5 +10,6 @@ # IoTronic Lightning-rod installation guide for Ubuntu 16.04

```
apt -y install unzip socat dsniff fuse libfuse-dev pkg-config python
apt -y install unzip socat dsniff fuse libfuse-dev pkg-config python git ntpdate
```
##### Install latest NodeJS release
##### Install latest NodeJS 7.x release
```

@@ -31,29 +32,8 @@ curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -

```
npm install -g --unsafe iotronic-lightning-rod
npm install -g --unsafe @mdslab/iotronic-lightning-rod
npm install -g --unsafe @mdslab/wstun
```
##### Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
$NODE_PATH/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: [ "arduino_yun" | "server" | "raspberry_pi"]
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server IP
```
Add ENV variables
```
echo "IOTRONIC_HOME=/var/lib/iotronic" | tee -a /etc/environment
echo "LIGHTNINGROD_HOME=/usr/lib/node_modules/iotronic-lightning-rod" | tee -a /etc/environment
source /etc/environment > /dev/null
```
## Install from source-code

@@ -63,3 +43,3 @@

```
npm install -g --unsafe gyp autobahn jsonfile nconf node-reverse-wstunnel tty.js fuse-bindings requestify is-running connection-tester log4js@1.1.1 q secure-keys fs-access mknod
npm install -g --unsafe gyp autobahn nconf @mdslab/wstun fuse-bindings requestify is-running connection-tester log4js@1.1.1 q fs-access mknod jsonfile
npm install -g --unsafe https://github.com/PlayNetwork/node-statvfs/tarball/v3.0.0

@@ -71,33 +51,44 @@ ```

mkdir /var/lib/iotronic/ && cd /var/lib/iotronic/
mkdir plugins && mkdir drivers
mkdir drivers/mountpoints/
cd /usr/lib/node_modules/
git clone git://github.com/MDSLab/s4t-lightning-rod.git
mv s4t-lightning-rod iotronic-lightning-rod
mkdir plugins && mkdir drivers
cp /var/lib/iotronic/iotronic-lightning-rod/etc/systemd/system/s4t-lightning-rod.service /etc/systemd/system/lightning-rod.service
sed -i "s/Environment=\"LIGHTNINGROD_HOME=\"/Environment=\"LIGHTNINGROD_HOME=\/var\/lib\/iotronic\/iotronic-lightning-rod\"/g" /etc/systemd/system/lightning-rod.service
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/etc/systemd/system/s4t-lightning-rod.service /etc/systemd/system/lightning-rod.service
sed -i "s/Environment=\"LIGHTNINGROD_HOME=\"/Environment=\"LIGHTNINGROD_HOME=\/usr\/lib\/node_modules\/@mdslab\/iotronic-lightning-rod\"/g" /etc/systemd/system/lightning-rod.service
chmod +x /etc/systemd/system/lightning-rod.service
systemctl daemon-reload
mkdir /var/log/iotronic/
touch /var/log/iotronic/lightning-rod.log
echo "IOTRONIC_HOME=/var/lib/iotronic/iotronic-lightning-rod" | tee -a /etc/environment
echo "LIGHTNINGROD_HOME=$IOTRONIC_HOME/lightning-rod" | tee -a /etc/environment
echo "IOTRONIC_HOME=/var/lib/iotronic" | tee -a /etc/environment
echo "LIGHTNINGROD_HOME=/usr/lib/node_modules/@mdslab/iotronic-lightning-rod" | tee -a /etc/environment
source /etc/environment > /dev/null
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/plugins-manager/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /usr/lib/node_modules/@mdslab/iotronic-lightning-rod/modules/drivers-manager/drivers.example.json /var/lib/iotronic/drivers/drivers.json
```
##### Configure Lightning-rod
Note that you will need the IP address of a working instance of a WAMP router (<WAMP_IP>), the IP address of a working instance of a Websocket reverse tunnel server (<WS_IP>), and the UUID of the node that you need to have previously registered on the IoTronic (<NODE_UUID>). Also, note that if while installing the IoTronic service, you configured a custom port and realm name for the WAMP router or a custom port for the Websocket reverse tunnel server, you will need to manually change the setting.json, accordingly.
## Configure Lightning-rod
At the end of the installation process we have to execute the LR configuration script:
```
cp /var/lib/iotronic/iotronic-lightning-rod/settings.example.json /var/lib/iotronic/settings.json
cp /var/lib/iotronic/iotronic-lightning-rod/modules/plugins-manager/plugins.example.json /var/lib/iotronic/plugins/plugins.json
cp /var/lib/iotronic/iotronic-lightning-rod/modules/drivers-manager/drivers.example.json /var/lib/iotronic/drivers/drivers.json
$NODE_PATH/@mdslab/iotronic-lightning-rod/scripts/lr_configure.sh
```
This script asks the following information:
```
* device type: "server"
sed -i "s/\"device\":.*\"\"/\"device\": \"server\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"code\":.*\"\"/\"code\": \"<NODE_UUID>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"bin\":.*\"\"/\"bin\": \"\/usr\/lib\/node_modules\/node-reverse-wstunnel\/bin\/wstt.js\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_wamp\":.*\"\"/\"url_wamp\": \"ws:\/\/<WAMP_IP>\"/g" /var/lib/iotronic/settings.json
sed -i "s/\"url_reverse\":.*\"\"/\"url_reverse\": \"ws:\/\/<WS_IP>\"/g" /var/lib/iotronic/settings.json
* Board ID: UUID released by the registration process managed by IoTronic.
* IoTronic server IP
* WAMP server URL
```
## Configure logrotate

@@ -104,0 +95,0 @@ nano /etc/logrotate.d/lightning-rod.log

@@ -1,12 +0,21 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2014 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Andrea Rocco Lotronto, Arthur Warnier, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Andrea Rocco Lotronto,
//# Giovanni Merlino, Arthur Warnier, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
// LIBRARIES

@@ -32,2 +41,3 @@ var fs = require("fs");

device = null; //valued in checkSettings
lyt_device = null; //valued here in main

@@ -50,2 +60,4 @@

//Init_Ligthning_Rod(function (check) {

@@ -74,48 +86,40 @@ manageBoard.Init_Ligthning_Rod(function (check) {

logger.info("[SYSTEM] - Iotronic server IP: "+wampIP);
logger.info('[SYSTEM] - DEVICE: ' + device);
var connectionTester = require('connection-tester');
//----------------------------------------
// 1. Set WAMP connection configuration
//----------------------------------------
var wampUrl = nconf.get('config:wamp:url_wamp')+":"+nconf.get('config:wamp:port_wamp')+"/ws";
var wampRealm = nconf.get('config:wamp:realm');
var wampConnection = new autobahn.Connection({
url: wampUrl,
realm: wampRealm,
max_retries: -1
});
connectionTester.test(wampIP, 8888, 1000, function (err, output) {
var wampIP = wampUrl.split("//")[1].split(":")[0];
logger.info("[SYSTEM] - WAMP server IP: "+wampIP);
//logger.debug("[WAMP] - CONNECTION STATUS: "+JSON.stringify(output));
logger.info("[SYSTEM] - Node ID: "+boardCode);
var reachable = output.success;
var error_test = output.error;
//logger.debug("[WAMP] - CONNECTION STATUS: "+reachable);
//----------------------------------------------------------------------------------------
// 3. On WAMP connection open the device will load LR libraries and start each LR module
//----------------------------------------------------------------------------------------
//This function is called as soon as the connection is created successfully
wampConnection.onopen = function (session, details) {
if (!reachable) {
logger.info('[WAMP] - Connection to WAMP server '+ wampUrl + ' created successfully:');
logger.info('[WAMP] |--> Realm: '+ wampRealm);
logger.info('[WAMP] |--> Session ID: '+ session._id);
//logger.debug('[WAMP] |--> Connection details:\n'+ JSON.stringify(details));
//CONNECTION STATUS: FALSE
logger.error("[CONNECTION] - Iotronic server unreachable (test result = " + reachable + ")");
logger.error("[CONNECTION] --> Error reported: " + error_test);
process.exit();
} else {
// Test if IoTronic is connected to the realm
session.call("s4t.iotronic.isAlive", [boardCode]).then(
logger.info('[SYSTEM] - DEVICE: ' + device);
function(response){
//----------------------------------------
// 1. Set WAMP connection configuration
//----------------------------------------
var wampUrl = nconf.get('config:wamp:url_wamp')+":"+nconf.get('config:wamp:port_wamp')+"/ws";
var wampRealm = nconf.get('config:wamp:realm');
var wampConnection = new autobahn.Connection({
url: wampUrl,
realm: wampRealm,
max_retries: -1
});
logger.info("[SYSTEM] - " + response.message );
var wampIP = wampUrl.split("//")[1].split(":")[0];
logger.info("[SYSTEM] - WAMP server IP: "+wampIP);
logger.info("[SYSTEM] - Node ID: "+boardCode);
//----------------------------------------------------------------------------------------
// 3. On WAMP connection open the device will load LR libraries and start each LR module
//----------------------------------------------------------------------------------------
//This function is called as soon as the connection is created successfully
wampConnection.onopen = function (session, details) {
if (keepWampAlive != null){

@@ -131,74 +135,5 @@

logger.info('[WAMP] - Connection to WAMP server '+ wampUrl + ' created successfully:');
logger.info('[WAMP] |--> Realm: '+ wampRealm);
logger.info('[WAMP] |--> Session ID: '+ session._id);
//logger.debug('[WAMP] |--> Connection details:\n'+ JSON.stringify(details));
// RPC registration of Board Management Commands
manageBoard.exportManagementCommands(session);
var configFile = JSON.parse(fs.readFileSync(SETTINGS, 'utf8'));
var board_config = configFile.config["board"];
logger.info("[CONFIGURATION] - Board configuration parameters: " + JSON.stringify(board_config));
//PROVISIONING: Iotronic sends coordinates to this device when its status is "new"
if(reg_status === "new"){
logger.info('[CONFIGURATION] - NEW BOARD CONFIGURATION STARTED... ');
session.call("s4t.board.provisioning", [boardCode]).then(
function(result){
logger.info("\n\nPROVISIONING "+boardCode+" RECEIVED: " + JSON.stringify(result) + "\n\n");
board_position = result.message[0];
board_config["position"]=result.message[0];
board_config["status"]="registered";
logger.info("\n[CONFIGURATION] - BOARD POSITION UPDATED: " + JSON.stringify(board_config["position"]));
//Updates the settings.json file
fs.writeFile(SETTINGS, JSON.stringify(configFile, null, 4), function(err) {
if(err) {
logger.error('Error writing settings.json file: ' + err);
} else {
logger.info("settings.json configuration file saved to " + SETTINGS);
//Calling the manage_WAMP_connection function that contains the logic that has to be performed if the device is connected to the WAMP server
manageBoard.manage_WAMP_connection(session, details);
}
});
//Create a backup file of settings.json
fs.writeFile(SETTINGS + ".BKP", JSON.stringify(configFile, null, 4), function(err) {
if(err) {
logger.error('Error writing settings.json.BKP file: ' + err);
} else {
logger.info("settings.json.BKP configuration file saved to " + SETTINGS + ".BKP");
}
});
}, session.log);
} else if(reg_status === "registered"){
//Calling the manage_WAMP_connection function that contains the logic that has to be performed if the device is connected to the WAMP server
manageBoard.manage_WAMP_connection(session, details);
} else{
logger.error('[CONFIGURATION] - WRONG BOARD STATUS: status allowed "new" or "registerd"!');
process.exit();
}
//----------------------------------------------------------------------------------------------------

@@ -213,3 +148,3 @@ // THIS IS AN HACK TO FORCE RECONNECTION AFTER A BREAK OF INTERNET CONNECTION

var connectionTester = require('connection-tester');
connectionTester.test(wampIP, 8888, 1000, function (err, output) {
connectionTester.test(wampIP, port_wamp, 1000, function (err, output) {

@@ -226,3 +161,3 @@ //logger.debug("[WAMP] - CONNECTION STATUS: "+JSON.stringify(output));

//CONNECTION STATUS: FALSE
logger.warn("[CONNECTION-RECOVERY] - INTERNET CONNECTION STATUS: "+reachable+ " - ERROR: "+error_test);
logger.warn("[CONNECTION-RECOVERY] - INTERNET CONNECTION STATUS: " + reachable + " - ERROR: " + error_test);
wamp_check = false;

@@ -237,90 +172,99 @@ online = false;

if(!online){
// In the previous checks the "online" flag was set to FALSE.
// The connection is come back ("online" is TRUE)
logger.info("[CONNECTION-RECOVERY] - INTERNET CONNECTION STATUS: "+reachable);
logger.info("[CONNECTION-RECOVERY] - INTERNET CONNECTION RECOVERED!");
logger.info("[CONNECTION-RECOVERY] - INTERNET CONNECTION STATUS: " + reachable);
logger.info("[CONNECTION-RECOVERY] ---> INTERNET CONNECTION RECOVERED!");
logger.info("[WAMP-RECOVERY] - WAMP connection checks started...");
//we need to test the WAMP connection
session.publish ('board.connection', [ 'alive-'+boardCode ], {}, { acknowledge: true}).then(
// Test if IoTronic is connected to the realm
session.call("s4t.iotronic.isAlive", [boardCode]).then(
function(publication) {
logger.info("[WAMP-ALIVE-STATUS] - WAMP ALIVE MESSAGE RESPONSE: published -> publication ID is " + JSON.stringify(publication));
function(response){
// WAMP connection is established and the previous connection fault didn't compromise the WAMP socket
// so we don't need to restore the WAMP connection and we set the connection status ("online") to TRUE.
wamp_check = true;
logger.info("[WAMP-RECOVERY] - WAMP CONNECTION STATUS: " + response.message);
online = true;
},
function(error) {
logger.warn("[WAMP-RECOVERY] - WAMP ALIVE MESSAGE: publication error " + JSON.stringify(error));
function (err) {
// IoTronic is not connected to the realm yet so LR need to try to reconnect later
wamp_check = false;
}
logger.debug("[WAMP-RECOVERY] - WAMP CONNECTION STATUS: " + JSON.stringify(err));
);
setTimeout(function(){
//It will wait the WAMP alive message response
setTimeout(function(){
// WAMP CONNECTION IS NOT ESTABLISHED: if after a connection fault the WAMP connection recovery procedure
// didn't start automatically we need to KILL the WAMP socket through the TCPKILL tool
// (problem noticed in WIFI connection with DSL internet connection).
// If the message sent to WAMP server was correctly published (so "wamp_check" is TRUE) means
// WAMP connection is established and the previous connection fault didn't compromise the WAMP socket
// so we don't need to restore the WAMP connection and we set the connection status ("online") to TRUE.
if (wamp_check){
logger.warn("[WAMP-RECOVERY] - WAMP CONNECTION STATUS: " + wamp_check);
// WAMP CONNECTION IS ESTABLISHED
logger.debug("[WAMP-ALIVE-STATUS] - WAMP CONNECTION STATUS: " + wamp_check);
online = true;
// Check if the tcpkill process was killed after a previous connection recovery through this check we will avoid to start another tcpkill process
var tcpkill_status = running(tcpkill_pid);
//logger.warn(boardCode+" needs to be restored!");
//manageNetworks.setSocatOnBoard([socatServer_ip, socatServer_port, socatBoard_ip, net_backend, boardCode, true] ); //TO RESTORE
logger.warn("[WAMP-RECOVERY] - TCPKILL STATUS: " + tcpkill_status + " - PID: " + tcpkill_pid);
}
else{
// at LR startup "tcpkill_pid" is NULL and in this condition "is-running" module return "true" that is a WRONG result!
if (tcpkill_status === false || tcpkill_pid == null){
// WAMP CONNECTION IS NOT ESTABLISHED: if after a connection fault the message publishing to WAMP server failed and the WAMP connection recovery procedure
// didn't start automatically we need to KILL the WAMP socket through the TCPKILL tool (problem noticed in WIFI connection with DSL internet connection).
logger.warn("[WAMP-RECOVERY] - Cleaning WAMP socket...");
var tcpkill_kill_count = 0;
logger.warn("[WAMP-ALIVE-STATUS] - WAMP CONNECTION STATUS: " + wamp_check);
//tcpkill -9 port 8181
var tcpkill = spawn('tcpkill',['-9','port','8181']);
tcpkill_pid = tcpkill.pid;
// Check if the tcpkill process was killed after a previous connection recovery
// Through this check we will avoid to start another tcpkill process
var tcpkill_status = running(tcpkill_pid);
tcpkill.stdout.on('data', function (data) {
logger.debug('[WAMP-RECOVERY] ... tcpkill stdout: ' + data);
});
logger.warn("[WAMP-ALIVE-STATUS] - TCPKILL STATUS: " + tcpkill_status + " - PID: " +tcpkill_pid);
tcpkill.stderr.on('data', function (data) {
logger.debug('[WAMP-RECOVERY] ... tcpkill stderr:\n' + data);
// at LR startup "tcpkill_pid" is NULL and in this condition "is-running" module return "true" that is a WRONG result!
if (tcpkill_status === false || tcpkill_pid == null){
//it will check if tcpkill is in listening state on the port 8181
if(data.toString().indexOf("listening") > -1){
logger.warn("[WAMP-RECOVERY] - Cleaning WAMP socket...");
var tcpkill_kill_count = 0;
// LISTENING: to manage the starting of tcpkill (listening on port 8181)
//tcpkill -9 port 8181
var tcpkill = spawn('tcpkill',['-9','port','8181']);
tcpkill_pid = tcpkill.pid;
}else if (data.toString().indexOf("win 0") > -1){
tcpkill.stdout.on('data', function (data) {
logger.debug('[WAMP-RECOVERY] ... tcpkill stdout: ' + data);
});
// TCPKILL DETECTED WAMP ACTIVITY (WAMP reconnection attempts)
// This is the stage triggered when the WAMP socket was killed by tcpkill and WAMP reconnection process automaticcally started:
// in this phase we need to kill tcpkill to allow WAMP reconnection.
try{
tcpkill.stderr.on('data', function (data) {
logger.debug('[WAMP-RECOVERY] ... killing tcpkill process with PID: ' + tcpkill_pid);
process.kill(tcpkill_pid);
logger.debug('[WAMP-RECOVERY] ... tcpkill stderr:\n' + data);
//double check: It will test after a while if the tcpkill process has been killed
setTimeout(function(){
//it will check if tcpkill is in listening state on the port 8181
if(data.toString().indexOf("listening") > -1){
if ( running(tcpkill_pid) || tcpkill_pid == null){
// LISTENING: to manage the starting of tcpkill (listening on port 8181)
logger.debug('[WAMP-RECOVERY] ... tcpkill['+tcpkill_pid+'] listening...');
tcpkill_kill_count = tcpkill_kill_count + 1;
//tcpkill_pid = tcpkill.pid;
//logger.debug('[WAMP-RECOVERY] ... tcpkill -9 port 8181 - PID ['+tcpkill_pid+']');
logger.warn("[WAMP-RECOVERY] ... tcpkill still running!!! PID ["+tcpkill_pid+"]");
logger.debug('[WAMP-RECOVERY] ... tcpkill killing retry_count '+ tcpkill_kill_count);
}else if (data.toString().indexOf("win 0") > -1){
tcpkill.kill('SIGINT');
// TCPKILL DETECTED WAMP ACTIVITY (WAMP reconnection attempts)
// This is the stage triggered when the WAMP socket was killed by tcpkill and WAMP reconnection process automaticcally started:
// in this phase we need to kill tcpkill to allow WAMP reconnection.
try{
}
logger.debug('[WAMP-RECOVERY] ... killing tcpkill process with PID: ' + tcpkill_pid);
process.kill(tcpkill_pid);
}, 3000);
}catch (e) {
logger.error('[WAMP-RECOVERY] ... tcpkill killing error: ', e);
}
tcpkill.kill('SIGINT');
//double check: It will test after a while if the tcpkill process has been killed

@@ -342,58 +286,32 @@ setTimeout(function(){

}catch (e) {
logger.error('[WAMP-RECOVERY] ... tcpkill killing error: ', e);
}
/*
tcpkill.kill('SIGINT');
//double check: It will test after a while if the tcpkill process has been killed
setTimeout(function(){
});
if ( running(tcpkill_pid) || tcpkill_pid == null){
tcpkill.on('close', function (code) {
tcpkill_kill_count = tcpkill_kill_count + 1;
logger.debug('[WAMP-RECOVERY] ... tcpkill killed!');
logger.info("[WAMP-RECOVERY] - WAMP socket cleaned!");
logger.warn("[WAMP-RECOVERY] ... tcpkill still running!!! PID ["+tcpkill_pid+"]");
logger.debug('[WAMP-RECOVERY] ... tcpkill killing retry_count '+ tcpkill_kill_count);
//The previous WAMP socket was KILLED and the automatic WAMP recovery process will start
//so the connection recovery is completed and "online" flag is set again to TRUE
online = true;
tcpkill.kill('SIGINT');
});
}
}else{
}, 3000);
*/
logger.warn('[WAMP-RECOVERY] ...tcpkill already started!');
}
}
});
}, 2 * 1000);
tcpkill.on('close', function (code) {
logger.debug('[WAMP-RECOVERY] ... tcpkill killed!');
logger.info("[WAMP-RECOVERY] - WAMP socket cleaned!");
//The previous WAMP socket was KILLED and the automatic WAMP recovery process will start
//so the connection recovery is completed and "online" flag is set again to TRUE
online = true;
});
}else{
logger.warn('[WAMP-RECOVERY] ...tcpkill already started!');
}
}
);
}, 2 * 1000); //time to wait for WAMP alive message response

@@ -417,114 +335,108 @@ }

//----------------------------------------------------------------------------------------------------
},
function (err) {
// IoTronic is not connected to the realm yet so LR need to try to reconnect later
logger.error("[SYSTEM] - IoTronic is not online: " + JSON.stringify(err) );
//if (err.error !== 'wamp.error.no_such_procedure') {}
logger.warn("[SYSTEM] - Lightning-rod reconnecting...");
};
wampConnection.close();
//-------------------------------
// 4. On WAMP connection close
//-------------------------------
//This function is called if there are problems with the WAMP connection
wampConnection.onclose = function (reason, details) {
setTimeout(function(){
wampConnection.open();
}, 10 * 1000); //time to wait for the next reconnection attempt
try{
wamp_check = true; // IMPORTANT: for ethernet connections this flag avoid to start recovery procedure (tcpkill will not start!)
}
);
logger.error('[WAMP] - Error in connecting to WAMP server!');
logger.error('- Reason: ' + reason);
logger.error('- Reconnection Details: ');
logger.error(" - retry_delay:", details.retry_delay);
logger.error(" - retry_count:", details.retry_count);
logger.error(" - will_retry:", details.will_retry);
if(wampConnection.isOpen){
logger.info("[WAMP] - connection is open!");
}
else{
logger.warn("[WAMP] - connection is closed!");
}
}
catch(err){
logger.warn('[WAMP] - Error in WAMP connection: '+ err);
}
//----------------------------------------------------------------------------------------------------
};
};
//-------------------------------
// 4. On WAMP connection close
//-------------------------------
//This function is called if there are problems with the WAMP connection
wampConnection.onclose = function (reason, details) {
//--------------------------------------------------------------
// 2. The selected device will connect to Iotronic WAMP server
//--------------------------------------------------------------
switch(device){
try{
case 'arduino_yun':
logger.info("[SYSTEM] - Lightning-rod Arduino Yun starting...");
var yun = require('./device/arduino_yun');
yun.Main(wampConnection, logger);
break;
wamp_check = true; // IMPORTANT: for ethernet connections this flag avoid to start recovery procedure (tcpkill will not start!)
case 'server':
logger.info("[SYSTEM] - Lightning-rod server starting...");
var server = require('./device/server');
server.Main(wampConnection, logger);
break;
logger.error('[WAMP] - Error in connecting to WAMP server!');
logger.error('- Reason: ' + reason);
logger.error('- Reconnection Details: ');
logger.error(" - retry_delay:", details.retry_delay);
logger.error(" - retry_count:", details.retry_count);
logger.error(" - will_retry:", details.will_retry);
case 'raspberry_pi':
logger.info("[SYSTEM] - Lightning-rod Raspberry Pi starting...");
var raspberry_pi = require('./device/raspberry_pi');
raspberry_pi.Main(wampConnection, logger);
break;
case 'android':
logger.info("[SYSTEM] - L-R Android starting...");
var yun = require('./device/android');
yun.Main(wampConnection, logger);
break;
default:
logger.warn('[SYSTEM] - Device "' + device + '" not supported!');
logger.warn('[SYSTEM] - Supported devices are: "server", "arduino_yun", "raspberry_pi".');
process.exit();
break;
if(wampConnection.isOpen){
logger.info("[WAMP] - connection is open!");
}
else{
logger.warn("[WAMP] - connection is closed!");
}
}
catch(err){
logger.warn('[WAMP] - Error in WAMP connection: '+ err);
}
});
};
//--------------------------------------------------------------
// 2. The selected device will connect to Iotronic WAMP server
//--------------------------------------------------------------
switch(device){
case 'arduino_yun':
var YunDevice = require('./device/lyt_arduino_yun');
lyt_device = new YunDevice(device);
logger.info("[SYSTEM] - Lightning-rod "+ lyt_device.name +" starting...");
lyt_device.Main(wampConnection, logger);
break;
case 'server':
var ServerDevice = require('./device/lyt_server');
lyt_device = new ServerDevice(device);
logger.info("[SYSTEM] - Lightning-rod "+ lyt_device.name +" starting...");
lyt_device.Main(wampConnection, logger);
break;
case 'raspberry_pi':
var RaspDevice = require('./device/lyt_raspberry_pi');
lyt_device = new RaspDevice(device);
logger.info("[SYSTEM] - Lightning-rod "+ lyt_device.name +" starting...");
lyt_device.Main(wampConnection, logger);
break;
case 'android':
var AndroidDevice = require('./device/lyt_android');
lyt_device = new AndroidDevice(device);
logger.info("[SYSTEM] - Lightning-rod "+ lyt_device.name +" starting...");
lyt_device.Main(wampConnection, logger);
break;
default:
logger.warn('[SYSTEM] - Device "' + device + '" not supported!');
logger.warn('[SYSTEM] - Supported devices are: "server", "arduino_yun", "raspberry_pi".');
process.exit();
break;
}
}
});

@@ -531,0 +443,0 @@

@@ -20,2 +20,3 @@ #!/usr/bin/env node

//###############################################################################
require("./lightning-rod.js");

@@ -1,9 +0,18 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################

@@ -39,8 +48,7 @@ //service logging configuration: "manageDrivers"

/*
This function mounts all enabled drivers ("autostart" flag set at true) every LR restarting.
In particular:
- at LR starting: each enabled driver will be mounted (by fuse)
- after a Internet/WAMP connection recovery we don't mount again each enabled driver (because it is already mounted) but we will only declare/register again the RPC functions.
*/
// RPC to mount all enabled drivers ("autostart" flag set at true) every LR restarting.
// In particular:
// - at LR starting: each enabled driver will be mounted (by fuse)
// - after a Internet/WAMP connection recovery we don't mount again each enabled driver (because it is already mounted) but we will only declare/register again the RPC functions.
exports.restartDrivers = function (){

@@ -60,3 +68,3 @@

logger.info('[DRIVER] - Restarting enabled drivers on board: ');
logger.info('[DRIVER] - Restarting enabled drivers on board...');

@@ -222,3 +230,3 @@ for(var i = 0; i < driver_num; i++) {

}else{
logger.info("[DRIVER] - "+driver_name+" --> Status -> "+status+": this plugin does not have to be started!");
logger.info("[DRIVER] - " + driver_name + ": this driver does not have to be started!");
}

@@ -246,116 +254,3 @@ }

// Wrapper for Fuse read-directory function
function readdirFunction(driver_name){
return function (mountpoint, cb) {
logger.debug("[DRIVER] - "+driver_name+" --> readdir(%s) - files list: %s", mountpoint, JSON.stringify(file_list[driver_name]) );
//if (mountpoint === '/') return cb(0, [driver_name]);
//if (mountpoint === '/'+driver_name) return cb(0, file_list[driver_name] );
if (mountpoint === '/') return cb(0, file_list[driver_name] );
cb(0);
};
}
// Wrapper for Fuse get-attr function
function getattrFunction(driver_name){
return function (mountpoint, cb) {
logger.debug("[DRIVER] - "+driver_name+" --> getattr(%s)", mountpoint);
driver_mp_node = mp_list[driver_name];
if(driver_mp_node[mountpoint].mp != undefined){
cb(0, driver_mp_node[mountpoint].mp );
return
}
cb(fuse.ENOENT);
}
}
// Wrapper for Fuse open-file function
function openFunction(driver_name){
return function (mountpoint, flags, cb) {
fd_index = fd_index + 1;
logger.debug("[DRIVER] - "+driver_name+" --> Open(%s, %d) - fd = %s", mountpoint, flags, fd_index);
cb(0, fd_index); //cb(0, 42) // 42 is an fd
}
}
// Wrapper for Fuse read-file function
function readFunction(driver_name, mirror_board){
return function (mountpoint, fd, buf, len, pos, cb) {
driver_mp_node = mp_list[driver_name];
var driver = drivers[driver_name];
// To read a file of a driver mounted locally that return as result the value of a local endpoint (a sensor, etc.)
if (driver_mp_node['/'].remote === "false"){
logger.debug("[DRIVER] - "+driver_name+" --> Read(%s, %d, %d, %d)", mountpoint, fd, len, pos);
driver[driver_mp_node[mountpoint].read_function]( function(read_content){
var buffer = new Buffer(read_content.toString(), 'utf-8');
var str = ''+buffer.slice(pos);
if (!str)
return cb(0);
buf.write(str);
return cb(str.length);
});
}else{
// To read a file of a driver mounted locally that return as result the value of a remote endpoint (i.e. a sensor on a remote board, etc.)
// A mirrored board is a board that share its hardware that can be used by a board that mount remotely the same driver.
var filename = mountpoint.replace('/','');
logger.debug('[DRIVER] - '+driver_name+' - REMOTE CALLING to '+mirror_board + ' RPC called: s4t.'+mirror_board+'.driver.'+driver_name+'.'+filename+'.read');
// Call the RPC read function of the board that shares the hardware
session_drivers.call('s4t.'+mirror_board+'.driver.'+driver_name+'.'+filename+'.read', [driver_name, filename]).then(
function(read_content){
var buffer = new Buffer(read_content.toString(), 'utf-8');
var str = ''+buffer.slice(pos);
if (!str)
return cb(0);
buf.write(str);
return cb(str.length);
}
);
}
}
}
// RPC function that allows to read the value of a file remotely.
// RPC allows to read the value of a file remotely.
// This remote call function is used in two cases:

@@ -410,3 +305,3 @@ // - REMOTE - FALSE: the cloud sends a request to know the content of a local file

return d.resolve(0);
logger.info('[DRIVER] - '+driver_name+' - MIRRORED read remote from '+mirror_board+': ['+filename+'] -> '+ str);
logger.info('[DRIVER] - '+driver_name+' - MIRRORED REMOTE READ from '+mirror_board+': ['+filename+'] -> '+ str);
d.resolve(str);

@@ -417,3 +312,3 @@

// call failed
logger.warn('[DRIVER] - '+driver_name+' - MIRRORED read remote from '+mirror_board+' failed! - Error: '+ JSON.stringify(error));
logger.warn('[DRIVER] - '+driver_name+' - MIRRORED REMOTE READ from '+mirror_board+' failed! - Error: '+ JSON.stringify(error));
var error_log = "ERROR: " + error["error"];

@@ -433,31 +328,3 @@ d.resolve( error_log );

// Wrapper for Fuse write-file function.
function writeFunction(driver, driver_name){
return function (mountpoint, fd, buffer, length, position, cb) {
logger.debug('[DRIVER] - '+driver_name+' --> Writing ', buffer.slice(0, length));
content = buffer.slice(0, length);
logger.debug('[DRIVER] - '+driver_name+' --> buffer content: ' + content.toString());
logger.debug('[DRIVER] - '+driver_name+' --> buffer length: ' + length.toString());
driver_mp_node = mp_list[driver_name];
if (driver_mp_node[mountpoint].write_function === null){
cb(fuse.EACCES);
} else {
driver[driver_mp_node[mountpoint].write_function ]( content, function(){
cb(length);
});
}
};
}
// RPC function that allows to write a file remotely.
// RPC allows to write a file remotely.
// This remote call function is used in two cases:

@@ -525,3 +392,3 @@ // - REMOTE - FALSE: the cloud sends a request to write in a local file

// Stub RPC function used for the files that don't have an implemented read/write function.
// Stub RPC to used for the files that don't have an implemented read/write function.
exports.NotAllowedRemoteFunction = function (args){

@@ -535,354 +402,3 @@

// Function used to convert file permission rappresentation from base 10 to 8
function MaskConversion(mode_b10){
//var mode_b10 = 100644//40755
mode_b8 = parseInt(mode_b10.toString(10), 8);
//logger.info("from b10 "+mode_b10+" to b8 "+mode_b8)
permission = mode_b8;
return permission
}
/*
function HumanMaskConversion(mode_b10){
mode_b8 = parseInt(mode_b10.toString(10), 8)
//logger.info("from b10 "+mode_b10+" to b8 "+mode_b8)
permission = mode_b8
return permission
}
*/
// Function used to manage the promise of the RPC function registration for each file enabled to be read remotely.
function ManageReadFileRegistration(registration, driver_name, file, driver_mp_node, idx, list){
driver_mp_node['/'+file.name].reg_read_function = registration;
logger.debug('[WAMP] - '+driver_name+' --> ' + file.name + ' read function registered!');
if (idx === list.length - 1){
mp_list[driver_name] = driver_mp_node;
logger.info("[DRIVER] - "+driver_name+" --> RPC read functions successfully registered!");
}
}
// Function used to manage the promise of the RPC function registration for each file enabled to be written remotely.
function ManageWriteFileRegistration(registration, driver_name, file, driver_mp_node, idx, list){
driver_mp_node['/'+file.name].reg_write_function = registration;
logger.debug('[WAMP] - '+driver_name+' --> ' + file.name + ' write function registered!');
if (idx === list.length - 1){
mp_list[driver_name] = driver_mp_node;
logger.info("[DRIVER] - "+driver_name+" --> RPC write functions successfully registered!");
}
}
// Function used to register WAMP write/read functions for each enabled file
function RegisterFiles(driver_name, file, driver_mp_node, idx, list){
if(file.read_function != undefined){
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.read', exports.readRemote ).then(
function(registration){
ManageReadFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}else{
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.read', exports.NotAllowedRemoteFunction ).then(
function(registration){
ManageReadFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}
if(file.write_function != undefined){
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.write', exports.writeRemote ).then(
function(registration){
ManageWriteFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}else{
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.write', exports.NotAllowedRemoteFunction ).then(
function(registration){
ManageWriteFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}
}
// Function used to mount via FUSE the driver and register the RPC functions for each file;
// if the driver mounting happens after a connection recovery we will only register again the RPC functions without re-mounting the driver via FUSE.
function LoadDriver(driver_name, mountpoint, remote, mirror_board){
var driver_path = DRIVERS_STORE+driver_name;
var driver_conf = driver_path+"/"+driver_name+".json";
var driver_module = driver_path+"/"+driver_name+".js";
var rest_response = {};
var d = Q.defer();
try{
var driver = require(driver_module);
var driverJSON = JSON.parse(fs.readFileSync(driver_conf, 'utf8'));
logger.debug('[DRIVER] - '+driver_name+' --> JSON file '+ driver_name +'.json successfully parsed!');
driver_name = driverJSON.name;
var type = driverJSON.type; //logger.info("\tfile type: " + type)
var permissions = MaskConversion(driverJSON.permissions); //logger.info("\tpermissions: " + MaskConversion(permissions))
//var root_permissions = MaskConversion(driverJSON.root_permissions);
var children = driverJSON.children; //logger.info("Files in the folder:")
logger.debug("[DRIVER] - "+driver_name+" --> driver configuration loaded!");
mp_list[driver_name]={};
driver_mp_node = mp_list[driver_name];
fuse_root_path='/';
var root_mp = {
mtime: new Date(),
atime: new Date(),
ctime: new Date(),
size: 100,
mode: permissions,
uid: process.getuid(),
gid: process.getgid()
};
driver_mp_node[fuse_root_path]={
name: driver_name,
mp: {},
remote: remote,
mirror_board: mirror_board
};
driver_mp_node[fuse_root_path].mp=root_mp;
driver_mp_node[fuse_root_path].type = "folder";
file_list[driver_name]=[];
children.forEach(function(file, idx, list) {
setTimeout(function() {
logger.debug("[DRIVER] - "+driver_name+" --> analyzing file: "+file.name);
file_list[driver_name].push(file.name);
//fuse_file_path='/'+driver_name+'/'+file.name;
fuse_file_path='/'+file.name;
driver_mp_node[fuse_file_path]={
name:"",
read_function: null,
write_function: null,
mp: {},
reg_read_function: null,
reg_write_function: null
};
if(file.read_function != undefined){
var read_function = file.read_function;
}else{
var read_function = null;
}
if(file.write_function != undefined){
var write_function = file.write_function;
}else{
var write_function = null;
}
var file_mp = {
mtime: new Date(),
atime: new Date(),
ctime: new Date(),
size: 100,
mode: MaskConversion(file.permissions),
uid: process.getuid(),
gid: process.getgid()
};
driver_mp_node[fuse_file_path].mp = file_mp;
driver_mp_node[fuse_file_path].name = file.name;
driver_mp_node[fuse_file_path].type = "file";
driver_mp_node[fuse_file_path].read_function = read_function;
driver_mp_node[fuse_file_path].write_function = write_function;
RegisterFiles(driver_name, file, driver_mp_node, idx, list);
if (idx === list.length - 1){
logger.info("[DRIVER] - "+driver_name+" --> Available files: %s", JSON.stringify(file_list[driver_name]));
}
}, 100); // end of setTimeout function
});
if ( reconnected === false ){
logger.debug("[DRIVER] - "+driver_name+" --> It is necessary to mount the driver (reconnected = " + reconnected + ")");
try{
drivers[driver_name] = driver;
var driverlib = drivers[driver_name];
driverlib['init']( function(init_response){
if(init_response.result == "SUCCESS"){
logger.info("[DRIVER] - "+driver_name+" --> " + init_response.message);
fuse.mount(mountpoint, {
readdir: readdirFunction(driver_name),
getattr: getattrFunction(driver_name),
open: openFunction(driver_name),
read: readFunction(driver_name, mirror_board), //read: readFunction(driver_name, filename, mirror_board),
write: writeFunction(driver, driver_name)
});
rest_response.message = "Driver '"+driver_name+"' successfully mounted!";
rest_response.result = "SUCCESS";
d.resolve(rest_response);
}
else{
logger.warn("[DRIVER] - "+driver_name+" --> " + init_response.message);
rest_response.message = "ERROR during "+driver_name+" initialization -> " +init_response.message;
rest_response.result = "ERROR";
d.reject(rest_response);
}
});
}
catch(err){
rest_response.message = "ERROR during "+driver_name+" (fuse) mounting: " +err;
rest_response.result = "ERROR";
logger.warn("[DRIVER] - "+driver_name+" --> " + rest_response.message);
d.reject(rest_response);
}
}else{
logger.debug("[DRIVER] - "+driver_name+" --> It is not necessary to mount the driver (reconnected = " + reconnected + ")");
if(reconnected === true) reconnected = false;
rest_response.message = "No need to mount driver after reconnection!";
rest_response.result = "SUCCESS";
d.resolve(rest_response);
}
}
catch(err){
rest_response.message = err;
rest_response.result = "ERROR";
d.reject(rest_response);
}
return d.promise;
}
// Function that creates the directory and then mounts the driver.
function MountpointCreation(driver_name, mountpoint, remote, mirror_board, d){
fs.mkdir(mountpoint, 0755, function() {
logger.debug("[DRIVER] - "+driver_name+" ----> folder "+mountpoint+" CREATED!");
AttachMountpoint(driver_name, mountpoint, remote, mirror_board, d);
});
}
// Function that calls the LoadDriver function that will mount the driver
function AttachMountpoint(driver_name, mountpoint, remote, mirror_board, d) {
LoadDriver(driver_name, mountpoint, remote, mirror_board).then(
function(result){
manageDriversConf("update", driver_name, null, "mounted", remote, mirror_board, function(mng_result){
logger.debug("[DRIVER] - "+driver_name+" --> Mounting: " + mng_result.message);
d.resolve(result);
logger.info("[DRIVER] - "+driver_name+" --> "+ result.message);
});
},
function (error) {
logger.warn("[DRIVER] - "+driver_name+" --> Error in LoadDriver function: "+ JSON.stringify(error));
d.reject(error);
}
);
}
//This function mounts a driver
// RPC to mount a driver
exports.mountDriver = function (args){

@@ -936,3 +452,3 @@

logger.error("[DRIVER] - "+driver_name+" --> "+rest_response.message);
d.reject(rest_response);
d.resolve(rest_response);

@@ -948,3 +464,3 @@ }

logger.error("[DRIVER] - "+driver_name+" --> "+rest_response.message);
d.reject(rest_response);
d.resolve(rest_response);
}

@@ -958,110 +474,3 @@

// Function to un-register from WAMP server the RPCs of each file of the driver during unmounting
function UnRegister(driver_name, file_node, restarting, rest_response, d, callback){
var driver_mp_node = mp_list[driver_name];
// If this data structure is not null means that the driver to unmount was mounted in this session and we need to unregister each RPC function related to each driver file
if (driver_mp_node != null){
logger.debug("[DRIVER] - "+driver_name+" --> Data structures to clean...");
// Unregistering RPCs for each file
file_node.forEach(function(file, idx, list) {
logger.debug("[DRIVER] - "+driver_name+" --> Unregistering ("+file.name+") read_function: " + JSON.stringify(driver_mp_node['/'+file.name].read_function));
if ( driver_mp_node['/'+file.name] != undefined) {
logger.debug("[DRIVER] - "+driver_name+" --> WAMP DRIVER STATUS: restarting = "+restarting + " - reconnected = " + reconnected);
if ( restarting === "undefined" && reconnected === false ){
//Unregistering read RPC functions
session_drivers.unregister(driver_mp_node['/'+file.name].reg_read_function).then(
function () {
// successfully unregistered
logger.debug("[DRIVER] - "+driver_name+" --> RPC read function of "+file.name +" unregistered!");
},
function (error) {
// unregister failed
logger.error("[DRIVER] - "+driver_name+" --> Error unregistering RPC read function of "+file);
}
);
//Unregistering write RPC functions
session_drivers.unregister(driver_mp_node['/'+file.name].reg_write_function).then(
function () {
// successfully unregistered
logger.debug("[DRIVER] - "+driver_name+" --> RPC write function of "+file.name +" unregistered!");
},
function (error) {
// unregister failed
logger.error("[DRIVER] - "+driver_name+" --> Error unregistering RPC write function of "+file);
}
);
}else{
logger.debug("[DRIVER] - No need to unregister any WAMP RPC!");
}
}
else{
logger.debug("[DRIVER] - "+driver_name+" --> I have not unregistered RPC file ("+file.name+") functions!");
}
// If it is the last file analyzed we have to clean the driver garbage
if (idx === list.length - 1){
//DATA cleaning-----------------------------------------------------------------------------------------
try{
logger.debug("[DRIVER] - "+driver_name+" --> Cleaning driver garbage...");
file_list[driver_name]=null;
delete file_list[driver_name];
logger.debug("[DRIVER] - "+driver_name+" --> Files removed from list!" );
mp_list[driver_name]=null;
delete mp_list[driver_name];
logger.debug("[DRIVER] - "+driver_name+" --> Mountpoints removed!");
callback({result:"SUCCESS", message:"RPCs unregistered"})
}
catch(err){
logger.error("[DRIVER] - "+driver_name+" --> Data cleaning error during unmounting: "+err);
}
//------------------------------------------------------------------------------------------------------
}
});
}
else{
logger.debug("[DRIVER] - "+driver_name+" --> No data structures to clean...");
callback({result:"SUCCESS", message:"RPCs unregistered"});
}
}
//This function unmounts a driver
// RPC to unmount a driver
exports.unmountDriver = function (args){

@@ -1125,3 +534,3 @@

logger.debug("[DRIVER] - "+driver_name+" --> Unmounting: " + mng_result.message);
logger.debug("[DRIVER] - "+driver_name+" --> " + mng_result.message);

@@ -1142,3 +551,3 @@ rest_response.message = "Driver '"+driver_name+"' successfully unmounted!";

logger.error("[DRIVER] - "+driver_name+" --> "+JSON.stringify(rest_response.message));
d.reject(err);
d.resolve(rest_response);

@@ -1165,3 +574,3 @@ }

logger.error("[DRIVER] - "+driver_name+" --> Error during driver configuration loading: "+err);
d.reject(err);
d.resolve(err);
}

@@ -1173,6 +582,6 @@

catch(err){
rest_response.message = "ERROR during '"+driver_name+"' (fuse) unmounting: " +err;
rest_response.message = "Generic error during '"+driver_name+"' (fuse) unmounting: " +err;
rest_response.result = "ERROR";
logger.error("[DRIVER] - "+driver_name+" --> "+ rest_response.message);
d.reject(err);
d.resolve(err);
}

@@ -1184,3 +593,4 @@

//This function injects a driver in the board: it is called (via RPC) from the Cloud side to manage a driver injection
// RPC to inject a driver in the board: it is called (via RPC) from the Cloud side to manage a driver injection
exports.injectDriver = function (args){

@@ -1194,2 +604,4 @@

logger.info("[DRIVER] - INJECTING driver '" + driver_name + "'...");
var d = Q.defer();

@@ -1201,34 +613,31 @@ var rpc_result = "";

var driver_file_name = driver_folder+'/' + driver_name + '.js';
var driver_schema_name = driver_folder+'/' + driver_name + '.json';
// Check driver folder
if ( fs.existsSync(driver_folder) === false ){
logger.debug("[DRIVER] - Called RPC injectDriver with: \n - driver_name = " + driver_name + "\n - autostart = " + autostart + "\n - driver_code = \n###############################################################################\n" + driver_code + "\n###############################################################################\n\n\n - driver_schema = \n###############################################################################\n" + driver_schema+"\n###############################################################################\n");
if (loglevel != "debug" || loglevel != "DEBUG")
logger.info("[DRIVER] - Called RPC injectDriver with: driver_name = " + driver_name + ", autostart = " + autostart);
// driver folder creation
fs.mkdir(driver_folder, function() {
// driver file creation
fs.writeFile(driver_file_name, driver_code, function(err) {
if(err) {
var driver_schema_name = driver_folder+'/' + driver_name + '.json';
rpc_result = 'Error writing '+ driver_file_name +' file: ' + err;
logger.error('[DRIVER] - '+driver_name+' --> ' + rpc_result);
d.resolve(rpc_result);
logger.debug("[DRIVER] - " + driver_name + " - Checking driver environment...");
} else {
cleanDriverData(driver_name).then(
function (clean_res) {
// driver schema file creation
fs.writeFile(driver_schema_name, driver_schema, function(err) {
if (clean_res.result == "SUCCESS"){
logger.debug("[DRIVER] - " + driver_name + " --> driver environment is clean!");
if (loglevel !== "debug" && loglevel !== "DEBUG")
logger.info("[DRIVER] - Called RPC injectDriver with: driver_name = " + driver_name + ", autostart = " + autostart);
else
logger.info("[DRIVER] - Called RPC injectDriver with: \n - driver_name = " + driver_name + "\n - autostart = " + autostart + "\n - driver_code = \n###############################################################################\n" + driver_code + "\n###############################################################################\n\n\n - driver_schema = \n###############################################################################\n" + driver_schema+"\n###############################################################################\n");
// driver folder creation
fs.mkdir(driver_folder, function() {
// driver file creation
fs.writeFile(driver_file_name, driver_code, function(err) {
if(err) {
rpc_result = 'Error writing '+ driver_schema +' file: ' + err;
rpc_result = 'Error writing '+ driver_file_name +' file: ' + err;
logger.error('[DRIVER] - '+driver_name+' --> ' + rpc_result);

@@ -1240,15 +649,32 @@

manageDriversConf("update", driver_name, autostart, "injected", null, null, function(mng_result){
logger.debug("[DRIVER] - "+driver_name+" --> Injecting: " + mng_result.message);
// driver schema file creation
fs.writeFile(driver_schema_name, driver_schema, function(err) {
rpc_result = "Driver " + driver_name + " successfully injected!";
logger.info("[DRIVER] --> " + rpc_result);
if(err) {
d.resolve(rpc_result);
rpc_result = 'Error writing '+ driver_schema +' file: ' + err;
logger.error('[DRIVER] - '+driver_name+' --> ' + rpc_result);
d.resolve(rpc_result);
} else {
manageDriversConf("update", driver_name, autostart, "injected", null, null, function(mng_result){
logger.debug("[DRIVER] - "+driver_name+" --> " + mng_result.message);
rpc_result = "Driver " + driver_name + " successfully injected!";
logger.info("[DRIVER] --> " + rpc_result);
d.resolve(rpc_result);
});
}
});
}

@@ -1258,17 +684,15 @@

}
});
});
});
} else{
rpc_result = "ERROR: "+driver_name+" driver's files already injected! - Remove the previous driver installation!";
logger.warn("[DRIVER] --> " + rpc_result);
d.resolve(rpc_result);
}else{
logger.error("[DRIVER] --> " + clean_res.message);
d.resolve(clean_res.message);
}
}
}
);
return d.promise;

@@ -1279,3 +703,80 @@

// RPC to totally remove a driver from the board
exports.removeDriver = function(args){
// Parsing the input arguments
var driver_name = String(args[0]);
logger.info("[DRIVER] - REMOVE DRIVER RPC called for " + driver_name + "...");
var d = Q.defer();
cleanDriverData(driver_name).then(
function (clean_res) {
if (clean_res.result == "SUCCESS"){
clean_res.message = "Driver '" + driver_name + "' successfully removed!";
logger.info("[PLUGIN] --> " + clean_res.message);
d.resolve(clean_res.message);
}else{
logger.error("[PLUGIN] --> " + clean_res.message);
d.resolve(clean_res.message);
}
}
);
return d.promise;
};
// Function to clean all plugin data (folder and configuration)
function cleanDriverData(driver_name){
var response = {
message: '',
result: ''
};
var d = Q.defer();
var driver_folder = DRIVERS_STORE + driver_name;
var mp_driver_folder = MP_DRIVERS + driver_name;
if ( fs.existsSync(driver_folder) === true ){
deleteFolderRecursive(driver_folder); //delete driver files folder
deleteFolderRecursive(mp_driver_folder); //delete driver mountpoint folder
logger.debug("[DRIVER] - " + driver_name + " --> driver folders deleted.");
}
else{
logger.debug("[DRIVER] - " + driver_name + " --> driver folder already deleted.");
}
manageDriversConf("remove", driver_name, null, null, null, null, function(mng_result) {
if(mng_result.result == "SUCCESS"){
response.result = mng_result.result;
response.message = "Driver successfully removed!";
d.resolve(response);
}else{
d.resolve(mng_result);
}
});
return d.promise;
}
// Function used to update/manage the status of the driver injected in the board

@@ -1285,11 +786,11 @@ function manageDriversConf(operation, driver_name, autostart, status, remote, mirror_board, callback){

try{
var driversConf = JSON.parse(fs.readFileSync(DRIVERS_SETTING, 'utf8'));
var mng_result = {};
switch(operation){
case 'update':
logger.debug("[DRIVER] - "+driver_name+" --> Updating drivers.json...");

@@ -1317,3 +818,2 @@ logger.debug("[DRIVER] - "+driver_name+" --> Parameters specified:\n - status: "+status+"\n - remote: "+remote+ "\n - mirror_board: "+mirror_board+"\n - autostart: "+autostart);

try{

@@ -1328,3 +828,3 @@ //Updates the JSON file

} else {
mng_result.message = "Drivers changes file saved to " + DRIVERS_SETTING;
mng_result.message = "drivers.json updated";
mng_result.result = "SUCCESS";

@@ -1342,9 +842,9 @@ callback(mng_result)

break;
case 'remove':
logger.debug("[DRIVER] - "+driver_name+" --> Removing driver board from drivers.json...");
if(driversConf["drivers"].hasOwnProperty(driver_name)){
logger.debug("[DRIVER] - "+driver_name+" --> removing driver data from drivers.json...");
if(driversConf["drivers"].hasOwnProperty(driver_name)){
try{

@@ -1356,11 +856,11 @@ driversConf.drivers[driver_name]=null;

if(err) {
mng_result.message = "drivers.json file updating FAILED: "+err;
mng_result.message = "drivers.json file updating failed: "+err;
mng_result.result = "ERROR";
logger.debug("[DRIVER] - "+driver_name+" --> " + mng_result.message );
logger.error("[DRIVER] - "+driver_name+" ----> " + mng_result.message );
callback(mng_result);
} else {
mng_result.message = "Driver board successfully removed from drivers.json!";
mng_result.message = "driver data removed!";
mng_result.result = "SUCCESS";
logger.debug("[DRIVER] - "+driver_name+" --> " + mng_result.message );
logger.debug("[DRIVER] - "+driver_name+" ----> " + mng_result.message );
callback(mng_result);

@@ -1375,16 +875,16 @@

mng_result.result = "ERROR";
logger.warn("[DRIVER] - "+driver_name+" --> "+ mng_result.message);
logger.warn("[DRIVER] - "+driver_name+" ----> "+ mng_result.message);
callback(mng_result);
}
}else {
mng_result.message = "Driver already removed from drivers.json";
mng_result.message = "driver data already removed";
mng_result.result = "SUCCESS";
logger.warn("[DRIVER] - "+driver_name+" --> "+ mng_result.message);
logger.debug("[DRIVER] - "+driver_name+" ----> "+ mng_result.message);
callback(mng_result);
}
}
break;
default:

@@ -1397,19 +897,18 @@ //DEBUG MESSAGE

break;
}
}
}
catch(err){
logger.error('[DRIVER] - '+driver_name+' --> Error parsing drivers.json file in manageDriversConf: '+err);
}
logger.error('[DRIVER] - '+driver_name+' --> Error parsing drivers.json file in manageDriversConf: '+err);
}
}
// Function used to delete all driver files during driver removing from the board
function deleteFolderRecursive(path){
if( fs.existsSync(path) ) {

@@ -1431,50 +930,591 @@ fs.readdirSync(path).forEach(function(file,index){

// Function to un-register from WAMP server the RPCs of each file of the driver during unmounting
function UnRegister(driver_name, file_node, restarting, rest_response, d, callback){
var driver_mp_node = mp_list[driver_name];
// This function totally removes a driver from the board
exports.removeDriver = function(args){
// Parsing the input arguments
driver_name = String(args[0]);
// If this data structure is not null means that the driver to unmount was mounted in this session and we need to unregister each RPC function related to each driver file
if (driver_mp_node != null){
logger.info("[DRIVER] - REMOVE DRIVER RPC called for " + driver_name +"...");
var d = Q.defer();
var rpc_result = "";
var driver_folder = DRIVERS_STORE + driver_name;
var mp_driver_folder = MP_DRIVERS+ driver_name;
logger.debug("[DRIVER] - "+driver_name+" --> Data structures to clean...");
// Check driver folder
if ( fs.existsSync(driver_folder) === true ){
// Unregistering RPCs for each file
file_node.forEach(function(file, idx, list) {
manageDriversConf("remove", driver_name, null, null, null, null, function(mng_result){
logger.debug("[DRIVER] - "+driver_name+" --> Unregistering ("+file.name+") read_function: " + JSON.stringify(driver_mp_node['/'+file.name].read_function));
deleteFolderRecursive(driver_folder); //delete driver files folder
deleteFolderRecursive(mp_driver_folder); //delete driver mountpoint folder
if ( driver_mp_node['/'+file.name] != undefined) {
rpc_result = "Driver " + driver_name + " successfully removed!";
logger.info("[DRIVER] - "+driver_name+" --> " + rpc_result);
logger.debug("[DRIVER] - "+driver_name+" --> WAMP DRIVER STATUS: restarting = "+restarting + " - reconnected = " + reconnected);
d.resolve(rpc_result);
if ( restarting === "undefined" && reconnected === false ){
//Unregistering read RPC functions
session_drivers.unregister(driver_mp_node['/'+file.name].reg_read_function).then(
function () {
// successfully unregistered
logger.debug("[DRIVER] - "+driver_name+" --> RPC read function of "+file.name +" unregistered!");
},
function (error) {
// unregister failed
logger.error("[DRIVER] - "+driver_name+" --> Error unregistering RPC read function of "+file);
}
);
//Unregistering write RPC functions
session_drivers.unregister(driver_mp_node['/'+file.name].reg_write_function).then(
function () {
// successfully unregistered
logger.debug("[DRIVER] - "+driver_name+" --> RPC write function of "+file.name +" unregistered!");
},
function (error) {
// unregister failed
logger.error("[DRIVER] - "+driver_name+" --> Error unregistering RPC write function of "+file);
}
);
}else{
logger.debug("[DRIVER] - No need to unregister any WAMP RPC!");
}
}
else{
logger.debug("[DRIVER] - "+driver_name+" --> I have not unregistered RPC file ("+file.name+") functions!");
}
// If it is the last file analyzed we have to clean the driver garbage
if (idx === list.length - 1){
//DATA cleaning-----------------------------------------------------------------------------------------
try{
logger.debug("[DRIVER] - "+driver_name+" --> Cleaning driver garbage...");
file_list[driver_name]=null;
delete file_list[driver_name];
logger.debug("[DRIVER] - "+driver_name+" --> Files removed from list!" );
mp_list[driver_name]=null;
delete mp_list[driver_name];
logger.debug("[DRIVER] - "+driver_name+" --> Mountpoints removed!");
callback({result:"SUCCESS", message:"RPCs unregistered"})
}
catch(err){
logger.error("[DRIVER] - "+driver_name+" --> Data cleaning error during unmounting: "+err);
}
//------------------------------------------------------------------------------------------------------
}
});
} else{
rpc_result = "WARNING - Folder of "+driver_name+" driver not found or already deleted!";
logger.warn("[DRIVER] - "+driver_name+" --> " + rpc_result);
d.resolve(rpc_result);
}
return d.promise;
};
}
else{
logger.debug("[DRIVER] - "+driver_name+" --> No data structures to clean...");
callback({result:"SUCCESS", message:"RPCs unregistered"});
}
}
// Function used to manage the promise of the RPC function registration for each file enabled to be read remotely.
function ManageReadFileRegistration(registration, driver_name, file, driver_mp_node, idx, list){
driver_mp_node['/'+file.name].reg_read_function = registration;
logger.debug('[WAMP] - '+driver_name+' --> ' + file.name + ' read function registered!');
if (idx === list.length - 1){
mp_list[driver_name] = driver_mp_node;
logger.info("[DRIVER] - "+driver_name+" --> RPC read functions successfully registered!");
}
}
// Function used to manage the promise of the RPC function registration for each file enabled to be written remotely.
function ManageWriteFileRegistration(registration, driver_name, file, driver_mp_node, idx, list){
driver_mp_node['/'+file.name].reg_write_function = registration;
logger.debug('[WAMP] - '+driver_name+' --> ' + file.name + ' write function registered!');
if (idx === list.length - 1){
mp_list[driver_name] = driver_mp_node;
logger.info("[DRIVER] - "+driver_name+" --> RPC write functions successfully registered!");
}
}
// Function used to register WAMP write/read functions for each enabled file
function RegisterFiles(driver_name, file, driver_mp_node, idx, list){
if(file.read_function != undefined){
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.read', exports.readRemote ).then(
function(registration){
ManageReadFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}else{
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.read', exports.NotAllowedRemoteFunction ).then(
function(registration){
ManageReadFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}
if(file.write_function != undefined){
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.write', exports.writeRemote ).then(
function(registration){
ManageWriteFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}else{
session_drivers.register('s4t.'+boardCode+'.driver.'+driver_name+'.'+file.name+'.write', exports.NotAllowedRemoteFunction ).then(
function(registration){
ManageWriteFileRegistration(registration, driver_name, file, driver_mp_node, idx, list);
}
);
}
}
// Function used to mount via FUSE the driver and register the RPC functions for each file;
// if the driver mounting happens after a connection recovery we will only register again the RPC functions without re-mounting the driver via FUSE;
function LoadDriver(driver_name, mountpoint, remote, mirror_board){
var driver_path = DRIVERS_STORE+driver_name;
var driver_conf = driver_path+"/"+driver_name+".json";
var driver_module = driver_path+"/"+driver_name+".js";
var rest_response = {};
var d = Q.defer();
try{
var driver = require(driver_module);
var driverJSON = JSON.parse(fs.readFileSync(driver_conf, 'utf8'));
logger.debug('[DRIVER] - '+driver_name+' --> JSON file '+ driver_name +'.json successfully parsed!');
driver_name = driverJSON.name;
var type = driverJSON.type; //logger.info("\tfile type: " + type)
var permissions = MaskConversion(driverJSON.permissions); //logger.info("\tpermissions: " + MaskConversion(permissions))
//var root_permissions = MaskConversion(driverJSON.root_permissions);
var children = driverJSON.children; //logger.info("Files in the folder:")
logger.debug("[DRIVER] - "+driver_name+" --> driver configuration loaded!");
mp_list[driver_name]={};
driver_mp_node = mp_list[driver_name];
fuse_root_path='/';
var root_mp = {
mtime: new Date(),
atime: new Date(),
ctime: new Date(),
size: 100,
mode: permissions,
uid: process.getuid(),
gid: process.getgid()
};
driver_mp_node[fuse_root_path]={
name: driver_name,
mp: {},
remote: remote,
mirror_board: mirror_board
};
driver_mp_node[fuse_root_path].mp=root_mp;
driver_mp_node[fuse_root_path].type = "folder";
file_list[driver_name]=[];
children.forEach(function(file, idx, list) {
setTimeout(function() {
logger.debug("[DRIVER] - "+driver_name+" --> analyzing file: "+file.name);
file_list[driver_name].push(file.name);
//fuse_file_path='/'+driver_name+'/'+file.name;
fuse_file_path='/'+file.name;
driver_mp_node[fuse_file_path]={
name:"",
read_function: null,
write_function: null,
mp: {},
reg_read_function: null,
reg_write_function: null
};
if(file.read_function != undefined){
var read_function = file.read_function;
}else{
var read_function = null;
}
if(file.write_function != undefined){
var write_function = file.write_function;
}else{
var write_function = null;
}
var file_mp = {
mtime: new Date(),
atime: new Date(),
ctime: new Date(),
size: 100,
mode: MaskConversion(file.permissions),
uid: process.getuid(),
gid: process.getgid()
};
driver_mp_node[fuse_file_path].mp = file_mp;
driver_mp_node[fuse_file_path].name = file.name;
driver_mp_node[fuse_file_path].type = "file";
driver_mp_node[fuse_file_path].read_function = read_function;
driver_mp_node[fuse_file_path].write_function = write_function;
RegisterFiles(driver_name, file, driver_mp_node, idx, list);
if (idx === list.length - 1){
logger.info("[DRIVER] - "+driver_name+" --> Available files: %s", JSON.stringify(file_list[driver_name]));
}
}, 100); // end of setTimeout function
});
if ( reconnected === false ){
logger.debug("[DRIVER] - "+driver_name+" --> It is necessary to mount the driver (reconnected = " + reconnected + ")");
try{
drivers[driver_name] = driver;
var driverlib = drivers[driver_name];
driverlib['init']( function(init_response){
if(init_response.result == "SUCCESS"){
logger.info("[DRIVER] - "+driver_name+" --> " + init_response.message);
fuse.mount(mountpoint, {
readdir: readdirFunction(driver_name),
getattr: getattrFunction(driver_name),
open: openFunction(driver_name),
read: readFunction(driver_name, mirror_board), //read: readFunction(driver_name, filename, mirror_board),
write: writeFunction(driver, driver_name)
});
rest_response.message = "Driver '"+driver_name+"' successfully mounted!";
rest_response.result = "SUCCESS";
d.resolve(rest_response);
}
else{
logger.warn("[DRIVER] - "+driver_name+" --> " + init_response.message);
rest_response.message = "ERROR during "+driver_name+" initialization -> " +init_response.message;
rest_response.result = "ERROR";
d.resolve(rest_response);
}
});
}
catch(err){
rest_response.message = "ERROR during "+driver_name+" (fuse) mounting: " +err;
rest_response.result = "ERROR";
logger.warn("[DRIVER] - "+driver_name+" --> " + rest_response.message);
d.resolve(rest_response);
}
}else{
logger.debug("[DRIVER] - "+driver_name+" --> It is not necessary to mount the driver (reconnected = " + reconnected + ")");
if(reconnected === true) reconnected = false;
rest_response.message = "No need to mount driver after reconnection!";
rest_response.result = "SUCCESS";
d.resolve(rest_response);
}
}
catch(err){
rest_response.message = err;
rest_response.result = "ERROR";
d.resolve(rest_response);
}
return d.promise;
}
// Function that creates the directory and then mounts the driver.
function MountpointCreation(driver_name, mountpoint, remote, mirror_board, d){
fs.mkdir(mountpoint, 0755, function() {
logger.debug("[DRIVER] - "+driver_name+" ----> folder "+mountpoint+" CREATED!");
AttachMountpoint(driver_name, mountpoint, remote, mirror_board, d);
});
}
// Function that calls the LoadDriver function that will mount the driver
function AttachMountpoint(driver_name, mountpoint, remote, mirror_board, d) {
LoadDriver(driver_name, mountpoint, remote, mirror_board).then(
function(result){
manageDriversConf("update", driver_name, null, "mounted", remote, mirror_board, function(mng_result){
logger.debug("[DRIVER] - "+driver_name+" --> " + mng_result.message);
d.resolve(result);
logger.info("[DRIVER] - "+driver_name+" --> "+ result.message);
});
},
function (error) {
logger.warn("[DRIVER] - "+driver_name+" --> Error in LoadDriver function: "+ JSON.stringify(error));
d.resolve(error);
}
);
}
// Wrapper for Fuse write-file function.
function writeFunction(driver, driver_name){
return function (mountpoint, fd, buffer, length, position, cb) {
logger.debug('[DRIVER] - '+driver_name+' --> Writing ', buffer.slice(0, length));
content = buffer.slice(0, length);
logger.debug('[DRIVER] - '+driver_name+' --> buffer content: ' + content.toString());
logger.debug('[DRIVER] - '+driver_name+' --> buffer length: ' + length.toString());
driver_mp_node = mp_list[driver_name];
if (driver_mp_node[mountpoint].write_function === null){
cb(fuse.EACCES);
} else {
driver[driver_mp_node[mountpoint].write_function ]( content, function(){
cb(length);
});
}
};
}
// Wrapper for Fuse read-directory function
function readdirFunction(driver_name){
return function (mountpoint, cb) {
logger.debug("[DRIVER] - "+driver_name+" --> readdir(%s) - files list: %s", mountpoint, JSON.stringify(file_list[driver_name]) );
//if (mountpoint === '/') return cb(0, [driver_name]);
//if (mountpoint === '/'+driver_name) return cb(0, file_list[driver_name] );
if (mountpoint === '/') return cb(0, file_list[driver_name] );
cb(0);
};
}
// Wrapper for Fuse get-attr function
function getattrFunction(driver_name){
return function (mountpoint, cb) {
logger.debug("[DRIVER] - "+driver_name+" --> getattr(%s)", mountpoint);
driver_mp_node = mp_list[driver_name];
if(driver_mp_node[mountpoint].mp != undefined){
cb(0, driver_mp_node[mountpoint].mp );
return
}
cb(fuse.ENOENT);
}
}
// Wrapper for Fuse open-file function
function openFunction(driver_name){
return function (mountpoint, flags, cb) {
fd_index = fd_index + 1;
logger.debug("[DRIVER] - "+driver_name+" --> Open(%s, %d) - fd = %s", mountpoint, flags, fd_index);
cb(0, fd_index); //cb(0, 42) // 42 is an fd
}
}
// Wrapper for Fuse read-file function
function readFunction(driver_name, mirror_board){
return function (mountpoint, fd, buf, len, pos, cb) {
driver_mp_node = mp_list[driver_name];
var driver = drivers[driver_name];
// To read a file of a driver mounted locally that return as result the value of a local endpoint (a sensor, etc.)
if (driver_mp_node['/'].remote === "false"){
logger.debug("[DRIVER] - "+driver_name+" --> Read(%s, %d, %d, %d)", mountpoint, fd, len, pos);
driver[driver_mp_node[mountpoint].read_function]( function(read_content){
var buffer = new Buffer(read_content.toString(), 'utf-8');
var str = ''+buffer.slice(pos);
if (!str)
return cb(0);
buf.write(str);
return cb(str.length);
});
}else{
// To read a file of a driver mounted locally that return as result the value of a remote endpoint (i.e. a sensor on a remote board, etc.)
// A mirrored board is a board that share its hardware that can be used by a board that mount remotely the same driver.
var filename = mountpoint.replace('/','');
logger.debug('[DRIVER] - '+driver_name+' - REMOTE CALLING to '+mirror_board + ' RPC called: s4t.'+mirror_board+'.driver.'+driver_name+'.'+filename+'.read');
// Call the RPC read function of the board that shares the hardware
session_drivers.call('s4t.'+mirror_board+'.driver.'+driver_name+'.'+filename+'.read', [driver_name, filename]).then(
function(read_content){
var buffer = new Buffer(read_content.toString(), 'utf-8');
var str = ''+buffer.slice(pos);
if (!str)
return cb(0);
buf.write(str);
return cb(str.length);
}
);
}
}
}
// Function used to convert file permission rappresentation from base 10 to 8
function MaskConversion(mode_b10){
//var mode_b10 = 100644//40755
mode_b8 = parseInt(mode_b10.toString(10), 8);
//logger.info("from b10 "+mode_b10+" to b8 "+mode_b8)
permission = mode_b8;
return permission
}
/*
function HumanMaskConversion(mode_b10){
mode_b8 = parseInt(mode_b10.toString(10), 8)
//logger.info("from b10 "+mode_b10+" to b8 "+mode_b8)
permission = mode_b8
return permission
}
*/
//This function exports all the functions in the module as WAMP remote procedure calls

@@ -1481,0 +1521,0 @@ exports.exportDriverCommands = function (session){

@@ -1,11 +0,19 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2014 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
var plugin_name;

@@ -12,0 +20,0 @@ var plugin_json;

@@ -1,10 +0,20 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2014 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Andrea Rocco Lotronto, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
//service logging configuration: "managePlugins"

@@ -25,140 +35,4 @@ var logger = log4js.getLogger('managePlugins');

// This function executes a syncronous plugin ("call" as the exection of a command that returns a value to the "caller"): it is called by Iotronic via RPC
exports.call = function (args, details){
//Parsing the input arguments
var plugin_name = String(args[0]);
var plugin_json = String(args[1]);
var d = Q.defer();
// The autostart parameter at RUN stage is OPTIONAL. It is used at this stage if the user needs to change the boot execution configuration of the plugin after the INJECTION stage.
var plugin_autostart = "";
logger.info('[PLUGIN] - Execution request for \"'+ plugin_name +'\" plugin with parameter json: '+plugin_json);
try{
//Reading the plugins.json configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
}
catch(err){
logger.error('[PLUGIN] --> Error parsing JSON file plugins.json');
}
//If the plugin exists
if(pluginsConf["plugins"].hasOwnProperty(plugin_name)){
logger.info("[PLUGIN] --> Plugin successfully loaded!");
//Check the plugin status
var status = pluginsConf.plugins[plugin_name].status;
if (status == "off" || status == "injected"){
logger.info("[PLUGIN] --> Plugin " + plugin_name + " being started");
//Create a new process that has plugin-wrapper as code
var child = cp.fork(LIGHTNINGROD_HOME + '/modules/plugins-manager/call-wrapper');
//Prepare the message I will send to the process with name of the plugin to start and json file as argument
var input_message = {
"plugin_name": plugin_name,
"plugin_json": JSON.parse(plugin_json)
};
child.on('message', function(msg) {
if(msg.name != undefined){
if (msg.status === "alive"){
//Creating the plugin json schema
var plugin_folder = PLUGINS_STORE + plugin_name;
var schema_outputFilename = plugin_folder + "/" + plugin_name + '.json';
fs.writeFile(schema_outputFilename, plugin_json, function(err) {
if(err) {
logger.error('[PLUGIN] --> Error parsing '+plugin_name+'.json file: ' + err);
} else {
logger.info('[PLUGIN] --> Plugin JSON schema saved to ' + schema_outputFilename);
// - change the plugin status from "off" to "on" and update the PID value
pluginsConf.plugins[plugin_name].status = "on";
pluginsConf.plugins[plugin_name].pid = child.pid;
fs.writeFile(PLUGINS_SETTING, JSON.stringify(pluginsConf, null, 4), function(err) {
if(err) {
logger.error('[PLUGIN] --> Error writing plugins.json file: ' + err);
} else {
logger.info("[PLUGIN] --> JSON file plugins.json updated -> " + plugin_name + ': status < '+ pluginsConf.plugins[plugin_name].status + ' > ' + pluginsConf.plugins[plugin_name].pid);
}
});
}
});
} else if(msg.status === "finish") {
logger.info("[PLUGIN] --> RESULT: ", msg.logmsg);
d.resolve(msg.logmsg);
} else if(msg.status === "fault") {
logger.info("[PLUGIN] --> RESULT: ", msg.logmsg);
d.resolve(msg.logmsg);
} else if(msg.level === "error") {
logger.error("[PLUGIN] --> "+ msg.name + ": " + msg.logmsg);
} else if(msg.level === "warn") {
logger.warn("[PLUGIN] --> "+ msg.name + ": " + msg.logmsg);
}
else{
logger.info("[PLUGIN] --> "+ msg.name + ": " + msg.logmsg);
}
}
else{
//serve per gestire il primo messaggio alla creazione del child
logger.info("[PLUGIN] --> " + msg);
}
});
//I send the input to the wrapper so that it can launch the proper plugin with the proper json file as argument
child.send(input_message);
return d.promise;
}
else{
logger.warn("[PLUGIN] --> Call already started!");
return 'Call already started on this board!';
}
}
else{
// Here the plugin does not exist
logger.warn("[PLUGIN] --> Call \"" + plugin_name + "\" does not exist on this board!");
return 'Call does not exist on this board!';
}
};
// This function checks if the plugin process is still alive otherwise starts it

@@ -347,2 +221,3 @@ function pluginStarter(plugin_name, timer, plugin_json_name, skip) {

// This function delete the timer associated with a plugin

@@ -369,3 +244,228 @@ function clearPluginTimer(plugin_name) {

// This function checks if the plugin has to be restarted
// Function used to delete all driver files during driver removing from the board
function deleteFolderRecursive(path){
if( fs.existsSync(path) ) {
fs.readdirSync(path).forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) {
// recurse
deleteFolderRecursive(curPath);
} else {
// delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
// Function to clean all plugin data (folder and configuration)
function cleanPluginData(plugin_name){
var response = {
message: '',
result: ''
};
var d = Q.defer();
var plugin_folder = PLUGINS_STORE + plugin_name;
if ( fs.existsSync(plugin_folder) === true ){
deleteFolderRecursive(plugin_folder); //delete plugin files and the folder
logger.debug('[PLUGIN] --> Plugin folder deleted.');
}
else{
logger.debug('[PLUGIN] --> Plugin folder already deleted.');
}
logger.debug('[PLUGIN] --> Plugin data cleaning...');
//Reading the plugins configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
if( pluginsConf["plugins"].hasOwnProperty(plugin_name) ){
var pluginStatus = pluginsConf.plugins[plugin_name]['status'];
pluginsConf.plugins[plugin_name]=null;
delete pluginsConf.plugins[plugin_name];
fs.writeFile(PLUGINS_SETTING, JSON.stringify(pluginsConf, null, 4), function(err) {
if(err) {
response.result = "ERROR";
response.message = "plugin.json updating FAILED: "+err;
d.resolve(response);
} else {
logger.debug("[PLUGIN] ----> plugins.json file updated!");
response.result = "SUCCESS";
d.resolve(response);
}
});
}else{
logger.debug("[PLUGIN] ----> plugins.json already clean!");
response.result = "SUCCESS";
d.resolve(response);
}
return d.promise;
}
// RPC to execute a syncronous plugin ("call" as the exection of a command that returns a value to the "caller"): it is called by Iotronic via RPC
exports.call = function (args, details){
//Parsing the input arguments
var plugin_name = String(args[0]);
var plugin_json = String(args[1]);
var d = Q.defer();
// The autostart parameter at RUN stage is OPTIONAL. It is used at this stage if the user needs to change the boot execution configuration of the plugin after the INJECTION stage.
var plugin_autostart = "";
logger.info('[PLUGIN] - Execution request for \"'+ plugin_name +'\" plugin with parameter json: '+plugin_json);
try{
//Reading the plugins.json configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
}
catch(err){
logger.error('[PLUGIN] --> Error parsing JSON file plugins.json');
}
//If the plugin exists
if(pluginsConf["plugins"].hasOwnProperty(plugin_name)){
logger.info("[PLUGIN] --> Plugin successfully loaded!");
//Check the plugin status
var status = pluginsConf.plugins[plugin_name].status;
if (status == "off" || status == "injected"){
logger.info("[PLUGIN] --> Plugin " + plugin_name + " being started");
//Create a new process that has plugin-wrapper as code
var child = cp.fork(LIGHTNINGROD_HOME + '/modules/plugins-manager/call-wrapper');
//Prepare the message I will send to the process with name of the plugin to start and json file as argument
var input_message = {
"plugin_name": plugin_name,
"plugin_json": JSON.parse(plugin_json)
};
child.on('message', function(msg) {
if(msg.name != undefined){
if (msg.status === "alive"){
//Creating the plugin json schema
var plugin_folder = PLUGINS_STORE + plugin_name;
var schema_outputFilename = plugin_folder + "/" + plugin_name + '.json';
fs.writeFile(schema_outputFilename, plugin_json, function(err) {
if(err) {
logger.error('[PLUGIN] --> Error parsing '+plugin_name+'.json file: ' + err);
} else {
logger.info('[PLUGIN] --> Plugin JSON schema saved to ' + schema_outputFilename);
// - change the plugin status from "off" to "on" and update the PID value
pluginsConf.plugins[plugin_name].status = "on";
pluginsConf.plugins[plugin_name].pid = child.pid;
fs.writeFile(PLUGINS_SETTING, JSON.stringify(pluginsConf, null, 4), function(err) {
if(err) {
logger.error('[PLUGIN] --> Error writing plugins.json file: ' + err);
} else {
logger.info("[PLUGIN] --> JSON file plugins.json updated -> " + plugin_name + ': status < '+ pluginsConf.plugins[plugin_name].status + ' > ' + pluginsConf.plugins[plugin_name].pid);
}
});
}
});
} else if(msg.status === "finish") {
logger.info("[PLUGIN] --> RESULT: ", msg.logmsg);
d.resolve(msg.logmsg);
} else if(msg.status === "fault") {
logger.info("[PLUGIN] --> RESULT: ", msg.logmsg);
d.resolve(msg.logmsg);
} else if(msg.level === "error") {
logger.error("[PLUGIN] --> "+ msg.name + ": " + msg.logmsg);
} else if(msg.level === "warn") {
logger.warn("[PLUGIN] --> "+ msg.name + ": " + msg.logmsg);
}
else{
logger.info("[PLUGIN] --> "+ msg.name + ": " + msg.logmsg);
}
}
else{
//serve per gestire il primo messaggio alla creazione del child
logger.info("[PLUGIN] --> " + msg);
}
});
//I send the input to the wrapper so that it can launch the proper plugin with the proper json file as argument
child.send(input_message);
return d.promise;
}
else{
logger.warn("[PLUGIN] --> Call already started!");
return 'Call already started on this board!';
}
}
else{
// Here the plugin does not exist
logger.warn("[PLUGIN] --> Call \"" + plugin_name + "\" does not exist on this board!");
return 'Call does not exist on this board!';
}
};
// RPC to check if the plugin has to be restarted
exports.pluginKeepAlive = function (plugin_name){

@@ -433,3 +533,3 @@

// This function is used to restart all enabled plugins at LR startup...moreover associates a timer with each plugin to check if the plugin process is alive
// RPC to restart all enabled plugins at LR startup...moreover associates a timer with each plugin to check if the plugin process is alive
exports.pluginsLoader = function (){

@@ -487,3 +587,3 @@

// This function puts in running an asynchronous plugin in a new process: it is called by Iotronic via RPC
// RPC to put in running an asynchronous plugin in a new process: it is called by Iotronic via RPC
exports.run = function (args){

@@ -505,3 +605,3 @@

logger.info('[PLUGIN] - Run plugin RPC called for plugin '+ plugin_name +' plugin...');
logger.info('[PLUGIN] - Run plugin RPC called for plugin "'+ plugin_name +'" plugin...');
logger.info("[PLUGIN] --> Input parameters:\n"+ plugin_json);

@@ -516,3 +616,3 @@

response.message = 'Error parsing plugins.json!';
logger.error('[PLUGIN] - '+plugin_name + ' plugin execution error: '+response.message);
logger.error('[PLUGIN] - "' + plugin_name + '" plugin execution error: '+response.message);
d.resolve(response);

@@ -530,3 +630,3 @@ }

if (status == "off" || status == "injected"){
logger.info('[PLUGIN] - '+ plugin_name + ' - Plugin starting...');

@@ -668,3 +768,3 @@

response.result = "ERROR";
response.message = "Plugin \"" + plugin_name + "\" does not exist on this board!";
response.message = "Plugin '" + plugin_name + "' does not exist on this board!";
logger.warn('[PLUGIN] - '+plugin_name + ' - '+response.message);

@@ -680,3 +780,3 @@ d.resolve(response);

// This function stop/kill a running asynchronous plugin: it is called by Iotronic via RPC
// RPC to stop/kill a running asynchronous plugin: it is called by Iotronic via RPC
exports.kill = function (args){

@@ -730,2 +830,6 @@

clearPluginTimer(plugin_name);
response.result = "SUCCESS";
response.message = 'Plugin killed!';
logger.info('[PLUGIN] - stop plugin '+plugin_name + ': '+response.message);
d.resolve(response);
}

@@ -735,6 +839,3 @@

response.result = "SUCCESS";
response.message = 'Plugin killed!';
logger.info('[PLUGIN] - stop plugin '+plugin_name + ': '+response.message);
d.resolve(response);

@@ -744,2 +845,3 @@ }

response.result = "ERROR";
response.code = "NO-RUN";
response.message = 'Plugin is not running on this board!';

@@ -750,3 +852,8 @@ logger.error('[PLUGIN] - stop plugin '+plugin_name + ': '+response.message);

}
}else{
response.result = "ERROR";
response.message = "Plugin '" + plugin_name + "' is not injected on this board!";
logger.error('[PLUGIN] - stop plugin ' + plugin_name + ': '+response.message);
d.resolve(response);
}

@@ -768,3 +875,3 @@

// This function manage the injection request of a plugin into the device: it is called by Iotronic via RPC
// RPC to manage the injection request of a plugin into the device: it is called by Iotronic via RPC
exports.injectPlugin = function(args){

@@ -780,3 +887,3 @@

logger.info("[PLUGIN] - Injecting plugin RPC called for "+plugin_name+" plugin...");
logger.info("[PLUGIN] --> Parameters injected: { plugin_name : " + plugin_name + ", autostart : " + autostart + " }");
logger.debug("[PLUGIN] --> Parameters injected: { plugin_name : " + plugin_name + ", autostart : " + autostart + " }");
logger.debug("[PLUGIN] --> plugin code:\n\n" + JSON.stringify(plugin_code) + "\n\n");

@@ -793,41 +900,22 @@

var fileName = plugin_folder + "/" + plugin_name + '.js';
cleanPluginData(plugin_name).then(
// Check plugin folder
if ( fs.existsSync(plugin_folder) === false ){
function (clean_res) {
// plugin folder creation
fs.mkdir(plugin_folder, function() {
if (clean_res.result == "SUCCESS"){
// Writing the file
clean_res.message = "plugin '" + plugin_name + "' environment is clean!";
logger.debug("[PLUGIN] ----> " + clean_res.message);
fs.writeFile(fileName, plugin_code, function(err) {
// plugin folder creation
fs.mkdir(plugin_folder, function() {
if(err) {
// Writing the file
fs.writeFile(fileName, plugin_code, function(err) {
response.result = "ERROR";
response.message = 'Error writing '+ fileName +' file: ' + err;
logger.error('[PLUGIN] --> ' + response.message);
d.resolve(response);
} else {
//Reading the plugins configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
//Update the data structure of the plugin
pluginsConf.plugins[plugin_name] = {};
pluginsConf.plugins[plugin_name]['status'] = "injected";
if(autostart != undefined){
pluginsConf.plugins[plugin_name]['autostart'] = autostart;
} else {
pluginsConf.plugins[plugin_name]['autostart'] = false;
}
//Update plugins.json config file
fs.writeFile(PLUGINS_SETTING, JSON.stringify(pluginsConf, null, 4), function(err) {
if(err) {
response.result = "ERROR";
response.message = 'Error writing plugins.json file: ' + err;
response.message = 'Error writing '+ fileName +' file: ' + err;
logger.error('[PLUGIN] --> ' + response.message);

@@ -838,27 +926,52 @@ d.resolve(response);

logger.debug("[PLUGIN] --> Plugins configuration file saved to " + PLUGINS_SETTING);
response.result = "SUCCESS";
response.message = "Plugin "+ plugin_name +" injected successfully!";
logger.info('[PLUGIN] --> ' + response.message);
d.resolve(response);
//Reading the plugins configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
//Update the data structure of the plugin
pluginsConf.plugins[plugin_name] = {};
pluginsConf.plugins[plugin_name]['status'] = "injected";
if(autostart != undefined){
pluginsConf.plugins[plugin_name]['autostart'] = autostart;
} else {
pluginsConf.plugins[plugin_name]['autostart'] = false;
}
//Update plugins.json config file
fs.writeFile(PLUGINS_SETTING, JSON.stringify(pluginsConf, null, 4), function(err) {
if(err) {
response.result = "ERROR";
response.message = 'Error writing plugins.json file: ' + err;
logger.error('[PLUGIN] --> ' + response.message);
d.resolve(response);
} else {
logger.debug("[PLUGIN] --> Configuration in plugins.json updated!");
response.result = "SUCCESS";
response.message = "Plugin '"+ plugin_name +"' injected successfully!";
logger.info('[PLUGIN] --> ' + response.message);
d.resolve(response);
}
});
}
});
}
});
});
});
}else{
} else{
logger.error("[PLUGIN] --> " + clean_res.message);
d.resolve(clean_res.message);
}
response.result = "ERROR";
response.message = "ERROR: "+plugin_name+" plugin's files already injected! - Remove the previous plugin installation!";
logger.warn('[PLUGIN] --> ' + response.message);
d.resolve(response);
}
}
);
return d.promise;

@@ -869,82 +982,65 @@

// Function used to delete all driver files during driver removing from the board
function deleteFolderRecursive(path){
if( fs.existsSync(path) ) {
fs.readdirSync(path).forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) {
// recurse
deleteFolderRecursive(curPath);
} else {
// delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
// This function manage the removal of a plugin from the device: it is called by Iotronic via RPC
// RPC to manage the removal of a plugin from the device: it is called by Iotronic via RPC
exports.removePlugin = function(args){
// Parsing the input arguments
plugin_name = String(args[0]);
var plugin_name = String(args[0]);
logger.info("[PLUGIN] - Removing plugin RPC called for " + plugin_name +" plugin...");
var d = Q.defer();
var response = {
message: '',
result: ''
};
var plugin_folder = PLUGINS_STORE + plugin_name;
var pluginFileName = plugin_folder + "/" + plugin_name + '.js';
var pluginConfFileName = plugin_folder + "/" + plugin_name+'.json';
var d = Q.defer();
if ( fs.existsSync(plugin_folder) === true ){
//Reading the plugins.json configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
var pid = pluginsConf.plugins[plugin_name].pid;
deleteFolderRecursive(plugin_folder); //delete plugin files folder
// if the plugin is not running the pid is NULL or "", in this condition "is-running" module return "true" that is a WRONG result!
if (running(pid) == false || pid == null || pid == ""){
logger.debug('[PLUGIN] --> Plugin data cleaning...');
if ( fs.existsSync(plugin_folder) === true ){
//Reading the plugins configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
cleanPluginData(plugin_name).then(
if( pluginsConf["plugins"].hasOwnProperty(plugin_name) ){
function (clean_res) {
var pluginStatus = pluginsConf.plugins[plugin_name]['status'];
if (clean_res.result == "SUCCESS"){
pluginsConf.plugins[plugin_name]=null;
delete pluginsConf.plugins[plugin_name];
logger.debug("[PLUGIN] --> Plugin board successfully removed from plugins.json!" );
response.message = "Plugin '" + plugin_name + "' successfully removed!";
response.result = clean_res.result;
logger.info("[PLUGIN] --> " + response.message);
d.resolve(response);
fs.writeFile(PLUGINS_SETTING, JSON.stringify(pluginsConf, null, 4), function(err) {
}else{
if(err) {
response = "plugin.json file updating FAILED: "+err;
logger.error("[PLUGIN] --> plugin.json updating FAILED: "+err);
d.resolve(response);
logger.error("[PLUGIN] --> " + clean_res.message);
d.resolve(clean_res);
} else {
}
logger.debug("[PLUGIN] --> plugins.json file updated!");
response = plugin_name+" completely removed from board!";
logger.info("[PLUGIN] --> " + plugin_name + " - plugin completely removed from board!");
d.resolve(response);
}
});
);
}else{
logger.warn("[PLUGIN] --> plugins.json already clean!");
response = plugin_name+" completely removed from board!";
logger.info("[PLUGIN] --> " + plugin_name + " - plugin completely removed from board!");
response.message = "Plugin folder ("+plugin_folder+") not found!";
response.result = "WARNING";
logger.warn("[PLUGIN] --> " + response.message);
d.resolve(response);
}
}else{
response = "Plugin "+pluginFileName+" not found!";
logger.warn("[PLUGIN] --> Plugin "+pluginFileName+" not found!");
response.message = "Plugin '" + plugin_name + "' is still running! Please stop it before remove it from the board.";
response.result = "WARNING";
logger.warn("[PLUGIN] --> " + response.message);
d.resolve(response);

@@ -954,101 +1050,100 @@

return d.promise;
};
/*
fs.exists(pluginFileName, function(exists) {
if(exists) {
logger.debug('[PLUGIN] --> File '+pluginFileName+' exists. Deleting now ...');
// RPC called to restart a plugin
exports.restartPlugin = function(args){
fs.unlink(pluginFileName, function(err) {
var plugin_name = String(args[0]);
if(err) {
response = "[PLUGIN] --> Plugin file removing FAILED: "+err;
logger.error(response);
d.resolve(response);
}
});
} else {
response = "Plugin "+pluginFileName+" not found!";
logger.warn("[PLUGIN] --> Plugin "+pluginFileName+" not found!");
}
logger.debug('[PLUGIN] --> Plugin data cleaning...');
logger.info('[PLUGIN] - Restart plugin RPC called for plugin "'+ plugin_name +'" plugin...');
//Reading the plugins configuration file
var d = Q.defer();
var response = {
message: '',
result: ''
};
// Get the plugin's configuration.
try{
//Reading the plugins.json configuration file
var pluginsConf = JSON.parse(fs.readFileSync(PLUGINS_SETTING, 'utf8'));
if( pluginsConf["plugins"].hasOwnProperty(plugin_name) ){
//If the plugin exists
if(pluginsConf["plugins"].hasOwnProperty(plugin_name)){
var pluginStatus = pluginsConf.plugins[plugin_name]['status'];
pluginsConf.plugins[plugin_name]=null;
delete pluginsConf.plugins[plugin_name];
logger.debug("[PLUGIN] --> Plugin board successfully removed from plugins.json!" );
fs.writeFile(PLUGINS_SETTING, JSON.stringify(pluginsConf, null, 4), function(err) {
exports.kill([plugin_name]).then(
if(err) {
response = "plugin.json file updating FAILED: "+err;
logger.error("[PLUGIN] --> plugin.json updating FAILED: "+err);
d.resolve(response);
} else {
logger.debug("[PLUGIN] --> plugins.json file updated!");
fs.exists(pluginConfFileName, function(exists) {
function (response) {
if(exists) {
if(response.result == "SUCCESS" || response.code == "NO-RUN"){
fs.unlink(pluginConfFileName, function (err) {
if (err){
response = pluginConfFileName+" file deleting FAILED: "+err;
logger.warn("[PLUGIN] --> "+pluginConfFileName+" file deleting FAILED: "+err);
var plugin_json_name = PLUGINS_STORE + plugin_name + "/" + plugin_name + '.json';
var plugin_json_schema = JSON.parse(fs.readFileSync(plugin_json_name, 'utf8'));
exports.run([plugin_name, JSON.stringify(plugin_json_schema)]).then(
function (response) {
if(response.result == "SUCCESS"){
response.result = "SUCCESS";
response.message = "Plugin '"+plugin_name+"' successfully restarted";
logger.info("[PLUGIN] - " + response.message);
d.resolve(response);
}else{
logger.debug("[PLUGIN] --> "+pluginConfFileName+" file successfully deleted!");
response = plugin_name+" completely removed from board!";
logger.info("[PLUGIN] --> " + plugin_name + " - plugin completely removed from board!");
response.result = "ERROR";
response.message = "Error restarting plugin '"+plugin_name+"' during starting procedure!";
logger.error("[PLUGIN] - " + response.message);
d.resolve(response);
}
});
}else{
logger.warn("[PLUGIN] --> "+pluginConfFileName+" file does not exist! - Plugin was in status: " + pluginStatus);
response = plugin_name+" completely removed from board!";
logger.info("[PLUGIN] --> " + plugin_name + " - plugin completely removed from board!");
d.resolve(response);
}
});
}
}
);
});
}else{
logger.warn("[PLUGIN] --> plugins.json already clean!");
response = plugin_name+" completely removed from board!";
logger.info("[PLUGIN] --> " + plugin_name + " - plugin completely removed from board!");
d.resolve(response);
}
});
*/
}else {
return d.promise;
};
response.result = "ERROR";
response.message = "Error restarting plugin '" + plugin_name + "' during killing procedure!";
logger.error("[PLUGIN] - " + response.message);
d.resolve(response);
}
}
);
}else{
// the plugin does not exist
response.result = "ERROR";
response.message = "Call \"" + plugin_name + "\" does not exist on this board!";
logger.error("[PLUGIN] - " + response.message);
d.resolve(response);
}
}
catch(err){
logger.error('[PLUGIN] --> Error parsing JSON file plugins.json');
}
return d.promise;
};
//This function exports all the functions in the module as WAMP remote procedure calls

@@ -1058,7 +1153,8 @@ exports.exportPluginCommands = function (session){

//Register all the module functions as WAMP RPCs
session.register('s4t'+ boardCode+'.plugin.run', exports.run);
session.register('s4t'+ boardCode+'.plugin.kill', exports.kill);
session.register('s4t'+ boardCode+'.plugin.inject', exports.injectPlugin);
session.register('s4t'+ boardCode+'.plugin.call', exports.call);
session.register('s4t'+ boardCode+'.plugin.remove', exports.removePlugin);
session.register('s4t.'+ boardCode+'.plugin.run', exports.run);
session.register('s4t.'+ boardCode+'.plugin.kill', exports.kill);
session.register('s4t.'+ boardCode+'.plugin.inject', exports.injectPlugin);
session.register('s4t.'+ boardCode+'.plugin.call', exports.call);
session.register('s4t.'+ boardCode+'.plugin.remove', exports.removePlugin);
session.register('s4t.'+ boardCode+'.plugin.restart', exports.restartPlugin);

@@ -1065,0 +1161,0 @@ logger.info('[WAMP-EXPORTS] Plugin commands exported to the cloud!');

@@ -1,10 +0,20 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
SETTINGS = process.env.IOTRONIC_HOME+'/settings.json';

@@ -24,15 +34,8 @@ nconf = require('nconf');

var requestify = require('requestify');
var Q = require("q");
var ckan_addr = 'smartme-data.unime.it';
var ckan_host = 'http://'+ckan_addr;
exports.getLogger = function (){
return logger;
};

@@ -58,3 +61,2 @@ exports.getPosition = function (){

exports.getLocalTime = function (){

@@ -74,67 +76,1 @@

};
exports.sendToCKAN = function (m_authid, m_resourceid, record, callback){
var http = require('http');
var payload = {
resource_id : m_resourceid,
method: 'insert',
records : record
};
var payloadJSON = JSON.stringify(payload);
var header = {
'Content-Type': "application/json",
'Authorization' : m_authid,
'Content-Length': Buffer.byteLength(payloadJSON)
};
var options = {
host: ckan_addr,
port: 80,
path: '/api/3/action/datastore_upsert',
method: 'POST',
headers: header
};
var req = http.request(options, function(res) {
res.setEncoding('utf-8');
var responseString = '';
res.on('data', function(data) {
console.log('On data:' + data);
});
res.on('end', function() {});
});
req.on('error', function(e) {
console.log('On Error:' + e);
});
req.write(payloadJSON);
req.end();
callback(payloadJSON);
};
exports.getCKANdataset = function(id, callback){
requestify.get(ckan_host + '/api/rest/dataset/'+id).then( function(response) {
var dataCKAN = response.getBody();
callback(dataCKAN);
});
};

@@ -1,10 +0,20 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2014 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Andrea Rocco Lotronto, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
var plugin_name;

@@ -11,0 +21,0 @@ var plugin_json;

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments, callback){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments, callback){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments, callback){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments, callback){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments, callback){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments, callback){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Francesco Longo, Giovanni Merlino
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
//JSON to send

@@ -2,0 +20,0 @@ // {

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments){

@@ -2,0 +20,0 @@

@@ -0,1 +1,19 @@

//############################################################################################
//##
//# Copyright (C) 2015-2016 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
exports.main = function (arguments){

@@ -2,0 +20,0 @@

@@ -1,10 +0,20 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2014 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Andrea Rocco Lotronto, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2017 Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
//service logging configuration: "manageCommands"

@@ -37,3 +47,3 @@ var logger = log4js.getLogger('manageServices');

logger.debug("[SERVICE] - RPC enableService called: " + args)
logger.debug("[SERVICE] - RPC enableService called: " + args);

@@ -47,3 +57,3 @@ //Looking for the process in the array

logger.info('[SERVICE] - Restoring tunnel for '+ serviceName + 'service...');
logger.info("[SERVICE] - Restoring tunnel for '"+ serviceName + "' service...");

@@ -220,3 +230,3 @@ //Killing the process

//Getting the path of the wstt.js module from the configuration file
//Getting the path of the wstun.js module from the configuration file
var reverseTunnellingClient = nconf.get('config:reverse:lib:bin');

@@ -265,67 +275,4 @@

/*
exports.exportService = function(args){
//Parsing arguments
serviceName = args[1];
remotePort = args[2];
operation = args[3];
//Getting information from the configuration file
localPort = nconf.get('config:board:services:'+serviceName+':port');
reverseTunnellingServer = nconf.get('config:reverse:server:url_reverse')+":"+nconf.get('config:reverse:server:port_reverse');
logger.info('[SERVICE] - '+ operation + ' service ' + serviceName + ' on local port ' + localPort + ' with remote port ' + remotePort + ' contacting remote server ' + reverseTunnellingServer);
//Getting the path of the wstt.js module from the configuration file
var reverseTunnellingClient = nconf.get('config:reverse:lib:bin');
//If the operation is start
if(operation === "start"){
//I spawn a process executing the reverse tunnel client with appropriate parameters
var spawn = require('child_process').spawn;
logger.debug('[SERVICE] - Executing command: ' + reverseTunnellingClient + ' -r '+remotePort+':'+'127.0.0.1'+':'+localPort + ' ' + reverseTunnellingServer);
//I insert the new service in the array so that I can find it later when I have to stop the service
var newService = {
key: serviceName,
process: spawn(reverseTunnellingClient, ['-r '+remotePort+':'+'127.0.0.1'+':'+localPort, reverseTunnellingServer])
};
servicesProcess.push(newService);
newService.process.stdout.on('data', function(data){
logger.debug('[SERVICE] - '+newService.key+' stdout of process ' + newService.process.pid + ': '+data);
});
newService.process.stderr.on('data', function(data){
logger.debug('[SERVICE] - '+newService.key+' stderr of process ' + newService.process.pid + ': '+ data);
});
newService.process.on('close', function(code){
logger.debug('[SERVICE] - '+newService.key+' child process ' + newService.process.pid + ' exited with code '+ code);
});
}
if(operation === "stop"){
//Looking for the process in the array
var i = findValue(servicesProcess, serviceName, 'key');
//Killing the process
logger.info('[SERVICE] - Killing '+serviceName+' process: ' + servicesProcess[i].process.pid);
servicesProcess[i].process.kill('SIGINT');
servicesProcess.splice(i,1);
}
};
*/
//This function exports all the functions in the module as WAMP remote procedure calls

@@ -344,3 +291,3 @@ exports.exportServiceCommands = function (session){

function (response) {
logger.info('[SERVICE] --> response from IoTronic: ' + response.message);
logger.info('[SERVICE] --> Response from IoTronic: ' + response.message);
logger.info('[SERVICE] --> Services restoring completed!');

@@ -347,0 +294,0 @@ }

@@ -1,10 +0,20 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
//service logging configuration: "manageFS"

@@ -11,0 +21,0 @@ var logger = log4js.getLogger('manage-FS-libs');

@@ -1,10 +0,20 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino, Nicola Peditto
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
//service logging configuration: "manageFS"

@@ -11,0 +21,0 @@ var logger = log4js.getLogger('manageFS');

@@ -1,11 +0,22 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Arthur Warnier, Fabio Verboso, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino,
//# Nicola Peditto, Fabio Verboso
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################
//service logging configuration: "manageNetworks"

@@ -24,3 +35,3 @@ var logger = log4js.getLogger('manageNetworks');

var socat_pid = null;
var wstt_pid = null;
var wstun_pid = null;

@@ -49,3 +60,3 @@

logger.info("[VNET] - Network manager loaded!");
logger.info("[VNET] - Network Manager loaded!");

@@ -75,4 +86,4 @@

logger.info("[VNET] - Socat parameters received: " + socatRes);
logger.info("[VNET] - Network backend used: " + net_backend);
logger.info("[VNET] --> Socat parameters received: " + socatRes);
logger.info("[VNET] --> Network backend used: " + net_backend);

@@ -90,7 +101,7 @@ exports.initNetwork(socatServer_ip, socatServer_port, socatBoard_ip, net_backend);

// 1. creates the SOCAT tunnel using the parameter received from Iotronic.
// 2. On SOCAT tunnel completion it will create the WSTT tunnel.
// 2. On SOCAT tunnel completion it will create the WSTUN tunnel.
// 3. On tunnels completion it will advise Iotronic; later Iotronic will add this device to its VLANs.
exports.initNetwork = function (socatServer_ip, socatServer_port, socatBoard_ip, net_backend) {
logger.info("[VNET] - Network initialization...");
//logger.info("[VNET] --> Network initialization...");

@@ -105,9 +116,9 @@ var spawn = require('child_process').spawn;

var socat_config = configFile.config["socat"];
var wstt_config = configFile.config["reverse"];
var wstun_config = configFile.config["reverse"];
logger.info("[VNET] - Boot status:");
logger.info("[VNET] --> Network Boot status:");
// Kill Socat and WSTT processes to clean network status after a network failure
logger.info("[VNET] --> Boot Socat PID: " + socat_pid);
// Kill Socat and WSTUN processes to clean network status after a network failure
logger.info("[VNET] ----> Boot Socat PID: " + socat_pid);

@@ -144,14 +155,14 @@ if (socat_pid != null) {

logger.info("[VNET] --> Boot WSTT PID: " + wstt_pid);
logger.info("[VNET] ----> Boot WSTUN PID: " + wstun_pid);
if (wstt_pid != null) {
if (wstun_pid != null) {
logger.warn("[VNET] ... WSTT cleaning PID [" + wstt_pid + "]");
logger.warn("[VNET] ... WSTUN cleaning PID [" + wstun_pid + "]");
try{
process.kill(wstt_pid)
process.kill(wstun_pid)
}catch (e) {
logger.error('[VNET] ... WSTT cleaning error: ', e);
logger.error('[VNET] ... WSTUN cleaning error: ', e);

@@ -161,11 +172,11 @@ }

} else {
var wstt_pid_conf = nconf.get('config:reverse:pid');
var wstun_pid_conf = nconf.get('config:reverse:pid');
if (wstt_pid_conf != "") {
if (wstun_pid_conf != "") {
if (running(wstt_pid_conf)) {
logger.warn("[VNET] ... WSTT first cleaning PID [" + wstt_pid_conf + "]");
process.kill(wstt_pid_conf)
if (running(wstun_pid_conf)) {
logger.warn("[VNET] ... WSTUN first cleaning PID [" + wstun_pid_conf + "]");
process.kill(wstun_pid_conf)
}else{
logger.debug("[VNET] ... WSTT no cleaning needed!");
logger.debug("[VNET] ... WSTUN no cleaning needed!");
}

@@ -184,3 +195,3 @@

"socatServer_ip": socatServer_ip,
"net_backend": net_backend,
"net_backend": net_backend
//"socatServer_port":socatServer_port, //TO RESTORE

@@ -198,22 +209,21 @@ //"boardCode": boardCode //TO RESTORE

// START WSTT ------------------------------------------------------------------------------------------------
logger.info("[VNET] - WSTT activating...");
// START WSTUN ------------------------------------------------------------------------------------------------
logger.info("[VNET] - WSTUN activating...");
logger.debug("[VNET] - WSTT - " + rtpath + ' -r ' + socatServer_port + ':localhost:' + basePort, reverseS_url);
var wstt_proc = spawn(rtpath, ['-r ' + socatServer_port + ':localhost:' + basePort, reverseS_url]);
//logger.debug("[VNET] - WSTT - " + rtpath + ' -r '+ socatServer_port +':localhost:'+basePort,reverseS_url);
logger.debug("[VNET] - WSTUN - " + rtpath + ' -r ' + socatServer_port + ':localhost:' + basePort, reverseS_url);
var wstun_proc = spawn(rtpath, ['-r ' + socatServer_port + ':localhost:' + basePort, reverseS_url]);
// Save WSTT PID to clean network status after a network failure
wstt_pid = wstt_proc.pid;
wstt_config["pid"] = wstt_pid;
update_net_conf(configFile, "WSTT");
// Save WSTUN PID to clean network status after a network failure
wstun_pid = wstun_proc.pid;
wstun_config["pid"] = wstun_pid;
update_net_conf(configFile, "WSTUN");
wstt_proc.stdout.on('data', function (data) {
logger.debug('[VNET] - WSTT - stdout: ' + data);
wstun_proc.stdout.on('data', function (data) {
logger.debug('[VNET] - WSTUN - stdout: ' + data);
});
wstt_proc.stderr.on('data', function (data) {
logger.debug('[VNET] - WSTT - stderr: ' + data);
wstun_proc.stderr.on('data', function (data) {
logger.debug('[VNET] - WSTUN - stderr: ' + data);
});
wstt_proc.on('close', function (code) {
logger.warn('[VNET] - WSTT - process exited with code ' + code);
wstun_proc.on('close', function (code) {
logger.warn('[VNET] - WSTUN - process exited with code ' + code);
});

@@ -224,3 +234,3 @@ //------------------------------------------------------------------------------------------------------------

logger.info('[VNET] - Sending notification to IOTRONIC: ' + msg.status + ' - ' + msg.logmsg + ' - PID: ' + msg.pid + ' - wstt pid: ' + wstt_pid);
logger.info('[VNET] - Sending notification to IOTRONIC: ' + msg.status + ' - ' + msg.logmsg + ' - PID: ' + msg.pid + ' - wstun pid: ' + wstun_pid);

@@ -232,9 +242,15 @@ // Save Socat PID to clean network status after a network failure

session_wamp.call('s4t.iotronic.vnet.result_network_board', [msg.logmsg, boardCode]).then(
function (response) {
logger.info('[VNET] --> response from IOTRONIC: \n' + response.message);
logger.info('[VNET] - TUNNELS CONFIGURATION BOARD SIDE COMPLETED!');
}
);
try{
session_wamp.call('s4t.iotronic.vnet.result_network_board', [msg.logmsg, boardCode]).then(
function (response) {
logger.info('[VNET] --> response from IOTRONIC: \n' + response.message);
logger.info('[VNET] - TUNNELS CONFIGURATION BOARD SIDE COMPLETED!');
}
);
}catch (e) {
logger.error('[VNET] --> Error calling IoTronic (RPC: s4t.iotronic.vnet.result_network_board): ', e);
}
}

@@ -395,5 +411,4 @@

add_vlan_iface.on('close', function (code) {
add_vlan_iface.on('close', function () {
logger.info('--> DELETED ' + iface);
});

@@ -413,5 +428,5 @@

//Register all the module functions as WAMP RPCs
session.register('s4t'+ boardCode + '.vnet.setSocatOnBoard', exports.setSocatOnBoard);
session.register('s4t'+ boardCode + '.vnet.addToNetwork', exports.addToNetwork);
session.register('s4t'+ boardCode + '.vnet.removeFromNetwork', exports.removeFromNetwork);
session.register('s4t.'+ boardCode + '.vnet.setSocatOnBoard', exports.setSocatOnBoard);
session.register('s4t.'+ boardCode + '.vnet.addToNetwork', exports.addToNetwork);
session.register('s4t.'+ boardCode + '.vnet.removeFromNetwork', exports.removeFromNetwork);

@@ -418,0 +433,0 @@ logger.info('[WAMP-EXPORTS] Network commands exported to the cloud!');

@@ -1,9 +0,19 @@

/*
* Apache License
* Version 2.0, January 2004
* http://www.apache.org/licenses/
*
* Copyright (c) 2015 2016 Dario Bruneo, Francesco Longo, Giovanni Merlino, Fabio Verboso, Nicola Peditto
*
*/
//############################################################################################
//##
//# Copyright (C) 2014-2017 Dario Bruneo, Francesco Longo, Giovanni Merlino,
//# Nicola Peditto, Fabio Verboso
//##
//# Licensed under the Apache License, Version 2.0 (the "License");
//# you may not use this file except in compliance with the License.
//# You may obtain a copy of the License at
//##
//# http://www.apache.org/licenses/LICENSE-2.0
//##
//# Unless required by applicable law or agreed to in writing, software
//# distributed under the License is distributed on an "AS IS" BASIS,
//# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//# See the License for the specific language governing permissions and
//# limitations under the License.
//##
//############################################################################################

@@ -100,3 +110,3 @@ nconf = require('nconf');

logger.debug('[VNET] --> WSTT configuration completed!');
logger.debug('[VNET] --> WSTUN configuration completed!');

@@ -103,0 +113,0 @@ //ip link set $TUNNAME up

{
"name": "@mdslab/iotronic-lightning-rod",
"version": "1.1.0",
"version": "2.0.0",
"description": "Implementation of the Lightning-rod, the Stack4Things node-side probe (this version works with the standalone version of IoTronic) http://stack4things.unime.it/",

@@ -19,3 +19,2 @@ "main": "lightning-rod.js",

"dependencies": {
"node-reverse-wstunnel": ">=0.1.2",
"autobahn": ">=0.9.9",

@@ -26,3 +25,3 @@ "nconf": ">=0.8.4",

"connection-tester": ">=0.1.1",
"log4js": "<=1.1.0",
"log4js":"<=1.1.1",
"q": ">=0.9.7",

@@ -29,0 +28,0 @@ "fs-access": ">=1.0.1",

@@ -15,3 +15,2 @@ {

},
"reverse":

@@ -28,3 +27,2 @@ {

},
"socat":{

@@ -36,3 +34,2 @@ "client":{

},
"board":

@@ -42,17 +39,2 @@ {

"status": "new",
"services":{
"ssh":{
"port":"22"
},
"tty":{
"port":"2233"
},
"ideino":{
"port":"2424"
},
"osjs":{
"port":"8000"
}
},
"position":{

@@ -66,3 +48,4 @@ "altitude":0,

}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc