daemons-1.1.9/0000755000004100000410000000000012246432262013203 5ustar www-datawww-datadaemons-1.1.9/Rakefile0000644000004100000410000000455712246432262014663 0ustar www-datawww-datarequire 'rubygems' #Gem::manage_gems require 'rake/gempackagetask' #require 'rake/testtask' require 'rake/packagetask' require 'rake/rdoctask' $LOAD_PATH << './lib' require 'daemons' PKG_NAME = "daemons" PKG_FILES = FileList[ "Rakefile", "Releases", "TODO", "README", "LICENSE", "setup.rb", "lib/**/*.rb", #"test/**/*", "examples/**/*.rb" ] #PKG_FILES.exclude(%r(^test/tmp/.+)) PKG_FILES.exclude(%r(\.pid$)) PKG_FILES.exclude(%r(\.log$)) PKG_FILES.exclude(%r(\.output$)) PKG_FILES.exclude(%r(\.txt$)) spec = Gem::Specification.new do |s| s.name = PKG_NAME s.version = Daemons::VERSION s.author = "Thomas Uehlinger" s.email = "th.uehlinger@gmx.ch" s.rubyforge_project = "daemons" s.homepage = "http://daemons.rubyforge.org" s.platform = Gem::Platform::RUBY s.summary = "A toolkit to create and control daemons in different ways" s.description = <<-EOF Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server) to be run as a daemon and to be controlled by simple start/stop/restart commands. You can also call blocks as daemons and control them from the parent or just daemonize the current process. Besides this basic functionality, daemons offers many advanced features like exception backtracing and logging (in case your ruby script crashes) and monitoring and automatic restarting of your processes if they crash. EOF #s.files = FileList["{test,lib}/**/*"].exclude("rdoc").to_a s.files = PKG_FILES s.require_path = "lib" s.autorequire = "daemons" s.has_rdoc = true s.extra_rdoc_files = ["README", "Releases", "TODO"] end Rake::GemPackageTask.new(spec) do |pkg| pkg.need_tar = true end #Rake::PackageTask.new("package") do |p| # p.name = PKG_NAME # p.version = Daemons::VERSION # p.need_tar = true # p.need_zip = true # p.package_files = PKG_FILES #end task :default => [:package] desc 'Show information about the gem.' task :debug_gem do puts spec.to_ruby end task :upload do sh "scp -r html/* uehli@rubyforge.org:/var/www/gforge-projects/daemons" end desc "Create the RDOC html files" rd = Rake::RDocTask.new("rdoc") { |rdoc| rdoc.rdoc_dir = 'html' rdoc.title = "Daemons" rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README' rdoc.rdoc_files.include('README', 'TODO', 'Releases') rdoc.rdoc_files.include('lib/**/*.rb') } daemons-1.1.9/examples/0000755000004100000410000000000012246432262015021 5ustar www-datawww-datadaemons-1.1.9/examples/call/0000755000004100000410000000000012246432262015734 5ustar www-datawww-datadaemons-1.1.9/examples/call/call.rb0000644000004100000410000000230112246432262017170 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' testfile = File.expand_path(__FILE__) + '.log' # On the first call to , an application group (accessible by Daemons.group) # will be created an the options will be kept within, so you only have to specify # :multiple once. # options = { :app_name => 'mytask', # :ontop => true, :multiple => true } Daemons.call(options) do File.open(testfile, 'w') {|f| f.puts "test" } loop { puts "1"; sleep 5 } end puts "first task started" Daemons.call do loop { puts "2"; sleep 4 } end puts "second task started" # NOTE: this process will exit after 5 seconds Daemons.call do puts "3" sleep 5 end puts "third task started" puts "waiting 20 seconds..." sleep(20) # This call would result in an exception as it will try to kill the third process # which has already terminated by that time; but using the 'true' parameter forces the # stop_all procedure. puts "trying to stop all tasks..." Daemons.group.stop_all(true) puts "done" daemons-1.1.9/examples/call/call_monitor.rb0000644000004100000410000000222612246432262020745 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' testfile = File.expand_path(__FILE__) + '.log' # On the first call to , an application group (accessible by Daemons.group) # will be created an the options will be kept within, so you only have to specify # :multiple once. # options = { # :ontop => true, :multiple => true, :monitor => true } Daemons.call(options) do loop { puts "1"; sleep 20 } end puts "first task started" # NOTE: this process will exit after 5 seconds Daemons.call do File.open(testfile, 'a') {|f| f.puts "started..." puts "2" sleep 5 f.puts "...exit" } end puts "second task started" puts "waiting 100 seconds..." sleep(100) # This call would result in an exception as it will try to kill the third process # which has already terminated by that time; but using the 'true' parameter forces the # stop_all procedure. puts "trying to stop all tasks..." Daemons.group.stop_all(true) puts "done" daemons-1.1.9/examples/run/0000755000004100000410000000000012246432262015625 5ustar www-datawww-datadaemons-1.1.9/examples/run/myserver_slowstop.rb0000755000004100000410000000061712246432262022007 0ustar www-datawww-data#!/usr/bin/env ruby # This is myserver_slowstop.rb, an example server that is to be controlled by daemons # and that does nothing really useful at the moment. # # Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts. trap('TERM') { puts "received TERM" # simulate the slow stopping sleep(10) exit } loop do puts 'ping from myserver.rb!' sleep(3) end daemons-1.1.9/examples/run/ctrl_proc_multiple.rb0000644000004100000410000000061312246432262022054 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :log_output => true, :multiple => true, } Daemons.run_proc('ctrl_proc_multiple.rb', options) do puts "hello" sleep(5) puts "done" enddaemons-1.1.9/examples/run/ctrl_keep_pid_files.rb0000644000004100000410000000053512246432262022143 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :keep_pid_files => true } Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options)daemons-1.1.9/examples/run/ctrl_ontop.rb0000644000004100000410000000052412246432262020336 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :ontop => true } Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options) daemons-1.1.9/examples/run/ctrl_normal.rb0000644000004100000410000000045212246432262020467 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb')) daemons-1.1.9/examples/run/ctrl_exit.rb0000644000004100000410000000051312246432262020146 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { } Daemons.run(File.join(File.dirname(__FILE__), 'myserver_exiting.rb'), options) daemons-1.1.9/examples/run/myserver_hanging.rb0000755000004100000410000000060212246432262021522 0ustar www-datawww-data#!/usr/bin/env ruby # This is myserver.rb, an example server that is to be controlled by daemons # and that does nothing really useful at the moment. # # Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts. trap('TERM') { puts "received TERM" loop do puts 'hanging!' sleep(3) end } loop do puts 'ping from myserver.rb!' sleep(3) end daemons-1.1.9/examples/run/myserver_exiting.rb0000644000004100000410000000020312246432262021550 0ustar www-datawww-dataloop do puts 'ping from myserver.rb!' puts 'this example server will exit in 3 seconds...' sleep(3) Process.exit end daemons-1.1.9/examples/run/myserver_crashing.rb0000644000004100000410000000055612246432262021712 0ustar www-datawww-data# This is myserver.rb, an example server that is to be controlled by daemons # and that does nothing really useful at the moment. # # Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts. loop do puts 'ping from myserver.rb!' puts 'this example server will crash in 3 seconds...' sleep(3) puts 'CRASH!' raise 'CRASH!' end daemons-1.1.9/examples/run/myserver.rb0000755000004100000410000000044512246432262020034 0ustar www-datawww-data#!/usr/bin/env ruby # This is myserver.rb, an example server that is to be controlled by daemons # and that does nothing really useful at the moment. # # Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts. loop do puts 'ping from myserver.rb!' sleep(3) end daemons-1.1.9/examples/run/ctrl_crash.rb0000644000004100000410000000057012246432262020300 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :log_output => true, :backtrace => true } Daemons.run(File.join(File.dirname(__FILE__), 'myserver_crashing.rb'), options) daemons-1.1.9/examples/run/ctrl_multiple.rb0000644000004100000410000000052712246432262021035 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :multiple => true } Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options) daemons-1.1.9/examples/run/ctrl_hanging.rb0000644000004100000410000000074712246432262020621 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { #:mode => :exec, :multiple => true, :no_pidfiles => true, :force_kill_waittime => 5 #:force_kill_waittime => -1 # do not wait before killing -9 } Daemons.run(File.join(File.dirname(__FILE__), 'myserver_hanging.rb'), options) daemons-1.1.9/examples/run/ctrl_proc_simple.rb0000644000004100000410000000053712246432262021517 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' Daemons.run_proc('ctrl_proc_simple.rb') do loop do puts 'ping from proc!' sleep(3) end end daemons-1.1.9/examples/run/ctrl_exec.rb0000644000004100000410000000052412246432262020123 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :mode => :exec } Daemons.run(File.join(File.dirname(__FILE__), 'myserver.rb'), options) daemons-1.1.9/examples/run/ctrl_proc_rand.rb0000644000004100000410000000110412246432262021141 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' Daemons.run_proc('myscript') do loop do file = File.open('/tmp/myscript.log', 'a') file.write(Random.rand) # breaks without seeding # file.write(Random.new.rand) # works without seeding # file.write(rand) # also works, but this is Kernel.rand() so its different file.write("\n") file.close() sleep 2 end enddaemons-1.1.9/examples/run/ctrl_proc.rb0000644000004100000410000000104512246432262020141 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :multiple => false, :ontop => false, :backtrace => true, :log_output => true, :monitor => true } Daemons.run_proc('ctrl_proc.rb', options) do loop do puts 'ping from proc!' sleep(3) end enddaemons-1.1.9/examples/run/ctrl_optionparser.rb0000644000004100000410000000170612246432262021727 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' require 'optparse' require 'logger' require 'ostruct' class MyApp < Logger::Application def initialize(args) super(self.class) @options = OpenStruct.new(:daemonize => true) opts = OptionParser.new do |opts| opts.banner = 'Usage: myapp [options]' opts.separator '' opts.on('-N','--no-daemonize',"Don't run as a daemon") do @options.daemonize = false end end @args = opts.parse!(args) end def run Daemons.run_proc('myapp',{:ARGV => @args, :ontop => !@options.daemonize}) do puts "@options.daemonize: #{@options.daemonize}" STDOUT.sync = true loop do print '.' sleep(2) end end end end myapp = MyApp.new(ARGV) myapp.rundaemons-1.1.9/examples/run/ctrl_slowstop.rb0000644000004100000410000000065212246432262021073 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { #:force_kill_waittime => 40 #:force_kill_waittime => -1 # do not wait before killing -9 } Daemons.run(File.join(File.dirname(__FILE__), 'myserver_slowstop.rb'), options) daemons-1.1.9/examples/run/ctrl_monitor.rb0000644000004100000410000000053712246432262020672 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :monitor => true } Daemons.run(File.join(File.dirname(__FILE__), 'myserver_crashing.rb'), options) daemons-1.1.9/examples/daemonize/0000755000004100000410000000000012246432262016774 5ustar www-datawww-datadaemons-1.1.9/examples/daemonize/daemonize.rb0000644000004100000410000000065212246432262021277 0ustar www-datawww-datalib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib')) if File.exist?(File.join(lib_dir, 'daemons.rb')) $LOAD_PATH.unshift lib_dir else begin; require 'rubygems'; rescue ::Exception; end end require 'daemons' options = { :log_output => true } testfile = File.expand_path(__FILE__) + '.txt' Daemons.daemonize(options) puts "some output..." File.open(testfile, 'w') {|f| f.write("test") } daemons-1.1.9/README0000644000004100000410000001350512246432262014067 0ustar www-datawww-data= Daemons Version 1.1.9 (See Releases for release-specific information) == What is Daemons? Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server) to be run as a daemon and to be controlled by simple start/stop/restart commands. If you want, you can also use daemons to run blocks of ruby code in a daemon process and to control these processes from the main application. Besides this basic functionality, daemons offers many advanced features like exception backtracing and logging (in case your ruby script crashes) and monitoring and automatic restarting of your processes if they crash. == Basic Usage You can use Daemons in four different ways: === 1. Create wrapper scripts for your server scripts or applications Layout: suppose you have your self-written server myserver.rb: # this is myserver.rb # it does nothing really useful at the moment loop do sleep(5) end To use myserver.rb in a production environment, you need to be able to run myserver.rb in the _background_ (this means detach it from the console, fork it in the background, release all directories and file descriptors). Just create myserver_control.rb like this: # this is myserver_control.rb require 'rubygems' # if you use RubyGems require 'daemons' Daemons.run('myserver.rb') And use it like this from the console: $ ruby myserver_control.rb start (myserver.rb is now running in the background) $ ruby myserver_control.rb restart (...) $ ruby myserver_control.rb stop For testing purposes you can even run myserver.rb without forking in the background: $ ruby myserver_control.rb run An additional nice feature of Daemons is that you can pass additional arguments to the script that should be daemonized by seperating them by two _hyphens_: $ ruby myserver_control.rb start -- --file=anyfile --a_switch another_argument === 2. Create wrapper scripts that include your server procs Layout: suppose you have some code you want to run in the background and control that background process from a script: # this is your code # it does nothing really useful at the moment loop do sleep(5) end To run this code as a daemon create myproc_control.rb like this and include your code: # this is myproc_control.rb require 'rubygems' # if you use RubyGems require 'daemons' Daemons.run_proc('myproc.rb') do loop do sleep(5) end end And use it like this from the console: $ ruby myproc_control.rb start (myproc.rb is now running in the background) $ ruby myproc_control.rb restart (...) $ ruby myproc_control.rb stop For testing purposes you can even run myproc.rb without forking in the background: $ ruby myproc_control.rb run === 3. Control a bunch of daemons from another application Layout: you have an application my_app.rb that wants to run a bunch of server tasks as daemon processes. # this is my_app.rb require 'rubygems' # if you use RubyGems require 'daemons' task1 = Daemons.call(:multiple => true) do # first server task loop { conn = accept_conn() serve(conn) } end task2 = Daemons.call do # second server task loop { something_different() } end # the parent process continues to run # we can even control our tasks, for example stop them task1.stop task2.stop exit === 4. Daemonize the currently running process Layout: you have an application my_daemon.rb that wants to run as a daemon (but without the ability to be controlled by daemons via start/stop commands) # this is my_daemons.rb require 'rubygems' # if you use RubyGems require 'daemons' # Initialize the app while we're not a daemon init() # Become a daemon Daemons.daemonize # The server loop loop { conn = accept_conn() serve(conn) } For further documentation, refer to the module documentation of Daemons. == Download and Installation *Download*: just go to http://rubyforge.org/projects/daemons/ Installation *with* RubyGems: $ su # gem install daemons Installation *without* RubyGems: $ tar xfz daemons-x.x.x.tar.gz $ cd daemons-x.x.x $ su # ruby setup.rb == Documentation For further documentation, refer to the module documentation of Daemons (click on Daemons). The RDoc documentation is also online at http://daemons.rubyforge.org == Author Written 2005-2010 by Thomas Uehlinger . Anonymous SVN checkout is available with "svn checkout http://daemons.rubyforge.org/svn/". == License Copyright (c) 2005-2012 Thomas Uehlinger Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. == Feedback and other resources At http://rubyforge.org/projects/daemons. daemons-1.1.9/lib/0000755000004100000410000000000012246432262013751 5ustar www-datawww-datadaemons-1.1.9/lib/daemons/0000755000004100000410000000000012246432262015377 5ustar www-datawww-datadaemons-1.1.9/lib/daemons/application.rb0000644000004100000410000003166012246432262020235 0ustar www-datawww-datarequire 'daemons/pidfile' require 'daemons/pidmem' require 'daemons/change_privilege' require 'daemons/daemonize' require 'timeout' module Daemons class Application attr_accessor :app_argv attr_accessor :controller_argv # the Pid instance belonging to this application attr_reader :pid # the ApplicationGroup the application belongs to attr_reader :group # my private options attr_reader :options SIGNAL = (RUBY_PLATFORM =~ /win32/ ? 'KILL' : 'TERM') def initialize(group, add_options = {}, pid = nil) @group = group @options = group.options.dup @options.update(add_options) @dir_mode = @dir = @script = nil @force_kill_waittime = @options[:force_kill_waittime] || 20 unless @pid = pid if @options[:no_pidfiles] @pid = PidMem.new elsif dir = pidfile_dir @pid = PidFile.new(dir, @group.app_name, @group.multiple) else @pid = PidMem.new end end end def change_privilege user = options[:user] group = options[:group] CurrentProcess.change_privilege(user, group) if user end def script @script || @group.script end def pidfile_dir Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script) end def logdir logdir = options[:log_dir] unless logdir logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir end logdir end def output_logfile (options[:log_output] && logdir) ? File.join(logdir, @group.app_name + '.output') : nil end def logfile logdir ? File.join(logdir, @group.app_name + '.log') : nil end # this function is only used to daemonize the currently running process (Daemons.daemonize) def start_none unless options[:ontop] Daemonize.daemonize(output_logfile, @group.app_name) else Daemonize.simulate(output_logfile) end @pid.pid = Process.pid # We need this to remove the pid-file if the applications exits by itself. # Note that at_text will only be run if the applications exits by calling # exit, and not if it calls exit! (so please don't call exit! # in your application! # at_exit { begin; @pid.cleanup; rescue ::Exception; end # If the option :backtrace is used and the application did exit by itself # create a exception log. if options[:backtrace] and not options[:ontop] and not $daemons_sigterm begin; exception_log(); rescue ::Exception; end end } # This part is needed to remove the pid-file if the application is killed by # daemons or manually by the user. # Note that the applications is not supposed to overwrite the signal handler for # 'TERM'. # trap(SIGNAL) { begin; @pid.cleanup; rescue ::Exception; end $daemons_sigterm = true if options[:hard_exit] exit! else exit end } end def start_exec if options[:backtrace] puts "option :backtrace is not supported with :mode => :exec, ignoring" end unless options[:ontop] Daemonize.daemonize(output_logfile, @group.app_name) else Daemonize.simulate(output_logfile) end # note that we cannot remove the pid file if we run in :ontop mode (i.e. 'ruby ctrl_exec.rb run') @pid.pid = Process.pid ENV['DAEMONS_ARGV'] = @controller_argv.join(' ') # haven't tested yet if this is really passed to the exec'd process... started() Kernel.exec(script(), *(@app_argv || [])) end def start_load unless options[:ontop] Daemonize.daemonize(output_logfile, @group.app_name) else Daemonize.simulate(output_logfile) end @pid.pid = Process.pid # We need this to remove the pid-file if the applications exits by itself. # Note that at_exit will only be run if the applications exits by calling # exit, and not if it calls exit! (so please don't call exit! # in your application! # at_exit { begin; @pid.cleanup; rescue ::Exception; end # If the option :backtrace is used and the application did exit by itself # create a exception log. if options[:backtrace] and not options[:ontop] and not $daemons_sigterm begin; exception_log(); rescue ::Exception; end end } # This part is needed to remove the pid-file if the application is killed by # daemons or manually by the user. # Note that the applications is not supposed to overwrite the signal handler for # 'TERM'. # $daemons_stop_proc = options[:stop_proc] trap(SIGNAL) { begin if $daemons_stop_proc $daemons_stop_proc.call end rescue ::Exception end begin; @pid.cleanup; rescue ::Exception; end $daemons_sigterm = true if options[:hard_exit] exit! else exit end } # Now we really start the script... $DAEMONS_ARGV = @controller_argv ENV['DAEMONS_ARGV'] = @controller_argv.join(' ') ARGV.clear ARGV.concat @app_argv if @app_argv started() # TODO: begin - rescue - end around this and exception logging load script() end def start_proc return unless p = options[:proc] myproc = proc do @pid.pid = Process.pid # We need this to remove the pid-file if the applications exits by itself. # Note that at_text will only be run if the applications exits by calling # exit, and not if it calls exit! (so please don't call exit! # in your application! # at_exit { begin; @pid.cleanup; rescue ::Exception; end # If the option :backtrace is used and the application did exit by itself # create a exception log. if options[:backtrace] and not options[:ontop] and not $daemons_sigterm begin; exception_log(); rescue ::Exception; end end } # This part is needed to remove the pid-file if the application is killed by # daemons or manually by the user. # Note that the applications is not supposed to overwrite the signal handler for # 'TERM'. # $daemons_stop_proc = options[:stop_proc] trap(SIGNAL) { begin if $daemons_stop_proc $daemons_stop_proc.call end rescue ::Exception end begin; @pid.cleanup; rescue ::Exception; end $daemons_sigterm = true if options[:hard_exit] exit! else exit end } started() p.call() end unless options[:ontop] Daemonize.call_as_daemon(myproc, output_logfile, @group.app_name) else Daemonize.simulate(output_logfile) myproc.call # why did we use this?? # Thread.new(&options[:proc]) # why did we use the code below?? # unless pid = Process.fork # @pid.pid = pid # Daemonize.simulate(logfile) # options[:proc].call # exit # else # Process.detach(@pid.pid) # end end end def start change_privilege @group.create_monitor(@group.applications[0] || self) unless options[:ontop] # we don't monitor applications in the foreground case options[:mode] when :none # this is only used to daemonize the currently running process start_none when :exec start_exec when :load start_load when :proc start_proc else start_load end end def started if pid = @pid.pid puts "#{self.group.app_name}: process with pid #{pid} started." STDOUT.flush end 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 def reload if @pid.pid == 0 zap start else begin Process.kill('HUP', @pid.pid) rescue # ignore end end 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. # def exception_log return unless logfile require 'logger' l_file = Logger.new(logfile) # the code below finds the last exception e = nil ObjectSpace.each_object {|o| if ::Exception === o e = o end } 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 ***" l_file.error e 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 ***" # this code logs every exception found in memory ObjectSpace.each_object {|o| if ::Exception === o l_file.error o end } l_file.close end def stop(no_wait = false) if not running? self.zap return end pid = @pid.pid # Catch errors when trying to kill a process that doesn't # exist. This happens when the process quits and hasn't been # restarted by the monitor yet. By catching the error, we allow the # pid file clean-up to occur. begin Process.kill(SIGNAL, pid) rescue Errno::ESRCH => e puts "#{e} #{pid}" puts "deleting pid-file." end if not no_wait if @force_kill_waittime > 0 puts "#{self.group.app_name}: trying to stop process with pid #{pid}..." STDOUT.flush begin Timeout::timeout(@force_kill_waittime) { while Pid.running?(pid) sleep(0.2) end } rescue Timeout::Error puts "#{self.group.app_name}: process with pid #{pid} won't stop, we forcefully kill it..." STDOUT.flush begin Process.kill('KILL', pid) rescue Errno::ESRCH end begin Timeout::timeout(20) { while Pid.running?(pid) sleep(1) end } rescue Timeout::Error puts "#{self.group.app_name}: unable to forcefully kill process with pid #{pid}." STDOUT.flush end end end end sleep(0.1) unless Pid.running?(pid) # We try to remove the pid-files by ourselves, in case the application # didn't clean it up. begin; @pid.cleanup; rescue ::Exception; end puts "#{self.group.app_name}: process with pid #{pid} successfully stopped." STDOUT.flush end end def zap @pid.cleanup end def zap! begin; @pid.cleanup; rescue ::Exception; end end def show_status running = self.running? 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 + ')' : ''}" 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. # def running? if @pid.exist? return Pid.running?(@pid.pid) end return false end end end daemons-1.1.9/lib/daemons/daemonize.rb0000644000004100000410000000766512246432262017715 0ustar www-datawww-datamodule Daemonize # Try to fork if at all possible retrying every 5 sec if the # maximum process limit for the system has been reached def safefork tryagain = true while tryagain tryagain = false begin if pid = fork return pid end rescue Errno::EWOULDBLOCK sleep 5 tryagain = true end end end module_function :safefork # 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 def simulate(logfile_name = nil) # Release old working directory Dir.chdir "/" close_io() # Free STDIN and point them somewhere sensible begin; STDIN.reopen "/dev/null"; rescue ::Exception; end end module_function :simulate # Call a given block as a daemon def call_as_daemon(block, logfile_name = nil, app_name = nil) # we use a pipe to return the PID of the daemon rd, wr = IO.pipe if tmppid = safefork # in the parent wr.close pid = rd.read.to_i rd.close Process.waitpid(tmppid) return pid else # in the child rd.close # Detach from the controlling terminal unless sess_id = Process.setsid raise Daemons.RuntimeException.new('cannot detach from controlling terminal') end # Prevent the possibility of acquiring a controlling terminal trap 'SIGHUP', 'IGNORE' exit if pid = safefork wr.write Process.pid wr.close $0 = app_name if app_name # Release old working directory Dir.chdir "/" close_io() redirect_io(logfile_name) # Split rand streams between spawning and daemonized process srand block.call exit end end module_function :call_as_daemon # Transform the current process into a daemon def daemonize(logfile_name = nil, app_name = nil) # Fork and exit from the parent safefork and exit # Detach from the controlling terminal unless sess_id = Process.setsid raise Daemons.RuntimeException.new('cannot detach from controlling terminal') end # Prevent the possibility of acquiring a controlling terminal trap 'SIGHUP', 'IGNORE' exit if pid = safefork $0 = app_name if app_name # Release old working directory Dir.chdir "/" close_io() redirect_io(logfile_name) # Split rand streams between spawning and daemonized process srand return sess_id end module_function :daemonize def close_io() # Make sure all input/output streams are closed # Part I: close all IO objects (except for STDIN/STDOUT/STDERR) ObjectSpace.each_object(IO) do |io| unless [STDIN, STDOUT, STDERR].include?(io) begin unless io.closed? io.close end rescue ::Exception end end end # Make sure all input/output streams are closed # Part II: close all file decriptors (except for STDIN/STDOUT/STDERR) ios = Array.new(8192) {|i| IO.for_fd(i) rescue nil}.compact ios.each do |io| next if io.fileno < 3 io.close end end module_function :close_io # Free STDIN/STDOUT/STDERR file descriptors and # point them somewhere sensible def redirect_io(logfile_name) begin; STDIN.reopen "/dev/null"; rescue ::Exception; end if logfile_name begin STDOUT.reopen logfile_name, "a" File.chmod(0644, logfile_name) STDOUT.sync = true rescue ::Exception begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end end else begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end end begin; STDERR.reopen STDOUT; rescue ::Exception; end STDERR.sync = true end module_function :redirect_io end daemons-1.1.9/lib/daemons/etc_extension.rb0000644000004100000410000000032412246432262020572 0ustar www-datawww-datarequire 'etc' Etc.instance_eval do def groupname(gid) Etc.group {|e| return e.name if gid == e.gid } nil end def username(uid) Etc.passwd {|e| return e.name if uid == e.uid } nil end end daemons-1.1.9/lib/daemons/change_privilege.rb0000644000004100000410000000110712246432262021216 0ustar www-datawww-datarequire 'daemons/etc_extension' class CurrentProcess def self.change_privilege(user, group=user) puts "Changing process privilege to #{user}:#{group}" uid, gid = Process.euid, Process.egid target_uid = Etc.getpwnam(user).uid target_gid = Etc.getgrnam(group).gid if uid != target_uid || gid != target_gid Process.initgroups(user, target_gid) Process::GID.change_privilege(target_gid) Process::UID.change_privilege(target_uid) end rescue Errno::EPERM => e raise "Couldn't change user and group to #{user}:#{group}: #{e}" end enddaemons-1.1.9/lib/daemons/pid.rb0000644000004100000410000000524612246432262016507 0ustar www-datawww-data module Daemons class Pid def Pid.running?(pid) return false unless pid # Check if process is in existence # The simplest way to do this is to send signal '0' # (which is a single system call) that doesn't actually # send a signal begin Process.kill(0, pid) return true rescue Errno::ESRCH return false rescue ::Exception # for example on EPERM (process exists but does not belong to us) return true #rescue Errno::EPERM # return false end end # def Pid.running?(pid, additional = nil) # match_pid = Regexp.new("^\\s*#{pid}\\s") # got_match = false # # #ps_all = IO.popen('ps ax') # the correct syntax is without a dash (-) ! # ps_in, ps_out, ps_err = Open3.popen3('ps ax') # the correct syntax is without a dash (-) ! # # return true unless ps_out.gets # # begin # ps_out.each { |psline| # next unless psline =~ match_pid # got_match = true # got_match = false if additional and psline !~ /#{additional}/ # break # } # ensure # begin; begin; ps_in.close; rescue ::Exception; end; begin; ps_out.close; rescue ::Exception; end; ps_err.close; rescue ::Exception; end # end # # # an alternative would be to use the code below, but I don't know whether this is portable # # `ps axo pid=`.split.include? pid.to_s # # return got_match # end # Returns the directory that should be used to write the pid file to # depending on the given mode. # # Some modes may require an additionaly hint, others may determine # the directory automatically. # # If no valid directory is found, returns nil. # def Pid.dir(dir_mode, dir, script) # nil script parameter is allowed as long as dir_mode is not :script return nil if dir_mode == :script && script.nil? case dir_mode when :normal return File.expand_path(dir) when :script return File.expand_path(File.join(File.dirname(script),dir)) when :system return '/var/run' else raise Error.new("pid file mode '#{dir_mode}' not implemented") end end # Initialization method def initialize end # Get method def pid end # Set method def pid=(p) end # Check whether the process is running def running? return Pid.running?(pid()) end # Cleanup method def cleanup end # Exist? method def exist? true end end enddaemons-1.1.9/lib/daemons/application_group.rb0000644000004100000410000001066112246432262021447 0ustar www-datawww-data module Daemons class ApplicationGroup attr_reader :app_name attr_reader :script attr_reader :monitor #attr_reader :controller attr_reader :options attr_reader :applications attr_accessor :controller_argv attr_accessor :app_argv attr_accessor :dir_mode attr_accessor :dir # true if the application is supposed to run in multiple instances attr_reader :multiple def initialize(app_name, options = {}) @app_name = app_name @options = options if options[:script] @script = File.expand_path(options[:script]) end #@controller = controller @monitor = nil #options = controller.options @multiple = options[:multiple] || false @dir_mode = options[:dir_mode] || :script @dir = options[:dir] || '' @keep_pid_files = options[:keep_pid_files] || false @no_pidfiles = options[:no_pidfiles] || false #@applications = find_applications(pidfile_dir()) @applications = [] end # Setup the application group. # Currently this functions calls find_applications which finds # all running instances of the application and populates the application array. # def setup @applications = find_applications(pidfile_dir()) end def pidfile_dir PidFile.dir(@dir_mode, @dir, script) end def find_applications(dir) if @no_pidfiles return find_applications_by_app_name(app_name) else return find_applications_by_pidfiles(dir) end end # TODO: identifiy the monitor process def find_applications_by_app_name(app_name) pids = [] begin x = `ps auxw | grep -v grep | awk '{print $2, $11, $12}' | grep #{app_name}` if x && x.chomp! processes = x.split(/\n/).compact processes = processes.delete_if do |p| pid, name, add = p.split(/\s/) # We want to make sure that the first part of the process name matches # so that app_name matches app_name_22 app_name != name[0..(app_name.length - 1)] and not add.include?(app_name) end pids = processes.map {|p| p.split(/\s/)[0].to_i} end rescue ::Exception end return pids.map {|f| app = Application.new(self, {}, PidMem.existing(f)) setup_app(app) app } end def find_applications_by_pidfiles(dir) pid_files = PidFile.find_files(dir, app_name, ! @keep_pid_files) #pp pid_files @monitor = Monitor.find(dir, app_name + '_monitor') pid_files.reject! {|f| f =~ /_monitor.pid$/} return pid_files.map {|f| app = Application.new(self, {}, PidFile.existing(f)) setup_app(app) app } end def new_application(add_options = {}) if @applications.size > 0 and not @multiple if options[:force] @applications.delete_if {|a| unless a.running? a.zap true end } end raise RuntimeException.new('there is already one or more instance(s) of the program running') unless @applications.empty? end app = Application.new(self, add_options) setup_app(app) @applications << app return app end def setup_app(app) app.controller_argv = @controller_argv app.app_argv = @app_argv end private :setup_app def create_monitor(an_app) return if @monitor if options[:monitor] @monitor = Monitor.new(an_app) @monitor.start(@applications) end end def start_all @monitor.stop if @monitor @monitor = nil @applications.each {|a| fork { a.start } } end def stop_all(no_wait = false) @monitor.stop if @monitor threads = [] @applications.each {|a| threads << Thread.new do a.stop(no_wait) end } threads.each {|t| t.join} end def reload_all @applications.each {|a| a.reload} end def zap_all @monitor.stop if @monitor @applications.each {|a| a.zap} end def show_status @applications.each {|a| a.show_status} end end end daemons-1.1.9/lib/daemons/pidfile.rb0000644000004100000410000000626012246432262017344 0ustar www-datawww-datarequire 'daemons/pid' module Daemons # === What is a Pid-File? # A Pid-File is a file containing the process identification number # (pid) that is stored in a well-defined location of the filesystem thus allowing other # programs to find out the pid of a running script. # # Daemons needs the pid of the scripts that are currently running in the background # to send them so called _signals_. Daemons uses the +TERM+ signal to tell the script # to exit when you issue a +stop+ command. # # === How does a Pid-File look like? # # Pid-Files generated by Daemons have to following format: # .rb.pid # (Note that is omitted if only one instance of the script can # run at any time) # # Each file just contains one line with the pid as string (for example 6432). # # === Where are the Pid-Files stored? # # Daemons is configurable to store the Pid-Files relative to three different locations: # 1. in a directory relative to the directory where the script (the one that is supposed to run # as a daemon) resides (:script option for :dir_mode) # 2. in a directory given by :dir (:normal option for :dir_mode) # 3. in the preconfigured directory /var/run (:system option for :dir_mode) # class PidFile < Pid attr_reader :dir, :progname, :multiple, :number def PidFile.find_files(dir, progname, delete = false) files = Dir[File.join(dir, "#{progname}*.pid")] files.delete_if {|f| not (File.file?(f) and File.readable?(f))} if delete files.delete_if do |f| pid = File.open(f) {|h| h.read}.to_i rsl = ! Pid.running?(pid) if rsl puts "pid-file for killed process #{pid} found (#{f}), deleting." begin; File.unlink(f); rescue ::Exception; end end rsl end end return files end def PidFile.existing(path) new_instance = PidFile.allocate new_instance.instance_variable_set(:@path, path) def new_instance.filename return @path end return new_instance end def initialize(dir, progname, multiple = false) @dir = File.expand_path(dir) @progname = progname @multiple = multiple @number = nil @number = 0 if multiple if multiple while File.exist?(filename) and @number < 1024 @number += 1 end if @number == 1024 raise RuntimeException('cannot run more than 1024 instances of the application') end end end def filename File.join(@dir, "#{@progname}#{ @number or '' }.pid") end def exist? File.exist? filename end def pid=(p) File.open(filename, 'w') {|f| f.chmod(0644) f.puts p #Process.pid } end def cleanup File.delete(filename) if pid == Process.pid end def pid begin File.open(filename) {|f| return f.gets.to_i } rescue ::Exception return nil end end end enddaemons-1.1.9/lib/daemons/controller.rb0000644000004100000410000000700112246432262020105 0ustar www-datawww-data module Daemons class Controller attr_reader :app_name attr_reader :group attr_reader :options COMMANDS = [ 'start', 'stop', 'restart', 'run', 'zap', 'reload', 'status' ] def initialize(options = {}, argv = []) @options = options @argv = argv # Allow an app_name to be specified. If not specified use the # basename of the script. @app_name = options[:app_name] if options[:script] @script = File.expand_path(options[:script]) @app_name ||= File.split(@script)[1] end @app_name ||= 'unknown_application' @command, @controller_part, @app_part = Controller.split_argv(argv) #@options[:dir_mode] ||= :script @optparse = Optparse.new(self) end # This function is used to do a final update of the options passed to the application # before they are really used. # # Note that this function should only update @options and no other variables. # def setup_options #@options[:ontop] ||= true end def run @options.update @optparse.parse(@controller_part).delete_if {|k,v| !v} setup_options() #pp @options @group = ApplicationGroup.new(@app_name, @options) @group.controller_argv = @controller_part @group.app_argv = @app_part @group.setup case @command when 'start' @group.new_application.start when 'run' @options[:ontop] ||= true @group.new_application.start when 'stop' @group.stop_all(@options[:no_wait]) when 'restart' unless @group.applications.empty? @group.stop_all sleep(1) @group.start_all else puts "Warning: no instances running. Starting..." @group.new_application.start end when 'reload' @group.reload_all when 'zap' @group.zap_all when 'status' unless @group.applications.empty? @group.show_status else puts "#{@group.app_name}: no instances running" end when nil raise CmdException.new('no command given') #puts "ERROR: No command given"; puts #print_usage() #raise('usage function not implemented') else raise Error.new("command '#{@command}' not implemented") end end # Split an _argv_ array. # +argv+ is assumed to be in the following format: # ['command', 'controller option 1', 'controller option 2', ..., '--', 'app option 1', ...] # # command must be one of the commands listed in COMMANDS # # *Returns*: the command as a string, the controller options as an array, the appliation options # as an array # def Controller.split_argv(argv) argv = argv.dup command = nil controller_part = [] app_part = [] if COMMANDS.include? argv[0] command = argv.shift end if i = argv.index('--') # Handle the case where no controller options are given, just # options after "--" as well (i == 0) controller_part = (i == 0 ? [] : argv[0..i-1]) app_part = argv[i+1..-1] else controller_part = argv[0..-1] end return command, controller_part, app_part end end end daemons-1.1.9/lib/daemons/pidmem.rb0000644000004100000410000000044012246432262017175 0ustar www-datawww-datarequire 'daemons/pid' module Daemons class PidMem < Pid attr_accessor :pid def PidMem.existing(numeric_pid) new_instance = PidMem.allocate new_instance.instance_variable_set(:@pid, numeric_pid) return new_instance end end end daemons-1.1.9/lib/daemons/monitor.rb0000644000004100000410000000576512246432262017430 0ustar www-datawww-data module Daemons require 'daemons/daemonize' class Monitor def self.find(dir, app_name) pid = PidFile.find_files(dir, app_name, false)[0] if pid pid = PidFile.existing(pid) unless PidFile.running?(pid.pid) begin; pid.cleanup; rescue ::Exception; end return end monitor = self.allocate monitor.instance_variable_set(:@pid, pid) return monitor end return nil end def initialize(an_app) @app = an_app @app_name = an_app.group.app_name + '_monitor' if an_app.pidfile_dir @pid = PidFile.new(an_app.pidfile_dir, @app_name, false) else @pid = PidMem.new end end def watch(applications) sleep(30) loop do applications.each {|a| sleep(10) unless a.running? a.zap! Process.detach(fork { a.start }) sleep(10) end } sleep(30) end end private :watch def start_with_pidfile(applications) fork do Daemonize.daemonize(nil, @app_name) begin @pid.pid = Process.pid # at_exit { # begin; @pid.cleanup; rescue ::Exception; end # } # This part is needed to remove the pid-file if the application is killed by # daemons or manually by the user. # Note that the applications is not supposed to overwrite the signal handler for # 'TERM'. # # trap('TERM') { # begin; @pid.cleanup; rescue ::Exception; end # exit # } watch(applications) rescue ::Exception => e begin File.open(@app.logfile, 'a') {|f| f.puts Time.now f.puts e f.puts e.backtrace.inspect } ensure begin; @pid.cleanup; rescue ::Exception; end exit! end end end end private :start_with_pidfile def start_without_pidfile(applications) Thread.new { watch(applications) } end private :start_without_pidfile def start(applications) return if applications.empty? if @pid.kind_of?(PidFile) start_with_pidfile(applications) else start_without_pidfile(applications) end end def stop begin pid = @pid.pid Process.kill(Application::SIGNAL, pid) Timeout::timeout(5) { while Pid.running?(pid) sleep(0.1) end } rescue ::Exception => e puts "#{e} #{pid}" puts "deleting pid-file." end # We try to remove the pid-files by ourselves, in case the application # didn't clean it up. begin; @pid.cleanup; rescue ::Exception; end end end enddaemons-1.1.9/lib/daemons/cmdline.rb0000644000004100000410000000624412246432262017345 0ustar www-datawww-data module Daemons class Optparse attr_reader :usage def initialize(controller) @controller = controller @options = {} @opts = OptionParser.new do |opts| opts.banner = "" # opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| # @options[:verbose] = v # end opts.on("-t", "--ontop", "Stay on top (does not daemonize)") do |t| @options[:ontop] = t end opts.on("-f", "--force", "Force operation") do |t| @options[:force] = t end opts.on("-n", "--no_wait", "Do not wait for processes to stop") do |t| @options[:no_wait] = t end #opts.separator "" #opts.separator "Specific options:" opts.separator "" opts.separator "Common options:" # No argument, shows at tail. This will print an options summary opts.on_tail("-h", "--help", "Show this message") do #puts opts #@usage = controller.print_usage() exit end # Switch to print the version. opts.on_tail("--version", "Show version") do puts "daemons version #{Daemons::VERSION}" exit end end begin @usage = @opts.to_s rescue ::Exception # work around a bug in ruby 1.9 @usage = < -- " puts puts "* where is one of:" puts " start start an instance of the application" puts " stop stop all instances of the application" puts " restart stop all instances and restart them afterwards" puts " reload send a SIGHUP to all instances of the application" puts " run start the application and stay on top" puts " zap set the application to a stopped state" puts " status show status (PID) of application instances" puts puts "* and where may contain several of the following:" puts @optparse.usage end def catch_exceptions(&block) begin block.call rescue CmdException, OptionParser::ParseError => e puts "ERROR: #{e.to_s}" puts print_usage() rescue RuntimeException => e puts "ERROR: #{e.to_s}" end end end end daemons-1.1.9/lib/daemons/exceptions.rb0000644000004100000410000000056412246432262020112 0ustar www-datawww-data module Daemons class Exception < ::RuntimeError end class RuntimeException < Exception end class CmdException < Exception end class Error < Exception end class SystemError < Error attr_reader :system_error def initialize(msg, system_error) super(msg) @system_error = system_error end end enddaemons-1.1.9/lib/daemons.rb0000644000004100000410000003202712246432262015730 0ustar www-datawww-datarequire 'optparse' require 'optparse/time' require 'daemons/pidfile' require 'daemons/cmdline' require 'daemons/exceptions' require 'daemons/monitor' require 'daemons/application' require 'daemons/application_group' require 'daemons/controller' require 'timeout' # All functions and classes that Daemons provides reside in this module. # # Daemons is normally invoked by one of the following four ways: # # 1. Daemons.run(script, options): # This is used in wrapper-scripts that are supposed to control other ruby scripts or # external applications. Control is completely passed to the daemons library. # Such wrapper script need to be invoked with command line options like 'start' or 'stop' # to do anything useful. # # 2. Daemons.run_proc(app_name, options) { (...) }: # This is used in wrapper-scripts that are supposed to control a proc. # Control is completely passed to the daemons library. # Such wrapper scripts need to be invoked with command line options like 'start' or 'stop' # to do anything useful. # # 3. Daemons.call(options) { block }: # Execute the block in a new daemon. Daemons.call will return immediately # after spawning the daemon with the new Application object as a return value. # # 4. Daemons.daemonize(options): # Daemonize the currently runnig process, i.e. the calling process will become a daemon. # # == What does daemons internally do with my daemons? # *or*:: why do my daemons crash when they try to open a file? # *or*:: why can I not see any output from the daemon on the console (when using for example +puts+)? # # From a technical aspect of view, daemons does the following when creating a daemon: # # 1. Forks a child (and exits the parent process, if needed) # 2. Becomes a session leader (which detaches the program from # the controlling terminal). # 3. Forks another child process and exits first child. This prevents # the potential of acquiring a controlling terminal. # 4. Changes the current working directory to "/". # 5. Clears the file creation mask (sets +umask+ to 0000). # 6. Closes file descriptors (reopens +STDOUT+ and +STDERR+ to point to a logfile if # possible). # # So what does this mean for your daemons: # - the current directory is '/' # - you cannot receive any input from the console (for example no +gets+) # - you cannot output anything from the daemons with +puts+/+print+ unless a logfile is used # # == How do PidFiles work? Where are they stored? # # Also, you are maybe interested in reading the documentation for the class PidFile. # There you can find out about how Daemons works internally and how and where the so # called PidFiles are stored. # module Daemons VERSION = "1.1.9" require 'daemons/daemonize' # Passes control to Daemons. # This is used in wrapper-scripts that are supposed to control other ruby scripts or # external applications. Control is completely passed to the daemons library. # Such wrapper script should be invoked with command line options like 'start' or 'stop' # to do anything useful. # # +script+:: This is the path to the script that should be run as a daemon. # Please note that Daemons runs this script with load