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.
su to root
ruby install.rb
build the docs if you want to
rdoc —main README.rdoc daemonize.rb README.rdoc
The Daemonize extension module is copywrited
free software by Travis Whitton
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.
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.
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.
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
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.
# 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
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
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
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
# 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
# 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
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
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
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
# 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.
Generated with the Darkfish Rdoc Generator 1.1.6.