berry: [Bug] yarn v2 does not forward SIGTERM to child processes

  • I’d be willing to implement a fix

Describe the bug This appears to be a regression from https://github.com/yarnpkg/yarn/issues/3424. Doing a kill on the yarn run process will leave the other processes hanging around. To me it seems that the correct behavior should be to forward the SIGTERM to any child processes that yarn has created.

Would be great to have this fixed since it is a prereq for me using yarn run in production, which I believe I need to do to use PnP in prod (unless there’s another way!). Kubernetes uses SIGTERM to signal containers to gracefully shut down and right now yarn doesn’t fit into that workflow because it can’t handle the signal.

To Reproduce Couldn’t figure out how to repro using Sherlock. As mentioned earlier, the reference link in the sherlock documentation is broken.

Here’s a minimal repro. To see the behavior, clone the repo, then:

  1. yarn run http-server in one window
  2. ps ax | grep http-server in another. You should see 3 processes, with node /usr/bin/yarn run http-server as the top process.
  3. kill the PID of the top process.
  4. ps ax | grep http-server again. You’ll see that the top process is gone, but the other two remain.

Environment if relevant (please complete the following information):

  • OS: [e.g. OSX, Linux, Windows, …]
  • Node version [e.g. 8.15.0, 10.15.1, …]
  • Yarn version [e.g. 2.0.0-rc1, …]

Thank you!

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 16 (8 by maintainers)

Most upvoted comments

Ah I assume the node repl puts the stdin in raw mode, which disables the entire INTR behaviour.

When in raw mode, input is always available character-by-character, not including modifiers. Additionally, all special processing of characters by the terminal is disabled, including echoing input characters. CTRL+C will no longer cause a SIGINT when in this mode. ~ https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode

Still, given the default behaviour of the signals caused by user interaction (SIGINT with INTR, SIGQUIT with its analogous QUIT, SIGTSTP and its analogous SUSP) I would propose not forwarding those signals. I fear having ctrl-c behave differently by triggering two consecutive SIGINTs is going to bite us harder than not supporting kill -SIGINT <pid of yarn>. For SIGINT it’s also dangerous to assume the user wants to quit the process, e.g. if I hit ^C in BASH I want to quit the active child but not BASH itself, so even implementing a timeout + kill signal if not finished in time would potentially cause issues.

@bgotink I think this is strange but not entirely unexpected behaviour. child_process is basically using popen underneath or at least purports to roughly conform to it. It’s POSIX standard that a keyboard ^c is an INTR, not directly a SIGINT (though it generates SIGINT(s)). One of differences is that with a INTR it is forwarded to children automatically. However, a naked SIGINT is not automatically forwarded. You can see someone who is confused about this discussing it here https://unix.stackexchange.com/questions/149741/why-is-sigint-not-propagated-to-child-process-when-sent-to-its-parent-process

Here is where the POSIX standard describes a INTR https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap11.html#tag_11_01_09

So the nub of it is that it is up to parent processes to forward signals in any POSIX system. With Yarn, as it is executing one process on behalf of the user (sort of as a proxy, where it sets up a certain environment for the child process), I would expect it to forward those signals (as it’s not a daemon manager or some such). I would also expect (as there is a 1:1 relationship with the parent and child process) that it would wait a reasonable amount of time for the child to exit and pass any exit code from the child as its own exit code.

yeah FWIW I ended up using a k8s preStop hook - seems like they had a workaround kind of built-in already. dumb-init looks like a good solution too. I’ll go ahead and close this; thx for the help all!

Looking at this post, I think I can make some sense of it:

  • By POSIX rule, ^C causes the shell to send a SIGINT to the foreground process group.
  • However, kill, by default, will only target one process, not a group. To target the whole group, it’d need to use the negative pid (ie kill -50606).

That would explain why the SIGINT goes down until it reaches the children, whereas the SIGTERM doesn’t. In fact if that’s true, sending a manual one-process SIGINT (kill 50606 -SIGINT) will have the same effect (or rather, it won’t stop anything, since we ignore it and the children won’t receive it).

With that in mind, remains to figure out what’s the correct behaviour … shouldn’t tools such as K8s send the SIGTERM signal to the process group, hence following the same behaviour as what ^C does? On the other hand, it may make sense for the parent to explicitly decide whether the signals should be forwarded or not.