ry / ebb fork watch download tarball
public
Description: web server
Homepage: http://ebb.rubyforge.org
Clone URL: git://github.com/ry/ebb.git
Ryan Dahl (author)
Thu Mar 06 08:17:42 -0800 2008
commit  9b05bbd9644cc6bbdd4f845196a715f21d91fbb2
tree    1c8e228362031077337ea044e521b56df8befb59
parent  98ceeb9fbacb61dd7dc61847d4c88f97420cddef
ebb / ruby_lib / daemonizable.rb
100644 99 lines (85 sloc) 3.011 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# Simplified version of Thin::Daemonizable by Marc-André Cournoyer
 
module Kernel
  unless respond_to? :daemonize # Already part of Ruby 1.9, yeah!
    # Turns the current script into a daemon process that detaches from the console.
    # It can be shut down with a TERM signal. Taken from ActiveSupport.
    def daemonize
      exit if fork # Parent exits, child continues.
      Process.setsid # Become session leader.
      exit if fork # Zap session leader. See [1].
      Dir.chdir "/" # Release old working directory.
      File.umask 0000 # Ensure sensible umask. Adjust as needed.
      STDIN.reopen "/dev/null" # Free file descriptors and
      STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
      STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
      trap("TERM") { exit }
    end
  end
end
 
module Process
  # Returns +true+ the process identied by +pid+ is running.
  def running?(pid)
    Process.getpgid(pid) != -1
  rescue Errno::ESRCH
    false
  end
  module_function :running?
end
 
# Moadule included in classes that can be turned into a daemon.
# Handle stuff like:
# * storing the PID in a file
# * redirecting output to the log file
# * killing the process gracefully
module Daemonizable
  attr_accessor :pid_file, :log_file, :timeout
  
  def pid
    File.exist?(pid_file) ? open(pid_file).read : nil
  end
  
  # Turns the current script into a daemon process that detaches from the console.
  def daemonize
    raise ArgumentError, 'You must specify a pid_file to deamonize' unless @pid_file
    
    pwd = Dir.pwd # Current directory is changed during daemonization, so store it
    Kernel.daemonize
    Dir.chdir pwd
    
    trap('HUP', 'IGNORE') # Don't die upon logout
    
    # Redirect output to the logfile
    [STDOUT, STDERR].each { |f| f.reopen @log_file, 'a' } if @log_file
    
    write_pid_file
    at_exit do
      log ">> Exiting!"
      remove_pid_file
    end
  end
  
  # Kill the process which PID is stored in +pid_file+.
  def self.kill(pid_file, timeout=60)
    if pid = open(pid_file).read
      pid = pid.to_i
      print "Sending INT signal to process #{pid} ... "
      begin
        Process.kill('INT', pid)
        Timeout.timeout(timeout) do
          sleep 0.1 while Process.running?(pid)
        end
      rescue Timeout::Error
        print "timeout, Sending KILL signal ... "
        Process.kill('KILL', pid)
      end
      puts "stopped!"
    else
      puts "Can't stop process, no PID found in #{@pid_file}"
    end
  rescue Errno::ESRCH # No such process
    puts "process not found!"
  ensure
    File.delete(pid_file) rescue nil
  end
  
  private
  
  def remove_pid_file
    File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
  end
  
  def write_pid_file
    log ">> Writing PID to #{@pid_file}"
    open(@pid_file,"w+") { |f| f.write(Process.pid) }
    File.chmod(0644, @pid_file)
  end
end