#!/usr/bin/env python
#
# synarere -- a trivial Python IRC bot.
# Copyright (C) 2010 Michael Rodriguez.
# Rights to this code are documented in docs/LICENSE.

"""Main program."""

# Import required Python modules.
import getopt, os, signal, sys

# Import required source modules.
from src import vars, instance, configparser, irc, logger, io, modules

def on_sigint(signum, frame):
    instance.exit('Caught SIGINT (terminal interrupt)', os.EX_OK)

def on_sigterm(signum, frame):
    instance.exit('Caught SIGTERM', os.EX_OK)

def on_sighup(signum, frame):
    instance.conf.rehash()

def print_clo_help():
    """Output command line options and their meanings."""

    print '-c (--config) <config>: Specify the configuration file to use.'
    print '-d (--debug): Start in debug mode.'
    print '-h (--help): Output this message.'
    print '-n (--nofork): Do not fork into the background (will output log messages)'
    print '-p (--pdebug): Start in the Python Debugger.'

def main(argv):
    """Program entry point."""

    # Are we root?
    if os.geteuid() is 0:
        print 'synarere will not run as root.'
        sys.exit(os.EX_SOFTWARE)

    # Parse command line options and parameter list.
    try:
        opts, args = getopt.getopt(argv, 'c:dhnp', ['config=', 'debug', 'help', 'nofork', 'pdebug'])
    except getopt.GetoptError, err:
        print '%s\n' % err
        print_clo_help()
        sys.exit(os.EX_USAGE)

    for opt, arg in opts:
        if opt in ('-c', '--config'):
            vars.config_file = arg
        elif opt in ('-d', '--debug'):
            vars.debug = True
        elif opt in ('-h', '--help'):
            print_clo_help()
            sys.exit(os.EX_OK)
        elif opt in ('-n', '--nofork'):
            vars.fork = False
        elif opt in ('-p', '--pdebug'):
            import pdb
            pdb.set_trace()

    signal.signal(signal.SIGINT, on_sigint)
    signal.signal(signal.SIGTERM, on_sigterm)
    signal.signal(signal.SIGHUP, on_sighup)
    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
    signal.signal(signal.SIGALRM, signal.SIG_IGN)
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
    signal.signal(signal.SIGWINCH, signal.SIG_IGN)
    signal.signal(signal.SIGTTIN, signal.SIG_IGN)
    signal.signal(signal.SIGTTOU, signal.SIG_IGN)
    signal.signal(signal.SIGTSTP, signal.SIG_IGN)

    print 'synarere -- a trivial Python IRC bot.'
    print 'Copyright (C) 2010 Michael Rodriguez.'
    print 'Rights to this code are documented in docs/LICENSE.\n'

    # Initialize the configuration parser.
    try:
        instance.conf = configparser.ConfigParser(vars.config_file)
    except configparser.Exception, errstr:
        print 'A fatal exception occurred while initializing the configuration parser: %s: %s.' % (vars.config_file, errstr)
        sys.exit(os.EX_CONFIG)

    # Check to see if we're already running.
    try:
        pid_file = open(instance.conf.get('options', 'pidfile')[0], 'r')

        try:
            pid = pid_file.read()

            if pid:
                pid = int(pid)

                try:
                    os.kill(pid, 0)
                except OSError:
                    pass

                else:
                    print >> sys.stderr, 'An instance of synarere is already running.'
                    sys.exit(os.EX_SOFTWARE)
        finally:
            pid_file.close()
    except IOError:
        pass

    # Fork into the background.
    if vars.fork:
        try:
            pid = os.fork()
        except OSError, e:
            return (e.errno, e.strerror)

        # This is the child process.
        if pid is 0:
            os.setsid()

            # Now the child fork()'s a child in order to prevent
            # acquisition of a controlling terminal.
            try:
                pid = os.fork()
            except OSError, e:
                return (e.errno, e.strerror)

            # This is the second child process.
            if pid is 0:
                os.chdir(os.getcwd())
                os.umask(0)

            # This is the first child.
            else:
                os._exit(0)

        # This is the parent.
        else:
            print 'PID:', pid
            print 'Running in background mode from: %s\n' % os.getcwd()
            os._exit(0)

        # Try to write the pid file.
        try:             
            pid_file = open(instance.conf.get('options', 'pidfile')[0], 'w')

            try:         
                pid_file.write(str(os.getpid()))
            finally:
                pid_file.close()
        except IOError, e:
            print >> sys.stderr, 'Unable to write PID to %s: %s.' % (os.getpid(), os.strerror(e.args[0]))

        # Try to close all open file descriptors.
        # If we can't find the max number, just close
        # the first 256.
        try:            
            maxfd = os.sysconf('SC_OPEN_MAX')
        except (AttributeError, ValueError):
            maxfd = 256
        
        for fd in range(0, maxfd):
            try:                
                os.close(fd)    
            except OSError:
                pass
            
        # Redirect the standard file descriptors to /dev/null.
        os.open('/dev/null', os.O_RDONLY)                     
        os.open('/dev/null', os.O_RDWR)  
        os.open('/dev/null', os.O_RDWR)
    else:
        print 'PID', os.getpid()
        print 'Running in foreground mode from: %s\n' % os.getcwd()

    # Initialize the logger.
    instance.logger = logger.Logger(instance.conf.get('logger', 'path')[0])

    # Connect to all IRC networks.
    irc.connect_to_all()
    
    # Load all modules.
    modules.load_all_from_conf()

    # Start the loop.
    io.io()

if __name__ == '__main__':
    main(sys.argv[1:])
