/**
* This file is part of Domotz Agent.
*
* @license
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
*
* @requires sandbox/library/device
* @requires sandbox/library/logger
* @requires sandbox/library/crypto
* @requires sandbox/library/buffer
* @copyright Copyright (C) Domotz Inc
*/
const deviceLibrary = require('./library/device');
const domotzContext = require('./context');
const logger = require('./library/logger').logger;
const util = require('util');
const vm = require('vm');
const driverTypes = require('./constants').driverTypes;
const errorTypes = require('./constants').errorTypes;
const rttOffset = 5000;
var newConsole = null;
var startTime = null;
var agentDriverSettings = null;
function failure(errorType) {
if (!errorType || !(errorType in errorTypes)) {
newConsole.warning('Error type %s is not recognized, converting to generic error', errorType);
errorType = errorTypes.GENERIC_ERROR;
}
process.send({
outcome: 'failure',
errorType: errorType,
log: newConsole.get(),
elapsed: new Date() - startTime,
});
}
function successConfigurationManagement(configurationData) {
newConsole.debug('Success called');
process.send({
outcome: 'success',
configurationData: configurationData,
log: newConsole.get(),
elapsed: new Date() - startTime,
});
}
function successGeneric(variables, table) {
newConsole.debug('Success called');
var tables;
// In case the user passes the table object as first argument
if (!table && variables && variables.isTable) {
tables = [variables.getResult()];
variables = null;
}
if (table && table.isTable) {
tables = [table.getResult()];
}
if (variables !== null && variables !== undefined) {
validateVariables(variables);
}
process.send({
outcome: 'success',
variables: variables,
tables: tables,
log: newConsole.get(),
elapsed: new Date() - startTime,
});
}
function validateVariables(variables) {
var uids = [];
for (var i = 0; i < variables.length; i++) {
var uid = variables[i].uid;
if (uids.indexOf(uid) > -1) {
newConsole.error('Duplicate variable uid received: ' + uid);
failure('PARSING_ERROR');
return;
}
uids.push(uid);
}
}
function timeoutReached() {
newConsole.warning('Timeout expired');
failure(errorTypes.TIMEOUT_ERROR);
}
function buildDomotzContext(message, newConsole) {
var context = domotzContext.createDomotzContext(message, newConsole);
context.failure = failure;
if (message.driverType === driverTypes.CONFIGURATION_MANAGEMENT) {
context.success = successConfigurationManagement;
} else if (message.driverType === driverTypes.GENERIC) {
context.success = successGeneric;
} else {
context.success = successGeneric;
}
return context;
}
process.on('message', function (message) {
agentDriverSettings = message.agentDriverSettings;
newConsole = logger(message.logLevel || 'info', agentDriverSettings.max_log_entries);
var context = vm.createContext({
D: buildDomotzContext(message, newConsole),
import: function () {
newConsole.error('Error: Import not possible in sandbox');
failure(errorTypes.IMPORT_NOT_ALLOWED);
},
require: function () {
newConsole.error('Error: Require not possible in sandbox');
failure(errorTypes.REQUIRE_NOT_ALLOWED);
},
console: newConsole,
});
// ToDo: check if this should not be included in the D context directly
if (message.device) {
context.D.device = deviceLibrary.device(message.device, agentDriverSettings, newConsole);
newConsole.debug('Created new device for script execution: %s', util.inspect(message.device));
}
const script = new vm.Script(message.script);
var timeout = message.timeout || 5000;
setTimeout(timeoutReached, timeout + rttOffset);
startTime = new Date();
script.runInContext(context, {
displayErrors: true,
fileName: 'custom script',
lineOffset: 0,
timeout: timeout,
});
});
process.on('uncaughtException', function (err) {
var errorText = err.toString();
if (errorText.indexOf('timed out') !== -1) {
newConsole.error('Script Execution timed out after: ' + (new Date() - startTime) + ' ms');
failure(errorTypes.TIMEOUT_ERROR);
} else {
newConsole.error('Caught exception: ' + errorText + '\n' + err.stack);
failure(errorTypes.GENERIC_ERROR);
}
});