Trap HUP not working

I know I must be doing something dumb here, but I can't figure out
what, and I'm about to give up. I'm setting a trap("HUP") to call a
couple of functions (basically to re-read a config). The first time
running a "kill -HUP" on the process works fine, but the second time it
exits with a "hangup". If I modify the trap to look like:

trap("HUP") {
  trap("HUP", "IGNORE")
  fun1
  fun2
}

It doesn't hangup, but only the first "kill -HUP" actually re-reads the
configuration.

This is ruby 1.8.1
Code follows:
<code>
require "yaml"
require "socket"
require "net/smtp"
require "file/tail"
require "getoptlong"

$config = {}
$reread = false
thishost = Socket.gethostname
tivenv = "/etc/Tivoli/setup_env.sh"
usetiv = false
daemon = false
logfile = nil
config_file = nil

opts = GetoptLong.new(
    ["--config-file", "-c", GetoptLong::REQUIRED_ARGUMENT],
    ["--daemon", "-d", GetoptLong::NO_ARGUMENT],
    ["--logfile", "-l", GetoptLong::REQUIRED_ARGUMENT]
)

logan_struct = Struct::new("LoganStruct",
  :pattern, :tmout, :mailhost, :email, :post, :expire)

def daemonize()
    fork and exit
    Process.setsid
    fork and exit
    File.open("/dev/null", "r+") do |devnull|
        $stdin.reopen(devnull)
        $stdout.reopen(devnull)
        $stderr.reopen(devnull)
    end

    Dir.chdir("/")
end

def source(file)
    response_match = Regexp.new(/^([^=]*)=(.*)/)

    `#{ENV['SHELL']} -c ". #{file}; set"`.each_line do |line|
        if response_match.match(line)
            k, v = line.gsub(/^([^=]*)=(.*)/, '\1 \2').split
            ENV[k] = v
        end
    end
end

def parseYamlConfig(file)
    if File.exists?(file) and File.stat(file).readable?
        $config = YAML::load(File.open(file))
    else
        raise "Unusable file #{file}"
    end

    $config
end

def setDefaultConfigVals(tmout = 3600)
    $config["patterns"].each do |pat|
        pat.expire = 0

        case pat.pattern
            when String
                pat.pattern = Regexp.new(pat.pattern)
        end

        if not pat.mailhost
            if $config["mailhost"]
                pat.mailhost = $config["mailhost"]
            end
        end

        if not pat.tmout
            if $config["tmout"]
                pat.tmout = $config["tmout"]
            else
                pat.tmout = tmout
            end
        end
    end
end

def lookForPatternsInLogfile(file)
    if File.exists?(file) and File.stat(file).readable?
        File::Tail::Logfile.open(file, :rewind=>0) do |log|
            log.tail do |line|
                $config["patterns"].each do |pat|
                    if pat.pattern.match(line)
                        yield pat, line
                    end
                end
            end
        end
    end
end

def sendMail(email, alert, host, mailhost = "mailhost")
    message = <<EndOfMsg

···

Subject: Log Analyzer Alert for #{host}
To: #{email}
From: LOGAN

The following pattern match was made on #{host} at #{Time.now}
#{alert}

EndOfMsg

    Net::SMTP.start(mailhost, 25) do |smtp|
        smtp.send_mail(message, '"LOGAN"', email)
    end
end

def postMessage(msg)
    system("postemsg #{msg} > /dev/null 2>&1")
end

opts.each do |opt, arg|
    case opt
        when "--daemon"
            daemon = true
        when "--logfile"
            logfile = File.expand_path(arg)
        when "--config-file"
            config_file = File.expand_path(arg)
    end
end

if not logfile or not config_file
    fail "Usage: #$0 [-d] -c config_file -l logfile"
end

trap("HUP") do
    trap("HUP", "IGNORE")
    parseYamlConfig(config_file) or fail $!
    setDefaultConfigVals
end

parseYamlConfig(config_file) or fail $!
setDefaultConfigVals
daemonize if daemon

if File.exists?(tivenv)
    source(tivenv)
    usetiv = true
end

lookForPatternsInLogfile(logfile) do |pat, msg|
    if pat.expire <= Time.now.to_i
        pat.expire = 0
    end

    if pat.expire == 0
        pat.expire = Time.now.to_i + pat.tmout
        if pat.email
            sendMail(pat.email, msg, thishost, pat.mailhost)
        end

        if pat.post
            postMessage(pat.post) if usetiv
        end
    end
end
</code>

untested, but i think you may have to do something like

I know I must be doing something dumb here, but I can't figure out
what, and I'm about to give up. I'm setting a trap("HUP") to call a
couple of functions (basically to re-read a config). The first time
running a "kill -HUP" on the process works fine, but the second time it
exits with a "hangup". If I modify the trap to look like:

trap("HUP") {
trap("HUP", "IGNORE")
fun1
fun2
}

It doesn't hangup, but only the first "kill -HUP" actually re-reads the
configuration.

maybe something like

   harp:~ > cat a.rb

   hup_proc = lambda {|*a|
     p 42
     trap 'HUP', hup_proc
   }

   trap 'HUP', hup_proc

   3.times{ Process::kill 'HUP', $$ }

   harp:~ > ruby a.rb
   42

hth.

-a

···

On Thu, 2 Jun 2005 wmwilson01@gmail.com wrote:
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

wmwilson01@gmail.com wrote:

I know I must be doing something dumb here, but I can't figure out
what, and I'm about to give up. I'm setting a trap("HUP") to call a
couple of functions (basically to re-read a config). The first time
running a "kill -HUP" on the process works fine, but the second time
it exits with a "hangup". If I modify the trap to look like:

trap("HUP") {
  trap("HUP", "IGNORE")
  fun1
  fun2
}

It doesn't hangup, but only the first "kill -HUP" actually re-reads
the configuration.

You surprise me: you are really surprised that only the first signal is
processed? I mean, you set the signal handler to IGNORE on first run and
apparently you never reset it.

As far as I can see signal handlers are never executed concurrently but if
you want to be sure, I'd use standard thread synchronization:

require 'thread'

$HUP_LOCK = Mutex.new

trap "HUP" do
  $HUP_LOCK.synchronize do
    puts "your code here (queued up)"
  end

  if $HUP_LOCK.try_lock
    puts "your code here (ingore duplicate invocations)"
    $HUP_LOCK.unlock
  end
end

Kind regards

    robert