Nodejs: Send Ctrl+C to a child process on Windows
Asked Answered
I

2

8

Hi I am using child_process.spwan to start a child process running a python script on Windows. The script listens on SIGINT to gracefully exits itself. But Windows does not support signals and all node did was simulating. So child_process.kill('SIGINT') on Windows is actually killing the process unconditionally (no graceful exit, python's SIGTERM/SIGINT handler not called). Also writing a ctrl+c character to stdin does not work either.

When I look into Python APIs, I got the CTRL_BREAK_EVENT and CTRL_C_EVENT that can serve the need. I am wondering if node has equivalent platform-specific APIs like these?

Related posts but not working ones: How to send control C node.js and child_processes sending crtl+c to a node.js spawned childprocess using stdin.write()?

Indult answered 18/10, 2016 at 20:33 Comment(1)
It is said to be impossible here: https://mcmap.net/q/1473635/-how-to-send-quot-ctrl-c-quot-to-child-process-in-node-jsLagging
D
1

You can use IPC messages to signal to the child that its time to stop and gracefully terminate. The below approach uses process.on('message') to listen for messages from the parent in the child process & child_process.send() to send messages from the parent to the child.

The below code has a 1 minute timeout set to exit if the child hangs or is taking to long to finish.

py-script-wrapper.js

// Handle messages sent from the Parent
process.on('message', (msg) => {
  if (msg.action === 'STOP') {
    // Execute Graceful Termination code
    process.exit(0); // Exit Process with no Errors
  }
});

Parent Process

const cp = require('child_process');
const py = cp.fork('./py-script-wrapper.js');

// On 'SIGINT'
process.on('SIGINT', () => {
  // Send a message to the python script
  py.send({ action: 'STOP' }); 

  // Now that the child process has gracefully terminated
  // exit parent process without error
  py.on('exit', (code, signal) => {
    process.exit(0);
  });

  // If the child took too long to exit
  // Kill the child, and exit with a failure code
  setTimeout(60000, () => {
    py.kill();
    process.exit(1);
  });

});
Desdemona answered 18/10, 2016 at 20:46 Comment(1)
Thanks. This certainly works when the child is a node process. But what if eventually I need to spawn a Python process (e.g., spawn('python', ['myscript.py']), which holds some resources (e.g., sockets) but does not speak Javascript at all. How can I IPC the python proc in a cross-platform manner?Indult
H
0

You could send a 'quit' command via stdin to the Pyhthon process, that worked for me. In Python you need to create a thread which reads from stdin using input, once this returns, you set an event flag. In your main application loop you regularly check whether the event has been set and exit the program.

Python application (script.py):

import threading
import sys

def quit_watch(event):
    input("Type enter to quit")
    event.set()

def main():
    stop = threading.Event()
    threading.Thread(target=quit_watch, args=[stop]).start()

    while True:
        # do work, regularly check if stop is set
        if stop.wait(1):
            print("Stopping application loop")
            break

if __name__ == '__main__':
    main()
    sys.exit(0)

Node.js application:

child_process = require('child_process')
child = child_process.spawn('python.exe', ['script.py'])
// check if process is still running
assert(child.kill(0) == true)
// to terminate gracefully, write newline to stdin
child.stdin.write('\n')
// check if process terminated itself
assert(child.kill(0) == false)
Heptameter answered 20/2, 2018 at 10:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.