| Module | Daemonize |
| In: |
lib/daemons/daemonize.rb
|
Call a given block as a daemon
# File lib/daemons/daemonize.rb, line 39
39: def call_as_daemon(block, logfile_name = nil, app_name = nil)
40: # we use a pipe to return the PID of the daemon
41: rd, wr = IO.pipe
42:
43: if tmppid = safefork
44: # in the parent
45:
46: wr.close
47: pid = rd.read.to_i
48: rd.close
49:
50: Process.waitpid(tmppid)
51:
52: return pid
53: else
54: # in the child
55:
56: rd.close
57:
58: # Detach from the controlling terminal
59: unless sess_id = Process.setsid
60: raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
61: end
62:
63: # Prevent the possibility of acquiring a controlling terminal
64: trap 'SIGHUP', 'IGNORE'
65: exit if pid = safefork
66:
67: wr.write Process.pid
68: wr.close
69:
70: $0 = app_name if app_name
71:
72: # Release old working directory
73: Dir.chdir "/"
74:
75: close_io()
76:
77: redirect_io(logfile_name)
78:
79: block.call
80:
81: exit
82: end
83: end
# File lib/daemons/daemonize.rb, line 118
118: def close_io()
119: # Make sure all input/output streams are closed
120: # Part I: close all IO objects (except for STDIN/STDOUT/STDERR)
121: ObjectSpace.each_object(IO) do |io|
122: unless [STDIN, STDOUT, STDERR].include?(io)
123: begin
124: unless io.closed?
125: io.close
126: end
127: rescue ::Exception
128: end
129: end
130: end
131:
132: # Make sure all input/output streams are closed
133: # Part II: close all file decriptors (except for STDIN/STDOUT/STDERR)
134: ios = Array.new(8192) {|i| IO.for_fd(i) rescue nil}.compact
135: ios.each do |io|
136: next if io.fileno < 3
137: io.close
138: end
139: end
Transform the current process into a daemon
# File lib/daemons/daemonize.rb, line 88
88: def daemonize(logfile_name = nil, app_name = nil)
89: # Split rand streams between spawning and daemonized process
90: srand
91:
92: # Fork and exit from the parent
93: safefork and exit
94:
95: # Detach from the controlling terminal
96: unless sess_id = Process.setsid
97: raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
98: end
99:
100: # Prevent the possibility of acquiring a controlling terminal
101: trap 'SIGHUP', 'IGNORE'
102: exit if pid = safefork
103:
104: $0 = app_name if app_name
105:
106: # Release old working directory
107: Dir.chdir "/"
108:
109: close_io()
110:
111: redirect_io(logfile_name)
112:
113: return sess_id
114: end
Free STDIN/STDOUT/STDERR file descriptors and point them somewhere sensible
# File lib/daemons/daemonize.rb, line 145
145: def redirect_io(logfile_name)
146: begin; STDIN.reopen "/dev/null"; rescue ::Exception; end
147:
148: if logfile_name
149: begin
150: STDOUT.reopen logfile_name, "a"
151: File.chmod(0644, logfile_name)
152: STDOUT.sync = true
153: rescue ::Exception
154: begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
155: end
156: else
157: begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
158: end
159:
160: begin; STDERR.reopen STDOUT; rescue ::Exception; end
161: STDERR.sync = true
162: end
Try to fork if at all possible retrying every 5 sec if the maximum process limit for the system has been reached
# File lib/daemons/daemonize.rb, line 5
5: def safefork
6: tryagain = true
7:
8: while tryagain
9: tryagain = false
10: begin
11: if pid = fork
12: return pid
13: end
14: rescue Errno::EWOULDBLOCK
15: sleep 5
16: tryagain = true
17: end
18: end
19: end
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
# File lib/daemons/daemonize.rb, line 26
26: def simulate(logfile_name = nil)
27: # Release old working directory
28: Dir.chdir "/"
29:
30: close_io()
31:
32: # Free STDIN and point them somewhere sensible
33: begin; STDIN.reopen "/dev/null"; rescue ::Exception; end
34: end