#!/usr/bin/ruby -w
#
# NAME
#
#   symbiosis-apache-logger - Log access requests on a per-domain basis.
#
# SYNOPSIS
#
#  symbiosis-apache-logger [ --max-files | -f <n> ] [ -s | --sync ]
#                          [ --uid | -u <n> ] | [ --gid | -g <n> ]
#                          [ --log-name | -l <filename> ] [ -h | --help ]
#                          [-m | --manual] [ -v | --verbose ] <default_filename>
#
# OPTIONS
#
#  -f, --max-files <n>     Maxium number of log files to hold open. Defaults to
#                          50.
#
#  -l, --log-name <f>      The name of the generated logs.  Defaults to "access.log"
#
#  -s, --sync              Open the file in sync mode, i.e. all data are
#                          immediately flushed to the OS and not buffered by
#                          the script.
#
#  -u, --uid <u>           Set the UID -- privileges are dropped if this is set.
#
#  -g, --gid <g>           Set the GID
#
#  -h, --help              Show a help message, and exit.
#
#  -m, --manual            Show this manual, and exit.
#
#  -v, --verbose           Show verbose errors
#
# USAGE
#
#  In haste.
#
# AUTHOR
#
#  Patrick J Cherry <patrick@bytemark.co.uk> 
#

require 'getoptlong'
require 'symbiosis/utils'

#
# The options set by the command line.  These are all global variables.
#
help          = false
manual        = false
$VERBOSE      = false
logger_args   = Hash.new
uid          = nil
gid          = nil

opts = GetoptLong.new(
  [ '--help',       '-h', GetoptLong::NO_ARGUMENT ],
  [ '--manual',     '-m', GetoptLong::NO_ARGUMENT ],
  [ '--verbose',    '-v', GetoptLong::NO_ARGUMENT ],
  [ '--max-files',  '-f', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--log-name',   '-l', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--uid',        '-u', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--gid',        '-g', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--sync'       ,'-s', GetoptLong::NO_ARGUMENT ]
)

begin
  opts.each do |opt,arg|
    case opt
    when '--help'
      help = true
    when '--manual'
      manual = true
    when '--verbose'
      $VERBOSE = true
    when "--sync"
      logger_args[:sync_io] = true
    when "--max-files"
      logger_args[:max_files] = arg.to_i
    when "--log-filename"
      logger_args[:log_filename] = arg
    when "--uid"
      uid = arg.to_i
    when "--gid"
      gid = arg.to_i
    end
  end
rescue => err
  # any errors, show the help
  warn err.to_s
  help = true
end

#
# This is the default log name
#
logger_args[:default_filename] = File.expand_path(ARGV.pop) if ARGV.size > 0

#
# Show the manual, or the help
#
Symbiosis::Utils.show_usage( __FILE__ ) if  help
Symbiosis::Utils.show_manual( __FILE__ ) if manual

#
#  If either happened we can exit.
#
if ( help or manual )
  exit 0
end


########################################################################
#
# Drop privs.  Make sure either both UID/GID are set, or neither.
#
unless [uid, gid].all?{|x| x.nil?} or [uid, gid].all?{|x| x.is_a?(Integer)}
  warn "#{$0}: Both UID and GID must be either unset or integers -- unsetting"
  uid = gid = nil
end

unless 0 == Process.uid
  warn "#{$0}: Unable to drop privileges if not running as root."
  uid = gid = nil
end

if uid and gid
  begin
    Process::Sys.setgid(gid) 
    Process::Sys.setuid(uid)
  rescue Errno::EPERM => err
    warn "#{$0}: Unable to drop privileges from #{Process.uid}:#{Process.gid} to #{uid}:#{gid}"
    uid = gid = nil
  end
end

#
# Set up the logger uid/gid.
#
if uid and gid
  logger_args[:uid] = uid 
  logger_args[:gid] = gid
end

require 'symbiosis/apache_logger'
require 'eventmachine'

EventMachine.run do
  logger = EventMachine.attach($stdin, Symbiosis::ApacheLogger, logger_args)

  #
  # trap HUP -- reopen all files.
  #
  %w(HUP USR1).each do |sig|
    trap(sig) do
      warn "#{$0}: Caught #{sig}" if $VERBOSE 
      if EventMachine.reactor_running?
       EventMachine.add_timer(0) do
         if logger.is_a?(Symbiosis::ApacheLogger)
          logger.close_filehandles
          logger.resume
         end
       end
      end
    end
  end
  
  #
  # term INT, TERM -- close all files and exit.
  #
  %w(QUIT TERM INT).each do |sig|
    trap(sig) do
      warn "#{$0}: Caught #{sig}" if $VERBOSE 
      if EventMachine.reactor_running?
       EventMachine.add_timer(0) do
         if logger.is_a?(Symbiosis::ApacheLogger)
          logger.close_filehandles
         end
         EventMachine.stop
       end
      end
      exit 0
    end
  end  
end

