# File lib/daemons/application.rb, line 28 28: def initialize(group, add_options = {}, pid = nil) 29: @group = group 30: @options = group.options.dup 31: @options.update(add_options) 32: 33: @dir_mode = @dir = @script = nil 34: 35: @force_kill_waittime = @options[:force_kill_waittime] || 20 36: 37: unless @pid = pid 38: if @options[:no_pidfiles] 39: @pid = PidMem.new 40: elsif dir = pidfile_dir 41: @pid = PidFile.new(dir, @group.app_name, @group.multiple) 42: else 43: @pid = PidMem.new 44: end 45: end 46: end
# File lib/daemons/application.rb, line 48 48: def change_privilege 49: user = options[:user] 50: group = options[:group] 51: CurrentProcess.change_privilege(user, group) if user 52: end
This is a nice little function for debugging purposes: In case a multi-threaded ruby script exits due to an uncaught exception it may be difficult to find out where the exception came from because one cannot catch exceptions that are thrown in threads other than the main thread.
This function searches for all exceptions in memory and outputs them to STDERR (if it is connected) and to a log file in the pid-file directory.
# File lib/daemons/application.rb, line 339 339: def exception_log 340: return unless logfile 341: 342: require 'logger' 343: 344: l_file = Logger.new(logfile) 345: 346: # the code below finds the last exception 347: e = nil 348: 349: ObjectSpace.each_object {|o| 350: if ::Exception === o 351: e = o 352: end 353: } 354: 355: l_file.info "*** below you find the most recent exception thrown, this will be likely (but not certainly) the exception that made the application exit abnormally ***" 356: l_file.error e 357: 358: l_file.info "*** below you find all exception objects found in memory, some of them may have been thrown in your application, others may just be in memory because they are standard exceptions ***" 359: 360: # this code logs every exception found in memory 361: ObjectSpace.each_object {|o| 362: if ::Exception === o 363: l_file.error o 364: end 365: } 366: 367: l_file.close 368: end
# File lib/daemons/application.rb, line 62 62: def logdir 63: logdir = options[:log_dir] 64: unless logdir 65: logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir 66: end 67: logdir 68: end
# File lib/daemons/application.rb, line 74 74: def logfile 75: logdir ? File.join(logdir, @group.app_name + '.log') : nil 76: end
# File lib/daemons/application.rb, line 70 70: def output_logfile 71: (options[:log_output] && logdir) ? File.join(logdir, @group.app_name + '.output') : nil 72: end
# File lib/daemons/application.rb, line 58 58: def pidfile_dir 59: Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script) 60: end
def run
if @group.controller.options[:exec] run_via_exec() else run_via_load() end
end
def run_via_exec
end
def run_via_load
end
# File lib/daemons/application.rb, line 324 324: def reload 325: Process.kill('HUP', @pid.pid) 326: rescue 327: # ignore 328: end
This function implements a (probably too simle) method to detect whether the program with the pid found in the pid-file is still running. It just searches for the pid in the output of ps ax, which is probably not a good idea in some cases. Alternatives would be to use a direct access method the unix process control system.
# File lib/daemons/application.rb, line 459 459: def running? 460: if @pid.exist? 461: return Pid.running?(@pid.pid) 462: end 463: 464: return false 465: end
# File lib/daemons/application.rb, line 54 54: def script 55: @script || @group.script 56: end
# File lib/daemons/application.rb, line 446 446: def show_status 447: running = self.running? 448: 449: puts "#{self.group.app_name}: #{running ? '' : 'not '}running#{(running and @pid.exist?) ? ' [pid ' + @pid.pid.to_s + ']' : ''}#{(@pid.exist? and not running) ? ' (but pid-file exists: ' + @pid.pid.to_s + ')' : ''}" 450: end
# File lib/daemons/application.rb, line 281 281: def start 282: change_privilege 283: @group.create_monitor(@group.applications[0] || self) unless options[:ontop] # we don't monitor applications in the foreground 284: 285: case options[:mode] 286: when :none 287: # this is only used to daemonize the currently running process 288: start_none 289: when :exec 290: start_exec 291: when :load 292: start_load 293: when :proc 294: start_proc 295: else 296: start_load 297: end 298: end
# File lib/daemons/application.rb, line 122 122: def start_exec 123: if options[:backtrace] 124: puts "option :backtrace is not supported with :mode => :exec, ignoring" 125: end 126: 127: unless options[:ontop] 128: Daemonize.daemonize(output_logfile, @group.app_name) 129: else 130: Daemonize.simulate(output_logfile) 131: end 132: 133: # note that we cannot remove the pid file if we run in :ontop mode (i.e. 'ruby ctrl_exec.rb run') 134: @pid.pid = Process.pid 135: 136: ENV['DAEMONS_ARGV'] = @controller_argv.join(' ') 137: # haven't tested yet if this is really passed to the exec'd process... 138: 139: started() 140: Kernel.exec(script(), *(@app_argv || [])) 141: #Kernel.exec(script(), *ARGV) 142: end
# File lib/daemons/application.rb, line 144 144: def start_load 145: unless options[:ontop] 146: Daemonize.daemonize(output_logfile, @group.app_name) 147: else 148: Daemonize.simulate(output_logfile) 149: end 150: 151: @pid.pid = Process.pid 152: 153: 154: # We need this to remove the pid-file if the applications exits by itself. 155: # Note that <tt>at_exit</tt> will only be run if the applications exits by calling 156: # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt> 157: # in your application! 158: # 159: at_exit { 160: begin; @pid.cleanup; rescue ::Exception; end 161: 162: # If the option <tt>:backtrace</tt> is used and the application did exit by itself 163: # create a exception log. 164: if options[:backtrace] and not options[:ontop] and not $daemons_sigterm 165: begin; exception_log(); rescue ::Exception; end 166: end 167: 168: } 169: 170: # This part is needed to remove the pid-file if the application is killed by 171: # daemons or manually by the user. 172: # Note that the applications is not supposed to overwrite the signal handler for 173: # 'TERM'. 174: # 175: $daemons_stop_proc = options[:stop_proc] 176: trap(SIGNAL) { 177: begin 178: if $daemons_stop_proc 179: $daemons_stop_proc.call 180: end 181: rescue ::Exception 182: end 183: 184: begin; @pid.cleanup; rescue ::Exception; end 185: $daemons_sigterm = true 186: 187: if options[:hard_exit] 188: exit! 189: else 190: exit 191: end 192: } 193: 194: # Now we really start the script... 195: $DAEMONS_ARGV = @controller_argv 196: ENV['DAEMONS_ARGV'] = @controller_argv.join(' ') 197: 198: ARGV.clear 199: ARGV.concat @app_argv if @app_argv 200: 201: started() 202: # TODO: begin - rescue - end around this and exception logging 203: load script() 204: end
this function is only used to daemonize the currently running process (Daemons.daemonize)
# File lib/daemons/application.rb, line 79 79: def start_none 80: unless options[:ontop] 81: Daemonize.daemonize(nil, @group.app_name) #(logfile) 82: else 83: Daemonize.simulate 84: end 85: 86: @pid.pid = Process.pid 87: 88: 89: # We need this to remove the pid-file if the applications exits by itself. 90: # Note that <tt>at_text</tt> will only be run if the applications exits by calling 91: # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt> 92: # in your application! 93: # 94: at_exit { 95: begin; @pid.cleanup; rescue ::Exception; end 96: 97: # If the option <tt>:backtrace</tt> is used and the application did exit by itself 98: # create a exception log. 99: if options[:backtrace] and not options[:ontop] and not $daemons_sigterm 100: begin; exception_log(); rescue ::Exception; end 101: end 102: 103: } 104: 105: # This part is needed to remove the pid-file if the application is killed by 106: # daemons or manually by the user. 107: # Note that the applications is not supposed to overwrite the signal handler for 108: # 'TERM'. 109: # 110: trap(SIGNAL) { 111: begin; @pid.cleanup; rescue ::Exception; end 112: $daemons_sigterm = true 113: 114: if options[:hard_exit] 115: exit! 116: else 117: exit 118: end 119: } 120: end
# File lib/daemons/application.rb, line 206 206: def start_proc 207: return unless p = options[:proc] 208: 209: myproc = proc do 210: # We need this to remove the pid-file if the applications exits by itself. 211: # Note that <tt>at_text</tt> will only be run if the applications exits by calling 212: # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt> 213: # in your application! 214: # 215: at_exit { 216: begin; @pid.cleanup; rescue ::Exception; end 217: 218: # If the option <tt>:backtrace</tt> is used and the application did exit by itself 219: # create a exception log. 220: if options[:backtrace] and not options[:ontop] and not $daemons_sigterm 221: begin; exception_log(); rescue ::Exception; end 222: end 223: 224: } 225: 226: # This part is needed to remove the pid-file if the application is killed by 227: # daemons or manually by the user. 228: # Note that the applications is not supposed to overwrite the signal handler for 229: # 'TERM'. 230: # 231: $daemons_stop_proc = options[:stop_proc] 232: trap(SIGNAL) { 233: begin 234: if $daemons_stop_proc 235: $daemons_stop_proc.call 236: end 237: rescue ::Exception 238: end 239: 240: begin; @pid.cleanup; rescue ::Exception; end 241: $daemons_sigterm = true 242: 243: if options[:hard_exit] 244: exit! 245: else 246: exit 247: end 248: } 249: 250: p.call() 251: end 252: 253: unless options[:ontop] 254: @pid.pid = Daemonize.call_as_daemon(myproc, output_logfile, @group.app_name) 255: 256: else 257: Daemonize.simulate(output_logfile) 258: 259: @pid.pid = Process.pid 260: 261: myproc.call 262: 263: # why did we use this?? 264: # Thread.new(&options[:proc]) 265: 266: # why did we use the code below?? 267: # unless pid = Process.fork 268: # @pid.pid = pid 269: # Daemonize.simulate(logfile) 270: # options[:proc].call 271: # exit 272: # else 273: # Process.detach(@pid.pid) 274: # end 275: end 276: 277: started() 278: end
# File lib/daemons/application.rb, line 300 300: def started 301: if pid = @pid.pid 302: puts "#{self.group.app_name}: process with pid #{pid} started." 303: STDOUT.flush 304: end 305: end
# File lib/daemons/application.rb, line 371 371: def stop(no_wait = false) 372: if not running? 373: self.zap 374: return 375: end 376: 377: pid = @pid.pid 378: 379: # Catch errors when trying to kill a process that doesn't 380: # exist. This happens when the process quits and hasn't been 381: # restarted by the monitor yet. By catching the error, we allow the 382: # pid file clean-up to occur. 383: begin 384: Process.kill(SIGNAL, pid) 385: rescue Errno::ESRCH => e 386: puts "#{e} #{pid}" 387: puts "deleting pid-file." 388: end 389: 390: if not no_wait 391: if @force_kill_waittime > 0 392: puts "#{self.group.app_name}: trying to stop process with pid #{pid}..." 393: STDOUT.flush 394: 395: begin 396: Timeout::timeout(@force_kill_waittime) { 397: while @pid.running? 398: sleep(0.2) 399: end 400: } 401: rescue Timeout::Error 402: puts "#{self.group.app_name}: process with pid #{pid} won't stop, we forcefully kill it..." 403: STDOUT.flush 404: 405: begin 406: Process.kill('KILL', pid) 407: rescue Errno::ESRCH 408: end 409: 410: begin 411: Timeout::timeout(20) { 412: while @pid.running? 413: sleep(1) 414: end 415: } 416: rescue Timeout::Error 417: puts "#{self.group.app_name}: unable to forcefully kill process with pid #{pid}." 418: STDOUT.flush 419: end 420: end 421: end 422: 423: 424: end 425: 426: sleep(0.1) 427: unless @pid.running? 428: # We try to remove the pid-files by ourselves, in case the application 429: # didn't clean it up. 430: begin; @pid.cleanup; rescue ::Exception; end 431: 432: puts "#{self.group.app_name}: process with pid #{pid} successfully stopped." 433: STDOUT.flush 434: end 435: 436: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.