update to npm launcher

This commit is contained in:
AJ ONeal 2018-06-27 21:35:14 -06:00
parent 5eb4cdefb7
commit 172eaea877
2 changed files with 253 additions and 114 deletions

View File

@ -8,7 +8,7 @@ var os = require('os');
//var url = require('url'); //var url = require('url');
var path = require('path'); var path = require('path');
var http = require('http'); var http = require('http');
var https = require('https'); //var https = require('https');
var YAML = require('js-yaml'); var YAML = require('js-yaml');
var recase = require('recase').create({}); var recase = require('recase').create({});
var camelCopy = recase.camelCopy.bind(recase); var camelCopy = recase.camelCopy.bind(recase);
@ -335,7 +335,72 @@ function askForConfig(answers, mainCb) {
} }
var utils = { var utils = {
putConfig: function putConfig(service, args, fn) { request: function request(opts, fn) {
if (!opts) { opts = {}; }
var service = opts.service || 'config';
var req = http.get({
socketPath: state._ipc.path
, method: opts.method || 'GET'
, path: '/rpc/' + service
}, function (resp) {
var body = '';
function finish() {
console.info("");
if (200 !== resp.statusCode) {
console.warn(resp.statusCode);
console.warn(body || ('get' + service + ' failed'));
//cb(new Error("not okay"), body);
return;
}
if (!body) { fn(null, null); return; }
try {
body = JSON.parse(body);
} catch(e) {
// ignore
}
fn(null, body);
}
if (resp.headers['content-length']) {
resp.on('data', function (chunk) {
body += chunk.toString();
});
resp.on('end', function () {
finish();
});
} else {
finish();
}
});
req.on('error', function (err) {
// ENOENT - never started, cleanly exited last start, or creating socket at a different path
// ECONNREFUSED - leftover socket just needs to be restarted
if ('ENOENT' === err.code || 'ECONNREFUSED' === err.code) {
if (opts._taketwo) {
console.error("Either the telebit service was not already (and could not be started) or its socket could not be written to.");
console.error(err);
return;
}
require('../usr/share/install-launcher.js').install({ env: process.env }, function (err) {
if (err) { fn(err); return; }
opts._taketwo = true;
utils.request(opts, fn);
});
return;
}
if ('ENOTSOCK' === err.code) {
console.error(err);
return;
}
console.error(err);
return;
});
}
, putConfig: function putConfig(service, args, fn) {
// console.log('got it', service, args); // console.log('got it', service, args);
var req = http.get({ var req = http.get({
socketPath: state._ipc.path socketPath: state._ipc.path
@ -516,6 +581,10 @@ var parsers = {
throw new Error("init must be the first argument"); throw new Error("init must be the first argument");
} }
argv.shift(); argv.shift();
utils.request({ service: 'config' }, function (err/*, body*/) {
if (err) { cb(err); return; }
// init --foo bar // init --foo bar
argv.forEach(function (arg, i) { argv.forEach(function (arg, i) {
if (!/^--/.test(arg)) { return; } if (!/^--/.test(arg)) { return; }
@ -564,6 +633,7 @@ var parsers = {
cb(null, answers); cb(null, answers);
}); });
});
} }
}; };

View File

@ -6,7 +6,9 @@ var mkdirp = require('mkdirp');
var exec = require('child_process').exec; var exec = require('child_process').exec;
var path = require('path'); var path = require('path');
module.exports.install = function (things) { var Launcher = module.exports;
Launcher.install = function (things, fn) {
if (!fn) { fn = function (err) { if (err) { console.error(err); } }; }
things = things || {}; things = things || {};
// in some future version we can take this file out // in some future version we can take this file out
// and accept process.env from things // and accept process.env from things
@ -15,11 +17,12 @@ module.exports.install = function (things) {
// Right now this is just for npm install -g and npx // Right now this is just for npm install -g and npx
if (things.env) { if (things.env) {
things.env.PATH = things.env.PATH || process.env.PATH; things.env.PATH = things.env.PATH || process.env.PATH;
} else {
things.env = process.env;
} }
var execOpts = { windowsHide: true, env: things.env || process.env }; things.argv = things.argv || process.argv;
var userspace = (!things.telebitUser || (things.telebitUser === os.userInfo().username)) ? true : false; things._execOpts = { windowsHide: true, env: things.env };
var telebitRoot = path.join(__dirname, '../..'); var telebitRoot = path.join(__dirname, '../..');
var telebitBinTpl = path.join(telebitRoot, 'usr/share/dist/bin/telebit.tpl');
var vars = { var vars = {
telebitPath: telebitRoot telebitPath: telebitRoot
, telebitUser: os.userInfo().username , telebitUser: os.userInfo().username
@ -29,7 +32,7 @@ module.exports.install = function (things) {
, path.join(os.homedir(), '.config/telebit') , path.join(os.homedir(), '.config/telebit')
, path.join(os.homedir(), '.local/share/telebit') , path.join(os.homedir(), '.local/share/telebit')
] ]
, telebitNode: (process.argv[0]||'').replace(/\.exe/i, '') // path.join(telebitRoot, 'bin/node') , telebitNode: (things.argv[0]||'').replace(/\.exe/i, '') // path.join(telebitRoot, 'bin/node')
, telebitBin: path.join(telebitRoot, 'bin/telebit') , telebitBin: path.join(telebitRoot, 'bin/telebit')
, telebitdBin: path.join(telebitRoot, 'bin/telebitd') , telebitdBin: path.join(telebitRoot, 'bin/telebitd')
, telebitJs: path.join(telebitRoot, 'bin/telebit.js') , telebitJs: path.join(telebitRoot, 'bin/telebit.js')
@ -37,53 +40,102 @@ module.exports.install = function (things) {
, telebitConfig: path.join(os.homedir(), '.config/telebit/telebit.yml') , telebitConfig: path.join(os.homedir(), '.config/telebit/telebit.yml')
, telebitdConfig: path.join(os.homedir(), '.config/telebit/telebitd.yml') , telebitdConfig: path.join(os.homedir(), '.config/telebit/telebitd.yml')
}; };
vars.telebitBinTpl = path.join(telebitRoot, 'usr/share/dist/bin/telebit.tpl');
vars.telebitNpm = path.resolve(vars.telebitNode, '../npm'); vars.telebitNpm = path.resolve(vars.telebitNode, '../npm');
vars.nodePath = path.resolve(vars.telebitNode, '../lib/node_modules'); vars.nodePath = path.resolve(vars.telebitNode, '../lib/node_modules');
vars.npmConfigPrefix = path.resolve(vars.telebitNode, '..'); vars.npmConfigPrefix = path.resolve(vars.telebitNode, '..');
vars.userspace = (!things.telebitUser || (things.telebitUser === os.userInfo().username)) ? true : false;
if (-1 === vars.telebitRwDirs.indexOf(vars.npmConfigPrefix)) { if (-1 === vars.telebitRwDirs.indexOf(vars.npmConfigPrefix)) {
vars.telebitRwDirs.push(vars.npmConfigPrefix); vars.telebitRwDirs.push(vars.npmConfigPrefix);
} }
vars.telebitRwDirs = vars.telebitRwDirs.join(' '); vars.telebitRwDirs = vars.telebitRwDirs.join(' ');
function getError(err, stderr) {
if (err) { return err; }
if (stderr) {
err = new Error(stderr);
err.code = 'ELAUNCHER';
return err;
}
}
var launchers = { var launchers = {
'launchctl': function () { 'node': function () {
var fs = require('fs');
var spawn = require('child_process').spawn;
var logpath = path.join(os.homedir(), '.local/share/telebit/var/log');
try {
mkdirp.sync(logpath);
} catch(e) {
if (fn) { fn(e); return; }
return;
}
var stdout = fs.openSync(path.join(logpath, 'info.log'), 'a');
var stderr = fs.openSync(path.join(logpath, 'error.log'), 'a');
var killed = 0;
var err;
var subprocess = spawn(
vars.telebitNode
, [ path.join(__dirname, '../../bin/telebitd.js')
, 'daemon'
, '--config'
, vars.telebitdConfig
]
, { detached: true
, stdio: [ 'ignore', stdout, stderr ]
}
);
subprocess.unref();
subprocess.on('error', function (_err) {
err = _err;
killed += 1;
});
subprocess.on('exit', function (code, signal) {
if (!err) { err = new Error('' + code + ' ' + signal + ' failure to launch'); }
killed += 1;
});
setTimeout(function () {
if (fn) { fn(null); return; }
}, 1 * 1000);
return;
}
, 'launchctl': function () {
var launcher = path.join(os.homedir(), 'Library/LaunchAgents/cloud.telebit.remote.plist'); var launcher = path.join(os.homedir(), 'Library/LaunchAgents/cloud.telebit.remote.plist');
try { try {
mkdirp.sync(path.join(os.homedir(), 'Library/LaunchAgents')); mkdirp.sync(path.join(os.homedir(), 'Library/LaunchAgents'));
mkdirp.sync(path.join(telebitRoot, 'bin')); mkdirp.sync(path.join(vars.telebitPath, 'bin'));
installLauncher.sync({ installLauncher.sync({
file: { file: {
tpl: telebitBinTpl tpl: vars.telebitBinTpl
, launcher: path.join(telebitRoot, 'bin/telebit') , launcher: path.join(vars.telebitPath, 'bin/telebit')
} }
, vars: vars , vars: vars
}); });
installLauncher({ installLauncher({
file: { file: {
tpl: path.join(telebitRoot, 'usr/share/dist/etc/skel/Library/LaunchAgents/cloud.telebit.remote.plist.tpl') tpl: path.join(vars.telebitPath, 'usr/share/dist/etc/skel/Library/LaunchAgents/cloud.telebit.remote.plist.tpl')
, launcher: launcher , launcher: launcher
} }
, vars: vars , vars: vars
}); });
var launcherstr = (userspace ? "" : "sudo ") + "launchctl "; var launcherstr = (vars.userspace ? "" : "sudo ") + "launchctl ";
exec(launcherstr + "unload -w " + launcher, execOpts, function (err, stdout, stderr) { exec(launcherstr + "unload -w " + launcher, things._execOpts, function (err, stdout, stderr) {
if (err) { console.error(err); } err = getError(err, stderr);
console.log((stdout||'').trim()); if (err) { fn(err); return; }
if (stderr) { //console.log((stdout||'').trim());
console.error(stderr); //console.log('unload worked?');
} exec(launcherstr + "load -w " + launcher, things._execOpts, function (err, stdout, stderr) {
console.log('unload worked?'); err = getError(err, stderr);
exec(launcherstr + "load -w " + launcher, execOpts, function (err, stdout, stderr) { if (err) { fn(err); return; }
if (err) { console.error(err); } //console.log((stdout||'').trim());
console.log((stdout||'').trim()); //console.log('load worked?');
if (stderr) { fn(null);
console.error(stderr);
}
console.log('load worked?');
}); });
}); });
} catch(e) { } catch(e) {
console.error("'" + launcher + "' error:"); console.error("'" + launcher + "' error:");
console.error(e); console.error(e);
if (fn) { fn(e); return; }
} }
} }
, 'systemctl': function () { , 'systemctl': function () {
@ -92,24 +144,25 @@ module.exports.install = function (things) {
mkdirp.sync(path.join(os.homedir(), '.config/systemd/user')); mkdirp.sync(path.join(os.homedir(), '.config/systemd/user'));
installLauncher({ installLauncher({
file: { file: {
tpl: path.join(telebitRoot, 'usr/share/dist/etc/skel/.config/systemd/user/telebit.service.tpl') tpl: path.join(vars.telebitPath, 'usr/share/dist/etc/skel/.config/systemd/user/telebit.service.tpl')
, launcher: launcher , launcher: launcher
} }
, vars: vars , vars: vars
}, function () { }, function () {
var launcherstr = (userspace ? "" : "sudo ") + "systemctl " + (userspace ? "--user " : ""); var launcherstr = (vars.userspace ? "" : "sudo ") + "systemctl " + (vars.userspace ? "--user " : "");
exec(launcherstr + "daemon-reload", execOpts, function (err, stdout, stderr) { exec(launcherstr + "daemon-reload", things._execOpts, function (err, stdout, stderr) {
if (err) { console.error(err); } err = getError(err, stderr);
console.log((stdout||'').trim()); if (err) { fn(err); return; }
if (stderr) { console.error(stderr); } //console.log((stdout||'').trim());
exec(launcherstr + "enable " + launcher, execOpts, function (err, stdout, stderr) { exec(launcherstr + "enable " + launcher, things._execOpts, function (err, stdout, stderr) {
if (err) { console.error(err); } err = getError(err, stderr);
console.log((stdout||'').trim()); if (err) { fn(err); return; }
if (stderr) { console.error(stderr); } //console.log((stdout||'').trim());
exec(launcherstr + "restart " + launcher, execOpts, function (err, stdout, stderr) { exec(launcherstr + "restart " + launcher, things._execOpts, function (err, stdout, stderr) {
if (err) { console.error(err); } err = getError(err, stderr);
console.log((stdout||'').trim()); if (err) { fn(err); return; }
if (stderr) { console.error(stderr); } //console.log((stdout||'').trim());
fn(null);
}); });
}); });
}); });
@ -117,13 +170,17 @@ module.exports.install = function (things) {
} catch(e) { } catch(e) {
console.error("'" + launcher + "' error:"); console.error("'" + launcher + "' error:");
console.error(e); console.error(e);
if (fn) { fn(e); return; }
} }
} }
, 'reg.exe': function () { , 'reg.exe': function () {
if (!vars.userspace) {
console.warn("sysetm-level, privileged services are not yet supported on windows");
}
vars.telebitNode += '.exe'; vars.telebitNode += '.exe';
var cmd = 'reg.exe add "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"' var cmd = 'reg.exe add "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"'
+ ' /V "Telebit" /t REG_SZ /D ' + ' /V "Telebit" /t REG_SZ /D '
+ '"' + process.argv[0] + ' /c ' // something like C:\\Program Files (x64)\nodejs\node.exe + '"' + things.argv[0] + ' /c ' // something like C:\\Program Files (x64)\nodejs\node.exe
+ [ path.join(__dirname, 'bin/telebitd.js') + [ path.join(__dirname, 'bin/telebitd.js')
, 'daemon' , 'daemon'
, '--config' , '--config'
@ -131,25 +188,24 @@ module.exports.install = function (things) {
].join(' ') ].join(' ')
+ '" /F' + '" /F'
; ;
exec(cmd, execOpts, function (err, stdout, stderr) { exec(cmd, things._execOpts, function (err, stdout, stderr) {
console.log((stdout||'').trim()); err = getError(err, stderr);
if (stderr) { if (err) { fn(err); return; }
console.error(stderr); //console.log((stdout||'').trim());
} fn(null);
run(err, 'launchctl');
}); });
} }
}; };
function run(err, launcher) { function run(err, launcher) {
if (err) { if (err) {
console.error("No luck with '" + launcher + "'"); console.error("No luck with '" + launcher + "', trying a child process instead...");
console.error(err); console.error(err);
return; launcher = 'node';
} }
if (launchers[launcher]) { if (launchers[launcher]) {
console.log('Launching with launcher ' + launcher); // console.log('Launching with launcher ' + launcher);
launchers[launcher](); launchers[launcher]();
return; return;
} else { } else {
@ -157,23 +213,30 @@ module.exports.install = function (things) {
} }
} }
if (things.launcher) {
if ('string' === typeof things.launcher) {
run(null, things.launcher);
return;
}
if ('function' === typeof things.launcher) {
things._vars = vars;
things._userspace = vars.userspace;
things.launcher(things);
return;
}
}
// could have used "command-exists" but I'm trying to stay low-dependency // could have used "command-exists" but I'm trying to stay low-dependency
// os.platform(), os.type() // os.platform(), os.type()
if (!/^win/i.test(os.platform())) { if (!/^win/i.test(os.platform())) {
if (/^darwin/i.test(os.platform())) { if (/^darwin/i.test(os.platform())) {
exec('type -p launchctl', execOpts, function (err, stdout, stderr) { exec('type -p launchctl', things._execOpts, function (err, stdout, stderr) {
console.log((stdout||'').trim()); err = getError(err, stderr);
if (stderr) {
console.error(stderr);
}
run(err, 'launchctl'); run(err, 'launchctl');
}); });
} else { } else {
exec('type -p systemctl', execOpts, function (err, stdout, stderr) { exec('type -p systemctl', things._execOpts, function (err, stdout, stderr) {
console.log((stdout||'').trim()); err = getError(err, stderr);
if (stderr) {
console.error(stderr);
}
run(err, 'systemctl'); run(err, 'systemctl');
}); });
} }
@ -186,7 +249,7 @@ module.exports.install = function (things) {
// https://social.msdn.microsoft.com/Forums/en-US/5b318f44-281e-4098-8dee-3ba8435fa391/add-registry-key-for-autostart-of-app-in-ice?forum=quebectools // https://social.msdn.microsoft.com/Forums/en-US/5b318f44-281e-4098-8dee-3ba8435fa391/add-registry-key-for-autostart-of-app-in-ice?forum=quebectools
// utils.elevate // utils.elevate
// https://github.com/CatalystCode/windows-registry-node // https://github.com/CatalystCode/windows-registry-node
exec('where reg.exe', execOpts, function (err, stdout, stderr) { exec('where reg.exe', things._execOpts, function (err, stdout, stderr) {
console.log((stdout||'').trim()); console.log((stdout||'').trim());
if (stderr) { if (stderr) {
console.error(stderr); console.error(stderr);
@ -197,5 +260,11 @@ module.exports.install = function (things) {
}; };
if (module === require.main) { if (module === require.main) {
module.exports.install({}); module.exports.install({
argv: process.argv
, env: process.env
}, function (err) {
if (err) { console.error(err); return; }
console.log("Telebit launched, or so it seems.");
});
} }