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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
module Daemonize
# Try to fork if at all possible retrying every 5 sec if the
# maximum process limit for the system has been reached
def safefork
tryagain = true
while tryagain
tryagain = false
begin
if pid = fork
return pid
end
rescue Errno::EWOULDBLOCK
sleep 5
tryagain = true
end
end
end
module_function :safefork
# Simulate the daemonization process (:ontop mode)
# NOTE: $stdout and $stderr will not be redirected to the logfile,
# because in :ontop mode, we normally want to see the output
def simulate(logfile_name = nil, app_name = nil)
$0 = app_name if app_name
# Release old working directory
Dir.chdir '/'
close_io
# Free $stdin and point it to somewhere sensible
begin; $stdin.reopen '/dev/null'; rescue ::Exception; end
# Split rand streams between spawning and daemonized process
srand
end
module_function :simulate
# Call a given block as a daemon
def call_as_daemon(block, logfile_name = nil, app_name = nil)
# we use a pipe to return the PID of the daemon
rd, wr = IO.pipe
if tmppid = safefork
# in the parent
wr.close
pid = rd.read.to_i
rd.close
Process.waitpid(tmppid)
return pid
else
# in the child
rd.close
# Detach from the controlling terminal
unless Process.setsid
fail Daemons.RuntimeException.new('cannot detach from controlling terminal')
end
# Prevent the possibility of acquiring a controlling terminal
trap 'SIGHUP', 'IGNORE'
exit if pid = safefork
wr.write Process.pid
wr.close
$0 = app_name if app_name
# Release old working directory
Dir.chdir '/'
close_io
redirect_io(logfile_name)
# Split rand streams between spawning and daemonized process
srand
block.call
exit
end
end
module_function :call_as_daemon
# Transform the current process into a daemon
def daemonize(logfile_name = nil, app_name = nil)
# Fork and exit from the parent
safefork && exit
# Detach from the controlling terminal
unless sess_id = Process.setsid
fail Daemons.RuntimeException.new('cannot detach from controlling terminal')
end
# Prevent the possibility of acquiring a controlling terminal
trap 'SIGHUP', 'IGNORE'
exit if safefork
$0 = app_name if app_name
# Release old working directory
Dir.chdir '/'
close_io
redirect_io(logfile_name)
# Split rand streams between spawning and daemonized process
srand
sess_id
end
module_function :daemonize
def close_io
# Make sure all input/output streams are closed
# Part I: close all IO objects (except for $stdin/$stdout/$stderr)
ObjectSpace.each_object(IO) do |io|
unless [$stdin, $stdout, $stderr].include?(io)
io.close rescue nil
end
end
# Make sure all input/output streams are closed
# Part II: close all file decriptors (except for $stdin/$stdout/$stderr)
3.upto(8192) do |i|
IO.for_fd(i).close rescue nil
end
end
module_function :close_io
# Free $stdin/$stdout/$stderr file descriptors and
# point them somewhere sensible
def redirect_io(logfile_name)
begin; $stdin.reopen '/dev/null'; rescue ::Exception; end
if logfile_name == 'SYSLOG'
# attempt to use syslog via syslogio
begin
require 'syslogio'
$stdout = ::Daemons::SyslogIO.new($0, :local0, :info, $stdout)
$stderr = ::Daemons::SyslogIO.new($0, :local0, :err, $stderr)
# error out early so we can fallback to null
$stdout.puts "no logfile provided, output redirected to syslog"
rescue ::Exception
# on unsupported platforms simply reopen /dev/null
begin; $stdout.reopen '/dev/null'; rescue ::Exception; end
begin; $stderr.reopen '/dev/null'; rescue ::Exception; end
end
elsif logfile_name
$stdout.reopen logfile_name, 'a'
File.chmod(0644, logfile_name)
$stdout.sync = true
begin; $stderr.reopen $stdout; rescue ::Exception; end
$stderr.sync = true
else
begin; $stdout.reopen '/dev/null'; rescue ::Exception; end
begin; $stderr.reopen '/dev/null'; rescue ::Exception; end
end
end
module_function :redirect_io
end
|