Thread#terminate and handling SIGTERM - ruby

Here's a simplified version of some code I wrote:
class InfiniteLoop
def run
trap('SIGTERM') do
puts 'exiting'
exit
end
loop {}
end
end
If I run:
InfiniteLoop.new.run
I can ctrl+c and get:
exiting
However, when I do this:
t = Thread.new { InfiniteLoop.new.run }
sleep 1
t.terminate
I don't see:
exiting
Can someone point me in the right direction here? I'd like to have the same behavior when terminating the thread.

If you are not sending a SIGTERM signal (via ctrl+c) the trap block is not executed.
See also the Kernel method:
at_exit { puts 'exiting' }

trap('SIGTERM') will only respond to the signals sent from OS land.
Thread#terminate is ruby code that will kill the thread.
I don't know of a way to specify behavior for a thread to take before it is killed. That might be interesting. But I don't think it exists, because the semantics of Thread#kill/terminate/join wouldn't really allow that.

Try trap("EXIT"). SIGTERM is sent by ctrl-C or a kill command. From the ruby docs:
The special signal name “EXIT” or signal number zero will be invoked just prior to program termination.

Related

ensure block of code runs when program exits

I want to make sure that a peice of code runs when the ruby program ends. I used the following ways but they do not work in some situations.
def a_method
# do some work
ensure
# code that must be run when method ends and if program exits when it still in this method.
end
def a_method
at_exit{
# run code that needs to be run when process exists
}
# do some work
ensure
# do code that needs to be run when method ends
end
those two methods works very well when the process is killed with a signal other than kill -9 'although I didn't tried all the signals'.
So is there a way to make sure that code runs even if the process is killed with this signal?
Signal 9 is non-catchable, non-ignorable kill, by design. Your at_exit will not run because the operating system will simply terminate any process that receives this signal, not giving it any chance to do any extra work.

Killing Threads in Ruby Shoes doesn't seem to work?

I try to make this piece of code works in Ruby Shoes, but I can't succeed to kill the thread named "airodump". Each time I click on the "stop scanning" button, it doesn't do anything :s
Thanks
button "scan networks" do
airodump = Thread.new do
`sudo airodump-ng --write tuto wlan0`
end
Thread.new do
button "Stop scanning" do
Thread.kill(airodump)
end
end
end
Thread killing (in any language) is a pretty limited operation. In your case, the thread is stuck in a blocking call - the call to the subshell - and therefore cannot be killed.
What you would need to do is kill the subprocess first. I don't know how to get the PID out of the backtick operator, so you would need to rather use Process.spawn (on ruby 1.9), gey the PID of the subprocess, and call Process.kill
I think the main problem is that you should use class variables so define
#airodump=Thread.new do
...
end
And than kill it by:
Thread.kill(#airodump)

Where goes signal sent to process which called system?

Given a very simple ruby script:
child = fork do
system 'sleep 10000'
end
5.times do
sleep 1
puts "send kill to #{child}"
Process.kill("QUIT", child)
end
QUIT signal is just lost. Where does it go? Something with default handler which just ignores it?
How to send signal to all processes created by that fork? Is it possible to do that without searching for all child processes?
The problem is that the system call creates yet another child process running the given command in a subshell, so there are actually three processes running in your example. Additionally, the Ruby Kernel#system command is implemented via the standard C function system(3), which calls fork and exec to create the new process and (on most systems) ignores SIGINT and SIGQUIT, and blocks SIGCHLD.
If you simply call sleep(10000) instead of system("sleep 10000") then things should work as you expect. You can also trap SIGQUIT in the child to handle it gracefully:
child = fork do
Signal.trap("QUIT") { puts "CHILD: ok, quitting time!"; exit }
sleep(10000)
end
If you really need to use a "system" call from the child process then you might be better off using an explicit fork/exec pair (instead of the implicit ones in the system call), so that you can perform your own signal handling in the third forked child.
I think that you are sending signal to fork process corectly. I think that the problem is with the system command. System command creates new fork and waits until it ends and I think that this waiting is blocking your quit signal. If you run your example as test.rb you'll see three processes:
test.rb
test.rb
sleep 10000
If you send signal "TERM" or "KILL" instead of "QUIT" the second test.rb will die but sleep 10000 will continue!

Ruby: How do make my program trap and exit properly from a signal?

When I Ctrl C my program on linux I often get output that looks similar to a stack-dump (a crash).
Can I catch the Ctrl-C signal and exit gracefully?
Thanks!
You can trap signals with Kernel.trap:
trap('INT') do
# exit gracefully
end

How can I handle (or prevent) SIGCHLD signals from ruby backquote invocations?

I have a long-running process with some child processes that must be restarted if they exit. To handle clean restarts of these child processes, I trap the exit signal with
trap("CLD") do
cpid = Process.wait
... handle cleanup ...
end
The long-running process occasionally needs to invoke 'curl' using a backquote as in
`/usr/bin/curl -m 60 http://localhost/central/expire`
The problem is that the backquote invocation is causing me to get a SIGCHLD and making my trap fire. This then gets stuck in the CLD trap because Process.wait does not finish. If there happen to be no (non-backquote) child processes at that time, the Process.wait instead gives an Errno::ECHILD exception.
I can circumvent this problem by wrapping the backquote call with this line before:
sig_handler = trap("CLD", "IGNORE") # Ignore child traps
and this line after the backquote invocation:
trap("CLD", sig_handler) # replace the handler
but this means that I may miss a signal from the (non-backquote) child processes during that window, so I'm not really happy with that.
So, is there a better way to do this?
(I am using ruby 1.9.1p243 on GNU/Linux 2.6.22.6 if it matters)
Update:
The code below illustrates the problem (and my current solution for it).
There seems to be some strange timing issue here since I don't always get the ECHILD exception. But just once is enough to mess things up.
#!/usr/bin/env ruby
require 'pp'
trap("CLD") do
cpid = nil
begin
puts "\nIn trap(CLD); about to call Process.wait"
cpid = Process.wait
puts "In trap(CLD); Noting that ssh Child pid #{cpid}: terminated"
puts "Finished Child termination trap"
rescue Errno::ECHILD
puts "Got Errno::ECHILD"
rescue Exception => excep
puts "Exception in CLD trap for process [#{cpid}]"
puts PP.pp(excep, '')
puts excep.backtrace.join("\n")
end
end
#Backtick problem shown (we get an ECHILD most of the time)
puts "About to invoke backticked curl"
`/usr/bin/curl -m 6 http://developer.yahooapis.com/TimeService/V1/getTime?appid=YahooDemo`
sleep 2; sleep 2 # Need two sleeps because the 1st gets terminated early by the trap
puts "Backticked curl returns"
# Using spawn
puts "About to invoke curl using spawn"
cpid = spawn("/usr/bin/curl -m 6 http://developer.yahooapis.com/TimeService/V1/getTime?appid=YahooDemo")
puts "spawned child pid is #{cpid} at #{Time.now}"
Start monitored subprocesses from a subprocess
Just start your tracked and monitored children from a child of your main process that never exits. That way it won't notice the backtick children exiting...
And if you do this, you could avoid the use of SIGCHLD entirely, as you could just use a loop with a wait in it to notice children exit events.
Other ideas:
ignore one SIGCHLD every time you execute a backtick command. ISTM that you might ignore a "real" SIGCHLD by accident this way, but that won't matter, because you would then get a "spurious" one that you would process.

Resources