Skip to content
Snippets Groups Projects
Commit 77c8ff85 authored by Cédric Moreau's avatar Cédric Moreau
Browse files

[enh] #940 Daemonizer is now a module

parent a426fad3
No related branches found
No related tags found
No related merge requests found
......@@ -10,8 +10,54 @@ module.exports = {
},
cli: [{
name: 'start',
desc: 'Start Duniter node daemon.',
desc: 'Starts Duniter as a daemon (background task).',
logs: false,
onConfiguredExecute: (server, conf, program, params) => co(function*() {
yield server.checkConfig()
const daemon = server.getDaemon('direct_start', 'start')
yield startDaemon(daemon)
})
}, {
name: 'stop',
desc: 'Stops Duniter daemon if it is running.',
logs: false,
onConfiguredExecute: (server, conf, program, params) => co(function*() {
const daemon = server.getDaemon()
yield stopDaemon(daemon)
})
}, {
name: 'restart',
desc: 'Stops Duniter daemon and restart it.',
logs: false,
onConfiguredExecute: (server, conf, program, params) => co(function*() {
yield server.checkConfig()
const daemon = server.getDaemon('direct_start', 'restart')
yield stopDaemon(daemon)
yield startDaemon(daemon)
})
}, {
name: 'status',
desc: 'Get Duniter daemon status.',
logs: false,
onConfiguredExecute: (server, conf, program, params) => co(function*() {
yield server.checkConfig()
const pid = server.getDaemon().status()
if (pid) {
console.log('Duniter is running using PID %s.', pid)
} else {
console.log('Duniter is not running.')
}
})
}, {
}, {
name: 'direct_start',
desc: 'Start Duniter node with direct output, non-daemonized.',
onDatabaseExecute: (server, conf, program, params, startServices) => co(function*() {
const logger = server.logger;
......@@ -29,31 +75,22 @@ module.exports = {
return new Promise(() => null); // Never ending
})
},{
name: 'status',
desc: 'Show Duniter node daemon status.',
logs: false,
onConfiguredExecute: (server) => needsToBeLaunchedByScript(server.logger)
},{
name: 'stop',
desc: 'Stop Duniter node daemon.',
logs: false,
onConfiguredExecute: (server) => needsToBeLaunchedByScript(server.logger)
},{
name: 'restart',
desc: 'Restart Duniter node daemon.',
logs: false,
onConfiguredExecute: (server) => needsToBeLaunchedByScript(server.logger)
}]
}
};
function ServerService(server) {
server.startService = () => Promise.resolve();
server.stopService = () => Promise.resolve();
return server;
function startDaemon(daemon) {
return new Promise((resolve, reject) => daemon.start((err) => {
if (err) return reject(err)
resolve()
}))
}
function needsToBeLaunchedByScript(logger) {
logger.error('This command must not be launched directly, please use duniter.sh script');
function stopDaemon(daemon) {
return new Promise((resolve, reject) => daemon.stop((err) => {
err && console.error(err);
if (err) return reject(err)
resolve()
}))
}
}
#!/usr/bin/env node
"use strict";
const directory = require('../app/lib/system/directory');
const path = require('path');
const spawn = require('child_process').spawn;
var daemon = getDaemon('start');
switch (process.argv[2]) {
case "start":
start(daemon);
break;
case "stop":
daemon.stop();
break;
case "status":
if (daemon.status()) {
console.log('Duniter daemon status: running');
process.exit(0)
} else {
console.log('Duniter daemon status: stopped');
process.exit(2)
}
case "sync":
case "reset":
const command = process.argv[2];
if (daemon.status()) {
console.log('Stopping daemon...');
daemon.stop(function() {
launchCmd(command);
});
} else {
launchCmd(command);
}
break;
case "restart":
daemon = getDaemon('start');
daemon.stop(function(err) {
err && console.error(err);
start(daemon);
});
break;
case "webstart":
daemon = getDaemon('webstart');
start(daemon);
break;
case "webrestart":
daemon = getDaemon('webstart');
daemon.stop(function(err) {
err && console.error(err);
start(daemon);
});
break;
case "logs":
console.log(directory.INSTANCE_HOMELOG_FILE);
process.exit(0);
break;
default:
console.log("Usage: [start|stop|restart|webstart|webrestart]");
}
function getDaemon(overrideCommand) {
return require("daemonize2").setup({
main: "duniter",
name: directory.INSTANCE_NAME,
pidfile: path.join(directory.INSTANCE_HOME, "app.pid"),
// We must redefine the main argument to 'start' because Duniter will receive it as command argument and does not
// know about 'restart' command.
argv: getCommand(overrideCommand)
});
}
function getCommand(overrideCommand) {
return process.argv.slice(2).map((arg, index) => index == 0 && overrideCommand ? overrideCommand : arg);
}
function getFullCommand(overrideCommand) {
let duniter = path.resolve(path.dirname(process.argv[1]), './duniter');
return [duniter].concat(getCommand(overrideCommand));
}
function launchCmd(cmd) {
let checkConf = spawn(process.argv[0], getFullCommand(cmd));
// Error messages
checkConf.stdout.on('data', (data) => console.log(data.toString('utf8')));
checkConf.stderr.on('data', (data) => console.error(data.toString('utf8')));
}
function start(daemonToStart) {
let checkConf = spawn(process.argv[0], getFullCommand('check-config'));
let echos = [];
// Error messages
checkConf.stdout.on('data', (data) => echos.push(data));
// checkConf.stderr.on('data', (data) => console.error(data.toString('utf8')));
// Result
checkConf.on('close', (code) => {
if (code !== 0 && code !== '0' && code !== '' && code !== null && code !== undefined) {
console.log('Error code \'%s\'', code);
echos.forEach((echo) => console.log(echo.toString('utf8').replace(/\n$/, '')));
console.log('You have configuration issues. Please fix them and retry to start your node with `duniter restart` or `duniter webrestart`.');
} else {
daemonToStart.start();
}
});
}
......@@ -3,7 +3,7 @@
##########################
# DUNITER EXECUTABLE
#
# Wraps bin/duniter.js that is called with Node.js
# Wraps bin/duniter that is called with Node.js
#
DEB_PACKAGING=
......@@ -35,38 +35,13 @@ duniter() {
VERSION=`$NODE -v`
if [[ $VERSION != v6* && $VERSION != v5* && $VERSION != v4* ]]; then
echo "$NODE v5 or v4 is required";
if [[ $VERSION != v6* ]]; then
echo "$NODE v6 is required";
else
case "$1" in
#---------------------------------
# DUNITER DAEMON MANAGEMENT
#---------------------------------
reset|start|stop|status|restart|webstart|webrestart)
$NODE "$DUNITER_DIR/bin/daemon" $*
;;
direct_start)
$NODE "$DUNITER_DIR/bin/duniter" start ${@:2}
;;
logs)
LOGS_FILE=`$NODE "$DUNITER_DIR/bin/daemon" $*`
tail -f -n 500 "$LOGS_FILE"
;;
#---------------------------------
# DUNITER CLI COMMANDS
#---------------------------------
*)
# Calls duniter JS command
$NODE "$DUNITER_DIR/bin/duniter" "$@"
;;
esac
fi;
}
......
......@@ -8,6 +8,7 @@ const Q = require('q');
const archiver = require('archiver');
const unzip = require('unzip2');
const fs = require('fs');
const daemonize = require("daemonize2")
const parsers = require('./app/lib/streams/parsers');
const constants = require('./app/lib/constants');
const fileDAL = require('./app/lib/dal/fileDAL');
......@@ -366,12 +367,73 @@ function Server (home, memoryOnly, overrideConf) {
return yield that.singleWritePromise(obj);
});
/*****************
* DAEMONIZATION
****************/
/**
* Get the daemon handle. Eventually give arguments to launch a new daemon.
* @param overrideCommand The new command to launch.
* @param insteadOfCmd The current command to be replaced by `overrideCommand` command.
* @returns {*} The daemon handle.
*/
this.getDaemon = function getDaemon(overrideCommand, insteadOfCmd) {
const mainModule = process.argv[1]
const argv = getCommand(overrideCommand, insteadOfCmd)
return daemonize.setup({
main: mainModule,
name: directory.INSTANCE_NAME,
pidfile: path.join(directory.INSTANCE_HOME, "app.pid"),
argv
});
}
/**
* Return current script full command arguments except the two firsts (which are node executable + js file).
* If the two optional `cmd` and `insteadOfCmd` parameters are given, replace `insteadOfCmd`'s value by `cmd` in
* the script arguments.
*
* Ex:
* * process.argv: ['/usr/bin/node', '/opt/duniter/sources/bin/duniter', 'restart', '--mdb', 'g1']
*
* Then `getCommand('direct_start', 'restart') will return:
*
* * ['direct_start', '--mdb', 'g1']
*
* This new array is what will be given to a *fork* of current script, resulting in a new process with:
*
* * process.argv: ['/usr/bin/node', '/opt/duniter/sources/bin/duniter', 'direct_start', '--mdb', 'g1']
*
* @param cmd
* @param insteadOfCmd
* @returns {*}
*/
function getCommand(cmd, insteadOfCmd) {
if (insteadOfCmd) {
// Return the same command args, except the command `insteadOfCmd` which is replaced by `cmd`
return process.argv.slice(2).map((arg) => {
if (arg == insteadOfCmd) {
return cmd
} else {
return arg
}
})
} else {
// Return the exact same args (generally for stop/status commands)
return process.argv.slice(2)
}
}
/**
* Retrieve the last linesQuantity lines from the log file.
* @param linesQuantity
*/
this.getLastLogLines = (linesQuantity) => this.dal.getLogContent(linesQuantity);
/*****************
* MODULES PLUGS
****************/
/**
* Default endpoint. To be overriden by a module to specify another endpoint value (for ex. BMA).
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment