#!/usr/bin/python2
#
# Chris Lumens <clumens@redhat.com>
#
# Copyright 2007, 2008 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use, modify,
# copy, or redistribute it subject to the terms and conditions of the GNU
# General Public License v.2.  This program is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  Any Red Hat
# trademarks that are incorporated in the source code or documentation are not
# subject to the GNU General Public License and may only be used or replicated
# with the express permission of Red Hat, Inc. 
#

import os, string, sys
from meh.dump import *
from meh.handler import *
from meh import Config as MehConfig

# Since X may be starting up immediately after firstboot crashes, we might
# not have any way to see the traceback.  This lets it be logged somewhere
# useful.  Set this up as early as possible.

# Set up some exception handling as early as possible, in case we traceback
# before any interface is even up, let alone configured.  This lets it be
# logged somewhere useful.  We will override this with python-meh later.
def earlyExnHandler(type, value, tb):
    import tempfile, traceback

    (fd, path) = tempfile.mkstemp("", "firstboot-", "/tmp")
    fo = os.fdopen(fd, "w")

    traceback.print_exception(type, value, tb, None, fo)
    fo.close()

    # Also print on stderr just in case we have time to see the problem.
    traceback.print_exception(type, value, tb)

sys.excepthook = earlyExnHandler

from firstboot.config import *
from firstboot.constants import *
from firstboot.loader import *
import logging
from optparse import OptionParser
from system_config_keyboard.keyboard import Keyboard

import gettext
_ = lambda x: gettext.ldgettext("firstboot", x)

def finish(opts):
    # Now make sure we don't run again on the next reboot.
    if not opts.test and not opts.debug:
        logging.debug("Writing /etc/sysconfig/firstboot file")
        writeSysconfigFile()

    if opts.reconfig and not opts.test:
        logging.debug("Removing /etc/reconfigSys")

        try:
            os.unlink("/etc/reconfigSys")
        except:
            pass

def getRunlevel():
    line = os.popen("/sbin/runlevel", "r").readline()
    line = string.strip(line)

    # This can happen in kadischi, for instance
    if line.startswith("unknown"):
        return 3
    else:
        tokens = string.split(line)
        return int(tokens[-1])

def writeSysconfigFile():
    fd = open("/etc/sysconfig/firstboot", "w")
    fd.write("RUN_FIRSTBOOT=NO\n")
    fd.close()

# Find the first directory in the themes dir that's not the default and use
# that.  If there is no theme, return None and the default will end up getting
# used.
def findDefaultThemeDir():
    import glob
    lst = filter(lambda d: d.find("default") == -1, glob.glob(BASEDIR+"themes/*"))
    if len(lst) == 0:
        return None
    else:
        return lst[0]

if __name__ == "__main__":
    logLevelMap = {"debug": logging.DEBUG, "info": logging.INFO,
                   "warning": logging.WARNING, "error": logging.ERROR,
                   "critical": logging.CRITICAL}

    op = OptionParser()
    op.add_option("-s", "--autoscreenshot", action="store_true", default=False,
                  help="take a screenshot on every page")
    op.add_option("-d", "--debug", action="store_true", default=False,
                  help="enable debugging mode")
    op.add_option("-g", "--forcegui", action="store_true", default=False,
                  help="use the GUI interface no matter what")
    op.add_option("-l", "--loglevel", type="choice",
                  choices=["debug", "info", "warning", "error", "critical"], default="error",
                  help="set the logging level: debug, info, warning, error, or critical [default: %default]")
    op.add_option("-m", "--moduleDir", action="store", default=BASEDIR+"modules/",
                  help="set the directory containing firstboot modules [default: %default]")
    op.add_option("-r", "--reconfig", action="store_true", default=False,
                  help="enable reconfiguration mode")
    op.add_option("-t", "--test", action="store_true", default=False,
                  help="only test, don't configure the system")
    op.add_option("--themeDir", action="store", dest="themeDir", default=findDefaultThemeDir(),
                  help="set the directory containing the theme [default: %default]")
    (opts, args) = op.parse_args()

    config.moduleDir = opts.moduleDir
    config.themeDir = opts.themeDir

    if opts.debug:
        opts.loglevel = "debug"

    if logLevelMap.has_key(opts.loglevel):
        logging.basicConfig(level=logLevelMap[opts.loglevel],
                            format="firstboot %(levelname)s: %(message)s")

    if opts.reconfig:
        logging.info("Starting up in reconfig mode")
        config.mode = config.mode | MODE_RECONFIG

    # First check to see if firstboot should even run.

    # check if firstboot has not been disabled
    config_path = "/etc/sysconfig/firstboot"
    if os.path.isfile(config_path):
        with open(config_path, "r") as f:
            buf = f.readlines()
            # check if one of the options in the config file (stripped of trailing newlines)
            # is equal to the option that disables firstboot
            if "RUN_FIRSTBOOT=NO" in (l.rstrip("\n") for l in buf):
                logging.error(_("Firstboot startup disabled in /etc/sysconfig/firstboot, exiting."))
                os._exit(0)

    # check if we are running as root
    if not opts.test and (os.getuid() > 0 or os.geteuid() > 0):
        logging.error(_("You must be root to run firstboot."))
        os._exit(0)

    # If we have a $DISPLAY set, we are either in debug mode or in reconfig
    # mode from a terminal already running under X.  Otherwise, run in text
    # mode if in runlevel 3, or start up our own X server.
    if os.environ.has_key("DISPLAY") or opts.debug:
        config.needInterface = True
        logging.debug("X is already running, not using any frontend")
    elif (getRunlevel() == 5 or not os.environ.has_key("DISPLAY") and
          opts.forcegui):
        from firstboot.xfrontend import XFrontEnd
        config.frontend = XFrontEnd()
        config.needInterface = True
        logging.debug("Using X frontend")
    else:
        logging.debug("Running text mode interface")
        if os.access("/usr/bin/setup", os.X_OK):
            os.system("/usr/bin/setup")

        finish(opts)
        os._exit(0)

    # If X was already running, we don't need to make a frontend so skip this
    # step.  This also means that frontends can't do anything besides set
    # themselves up.
    if config.frontend is not None:
        logging.debug("Starting frontend")
        config.frontend.start()

    # setup the meh text interface
    import meh.ui.text
    ExceptionIntf = meh.ui.text.TextIntf

    if config.needInterface:
        from firstboot.interface import *
        config.interface = Interface(autoscreenshot=opts.autoscreenshot,
                                     testing=opts.test)
        logging.debug("Using GTK interface")

        # setup the meh gui interface
        import meh.ui.gui
        ExceptionIntf = meh.ui.gui.GraphicalIntf

    # This must come as early as possible so we can present the UI for the
    # widest class of problems, BUT it also has to come late enough for us
    # to have already imported gtk.
    handler_conf = MehConfig(programName="firstboot",
                             programVersion="1.110.15")
    handler = ExceptionHandler(handler_conf, ExceptionIntf(), ExceptionDump)
    handler.install(config)

    config.moduleList = loadModules(config.moduleDir, config.mode)
    if not config.moduleList:
        logging.error(_("No firstboot modules were found."))
        raise RuntimeError, _("No firstboot modules were found.")

    config.interface.moduleList = config.moduleList

    # Set up the keyboard just in case we're running as a real program.
    kbd = Keyboard()
    kbd.read()
    kbd.activate()

    if config.interface is None:
        logging.error(_("Could not create any firstboot interface."))
        raise RuntimeError, _("Could not create any firstboot interface.")

    win = config.interface.createMainWindow()
    config.interface.createSidebar()
    config.interface.createScreens()
    config.interface.run()

    # We arrive back here after the interface has been torn down.  Now
    # kill whatever frontend is running.
    if config.frontend is not None:
        logging.debug("Stopping frontend")
        config.frontend.stop()

    finish(opts)

    # If X is still running for some reason, exiting now should take it down.
    os._exit(0)
