module RSence::Daemon
@private The Controller module handles the daemonizing
operations of the process referred to as +daemon+
Public Class Methods
Main entry point called from the daemon process passing self
as daemon
.
# File lib/rsence/daemon.rb, line 241 def self.daemonize( daemon ) # Uses the command-line tool command to decide what to do. case RSence.cmd when :run run( daemon ) when :start start( daemon ) when :stop stop( daemon ) when :restart stop( daemon ) start( daemon ) when :save save( daemon ) end end
Removes a signal response file.
# File lib/rsence/daemon.rb, line 71 def self.delete_signal_response( daemon, signal ) pid_fn = daemon.pid_fn RSence::SIGComm.delete_signal_response( pid_fn ) end
Removes all pid files that were left around if a process died unexpectedly.
# File lib/rsence/daemon.rb, line 140 def self.delete_stale_pids( daemon ) ( pid_fn_path, pid_fn_name ) = File.split( daemon.pid_fn ) Dir.entries( pid_fn_path ).each do | item_fn | item_path = File.join( pid_fn_path, item_fn ) if item_fn.start_with?( pid_fn_name ) and File.file?( item_path ) puts "Stale pid file (#{item_fn}), removing.." if RSence.args[:verbose] File.delete( item_path ) end end end
Creates pid file and traps signal.
# File lib/rsence/daemon.rb, line 152 def self.init_pid( daemon ) if RSence.pid_support? is_running = status( daemon ) if is_running puts "RSence is already running." puts "Stop the existing process first: see 'rsence help stop'" if RSence.launch_pid != Process.pid Process.kill( 'INT', RSence.launch_pid ) end exit elsif not is_running delete_stale_pids( daemon ) end trap_signals( daemon ) pid = Process.pid write_pid( daemon, pid ) return pid else trap_windows_signals( daemon ) return false end end
Reads the process id from disk, the pid_fn method of the daemon contains the name of the pid file. Returns nil on errors.
# File lib/rsence/daemon.rb, line 37 def self.read_pid( daemon ) File.read( daemon.pid_fn ).to_i rescue nil end
# File lib/rsence/daemon.rb, line 41 def self.responds?( daemon ) wait_signal_response( daemon, RSence.info_signal_name ) end
Inits the pid, signals and then starts the server in the foreground
# File lib/rsence/daemon.rb, line 176 def self.run( daemon ) init_pid( daemon ) daemon.run exit end
Sends the PWR signal to the process, which in turn calls the save method of the daemon.
# File lib/rsence/daemon.rb, line 203 def self.save( daemon ) status_ = status( daemon ) if status_ if wait_signal_response( daemon, 'PWR', 10, 'saving.', 'saved', 0.3 ) puts "Session data saved." else puts "Warning: saving timed out! Session data not saved." end elsif status_ == false puts "Warning, no such process (#{pid}) running: unable to save." elsif status_ == nil puts "No pid file: unable to save." else throw "Unexpected process status: #{status_.inspect}" end end
Inits the pid, signals and then starts the daemon in the background and waits until the daemon sends the TERM signal.
# File lib/rsence/daemon.rb, line 184 def self.start( daemon ) fork do exit if fork init_pid( daemon ) daemon.start end Signal.trap('INT') do puts "RSence startup failed. Please inspect the log and/or run in debug mode." exit end Signal.trap('TERM') do puts "RSence is online at http://#{daemon.addr}:#{daemon.port}/" exit end sleep 1 while true end
Redirects standard input and errors to the log files
# File lib/rsence/daemon.rb, line 54 def self.start_logging( daemon ) outpath = "#{daemon.log_fn}.stdout" errpath = "#{daemon.log_fn}.stderr" STDOUT.reopen( outpath, (File.exist?( outpath ) ? 'a' : 'w') ) STDOUT.sync = true STDERR.reopen( errpath, (File.exist?( errpath ) ? 'a' : 'w') ) STDERR.sync = true end
Reads the pid file and calls the process. Returns true if the process responds, false otherwise (no process)
# File lib/rsence/daemon.rb, line 47 def self.status( daemon ) pid = read_pid( daemon ) return nil if not pid return responds?( daemon ) end
Sends the TERM signal to the process, which in turn calls the stop method of the daemon
# File lib/rsence/daemon.rb, line 222 def self.stop( daemon )#,is_restart=false) status_ = status( daemon ) if status_ if wait_signal_response( daemon, 'TERM', 10, 'killing.', 'killed', 0.3 ) puts "RSence is terminated now." else puts "Warning: termination timed out!" puts "RSence might still be running, please ensure manually." end elsif status_ == false puts "Warning, no such process (#{read_pid(daemon)}) running." elsif status_ == nil puts "Warning, no pid file (process not running)." else throw "Unexpected process status: #{status_.inspect}" end end
Traps common POSIX signals
# File lib/rsence/daemon.rb, line 99 def self.trap_signals( daemon ) # Triggered with 'kill -USR1 `cat rsence.pid`' Signal.trap( 'USR1' ) do daemon.usr1 write_signal_response( daemon, 'USR1' ) end Signal.trap( 'USR2' ) do daemon.usr2 write_signal_response( daemon, 'USR2' ) end # Signal.trap( 'PWR' ) do # daemon.pwr # write_signal_response( daemon, 'PWR' ) # end Signal.trap( 'ALRM' ) do daemon.alrm write_signal_response( daemon, 'ALRM' ) end Signal.trap( RSence.info_signal_name ) do daemon.info write_signal_response( daemon, RSence.info_signal_name ) end ['INT', 'TERM', 'KILL'].each do | signal | Signal.trap( signal ) do puts "RSence killed with signal #{signal.inspect}" if RSence.args[:verbose] daemon.usr1 daemon.stop delete_stale_pids( daemon ) write_signal_response( daemon, signal ) puts "Shutdown complete." exit end end Signal.trap('HUP') do daemon.stop daemon.start end end
Signal trapping for windows. The only supported POSIX signal trappable seems to be INT (CTRL-C).
# File lib/rsence/daemon.rb, line 86 def self.trap_windows_signals( daemon ) ['INT'].each do | signal | Signal.trap( signal ) do puts "RSence killed with signal #{signal.inspect}" if RSence.args[:verbose] daemon.usr1 daemon.stop puts "Shutdown complete." exit end end end
Waits for a signal response.
# File lib/rsence/daemon.rb, line 77 def self.wait_signal_response( daemon, signal, timeout = 10, debug_pre = false, debug_suf = false, sleep_secs = 0.2 ) pid = read_pid( daemon ) pid_fn = daemon.pid_fn return RSence::SIGComm.wait_signal_response( pid, pid_fn, signal, timeout, debug_pre, debug_suf, sleep_secs ) end
Writes the process id to disk, the pid_fn method of the daemon contains the name of the pid file.
# File lib/rsence/daemon.rb, line 29 def self.write_pid( daemon, pid ) File.open( daemon.pid_fn, 'w' ) do |pidfile| pidfile.write( pid ) end end
Writes a signal response file containing the pid.
# File lib/rsence/daemon.rb, line 64 def self.write_signal_response( daemon, signal ) pid = Process.pid.to_s pid_fn = daemon.pid_fn RSence::SIGComm.write_signal_response( pid, pid_fn, signal ) end