From 182e532052dfea1e10733f67a19449de0c4d0f6a Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Wed, 29 Jan 2020 11:41:44 +0000 Subject: [PATCH] feat: force kill daemons after timeout (#441) This PR re-adds a feature that was removed in 1.0.0 whereby daemons are force killed with a SIGKILL when they do not stop within a timeout. It adds 2 new options to the `Daemon` class - `forceKill` (boolean - default `true`) and `forceKillTimeout` (number - default 5000). closes #438 Co-authored-by: Alex Potsides --- README.md | 2 ++ src/factory.js | 4 +++- src/index.js | 2 ++ src/ipfsd-daemon.js | 22 +++++++++++++++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 231aa250..5f1b2761 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,8 @@ Type: [Object] - `ipfsModule.path` **[string]** Path to a IPFS API implementation to be required. (defaults to the local require.resolve('ipfs')) - `ipfsBin` **[string]** Path to a IPFS exectutable . (defaults to the local 'js-ipfs/src/bin/cli.js') - `ipfsOptions` **[IpfsOptions]** Options for the IPFS instance same as https://github.com/ipfs/js-ipfs#ipfs-constructor. `proc` nodes receive these options as is, daemon nodes translate the options as far as possible to cli arguments. +- `forceKill` **[boolean]** - Whether to use SIGKILL to quit a daemon that does not stop after `.stop()` is called. (default `true`) +- `forceKillTimeout` **[Number]** - How long to wait before force killing a daemon in ms. (default `5000`) ## ipfsd-ctl environment variables diff --git a/src/factory.js b/src/factory.js index 4a16b7b7..fa79d926 100644 --- a/src/factory.js +++ b/src/factory.js @@ -25,7 +25,9 @@ const defaults = { path: require.resolve('ipfs-http-client'), ref: require('ipfs-http-client') }, - ipfsOptions: {} + ipfsOptions: {}, + forceKill: true, + forceKillTimeout: 5000 } /** diff --git a/src/index.js b/src/index.js index 18e63bad..0103a7b4 100644 --- a/src/index.js +++ b/src/index.js @@ -85,6 +85,8 @@ module.exports = { * @property {string} [ipfsModule.path] - Path to a IPFS API implementation to be required. (defaults to the local require.resolve('ipfs')) * @property {String} [ipfsBin] - Path to a IPFS exectutable . (defaults to the local 'js-ipfs/src/bin/cli.js') * @property {IpfsOptions} [ipfsOptions] - Options for the IPFS node. + * @property {boolean} [forceKill] - Whether to use SIGKILL to quit a daemon that does not stop after `.stop()` is called. (default true) + * @property {Number} [forceKillTimeout] - How long to wait before force killing a daemon in ms. (default 5000) */ /** diff --git a/src/ipfsd-daemon.js b/src/ipfsd-daemon.js index 13fc3340..12c17790 100644 --- a/src/ipfsd-daemon.js +++ b/src/ipfsd-daemon.js @@ -239,7 +239,27 @@ class Daemon { return this } - await this.api.stop() + let killTimeout + let killed = false + if (this.opts.forceKill !== false) { + killTimeout = setTimeout(() => { + // eslint-disable-next-line no-console + console.error(new Error(`Timeout stopping ${this.opts.type} node. Process ${this.subprocess.pid} will be force killed now.`)) + killed = true + + this.subprocess.kill('SIGKILL') + }, this.opts.forceKillTimeout) + } + + try { + await this.api.stop() + } catch (err) { + if (!killed) throw err // if was killed then ignore error + + daemonLog.info('Daemon was force killed') + } + + clearTimeout(killTimeout) this.subprocess.stderr.removeAllListeners() this.subprocess.stdout.removeAllListeners() this.started = false