yunkong2
Version:
automate your life - platfom
534 lines (475 loc) • 22.3 kB
JavaScript
/**
*
* yunkong2 installer from npm
*
* Copyright 1'2015-2018 bluefox <dogafox@gmail.com>
*
*
*/
/* jshint -W097 */// jshint strict:false
/*jslint node: true */
;
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);