UNPKG

yunkong2

Version:

automate your life - platfom

534 lines (475 loc) 22.3 kB
/** * * yunkong2 installer from npm * * Copyright 1'2015-2018 bluefox <dogafox@gmail.com> * * */ /* jshint -W097 */// jshint strict:false /*jslint node: true */ 'use strict'; const yargs = require('yargs') .usage('Commands:\n' + '$0 [--objects <host>] [--states <host>] [custom]\n') .default('objects', '127.0.0.1') .default('states', '127.0.0.1') .default('lang', 'en'); const fs = require('fs-extra'); const path = require('path'); const child_process = require('child_process'); const exec = child_process.exec; const tools = require('./tools.js'); /** The location of this module's root dir. E.g. /opt/yunkong2 */ const rootDir = path.join(__dirname, '..'); /** The location of the js-controller module. E.g. /opt/yunkong2/node_modules/yunkong2.js-controller */ const controllerDir = path.join(rootDir, 'node_modules/yunkong2.js-controller/'); /** The location of the executable "yunkong2" (in the js-controller directory) */ const yunkong2Executable = path.join(controllerDir, 'yunkong2'); /** The location of the executable "yunkong2" inside the root directory (links to the one in js-controller) */ const yunkong2RootExecutable = path.join(rootDir, 'yunkong2'); /** The location of the executable "iob" (in the js-controller directory) */ const iobExecutable = path.join(controllerDir, 'iob'); /** The location of the executable "iob" inside the root directory (links to the one in js-controller) */ const iobRootExecutable = path.join(rootDir, 'iob'); /** The location of the log directory */ const logDir = path.join(rootDir, 'log'); /** The location of js-controller's main module (relative) */ const jsControllerMainModule = 'node_modules/yunkong2.js-controller/yunkong2.js'; /** The location of js-controller's main module (absolute) */ const jsControllerMainModuleAbsolute = path.join(rootDir, jsControllerMainModule); /** The command line to execute yunkong2 */ const commandLine = `node ${jsControllerMainModule} $1 $2 $3 $4 $5`; /** The command line to execute yunkong2 (absolute path) */ const commandLineAbsolute = `node ${jsControllerMainModuleAbsolute} $1 $2 $3 $4 $5`; const debug = !!process.env.IOB_DEBUG; let linuxAutoStart = null; let linuxInstallSh = null; // Save objects before exit function processExit(exitCode) { process.exit(exitCode); } function setupWindows(callback) { fs.writeFileSync(yunkong2RootExecutable + '.bat', commandLine.replace(/\$/g, '%')); fs.writeFileSync(iobRootExecutable + '.bat', commandLine.replace(/\$/g, '%')); console.log('Write "yunkong2 start" to start the yunkong2'); if (!fs.existsSync(process.env['APPDATA'] + '/npm')) fs.mkdirSync(process.env['APPDATA'] + '/npm'); if (!fs.existsSync(process.env['APPDATA'] + '/npm-cache')) fs.mkdirSync(process.env['APPDATA'] + '/npm-cache'); // Copy the files from /install/windows to the root dir tools.copyFilesRecursiveSync( path.join(rootDir, 'install/windows'), rootDir ); // Call npm install node-windows // js-controller installed as npm const npmRootDir = rootDir.replace(/\\/g, '/'); console.log('npm install node-windows@0.1.14 --production --save --prefix "' + npmRootDir + '"'); const child = exec('npm install node-windows@0.1.14 --production --save --prefix "' + npmRootDir + '"'); child.stderr.pipe(process.stdout); child.on('exit', () => { // call node install.js // install node as service const child1 = exec('node "' + path.join(rootDir, 'install.js') + '"'); child1.stderr.pipe(process.stdout); child1.on('exit', () => { console.log('yunkong2 service installed. Write "serviceyunkong2 start" to start the service and go to http://localhost:8081 to open the admin UI.'); console.log('To see the outputs do not start the service, but write "node node_modules/yunkong2.js-controller/controller"'); if (callback) callback(); }); }); } function log(text) { debug && console.log('[INSTALL] ' + text); } function setupFreeBSD(callback) { log('Execute install for FreeBSD'); fs.writeFileSync(yunkong2RootExecutable, commandLine, { mode: '755' }); fs.writeFileSync(iobRootExecutable, commandLine, { mode: '755' }); console.log('Write "./iob start" to start the yunkong2'); // create try { if (!fs.existsSync(yunkong2Executable)) { fs.writeFileSync(yunkong2Executable, "#!/usr/bin/env node\nrequire('./lib/installSetup.js');"); } fs.chmodSync(yunkong2RootExecutable, '755'); fs.chmodSync(yunkong2Executable, '755'); try { if (!fs.existsSync(iobExecutable)) { fs.writeFileSync(iobExecutable, "#!/usr/bin/env node\nrequire('./lib/installSetup.js');"); } fs.chmodSync(iobRootExecutable, '755'); fs.chmodSync(iobExecutable, '755'); } catch (e) { console.error('Cannot set permissions of ' + iobExecutable); console.log('You can still manually copy '); } } catch (e) { console.error('Cannot set permissions of ' + yunkong2Executable); console.log('You can still manually copy '); } try { // check if /etc/init.d/ exists if (fs.existsSync('/usr/local/bin')) { fs.writeFileSync('/usr/local/bin/yunkong2', commandLineAbsolute, { mode: '755' }); fs.chmodSync('/usr/local/bin/yunkong2', '755'); } } catch (e) { console.warn('Cannot create file /usr/local/bin/yunkong2!. Non critical'); // create files for manual coping try { fs.writeFileSync(path.join(rootDir, 'install/yunkong2'), commandLineAbsolute, { mode: '755' }); } catch (e) { // don't care } console.log(''); console.log('-----------------------------------------------------'); console.log('You can manually copy file into /usr/local/bin/. Just write:'); console.log(' cp ' + path.join(rootDir, 'install/yunkong2') + ' /usr/local/bin/'); console.log('-----------------------------------------------------'); } if (fs.existsSync('/usr/local/etc/rc.d')) { // replace @@PATH@@ with position of let txt = linuxAutoStart || fs.readFileSync(rootDir + 'install/freebsd/yunkong2'); txt = txt.toString().replace(/@@PATH@@/g, controllerDir); txt = txt.toString().replace(/@@HOME@@/g, rootDir); try { if (fs.existsSync(path.join(rootDir, 'install/freebsd/'))) { fs.writeFileSync(path.join(rootDir, 'install/freebsd/yunkong2'), txt, { mode: '755' }); fs.chmodSync(path.join(rootDir, 'install/freebsd/yunkong2'), '755'); } } catch (e) { // don't care } try { // copy yunkong2 from install/freebsd to /usr/local/etc/rc.d fs.writeFileSync('/usr/local/etc/rc.d/yunkong2', txt); txt = linuxInstallSh || fs.readFileSync(path.join(rootDir, 'install/freebsd/install.sh')); txt = txt.toString().replace(/@@PATH@@/g, controllerDir); fs.writeFileSync(path.join(rootDir, 'install.sh'), txt, { mode: '755' }); fs.chmodSync(path.join(rootDir, 'install.sh'), '755'); } catch (err) { console.error('Cannot copy file to /usr/local/etc/rc.d/yunkong2: ' + err); console.log(''); console.log('-----------------------------------------------------'); console.log('You can manually copy file and install autostart: '); console.log(' cp ' + path.join(rootDir, 'install/freebsd/yunkong2') + ' /usr/local/etc/rc.d/'); console.log(' chmod 755 /usr/local/etc/rc.d/yunkong2'); console.log(' sh ' + path.join(rootDir, 'install/freebsd/install.sh')); console.log('-----------------------------------------------------'); console.log(' or just start "sh ' + path.join(rootDir, 'install.sh') + '"'); console.log('-----------------------------------------------------'); if (callback) callback(); } // js-controller installed as npm const child = exec('bash ' + path.normalize(path.join(rootDir, 'install.sh'))); child.stderr.pipe(process.stdout); child.on('exit', () => { console.log('Auto-start was enabled. Write "update-rc.d -f yunkong2.sh remove" to disable auto-start'); console.log('yunkong2 is started. Go to "http://ip-addr:8081" to open the admin UI.'); callback && callback(); }); } else { callback && callback(); } } function getNode() { if (fs.existsSync('/usr/bin/node')) { return '/usr/bin/node'; } else if (fs.existsSync('/usr/bin/nodejs')) { return '/usr/bin/nodejs'; } else if (fs.existsSync('/usr/sbin/nodejs')) { return '/usr/sbin/nodejs'; } else if (fs.existsSync('/usr/sbin/node')) { return '/usr/sbin/node'; } else { return '/usr/bin/node'; } } function isRoot() { return process.getuid && process.getuid() === 0; } function checkUserLinux() { try { log('Check user yunkong2'); const content = fs.readFileSync('/etc/passwd'); const users = content.toString('utf8').split('\n'); return !!users.find(line => line.split(':')[0] === 'yunkong2'); } catch (e) { console.error('Cannot read /etc/passwd: ' + e); return false; } } function createUserLinux(callback) { if (checkUserLinux()) { log('User yunkong2 exists'); return callback && callback(); } else if (isRoot()) { log('Create user yunkong2'); exec('useradd -m -s /usr/sbin/nologin yunkong2', err => { // add to serial exec('usermod -a -G dialout yunkong2', () => { exec('usermod -a -G tty yunkong2', () => { // Add to GPIO group exec('usermod -a -G gpio yunkong2', () => { callback(err); }); }); }); }); } else { log('Required root rights to create user'); callback('Not root'); } } function chownLinux(user, callback) { if (user === 'root') { callback && callback(); } else if (isRoot()) { log('Change owner of all files'); exec('chown yunkong2 * -R', { cwd: rootDir }, err => { err && console.error('Cannot chwon: ' + err); exec('chown ' + rootDir + ' * -R', err => callback(err)); }); } else { console.warn('Write\n "chown yunkong2 * -R"\nas root to change owner'); callback && callback(); } } function setupLinux(callback) { log('Execute install for Liunx'); // create try { if (!fs.existsSync(yunkong2RootExecutable)) { log('Create ' + yunkong2RootExecutable); fs.writeFileSync(yunkong2RootExecutable, commandLine, { mode: '755' }); } fs.chmodSync(yunkong2RootExecutable, '755'); if (!fs.existsSync(iobRootExecutable)) { log('Create ' + iobRootExecutable); fs.writeFileSync(iobRootExecutable, commandLine, { mode: '755' }); } fs.chmodSync(iobRootExecutable, '755'); if (!fs.existsSync(yunkong2Executable)) { log('Create ' + yunkong2Executable); fs.writeFileSync(yunkong2Executable, "#!/usr/bin/env node\nrequire('./lib/installSetup.js');", { mode: '755' }); } fs.chmodSync(yunkong2Executable, '755'); if (!fs.existsSync(iobExecutable)) { log('Create ' + iobExecutable); fs.writeFileSync(iobExecutable, "#!/usr/bin/env node\nrequire('./lib/installSetup.js');", { mode: '755' }); } fs.chmodSync(iobExecutable, '755'); } catch (e) { console.error('Cannot set permissions of ' + yunkong2Executable); console.log('You can still manually copy '); } try { // check if /etc/init.d/ exists if (fs.existsSync('/usr/bin')) { log('Create /usr/bin/yunkong2'); fs.writeFileSync('/usr/bin/yunkong2', commandLineAbsolute, { mode: '755' }); fs.chmodSync('/usr/bin/yunkong2', '755'); log('Create /usr/bin/iob'); fs.writeFileSync('/usr/bin/iob', commandLineAbsolute, { mode: '755' }); fs.chmodSync('/usr/bin/iob', '755'); } } catch (e) { console.warn('Cannot create file /usr/bin/yunkong2!. Non critical'); console.log(''); console.log('-----------------------------------------------------'); console.log('You can manually copy file into /usr/bin/. Just write:'); console.log(' sudo cp ' + yunkong2RootExecutable + ' /usr/bin/'); console.log(' sudo chmod 755 /usr/bin/yunkong2'); console.log('-----------------------------------------------------'); } if (!process.env.IOB_FORCE_INITD && fs.existsSync('/lib/systemd/system/')) { log('Install systemd script'); createUserLinux(err => { err && console.error('Cannot create user "yunkong2": ' + err); const user = err ? 'root' : 'yunkong2'; const nodePath = getNode(); const systemd = '[Unit]\n' + 'Description=yunkong2 Server\n' + 'Documentation=http://yunkong2.net\n' + 'After=network.target\n' + '\n' + '[Service]\n' + 'Type=simple\n' + 'User=' + user + '\n' + 'ExecStart=' + nodePath + ' "' + path.join(controllerDir, 'controller.js') + '"\n' + 'Restart=on-failure\n' + '\n' + '[Install]\n' + 'WantedBy=multi-user.target'; log('Create /lib/systemd/system/yunkong2.service'); log(systemd); fs.writeFileSync('/lib/systemd/system/yunkong2.service', systemd, { mode: '755' }); chownLinux(user, () => { if (!tools.isAutomatedInstallation()) { console.log(''); console.log(''); console.log('==================================================='); // For non-automated installations print the next steps if (isRoot()) { console.log('Write \n' + ' systemctl daemon-reload\n' + ' systemctl enable yunkong2\n' + ' systemctl start yunkong2\n' + '\n' + 'to enable auto-start and to start yunkong2'); } else { console.log('Write \n' + ' sudo systemctl daemon-reload\n' + ' sudo systemctl enable yunkong2\n' + ' sudo systemctl start yunkong2\n' + '\n' + 'to enable auto-start and to start yunkong2'); } console.log('==================================================='); console.log(''); console.log(''); } callback && callback(); }); }); } else if (fs.existsSync('/etc/init.d/')) { // check if /etc/init.d/ exists log('Install init.d script'); let txt = linuxAutoStart; txt = txt.toString().replace(/@@PATH@@/g, controllerDir); txt = txt.toString().replace(/@@HOME@@/g, rootDir); try { if (fs.existsSync(path.join(rootDir, 'install/linux/'))) { log('Create ' + path.join(rootDir, 'install/linux/yunkong2.sh')); fs.writeFileSync(path.join(rootDir, 'install/linux/yunkong2.sh'), txt, { mode: '755' }); fs.chmodSync(path.join(rootDir, 'install/linux/yunkong2.sh'), '755'); } log('Create /etc/init.d/yunkong2.sh'); // copy yunkong2.sh from install/linux to /etc/init.d/ fs.writeFileSync('/etc/init.d/yunkong2.sh', txt, { mode: '755' }); txt = linuxInstallSh; txt = txt.toString().replace(/@@PATH@@/g, controllerDir); log('Create ' + path.join(rootDir, 'install.sh')); fs.writeFileSync(path.join(rootDir, 'install.sh'), txt, { mode: '755' }); fs.chmodSync(path.join(rootDir, 'install.sh'), '755'); } catch (err) { console.error('Cannot copy file to /etc/init.d/yunkong2.sh: ' + err); console.log(''); console.log('-----------------------------------------------------'); console.log('You can manually copy file and install autostart: '); console.log(' sudo cp ' + path.join(rootDir, 'install/linux/yunkong2.sh') + ' /etc/init.d/'); console.log(' sudo chmod 755 /etc/init.d/yunkong2.sh'); console.log(' sudo bash ' + path.join(rootDir, 'install/linux/install.sh')); console.log('-----------------------------------------------------'); console.log(' or just start "sudo bash ' + path.join(rootDir, 'install.sh') + '"'); console.log('-----------------------------------------------------'); callback && callback(); } // js-controller installed as npm let child; // call //echo "Set permissions..." //find /opt/yunkong2/ -type d -exec chmod 755 {} \; //find /opt/yunkong2/ -type f -exec chmod 755 {} \; //chown -R $IO_USER:$IO_USER /opt/yunkong2/ //chmod 755 /etc/init.d/yunkong2.sh //#Replace user pi with current user //sed -i -e "s/yunkong2USER=.*/yunkong2USER=$IO_USER/" /etc/init.d/yunkong2.sh //chown root:root /etc/init.d/yunkong2.sh //update-rc.d /etc/init.d/yunkong2.sh defaults log('Execute ' + path.normalize(path.join(rootDir, 'install.sh'))); log(txt); if (gIsSudo) { child = exec('sudo bash ' + path.normalize(path.join(rootDir, 'install.sh'))); } else { child = exec('bash ' + path.normalize(path.join(rootDir, 'install.sh'))); } child.stderr.pipe(process.stdout); child.on('exit', errCode => { log('Exit code: ' + errCode); console.log('Auto-start was enabled. Write "update-rc.d -f yunkong2.sh remove" to disable auto-start'); console.log('yunkong2 is started. Go to "http://ip-addr:8081" to open the admin UI.'); callback && callback(); }); } } const gIsSudo = false; /** * Installs the core yunkong2 packages * @param {function} callback */ function setup(callback) { let config; const platform = require('os').platform(); // We no longer create package.json and delete package-lock.json here // When the installation routine is run correctly, these will be in sync // We also no longer install the adapters manually, npm does that for us. log('All packages installed. Execute install.'); if (!fs.existsSync(path.join(rootDir, 'yunkong2-data', 'yunkong2.json'))) { if (fs.existsSync(path.join(controllerDir, 'conf', 'yunkong2-dist.json'))) { log('Create yunkong2.json'); config = require(path.join(controllerDir, 'conf', 'yunkong2-dist.json')); console.log('creating conf/yunkong2.json'); config.objects.host = yargs.argv.objects || '127.0.0.1'; config.states.host = yargs.argv.states || '127.0.0.1'; config.dataDir = tools.getDefaultDataDir(); // Create default data dir fs.ensureDirSync(config.dataDir); fs.writeFileSync(tools.getConfigFileName(), JSON.stringify(config, null, 2)); } else { console.log('Could not find "' + controllerDir + '/conf/yunkong2-dist.json". Possible yunkong2.js-controller was not installed'); } } if (fs.existsSync('/etc/init.d/')) { linuxAutoStart = fs.readFileSync(path.join(rootDir, 'install/linux/yunkong2.sh')); linuxInstallSh = fs.readFileSync(path.join(rootDir, 'install/linux/install.sh')); } else if (fs.existsSync('/usr/local/etc/rc.d')) { linuxAutoStart = fs.readFileSync(path.join(rootDir, 'install/freebsd/yunkong2')); linuxInstallSh = fs.readFileSync(path.join(rootDir, 'install/freebsd/install.sh')); } else if (process.platform.startsWith('win')) { // Files are being copied inside setupWindows } try { // Create yunkong2.sh and bat if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir); } if (platform === 'linux' || platform === 'darwin') { setupLinux(callback); } else if (platform.match(/^win/)) { setupWindows(callback); } else if (platform === 'freebsd') { setupFreeBSD(callback); } else { console.warn('Unknown platform so autostart is not enabled'); callback && callback(); } } catch (e) { console.log('Non-critical error: ' + e.message); callback && callback(); } } /* function setChmod(callback) { const platform = require('os').platform(); console.log('Host "' + require('os').hostname() + '" (' + platform + ') updated'); // Call command chmod +x __dirname if under linux or darwin if (platform === 'linux' || platform === 'darwin') { let dir = __dirname.replace(/\\/g, '/'); // remove last /lib' const parts = dir.split('/'); parts.pop(); dir = parts.join('/'); const cmd = 'chmod -R 755 ' + dir; console.log('Execute: ' + cmd); const child = exec(cmd); child.stderr.pipe(process.stdout); child.on('exit', () => { console.log('Chmod finished. Restart controller'); if (callback) callback(); }); } else { if (callback) callback(); } }*/ setup(processExit);