/** * * js-controller Controller start/stop and install script * * 7'2014-2018 bluefox * 2014 hobbyquaker * */ /* jshint -W097 */ /* jshint strict:false */ /* jslint node: true */ 'use strict'; // TODO need info about progress of stopping var fs = require('fs'); var tools = require(__dirname + '/tools'); require('events').EventEmitter.prototype._maxListeners = 100; process.setMaxListeners(0); var yargs; function initYargs() { yargs = require('yargs') .usage('Commands:\n' + tools.appName + ' setup [--objects ] [--states ] [custom]\n' + tools.appName + ' start\n' + tools.appName + ' stop\n' + tools.appName + ' start \n' + tools.appName + ' stop \n' + tools.appName + ' start all\n' + tools.appName + ' restart\n' + tools.appName + ' restart \n' + tools.appName + ' add [desiredNumber] [--enabled] [--host ] [--port ]\n' + tools.appName + ' install \n' + tools.appName + ' url []\n' + tools.appName + ' del \n' + tools.appName + ' del .\n' + tools.appName + ' update [repository url] [--updatable/--u] [--installed/--i]\n' + tools.appName + ' upgrade [repository url]\n' + tools.appName + ' upgrade self [repository url]\n' + tools.appName + ' upgrade [repository url]\n' + tools.appName + ' upload \n' + tools.appName + ' upload all\n' + tools.appName + ' upload \n' + tools.appName + ' object get \n' + tools.appName + ' object del \n' + tools.appName + ' object chmod [state-mode] \n' + tools.appName + ' object chown \n' + tools.appName + ' object list \n' + tools.appName + ' state get \n' + tools.appName + ' state getplain \n' + tools.appName + ' state set [ack]\n' + tools.appName + ' state del \n' + tools.appName + ' message [.instanceid] []\n' + tools.appName + ' list [filter]\n' + tools.appName + ' chmod \n' + tools.appName + ' chown \n' + tools.appName + ' touch \n' + tools.appName + ' rm \n' + tools.appName + ' file read <' + tools.appName + '-path-to-read> []\n' + tools.appName + ' file write <' + tools.appName + '-path-to-read> \n' + tools.appName + ' user add [--ingroup group] [--password pass]\n' + tools.appName + ' user del \n' + tools.appName + ' user passwd [--password pass]\n' + tools.appName + ' user enable \n' + tools.appName + ' user disable \n' + tools.appName + ' user get \n' + tools.appName + ' user check [--password pass]\n' + tools.appName + ' group add \n' + tools.appName + ' group del \n' + tools.appName + ' group list \n' + tools.appName + ' group enable \n' + tools.appName + ' group disable \n' + tools.appName + ' group get \n' + tools.appName + ' group adduser \n' + tools.appName + ' group deluser \n' + tools.appName + ' set . [--port port] [--ip address] [--ssl true|false]\n' + tools.appName + ' license \n' + tools.appName + ' clean\n' + tools.appName + ' backup\n' + tools.appName + ' restore \n' + tools.appName + ' --timeout 5000\n' + tools.appName + ' status\n' + tools.appName + ' repo [name]\n' + tools.appName + ' repo add \n' + tools.appName + ' repo set \n' + tools.appName + ' repo del \n' + tools.appName + ' uuid\n' + tools.appName + ' unsetup\n' + tools.appName + ' multihost [--secure true|false]\n' + tools.appName + ' multihost browse\n' + tools.appName + ' multihost connect\n' + tools.appName + ' version [adapter]\n' + tools.appName + ' [adapter] -v\n') //.default('objects', '127.0.0.1') //.default('states', '127.0.0.1') //.default('lang', 'en') ; return yargs; } function showHelp(_yargs) { if (_yargs) { _yargs.showHelp(); } else if (yargs) { yargs.showHelp(); } } var Objects; // constructor var objects; // instance var States; // constructor var states; // instance // params can have: // pretty, force, password, ingroup, v, version, timeout, // enabled, disabled, port, ssl, ip, updatable, host, enabled, port, // objects, states function processCommand(command, args, params, callback) { if (typeof args === 'function') { callback = args; args = null; } if (typeof params === 'function') { callback = params; params = null; } if (!params) params = {}; if (!args) args = []; if (!callback) callback = processExit; switch (command) { case 'start': case 'stop': (function () { // Start stop of adapter if (args[0]) { Objects = require(__dirname + '/objects'); var adapter = args[0]; // If user accidentally wrote tools.appName.adapter => remove adapter var regExp = new RegExp('^' + tools.appName + '\\.', 'i'); if (adapter && regExp.test(adapter)) { adapter = adapter.substring(tools.appName.length + 1); } dbConnect(params, function () { if (adapter === 'all') { objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, objs) { var count = 0; for (var i = 0; i < objs.rows.length; i++) { if (objs.rows[i].value.type !== 'instance') continue; var obj = objs.rows[i].value; if (command === 'start') { if (!obj.common.enabled) { obj.common.enabled = true; count++; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject(obj._id, obj, function () { console.log('Adapter "' + adapter + '" started.'); if (!--count) callback(); }); } } else { if (obj.common.enabled) { obj.common.enabled = false; count++; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject(obj._id, obj, function () { console.log('Adapter "' + adapter + '" stopped.'); if (!--count) callback(); }); } } } if (!count) callback(); }); } else { if (adapter.indexOf('.') === -1) { objects.getObjectList({startkey: 'system.adapter.' + adapter + '.', endkey: 'system.adapter.' + adapter + '.\u9999'}, function (err, objs) { var obj; if (!err && objs) { for (var i = 0; i < objs.rows.length; i++) { if (objs.rows[i].value.type !== 'instance') continue; if (obj) { console.log('Please enter instance of adapter, e.g. "' + obj._id.replace('system.adapter.', '') + '"'); callback(1); } obj = objs.rows[i].value; } } if (!obj) { console.log('Cannot find any instances of "' + adapter + '"'); callback(1); } else { if (command === 'start') { if (!obj.common.enabled) { obj.common.enabled = true; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject(obj._id, obj, function () { console.log('Adapter "' + obj._id.replace('system.adapter.', '') + '" started.'); callback(); }); } else { callback(); } } else { if (obj.common.enabled) { obj.common.enabled = false; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject(obj._id, obj, function () { console.log('Adapter "' + obj._id.replace('system.adapter.', '') + '" stopped.'); callback(); }); } else { callback(); } } } }); } else { objects.getObject('system.adapter.' + adapter, function (err, obj) { if (!err && obj) { if (command === 'start') { obj.common.enabled = true; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject('system.adapter.' + adapter, obj, function () { console.log('Adapter "' + adapter + '" started.'); callback(); }); } else { obj.common.enabled = false; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject('system.adapter.' + adapter, obj, function () { console.log('Adapter "' + adapter + '" stopped.'); callback(); }); } } else { console.log('Adapter "' + adapter + '" does not exist.'); callback(24); } }); } } }); } else { var memoryLimitMB = 0; try { var config = require(tools.getConfigFileName()); if (config && config.system && config.system.memoryLimitMB) { memoryLimitMB = parseInt(config.system.memoryLimitMB, 10); } } catch (err) { console.warn('Cannot read memoryLimitMB'); console.warn('May be config file does not exist.\nPlease call "' + tools.appName + ' setup first" to initialize the settings.') } var startObj = { main: '../controller.js', name: tools.appName + ' controller', pidfile: __dirname + '/' + tools.appName + '.pid', cwd: '../', stopTimeout: 6000 }; if (memoryLimitMB) startObj.args = '--max-old-space-size=' + memoryLimitMB; var daemon = require('daemonize2').setup(startObj); daemon.on('error', function (error) { console.log('Error: ' + error.message); }); daemon.on('stopped', function () { // start KILLALL script if something still runs if (command === 'stop' && !require('os').platform().match(/^win/)) { var data = ''; fs.chmodSync(__dirname + '/../killall.sh', '777'); var child = require('child_process').spawn(__dirname + '/../killall.sh', []); child.stdout.on('data', function (data) { data += data.toString().replace('\n', ''); }); child.stderr.on('data', function (data) { data += data.toString().replace('\n', ''); }); child.on('exit', function (exitCode) { console.log('Exit code for "killall.sh": ' + exitCode); callback(); }); } }); daemon[command](); } })(); break; case 'status': case 'isrun': (function () { dbConnect(params, function (objects, states, isOffline) { if (isOffline) { console.log(tools.appName + ' is not running'); callback(100); } else { console.log(tools.appName + ' is running'); callback(); } }); })(); break; case 'r': case 'restart': (function () { if (args[0]) { Objects = require(__dirname + '/objects'); var adapter = args[0]; // If user accidentally wrote tools.appName.adapter => remove adapter var regExp = new RegExp('^' + tools.appName + '\\.', 'i'); if (adapter && regExp.test(adapter)) { adapter = adapter.substring(tools.appName.length + 1); } dbConnect(params, function () { if (adapter.indexOf('.') === -1) { objects.getObjectList({startkey: 'system.adapter.' + adapter + '.', endkey: 'system.adapter.' + adapter + '.\u9999'}, function (err, objs) { var obj; if (!err && objs) { for (var i = 0; i < objs.rows.length; i++) { if (objs.rows[i].value.type !== 'instance') continue; if (obj) { console.log('Please enter instance of adapter, e.g. "' + obj._id.replace('system.adapter.', '') + '"'); callback(1); } obj = objs.rows[i].value; } } if (!obj) { console.log('Cannot find any instances of "' + adapter + '"'); callback(1); } else { obj.common.enabled = true; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject(obj._id, obj, function (err) { console.log('Adapter "' + obj._id.replace('system.adapter.', '') + '" restarted.'); callback(); }); } }); } else { objects.getObject('system.adapter.' + adapter, function (err, obj) { if (!err && obj) { obj.common.enabled = true; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject('system.adapter.' + adapter, obj, function () { console.log('Adapter "' + adapter + '" restarted.'); callback(); }); } else { console.log('Adapter "' + adapter + '" does not exist.'); callback(24); } }); } }); } else { var memoryLimitMB = 0; try { var config = require(tools.getConfigFileName()); if (config && config.system && config.system.memoryLimitMB) { memoryLimitMB = parseInt(config.system.memoryLimitMB, 10); } } catch (err) { console.warn('Cannot read memoryLimitMB'); } var startObj = { main: '../controller.js', name: tools.appName + ' controller', pidfile: __dirname + '/' + tools.appName + '.pid', cwd: '../', stopTimeout: 1000 }; if (memoryLimitMB) startObj.args = '--max-old-space-size=' + memoryLimitMB; var daemon = require('daemonize2').setup(startObj); daemon.on('stopped', function () { daemon.start(); }).on('notrunning', function () { daemon.start(); }).on('error', function (error) { console.log('Error: ' + error.message); }); daemon.stop(); } })(); break; case '_restart': restartController(function () { callback(); }); break; case 'update': (function () { Objects = require(__dirname + '/objects'); var repoUrl = args[0]; // Repo url or name dbConnect(params, function () { var Repo = require(__dirname + '/setup/setupRepo.js'); var repo = new Repo({ objects: objects }); repo.showRepo(repoUrl, params, function () { setTimeout(function () { callback(); }, 2000); }); }); })(); break; case 'setup': (function () { if (args[0] === 'custom') { var readline = require('readline'); var config; // read actual configuration try { if (fs.existsSync(tools.getConfigFileName())) { config = JSON.parse(fs.readFileSync(tools.getConfigFileName(), 'utf8')); } else { config = require(__dirname + '/../conf/' + tools.appName + '-dist.json'); } } catch (e) { config = require(__dirname + '/../conf/' + tools.appName + '-dist.json'); } var rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question('Type of objects DB [(f)ile, (c)ouch, (r)edis], default [file]: ', function (otype) { if (!otype) { otype = 'file'; } else { otype = (otype || '').toLowerCase(); if (otype === 'r') otype = 'redis'; if (otype === 'f') otype = 'file'; if (otype === 'c') otype = 'couch'; if (otype !== 'file' && otype !== 'couch' && otype !== 'redis') { console.log('Unknown objects type: ' + otype); callback(23); } } rl.question('Host / Unix Socket of objects DB(' + otype + '), default[127.0.0.1]: ', function (ohost) { if (!ohost) { ohost = '127.0.0.1'; } else { ohost = (ohost || '').toLowerCase(); } var op; if (otype === 'file') { op = 9001; } else if (otype === 'redis') { op = 6379; } else if (otype === 'couch') { op = 5984; } rl.question('Port of objects DB(' + otype + '), default[' + op + ']: ', function (oport) { var ot; if (!oport) { if (otype === 'file') { oport = 9001; ot = 'file'; } else if (otype === 'redis') { ot = 'redis'; oport = 6379; } else if (otype === 'couch') { ot = 'couch'; oport = 5984; } } else { oport = parseInt(oport, 10); if (isNaN(oport)) { console.log('Invalid objects port: ' + oport); callback(23); } } rl.question('Type of states DB [(f)file, (r)edis], default [' + ot + ']: ', function (stype) { if (!stype) { stype = ot; } else { stype = (stype || '').toLowerCase(); if (stype === 'r') stype = 'redis'; if (stype === 'f') stype = 'file'; if (stype !== 'file' && stype !== 'redis') { console.log('Unknown states type: ' + stype); callback(23); } } rl.question('Host / Unix Socket of states DB (' + stype + '), default[' + ohost + ']: ', function (shost) { if (!shost) { shost = ohost; } else { shost = (shost || '').toLowerCase(); } var sp; if (stype === 'file') { sp = 9000; } else if (stype === 'redis') { sp = 6379; } rl.question('Port of states DB (' + stype + '), default[' + sp + ']: ', function (sport) { if (!sport) { if (stype === 'file') { sport = 9000; } else if (stype === 'redis') { sport = 6379; } } else { sport = parseInt(sport, 10); if (isNaN(sport)) { console.log('Invalid states port: ' + sport); callback(23); } } if ((stype === 'file' && (shost === 'localhost' || shost === '127.0.0.1')) || (otype === 'file' && (ohost === 'localhost' || ohost === '127.0.0.1'))) { rl.question('Data directory (file), default[../' + tools.getDefaultDataDir() + ']: ', function (dir) { if (!dir) dir = tools.getDefaultDataDir(); rl.question('Host name of this machine [' + require('os').hostname() + ']: ', function (hname) { if (!hname) { hname = ''; } else { hname = (hname || ''); if (hname.match(/\s/)) { console.log('Invalid host name: ' + hname); callback(23); } } rl.close(); console.log('creating conf/' + tools.appName + '.json'); config.system = config.system || {}; config.system.hostname = hname; config.objects.host = ohost; config.objects.type = otype; config.objects.port = oport; if (config.objects.type === 'file') config.objects.dataDir = dir; config.states.host = shost; config.states.type = stype; config.states.port = sport; if (config.states.type === 'file') config.states.dataDir = dir; fs.writeFileSync(tools.getConfigFileName(), JSON.stringify(config, null, 2)); }); }); } else { rl.question('Host name of this machine [' + require('os').hostname() + ']: ', function (hname) { if (!hname) { hname = ''; } else { hname = (hname || ''); if (hname.match(/\s/)) { console.log('Invalid host name: ' + hname); callback(23); } } rl.close(); console.log('creating conf/' + tools.appName + '.json'); config.system = config.system || {}; config.system.hostname = hname; config.objects.host = ohost; config.objects.type = otype; config.objects.port = oport; config.states.host = shost; config.states.type = stype; config.states.port = sport; config.states.dataDir = undefined; config.objects.dataDir = undefined; fs.writeFileSync(tools.getConfigFileName(), JSON.stringify(config, null, 2)); }); } }); }); }); }); }); }); } else { var Setup = require(__dirname + '/setup/setupSetup.js'); var setup = new Setup({ dbConnect: dbConnect, processExit: callback, params: params }); var i = 0; var isFirst; var isRedis; while (args[i] !== undefined) { if (args[i] === 'first' || args[i] === '--first') { isFirst = true; } else if (args[i] === 'redis' || args[i] === '--redis') { isRedis = true; } i++; } setup.setup(function (isFirst, isRedis) { if (isFirst) { var Install = require(__dirname + '/setup/setupInstall.js'); var install = new Install({ objects: objects, states: states, installNpm: installNpm, getRepository: getRepository, processExit: callback, params: params }); //install admin adapter install.createInstance('admin', {enabled: true, ignoreIfExists: true}, function () { // check if discovery is installed too try { var path = require.resolve(tools.appName + '.discovery'); if (path) { install.createInstance('discovery', {enabled: true, ignoreIfExists: true}, function () { callback(); }); } } catch (e) { // no discovery found callback(); } }); } else { callback(); } }, isFirst, isRedis); } })(); break; case 'url': (function () { Objects = require(__dirname + '/objects'); var url = args[0]; var name = args[1]; if (url[0] === '"' && url[url.length - 1] === '"') { url = url.substring(1, url.length - 1); } // try to fix URL if (url.match(/^https:\/\/github\.com\//)) { url = url.replace(/\.git$/, ''); if (!url.match(/\.zip$/) && !url.match(/\.gz$/) && !url.match(/\/tarball\/[^\/]+$/)) { url += '/tarball/master'; } } console.log('install ' + url); dbConnect(params, function () { var Install = require(__dirname + '/setup/setupInstall.js'); var install = new Install({ objects: objects, states: states, installNpm: installNpm, getRepository: getRepository, processExit: callback, params: params }); install.npmInstall(url, true, false, function (_url, installDir) { var Upload = require(__dirname + '/setup/setupUpload.js'); var upload = new Upload({ states: states, objects: objects, processExit: callback }); // Try to extract name from URL if (!name) { if (url.match(/\.tgz$|\.zip/)) { var parts = url.split('/'); var last = parts.pop(); var mm = last.match(/\.([-_\w\d]+)\-[.\d]+/); if (mm) { name = mm[1]; } } else { var reG = new RegExp(tools.appName + '\\.([-_\\w\\d]+)\\/'); var m = reG.exec(url); if (m) { name = m[1]; } else { var reg = new RegExp(tools.appName.toLowerCase() + '\\.([-_\\w\\d]+)\\/'); m = reg.exec(url); if (m) name = m[1]; } } } if (name) { upload.uploadAdapter(name, true, true, function () { upload.uploadAdapter(name, false, true, function () { callback(); }); }); } else { // Try to find io-package.json with newest date var dirs = fs.readdirSync(installDir); var date = null; var dir = null; for (var i = 0; i < dirs.length; i++) { if (fs.existsSync(installDir + '/' + dirs[i] + '/io-package.json')) { var stat = fs.statSync(installDir + '/' + dirs[i] + '/io-package.json'); if (!date || stat.mtime.getTime() > date.getTime()) { dir = dirs[i]; date = stat.mtime; } } } // if modify time is not older than one hour if (dir && (new Date()).getTime() - date.getTime() < 3600000) { name = dir.substring(tools.appName.length + 1); upload.uploadAdapter(name, true, true, function () { upload.uploadAdapter(name, false, true, function () { callback(); }); }); } else { callback(); } } }); }); })(); break; case 'a': case 'add': case 'install': case 'i': (function () { Objects = require(__dirname + '/objects'); let name = args[0]; let instance = args[1]; let repoUrl = args[2]; if (parseInt(instance, 10).toString() !== (instance || '').toString()) { repoUrl = instance; instance = null; } if (parseInt(repoUrl, 10).toString() === (repoUrl || '').toString()) { const temp = instance; instance = repoUrl; repoUrl = temp; } if (parseInt(instance, 10).toString() === (instance || '').toString()) { instance = parseInt(instance, 10); params.instance = instance; } // If user accidentally wrote tools.appName.adapter => remove adapter const regExp = new RegExp('^' + tools.appName + '\\.', 'i'); if (name && regExp.test(name)) { name = name.substring(tools.appName.length + 1); } let adapterDir = tools.getAdapterDir(name); dbConnect(params, function () { const Install = require(__dirname + '/setup/setupInstall.js'); let install = new Install({ objects: objects, states: states, installNpm: installNpm, getRepository: getRepository, processExit: callback, params: params }); if (!fs.existsSync(adapterDir)) { install.downloadPacket(repoUrl, name, null, function () { if (command !== 'install' && command !== 'i') { install.createInstance(name, params, function () { callback(); }); } else { const Upload = require(__dirname + '/setup/setupUpload.js'); let upload = new Upload({ states: states, objects: objects, processExit: callback }); // create objects install.uploadStaticObjects(name, function () { upload.uploadAdapter(name, true, true, function () { upload.uploadAdapter(name, false, true, function () { callback(); }); }); }); } }); } else { if (command !== 'install' && command !== 'i') { install.createInstance(name, params, function () { callback(); }); } else { console.log('adapter "' + name + '" yet installed. Use "upgrade" to install newer version.'); callback(51); } } }); })(); break; case 'upload': case 'u': (function () { Objects = require(__dirname + '/objects'); var name = args[0]; var subTree = args[1]; if (name) { dbConnect(params, function () { var Upload = require(__dirname + '/setup/setupUpload.js'); var upload = new Upload({ states: states, objects: objects, processExit: callback }); if (name === 'all') { objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, objs) { var adapters = []; for (var i = 0; i < objs.rows.length; i++) { if (objs.rows[i].value.type !== 'adapter') continue; adapters.push(objs.rows[i].value.common.name); } upload.uploadAdapterFull(adapters, function () { callback(); }); }); } else { // if upload of file if (name.indexOf('.') !== -1) { if (!subTree) { console.log('Please specify target name, like:\n ' + tools.appName + ' upload /file/picture.png /vis.0/main/img/picture.png'); callback(1); } upload.uploadFile(name, subTree, function (err, newName) { if (!err) console.log('File "' + name + '" is successfully saved under ' + newName); callback(err ? 40 : undefined); }); } else { if (subTree) { upload.uploadAdapter(name, false, true, subTree, function () { callback(); }); } else { upload.uploadAdapter(name, true, true, function () { upload.upgradeAdapterObjects(name, function () { upload.uploadAdapter(name, false, true, function () { callback(); }); }); }); } } } }); } else { console.log('No adapter name found!'); showHelp(); callback(1); } })(); break; case 'delete': case 'del': (function () { var adpr = args[0]; var instance = args[1]; // If user accidentally wrote tools.appName.adapter => remove adapter var regExp = new RegExp('^' + tools.appName + '\\.', 'i'); if (adpr && regExp.test(adpr)) { adpr = adpr.substring(tools.appName.length + 1); } if (!adpr) { showHelp(); callback(2); } if (adpr && adpr.indexOf('.') !== -1) { var parts = adpr.split('.'); adpr = parts[0]; instance = parts[1]; } if (instance || instance === 0) { dbConnect(params, function () { var Install = require(__dirname + '/setup/setupInstall.js'); var install = new Install({ objects: objects, states: states, installNpm: installNpm, getRepository: getRepository, processExit: callback, params: params }); console.log('Delete adapter "' + adpr + '.' + instance + '"'); install.deleteInstance(adpr, instance, function () { callback(); }); }); } else { dbConnect(params, function () { var Install = require(__dirname + '/setup/setupInstall.js'); var install = new Install({ objects: objects, states: states, installNpm: installNpm, getRepository: getRepository, processExit: callback, params: params }); console.log('Delete adapter "' + adpr + '"'); install.deleteAdapter(adpr, function (a, resultCode) { callback(resultCode); }); }); } })(); break; case 'unsetup': (function () { const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout }); rl.question('UUID will be deleted. Are you sure? [y/N]: ', function (answer) { rl.close(); answer = answer.toLowerCase(); if (answer === 'y' || answer === 'yes' || answer === 'ja' || answer === 'j') { dbConnect(params, function () { objects.delObject('system.meta.uuid', function (err) { if (err) { console.log('uuid cannot be deleted: ' + err); } else { console.log('system.meta.uuid deleted'); } objects.getObject('system.config', function (err, obj) { if (obj.common.licenseConfirmed || obj.common.language || (obj.native && obj.native.secret)) { obj.common.licenseConfirmed = false; obj.common.language = ''; if (obj.native) delete obj.native.secret; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject('system.config', obj, function (err) { if (err) { console.log('not found: ' + err); callback(3); } else { console.log('system.config reset'); callback(); } }); } else { console.log('system.config is OK'); callback(); } }); }); }); } else { console.log('Nothing deleted'); callback(); } }); }()); break; case 'o': case 'object': (function () { var cmd = args[0]; var id = args[1]; var pattern; if (cmd === 'chmod') { var modeObject = args[1]; var modeState = args[2]; pattern = args[3]; if (!modeObject) { console.log('No mode found. Example: "object chmod 644 system.*"'); callback(1); return; } else { //yargs has converted it to number modeObject = parseInt(modeObject.toString(), 16); if (modeState) { modeState = modeState.toString(); if (modeState[0] < '0' || modeState > '7') { pattern = modeState; modeState = undefined } else { modeState = parseInt(modeState.toString(), 16); } } } if (!pattern) { console.log('No pattern found. Example: "object chmod 644 system.*"'); callback(1); return; } dbConnect(params, function () { objects.chmodObject(pattern, {user: 'system.user.admin', object: modeObject, state: modeState}, function (err, processed) { if (err) { console.error(err); } else { if (processed) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); list.showObjectHeader(); for (var i = 0; i < processed.length; i++) { list.showObject(processed[i]); } } } setTimeout(function () { callback(); }, 1000); }); }); } else if (cmd === 'chown') { var user = args[1]; var group = args[2]; pattern = args[3]; if (!pattern) { pattern = group; group = undefined; } if (!user) { console.log('No user found. Example: "object chown user system.*"'); callback(1); } else if (user.substring(12) !== 'system.user.') { user = 'system.user.' + user; } if (group && group.substring(13) !== 'system.group.') { group = 'system.group.' + group; } if (!pattern) { console.log('No file path found. Example: "object chown user system.*"'); callback(1); return; } dbConnect(params, function () { objects.chownObject(pattern, {user: 'system.user.admin', owner: user, ownerGroup: group}, function (err, processed) { if (err) { console.error(err); } else { if (processed) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); list.showObjectHeader(); for (var i = 0; i < processed.length; i++) { list.showObject(processed[i]); } } } setTimeout(function () { callback(); }, 1000); }); }); } else if (cmd === 'list' || cmd === 'l') { pattern = args[1]; if (pattern) { pattern = {startkey: pattern.replace('*', ''), endkey: pattern.replace('*', '\u9999')}; } dbConnect(params, function () { objects.getObjectList(pattern, {user: 'system.user.admin', sorted: true}, function (err, processed) { if (err) { console.error(err); callback(33); } if (processed) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); list.showObjectHeader(); for (var id = 0; id < processed.rows.length; id++) { list.showObject(processed.rows[id].value); } } setTimeout(function () { callback(); }, 1000); }); }); } else if (id) { Objects = require(__dirname + '/objects'); if (cmd === 'get') { dbConnect(params, function () { objects.getObject(id, function (err, res) { if (err || !res) { console.log('not found'); callback(3); } else { if (params.pretty) { console.log(JSON.stringify(res, null, 2)); } else { console.log(JSON.stringify(res)); } callback(); } }); }); } else if (cmd === 'del' || cmd === 'delete') { dbConnect(params, function () { objects.delObject(id, function (err) { if (err) { console.log('not found: ' + err); callback(3); } else { console.log(id + ' deleted'); callback(); } }); }); } else { console.log('Unknown command or empty: "' + cmd + '"'); callback(3); } } })(); break; case 's': case 'state': (function () { var cmd = args[0]; var id = args[1]; if (id) { dbConnect(params, function () { if (cmd === 'get') { states.getState(id, function (err, obj) { if (err || !obj) { console.log('Error: ' + err); } else { if (params.pretty) { console.log(JSON.stringify(obj, null, 2)); } else { console.log(JSON.stringify(obj)); } } callback(); }); } else if (cmd === 'getplain') { states.getState(id, function (err, obj) { if (err || !obj) { console.log('Error: ' + err); } else { if (obj) { console.log(obj.val); console.log(obj.ack); console.log(obj.from); console.log(obj.ts); console.log(obj.lc); } else { console.log(null); } } callback(); }); } else if (cmd === 'set') { var val = args[2]; var ack = args[3]; if (val === undefined) { console.log('Invalid format: No value found.'); showHelp(); callback(); } else { if (ack === undefined) { console.log('Set "' + id + '" with value: ' + val); states.setState(id, val, function () { callback(); }); } else { console.log('Set "' + id + '" with value: ' + val + ' and ack flag ' + ack); states.setState(id, {val: val, ack: ack}, function () { callback(); }); } } } else if (cmd === 'del' || cmd === 'delete') { states.delState(id, function (err) { if (err) { console.log('not found: ' + err); callback(3); } else { console.log(id + ' deleted'); callback(); } }); } else if (cmd === 'chmod' || cmd === 'chwon') { console.log('Please use object command for that: "' + tools.appName + ' object ' + cmd + ' ' + (args[1] || '') + ' ' + (args[1] || '') + ' ' + (args[2] || '') + '"'); callback(1); } else { console.log('Invalid format: unknown state command'); showHelp(); callback(1); } }); } else { console.log('Invalid format: no id found'); showHelp(); callback(1); } })(); break; case 'msg': case 'message': (function () { var adapter = args[0]; var instances = []; if (adapter) { if (adapter.indexOf('.') !== -1) { instances.push('system.adapter.' + adapter); } dbConnect(params, function () { objects.getObjectView('system', 'instance', {startkey: 'system.adapter.' + adapter, endkey: 'system.adapter.' + adapter + '.\u9999'}, null, function (err, res) { if (res && res.rows.length) { if (instances.length === 0) { for (var t = 0; t < res.rows.length; t++) { instances.push(res.rows[t].id); } } var cmd = args[1]; var msg = args[2]; if (!msg) { msg = cmd; cmd = 'send'; } if (msg && (typeof msg === 'string') && (msg[0] === '{') && (msg[msg.length - 1] === '}')) { msg = JSON.parse(msg); } if (!msg && msg !== 0) { console.log('Invalid format: No message found.'); showHelp(); callback(); } else { var exitCount = instances.length; for (var i = 0; i < instances.length; i++) { console.log('Send command "' + cmd + '" to ' + instances[i] + ' with "' + msg + '"'); states.pushMessage(instances[i], {command: cmd, message: msg, from: 'setup'}, function() { exitCount--; if (exitCount === 0) callback(); }); } } } else { console.log('No one instance of adapter "' + adapter + '" found'); callback(4); } }); }); } else { console.log('Invalid format: no adapter found'); showHelp(); callback(1); } })(); break; case 'upgrade': (function () { Objects = require(__dirname + '/objects'); var adapter = args[0]; var repoUrl = args[1]; var regExp = new RegExp('^' + tools.appName + '\\.', 'i'); // If user accidentally wrote tools.appName.adapter => remove adapter if (adapter && regExp.test(adapter)) { adapter = adapter.substring(tools.appName.length + 1); } if (adapter && !repoUrl && adapter.indexOf('/') !== -1) { repoUrl = adapter; adapter = null; } dbConnect(params, function () { var Upgrade = require(__dirname + '/setup/setupUpgrade.js'); var upgrade = new Upgrade({ objects: objects, states: states, installNpm: installNpm, getRepository: getRepository, params: params, processExit: callback, restartController: restartController }); if (adapter) { if (adapter === 'self') { upgrade.upgradeController(repoUrl, params.force, function () { callback(); }); } else { upgrade.upgradeAdapter(repoUrl, adapter, params.force, function () { callback(); }); } } else { getRepository(repoUrl, function (err, links) { var result = []; for (var name in links) { if (links.hasOwnProperty(name)) { result.push(name); } } if (err) console.log(err); if (links) { result.sort(); upgrade.upgradeAdapterHelper(links, result, 0, false, function () { upgrade.upgradeController(links, params.force, function () { callback(); }); }); } else { // No information callback(26); } }); } }); })(); break; case 'clean': (function () { var yes = args[0]; if (yes !== 'yes') { console.log('Command "clean" clears all Objects and States. To execute it write "' + tools.appName + ' clean yes"'); } else { dbConnect(params, function (obj, stat, isNotRun) { if (!isNotRun) { console.error('Stop ' + tools.appName + ' first!'); callback(1); return; } cleanDatabase(true, function (count) { console.log('Deleted ' + count + ' states'); restartController(function () { console.log('Restarting ' + tools.appName + '...'); callback(); }); }); }); } })(); break; case 'restore': (function () { var Backup = require(__dirname + '/setup/setupBackup.js'); dbConnect(params, function (obj, stat, isNotRun) { if (!isNotRun) { console.error('Stop ' + tools.appName + ' first!'); callback(1); return; } var backup = new Backup({ states: states, objects: objects, cleanDatabase: cleanDatabase, restartController: restartController, processExit: callback }); backup.restoreBackup(args[0], function () { console.log("System successfully restored!"); callback(0); }); }); })(); break; case 'backup': (function () { var name = args[0]; var Backup = require(__dirname + '/setup/setupBackup.js'); dbConnect(params, function () { var backup = new Backup({ states: states, objects: objects, cleanDatabase: cleanDatabase, restartController: restartController, processExit: callback }); backup.createBackup(name, function (filePath) { console.log('Backup created: ' + filePath); callback(0); }); }); })(); break; case 'l': case 'list': (function () { dbConnect(params, function () { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); list.list(args[0], args[1], params); }); })(); break; case 'touch': (function () { var pattern = args[0]; if (!pattern) { console.log('No file path found. Example: "touch /vis.0/main/*"'); callback(1); return; } dbConnect(params, function () { // extract id pattern = pattern.replace(/\\/g, '/'); if (pattern[0] === '/') pattern = pattern.substring(1); if (pattern === '*') { objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, arr) { if (!err && arr && arr.rows) { var files = []; var count = 0; for (var i = 0; i < arr.rows.length; i++) { if (arr.rows[i].value.type !== 'adapter') continue; count++; objects.touch(arr.rows[i].value.common.name, '*', {user: 'system.user.admin'}, function (err, processed, _id) { if (!err && processed) { files.push({id: _id, processed: processed}); } if (!--count) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); files.sort(function (a, b) { return a.id.localeCompare(b.id); }); for (var k = 0; k < files.length; k++) { for (var t = 0; t < files[k].processed.length; t++) { list.showFile(files[k].id, files[k].processed[t].path, files[k].processed[t]); } } setTimeout(function () { callback(); }, 1000); } }); } if (!count) { console.log('Nothing found'); callback(); } } }); } else { var parts = pattern.split('/'); var id = parts.shift(); var path = parts.join('/'); objects.touch(id, path, {user: 'system.user.admin'}, function (err, processed) { if (err) { console.error(err); } else { if (processed) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); for (var i = 0; i < processed.length; i++) { list.showFile(id, processed[i].path, processed[i]); } } } setTimeout(function () { callback(); }, 1000); }); } }); })(); break; case 'rm': (function () { var pattern = args[0]; if (!pattern) { console.log('No file path found. Example: "touch /vis.0/main/*"'); callback(1); return; } dbConnect(params, function () { // extract id pattern = pattern.replace(/\\/g, '/'); if (pattern[0] === '/') pattern = pattern.substring(1); if (pattern === '*') { objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, arr) { if (!err && arr && arr.rows) { var files = []; var count = 0; for (var i = 0; i < arr.rows.length; i++) { if (arr.rows[i].value.type !== 'adapter') continue; count++; objects.rm(arr.rows[i].value.common.name, '*', {user: 'system.user.admin'}, function (err, processed, _id) { if (!err && processed) { files.push({id: _id, processed: processed}); } if (!--count) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); files.sort(function (a, b) { return a.id.localeCompare(b.id); }); list.showFileHeader(); for (var k = 0; k < files.length; k++) { for (var t = 0; t < files[k].processed.length; t++) { list.showFile(files[k].id, files[k].processed[t].path, files[k].processed[t]); } } setTimeout(function () { callback(); }, 1000); } }); } if (!count) { console.log('Nothing found'); callback(); } } }); } else { var parts = pattern.split('/'); var id = parts.shift(); var path = parts.join('/'); objects.rm(id, path, {user: 'system.user.admin'}, function (err, processed) { if (err) { console.error(err); } else { if (processed) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); list.showFileHeader(); for (var i = 0; i < processed.length; i++) { list.showFile(id, processed[i].path, processed[i]); } } } setTimeout(function () { callback(); }, 1000); }); } }); })(); break; case 'chmod': (function () { var mode = args[0]; var pattern = args[1]; if (!mode) { console.log('No mode found. Example: "chmod 777 /vis.0/main/*"'); callback(1); return; } else { //yargs has converted it to number mode = parseInt(mode.toString(), 16); } if (!pattern) { console.log('No file path found. Example: "chmod 777 /vis.0/main/*"'); callback(1); return; } dbConnect(params, function () { // extract id pattern = pattern.replace(/\\/g, '/'); if (pattern[0] === '/') pattern = pattern.substring(1); if (pattern === '*') { objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, arr) { if (!err && arr && arr.rows) { var files = []; var count = 0; for (var i = 0; i < arr.rows.length; i++) { if (arr.rows[i].value.type !== 'adapter') continue; count++; objects.chmodFile(arr.rows[i].value.common.name, '*', {user: 'system.user.admin', mode: mode}, function (err, processed, _id) { if (!err && processed) { files.push({id: _id, processed: processed}); } if (!--count) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); files.sort(function (a, b) { return a.id.localeCompare(b.id); }); list.showFileHeader(); for (var k = 0; k < files.length; k++) { for (var t = 0; t < files[k].processed.length; t++) { list.showFile(files[k].id, files[k].processed[t].path, files[k].processed[t]); } } setTimeout(function () { callback(); }, 1000); } }); } if (!count) { console.log('Nothing found'); callback(); } } }); } else { var parts = pattern.split('/'); var id = parts.shift(); var path = parts.join('/'); objects.chmodFile(id, path, {user: 'system.user.admin', mode: mode}, function (err, processed) { if (err) { console.error(err); } else { if (processed) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); list.showFileHeader(); for (var i = 0; i < processed.length; i++) { list.showFile(id, processed[i].path, processed[i]); } } } setTimeout(function () { callback(); }, 1000); }); } }); })(); break; case 'chown': (function () { var user = args[0]; var group = args[1]; var pattern = args[2]; if (!pattern) { pattern = group; group = undefined; } if (!user) { console.log('No user found. Example: "chown user /vis.0/main/*"'); callback(1); } else if (user.substring(12) !== 'system.user.') { user = 'system.user.' + user; } if (group && group.substring(13) !== 'system.group.') { group = 'system.group.' + group; } if (!pattern) { console.log('No file path found. Example: "chown user /vis.0/main/*"'); callback(1); return; } dbConnect(params, function () { // extract id pattern = pattern.replace(/\\/g, '/'); if (pattern[0] === '/') pattern = pattern.substring(1); if (pattern === '*') { objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, arr) { if (!err && arr && arr.rows) { var files = []; var count = 0; for (var i = 0; i < arr.rows.length; i++) { if (arr.rows[i].value.type !== 'adapter') continue; count++; objects.chownFile(arr.rows[i].value.common.name, '*', {user: 'system.user.admin', owner: user, ownerGroup: group}, function (err, processed, _id) { if (!err && processed) { files.push({id: _id, processed: processed}); } if (!--count) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); files.sort(function (a, b) { return a.id.localeCompare(b.id); }); list.showFileHeader(); for (var k = 0; k < files.length; k++) { for (var t = 0; t < files[k].processed.length; t++) { list.showFile(files[k].id, files[k].processed[t].path, files[k].processed[t]); } } setTimeout(function () { callback(); }, 1000); } }); } if (!count) { console.log('Nothing found'); callback(); } } }); } else { var parts = pattern.split('/'); var id = parts.shift(); var path = parts.join('/'); objects.chownFile(id, path, {user: 'system.user.admin', owner: user, ownerGroup: group}, function (err, processed) { if (err) { console.error(err); } else { // call here list if (processed) { var List = require(__dirname + '/setup/setupList.js'); var list = new List({ states: states, objects: objects, processExit: callback }); list.showFileHeader(); for (var i = 0; i < processed.length; i++) { list.showFile(id, processed[i].path, processed[i]); } } } setTimeout(function () { callback(); }, 1000); }); } }); })(); break; case 'user': (function () { var command = args[0] || ''; var user = args[1] || ''; if (user && user.match(/^system\.user\./)) user = user.substring('system.user.'.length); dbConnect(params, function () { var Users = require(__dirname + '/setup/setupUsers.js'); var users = new Users({ objects: objects, processExit: callback }); var password = params.password; var group = params.ingroup || 'system.group.administrator'; if (command === 'add') { users.addUserPrompt(user, group, password, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" created (Group: ' + group.replace('system.group.', '') + ')'); callback(); } }); } else if (command === 'del' || command === 'delete') { users.delUser(user, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" deleted'); callback(); } }); } else if (command === 'check') { users.checkUserPassword(user, password, function (err) { if (err) { console.error(err); callback(30); } else { console.log('Password for user "' + user + '" matched.'); callback(); } }); } else if (command === 'set' || command === 'passwd') { users.setUserPassword(user, password, function (err) { if (err) { console.error(err); callback(30); } else { console.log('Password for "' + user + '" was successfully set.'); callback(); } }); } else if (command === 'enable' || command === 'e') { users.enableUser(user, true, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" was successfully enabled.'); callback(); } }); } else if (command === 'disable' || command === 'd') { users.enableUser(user, false, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" was successfully disabled.'); callback(); } }); } else if (command === 'get') { users.getUser(user, function (err, isEnabled) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" is ' + (isEnabled ? 'enabled' : 'disabled')); callback(); } }); } else { console.warn('Unknown command "' + command + '". Available commands are: add, del, passwd, enable, disable, check, get'); callback(1); } }); })(); break; case 'g': case 'group': (function () { var command = args[0] || ''; var group = args[1] || ''; var user = args[2] || ''; if (group && group.match(/^system\.group\./)) group = group.substring('system.group.'.length); if (user && user.match(/^system\.user\./)) user = user.substring('system.user.'.length); if (!command) { console.warn('Unknown command "' + command + '". Available commands are: add, del, passwd, enable, disable, list, get'); return callback(1); } if (!group) { console.warn('Please define group name: group ' + command + ' groupName'); return callback(30); } dbConnect(params, function () { var Users = require(__dirname + '/setup/setupUsers.js'); var users = new Users({ objects: objects, processExit: callback }); if (command === 'useradd' || command === 'adduser') { if (!user) { console.warn('Please define user name: group useradd groupName userName'); callback(30); } users.addUserToGroup(user, group, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" created'); callback(); } }); } else if (command === 'userdel' || command === 'deluser') { if (!user) { console.warn('Please define user name: group userdel groupName userName'); callback(30); } users.removeUserFromGroup(user, group, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" created'); callback(); } }); } else if (command === 'add') { users.addGroup(group, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + group + '" created'); callback(); } }); } else if (command === 'del' || command === 'delete') { users.delGroup(group, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + group + '" deleted'); callback(); } }); } else if (command === 'list' || command === 'l') { users.getGroup(group, function (err, isEnabled, list) { if (err) { console.error(err); callback(30); } else { console.log('Group "' + group + '" is ' + (isEnabled ? 'enabled' : 'disabled') + ' and has following members:'); if (list) { for (var i = 0; i < list.length; i++) { console.log(list[i].substring('system.user.'.length)); } } callback(); } }); } else if (command === 'enable' || command === 'e') { users.enableGroup(group, true, function (err) { if (err) { console.error(err); callback(30); } else { console.log('Group "' + group + '" was successfully enabled.'); callback(); } }); } else if (command === 'disable' || command === 'd') { users.enableGroup(group, false, function (err) { if (err) { console.error(err); callback(30); } else { console.log('Group "' + group + '" was successfully disabled.'); callback(); } }); } else if (command === 'get') { users.getGroup(group, function (err, isEnabled, list) { if (err) { console.error(err); callback(30); } else { console.log('Group "' + group + '" is ' + (isEnabled ? 'enabled' : 'disabled')); callback(); } }); } else { console.warn('Unknown command "' + command + '". Available commands are: add, del, passwd, enable, disable, list, get'); callback(1); } }); })(); break; case 'adduser': (function () { var user = args[0]; var group = params.ingroup || 'system.group.administrator'; var password = params.password; dbConnect(params, function () { var Users = require(__dirname + '/setup/setupUsers.js'); var users = new Users({ objects: objects, processExit: callback }); users.addUserPrompt(user, group, password, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" created (Group: ' + group.replace('system.group.', '') + ')'); callback(); } }); }); })(); break; case 'passwd': (function () { var user = args[0]; var password = params.password; dbConnect(params, function () { var Users = require(__dirname + '/setup/setupUsers.js'); var users = new Users({ objects: objects, processExit: callback }); users.setUserPassword(user, password, function (err) { if (err) { console.error(err); callback(30); } else { console.log('Password for "' + user + '" was successfully set.'); callback(); } }); }); })(); break; case 'ud': case 'udel': case 'userdel': case 'deluser': (function () { var user = args[0]; dbConnect(params, function () { var Users = require(__dirname + '/setup/setupUsers.js'); var users = new Users({ objects: objects, processExit: callback }); users.delUser(user, function (err) { if (err) { console.error(err); callback(30); } else { console.log('User "' + user + '" deleted'); callback(); } }); }); })(); break; // Create package.json in /opt/' + tools.appName + ' case 'package': (function () { var json = { name: tools.appName, engines: { node: '>=0.8' }, optionalDependencies: { }, dependencies: {}, author: 'bluefox ' }; json.dependencies[tools.appName + '.js-controller'] = '*'; json.dependencies[tools.appName + '.admin'] = '*'; tools.getRepositoryFile(null, function (err, sources) { if (sources) { for (var s in sources) { if (sources.hasOwnProperty(s)) { if (sources[s].url) { if (!json.dependencies[tools.appName + '.' + s]) { json.optionalDependencies[tools.appName + '.' + s] = sources[s].url; } } else { if (!json.dependencies[tools.appName + '.' + s]) { json.optionalDependencies[tools.appName + '.' + s] = '*'; } } } } } fs.writeFileSync(__dirname + '/../../../package.json', JSON.stringify(json, null, 2)); callback(); }); })(); break; case 'set': (function () { var instance = args[0]; if (!instance) { console.warn('please specify instance.'); callback(1); } if (instance.indexOf('.') === -1) { console.warn('please specify instance, like "' + instance + '.0"'); callback(1); } dbConnect(params, function () { objects.getObject('system.adapter.' + instance, function (err, obj) { if (!err && obj) { var changed = false; for (var a = 0; a < process.argv.length; a++) { if (process.argv[a].match(/^--/) && process.argv[a + 1] && !process.argv[a + 1].match(/^--/)) { var attr = process.argv[a].substring(2); var val = process.argv[a + 1]; if (val === 'true') val = true; if (val === 'false') val = false; if (parseFloat(val).toString() === val) val = parseFloat(val); if (attr.indexOf('.') !== -1) { var parts = attr.split('.'); if (!obj.native[parts[0]] || obj.native[parts[0]][parts[1]] === undefined) { console.warn('Adapter "' + instance + '" has no setting "' + attr + '".'); } else { changed = true; obj.native[parts[0]][parts[1]] = val; console.log('New ' + attr + ' for "' + instance + '" is: ' + val); } } else { if (obj.native[attr] === undefined) { console.warn('Adapter "' + instance + '" has no setting "' + attr + '".'); } else { changed = true; obj.native[attr] = val; console.log('New ' + attr + ' for "' + instance + '" is: ' + val); } } a++; } } if (changed) { obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject('system.adapter.' + instance, obj, function () { console.log('Instance settings for "' + instance + '" are changed.'); callback(); }); } else { console.log('No parameters set.'); callback(); } } else { console.error('Instance "' + instance + '" does not exist.'); callback(24); } }); }); })(); break; case 'host': (function () { var change = args[0]; var oldHostname; var newHostname; if (!change) { console.warn('Please write "' + tools.appName + ' host this" to use this host ("' + tools.getHostName() + '") in ' + tools.appName + ' for all instances.'); callback(1); } var config = tools.getConfigFileName(); var data = JSON.parse(fs.readFileSync(config, 'utf8')); if (change === 'set') { oldHostname = tools.getHostName(); newHostname = args[1]; if (!newHostname) { console.error('To change host name call: ' + tools.appName + ' host set newName'); callback(34); } data.system = data.system || {}; data.system.hostname = newHostname; fs.writeFileSync(config, JSON.stringify(data)); } else if (change === 'remove') { oldHostname = args[1]; newHostname = tools.getHostName(); if (!oldHostname) { console.error('Host to remove is not defined. Usage: ' + tools.appName + ' host remove '); callback(34); } } else { oldHostname = (change !== 'self' && change !== 'this') ? change : null; newHostname = require('os').hostname(); } dbConnect(params, function (_objects, _states, isOffline) { if (!isOffline) { console.error('Cannot execute changes on running system. Stop ' + tools.appName + ' first.'); callback(30); } var count = 0; // find first host objects.getObjectList({startkey: 'system.host.', endkey: 'system.host.\u9999'}, function (err, objs) { if (!oldHostname) { var hostCount = 0; for (var j = 0; j < objs.rows.length; j++) { if (objs.rows[j].value.type === 'host') { var hNameM = objs.rows[j].value._id.match(/^system\.host\.([^.]+)(\..*)?$/); if (hNameM) { oldHostname = hNameM[1]; hostCount++; } } } if (hostCount > 1) { console.warn('More than one host found. You must specifiy which hist must be renamed.'); callback(30); } } if (change !== 'set' && change !== 'remove' && oldHostname && data.system && data.system.hostname === oldHostname) { data.system = data.system || {}; data.system.hostname = newHostname; fs.writeFileSync(config, JSON.stringify(data)); } hostCount = 0; for (var i = 0; i < objs.rows.length; i++) { if (objs.rows[i].value.type !== 'host' && objs.rows[i].value.type !== 'state') continue; var hNameM = objs.rows[i].value._id.match(/^system\.host\.([^.]+)(\..*)?$/); var hName = null; if (hNameM) { hName = hNameM[1]; } else { continue; } var obj = objs.rows[i].value; if (obj.type === 'host') { if (hName === newHostname && change !== 'remove') { hostCount++; if (hostCount > 1) { console.error('Host with actual hostname "' + hName + '" found. Cannot rename it.'); continue; } } } if (hName === oldHostname) { if (obj.type === 'host') { count++; objects.delObject(obj._id, function (err) { if (err) console.error('Cannot delete object: ' + err); if (!--count) callback(); }); if (change !== 'remove') { console.log('Rename host "' + obj._id + '" to system.host.' + newHostname); obj._id = 'system.host.' + newHostname; obj.common.name = obj._id; obj.common.hostname = newHostname; obj.common.address = []; obj.common.cmd = ''; obj.common.native = {process: {}, os: {}, hardware: {}}; count++; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject(obj._id, obj, function (err) { if (err) console.error('Cannot set object: ' + err); if (!--count) callback(); }); } else { console.log('Remove host "' + obj._id + '"'); } } else { // state count++; objects.delObject(obj._id, function (err) { if (err) console.error('Cannot delete object: ' + err); if (!--count) callback(); }); if (change !== 'remove') { console.log('Rename state "' + obj._id + '".'); obj._id = 'system.host.' + newHostname + (hNameM[2] || ''); count++; obj.from = 'system.host.' + tools.getHostName() + '.cli'; obj.ts = new Date().getTime(); objects.setObject(obj._id, obj, function (err) { if (err) console.error('Cannot set object: ' + err); if (!--count) callback(); }); } else { console.log('Remove state "' + obj._id + '".'); } } } } objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, objs) { for (var i = 0; i < objs.rows.length; i++) { if (objs.rows[i].value.type !== 'instance') continue; if (objs.rows[i].value.common.host === oldHostname) { count++; console.log('Instance host changed for "' + objs.rows[i].value._id + '" from "' + objs.rows[i].value.common.host + '" to "' + newHostname + '".'); objs.rows[i].value.common.host = newHostname; objs.rows[i].value.from = 'system.host.' + tools.getHostName() + '.cli'; objs.rows[i].value.ts = new Date().getTime(); objects.setObject(objs.rows[i].value._id, objs.rows[i].value, function (err) { if (err) console.error('Cannot set object: ' + err); if (!--count) callback(); }); } } if (!count) { console.warn('No instances found for host "' + change + '".'); callback(); } }); }); }); })(); break; case 'visdebug': (function () { var widgetset = args[0]; if (widgetset && widgetset.match('/^vis-/')) { widgetset = widgetset.substring(4); } var VisDebug = require(__dirname + '/setup/setupVisDebug.js'); dbConnect(params, function (_objects) { var visDebug = new VisDebug({ objects: _objects, processExit: callback }); visDebug.enableDebug(widgetset); }); })(); break; case 'file': case 'f': (function () { var cmd = args[0]; if (cmd !== 'read' && cmd !== 'r' && cmd !== 'w' && cmd !== 'write') { console.log('Invalid parameters: write "file read /vis.0/main/img/picture.png /opt/picture/image.png" to read the file'); callback(1); } if (!args[1]) { console.log('Invalid parameters: write "file read /vis.0/main/img/picture.png /opt/picture/image.png" to read the file'); callback(1); } dbConnect(params, function (_objects) { if (cmd === 'read' || cmd ==='r') { var toRead = args[1]; var parts = toRead.replace(/\\/g, '/').split('/'); var path = (args[2] || process.cwd()).replace(/\\/g, '/').split('/'); var file = path[path.length - 1]; if (!file.match(/\.[a-zA-Z0-9]+$/)) { path.push(parts[parts.length - 1]); } var adapt = parts.shift(); if (!adapt) adapt = parts.shift(); _objects.readFile(adapt, parts.join('/'), function (err, data) { if (err) console.error(err); if (data) { fs.writeFileSync(path.join('/'), data); console.log('File "' + toRead + '" stored as "' + path.join('/') + '"'); } callback(0); }); } else { var toRead = args[1]; var parts = toRead.replace(/\\/g, '/').split('/'); var path = args[2].replace(/\\/g, '/').split('/'); var file = path[parts.length - 1]; if (!file) { path.splice(path.length - 1, 1); file = path[path.length - 1]; } if (!file.match(/\.[a-zA-Z0-9]+$/)) { path.push(parts[parts.length - 1]); } var adapt = path.shift(); if (!adapt) adapt = path.shift(); var data = fs.readFileSync(toRead); _objects.writeFile(adapt, path.join('/'), data, function (err) { console.log('File "' + toRead + '" stored as "' + path.join('/') + '"'); callback(0); }); } }); })(); break; case 'id': case 'uuid': (function () { dbConnect(params, function (objects) { objects.getObject('system.meta.uuid', function (err, obj) { if (err) { console.error('Error: ' + err); callback(101); } if (obj && obj.native) { console.log(obj.native.uuid); callback(); } else { console.error('Error: no UUID found'); callback(101); } }); }); })(); break; case 'v': case 'version': (function () { var adapter = args[0]; if (adapter) { try { iopckg = require(tools.appName + '.' + adapter + '/package.json'); } catch (err) { iopckg = {version: '"' + adapter + '" not found'}; } } else { iopckg = require(__dirname + '/../package.json'); } console.log(iopckg.version); callback(); })(); break; case 'checklog': (function () { dbConnect(params, function (objects, states, isOffline) { if (isOffline) { console.log(tools.appName + ' is not running'); callback(100); } else { console.log(tools.appName + ' is running'); objects.getObjectList({startkey: 'system.host.', endkey: 'system.host.' + '\u9999'}, null, function (err, res) { if (!err && res.rows.length) { for (var i = 0; i < res.rows.length; i++) { var parts = res.rows[i].id.split('.'); // ignore system.host.name.alive and so on if (parts.length === 3) { states.pushMessage(res.rows[i].id, {command: 'checkLogging', message: null, from: 'console'}); } } } setTimeout(function () { callback(); }, 200); }); } }); })(); break; case 'repo': (function () { Objects = require(__dirname + '/objects'); var repoUrlOrCommand = args[0]; // Repo url or name or "add" / "del" / "set" / "show" / "addset" var repoName = args[1]; // Repo url or name var repoUrl = args[2]; // Repo url or name if (repoUrlOrCommand !== 'add' && repoUrlOrCommand !== 'del' && repoUrlOrCommand !== 'set' && repoUrlOrCommand !== 'show' && repoUrlOrCommand !== 'addset') { repoUrl = repoUrlOrCommand; repoUrlOrCommand = 'show'; } dbConnect(params, function () { var Repo = require(__dirname + '/setup/setupRepo.js'); var repo = new Repo({ objects: objects }); if (repoUrlOrCommand === 'show') { repo.showRepoStatus(callback); } else if (repoUrlOrCommand === 'add' || repoUrlOrCommand === 'del' || repoUrlOrCommand === 'set' || repoUrlOrCommand === 'addset') { if (!repoName || !repoName.match(/[-_\w\d]+/)) { console.error('Invalid repository name: "' + repoName + '"'); callback(); } else { if (repoUrlOrCommand === 'add' || repoUrlOrCommand === 'addset') { if (!repoUrl) { console.warn('Please define repository URL or path: ' + tools.appName + ' add '); callback(45); } else { repo.add(repoName, repoUrl, function (err) { if (err) { console.error(err); callback(45); } else { if (repoUrlOrCommand === 'addset') { repo.setActive(repoName, function (err) { if (err) { console.error(err); callback(45); } else { console.log('Repository "' + repoName + '" set as active: "' + repoUrl + '"'); repo.showRepoStatus(callback); } }); } else { console.log('Repository "' + repoName + '" added as "' + repoUrl + '"'); repo.showRepoStatus(callback); } } }); } } else if (repoUrlOrCommand === 'set') { repo.setActive(repoName, function (err) { if (err) { console.error(err); callback(45); } else { console.log('Repository "' + repoName + '" set as active.'); repo.showRepoStatus(callback); } }); } else if (repoUrlOrCommand === 'del') { repo.del(repoName, function (err) { if (err) { console.error(err); callback(45); } else { console.log('Repository "' + repoName + '" deleted.'); repo.showRepoStatus(callback); } }); } else { console.warn('Unknown repo command: ' + repoUrlOrCommand); callback(105); } } } }); })(); break; case 'multihost': case 'mh': (() => { var cmd = args[0]; if (cmd !== 'c' && cmd !== 'connect' && cmd !== 's' && cmd !== 'status' && cmd !== 'b' && cmd !== 'browse' && cmd !== 'e' && cmd !== 'enable' && cmd !== 'd' && cmd !== 'disable') { console.log('Invalid parameters. Following is possible: enable, browse, connect, status'); callback(1); } else { dbConnect(params, function () { var Multihost = require(__dirname + '/setup/setupMultihost.js'); var mh = new Multihost({ params: params, processExit: callback, objects: objects }); if (cmd === 's' || cmd === 'status') { mh.status(function () { callback(30); }); } else if (cmd === 'b' || cmd === 'browse') { mh.browse(function (err, list) { if (err) { console.error(err); callback(30); } else { mh.showHosts(list); callback(); } }); } else if (cmd === 'e' || cmd === 'enable') { mh.enable(true, function (err) { if (err) { console.error(err); callback(1); } else { states.pushMessage('system.host.' + tools.getHostName(), {command: 'updateMultihost', message: null, from: 'setup'}, function() { callback(); }); } }); } else if (cmd === 'd' || cmd === 'disable') { mh.enable(false, function (err) { if (err) { console.error(err); callback(1); } else { states.pushMessage('system.host.' + tools.getHostName(), {command: 'updateMultihost', message: null, from: 'setup'}, function() { callback(); }); } }); } else if (cmd === 'c' || cmd === 'connect') { mh.connect(args[1], args[2], function (err) { if (err) { console.error(err) } callback(err ? 1 : 0); }); } }); } })(); break; case 'vendor': (() => { const password = args[0]; const file = args[1]; if (!password) { console.warn(`Please specify the password to update the vendor information!\n${tools.appName.toLowerCase()} vendor `); callback(1); } if (!file) { console.warn(`Please specify the path to the vendor file to update the vendor information!\n${tools.appName.toLowerCase()} vendor `); callback(1); } else { dbConnect(params, function () { const Vendor = require('./setup/setupVendor'); const vendor = new Vendor({ objects: objects }); vendor.checkVendor(file, password).then(() => { console.log(`Synchronised vendor information.`); callback(); }).catch(err => { console.error(`Cannot update vendor information: ${JSON.stringify(err)}`); callback(1); }); }); } })(); break; case 'license': (() => { const file = args[0]; if (!file) { console.warn(`Please specify the path to the license file or place license text directly!\n${tools.appName.toLowerCase()} license `); callback(1); } else { dbConnect(params, function () { const License = require('./setup/setupLicense'); const license = new License({ objects: objects }); license.setLicense(file).then(type => { console.log(`License ${type} updated.`); callback(); }).catch(err => { console.error(`Cannot update license: ${JSON.stringify(err)}`); callback(1); }); }); } })(); break; default: if (params.v || params.version) { var iopckg; if (command) { try { iopckg = require(tools.appName + '.' + command + '/package.json'); } catch (err) { iopckg = {version: '"' + command + '" not found'}; } } else { iopckg = require(__dirname + '/../package.json'); } console.log(iopckg.version); } else { showHelp(); callback(1); } callback(); break; } } // Save objects before exit function processExit(exitCode) { if (objects && objects.destroy) objects.destroy(); if (states && states.destroy) states.destroy(); process.exit(exitCode); } function cleanDatabase(isDeleteDb, callback) { var taskCnt = 0; if (isDeleteDb) { objects.destroyDB(function () { // Clean up states states.getKeys('*', function (err, obj) { var delState = []; var i; if (obj) { for (i = 0; i < obj.length; i++) { delState.push(obj[i]); } } taskCnt = 0; for (i = 0; i < obj.length; i++) { taskCnt++; states.delState(delState[i], function () { if (!(--taskCnt) && callback) callback(obj.length); }); } }); }); } else { // Clean only objects, not the views objects.getObjectList({startkey: '\u0000', endkey: '\u9999'}, function (err, res) { if (!err && res.rows.length) { console.log('clean ' + res.rows.length + ' objects...'); for (var i = 0; i < res.rows.length; i++) { //console.log('Delete ' + res.rows[i].id); objects.delObject(res.rows[i].id); } } // Clean up states states.getKeys('*', function (err, obj) { var delState = []; var i; if (obj) { for (i = 0; i < obj.length; i++) { delState.push(obj[i]); } } taskCnt = 0; console.log('clean ' + obj.length + ' states...'); for (i = 0; i < obj.length; i++) { taskCnt++; states.delState(delState[i], function () { if (!(--taskCnt) && callback) callback(obj.length); }); } if (!taskCnt && callback) callback(obj.length); }); }); } } function restartController(callback) { var spawn = require('child_process').spawn; console.log('Starting node restart.js'); var child = spawn('node', [__dirname + '/restart.js'], { detached: true, stdio: ['ignore', 'ignore', 'ignore'] }); child.unref(); if (callback) { callback(); } else { processExit(); } } function installNpm(adapter, callback) { var path = __dirname; if (typeof adapter === 'function') { callback = adapter; adapter = undefined; } if (adapter) { path = tools.getAdapterDir(adapter); } // iob_npm.done file was created if "npm i" yet called there if (fs.existsSync(path + '/package.json') && !fs.existsSync(path + '/iob_npm.done')) { tools.disablePackageLock(function (err) { var cmd = 'npm install --production'; console.log(cmd + ' (System call) in "' + path + '"'); // Install node modules as system call // System call used for update of js-controller itself, // because during installation npm packet will be deleted too, but some files must be loaded even during the install process. var exec = require('child_process').exec; var child = exec(cmd, { cwd: path }); child.stderr.pipe(process.stdout); child.on('exit', function (code, signal) { if (code) { console.log('Cannot install ' + tools.appName + '.' + adapter + ': ' + code); (callback || processExit)(25); } // command succeeded if (callback) callback(null, adapter); }); }); } else { if (callback) callback(null, adapter); } } function getRepository(repoUrl, params, callback) { if (typeof params === 'function') { callback = params; params = {}; } params = params || {}; if (!repoUrl || typeof repoUrl !== 'object') { if (!objects) { dbConnect(params, function () { getRepository(repoUrl, params, callback); }); } else { // try to read repository objects.getObject('system.config', function (err, systemConfig) { objects.getObject('system.repositories', function (err, repos) { // Check if repositories exists if (!err && repos && repos.native && repos.native.repositories) { var active = systemConfig.common.activeRepo; if (repos.native.repositories[active]) { if (typeof repos.native.repositories[active] === 'string') { repos.native.repositories[active] = { link: repos.native.repositories[active], json: null }; } // If repo is not yet loaded if (!repos.native.repositories[active].json) { console.log('Update repository "' + active + '" under "' + repos.native.repositories[active].link + '"'); // Load it tools.getRepositoryFile(repos.native.repositories[active].link, function (err, sources) { repos.native.repositories[active].json = sources; repos.from = 'system.host.' + tools.getHostName() + '.cli'; repos.ts = new Date().getTime(); // Store uploaded repo objects.setObject('system.repositories', repos, function () { callback(null, sources); }); }); } else { // We have already repo, give it back callback(null, repos.native.repositories[active].json); } } else { console.log('Requested repository "' + active + '" does not exit in config.'); callback(25); } } else { console.log('No repositories defined.'); callback(25); } }); }); } } else { callback(null, repoUrl); } } function dbConnect(onlyCheck, params, callback) { if (typeof onlyCheck === 'object') { callback = params; params = onlyCheck; onlyCheck = false; } if (typeof onlyCheck === 'function') { callback = onlyCheck; onlyCheck = false; } if (typeof params === 'function') { callback = params; params = null; } params = params || {}; if (objects && states) { callback(objects, states); return; } var config = require(tools.getConfigFileName()); if (!config.states) config.states = {type: 'file'}; if (!config.objects) config.objects = {type: 'file'}; Objects = require(__dirname + '/objects'); States = require(__dirname + '/states'); // Give to controller 2 seconds for connection var isObjectConnected = false; var isStatesConnected = false; // Detect timeout or try to open file itself setTimeout(function () { if (isObjectConnected && isStatesConnected) return; if (onlyCheck) { if (typeof callback === 'function') callback(null, null, true); return; } if (!isObjectConnected) { if (config.objects.type === 'file') { // Just open in memory DB itself Objects = require(__dirname + '/objects/objectsInMemServer'); objects = new Objects({ connection: config.objects, logger: { silly: function (msg) { }, debug: function (msg) { }, info: function (msg) { }, warn: function (msg) { console.log(msg); }, error: function (msg) { console.log(msg); } }, connected: function () { isObjectConnected = true; if (isStatesConnected && typeof callback === 'function') callback(objects, states, true); } }); } else { console.log('No connection to ' + config.objects.host + ':' + config.objects.port + '[' + config.objects.type + ']'); processExit(22); } } if (!isStatesConnected) { if (config.states.type === 'file') { // Just open in memory DB itself States = require(__dirname + '/states/statesInMemServer'); states = new States({ connection: config.states, logger: { silly: function (msg) { }, debug: function (msg) { }, info: function (msg) { }, warn: function (msg) { console.log(msg); }, error: function (msg) { console.log(msg); } }, connected: function () { isStatesConnected = true; if (isObjectConnected && typeof callback === 'function') callback(objects, states, true); } }); } else { console.log('No connection to states ' + config.states.host + ':' + config.states.port + '[' + config.states.type + ']'); processExit(22); } } }, params.timeout || config.objects.connectTimeout || 2000); // try to connect as client objects = new Objects({ connection: config.objects, logger: { silly: function (msg) { }, debug: function (msg) { }, info: function (msg) { }, warn: function (msg) { console.log(msg); }, error: function (msg) { console.log(msg); } }, connected: function () { if (isObjectConnected) return; isObjectConnected = true; if (isStatesConnected && typeof callback === 'function') callback(objects, states); } }); states = new States({ connection: config.states, logger: { silly: function (msg) { }, debug: function (msg) { }, info: function (msg) { }, warn: function (msg) { console.log(msg); }, error: function (msg) { console.log(msg); } }, connected: function () { if (isStatesConnected) return; isStatesConnected = true; if (isObjectConnected && typeof callback === 'function') callback(objects, states); } }); } module.exports.processCommand = function (_objects, _states, command, args, params, callback) { objects = _objects; states = _states; processCommand(command, args, params, callback); }; module.exports.execute = function () { // direct call var _yargs = initYargs(); var command = _yargs.argv._[0]; var args = []; for (var a = 1; a < _yargs.argv._.length; a++) { args.push(_yargs.argv._[a]); } processCommand(command, args, _yargs.argv, processExit); };