How can I create a daemon with Thor (ruby)? - ruby

I would like to use the popular Thor gem to create a daemonized task. My Thor class looks like this:
require 'rubygems'
require 'daemons'
require 'thor'
class CLI < Thor
desc "start", "Startup the App"
method_option :daemonize, :aliases => "-d", :default => false, :type => :boolean, :banner => "Run as daemon"
def start
run_app(options[:daemonize])
end
desc "stop", "Stop the daemon"
def stop
stop_app
end
no_tasks {
def run_app(run_as_daemon)
# Run the application code
Daemons.daemonize if run_as_daemon
# loop until stopped or interrupted
# ...
end
def stop_app
#stop the app
end
}
end
So here I've setup a basic thor class with two tasks, start and stop. I'm also, currently using the Daemons gem, but that isn't required. The part that I'm struggling with is that when this app runs as "run_thor_app.rb start" everything runs just fine. Obviously the stop task isn't needed in this instance. But when I run "run_thor_app.rb start -d" the app runs until Daemons.daemonize runs and then it quits. Checking the running processes shows that nothing is running in the background.
Even if something were running, I wouldn't know how to approach the stop task. For example, how do you detect that the app is running as a daemon and stop it. I've looked at Daemons::Monitor, but the documentation isn't clear on how that works and when I tried it, it didn't work.
It seems to me that this would be a good use case for something that is built into Thor, but searching through the code on github hasn't revealed anything to me. Maybe I just missed it somewhere. In any case, I think it would be good to document a best practice or a pattern for handling daemons with Thor for others to reference.

The way you usually manage daemon processes is by having them write their PID in a file. This makes it possible for another process to discover the daemon's PID, and kill it (or send some other signal).
Your code should work. I tried a bare bones script that used the deamons gem, and it took me a few tries until I found the deamonized process. I figured it would get the same name as the parent process, or something similar, but instead it's name was "self". Remember that the daemonized process will no longer write to STDOUT.
Anyway, try this:
# set up everything
# then daemonize
Daemons.daemonize
# and write a pid file
File.open('/tmp/mydaemon.pid', 'w') { |f| f.puts(Process.pid) }
loop do
# do something
# this loop is important, if the script ends the daemon dies
end
and check the /tmp/mydaemon.pid file for the PID. Then run ps ax | grep x where x is the PID. Run cat /tmp/mydaemon.pid | xargs kill` to kill the daemon.
I think the daemons' gem has some helpers for managing PidFiles, check out PidFile in http://rubydoc.info/gems/daemons/1.1.0/frames

Related

kill ruby daemon process within a rake task

I am using needing to start and stop a server during some rake tasks. I am using the following code:
task :start_server do
job = fork do
system `http-server ./_site -p 4000`
end
Process.detach(job)
#pid = Process.pid
end
task :stop_server do
puts "stopping server"
system Process.kill('QUIT', #pid)
end
The start works fine but I cannot get it to stop.
I am calling these tasks within capistrano deploy e.g. after 'pdf:generate_pdf', 'pdf:stop_server'
I dont get an error but I can still see pages being served by the web server.
Is there a better way to end the process?
I have found that this command stops the http-server perfectly.
task :stop_server do
system("pkill -f http-server")
end

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).

Use whenever with sinatra

I'm trying to get whenever to work with sinatra. When I run the whenever command, I get the generated cron tab. But the problem is, that in my sinatra app, I don't have a script/runner file, which is present in Rails.
How do I get this runner, or is there a whenever command to generate one?
thx!
You can use a rake task in place of script/runner. The Whenever gem supports defining the job via a rake task (and more in fact)
Sample:
# config/schedule.rb
every 3.hours do
rake "destroy_all"
end
and in your Rakefile: (for lack of good examples)
task :destroy_all do
puts "Do not do this"
# sh "rm -rf ."
end

Manage a Thin server through a Ruby script

I have a project where I have a sinatra app and I want to launch it with thin through a admin ruby script file. I want to be able to start, stop and restart it, also being able to daemonize it if asked to. This is, I want to have something like this in my script:
bin/myscript
require 'MyCLI'
MyCLI.new(ARGV).run
lib/mycli.rb
class MyCLI
# instantiate and other methods (inspired by thin runner)
...
def run
# parse commands and options
...
# then process command
case #command
when 'start'
#server = Thin::Server.new(host, port, MyModule::MyAppClass)
#server.start
when 'stop'
# ?
when 'restart'
# ?
else
raise "Unknown command"
end
end
end
But I'm struggling with some problems,
I need to daemonize it or not, depending on some command option and cant find if this is possible to do passing some parameter to #new after reading docs and digging in some of the code.
Stopping would be as easy as #server.stop, but as my script instantiates a mycli object at each command line request, I do not have a single object so #server vanishes after the start request, so I think that the only solution would be to control the PID (right??), but cant find how thin manages that. Also, running it in foreground would not work with this pid approach I presume.
What would be the proper way to restart it?
Has anyone a best solution for this?
I end up using Rack::Server.start(app, host, port, env, daemonize(Y/N), pid_file).
It works great, and it will pick up the thin handler if available.

How to ensure a rake task only running a process at a time

I use crontab to invoke rake task at some time for example: every 3 hour
I want to ensure that when crontab ready to execute the rake task
it can check the rake task is running. if it is so don't execute.
how to do this. thanks.
I'll leave this here because I think it's useful:
task :my_task do
pid_file = '/tmp/my_task.pid'
raise 'pid file exists!' if File.exists? pid_file
File.open(pid_file, 'w'){|f| f.puts Process.pid}
begin
# execute code here
ensure
File.delete pid_file
end
end
You could use a lock file for this. When the task runs, try to grab the lock and run the rake task if you get the lock. If you don't get the lock, then don't run rake; you might want to log an error or warning somewhere too or you can end up with your rake task not doing anything for weeks or months before you know about it. When rake exits, unlock the lock file.
Something like RAA might help but I haven't used it so maybe not.
You could also use a PID file. You'd have a file somewhere that holds the rake processes process ID. Before starting rake, you read the PID from that file and see if the process is running; if it isn't then start up rake and write its PID to the PID file. When rake exists, delete the PID file. You'd want to combine this with locking on the PID file if you want to be really strict but this depends on your particular situation.
All you need is a gem named pidfile.
Add this to your Gemfile:
gem 'pidfile', '>= 0.3.0'
And the task could be:
desc "my task"
task :my_task do |t|
PidFile.new(piddir: "/var/lock", pidfile: "#{t.name}.pid")
# do something
end

Resources