Kill detached child process in Node - node.js

I'm not fully understand why I can't kill a detached process. Can someone help me out?
Server (child process)
const server = spawn(
'npm',
[
'run',
'watch:be',
],
{
detached: true,
},
);
Await for the server to up and running
await waitOn({
resources: [
`https://localhost:${process.env.SERVER_PORT}`,
],
delay: 1000,
timeout: 30000,
});
console.log('server is up and running');
Wait a couple more seconds
await new Promise((resolve, reject): void => {
setTimeout((): void => {
resolve();
}, 2000);
});
console.log('Run test');
Kill the child server
server.kill();
console.log('Shutdown server');
All of these are in the same file.
The child process opened a new terminal window (when it does spawn, which is expected), but doesn't close when kill. Can someone point out what I have done wrong?

server.kill();
As per the node.js documentation, The subprocess.kill() method sends a signal to the child process. When you use the detached option, the node creates a separate process group for the child process and it is not part of the same process anymore.
detached <boolean>: Prepare child to run independently of its parent process
That is the reason kill is not appropriate to use when detached is used.
This has been discussed here:
https://github.com/nodejs/node/issues/2098
As suggested in the above link, you should use process.kill to kill the process (https://nodejs.org/api/process.html#process_process_kill_pid_signal). This should probably work for you:
process.kill(-server.pid)

You said that "The child process opened a new terminal window ..."
Based on this behaviour, it seems that your OS is Windows.
On Windows, setting options.detached to true makes it possible for the child process to continue running after the parent exits. The child will have its own console window. Once enabled for a child process, it cannot be disabled.
source
For process.kill, the second arg is the signal you want to send. By default, this signal is SIGTERM. However, SIGTERM doesn't seem to be supported on Windows, according to the Signal Events section of the Node.js docs.
'SIGTERM' is not supported on Windows, it can be listened on.
Maybe try
process.kill(server.pid, 'SIGHUP')
or
process.kill(server.pid, 'SIGINT')
(This works on macOS but I've not tried it on Windows.)

Related

Node JS - Cannot Kill Process Executed with Child Process Exec

We are trying to kill the process of a chrome browser launched with nodes child_process exec command
var process = cp.exec(`"chrome.exe" --app="..."`, () => {}); // working great
But when we try
process.kill(); //nothing happens...
Does the process refer to the chrome window or something else? if not, how can we get a hold of the newly opened chrome window process, PID, etc...?
Any help would be great...
Note - we have tried using the chrome_launcher NPM but it didn't help because we couldn't open chrome in kiosk mode without fullscreen, but this is an issue for a different question...
Try the PID hack
We can start child processes with {detached: true} option so those processes will not be attached to main process but they will go to a new group of processes.
Then using process.kill(-pid) method on main process we can kill all processes that are in the same group of a child process with the same pid group. In my case, I only have one processes in this group.
var spawn = require('child_process').spawn;
var child = spawn('your-command', {detached: true});
process.kill(-child.pid);
I built a cross platform npm package that wraps up spawning and killing child processes from node, give it a try.
https://www.npmjs.com/package/subspawn
I am not able to add comment, so I am saying it directly in the answer:
How to kill process with node js
If you check the link above you need library as follows
https://www.npmjs.com/package/fkill
Usage Example taken from stackoverflow question
const fkill = require('fkill');
fkill(1337).then(() => {
console.log('Killed process');
});
fkill('Safari');
fkill([1337, 'Safari']);
I also found this library to check running processes
https://github.com/neekey/ps

node.js - How to spawn a child process without blocking stdin of parent process?

I'm making an interactive CLI in node (and blessed.js) that spawns child processes every couple of seconds to run some Python scripts. These scripts modify a set of JSON files that the CLI pulls from.
The problem is that the CLI must be able to accept input from the user at all times, and when these child processes are spawned, the stdin of the CLI/parent process appears to be blocked and it looks like the Python scripts are executing in the foreground. Here's the code I'm using to run the Python scripts:
const { spawn } = require("child_process");
function execPythonScript(args) {
return spawn("python3", args, { stdio: "ignore" });
}
execPythonScript(["path_to_script.py"]);
I've also tried running the script as a background process, i.e. execPythonScript(["path_to_script.py", "&"]), but to no avail. Any ideas?
Thanks in advance!
UPDATE:
I'm starting to suspect this is an issue with blessed and not child-process, since I've exhausted all relevant methods (and their arguments) for spawning non-blocking background processes, and the problem still persists.
Each blessed instance uses process.stdin for input by default, but I figured it's possible that the stdin stream gets used up by the child processes, even though I'm spawning them with stdio set to "ignore". So I tried using ttys and instantiating blessed.screen to read from the active terminal (/dev/tty) instead of /dev/stdin:
const ttys = require("ttys");
screen = blessed.screen({
input: ttys.stdin, // Instead of process.stdin
smartCSR: true
});
But it's still blocking...
You also need to detach the process:
spawn("python3", args, { stdio: "ignore", detached: true })
This is a useful primer.

How to send "CTRL+C" to child process in Node.js?

I tried to spawn child process - vvp (https://linux.die.net/man/1/vvp). At the certain time, I need to send CTRL+C to that process.
I am expecting that simulation will be interrupted and I get the interactive prompt. And after that I can continue the simulation by send command to the child process.
So, I tried something like this:
var child = require('child_process');
var fs = require('fs');
var vcdGen = child.spawn('vvp', ['qqq'], {});
vcdGen.stdout.on('data', function(data) {
console.log(data.toString())
});
setTimeout(function() {
vcdGen.kill('SIGINT');
}, 400);
In that case, a child process was stopped.
I also tried vcdGen.stdin.write('\x03') instead of vcdGen.kill('SIGINT'); but it isn't work.
Maybe it's because of Windows?
Is there any way to achieve the same behaviour as I got in cmd?
kill only really supports a rude process kill on Windows - the application signal model in Windows and *nix isn't compatible. You can't pass Ctrl+C through standard input, because it never comes through standard input - it's a function of the console subsystem (and thus you can only use it if the process has an attached console). It creates a new thread in the child process to do its work.
There's no supported way to do this programmatically. It's a feature for the user, not the applications. The only way to do this would be to do the same thing the console subsystem does - create a new thread in the target application and let it do the signalling. But the best way would be to simply use coöperative signalling instead - though that of course requires you to change the target application to understand the signal.
If you want to go the entirely unsupported route, have a look at https://stackoverflow.com/a/1179124/3032289.
If you want to find a middle ground, there's a way to send a signal to yourself, of course. Which also means that you can send Ctrl+C to a process if your consoles are attached. Needless to say, this is very tricky - you'd probably want to create a native host process that does nothing but create a console and run the actual program you want to run. Your host process would then listen for an event, and when the event is signalled, call GenerateConsoleCtrlEvent.

child process not calling close event

I have the following node.js code:
var testProcess = spawn(item.testCommand, [], {
cwd: process.cwd(),
stdio: ['ignore', process.stdout, process.stderr]
});
testProcess.on('close', function(data) {
console.log('test');
});
waitpid(testProcess.pid);
testProcess.kill();
however the close method never gets calls.
The end result I am looking for is that I spwan a process and the the script waits for that child processs to finish (which waitpid() is doing correctly). I want the output/err of the child process to be display to the screen (which the stdio config is doing correctly). I also want to perform code on the close of the child process which I was going to do in the close event (also tried exit), but it does not fire.
Why is the event not not firing?
http://nodejs.org/api/process.html
Note that just because the name of this function is process.kill, it is really just a signal sender, like the kill system call. The signal sent may do something other than kill the target process.
You can specify the signal while Kill() call.
Looking at waitpid() I found out that it returns an object with the exitCode. I changed my code so that I just perform certain actions based on what the value of the exitCode is.

Detect when parent process exits

I will have a parent process that is used to handle webserver restarts. It will signal the child to stop listening for new requests, the child will signal the parent that it has stopped listening, then the parent will signal the new child that it can start listening. In this way, we can accomplish less than 100ms down time for a restart of that level (I have a zero-downtime grandchild restart also, but that is not always enough of a restart).
The service manager will kill the parent when it is time for shutdown. How can the child detect that the parent has ended?
The signals are sent using stdin and stdout of the child process. Perhaps I can detect the end of an stdin stream? I am hoping to avoid a polling interval. Also, I would like this to be a really quick detection if possible.
a simpler solution could be by registering for 'disconnect' in the child process
process.on('disconnect', function() {
console.log('parent exited')
process.exit();
});
This answer is just for providing an example of the node-ffi solution that entropo has proposed (above) (as mentioned it will work on linux):
this is the parent process, it is spawning the child and then exit after 5 seconds:
var spawn = require('child_process').spawn;
var node = spawn('node', [__dirname + '/child.js']);
setTimeout(function(){process.exit(0)}, 5000);
this is the child process (located in child.js)
var FFI = require('node-ffi');
var current = new FFI.Library(null, {"prctl": ["int32", ["int32", "uint32"]]})
//1: PR_SET_PDEATHSIG, 15: SIGTERM
var returned = current.prctl(1,15);
process.on('SIGTERM',function(){
//do something interesting
process.exit(1);
});
doNotExit = function (){
return true;
};
setInterval(doNotExit, 500);
without the current.prctl(1,15) the child will run forever even if the parent is dying. Here it will be signaled with a SIGTERM which will be handled gracefully.
Could you just put an exit listener in the parent process that signals the children?
Edit:
You can also use node-ffi (Node Foreign Function Interface) to call ...
prctl(PR_SET_PDEATHSIG, SIGHUP);
... in Linux. ( man 2 prctl )
I start Node.JS from within a native OSX application as a background worker. To make node.js exit when the parent process which consumes node.js stdout dies/exits, I do the following:
// Watch parent exit when it dies
process.stdout.resume();
process.stdout.on('end', function() {
 process.exit();
});
Easy like that, but I'm not exactly sure if it's what you've been asking for ;-)

Resources