96

I'm following the guidance here (listening for SIGINT events) to gracefully shutdown my Windows-8-hosted node.js application in response to Ctrl+C or server shutdown.

But Windows doesn't have SIGINT. I also tried process.on('exit'), but that seems to late to do anything productive.

On Windows, this code gives me: Error: No such module

process.on( 'SIGINT', function() {
  console.log( "\ngracefully shutting down from  SIGINT (Crtl-C)" )
  // wish this worked on Windows
  process.exit( )
})

On Windows, this code runs, but is too late to do anything graceful:

process.on( 'exit', function() {
  console.log( "never see this log message" )
})

Is there a SIGINT equivalent event on Windows?

Simson
  • 3,373
  • 2
  • 24
  • 38
Q 4
  • 1,231
  • 2
  • 11
  • 11
  • This problem randomly occured to me today and I think it has something to do with the readline module itself. I couldn't do any testing but since I added this module I started having the problem. – Sv443 Feb 01 '19 at 20:47

8 Answers8

166

You have to use the readline module and listen for a SIGINT event:

http://nodejs.org/api/readline.html#readline_event_sigint

if (process.platform === "win32") {
  var rl = require("readline").createInterface({
    input: process.stdin,
    output: process.stdout
  });

  rl.on("SIGINT", function () {
    process.emit("SIGINT");
  });
}

process.on("SIGINT", function () {
  //graceful shutdown
  process.exit();
});
knownasilya
  • 5,998
  • 4
  • 36
  • 59
Gabriel Llamas
  • 18,244
  • 26
  • 87
  • 112
  • most excellent update, thanks! setting this as the answer as the previous answer (listen for keypress) no longer works. – Q 4 Feb 16 '13 at 00:50
  • 69
    This is ridiculous. Why isn't this handled by the node core? – balupton Nov 19 '13 at 12:00
  • 5
    Because when you listen to stdin the process never finishes until you send a SIGINT signal explicitly. – Gabriel Llamas Dec 05 '13 at 09:19
  • 1
    How do you send a SIGINT to a child process on windows? My child.kill('SIGINT') does not seem to work even with readline. – Nick Sotiros May 16 '14 at 23:34
  • Windows does not have signals, you cannot send a SIGINT to a child process. The main process is who can listen the stdin and that's how you can mimic the SIGINT on Windows, but child processes can't. – Gabriel Llamas May 18 '14 at 14:54
  • 2
    Therefore, you need to send a random message from the parent to the child, for example: "SIGINT". – Gabriel Llamas May 18 '14 at 19:28
  • @GabrielLlamas this can't intercept `process.kill(pid, 'SIGINT')` though, can it? – Andy Oct 29 '16 at 02:39
  • Just windows things – Luca Steeb Jan 04 '17 at 19:49
  • Thank you so much for this! This problem was driving me crazy! – Derik Taylor Mar 16 '17 at 23:44
  • 14
    Looks like this got solved quite a while ago: https://github.com/nodejs/node-v0.x-archive/issues/5054 – SimonSimCity Nov 20 '17 at 10:01
  • 1
    It doesn't seem to work from a Windows Powershell prompt. – John Mills Feb 11 '21 at 06:55
  • @SimonSimCity If so, not sure what I'm doing wrong because adding a SIGINT handler results in ctrl+c being ignored and my function not being called. Node running under Windows 10 CMD.EXE. – Michael Oct 05 '22 at 19:07
  • This solution doesn't work for me either... I don't get SIGINT until nodejs goes to perform some I/O (i.e. something like console.log) so it can be a very long time between hitting ctrl+c until SIGINT actually works, which pretty much defeats the whole purpose (saving the current state and exiting without having to wait for output) – Michael Oct 05 '22 at 19:17
31

I'm not sure as of when, but on node 8.x and on Windows 10 the original question code simply works now.

process.on( "SIGINT", function() {
  console.log( "\ngracefully shutting down from SIGINT (Crtl-C)" );
  process.exit();
} );

process.on( "exit", function() {
  console.log( "never see this log message" );
} );

setInterval( () => console.log( "tick" ), 2500 );

enter image description here

also works with a windows command prompt.

Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122
8

Unless you need the "readline" import for other tasks, I would suggest importing "readline" once the program has verified that it's running on Windows. Additionally, for those who might be unaware - this works on both Windows 32-bit and Windows 64-bit systems (which will return the keyword "win32"). Thanks for this solution Gabriel.

if (process.platform === 'win32') {
  require('readline')
    .createInterface({
      input: process.stdin,
      output: process.stdout
    })
    .on('SIGINT', function () {
      process.emit('SIGINT');
    });
}

process.on('SIGINT', function () {
  // graceful shutdown
  process.exit();
});
tim-montague
  • 16,217
  • 5
  • 62
  • 51
8

Nowadays it just works on all platforms, including Windows.

The following code logs and then terminates properly on Windows 10:

process.on('SIGINT', () => {
    console.log("Terminating...");
    process.exit(0);
});
Heinrich Ulbricht
  • 10,064
  • 4
  • 54
  • 85
  • 3
    This does NOT work for me under cygwin. Using latest Windows 10 (automatic updates), node version 8.11.4. By "it does NOT work" I mean that 1) the process does terminate, but 2) the message is not logged to the console and 3) HTTP connections created are not shut down. I should add, however, that I tried it under PowerShell, and it works as expected there. But I switched to cygwin because PowerShell has a bad curl command. Darn! – John Deighan Sep 10 '18 at 03:15
  • 3
    It stopped working for me just today for no apparent reason (no updates at all) and I still don't know why. The accepted answer fixed it. – Sv443 Feb 01 '19 at 20:44
  • git bash windows doesn't work – java-addict301 Sep 30 '21 at 12:49
  • This code does *not* work for me on Windows 10 under CMD.EXE ... instead of CTRL+C terminating the program, it is completely ignored and my function is not called. – Michael Oct 05 '22 at 19:06
  • Ok I take back part of that... it does work, but it does not work until I go to output something to the console, which kind of defeats the whole purpose of SIGINT which is to get *immediate* attention. – Michael Oct 05 '22 at 19:23
4

Currently there is still no support in node for capturing the windows console control events, so there are no equivalents to the POSIX signals:

https://github.com/joyent/node/issues/1553

However the tty module documentation does give an example of a mechanism to capture the key presses in order to initiate a graceful shutdown, but then this does only work for ctrl+c.

var tty = require('tty');

process.stdin.resume();
tty.setRawMode(true);

process.stdin.on('keypress', function(char, key) {
  if (key && key.ctrl && key.name == 'c') {
    console.log('graceful exit of process %d', process.pid);
    process.exit();
  }
});
Pero P.
  • 25,813
  • 9
  • 61
  • 85
  • Thanks, been looking for this info, this is an acceptable substitution for me as long as I can implement it on the server for for CTRL+C. +1 (But.. Any idea whether this will effect performance by adding event listeners on the process?) – Cory Gross Jul 28 '12 at 22:19
  • I tried doing this, but when my server is running the game loop stdin does not seem to be available, CTRL+C doesn't work when I use the above. – Cory Gross Jul 28 '12 at 23:37
1

Nothing above worked for me, so workaround has been to have a readline hanging and catch the signal from there.

Here my solution:

const readline = require('readline');

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

// Flag to be able to force the shutdown
let isShuttingDown = false;

// https://nodejs.org/api/readline.html
rl.on('SIGINT', async () => {
  if (isShuttingDown) {
    logger.info("Forcing shutdown, bye.");
    process.exit();
  } else {
    if (!<yourIsCleanupNecessaryCheck>()) {
      logger.info("No cleanup necessary, bye.");
      process.exit();
    } else {
      logger.info("Closing all opened pages in three seconds (press Ctrl+C again to quit immediately and keep the pages opened) ...");
      isShuttingDown = true;
      await sleep(3000);
      await <yourCleanupLogic>();
      logger.info("All pages closed, bye.");
      process.exit();
    }
  }

function sleep(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

It's quite vanilla, it's asynchronous, and it works both on MacOS 11.3 and Windows 10 (at the time of writing).

pierpytom
  • 1,202
  • 1
  • 12
  • 25
0

Since node.js 0.8 the keypress event no longer exists. There is however an npm package called keypress that reimplements the event.

Install with npm install keypress, then do something like:

// Windows doesn't use POSIX signals
if (process.platform === "win32") {
    const keypress = require("keypress");
    keypress(process.stdin);
    process.stdin.resume();
    process.stdin.setRawMode(true);
    process.stdin.setEncoding("utf8");
    process.stdin.on("keypress", function(char, key) {
        if (key && key.ctrl && key.name == "c") {
            // Behave like a SIGUSR2
            process.emit("SIGUSR2");
        } else if (key && key.ctrl && key.name == "r") {
            // Behave like a SIGHUP
            process.emit("SIGHUP");
        }
    });
}
mekwall
  • 28,614
  • 6
  • 75
  • 77
0

Windows + Git Bash/Cygwin solution:

None of the other solutions for Windows and Git Bash worked, so my solution was to simply use WINPTY as follows to launch Node:

My package.json has this start script:

"start": "winpty node app.js"

This was inspired by the accepted answer here for similar Python issue:

python error Suppressing signal 18 to win32

Note: WINPTY runs on Windows XP and beyond.

java-addict301
  • 3,220
  • 2
  • 25
  • 37