#!/usr/bin/env python
"""
Run an "information broker" that forwards streams of
NetLogger best-practices log files to any number of clients.
Both incoming and outgoing streams are sent over TCP.
"""
__rcsid__ = "$Id$"
__author__ = "Dan Gunter"

import asyncore
import logging
import socket
import sys
import time
from netlogger import info_broker
from netlogger import util
from netlogger.nllog import OptionParser, get_logger

#
# Exceptions
#

# (nada)

#
# Signal handlers
#

# Stop things that are in a loop
g_stop = False

def on_kill(signo, frame):
    """Signal handler for a graceful exit.
    """
    global g_stop
    log = get_logger(__file__)
    log.warn("killed", signo=signo)
    g_stop = True
    time.sleep(1)
    sys.exit(0)

def on_hup(signo, frame):
    pass

#
# Run function
#

def run(host, fanin_port, fanout_port):
    """Bind to the given host:port and run until stopped
    """
    global g_stop
    log = get_logger(__file__)
    input_server = info_broker.InputServer(fanin_port)
    output_server = info_broker.OutputServer(fanout_port)
    input_server.set_output(output_server)
    while not g_stop:
        asyncore.loop()

#
# Main
#

def main():
    """Program entry point.
    """
    # Option parsing setup
    usage = "%prog [options]"
    desc = ' '.join(__doc__.split())
    parser = OptionParser(usage=usage, description=desc)
    parser.add_option('-l', dest='host', metavar='ADDR', default='localhost',
                      help='Bind to local interface ADDR (default=%default')
    parser.add_option('-p', dest='fanin_port', type='int', metavar='PORT',
                      default=info_broker.FANIN_PORT,
                      help="Listen for incoming data streams on PORT"
                      " (default=%default)")
    parser.add_option('-P', dest='fanout_port', type='int', metavar='PORT',
                      default=info_broker.FANOUT_PORT,
                      help="Listen for client connections on PORT"
                      " (default=%default)")
    options, args = parser.parse_args(sys.argv[1:])
    # Check port range
    if options.fanin_port < 0 or options.fanin_port > 65535:
        parser.error("Incoming data port out of range")
    if options.fanout_port < 0 or options.fanout_port > 65535:
        parser.error("Client port out of range")
    # Get log object
    log = get_logger(__file__)  # Must come after parsing args 
    # Init signal handlers
    util.handleSignals(
        (on_kill, ('SIGTERM', 'SIGINT', 'SIGUSR2')),
        (on_hup, ('SIGHUP',)) )
    # Run
    try:
        log.info("run.start")
        run(options.host, options.fanin_port, options.fanout_port)
        log.info("run.end", status=0)
    except Exception, error:
        log.critical("run.error", msg=error, trace=util.traceback())
        status = -1
    return status

if __name__ == '__main__':
    sys.exit(main())
