Getting list of current signal handlers without overriding, in Ruby
Lately I have been very interested about Signal handling. So, this holiday season I embarked on a teeny journey of writing a small project.
One thing I noticed when inspecting signals is that there isn’t really an easy way of doing so. Or, especially, getting the “current” action handler for a given Signal. For instance, whether a signal (
INT, etc) has an action of
SIG_IGN or a pointer reference.
Maybe its not a very common use case. Either way, while working on a few ruby projects, I went super deep into digging signal handling (rabbit holes?) and would have loved to access a tool that would give me the action handlers for signals. There are some ways suggested in ruby, like:
old_handler = Signal.trap("TERM", "DEFAULT")
# do something with old_handler
Signal.trap("TERM", old_handler) # restore back
Signal.trap returns the old handler for the given signal. So, here you can trap a signal with a different action and upon retrieval, restore it back and this way you can know what the “current” action is.
I found this solution a little too aggressive. Because of the non-atomicity, a lot of things can happen between overriding a signal’s action and restoring back, depending on the application.
To see how things worked internally, I went down digging ruby’s signal.c.
I loved learning more about ruby internals. I can’t wait to dive deeper and learn more about ruby and behind the scenes.
That said, basically, it boils down to how the class is using C library function
signal (source code: here) and a few other implementations. A little about
signal() sets the disposition of the signal signum to handler, which
is either SIG_IGN, SIG_DFL, or the address of a programmer-defined
function (a “signal handler”).
Given this, I decided to explore other avenues where I can retrieve a signal’s action handler much more gracefully.
“EXIT” => “DEFAULT”,
“HUP” => “DEFAULT”,
“INT” => “DEFAULT”,
“QUIT” => “DEFAULT”,
“ILL” => “DEFAULT”,
“TRAP” => “SYSTEM_DEFAULT”,
“ABRT” => “SYSTEM_DEFAULT”,
“IOT” => “SYSTEM_DEFAULT”,
“EMT” => “SYSTEM_DEFAULT”,
“FPE” => “SYSTEM_DEFAULT”,
Now, you can easily tell the action handlers of any signal, without having to override or restore it.
Note: The gem has a minor limitation of not being able to parse any pointer references as action handler. So, it displays them as a handler set by ruby (which is originally the case).
Writing a Ruby gem with C extension
I feel, the best part about this little holiday project was being able to write a Ruby C extension. Building a C extension in ruby is absolutely painless. The ruby C api is not very hard to understand (requires some basic C understanding but thats all) and can easily be exposed to any extension.
If you are interested in building C extensions, here are some places that I found helpful.
- A cheat sheet for Ruby’s C API
- Creating a gem that includes an extension by bundler docs
- Writing Ruby C extensions: Part 1 by tenderlove (this may be a little too old)
- Writing Ruby C extensions: Part 2 by tenderlove (this may be a little too old)
Lastly, if you have run into this scenario or have built similar tools around inspecting Signals, or or or… if you have any feedback on the new gem, I’d love to hear from you.