#--
###############################################################################
# daemonize.rb is a slightly modified version of daemonize.rb was             #
# from the Daemonize Library written by Travis Whitton                        #
# for details, read the notice below                                          #
###############################################################################
#++
#
#
# =Daemonize Library
# 
# February. 4, 2005 Travis Whitton <whitton@atlantic.net>
# 
# Daemonize allows you to easily modify any existing Ruby program to run
# as a daemon. See README.rdoc for more details.
# 
# == How to install
# 1. su to root
# 2. ruby install.rb
# build the docs if you want to
# 3. rdoc --main README.rdoc daemonize.rb README.rdoc
# 
# == Copying
# The Daemonize extension module is copywrited free software by Travis Whitton
# <whitton@atlantic.net>. You can redistribute it under the terms specified in
# the COPYING file of the Ruby distribution.
# 
# == WARRANTY
# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
# PURPOSE.
#
#
# ----
#
# == Purpose
# 
# Daemonize is a module derived from Perl's Proc::Daemon module. This module
# allows you to easily modify any existing Ruby program to run as a daemon.
# A daemon is a process that runs in the background with no controlling terminal.
# Generally servers (like FTP and HTTP servers) run as daemon processes.
# Note, do not make the mistake that a daemon == server. Converting a program
# to a daemon by hand is a relatively simple process; however, this module will
# save you the effort of repeatedly looking up the procedure, and it will also
# insure that your programs are daemonized in the safest and most corrects
# fashion possible.
# 
# == Procedure
# 
# The Daemonize module does the following:
# 
# Forks a child and exits the parent process.
# 
# Becomes a session leader (which detaches the program from
# the controlling terminal).
# 
# Forks another child process and exits first child. This prevents
# the potential of acquiring a controlling terminal.
# 
# Changes the current working directory to "/".
# 
# Clears the file creation mask.
# 
# Closes file descriptors.
# 
# == Example usage
# 
# Using the Daemonize module is extremely simple:
# 
#     require 'daemonize'
# 
#     class TestDaemon
#       include Daemonize
# 
#       def initialize
#         daemonize()
#         loop do
#           # do some work here
#         end
#       end
#     end
# 
# == Credits
# 
# Daemonize was written by Travis Whitton and is based on Perl's
# Proc::Daemonize, which was written by Earl Hood. The above documentation
# is also partially borrowed from the Proc::Daemonize POD documentation.



module Daemonize
  VERSION = "0.1.1m"

  # 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
  
  
  def simulate(logfile_name = nil)
    # NOTE: STDOUT and STDERR will not be redirected to the logfile, because in :ontop mode, we normally want to see the output
    
    Dir.chdir "/"   # Release old working directory

    # Make sure all file descriptors are closed
    ObjectSpace.each_object(IO) do |io|
      unless [STDIN, STDOUT, STDERR].include?(io)
        begin
          unless io.closed?
            io.close
          end
        rescue ::Exception
        end
      end
    end

    # Free file descriptors and
    # point them somewhere sensible
    # STDOUT/STDERR should go to a logfile
    
    begin; STDIN.reopen "/dev/null"; rescue ::Exception; end       
  end
  module_function :simulate
  
  
  def call_as_daemon(block, logfile_name = nil, app_name = nil)
    rd, wr = IO.pipe
    
    if tmppid = safefork
      # parent
      wr.close
      pid = rd.read.to_i
      rd.close
      
      Process.waitpid(tmppid)
      
      return pid
    else
      # 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
      #if oldmode.zero?
        trap 'SIGHUP', 'IGNORE'
        exit if pid = safefork
      #end
  
      wr.write Process.pid
      wr.close
      
      # Always handle INT signal (thanks to Vit Ondruch)
      #trap 'INT', 'DEFAULT'  # make sure we always handle the 'INT' signal
      
      $0 = app_name if app_name
      
      Dir.chdir "/"   # Release old working directory
  
      # Make sure all file descriptors are closed
      ObjectSpace.each_object(IO) do |io|
        unless [STDIN, STDOUT, STDERR].include?(io)
          begin
            unless io.closed?
              io.close
            end
          rescue ::Exception
          end
        end
      end
      
      ios = Array.new(8192){|i| IO.for_fd(i) rescue nil}.compact
      ios.each do |io|
        next if io.fileno < 3
        io.close
      end

  
      redirect_io(logfile_name)  
    
      block.call
      
      exit
    end
  end
  module_function :call_as_daemon
  
  
  # This method causes the current running process to become a daemon
  def daemonize(logfile_name = nil, app_name = nil)
    srand # Split rand streams between spawning and daemonized process
    safefork and exit # Fork and exit from the parent

    # 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
    #if oldmode.zero?
      trap 'SIGHUP', 'IGNORE'
      exit if pid = safefork
    #end

    # Always handle INT signal (thanks to Vit Ondruch)
    #trap 'INT', 'DEFAULT'  # make sure we always handle the 'INT' signal
    
    $0 = app_name if app_name
    
    Dir.chdir "/"   # Release old working directory

    # Make sure all file descriptors are closed
    ObjectSpace.each_object(IO) do |io|
      unless [STDIN, STDOUT, STDERR].include?(io)
        begin
          unless io.closed?
            io.close
          end
        rescue ::Exception
        end
      end
    end

    redirect_io(logfile_name)
    
    #return oldmode ? sess_id : 0   # Return value is mostly irrelevant
    return sess_id
  end
  module_function :daemonize
  
  
  # Free file descriptors and
  # point them somewhere sensible
  # STDOUT/STDERR should go to a logfile
  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
