#!/usr/bin/python
#
# anaconda: The Red Hat Linux Installation program
#
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
# Red Hat, Inc.  All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty 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, see <http://www.gnu.org/licenses/>.
#
# Author(s): Brent Fox <bfox@redhat.com>
#            Mike Fulbright <msf@redhat.com>
#            Jakub Jelinek <jakub@redhat.com>
#            Jeremy Katz <katzj@redhat.com>
#            Chris Lumens <clumens@redhat.com>
#            Paul Nasrat <pnasrat@redhat.com>
#            Erik Troan <ewt@rpath.com>
#            Matt Wilson <msw@rpath.com>
#

# This toplevel file is a little messy at the moment...

import sys, os, re, time, subprocess
from optparse import OptionParser
from tempfile import mkstemp

# keep up with process ID of miniwm if we start it

miniwm_pid = None

# Make sure messages sent through python's warnings module get logged.
def AnacondaShowWarning(message, category, filename, lineno, file=sys.stderr, line=None):
    log.warning("%s" % warnings.formatwarning(message, category, filename, lineno, line))

# start miniWM
def startMiniWM(root='/'):
    (rd, wr) = os.pipe()
    childpid = os.fork()
    if not childpid:
        if os.access("./mini-wm", os.X_OK):
            cmd = "./mini-wm"
        elif os.access(root + "/usr/bin/mini-wm", os.X_OK):
            cmd = root + "/usr/bin/mini-wm"
        else:
            return None
        
        os.dup2(wr, 1)
        os.close(wr)
        args = [cmd, '--display', ':1']
        os.execv(args[0], args)
        sys.exit (1)
    else:
        # We need to make sure that mini-wm is the first client to
        # connect to the X server (see bug #108777).  Wait for mini-wm
        # to write back an acknowledge token.
        os.read(rd, 1)

    return childpid

# function to handle X startup special issues for anaconda
def doStartupX11Actions(runres="800x600"):
    global miniwm_pid

    setupGraphicalLinks()

    # now start up mini-wm
    try:
        miniwm_pid = startMiniWM()
        log.info("Started mini-wm")

    except:
        miniwm_pid = None
        log.error("Unable to start mini-wm")

    if miniwm_pid is not None:
        import xutils
        import gtk

        try:
            i = gtk.Invisible()
            i.selection_owner_set("_ANACONDA_MINI_WM_RUNNING")

            xutils.setRootResource('Xcursor.size', '24')
            xutils.setRootResource('Xcursor.theme', 'Bluecurve')
            xutils.setRootResource('Xcursor.theme_core', 'true')

            xutils.setRootResource('Xft.antialias', '1')
            xutils.setRootResource('Xft.hinting', '1')
            xutils.setRootResource('Xft.hintstyle', 'hintslight')
            xutils.setRootResource('Xft.rgba', 'none')
        except:
            sys.stderr.write("X SERVER STARTED, THEN FAILED");
            raise RuntimeError, "X server failed to start"

def doShutdownX11Actions():
    global miniwm_pid
    
    if miniwm_pid is not None:
        try:
            os.kill(miniwm_pid, 15)
            os.waitpid(miniwm_pid, 0)
        except:
            pass

def setupPythonUpdates():
    from distutils.sysconfig import get_python_lib

    if not os.path.exists("/tmp/updates"):
        return

    for pkg in os.listdir("/tmp/updates"):
        d = "/tmp/updates/%s" % pkg

        if not os.path.isdir(d):
            continue

        # See if the package exists in /usr/lib{64,}/python/?.?/site-packages.
        # If it does, we can set it up as an update.  If not, the pkg is
        # likely a completely new directory and should not be looked at.
        dest = "%s/%s" % (get_python_lib(), pkg)
        if not os.access(dest, os.R_OK):
            dest = "%s/%s" % (get_python_lib(1), pkg)
            if not os.access(dest, os.R_OK):
                continue

        contents = os.listdir(d)

        # Symlink over everything that's in the python libdir but not in
        # the updates directory.
        for f in filter(lambda fn: fn not in contents, os.listdir(dest)):
            if f.endswith(".pyc") or f.endswith(".pyo"):
                continue

            os.symlink("%s/%s" % (dest, f), "/tmp/updates/%s/%s" % (pkg, f))

    if os.access("/tmp/updates/70-anaconda.rules", os.R_OK):
        import shutil
        shutil.copyfile("/tmp/updates/70-anaconda.rules",
                        "/etc/udev/rules.d/70-anaconda.rules")

def parseOptions():
    def resolution_cb (option, opt_str, value, parser):
        parser.values.runres = value

    op = OptionParser()
    # Interface
    op.add_option("-C", "--cmdline", dest="display_mode", action="store_const", const="c",
                  default="g")
    op.add_option("-G", "--graphical", dest="display_mode", action="store_const", const="g")
    op.add_option("-T", "--text", dest="display_mode", action="store_const", const="t")

    # Network
    op.add_option("--noipv4", action="store_true", default=False)
    op.add_option("--noipv6", action="store_true", default=False)
    op.add_option("--proxy")
    op.add_option("--proxyAuth")

    # Method of operation
    op.add_option("--autostep", action="store_true", default=False)
    op.add_option("-d", "--debug", dest="debug", action="store_true", default=False)
    op.add_option("--kickstart", dest="ksfile")
    op.add_option("--rescue", dest="rescue", action="store_true", default=False)
    op.add_option("--targetarch", dest="targetArch", nargs=1, type="string")

    op.add_option("-m", "--method", dest="method", default=None)
    op.add_option("--repo", dest="method", default=None)
    op.add_option("--stage2", dest="stage2", default=None)

    op.add_option("--liveinst", action="store_true", default=False)

    # Display
    op.add_option("--headless", dest="isHeadless", action="store_true", default=False)
    op.add_option("--nofb")
    op.add_option("--resolution", action="callback", callback=resolution_cb, dest="runres",
                  default="800x600", nargs=1, type="string")
    op.add_option("--serial", action="store_true", default=False)
    op.add_option("--usefbx", dest="xdriver", action="store_const", const="fbdev")
    op.add_option("--virtpconsole")
    op.add_option("--vnc", action="store_true", default=False)
    op.add_option("--vncconnect")
    op.add_option("--xdriver", dest="xdriver", action="store", type="string", default=None)

    # Language
    op.add_option("--keymap")
    op.add_option("--kbdtype")
    op.add_option("--lang")

    # Obvious
    op.add_option("--loglevel")
    op.add_option("--syslog")

    op.add_option("--noselinux", dest="selinux", action="store_false", default=True)
    op.add_option("--selinux", action="store_true")

    op.add_option("--nompath", dest="mpath", action="store_false", default=True)
    op.add_option("--mpath", action="store_true")

    op.add_option("--nodmraid", dest="dmraid", action="store_false", default=True)
    op.add_option("--dmraid", action="store_true")

    op.add_option("--noibft", dest="ibft", action="store_false", default=True)
    op.add_option("--ibft", action="store_true")
    op.add_option("--noiscsi", dest="iscsi", action="store_false", default=False)
    op.add_option("--iscsi", action="store_true")

    # Miscellaneous
    op.add_option("--module", action="append", default=[])
    op.add_option("--nomount", dest="rescue_nomount", action="store_true", default=False)
    op.add_option("--updates", dest="updateSrc", action="store", type="string")
    op.add_option("--dogtail", dest="dogtail",   action="store", type="string")
    op.add_option("--dlabel", action="store_true", default=False)

    # Deprecated, unloved, unused
    op.add_option("-r", "--rootPath", dest="unsupportedMode",
                  action="store_const", const="root path")
    op.add_option("-t", "--test", dest="unsupportedMode",
                  action="store_const", const="test")

    return op.parse_args()

def setupPythonPath():
    haveUpdates = False
    for ndx in range(len(sys.path)-1, -1, -1):
        if sys.path[ndx].endswith('updates'):
            haveUpdates = True
            break

    if haveUpdates:
        sys.path.insert(ndx+1, '/usr/lib/anaconda')
        sys.path.insert(ndx+2, '/usr/lib/anaconda/textw')
        sys.path.insert(ndx+3, '/usr/lib/anaconda/iw')
    else:
        sys.path.insert(0, '/usr/lib/anaconda')
        sys.path.insert(1, '/usr/lib/anaconda/textw')
        sys.path.insert(2, '/usr/lib/anaconda/iw')

    sys.path.append('/usr/share/system-config-date')

def addPoPath(dir):
    """ Looks to see what translations are under a given path and tells
    the gettext module to use that path as the base dir """
    for d in os.listdir(dir):
        if not os.path.isdir("%s/%s" %(dir,d)):
            continue
        if not os.path.exists("%s/%s/LC_MESSAGES" %(dir,d)):
            continue
        for basename in os.listdir("%s/%s/LC_MESSAGES" %(dir,d)):
            if not basename.endswith(".mo"):
                continue
            log.info("setting %s as translation source for %s" %(dir, basename[:-3]))
            gettext.bindtextdomain(basename[:-3], dir)

def setupTranslations():
    if os.path.isdir("/tmp/updates/po"):
        addPoPath("/tmp/updates/po")
    gettext.textdomain("anaconda")

def setupEnvironment():
    # Silly GNOME stuff
    if os.environ.has_key('HOME') and not os.environ.has_key("XAUTHORITY"):
        os.environ['XAUTHORITY'] = os.environ['HOME'] + '/.Xauthority'
    os.environ['HOME'] = '/tmp'
    os.environ['LC_NUMERIC'] = 'C'
    os.environ["GCONF_GLOBAL_LOCKS"] = "1"

    # In theory, this gets rid of our LVM file descriptor warnings
    os.environ["LVM_SUPPRESS_FD_WARNINGS"] = "1"

    # make sure we have /sbin and /usr/sbin in our path
    os.environ["PATH"] += ":/sbin:/usr/sbin"

    # we can't let the LD_PRELOAD hang around because it will leak into
    # rpm %post and the like.  ick :/
    if os.environ.has_key("LD_PRELOAD"):
        del os.environ["LD_PRELOAD"]

    os.environ["GLADEPATH"] = "/tmp/updates/:/tmp/updates/ui/:ui/:/usr/share/anaconda/ui/:/usr/share/python-meh/"
    os.environ["PIXMAPPATH"] = "/tmp/updates/pixmaps/:/tmp/updates/:/tmp/product/pixmaps/:/tmp/product/:pixmaps/:/usr/share/anaconda/pixmaps/:/usr/share/pixmaps/:/usr/share/anaconda/:/usr/share/python-meh/"

def setupLoggingFromOpts(opts):
    if opts.loglevel and anaconda_log.logLevelMap.has_key(opts.loglevel):
        level = anaconda_log.logLevelMap[opts.loglevel]
        anaconda_log.logger.tty_loglevel = level
        anaconda_log.setHandlersLevel(log, level)
        anaconda_log.setHandlersLevel(storage.storage_log.logger, level)

    if opts.syslog:
        anaconda_log.logger.remote_syslog = opts.syslog
        if opts.syslog.find(":") != -1:
            (host, port) = opts.syslog.split(":")
            anaconda_log.logger.addSysLogHandler(log, host, port=int(port))
        else:
            anaconda_log.logger.addSysLogHandler(log, opts.syslog)

# ftp installs pass the password via a file in /tmp so
# ps doesn't show it
def expandFTPMethod(str):
    ret = None

    try:
        filename = str[1:]
        ret = open(filename, "r").readline()
        ret = ret[:len(ret) - 1]
        os.unlink(filename)
        return ret
    except:
        return None

def runVNC():
    global vncS
    vncS.startServer()

    child = os.fork()
    if child == 0:
        for p in ('/tmp/updates/pyrc.py', \
                '/usr/lib/anaconda-runtime/pyrc.py'):
            if os.access(p, os.R_OK|os.X_OK):
                os.environ['PYTHONSTARTUP'] = p
                break

        while True:
            # s390/s390x are the only places we /really/ need a shell on tty1,
            # and everywhere else this just gets in the way of pdb.  But we
            # don't want to return, because that'll return try to start X
            # a second time.
            if iutil.isConsoleOnVirtualTerminal():
                    time.sleep(10000)
            else:
                    print _("Press <enter> for a shell")
                    sys.stdin.readline()
                    iutil.execConsole()

def checkMemory(anaconda):
    if iutil.memInstalled() < isys.MIN_RAM:
        from snack import SnackScreen, ButtonChoiceWindow

        screen = SnackScreen()
        ButtonChoiceWindow(screen, _('Fatal Error'),
                            _('You do not have enough RAM to install %s '
                              'on this machine.\n'
                              '\n'
                              'Press <return> to reboot your system.\n')
                           %(product.productName,),
                           buttons = (_("OK"),))
        screen.finish()
        sys.exit(0)

    # override display mode if machine cannot nicely run X
    if not flags.usevnc:
        if anaconda.displayMode not in ('t', 'c') and iutil.memInstalled() < isys.MIN_GUI_RAM:
            stdoutLog.warning(_("You do not have enough RAM to use the graphical "
                                "installer.  Starting text mode."))
            anaconda.displayMode = 't'
            time.sleep(2)

def setupGraphicalLinks():
    for i in ( "imrc", "im_palette.pal", "gtk-2.0", "pango", "fonts",
               "fb.modes"):
        try:
            if os.path.exists("/mnt/runtime/etc/%s" %(i,)):
                os.symlink ("../mnt/runtime/etc/" + i, "/etc/" + i)
        except:
            pass

def handleSshPw(anaconda):
    import users
    u = users.Users(anaconda)

    userdata = anaconda.ksdata.sshpw.dataList()
    for ud in userdata:
        if u.checkUserExists(ud.username, root="/"):
            u.setUserPassword(username=ud.username, password=ud.password,
                              isCrypted=ud.isCrypted, lock=ud.lock)
        else:
            u.createUser(name=ud.username, password=ud.password,
                         isCrypted=ud.isCrypted, lock=ud.lock,
                         root="/")

    del u

def createSshKey(algorithm, keyfile):
    path = '/etc/ssh/%s' % (keyfile,)
    argv = ['-q','-t',algorithm,'-f',path,'-C','','-N','']
    log.info("running \"%s\"" % (" ".join(['ssh-keygen']+argv),))

    so = "/tmp/ssh-keygen-%s-stdout.log" % (algorithm,)
    se = "/tmp/ssh-keygen-%s-stderr.log" % (algorithm,)
    iutil.execWithRedirect('ssh-keygen', argv, stdout=so, stderr=se)

def fork_orphan():
    """Forks an orphan.
    
    Returns 1 in the parent and 0 in the orphaned child.
    """
    intermediate = os.fork()
    if not intermediate:
        if os.fork():
            # the intermediate child dies
            os._exit(0)
        return 0;
    # the original process waits for the intermediate child
    os.waitpid(intermediate, 0)
    return 1

def startSsh():
    if not flags.sshd:
        return   
    if iutil.isS390():
        return

    if not fork_orphan():
        os.mkdir("/var/log", 0755)
        os.open("/var/log/lastlog", os.O_RDWR | os.O_CREAT, 0644)
        ssh_keys = {
            'rsa1':'ssh_host_key',
            'rsa':'ssh_host_rsa_key',
            'dsa':'ssh_host_dsa_key',
            }
        for (algorithm, keyfile) in ssh_keys.items():
            createSshKey(algorithm, keyfile)
        args = ["/sbin/sshd", "-f", "/etc/ssh/sshd_config.anaconda"]
        os.execv("/sbin/sshd", args)
        sys.exit(1)

def startDebugger(signum, frame):
    import epdb
    epdb.serve(skip=1)

class Anaconda(object):
    def __init__(self):
        import desktop, dispatch, firewall, security
        import system_config_keyboard.keyboard as keyboard
        from flags import flags

        self._backend = None
        self._bootloader = None
        self.canReIPL = False
        self.desktop = desktop.Desktop()
        self.dir = None
        self.dispatch = dispatch.Dispatcher(self)
        self.displayMode = None
        self.extraModules = []
        self.firewall = firewall.Firewall()
        self.id = None
        self._instClass = None
        self._instLanguage = None
        self._intf = None
        self.isHeadless = False
        self.keyboard = keyboard.Keyboard()
        self.ksdata = None
        self.mediaDevice = None
        self.methodstr = None
        self._network = None
        self._platform = None
        self.proxy = None
        self.proxyUsername = None
        self.proxyPassword = None
        self.reIPLMessage = None
        self.rescue = False
        self.rescue_mount = True
        self.rootParts = None
        self.rootPath = "/mnt/sysimage"
        self.security = security.Security()
        self.simpleFilter = True
        self.stage2 = None
        self._storage = None
        self._timezone = None
        self.updateSrc = None
        self.upgrade = flags.cmdline.has_key("preupgrade")
        self.upgradeRoot = None
        self.upgradeSwapInfo = None
        self._users = None

        # *sigh* we still need to be able to write this out
        self.xdriver = None

    @property
    def backend(self):
        if not self._backend:
            b = self.instClass.getBackend()
            self._backend = apply(b, (self, ))

        return self._backend

    @property
    def bootloader(self):
        if not self._bootloader:
            import booty
            self._bootloader = booty.getBootloader(self)

        return self._bootloader

    @property
    def firstboot(self):
        from pykickstart.constants import FIRSTBOOT_SKIP, FIRSTBOOT_DEFAULT

        if self.ksdata:
            return self.ksdata.firstboot.firstboot
        elif iutil.isS390():
            return FIRSTBOOT_SKIP
        else:
            return FIRSTBOOT_DEFAULT

    @property
    def instClass(self):
        if not self._instClass:
            from installclass import DefaultInstall
            self._instClass = DefaultInstall()

        return self._instClass

    @property
    def instLanguage(self):
        if not self._instLanguage:
            import language
            self._instLanguage = language.Language(self.displayMode)

        return self._instLanguage

    def _getInterface(self):
        return self._intf

    def _setInterface(self, v):
        # "lambda cannot contain assignment"
        self._intf = v

    def _delInterface(self):
        del self._intf

    intf = property(_getInterface, _setInterface, _delInterface)

    @property
    def network(self):
        if not self._network:
            import network
            self._network = network.Network()

        return self._network

    @property
    def platform(self):
        if not self._platform:
            import platform
            self._platform = platform.getPlatform(self)

        return self._platform

    @property
    def protected(self):
        import stat

        if os.path.exists("/dev/live") and \
           stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]):
            return [os.readlink("/dev/live")]
        elif self.methodstr and self.methodstr.startswith("hd:"):
            method = self.methodstr[3:]
            return [method.split(":", 3)[0]]
        else:
            return []

    @property
    def users(self):
        if not self._users:
            import users
            self._users = users.Users(self)

        return self._users

    @property
    def storage(self):
        if not self._storage:
            import storage
            self._storage = storage.Storage(self)

        return self._storage

    @property
    def timezone(self):
        if not self._timezone:
            import timezone
            self._timezone = timezone.Timezone()
            self._timezone.setTimezoneInfo(self.instLanguage.getDefaultTimeZone(self.rootPath))

        return self._timezone

    def dumpState(self):
        from meh.dump import ReverseExceptionDump
        from inspect import stack as _stack

        # Skip the frames for dumpState and the signal handler.
        stack = _stack()[2:]
        stack.reverse()
        exn = ReverseExceptionDump((None, None, stack), self.mehConfig)

        (fd, filename) = mkstemp("", "anaconda-tb-", "/tmp")
        fo = os.fdopen(fd, "w")

        exn.write(self, fo)

    def initInterface(self):
        if self._intf:
            raise RuntimeError, "Second attempt to initialize the InstallInterface"

        # setup links required by graphical mode if installing and verify display mode
        if self.displayMode == 'g':
            stdoutLog.info (_("Starting graphical installation."))

            try:
                from gui import InstallInterface
            except Exception, e:
                stdoutLog.error("Exception starting GUI installer: %s" %(e,))
                # if we're not going to really go into GUI mode, we need to get
                # back to vc1 where the text install is going to pop up.
                if not flags.livecdInstall:
                    isys.vtActivate (1)
                stdoutLog.warning("GUI installer startup failed, falling back to text mode.")
                self.displayMode = 't'
                if 'DISPLAY' in os.environ.keys():
                    del os.environ['DISPLAY']
                time.sleep(2)

        if self.displayMode == 't':
            from text import InstallInterface
            if not os.environ.has_key("LANG"):
                os.environ["LANG"] = "en_US.UTF-8"

        if self.displayMode == 'c':
            from cmdline import InstallInterface

        self._intf = InstallInterface()
        return self._intf

    def writeXdriver(self, root = None):
        # this should go away at some point, but until it does, we
        # need to keep it around.
        if self.xdriver is None:
            return
        if root is None:
            root = self.rootPath
        if not os.path.isdir("%s/etc/X11" %(root,)):
            os.makedirs("%s/etc/X11" %(root,), mode=0755)
        f = open("%s/etc/X11/xorg.conf" %(root,), 'w')
        f.write('Section "Device"\n\tIdentifier "Videocard0"\n\tDriver "%s"\nEndSection\n' % self.xdriver)
        f.close()

    def setMethodstr(self, methodstr):
        if methodstr.startswith("cdrom://"):
            (device, tree) = string.split(methodstr[8:], ":", 1)

            if not tree.startswith("/"):
                tree = "/%s" %(tree,)

            if device.startswith("/dev/"):
                device = device[5:]

            self.mediaDevice = device
            self.methodstr = "cdrom://%s" % tree
        else:
            self.methodstr = methodstr

    def requiresNetworkInstall(self):
        fail = False
        numNetDevs = isys.getNetworkDeviceCount()

        if self.methodstr is not None:
            if (self.methodstr.startswith("http") or \
                self.methodstr.startswith("ftp://") or \
                self.methodstr.startswith("nfs:")) and \
               numNetDevs == 0:
                fail = True
        elif self.stage2 is not None:
            if self.stage2.startswith("cdrom://") and \
               not os.path.isdir("/mnt/stage2/Packages") and \
               numNetDevs == 0:
                fail = True

        if fail:
            log.error("network install required, but no network devices available")

        return fail

    def write(self):
        self.writeXdriver()
        self.instLanguage.write(self.rootPath)

        self.timezone.write(self.rootPath)
        self.network.write(instPath=self.rootPath, anaconda=self)
        self.desktop.write(self.rootPath)
        self.users.write(self.rootPath)
        self.security.write(self.rootPath)
        self.firewall.write(self.rootPath)

        if self.ksdata:
            for svc in self.ksdata.services.disabled:
                iutil.execWithRedirect("/sbin/chkconfig",
                                       [svc, "off"],
                                       stdout="/dev/tty5", stderr="/dev/tty5",
                                       root=self.rootPath)

            for svc in self.ksdata.services.enabled:
                iutil.execWithRedirect("/sbin/chkconfig",
                                       [svc, "on"],
                                       stdout="/dev/tty5", stderr="/dev/tty5",
                                       root=self.rootPath)

    def writeKS(self, filename):
        import urllib
        from pykickstart.version import versionToString, DEVEL

        f = open(filename, "w")

        f.write("# Kickstart file automatically generated by anaconda.\n\n")
        f.write("#version=%s\n" % versionToString(DEVEL))

        if self.upgrade:
            f.write("upgrade\n");
        else:
            f.write("install\n");

        m = None

        if self.methodstr:
            m = self.methodstr
        elif self.stage2:
            m = self.stage2

        if m:
            if m.startswith("cdrom:"):
                f.write("cdrom\n")
            elif m.startswith("hd:"):
                if m.count(":") == 3:
                    (part, fs, dir) = string.split(m[3:], ":")
                else:
                    (part, dir) = string.split(m[3:], ":")

                f.write("harddrive --partition=%s --dir=%s\n" % (part, dir))
            elif m.startswith("nfs:"):
                if m.count(":") == 3:
                    (server, opts, dir) = string.split(m[4:], ":")
                    f.write("nfs --server=%s --opts=%s --dir=%s" % (server, opts, dir))
                else:
                    (server, dir) = string.split(m[4:], ":")
                    f.write("nfs --server=%s --dir=%s\n" % (server, dir))
            elif m.startswith("ftp://") or m.startswith("http"):
                f.write("url --url=%s\n" % urllib.unquote(m))

        # Some kickstart commands do not correspond to any anaconda UI
        # component.  If this is a kickstart install, we need to make sure
        # the information from the input file ends up in the output file.
        if self.ksdata:
            f.write(self.ksdata.user.__str__())
            f.write(self.ksdata.services.__str__())
            f.write(self.ksdata.reboot.__str__())

        self.instLanguage.writeKS(f)

        if not self.isHeadless:
            self.keyboard.writeKS(f)
            self.network.writeKS(f)

        self.timezone.writeKS(f)
        self.users.writeKS(f)
        self.security.writeKS(f)
        self.firewall.writeKS(f)

        self.storage.writeKS(f)
        self.bootloader.writeKS(f)

        if self.backend:
            self.backend.writeKS(f)
            self.backend.writePackagesKS(f, self)

        # Also write out any scripts from the input ksfile.
        if self.ksdata:
            for s in self.ksdata.scripts:
                f.write(s.__str__())

        # make it so only root can read, could have password
        os.chmod(filename, 0600)

if __name__ == "__main__":
    setupPythonPath()

    # Allow a file to be loaded as early as possible
    try:
        import updates_disk_hook
    except ImportError:
        pass

    # Set up logging as early as possible.
    import logging
    import anaconda_log

    log = logging.getLogger("anaconda")
    stdoutLog = logging.getLogger("anaconda.stdout")

    # pull this in to get product name and versioning
    import product

    # this handles setting up updates for pypackages to minimize the set needed
    setupPythonUpdates()

    import signal, string, isys, iutil, time
    import warnings
    import vnc
    import users
    import kickstart
    import storage.storage_log

    from flags import flags

    # the following makes me very sad. -- katzj
    # we have a slightly different set of udev rules in the second 
    # stage than the first stage.  why this doesn't get picked up
    # automatically, I don't know.  but we need to trigger so that we
    # have all the information about netdevs that we care about for 
    # NetworkManager in the udev database
    from baseudev import udev_trigger, udev_settle
    udev_trigger("net")
    udev_settle()
    # and for added fun, once doesn't seem to be enough?  so we 
    # do it twice, it works and we scream at the world "OH WHY?"
    udev_trigger("net")
    udev_settle()

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

    anaconda = Anaconda()
    warnings.showwarning = AnacondaShowWarning
    setupTranslations()

    # reset python's default SIGINT handler
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    signal.signal(signal.SIGSEGV, isys.handleSegv)

    setupEnvironment()

    pidfile = open("/var/run/anaconda.pid", "w")
    pidfile.write("%s\n" % (os.getpid(),))
    del pidfile
    # add our own additional signal handlers
    signal.signal(signal.SIGHUP, startDebugger)

    # we need to do this really early so we make sure its done before rpm
    # is imported
    iutil.writeRpmPlatform()

    graphical_failed = 0
    vncS = vnc.VncServer()          # The vnc Server object.
    vncS.anaconda = anaconda
    xserver_pid = None

    (opts, args) = parseOptions()

    if opts.unsupportedMode:
        stdoutLog.error("Running anaconda in %s mode is no longer supported." % opts.unsupportedMode)
        sys.exit(0)

    # Now that we've got arguments, do some extra processing.
    setupLoggingFromOpts(opts)

    # Default is to prompt to mount the installed system.
    anaconda.rescue_mount = not opts.rescue_nomount

    if opts.dlabel: #autodetected driverdisc in use
        flags.dlabel = True

    anaconda.displayMode = opts.display_mode
    anaconda.isHeadless = opts.isHeadless

    if opts.noipv4:
        flags.useIPv4 = False

    if opts.noipv6:
        flags.useIPv6 = False

    if opts.proxy:
        anaconda.proxy = opts.proxy

        if opts.proxyAuth:
            filename = opts.proxyAuth
            ret = open(filename, "r").readlines()
            os.unlink(filename)

            anaconda.proxyUsername = ret[0].rstrip()
            if len(ret) == 2:
                anaconda.proxyPassword = ret[1].rstrip()

    if opts.updateSrc:
        anaconda.updateSrc = opts.updateSrc

    if opts.method:
        if opts.method[0] == '@':
            opts.method = expandFTPMethod(opts.method)

        anaconda.setMethodstr(opts.method)
    else:
        anaconda.methodstr = None

    if opts.stage2:
        if opts.stage2[0] == '@':
            opts.stage2 = expandFTPMethod(opts.stage2)

        anaconda.stage2 = opts.stage2

    if opts.liveinst:
        flags.livecdInstall = True

    if opts.module:
        for mod in opts.module:
            (path, name) = string.split(mod, ":")
            anaconda.extraModules.append((path, name))

    if opts.vnc:
        flags.usevnc = 1
        anaconda.displayMode = 'g'
        vncS.recoverVNCPassword()

        # Only consider vncconnect when vnc is a param
        if opts.vncconnect:
            cargs = string.split(opts.vncconnect, ":")
            vncS.vncconnecthost = cargs[0]
            if len(cargs) > 1 and len(cargs[1]) > 0:
                if len(cargs[1]) > 0:
                    vncS.vncconnectport = cargs[1]

    if opts.ibft:
        flags.ibft = 1

    if opts.iscsi:
        flags.iscsi = 1

    if opts.targetArch:
        flags.targetarch = opts.targetArch

    # set flags 
    flags.dmraid = opts.dmraid
    flags.mpath = opts.mpath
    flags.selinux = opts.selinux

    if opts.serial:
        flags.serial = True
    if opts.virtpconsole:
        flags.virtpconsole = opts.virtpconsole

    if opts.xdriver:
        anaconda.xdriver = opts.xdriver
        anaconda.writeXdriver(root="/")

    if not flags.livecdInstall:
        isys.auditDaemon()

    # setup links required for all install types
    for i in ( "services", "protocols", "nsswitch.conf", "joe", "selinux",
               "mke2fs.conf" ):
        try:
            if os.path.exists("/mnt/runtime/etc/" + i):
                os.symlink ("../mnt/runtime/etc/" + i, "/etc/" + i)
        except:
            pass

    # This is the one place we do all kickstart file parsing.
    if opts.ksfile:
        kickstart.preScriptPass(anaconda, opts.ksfile)
        anaconda.ksdata = kickstart.parseKickstart(anaconda, opts.ksfile)
        opts.rescue = anaconda.ksdata.rescue.rescue

        # we need to have a libuser.conf that points to the installer root for
        # sshpw, but after that we start sshd, we need one that points to the
        # install target.
        luserConf = users.createLuserConf(instPath="")
        handleSshPw(anaconda)
        startSsh()
        del(os.environ["LIBUSER_CONF"])

    users.createLuserConf(anaconda.rootPath)

    if opts.rescue:
        anaconda.rescue = True

        import rescue

        if anaconda.ksdata:
            anaconda.instClass.configure(anaconda)

            # We need an interface before running kickstart execute methods for
            # storage.
            from snack import *
            screen = SnackScreen()
            anaconda.intf = rescue.RescueInterface(screen)

            anaconda.ksdata.execute()

            anaconda.intf = None
            screen.finish()

            # command line 'nomount' overrides kickstart /same for vnc/
            anaconda.rescue_mount = not (opts.rescue_nomount or anaconda.ksdata.rescue.nomount)

        rescue.runRescue(anaconda)

        # shouldn't get back here
        sys.exit(1)

    if anaconda.ksdata:
        if anaconda.ksdata.vnc.enabled:
            flags.usevnc = 1
            anaconda.displayMode = 'g'

            if vncS.password == "":
                vncS.password = anaconda.ksdata.vnc.password

            if vncS.vncconnecthost == "":
                vncS.vncconnecthost = anaconda.ksdata.vnc.host

            if vncS.vncconnectport == "":
                vncS.vncconnectport = anaconda.ksdata.vnc.port

        flags.vncquestion = False

    # disable VNC over text question when not enough memory is available
    if iutil.memInstalled() < isys.MIN_GUI_RAM:
        flags.vncquestion = False


    if anaconda.displayMode == 't' and flags.vncquestion: #we prefer vnc over text mode, so ask about that
        title = _("Would you like to use VNC?")
        message = _("Text mode provides a limited set of installation options.  "
                    "It does not allow you to specify your own partitioning "
                    "layout or package selections.  Would you like to use VNC "
                    "mode instead?")

        ret = vnc.askVncWindow(title, message)
        if ret != -1:
            anaconda.displayMode = 'g'
            flags.usevnc = 1
            if ret is not None:
                vncS.password = ret

    if opts.debug:
        flags.debug = True

    log.info("anaconda called with cmdline = %s" %(sys.argv,))
    log.info("Display mode = %s" % anaconda.displayMode)
    log.info("Default encoding = %s " % sys.getdefaultencoding())

    checkMemory(anaconda)

    #
    # now determine if we're going to run in GUI or TUI mode
    #
    # if no X server, we have to use text mode
    if not flags.livecdInstall and not iutil.isS390() and not os.access("/usr/bin/Xorg", os.X_OK):
         stdoutLog.warning(_("Graphical installation is not available. "
                             "Starting text mode."))
         time.sleep(2)
         anaconda.displayMode = 't'

    # s390/iSeries checks
    if anaconda.isHeadless and anaconda.displayMode == "g" and not \
       (os.environ.has_key("DISPLAY") or flags.usevnc):
        stdoutLog.warning(_("DISPLAY variable not set. Starting text mode."))
        anaconda.displayMode = 't'
        graphical_failed = 1
        time.sleep(2)

    # if DISPLAY not set either vnc server failed to start or we're not
    # running on a redirected X display, so start local X server
    if anaconda.displayMode == 'g' and not os.environ.has_key('DISPLAY') and not flags.usevnc:
        try:
            # The following code depends on no SIGCHLD being delivered, possibly
            # only except the one from a failing X.org. Thus make sure before
            # entering this section that all the other children of anaconda have
            # terminated or were forked into an orphan (which won't deliver a
            # SIGCHLD to mess up the fragile signaling below).

            # start X with its USR1 handler set to ignore.  this will make it send
            # us SIGUSR1 if it succeeds.  if it fails, catch SIGCHLD and bomb out.

            def sigchld_handler(num, frame):
                raise OSError

            def sigusr1_handler(num, frame):
                pass

            def preexec_fn():
                signal.signal(signal.SIGUSR1, signal.SIG_IGN)

            old_sigusr1 = signal.signal(signal.SIGUSR1, sigusr1_handler)
            old_sigchld = signal.signal(signal.SIGCHLD, sigchld_handler)
            xout = open("/dev/tty5", "w")

            proc = subprocess.Popen(["Xorg", "-br", "-logfile", "/tmp/X.log",
                                     ":1", "vt6", "-s", "1440", "-ac",
                                     "-nolisten", "tcp", "-dpi", "96"],
                                     close_fds=True, stdout=xout, stderr=xout,
                                     preexec_fn=preexec_fn)

            signal.pause()

            os.environ["DISPLAY"] = ":1"
            doStartupX11Actions(opts.runres)
            xserver_pid = proc.pid
        except (OSError, RuntimeError):
            stdoutLog.warning(" X startup failed, falling back to text mode")
            anaconda.displayMode = 't'
            graphical_failed = 1
            time.sleep(2)
        finally:
            signal.signal(signal.SIGUSR1, old_sigusr1)
            signal.signal(signal.SIGCHLD, old_sigchld)

    if anaconda.displayMode == 't' and graphical_failed and not anaconda.ksdata:
        ret = vnc.askVncWindow()
        if ret != -1:
            anaconda.displayMode = 'g'
            flags.usevnc = 1
            if ret is not None:
                vncS.password = ret

    # if they want us to use VNC do that now
    if anaconda.displayMode == 'g' and flags.usevnc:
        runVNC()
        doStartupX11Actions(opts.runres)

    # with X running we can initialize the UI interface
    anaconda.initInterface()
    anaconda.instClass.configure(anaconda)

    # comment out the next line to make exceptions non-fatal
    from exception import initExceptionHandling
    anaconda.mehConfig = initExceptionHandling(anaconda)

    # add our own additional signal handlers
    signal.signal(signal.SIGUSR2, lambda signum, frame: anaconda.dumpState())

    # download and run Dogtail script
    if opts.dogtail:
       try:
           import urlgrabber

           try:
               fr = urlgrabber.urlopen(opts.dogtail)
           except urlgrabber.grabber.URLGrabError, e:
               log.error("Could not retrieve Dogtail script from %s.\nError was\n%s" % (opts.dogtail, e))
               fr = None
                           
           if fr:
               (fw, testcase) = mkstemp(prefix='testcase.py.', dir='/tmp')
               os.write(fw, fr.read())
               fr.close()
               os.close(fw)
               
               # download completed, run the test
               if not os.fork():
                   # we are in the child
                   os.chmod(testcase, 0755)
                   os.execv(testcase, [testcase])
                   sys.exit(0)
               else:
                   # we are in the parent, sleep to give time for the testcase to initialize
                   # todo: is this needed, how to avoid possible race conditions
                   time.sleep(1)
       except Exception, e:
           log.error("Exception %s while running Dogtail testcase" % e)

    if opts.lang:
        # this is lame, but make things match what we expect (#443408)
        opts.lang = opts.lang.replace(".utf8", ".UTF-8")
        anaconda.dispatch.skipStep("language", permanent = 1)
        anaconda.instLanguage.instLang = opts.lang
        anaconda.instLanguage.systemLang = opts.lang
        anaconda.timezone.setTimezoneInfo(anaconda.instLanguage.getDefaultTimeZone(anaconda.rootPath))

    if opts.keymap:
        anaconda.dispatch.skipStep("keyboard", permanent = 1)
        anaconda.keyboard.set(opts.keymap)
        anaconda.keyboard.activate()

    if anaconda.ksdata:
        import storage

        # Before we set up the storage system, we need to know which disks to
        # ignore, etc.  Luckily that's all in the kickstart data.
        anaconda.storage.zeroMbr = anaconda.ksdata.zerombr.zerombr
        anaconda.storage.ignoredDisks = anaconda.ksdata.ignoredisk.ignoredisk
        anaconda.storage.exclusiveDisks = anaconda.ksdata.ignoredisk.onlyuse

        if anaconda.ksdata.clearpart.type is not None:
            anaconda.storage.clearPartType = anaconda.ksdata.clearpart.type
            anaconda.storage.clearPartDisks = anaconda.ksdata.clearpart.drives
            if anaconda.ksdata.clearpart.initAll:
                anaconda.storage.reinitializeDisks = anaconda.ksdata.clearpart.initAll

        storage.storageInitialize(anaconda)

        # Now having initialized storage, we can apply all the other kickstart
        # commands.  This gives us the ability to check that storage commands
        # are correctly formed and refer to actual devices.
        anaconda.ksdata.execute()

    # set up the headless case
    if anaconda.isHeadless:
        anaconda.dispatch.skipStep("keyboard", permanent = 1)

    if not anaconda.ksdata:
        anaconda.instClass.setSteps(anaconda)
    else:
        kickstart.setSteps(anaconda)

    try:
        anaconda.intf.run(anaconda)
    except SystemExit, code:
        anaconda.intf.shutdown()

    if anaconda.ksdata and anaconda.ksdata.reboot.eject:
        for drive in anaconda.storage.devicetree.devices:
            if drive.type != "cdrom":
                continue

            log.info("attempting to eject %s" % drive.path)
            drive.eject()

    del anaconda.intf

# vim:tw=78:ts=4:et:sw=4
