I'm trying to programmatically spawn a Puma process then kill it via sending it TERM.
To do this I'm using Process.spawn which returns a pid. This PID is not the PID of the puma process but rather the PID of a shell command that spawns puma
pid = Process.spawn "bundle exec puma test/fixtures/app.ru -w 3 -t 1:1 -p 0 -e development > test/logs/puma_1961_1393875906.917352.log"
=> 10711
Now I can run ps aux | grep puma and I can see it is running
schneems 10719 0.0 0.1 2488912 7564 s000 S+ 1:57PM 0:00.02 puma: cluster worker: 10712
schneems 10718 0.0 0.1 2488912 7524 s000 S+ 1:57PM 0:00.02 puma: cluster worker: 10712
schneems 10717 0.0 0.1 2489936 7652 s000 S+ 1:57PM 0:00.02 puma: cluster worker: 10712
schneems 10712 0.0 0.3 2478612 24596 s000 S+ 1:57PM 0:00.47 ruby /Users/schneems/.gem/ruby/2.1.1/bin/puma test/fixtures/app.ru -w 3 -t 1:1 -p 0 -e development
However you'll notice as I mentioned before the PID returned 10711 is not listed. It is actually a (sh) process
$ ps -p 10711
PID TTY TIME CMD
10711 ttys000 0:00.00 (sh)
Now back to Ruby land. When I try to terminate puma via Process.kill('TERM', pid) the shell process is terminated but puma continues to run in the background. Puma never receives SIGTERM.
puts pid
10711
Process.kill("TERM", pid)
=> 1
Process.wait(pid)
Is there another way to consistently spawn and kill puma from inside Ruby? Any clues why the spawned process isn't sending signals to it's child (puma). Is this a bug in my OS, Ruby, or Puma? Perhaps this is expected behavior?
This is my app.ru https://gist.github.com/schneems/18c216cc159772c80361
The quickest hack is to use exec:
# Bad quick fix
pid = Process.spawn "exec bundle exec puma test/fixtures/app.ru -w 3 -t 1:1 -p 0 -e development > test/logs/puma_1961_1393875906.917352.log"
This replaces the shell with the invoked process.
However, you should never use Process.spawn with a shell command. When combined with variables, it leads to surprising, insecure and unpredictable behavior. Instead, you should separate the arguments and set up the redirection yourself:
# Good solution
pid = Process.spawn("bundle", "exec", "puma", "test/fixtures/app.ru", "-w", "3", "-t", "1:1", "-p", "0", "-e", "development", :out=>"test/logs/puma_1961_1393875906.917352.log")
This avoids the shell in the first place.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With