Class: Demon
- Inherits:
-
Object
- Object
- Demon
- Defined in:
- lib/demon.rb,
lib/demon.rb
Constant Summary collapse
- Version =
'0.0.666'
- Load =
Kernel.method(:load)
Class Method Summary collapse
- .dependencies ⇒ Object
- .help ⇒ Object
- .libdir(*args, &block) ⇒ Object
- .load(*args, &block) ⇒ Object
- .modes ⇒ Object
- .run(*args, &block) ⇒ Object
- .start(*args, &block) ⇒ Object
- .version ⇒ Object
Instance Method Summary collapse
- #boot! ⇒ Object
- #cap?(&block) ⇒ Boolean
- #cmdline! ⇒ Object
- #current_path_for(path) ⇒ Object
-
#daemonize!(options = {}, &block) ⇒ Object
daemonize{|pid| puts “the pid of the daemon is #{ pid }”}.
- #detach!(&block) ⇒ Object
- #generate_cmdline ⇒ Object
-
#initialize(*args, &block) ⇒ Demon
constructor
A new instance of Demon.
- #keep_ios(*ios) ⇒ Object
- #lock!(options = {}) ⇒ Object
- #log! ⇒ Object
- #logger ⇒ Object
- #logger=(logger) ⇒ Object
- #logging! ⇒ Object
- #logging_errors(&block) ⇒ Object
- #mode_fuser ⇒ Object
- #mode_help ⇒ Object
- #mode_log ⇒ Object
- #mode_modes ⇒ Object
- #mode_pid ⇒ Object
- #mode_ping ⇒ Object
- #mode_restart ⇒ Object
- #mode_root ⇒ Object
- #mode_run ⇒ Object
- #mode_signal(signal = 'SIGUSR2') ⇒ Object
- #mode_start ⇒ Object
- #mode_stop ⇒ Object
- #mode_tail ⇒ Object
- #pid! ⇒ Object
- #prefix ⇒ Object
- #prefix=(prefix) ⇒ Object
- #process_signals ⇒ Object
- #production? ⇒ Boolean
- #redeployed? ⇒ Boolean
- #redirect_io!(options = {}) ⇒ Object
- #restart! ⇒ Object
- #run(which = :run, &block) ⇒ Object
- #run_forever_handling_signals_and_logging_errors!(&block) ⇒ Object
- #signal_if_redeployed! ⇒ Object
- #signaled? ⇒ Boolean
- #sleeping?(&block) ⇒ Boolean
- #start(which = :start, &block) ⇒ Object
- #trap! ⇒ Object
- #unlock! ⇒ Object
- #wait(seconds) ⇒ Object
- #which_ruby ⇒ Object
Constructor Details
#initialize(*args, &block) ⇒ Demon
Returns a new instance of Demon.
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 |
# File 'lib/demon.rb', line 66 def initialize(*args, &block) # @options = Map.(args) # @__file__ = ( @options[:script] or @options[:file] or @options[:__file__] or args.shift or (block ? eval('File.expand_path(__FILE__)', block.binding) : nil) ) raise("no __FILE__ groked!") unless @__file__ @root = @options[:root] @mode = @options[:mode] # @script = File.(@__file__) raise("no script groked!") unless test(?s, @script) # @cmdline = generate_cmdline @dirname = File.(File.dirname(@script)) @basename = File.basename(@script) @script_root = File.(File.dirname(@script)) # rails_root = @script_root seems_to_be_a_rails_app = false 42.times do seems_to_be_a_rails_app = %w( app/controllers app/models app/views config Rakefile ).all? do |subdir| test(?e, File.join(rails_root, subdir)) end if seems_to_be_a_rails_app or rails_root == '/' break end rails_root = File.(File.dirname(rails_root)) end if seems_to_be_a_rails_app @rails_root = rails_root @demon_dir = File.join(@rails_root, 'log', 'demon') @restart_txt = File.join(@rails_root, 'tmp', 'restart.txt') self.prefix = File.join(@demon_dir, @basename) @root = @rails_root else @rails_root = false @demon_dir = @root || "#{ @script }.demon" @restart_txt = File.join(@demon_dir, 'restart.txt') self.prefix = @demon_dir @root = @demon_dir end # @signals = [] @started_at = Time.now @sleeping = false @ppid = Process.pid # STDOUT.sync = true STDERR.sync = true self end |
Class Method Details
.dependencies ⇒ Object
30 31 32 33 34 |
# File 'lib/demon.rb', line 30 def dependencies { 'map' => [ 'map' , ' >= 6.0.0' ] } end |
.help ⇒ Object
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/demon.rb', line 245 def Demon.help { 'start' => 'start in daemon mode', 'run' => 'run in the foreground, but otherwise like a daemon', 'stop' => 'stop any currently running daemon', 'restart' => 'restart any currently running daemon, or start a new one', 'pid' => 'print the pid of the running daemon, iff any', 'ping' => 'ensure a daemon is running, start one iff not', 'signal' => 'hit the daemon, if any, with SIGUSR2', 'tail' => 'tail -F all auxillary files (lock files, logs, etc)', 'fuser' => 'report the fuser of any auxillary files (lock files, logs, etc)', 'log' => 'display the location of the log file', 'root' => 'display the location of the root daemon dir (lock files, logs, etc)', 'modes' => 'print all modes, even those without "help"', 'help' => 'this message' } end |
.libdir(*args, &block) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/demon.rb', line 12 def libdir(*args, &block) @libdir ||= File.(__FILE__).sub(/\.rb$/,'') libdir = args.empty? ? @libdir : File.join(@libdir, *args.map{|arg| arg.to_s}) ensure if block begin $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.first==libdir module_eval(&block) ensure $LOAD_PATH.shift() if $LOAD_PATH.first==libdir end end end |
.load(*args, &block) ⇒ Object
26 27 28 |
# File 'lib/demon.rb', line 26 def load(*args, &block) libdir{ Load.call(*args, &block) } end |
.modes ⇒ Object
236 237 238 |
# File 'lib/demon.rb', line 236 def Demon.modes instance_methods.grep(/mode_(.*)/).map{|mode| mode.to_s.split('_').last} end |
.run(*args, &block) ⇒ Object
208 209 210 |
# File 'lib/demon.rb', line 208 def Demon.run(*args, &block) new(*args).tap{|demon| demon.run(&block)} end |
.start(*args, &block) ⇒ Object
198 199 200 |
# File 'lib/demon.rb', line 198 def Demon.start(*args, &block) new(*args).tap{|demon| demon.start(&block)} end |
Instance Method Details
#boot! ⇒ Object
470 471 472 473 474 475 476 |
# File 'lib/demon.rb', line 470 def boot! if @rails_root Dir.chdir(@rails_root) require File.join(@rails_root, 'config', 'boot') require File.join(@rails_root, 'config', 'environment') end end |
#cap?(&block) ⇒ Boolean
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
# File 'lib/demon.rb', line 565 def cap?(&block) realpath = proc do |path| begin (path.is_a?(Pathname) ? path : Pathname.new(path.to_s)).realpath.to_s rescue Errno::ENOENT nil end end cap_root = realpath[@rails_root || @root] shared_path = File.('../../shared', cap_root) cap_path = File.dirname(shared_path) shared_public_system_path = File.('../../shared/system') public_path = File.join(cap_root, 'public') public_system_path = File.join(public_path.to_s, 'system') is_cap_deploy = test(?e, shared_public_system_path) and test(?l, public_system_path) and realpath[shared_public_system_path] == realpath[public_system_path] return false unless is_cap_deploy args = if block [cap_path].slice(block.arity > 0 ? (0 ... block.arity) : (0 .. -1)) else [] end block ? block.call(*args) : cap_path end |
#cmdline! ⇒ Object
505 506 507 508 509 |
# File 'lib/demon.rb', line 505 def cmdline! open(@cmdline_file, 'w+') do |fd| fd.puts(Array(@cmdline).join(' ')) end end |
#current_path_for(path) ⇒ Object
554 555 556 |
# File 'lib/demon.rb', line 554 def current_path_for(path) path.to_s.gsub(%r|\breleases/\d+\b|, 'current') end |
#daemonize!(options = {}, &block) ⇒ Object
daemonize{|pid| puts “the pid of the daemon is #{ pid }”}
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 |
# File 'lib/demon.rb', line 645 def daemonize!( = {}, &block) # optional directory and umask # chdir = [:chdir] || ['chdir'] || '.' umask = [:umask] || ['umask'] || 0 # drop to the background avoiding the possibility of zombies.. # detach!(&block) # close all open io handles *except* these ones # keep_ios(STDIN, STDOUT, STDERR, @lock) # sane directory and umask # Dir::chdir(chdir) File::umask(umask) # global daemon flag # $DAEMON = true end |
#detach!(&block) ⇒ Object
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 |
# File 'lib/demon.rb', line 669 def detach!(&block) # setup a pipe to relay the grandchild pid through # a, b = IO.pipe # in the parent we wait for the pid, wait on our child to avoid zombies, and # then exit # if fork b.close pid = Integer(a.read.strip) a.close block.call(pid) if block Process.waitall exit! end # the child simply exits so it can be reaped - avoiding zombies. the pipes # are inherited in the grandchild # if fork exit! end # finally, the grandchild sends it's pid back up the pipe to the parent is # aware of the pid # a.close b.puts(Process.pid) b.close # might as well nohup too... # Process::setsid rescue nil end |
#generate_cmdline ⇒ Object
549 550 551 552 |
# File 'lib/demon.rb', line 549 def generate_cmdline current_script = current_path_for(@script) [which_ruby, current_script, 'start'] end |
#keep_ios(*ios) ⇒ Object
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
# File 'lib/demon.rb', line 764 def keep_ios(*ios) filenos = [] ios.flatten.compact.each do |io| begin fileno = io.respond_to?(:fileno) ? io.fileno : Integer(io) filenos.push(fileno) rescue Object next end end ObjectSpace.each_object(IO) do |io| begin fileno = io.fileno next if filenos.include?(fileno) io.close unless io.closed? rescue Object next end end end |
#lock!(options = {}) ⇒ Object
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
# File 'lib/demon.rb', line 478 def lock!( = {}) complain = ['complain'] || [:complain] fd = open(@lock_file, 'r+') status = fd.flock(File::LOCK_EX|File::LOCK_NB) unless status == 0 if complain pid = Integer(IO.read(@pid_file)) rescue '?' warn("instance(#{ pid }) is already running!") end exit(42) end @lock = fd # prevent garbage collection from closing the file! at_exit{ unlock! } end |
#log! ⇒ Object
537 538 539 540 541 542 |
# File 'lib/demon.rb', line 537 def log! logger.info("START - #{ Process.pid }") at_exit do logger.info("STOP - #{ Process.pid }") rescue nil end end |
#logger ⇒ Object
619 620 621 622 623 624 |
# File 'lib/demon.rb', line 619 def logger @logger ||= ( require 'logger' unless defined?(Logger) Logger.new(STDERR) ) end |
#logger=(logger) ⇒ Object
626 627 628 |
# File 'lib/demon.rb', line 626 def logger=(logger) @logger = logger end |
#logging! ⇒ Object
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 |
# File 'lib/demon.rb', line 737 def logging! number_rolled = 7 megabytes = 2 ** 20 max_size = 42 * megabytes @logger = if STDIN.tty? if defined?(Logging) ::Logging.logger(STDERR) else ::Logger.new(STDERR) end else if defined?(Logging) = defined?(Lockfile) ? {:safe => true} : {} ::Logging.logger(@log_file, number_rolled, max_size, ) else ::Logger.new(@log_file, number_rolled, max_size) end end @logger.level = ::Logger::INFO rescue nil if production? @logger.level = ::Logger::DEBUG if STDERR.tty? @logger end |
#logging_errors(&block) ⇒ Object
630 631 632 633 634 635 636 637 638 639 |
# File 'lib/demon.rb', line 630 def logging_errors(&block) begin block.call() rescue SignalException => e logger.info(e) exit(0) rescue => e logger.error(e) end end |
#mode_fuser ⇒ Object
356 357 358 |
# File 'lib/demon.rb', line 356 def mode_fuser exec("fuser #{ @lock_file.inspect }") end |
#mode_help ⇒ Object
263 264 265 266 |
# File 'lib/demon.rb', line 263 def mode_help puts(Demon.help.to_yaml) exit(42) end |
#mode_log ⇒ Object
413 414 415 416 |
# File 'lib/demon.rb', line 413 def mode_log puts(@log_file) exit(42) end |
#mode_modes ⇒ Object
240 241 242 243 |
# File 'lib/demon.rb', line 240 def mode_modes puts Demon.modes.join('|') exit(42) end |
#mode_pid ⇒ Object
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/demon.rb', line 340 def mode_pid pid = Integer(IO.read(@pid_file)) rescue nil if pid begin Process.kill(0, pid) puts(pid) exit(0) rescue Errno::ESRCH exit(1) end else exit(1) end exit(1) end |
#mode_ping ⇒ Object
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/demon.rb', line 268 def mode_ping pid = Integer(IO.read(@pid_file)) rescue nil if pid signaled = false begin Process.kill('SIGALRM', pid) signaled = true rescue Object nil end if signaled STDOUT.puts(pid) exit end end Kernel.exec("#{ @script } start") end |
#mode_restart ⇒ Object
328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/demon.rb', line 328 def mode_restart begin pid = Integer(IO.read(@pid_file)) rescue nil Process.kill('HUP', pid) puts "Process #{pid} signaled to restart" exit(0) rescue puts "No running process found. Starting a new one." mode_start end end |
#mode_root ⇒ Object
418 419 420 421 |
# File 'lib/demon.rb', line 418 def mode_root puts(@root) exit(42) end |
#mode_run ⇒ Object
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/demon.rb', line 290 def mode_run lock!(:complain => true) pid! cmdline! trap! boot! logging! log! end |
#mode_signal(signal = 'SIGUSR2') ⇒ Object
403 404 405 406 407 408 409 410 411 |
# File 'lib/demon.rb', line 403 def mode_signal(signal = 'SIGUSR2') pid = Integer(IO.read(@pid_file)) rescue nil if pid Process.kill(signal, pid) puts(pid) exit(0) end exit(42) end |
#mode_start ⇒ Object
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/demon.rb', line 306 def mode_start lock!(:complain => true) daemonize!{|pid| puts(pid)} redirect_io! pid! cmdline! trap! boot! logging! signal_if_redeployed! log! end |
#mode_stop ⇒ Object
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'lib/demon.rb', line 360 def mode_stop pid = Integer(IO.read(@pid_file)) rescue nil if pid alive = true %w( QUIT TERM ).each do |signal| begin Process.kill(signal, pid) rescue Errno::ESRCH nil end 42.times do begin Process.kill(0, pid) sleep(rand) rescue Errno::ESRCH alive = false puts(pid) exit(0) end end end if alive begin Process.kill(-9, pid) sleep(rand) rescue Errno::ESRCH nil end begin Process.kill(0, pid) rescue Errno::ESRCH puts(pid) exit(0) end end end exit(1) end |
#mode_tail ⇒ Object
423 424 425 426 |
# File 'lib/demon.rb', line 423 def mode_tail system("tail -F #{ @stdout_file.inspect } #{ @stderr_file.inspect } #{ @log_file.inspect }") exit(42) end |
#pid! ⇒ Object
498 499 500 501 502 503 |
# File 'lib/demon.rb', line 498 def pid! open(@pid_file, 'w+') do |fd| fd.puts(Process.pid) end at_exit{ FileUtils.rm_f(@pid_file) } end |
#prefix ⇒ Object
146 147 148 |
# File 'lib/demon.rb', line 146 def prefix @prefix end |
#prefix=(prefix) ⇒ Object
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/demon.rb', line 150 def prefix=(prefix) @prefix = File.(prefix.to_s) @lock_file = File.join(@prefix, 'lock') @log_file = File.join(@prefix, 'log') @pid_file = File.join(@prefix, 'pid') @cmdline_file = File.join(@prefix, 'cmdline') @stdin_file = File.join(@prefix, 'stdin') @stdout_file = File.join(@prefix, 'stdout') @stderr_file = File.join(@prefix, 'stderr') FileUtils.mkdir_p(@prefix) %w( lock log pid cmdline stdin stdout stderr ).each do |which| file = instance_variable_get("@#{ which }_file") FileUtils.touch(file) end @prefix end |
#process_signals ⇒ Object
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/demon.rb', line 429 def process_signals if signaled? signals.uniq.each do |signal| case signal.to_s when /HUP/i logger.info('RESTART - signal') restart! when /USR1/i logger.info('RESTART - deploy') restart! when /USR2/i nil when /ALRM/i nil end end signals.clear end end |
#production? ⇒ Boolean
599 600 601 602 603 604 605 |
# File 'lib/demon.rb', line 599 def production? if defined?(Rails.env) Rails.env.production? else true end end |
#redeployed? ⇒ Boolean
544 545 546 547 |
# File 'lib/demon.rb', line 544 def redeployed? t = File.stat(current_path_for(@restart_txt)).mtime rescue @started_at t > @started_at end |
#redirect_io!(options = {}) ⇒ Object
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 |
# File 'lib/demon.rb', line 705 def redirect_io!( = {}) stdin = [:stdin] || @stdin_file stdout = [:stdout] || @stdout_file stderr = [:stderr] || @stderr_file { STDIN => stdin, STDOUT => stdout, STDERR => stderr }.each do |io, file| opened = false fd = case when file.is_a?(IO) file when file.to_s == 'null' opened = true open('/dev/null', 'ab+') else opened = true open(file, 'ab+') end begin fd.sync = true rescue nil fd.truncate(0) rescue nil io.reopen(fd) ensure fd.close rescue nil if opened end end end |
#restart! ⇒ Object
458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/demon.rb', line 458 def restart! exit!(0) if fork logger.info('CMD - %s' % Array(@cmdline).join(' ')) unlock! keep_ios(STDIN, STDOUT, STDERR) Kernel.exec(*@cmdline) end |
#run(which = :run, &block) ⇒ Object
202 203 204 205 206 |
# File 'lib/demon.rb', line 202 def run(which = :run, &block) mode = "mode_#{ which }".downcase send(mode) if respond_to?(mode) run_forever_handling_signals_and_logging_errors!(&block) end |
#run_forever_handling_signals_and_logging_errors!(&block) ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/demon.rb', line 220 def run_forever_handling_signals_and_logging_errors!(&block) loop do catch(:signals) do process_signals begin block.call() if block rescue => e logger.error(e) ensure wait(420) unless $! # NOTE: signals wake this up! end end end end |
#signal_if_redeployed! ⇒ Object
525 526 527 528 529 530 531 532 533 534 535 |
# File 'lib/demon.rb', line 525 def signal_if_redeployed! seconds = production? ? 10 : 1 Thread.new do Thread.current.abort_on_exception = true loop do Kernel.sleep(seconds) Process.kill(:USR1, Process.pid) if redeployed? end end end |
#signaled? ⇒ Boolean
615 616 617 |
# File 'lib/demon.rb', line 615 def signaled? !signals.empty? end |
#sleeping?(&block) ⇒ Boolean
607 608 609 610 611 612 613 |
# File 'lib/demon.rb', line 607 def sleeping?(&block) if block block.call if @sleeping else @sleeping == true end end |
#start(which = :start, &block) ⇒ Object
192 193 194 195 196 |
# File 'lib/demon.rb', line 192 def start(which = :start, &block) mode = "mode_#{ which }".downcase send(mode) if respond_to?(mode) run_forever_handling_signals_and_logging_errors!(&block) end |
#trap! ⇒ Object
511 512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'lib/demon.rb', line 511 def trap! %w( SIGHUP SIGALRM SIGUSR1 SIGUSR2 ).each do |signal| trap(signal) do |sig| signals.push(signal) logger.debug("SIGNAL - #{ signal }") throw(:signals, signal) if sleeping? end end trap('SIGQUIT'){ exit(42) } trap('SIGTERM'){ exit(42) } trap('SIGINT'){ exit(42) } end |
#unlock! ⇒ Object
494 495 496 |
# File 'lib/demon.rb', line 494 def unlock! @lock.flock(File::LOCK_UN|File::LOCK_NB) if @lock end |
#wait(seconds) ⇒ Object
449 450 451 452 453 454 455 456 |
# File 'lib/demon.rb', line 449 def wait(seconds) begin @sleeping = true Kernel.sleep(seconds) ensure @sleeping = false end end |
#which_ruby ⇒ Object
558 559 560 561 562 563 |
# File 'lib/demon.rb', line 558 def which_ruby c = ::RbConfig::CONFIG ruby = File::join(c['bindir'], c['ruby_install_name']) << c['EXEEXT'] raise "ruby @ #{ ruby } not executable!?" unless test(?e, ruby) ruby end |