diff --git a/app/modules/daemon.js b/app/modules/daemon.js index f572511ddd174bed8afea900cf03c69c9d089891..d70ede03fdef0307bbeff41b77d5a7b02dacd7f0 100644 --- a/app/modules/daemon.js +++ b/app/modules/daemon.js @@ -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() + })) +} } diff --git a/bin/daemon b/bin/daemon deleted file mode 100755 index ad2748e694a240592b182c4218b5dda0783ebd18..0000000000000000000000000000000000000000 --- a/bin/daemon +++ /dev/null @@ -1,119 +0,0 @@ -#!/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(); - } - }); -} diff --git a/duniter.sh b/duniter.sh index c3f7f993eaac164d2cbe4ae756d355cbaa5165e6..4e71d991c302b654455ae2d5bf2da94ac391662a 100755 --- a/duniter.sh +++ b/duniter.sh @@ -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; } diff --git a/server.js b/server.js index 2f505d54de53ed66b2dc2f5659ceebdb6653a98f..dd69d2b19ce99ce96305597f4ba30595bf03564d 100644 --- a/server.js +++ b/server.js @@ -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). */