1 # Copyright (c) 2007 Marc-André Cournoyer
5 unless respond_to? :daemonize # Already part of Ruby 1.9, yeah!
6 # Turns the current script into a daemon process that detaches from the console.
7 # It can be shut down with a TERM signal. Taken from ActiveSupport.
9 exit if fork # Parent exits, child continues.
10 Process.setsid # Become session leader.
11 exit if fork # Zap session leader. See [1].
12 Dir.chdir "/" # Release old working directory.
13 File.umask 0000 # Ensure sensible umask. Adjust as needed.
14 STDIN.reopen "/dev/null" # Free file descriptors and
15 STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
16 STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
23 # Returns +true+ the process identied by +pid+ is running.
25 Process.getpgid(pid) != -1
29 module_function :running?
32 # Module included in classes that can be turned into a daemon.
34 # * storing the PID in a file
35 # * redirecting output to the log file
36 # * changing processs privileges
37 # * killing the process gracefully
39 attr_accessor :pid_file, :log_file
41 def self.included(base)
42 base.extend ClassMethods
46 File.exist?(pid_file) ? open(pid_file).read : nil
49 # Turns the current script into a daemon process that detaches from the console.
51 raise ArgumentError, 'You must specify a pid_file to deamonize' unless @pid_file
53 pwd = Dir.pwd # Current directory is changed during daemonization, so store it
54 super # Calls Kernel#daemonize
57 trap('HUP', 'IGNORE') # Don't die upon logout
59 # Redirect output to the logfile
60 [STDOUT, STDERR].each { |f| f.reopen @log_file, 'a' } if @log_file
69 # Change privileges of the process
70 # to the specified user and group.
71 def change_privilege(user, group=user)
72 log ">> Changing process privilege to #{user}:#{group}"
74 uid, gid = Process.euid, Process.egid
75 target_uid = Etc.getpwnam(user).uid
76 target_gid = Etc.getgrnam(group).gid
78 if uid != target_uid || gid != target_gid
79 # Change process ownership
80 Process.initgroups(user, target_gid)
81 Process::GID.change_privilege(target_gid)
82 Process::UID.change_privilege(target_uid)
84 rescue Errno::EPERM => e
85 log "Couldn't change user and group to #{user}:#{group}: #{e}"
89 # Kill the process which PID is stored in +pid_file+.
90 def kill(pid_file, timeout=60)
91 if pid = open(pid_file).read
93 print "Sending INT signal to process #{pid} ... "
95 Process.kill('INT', pid)
96 Timeout.timeout(timeout) do
97 sleep 0.1 while Process.running?(pid)
100 print "timeout, Sending KILL signal ... "
101 Process.kill('KILL', pid)
105 puts "Can't stop process, no PID found in #{@pid_file}"
107 rescue Errno::ESRCH # No such process
108 puts "process not found!"
110 File.delete(pid_file) rescue nil
116 File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
120 log ">> Writing PID to #{@pid_file}"
121 FileUtils.mkdir_p File.dirname(@pid_file)
122 open(@pid_file,"w") { |f| f.write(Process.pid) }
123 File.chmod(0644, @pid_file)