UNPKG

domotz-remote-pawn

Version:

Domotz Agent

233 lines (215 loc) 8.43 kB
/** * 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/>. * * @module sandbox/runner * @private * @copyright Copyright (C) Domotz Inc */ const path = require('path'); /** * @constant {number} * @type {number} * @default */ const DEFAULT_TIMEOUT = 5000; /** * @readonly * @enum {number} */ const DEFAULT_CUSTOM_DRIVER_SETTINGS = { max_log_entries: 100, max_var_id_len: 50, max_var_per_device: 100, max_var_unit_len: 10, max_data_len: 4096, max_configuration_backup_size: 1048536, max_ignored_lines: 10, max_ignored_line_len: 50, }; const driverTypes = require('./constants').driverTypes; const valueTypes = require('./constants').valueTypes; var agentDriverSettings = null; var dryRun = null; var driverType = null; function getDriverSettings(SettingsManager) { var driverSettings = Object(); for (const key of Object.keys(DEFAULT_CUSTOM_DRIVER_SETTINGS)) { driverSettings[key] = SettingsManager.getSetting('configuration.settings.custom_driver.' + key, DEFAULT_CUSTOM_DRIVER_SETTINGS[key]); } driverSettings.use_system_ssh_shell_sequence = SettingsManager.useSystemSSHShellSequence; return driverSettings; } /** * Checks if a variable is valid * @param {*} variable - a custom variable * @returns {object|null} - new instance of the same variable if it is valid, null otherwise. */ function checkVariable(variable) { if (variable === null || variable === undefined) { return null; } if (variable.uid === null || variable.uid === undefined || variable.uid.length < 1 || variable.uid.length > agentDriverSettings.max_var_id_len) { return null; } if (variable.value === undefined) { // must be null or string; anything else means createVariable hasn't been used; return null; } if (variable.valueType && !(variable.valueType in valueTypes)) { variable.valueType = null; } return { uid: variable.uid, label: variable.label, unit: variable.unit, valueType: variable.valueType, value: variable.value, }; } /** * Function validateBackup * @function * @param {ConfigurationBackup} configurationBackup - The backup object to be validated * @return {ConfigurationBackup} */ function validateBackup(configurationBackup) { var validatedBackup = {}; if (configurationBackup === undefined || configurationBackup === null) { return null; } validatedBackup.label = configurationBackup.label.toString() || 'Custom Driver Configuration Backup'; if (configurationBackup.running === undefined || configurationBackup.running === null) { return null; } else if (agentDriverSettings.max_config_backup_size < configurationBackup.running.length) { return null; } else { validatedBackup.running = configurationBackup.running; } if ( configurationBackup.startup !== undefined && configurationBackup.startup !== null && agentDriverSettings.max_config_backup_size < configurationBackup.startup.length ) { validatedBackup.startup = null; } else { validatedBackup.startup = configurationBackup.startup || null; } if (Array.isArray(configurationBackup.ignoredLines) && configurationBackup.ignoredLines.length <= agentDriverSettings.max_ignored_lines) { validatedBackup.ignoredLines = []; for (var i = 0; i < configurationBackup.ignoredLines.length; i++) { var line = configurationBackup.ignoredLines[i]; if (typeof(line) === "string" && line.length <= agentDriverSettings.max_ignored_line_len) { validatedBackup.ignoredLines.push(line); } } } else { validatedBackup.ignored_lines = null; } return validatedBackup; } function handleSuccessfulResponseData(response) { // this function is executed in the sandboxRunner process, so no chance of corruption of the checks if the driver escapes the vm if (dryRun !== true) { response.log = null; } if (driverType === driverTypes.GENERIC && response.variables) { var checkedVariables = []; for (var i = 0; i < response.variables.length; i++) { var checkedVariable = checkVariable(response.variables[i]); if (checkedVariable !== null && checkedVariable !== undefined) { checkedVariables.push(checkedVariable); } } response.variables = checkedVariables; response.configurationData = null; } else if (driverType === driverTypes.CONFIGURATION_MANAGEMENT && response.configurationData) { response.configurationData = validateBackup(response.configurationData); response.variables = null; response.tables = null; } return response; } function handleFailedResponse(response) { if (!response.errorType) { response.errorType = 'GENERIC_ERROR'; } return response; } function createMessageListener(subProcess, onError, onSuccess, myConsole) { return function (response) { myConsole.debug('Response outcome: ', response.outcome); myConsole.debug('Response log:' + response.log); if (response.variables) { myConsole.debug('Response variables: ' + JSON.stringify(response.variables)); } if (subProcess !== null) { subProcess.kill(); subProcess = null; if (response.outcome === 'success') { response = handleSuccessfulResponseData(response); return onSuccess(response); } else { response = handleFailedResponse(response); return onError(response); } } else { myConsole.error('Custom Driver sandbox process already stopped. Usage of further callbacks for success or failure are ignored'); } }; } function createErrorListener(subProcess, onError, myConsole) { return function () { myConsole.debug('Process exited'); if (subProcess !== null && subProcess.killed !== true) { myConsole.error('Sandbox exited without sending back message - error'); return onError(Error('Sandbox exited without sending back message')); } }; } function sandboxRunner(data, resourceLocator, cid, onError, onSuccess) { var subProcess = require('child_process').fork(__dirname + path.sep + 'sandbox.js'); var scriptText = data.code; var device = data.device; var timeout = data.timeout || DEFAULT_TIMEOUT; var logLevel = data.logLevel || 'warning'; var myConsole = resourceLocator.log.decorateLogs().decorate('(' + cid + ')'); var parameters = data.parameters; // Set values to Global Variables dryRun = data.dry_run; agentDriverSettings = getDriverSettings(resourceLocator.settingsManager); driverType = data.driver_type || driverTypes.GENERIC; if (!scriptText || !device) { return onError(Error('Missing script text or device: ' + JSON.stringify(data))); } if (!device.ip) { device.ip = resourceLocator.interfacesBindingStorage.getIpsFromMac(device.hw_address)[0]; } myConsole.debug('Script %s', scriptText); myConsole.debug('Device: %s', JSON.stringify(device)); subProcess.on('message', createMessageListener(subProcess, onError, onSuccess, myConsole)); subProcess.on('exit', createErrorListener(subProcess, onError, myConsole)); subProcess.send({ script: scriptText.toString(), device: device, driverType: driverType, timeout: timeout, logLevel: logLevel, agentDriverSettings: agentDriverSettings, parameters: parameters, }); } module.exports.sandboxRunner = sandboxRunner;