-
-
Notifications
You must be signed in to change notification settings - Fork 2k
weird bundle exec - nohup behavior where SIGHUP gets raised to child process #6150
Comments
Thanks for the detailed report! A PR fixing this issue would be very welcome 👍 |
What would be the work around for bundle exec executable_name ? Where the executable name is only available when doing bundle exec? |
I wonder if this change in 1.13 is responsible |
@segiddins I am unable to follow that link, but, I believe it started with this change introducing the unexpectedness with Signals from: 93e11fc#diff-fcaa44c40b054ace4b039a38e3ffe82fR63 Before I proceed, IMPORTANT NOTE: This bug only exists when you vendor in your gems (which requires Explanation: Like I mentioned, this only happens when you vendor in.
So, when a This entire phenomenon, can by tried by this simple script: test.rb ( IGNORE_SIGNALS = %w[SEGV BUS ILL FPE VTALRM KILL STOP]
signals = Signal.list.keys - IGNORE_SIGNALS
signals.each do |s|
trap(s, "DEFAULT")
end
sleep 100 Now, try using
You will see the nohup died. Next, add I will try to cut a PR soon. |
I am curious, why does bundler needs to swallow any Signal at all? The changes that were introduced to fix the issue: #4568, (which I think is the probable cause for this current issue) seems a little unclear to me, so I am curious :). I am also unable to reproduce the original issue from #4568
I am trying to determine whether it makes sense to add Or just not have signals get swallowed by bundler at all, but leave it to to executables to manage it. For example, puma does a lot of custom stuff to handle signals |
@shayonj the change was to allow signals to be passed to children, but perhaps it is no longer needed? Can you also test this after @segiddins any thoughts here? we didn't end up reverting the exec/load stuff, did we? |
@indirect ah! I believe that makes sense, thanks! And, yep, I can no longer replicate the original issue after setting Looks like |
Hmm. So maybe this means we can stop reserving signals in some versions of Ruby? Or maybe Puma changed the way that it traps signals, and our work-around is no longer needed. |
Given that we're just setting the signal handlers all back to their default, I wonder if Ruby's default handler for |
hm, perhaps I am not following 😅 I believe along with trapping signal handlers, we are also swallowing them (i.e we are not re-raising them), which is why test.rb IGNORE_SIGNALS = %w[SEGV BUS ILL FPE VTALRM KILL STOP]
signals = Signal.list.keys - IGNORE_SIGNALS
signals.each do |s|
trap(s, "DEFAULT")
end
Kernel.exec("ruby ./test1.rb")
# Kernel.load("./test1.rb") test1.rb Signal.trap("HUP") do
puts "--> Received hup from test.rb"
end
sleep 100 Now, run this via
And kill via
You will see that, In short, I am wondering if we need to trap any signals at all. I still can't replicate the puma specific behavior from the past issue:D. |
But all we’re doing is trapping with the default handler, which in theory should be no different than not calling trap at all |
[UPDATE] Yeah, though not entirely. Setting I went digging and it turns out, That said, a better solution might be: bundler restores a signal back to previous status if a present one, otherwise sets the (I discussed the same issue here https://bugs.ruby-lang.org/issues/14196) [UPDATE 2] Here is the dump of the signals that Bundler is overriding from
|
Thanks. Looks like we might need to re-consider setting everything to |
yes.
Do we know if there are some specific signals that Bundler wish to trap? If I am getting this right, anything thats not in |
We've historically trapped signals such as |
Only trap INT signal and set to DEFAULT ### What was the end-user problem that led to this PR? The problem was commands like `nohup bundler exec {program}` wouldn't work as intended. For example, if a `HUP` signal were to be sent to the process running the `bundle exec ..`, it should in theory not terminate. Because, `nohup` would `IGNORE` that signal. But, that is not what the case is case is currently. ### What was your diagnosis of the problem? My diagnosis was, if a process/bundler execution already had a `SIGNAL` set to it, example `HUP`, then performing `bundle exec {program}`, would mean that bundler resets any prior `SIGNAL`s on that process and sets them to `DEFAULT`. ### What is your fix for the problem, implemented in this PR? My fix to the problem is to only trap `SIGNAL`s that we think should be set to `DEFAULT`, in this case, `INT`. ### Why did you choose this fix out of the possible options? I chose this fix because its lot less aggressive than setting every signal to `DEFAULT`, and gives us the work with a smaller set of `SIGNAL`s. It also felt cleaner than having to trap a signal first and then restore to its predecessor value. ---- This is a dump that shows the before and after signals, when `nohup bundle exec {program }` gets run. ``` SIGEXIT: Before Handler: (), Current Handler: (DEFAULT) SIGHUP: Before Handler: (IGNORE), Current Handler: (DEFAULT) SIGINT: Before Handler: (#<Proc:0x00007f8e100534f8@/Users/<>/.rvm/gems/ruby-2.4.2/gems/bundler-1.16.0/exe/bundle:5>), Current Handler: (DEFAULT) SIGQUIT: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGTRAP: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGABRT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGIOT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGEMT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGSYS: Before Handler: (), Current Handler: (DEFAULT) SIGPIPE: Before Handler: (), Current Handler: (DEFAULT) SIGALRM: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGTERM: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGURG: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTSTP: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCONT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCHLD: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCLD: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTTIN: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTTOU: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGIO: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGXCPU: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGXFSZ: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGPROF: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGWINCH: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGUSR1: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGUSR2: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGINFO: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) ``` From this, we can see only `INT` is being trapped by bundler ``` SIGINT: Before Handler: (#<Proc:0x00007f8e100534f8@/Users/<>/.rvm/gems/ruby-2.4.2/gems/bundler-1.16.0/exe/bundle:5>), Current Handler: (DEFAULT) ``` hence, the only one being restored back to `DEFAULT` ---- Issue: #6150
@segiddins can this be closed? |
Only trap INT signal and set to DEFAULT ### What was the end-user problem that led to this PR? The problem was commands like `nohup bundler exec {program}` wouldn't work as intended. For example, if a `HUP` signal were to be sent to the process running the `bundle exec ..`, it should in theory not terminate. Because, `nohup` would `IGNORE` that signal. But, that is not what the case is case is currently. ### What was your diagnosis of the problem? My diagnosis was, if a process/bundler execution already had a `SIGNAL` set to it, example `HUP`, then performing `bundle exec {program}`, would mean that bundler resets any prior `SIGNAL`s on that process and sets them to `DEFAULT`. ### What is your fix for the problem, implemented in this PR? My fix to the problem is to only trap `SIGNAL`s that we think should be set to `DEFAULT`, in this case, `INT`. ### Why did you choose this fix out of the possible options? I chose this fix because its lot less aggressive than setting every signal to `DEFAULT`, and gives us the work with a smaller set of `SIGNAL`s. It also felt cleaner than having to trap a signal first and then restore to its predecessor value. ---- This is a dump that shows the before and after signals, when `nohup bundle exec {program }` gets run. ``` SIGEXIT: Before Handler: (), Current Handler: (DEFAULT) SIGHUP: Before Handler: (IGNORE), Current Handler: (DEFAULT) SIGINT: Before Handler: (#<Proc:0x00007f8e100534f8@/Users/<>/.rvm/gems/ruby-2.4.2/gems/bundler-1.16.0/exe/bundle:5>), Current Handler: (DEFAULT) SIGQUIT: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGTRAP: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGABRT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGIOT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGEMT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGSYS: Before Handler: (), Current Handler: (DEFAULT) SIGPIPE: Before Handler: (), Current Handler: (DEFAULT) SIGALRM: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGTERM: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGURG: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTSTP: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCONT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCHLD: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCLD: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTTIN: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTTOU: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGIO: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGXCPU: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGXFSZ: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGPROF: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGWINCH: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGUSR1: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGUSR2: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGINFO: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) ``` From this, we can see only `INT` is being trapped by bundler ``` SIGINT: Before Handler: (#<Proc:0x00007f8e100534f8@/Users/<>/.rvm/gems/ruby-2.4.2/gems/bundler-1.16.0/exe/bundle:5>), Current Handler: (DEFAULT) ``` hence, the only one being restored back to `DEFAULT` ---- Issue: #6150 (cherry picked from commit 5cf764d)
@colby-swandale do you have a (rough, even) timeline of when you'll be promoting this tag and and submitting 1.16.2 to rubygems.org? Just seeing whether I'm OK to wait (this problem has bit me recently) a few, say, days, or whether I should tool my own workaround by building bundler from the 1-16-stable branch. |
Only trap INT signal and set to DEFAULT ### What was the end-user problem that led to this PR? The problem was commands like `nohup bundler exec {program}` wouldn't work as intended. For example, if a `HUP` signal were to be sent to the process running the `bundle exec ..`, it should in theory not terminate. Because, `nohup` would `IGNORE` that signal. But, that is not what the case is case is currently. ### What was your diagnosis of the problem? My diagnosis was, if a process/bundler execution already had a `SIGNAL` set to it, example `HUP`, then performing `bundle exec {program}`, would mean that bundler resets any prior `SIGNAL`s on that process and sets them to `DEFAULT`. ### What is your fix for the problem, implemented in this PR? My fix to the problem is to only trap `SIGNAL`s that we think should be set to `DEFAULT`, in this case, `INT`. ### Why did you choose this fix out of the possible options? I chose this fix because its lot less aggressive than setting every signal to `DEFAULT`, and gives us the work with a smaller set of `SIGNAL`s. It also felt cleaner than having to trap a signal first and then restore to its predecessor value. ---- This is a dump that shows the before and after signals, when `nohup bundle exec {program }` gets run. ``` SIGEXIT: Before Handler: (), Current Handler: (DEFAULT) SIGHUP: Before Handler: (IGNORE), Current Handler: (DEFAULT) SIGINT: Before Handler: (#<Proc:0x00007f8e100534f8@/Users/<>/.rvm/gems/ruby-2.4.2/gems/bundler-1.16.0/exe/bundle:5>), Current Handler: (DEFAULT) SIGQUIT: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGTRAP: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGABRT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGIOT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGEMT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGSYS: Before Handler: (), Current Handler: (DEFAULT) SIGPIPE: Before Handler: (), Current Handler: (DEFAULT) SIGALRM: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGTERM: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGURG: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTSTP: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCONT: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCHLD: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGCLD: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTTIN: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGTTOU: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGIO: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGXCPU: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGXFSZ: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGPROF: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGWINCH: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) SIGUSR1: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGUSR2: Before Handler: (DEFAULT), Current Handler: (DEFAULT) SIGINFO: Before Handler: (SYSTEM_DEFAULT), Current Handler: (DEFAULT) ``` From this, we can see only `INT` is being trapped by bundler ``` SIGINT: Before Handler: (#<Proc:0x00007f8e100534f8@/Users/<>/.rvm/gems/ruby-2.4.2/gems/bundler-1.16.0/exe/bundle:5>), Current Handler: (DEFAULT) ``` hence, the only one being restored back to `DEFAULT` ---- Issue: #6150 (cherry picked from commit 5cf764d)
At the company I work for we ran into this weird problem when running processes through
nohup
andbundle exec
.nohup
is a utility that is supposed to trap SIGHUPs from bubbling to a child process, thus allowing a user to daemonize a command by running it withnohup
and then closing the parent shell.For whatever reason, with more recent versions of bundler past 1.12.5 SIGHUPs bubble up to the child process. 1.12.5 works great. We also found a workaround with more recent versions of bundler where the ordering of the commands changes the behavior -
nohup bundle exec
fails andbundle exec nohup
works. Here's an example of the test which I uploaded here - https://github.com/oneamtu/bundler_nohup_test:Environment
Bundler Build Metadata
Bundler settings
Gemfile
Gemfile
Gemfile.lock
I've been meaning to dig into bundler a bit and patch it, but I figured this info would be good if anyone else is experiencing this issue since it includes a workaround. I'm happy to own it, let me know if you have any particular directions I should go in.
The text was updated successfully, but these errors were encountered: