module RSence::Daemon

@private The Controller module handles the daemonizing

operations of the process referred to as +daemon+

Public Class Methods

daemonize( daemon ) click to toggle source

Main entry point called from the daemon process passing self as daemon.

# File lib/rsence/daemon.rb, line 249
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
delete_signal_response( daemon, signal ) click to toggle source

Removes a signal response file.

# File lib/rsence/daemon.rb, line 79
def self.delete_signal_response( daemon, signal )
  pid_fn = daemon.pid_fn
  RSence::SIGComm.delete_signal_response( pid_fn )
end
delete_stale_pids( daemon ) click to toggle source

Removes all pid files that were left around if a process died unexpectedly.

# File lib/rsence/daemon.rb, line 148
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
init_pid( daemon ) click to toggle source

Creates pid file and traps signal.

# File lib/rsence/daemon.rb, line 160
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
read_pid( daemon ) click to toggle source

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 45
def self.read_pid( daemon )
  File.read( daemon.pid_fn ).to_i rescue nil
end
responds?( daemon ) click to toggle source
# File lib/rsence/daemon.rb, line 49
def self.responds?( daemon )
  wait_signal_response( daemon, RSence.info_signal_name )
end
run( daemon ) click to toggle source

Inits the pid, signals and then starts the server in the foreground

# File lib/rsence/daemon.rb, line 184
def self.run( daemon )
  init_pid( daemon )
  daemon.run
  exit
end
save( daemon ) click to toggle source

Sends the PWR signal to the process, which in turn calls the save method of the daemon.

# File lib/rsence/daemon.rb, line 211
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
start( daemon ) click to toggle source

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 192
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
start_logging( daemon ) click to toggle source

Redirects standard input and errors to the log files

# File lib/rsence/daemon.rb, line 62
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
status( daemon ) click to toggle source

Reads the pid file and calls the process. Returns true if the process responds, false otherwise (no process)

# File lib/rsence/daemon.rb, line 55
def self.status( daemon )
  pid = read_pid( daemon )
  return nil if not pid
  return responds?( daemon )
end
stop( daemon ) click to toggle source

Sends the TERM signal to the process, which in turn calls the stop method of the daemon

# File lib/rsence/daemon.rb, line 230
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
trap_signals( daemon ) click to toggle source

Traps common POSIX signals

# File lib/rsence/daemon.rb, line 107
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
trap_windows_signals( daemon ) click to toggle source

Signal trapping for windows. The only supported POSIX signal trappable seems to be INT (CTRL-C).

# File lib/rsence/daemon.rb, line 94
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
wait_signal_response( daemon, signal, timeout = 10, debug_pre = false, debug_suf = false, sleep_secs = 0.2 ) click to toggle source

Waits for a signal response.

# File lib/rsence/daemon.rb, line 85
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
write_pid( daemon, pid ) click to toggle source

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 37
def self.write_pid( daemon, pid )
  File.open( daemon.pid_fn, 'w' ) do |pidfile|
    pidfile.write( pid )
  end
end
write_signal_response( daemon, signal ) click to toggle source

Writes a signal response file containing the pid.

# File lib/rsence/daemon.rb, line 72
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