Daemonize

Daemonize Library

February. 4, 2005 Travis Whitton

Daemonize allows you to easily modify any existing Ruby program to run as a daemon. See README.rdoc for more details.

How to install

  1. su to root

  2. ruby install.rb

build the docs if you want to

  1. rdoc —main README.rdoc daemonize.rb README.rdoc

Copying

The Daemonize extension module is copywrited free software by Travis Whitton . You can redistribute it under the terms specified in the COPYING file of the Ruby distribution.

WARRANTY

THIS SOFTWARE IS PROVIDED “AS IS” AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.


Purpose

Daemonize is a module derived from Perl’s Proc::Daemon module. This module allows you to easily modify any existing Ruby program to run as a daemon. A daemon is a process that runs in the background with no controlling terminal. Generally servers (like FTP and HTTP servers) run as daemon processes. Note, do not make the mistake that a daemon == server. Converting a program to a daemon by hand is a relatively simple process; however, this module will save you the effort of repeatedly looking up the procedure, and it will also insure that your programs are daemonized in the safest and most corrects fashion possible.

Procedure

The Daemonize module does the following:

Forks a child and exits the parent process.

Becomes a session leader (which detaches the program from the controlling terminal).

Forks another child process and exits first child. This prevents the potential of acquiring a controlling terminal.

Changes the current working directory to “/”.

Clears the file creation mask.

Closes file descriptors.

Example usage

Using the Daemonize module is extremely simple:

    require 'daemonize'

    class TestDaemon
      include Daemonize

      def initialize
        daemonize()
        loop do
          # do some work here
        end
      end
    end

Credits

Daemonize was written by Travis Whitton and is based on Perl’s Proc::Daemonize, which was written by Earl Hood. The above documentation is also partially borrowed from the Proc::Daemonize POD documentation.

Constants

VERSION

Public Class Methods

call_as_daemon(block, logfile_name = nil, app_name = nil) click to toggle source
     # File lib/daemons/daemonize.rb, line 142
142:   def call_as_daemon(block, logfile_name = nil, app_name = nil)
143:     rd, wr = IO.pipe
144:     
145:     if tmppid = safefork
146:       # parent
147:       wr.close
148:       pid = rd.read.to_i
149:       rd.close
150:       
151:       Process.waitpid(tmppid)
152:       
153:       return pid
154:     else
155:       # child
156:       
157:       rd.close
158:       
159:       # Detach from the controlling terminal
160:       unless sess_id = Process.setsid
161:         raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
162:       end
163:   
164:       # Prevent the possibility of acquiring a controlling terminal
165:       #if oldmode.zero?
166:         trap 'SIGHUP', 'IGNORE'
167:         exit if pid = safefork
168:       #end
169:   
170:       wr.write Process.pid
171:       wr.close
172:       
173:       $0 = app_name if app_name
174:       
175:       Dir.chdir "/"   # Release old working directory
176:       File.umask 0000 # Insure sensible umask
177:   
178:       # Make sure all file descriptors are closed
179:       ObjectSpace.each_object(IO) do |io|
180:         unless [STDIN, STDOUT, STDERR].include?(io)
181:           begin
182:             unless io.closed?
183:               io.close
184:             end
185:           rescue ::Exception
186:           end
187:         end
188:       end
189:       
190:       ios = Array.new(8192){|i| IO.for_fd(i) rescue nil}.compact
191:       ios.each do |io|
192:         next if io.fileno < 3
193:         io.close
194:       end
195: 
196:   
197:       redirect_io(logfile_name)  
198:     
199:       block.call
200:       
201:       exit
202:     end
203:   end
daemonize(logfile_name = nil, app_name = nil) click to toggle source

This method causes the current running process to become a daemon

     # File lib/daemons/daemonize.rb, line 208
208:   def daemonize(logfile_name = nil, app_name = nil)
209:     srand # Split rand streams between spawning and daemonized process
210:     safefork and exit # Fork and exit from the parent
211: 
212:     # Detach from the controlling terminal
213:     unless sess_id = Process.setsid
214:       raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
215:     end
216: 
217:     # Prevent the possibility of acquiring a controlling terminal
218:     #if oldmode.zero?
219:       trap 'SIGHUP', 'IGNORE'
220:       exit if pid = safefork
221:     #end
222: 
223:     $0 = app_name if app_name
224:     
225:     Dir.chdir "/"   # Release old working directory
226:     File.umask 0000 # Insure sensible umask
227: 
228:     # Make sure all file descriptors are closed
229:     ObjectSpace.each_object(IO) do |io|
230:       unless [STDIN, STDOUT, STDERR].include?(io)
231:         begin
232:           unless io.closed?
233:             io.close
234:           end
235:         rescue ::Exception
236:         end
237:       end
238:     end
239: 
240:     redirect_io(logfile_name)
241:     
242:     #return oldmode ? sess_id : 0   # Return value is mostly irrelevant
243:     return sess_id
244:   end
redirect_io(logfile_name) click to toggle source

Free file descriptors and point them somewhere sensible STDOUT/STDERR should go to a logfile

     # File lib/daemons/daemonize.rb, line 251
251:   def redirect_io(logfile_name)
252:     begin; STDIN.reopen "/dev/null"; rescue ::Exception; end       
253:      
254:     if logfile_name
255:       begin
256:         STDOUT.reopen logfile_name, "a" 
257:         File.chmod(0644, logfile_name)
258:         STDOUT.sync = true
259:       rescue ::Exception
260:         begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
261:       end
262:     else
263:       begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
264:     end
265:     
266:     begin; STDERR.reopen STDOUT; rescue ::Exception; end
267:     STDERR.sync = true
268:   end
safefork() click to toggle source

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 97
 97:   def safefork
 98:     tryagain = true
 99: 
100:     while tryagain
101:       tryagain = false
102:       begin
103:         if pid = fork
104:           return pid
105:         end
106:       rescue Errno::EWOULDBLOCK
107:         sleep 5
108:         tryagain = true
109:       end
110:     end
111:   end
simulate(logfile_name = nil) click to toggle source
     # File lib/daemons/daemonize.rb, line 115
115:   def simulate(logfile_name = nil)
116:     # NOTE: STDOUT and STDERR will not be redirected to the logfile, because in :ontop mode, we normally want to see the output
117:     
118:     Dir.chdir "/"   # Release old working directory
119:     File.umask 0000 # Insure sensible umask
120: 
121:     # Make sure all file descriptors are closed
122:     ObjectSpace.each_object(IO) do |io|
123:       unless [STDIN, STDOUT, STDERR].include?(io)
124:         begin
125:           unless io.closed?
126:             io.close
127:           end
128:         rescue ::Exception
129:         end
130:       end
131:     end
132: 
133:     # Free file descriptors and
134:     # point them somewhere sensible
135:     # STDOUT/STDERR should go to a logfile
136:     
137:     begin; STDIN.reopen "/dev/null"; rescue ::Exception; end       
138:   end

Private Instance Methods

call_as_daemon(block, logfile_name = nil, app_name = nil) click to toggle source
     # File lib/daemons/daemonize.rb, line 142
142:   def call_as_daemon(block, logfile_name = nil, app_name = nil)
143:     rd, wr = IO.pipe
144:     
145:     if tmppid = safefork
146:       # parent
147:       wr.close
148:       pid = rd.read.to_i
149:       rd.close
150:       
151:       Process.waitpid(tmppid)
152:       
153:       return pid
154:     else
155:       # child
156:       
157:       rd.close
158:       
159:       # Detach from the controlling terminal
160:       unless sess_id = Process.setsid
161:         raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
162:       end
163:   
164:       # Prevent the possibility of acquiring a controlling terminal
165:       #if oldmode.zero?
166:         trap 'SIGHUP', 'IGNORE'
167:         exit if pid = safefork
168:       #end
169:   
170:       wr.write Process.pid
171:       wr.close
172:       
173:       $0 = app_name if app_name
174:       
175:       Dir.chdir "/"   # Release old working directory
176:       File.umask 0000 # Insure sensible umask
177:   
178:       # Make sure all file descriptors are closed
179:       ObjectSpace.each_object(IO) do |io|
180:         unless [STDIN, STDOUT, STDERR].include?(io)
181:           begin
182:             unless io.closed?
183:               io.close
184:             end
185:           rescue ::Exception
186:           end
187:         end
188:       end
189:       
190:       ios = Array.new(8192){|i| IO.for_fd(i) rescue nil}.compact
191:       ios.each do |io|
192:         next if io.fileno < 3
193:         io.close
194:       end
195: 
196:   
197:       redirect_io(logfile_name)  
198:     
199:       block.call
200:       
201:       exit
202:     end
203:   end
daemonize(logfile_name = nil, app_name = nil) click to toggle source

This method causes the current running process to become a daemon

     # File lib/daemons/daemonize.rb, line 208
208:   def daemonize(logfile_name = nil, app_name = nil)
209:     srand # Split rand streams between spawning and daemonized process
210:     safefork and exit # Fork and exit from the parent
211: 
212:     # Detach from the controlling terminal
213:     unless sess_id = Process.setsid
214:       raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
215:     end
216: 
217:     # Prevent the possibility of acquiring a controlling terminal
218:     #if oldmode.zero?
219:       trap 'SIGHUP', 'IGNORE'
220:       exit if pid = safefork
221:     #end
222: 
223:     $0 = app_name if app_name
224:     
225:     Dir.chdir "/"   # Release old working directory
226:     File.umask 0000 # Insure sensible umask
227: 
228:     # Make sure all file descriptors are closed
229:     ObjectSpace.each_object(IO) do |io|
230:       unless [STDIN, STDOUT, STDERR].include?(io)
231:         begin
232:           unless io.closed?
233:             io.close
234:           end
235:         rescue ::Exception
236:         end
237:       end
238:     end
239: 
240:     redirect_io(logfile_name)
241:     
242:     #return oldmode ? sess_id : 0   # Return value is mostly irrelevant
243:     return sess_id
244:   end
redirect_io(logfile_name) click to toggle source

Free file descriptors and point them somewhere sensible STDOUT/STDERR should go to a logfile

     # File lib/daemons/daemonize.rb, line 251
251:   def redirect_io(logfile_name)
252:     begin; STDIN.reopen "/dev/null"; rescue ::Exception; end       
253:      
254:     if logfile_name
255:       begin
256:         STDOUT.reopen logfile_name, "a" 
257:         File.chmod(0644, logfile_name)
258:         STDOUT.sync = true
259:       rescue ::Exception
260:         begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
261:       end
262:     else
263:       begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
264:     end
265:     
266:     begin; STDERR.reopen STDOUT; rescue ::Exception; end
267:     STDERR.sync = true
268:   end
safefork() click to toggle source

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 97
 97:   def safefork
 98:     tryagain = true
 99: 
100:     while tryagain
101:       tryagain = false
102:       begin
103:         if pid = fork
104:           return pid
105:         end
106:       rescue Errno::EWOULDBLOCK
107:         sleep 5
108:         tryagain = true
109:       end
110:     end
111:   end
simulate(logfile_name = nil) click to toggle source
     # File lib/daemons/daemonize.rb, line 115
115:   def simulate(logfile_name = nil)
116:     # NOTE: STDOUT and STDERR will not be redirected to the logfile, because in :ontop mode, we normally want to see the output
117:     
118:     Dir.chdir "/"   # Release old working directory
119:     File.umask 0000 # Insure sensible umask
120: 
121:     # Make sure all file descriptors are closed
122:     ObjectSpace.each_object(IO) do |io|
123:       unless [STDIN, STDOUT, STDERR].include?(io)
124:         begin
125:           unless io.closed?
126:             io.close
127:           end
128:         rescue ::Exception
129:         end
130:       end
131:     end
132: 
133:     # Free file descriptors and
134:     # point them somewhere sensible
135:     # STDOUT/STDERR should go to a logfile
136:     
137:     begin; STDIN.reopen "/dev/null"; rescue ::Exception; end       
138:   end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.