Ruby is very powerful language, not only for web development as many can think for the first time. It is also providing all necessary resources to build system utilities and daemons. So, this post exactly about it.
Long time ago i was looking for some tool to write simple daemon in a short term. I didn`t choose C just because i was able to develop program only on special computer (i mean “production-like” environment) and the problem wasnt critical to resources. In other words – this daemon was working only few days to complete the task. Ok, lets see what we`ve got:
#!/usr/bin/ruby # --------------------------------------------------------------------- # MODULES # --------------------------------------------------------------------- require 'daemonize' include Daemonize include Process # --------------------------------------------------------------------- # CONFIGURATION # --------------------------------------------------------------------- $daemon = { :name => "Test Daemon", # daemon name :abbr => "testd", # daemon abbreviation :author => "(c) 2008 author", # daemon author :version => "0.1", # actual version :file_log => "/var/log/testdaemon.log", # log path :file_pid => "/var/run/testdaemon.pid", # process id path :delay_sleep => 1, # seconds :user => 'tux', # working data user :grp => 'tux', # working data group :background => false, # background mode :work => true # daemon work flag } $daemon_log = nil $daemon_pid = nil # --------------------------------------------------------------------- def daemon_log(str) puts "[#{Time.now.strftime("%m/%d/%Y-%H:%M:%S")}] #{str}" end def daemon_terminate $daemon[:work] = false end def daemon_stop daemon_log("Stopping working process...") $daemon_pid.close File.delete($daemon[:file_pid]) end def daemon_start if File.exist?($daemon[:file_pid]) then daemon_log("Process already running. If it`s not - remove the pid file") exit end daemon_log("Starting process...") daemonize if $daemon[:background] begin $daemon_pid = File.new($daemon[:file_pid],"w") rescue Errno::EACCES daemon_log("Cannot create PID file. Check the permissions and try again!") $daemon_pid = nil exit end daemon_work end def daemon_work if ($daemon_pid) then $daemon_pid.sync = true $daemon_pid.puts(Process.pid.to_s) begin while $daemon[:work] do daemon_log("Daemon working") daemon_handle_signals sleep($daemon[:delay_sleep]) end rescue Exception => e daemon_log("Error: #{e.message}") end daemon_stop end end def daemon_handle_signals # termination signal Signal.trap("TERM") do daemon_log("TERM signal received.") daemon_terminate end # kill signal Signal.trap("KILL") do daemon_log("KILL signal received.") daemon_terminate end # keyboard interruption Signal.trap("INT") do daemon_log("SIGINT signal received.") daemon_terminate end Signal.trap("TSTP") do daemon_log("SIGTSTP signal received.") end end def daemon_show_version puts "#{$daemon[:name]} v#{$daemon[:version]} #{$daemon[:author]}" end def daemon_show_usage daemon_show_version puts "Usage:" puts " -b, --background work in background mode" puts " -v, --version view version of daemon" puts " -h, --help view this help" end def daemon_parse_opts return true if ARGV.length == 0 case ARGV[0] when '-b', '--background' $daemon[:background] = true; return true when '-v', '--version' daemon_show_version when '-h', '--help' daemon_show_usage else puts "Invalid argument: #{ARGV[0]}" if !ARGV[0].nil? daemon_show_usage end return false end def daemon_main daemon_start if daemon_parse_opts end daemon_main
This is just a basic structure of daemon, it supports background mode. I removed all unimportant information and left only main program cycles. As you can see, the entry point of whole program – daemon_main procedure. It parsing the command line parameters. In this example there is no required parameters, so daemon will run in basic mode (not background). To enable background you should specify the -d (or –background) option. Also, very important – this example needs to be executed under root or other user that have access to /var/run. Other way, PID file path can be changed to whatever you want (file_pid key). All daemon configuration variables stored in array $daemon. Also, it supports system signals handling, like SIGTERM or SIGKILL (all ruby signal constants u can explore here).
I think that`s it. Not a lot, but very simple. Download script: http://files.sosedoff.com/b57415aa/