Kill process and sub-processes in Ruby on Windows - ruby

Currently I'm doing this in one command prompt
require 'win32/process'
p = Process.spawn("C:/ruby193/bin/bundle exec rails s")
puts p
Process.waitpid(p)
and then in another
require 'win32/process'
Process.kill(1,<p>)
The problem is that the process I spawn (the Rails server in this case) spawns a chain of sub-processes. The kill command doesn't kill them, it just leaves them orphaned with no parent.
Any ideas how can I kill the whole spawned process and all its children?

I eventually solved this in the following manner
First I installed the sys-proctable gem
gem install 'sys-proctable'
then used the originally posted code to spawn the process, and the following to kill it (error handling omitted for brevity)
require 'win32/process'
require 'sys/proctable'
include Win32
include Sys
to_kill = .. // PID of spawned process
ProcTable.ps do |proc|
to_kill << proc.pid if to_kill.include?(proc.ppid)
end
Process.kill(9, *to_kill)
to_kill.each do |pid|
Process.waitpid(pid) rescue nil
end
You could change the kill 9 to something a little less offensive of course, but this is the gist of the solution.

One-script solution without any gems. Run the script, CTRL-C to stop everything:
processes = []
processes << Process.spawn("<your process>")
loop do
trap("INT") do
processes.each do |p|
Process.kill("KILL", p) rescue nil
Process.wait(p) rescue nil
end
exit 0
end
sleep(1)
end

Related

[Ruby 1.9][Windows] Sending Ctrl-C interrupt signal to a spawned subprocess

I have a main script in Ruby 1.9.3 running on Windows. It's will start another Ruby script that runs as a daemon, do its own stuff, then end the daemon by sending an "INT" signal. The main script and daemon don't otherwise do any data exchange.
The daemon itself can run as a standalone, and we terminate it with Ctrl-C. Here's the part that prepares it for the signal:
def setup_ctrl_c_to_quit
Thread.new do
trap("INT") do
puts "got INT signal"
exit
end
while true
sleep 1
end
end
end
I am currently having trouble having the main script launching and terminating the daemon. Currently, I can start the daemon through spawn and detach as such:
def startDaemon
#daemonPID = spawn("ruby c:/some_folder/daemon.rb", :new_pgroup=>true, :err=>:out)
puts "DaemonPID #{#daemonPID}"
daemonDetatch = Process.detach(#daemonPID)
puts "Detached Daemon . Entering sleep...."
sleep 15
puts "Is daemon detached thread alive? => #{daemonDetatch.alive?}"
puts "Attempt to kill daemon...."
Process.kill( "INT", #daemonPID )
sleep 5
puts "Is daemon detached thread still alive? => #{daemonDetatch.alive?}"
end
Ideally, the last puts statement should show daemonDetatch.alive? to be false. In reality, not only does daemonDetatch.alive? still ended up being true by the end, the daemon also can be found as still running in both the Task Manager and other 3rd party apps such as Process Explorer.
The first question I have is with the spawn(...) function. The official documentation said that :new_pgroup "is necessary for Process.kill(:SIGINT, pid) on the subprocess" send it determines whether the subprocess becomes a new group or not. I've toggled with this paramter, but it didn't seem to make a difference.
Also, I am planning to give this solution a try, which involves using the win32-process gem. I am just wondering if there are other solutions out there.
[Edit]
I have validated the PID of the daemon obtained in the main script, the daemon itself (with $$), and Process Explore, and they are all the same.
I have gotten suggestion from many others to just use "taskkill /f" to terminate the daemon. That will indeed end the daemon, but the daemon cannot trap the "TERM" or "KILL" signals the same way it traps "INT", meaning it will be unable to run its clean-up/quit routine.

Is there a way to use a keystroke to invoke the pry ruby gem?

I was just thinking about how great it would be to be able to run a program and then hit a keystroke to invoke pry and debug. Maybe there is a gem out there that injects binding.pry dynamically during runtime that I don't know about. If there isn't, how would you make a keystroke that inserts binding.pry before the next line of ruby script that is about to execute?
Assuming a POSIX OS, you could try adding a signal handler in your ruby program. The ruby documentation even gives an example of your use case:
.. your process may trap the USR1 signal and use it to toggle debugging (http://www.ruby-doc.org/core-2.1.3/Signal.html)
Signal.trap('USR1') do
binding.pry
end
Then, to send the signal:
kill -s SIGUSR1 [pid]
Edit: A more complete example: application.rb
My naïve suggestion above will fail with a ThreadError: current thread not owner. Here's a better example using a global $debug flag.
#!/usr/bin/env ruby
require 'pry'
$debug = false
Signal.trap('USR1') do
puts 'trapped USR1'
$debug = true
end
class Application
def run
while true
print '.'
sleep 5
binding.pry if $debug
end
end
end
Application.new.run
This seems to work best when application.rb is running in the foreground in one shell, and you send the SIGUSR1 signal from a separate shell.
Tested in Mac OS 10.9.5. YMMV.

Forcing Code in Ruby on Windows when X button is hit

While writing a ruby script on Windows (ruby -v outputs ruby 1.9.3p545) I encountered an interesting and rather specific problem. I was attempting to close an opened file if a user terminates execution. For example,
begin
f = File.open("monkeys.txt", "w+")
#stuff with the file
rescue Exception => e #I know this is a bad idea
puts e.backtrace
ensure
f.close
end
Now, this works if I terminate execution via Ctrl+C while running this in cmd. However, when I hit the "X" on the cmd prompt window, the code in the ensure block doesn't run. I tried something like...
at_exit do
f.close if !f.closed?
end
...but that still doesn't execute the code I want it to when the X button is hit.
So, what do I do in order to force "ensure" code in Ruby if it's closed via that X button?
Well, I don't really program for windows, so I might get lost on the details, but let me try to shed some light with this workaround for Linux:
#ppid = Process.ppid
pid = fork do
loop do
sleep(1)
begin
Process.getsid(#ppid)
rescue Errno::ESRCH
File.new("process_down.txt", "a+")
exit(1)
end
end
end
Process.detach(pid)
puts "Process detached"
What this does is it creates a forked process, detaches it from the main process and keeps listening for when the main process is killed (it'll throw Errno::ESRCH on Process.getsid if the #ppid is no longer there), so it'll create a .txt file and exit. I don't know how to handle forking and pids in windows, but that's just to try and show you some possibilities =]

Rakefile - stop every tasks in a multitask

I have an application running with Flask, and use Compass as css preprocessor. Which means I need to start the python server and compass for development. I made what I thought was a clever Rakefile to start everything from one command and have everything run in only one terminal window.
Everything works, but the problem is when I try to stop everything (with cmd + c), it only kills the compass task and the Flask server keeps running. How can I make sure every tasks are stopped? Or is there an alternative to simultaneously launch several tasks without this issue?
Here is my rakefile, pretty simple:
# start compass
task :compass do
system "compass watch"
end
# start the flask server
task :python do
system "./server.py"
end
# open the browser once everything is ready
task :open do
`open "http://127.0.0.1:5000"`
end
# the command I run: `$ rake server`
multitask :server => ['compass', 'python', 'open']
EDIT
For the record, I was using a Makefile and everything worked perfectly. But I changed part of my workflow and started using a Rakefile, so I Rakefile'd everything and got rid of the Makefile for simplicity.
That is because system creates new processes for your commands. To make sure they are killed alongside your ruby process, you will need to kill them yourself. For this you need to know their process ids, which system does not provide, but spawn does. Then you can wait for them to exit, or kill the sub-processes when you hit ^C.
An example:
pids = []
task :foo do
pids << spawn("sleep 3; echo foo")
end
task :bar do
pids << spawn("sleep 3; echo bar")
end
desc "run"
multitask :run => [:foo, :bar] do
begin
puts "run"
pids.each { |pid| Process.waitpid(pid) }
rescue
pids.each { |pid| Process.kill("TERM", pid) }
exit
end
end
If you do a rake run on that, the commands get executed, but when you abort, the tasks are sent the TERM signal. There's still an exception that makes it to the top level, but I guess for a Rakefile that is not meant to be published that does not matter too much. Waiting for the processes is necessary or the ruby process will finish before the others and the pids are lost (or have to be dug out of ps).

Need help understanding ruby's Process.detach

I'm learning systems programming through ruby and I'm having trouble understanding this behavior:
pid = fork do
Signal.trap("USR1") do
puts "hello!"
end
Signal.trap("TERM") do
puts "Terminating"
exit
end
loop do
end
end
Process.detach(pid)
Process.kill("USR1", pid)
Process.kill("USR1", pid)
Process.kill("USR1", pid)
Process.kill("USR1", pid)
Process.kill("TERM", pid)
This outputs as I expect:
hello!
hello!
hello!
hello!
Terminating
However if I comment out Process.detach, the child process seems to only respond to the signal once (and after termination?):
Terminating
hello!
I'm confused as to why this happens when I don't detach the process, even though I sent it USR1 four times. Can someone help explain this behavior? I think I'm not understanding what it means to detach a process.
Thanks so much!
It's all down to timing, I suspect - that is, the difference is due to how the instructions of your main process and the forked process are scheduled to run relative to each other.
When you do Process.detach, a new thread is created which waits on the exit result of the given process. You can replace the Process.detach with
Thread.new { Process.wait(pid) }
and get the same effect. I suspect that calling detach (and spawning a new thread) gives your forked process, as a side effect, a chance to be scheduled.
If you have no detach, then I would guess your forked process doesn't get a chance to run by the time you tell it to die.
You can see what I mean by relative timing by inserting some sleep calls in your code to see if you can get the same observed behavior without the detach.
For example, this seems to work for me, though your mileage may vary depending on your host platform:
pid = fork do
Signal.trap("USR1") do
puts "hello!"
end
Signal.trap("TERM") do
puts "Terminating"
exit
end
loop do
end
end
sleep(1)
Process.kill("USR1", pid)
Process.kill("USR1", pid)
Process.kill("USR1", pid)
Process.kill("USR1", pid)
sleep(1)
Process.kill("TERM", pid)
This produces:
hello!
hello!
hello!
hello!
Terminating

Resources