import rpm, os
rpm.addMacro("_i18ndomains", "redhat-dist");

import iutil, isys
from lilo import LiloConfiguration
arch = iutil.getArch ()
if arch == "sparc":
    from silo import SiloInstall
elif arch == "alpha":
    from milo import MiloInstall, onMILO
import string
import socket
import crypt
import whrandom
import pcmcia
import _balkan
import kudzu
from kbd import Keyboard
from simpleconfig import SimpleConfigFile
from mouse import Mouse
from xf86config import XF86Config
import errno
import raid
import fstab
import time
import gettext_rh
from translate import _

class LogFile:
    def __init__ (self, serial, reconfigOnly, test):
	if serial or reconfigOnly:
	    self.logFile = open("/tmp/install.log", "w")
	elif reconfigOnly:
	    self.logFile = open("/tmp/reconfig.log", "w")
	elif test:
	    self.logFile = open("/tmp/anaconda-debug.log", "w")
	else:
	    self.logFile = open("/dev/tty3", "w")

    def __call__ (self, format, *args):
        if args:
            self.logFile.write ("* %s\n" % (format % args))
        else:
            self.logFile.write ("* %s\n" % format)

    def getFile (self):
        return self.logFile.fileno ()
            
class NetworkDevice (SimpleConfigFile):
    def __str__ (self):
        s = ""
        s = s + "DEVICE=" + self.info["DEVICE"] + "\n"
        keys = self.info.keys ()
        keys.sort ()
        keys.remove ("DEVICE")
        for key in keys:
            s = s + key + "=" + self.info[key] + "\n"
        return s

    def __init__ (self, dev):
        self.info = { "DEVICE" : dev, "ONBOOT" : "yes" }

class Network:
    def __init__ (self):
        self.netdevices = {}
        self.gateway = ""
        self.primaryNS = ""
        self.secondaryNS = ""
        self.ternaryNS = ""
        self.domains = []
        self.readData = 0
	self.isConfigured = 0
        self.hostname = "localhost.localdomain"
        try:
            f = open ("/tmp/netinfo", "r")
        except:
            pass
        else:
            lines = f.readlines ()
	    f.close ()
            info = {}
	    self.isConfigured = 1
            for line in lines:
                netinf = string.splitfields (line, '=')
                info [netinf[0]] = string.strip (netinf[1])
            self.netdevices [info["DEVICE"]] = NetworkDevice (info["DEVICE"])
            if info.has_key ("IPADDR"):
                self.netdevices [info["DEVICE"]].set (("IPADDR", info["IPADDR"]))
            if info.has_key ("NETMASK"):
                self.netdevices [info["DEVICE"]].set (("NETMASK", info["NETMASK"]))
            if info.has_key ("BOOTPROTO"):
                self.netdevices [info["DEVICE"]].set (("BOOTPROTO", info["BOOTPROTO"]))
            if info.has_key ("GATEWAY"):
                self.gateway = info["GATEWAY"]
            if info.has_key ("DOMAIN"):
                self.domains.append(info["DOMAIN"])
            if info.has_key ("HOSTNAME"):
                self.hostname = info["HOSTNAME"]
            
            self.readData = 1
	try:
	    f = open ("/etc/resolv.conf", "r")
	except:
	    pass
	else:
	    lines = f.readlines ()
	    f.close ()
	    for line in lines:
		resolv = string.split (line)
		if resolv[0] == 'nameserver':
		    if self.primaryNS == "":
			self.primaryNS = resolv[1]
		    elif self.secondaryNS == "":
			self.secondaryNS = resolv[1]
		    elif self.ternaryNS == "":
			self.ternaryNS = resolv[1]

    def getDevice(self, device):
	return self.netdevices[device]

    def available (self):
        f = open ("/proc/net/dev")
        lines = f.readlines()
        f.close ()
        # skip first two lines, they are header
        lines = lines[2:]
        for line in lines:
            dev = string.strip (line[0:6])
            if dev != "lo" and not self.netdevices.has_key (dev):
                self.netdevices[dev] = NetworkDevice (dev)
        return self.netdevices

    def lookupHostname (self):
	# can't look things up if they don't exist!
	if not self.hostname or self.hostname == "localhost.localdomain": return None

	if not self.isConfigured:
	    for dev in self.netdevices.values():
		if dev.get('bootproto') == "dhcp":
		    self.primaryNS = isys.pumpNetDevice(dev.get('device'))
		elif dev.get('ipaddr') and dev.get('netmask'):
		    isys.configNetDevice(dev.get('device'),
			    dev.get('ipaddr'), dev.get('netmask'),
			    self.gateway)

	if not self.primaryNS: return

	f = open("/etc/resolv.conf", "w")
	f.write("nameserver %s\n" % self.primaryNS)
	f.close()
	isys.resetResolv()
	isys.setResolvRetry(2)

	try:
	    ip = socket.gethostbyname(self.hostname)
	except socket.error:
	    return None

	return ip

    def nameservers (self):
        return [ self.primaryNS, self.secondaryNS, self.ternaryNS ]

class Password:
    def __init__ (self):
        self.crypt = None
	self.pure = None

    def getPure(self):
	return self.pure

    def set (self, password, isCrypted = 0):
	if isCrypted:
	    self.crypt = password
	    self.pure = None
	else:
            salt = (whrandom.choice (string.letters +
                                     string.digits + './') + 
                    whrandom.choice (string.letters +
                                     string.digits + './'))
            self.crypt = crypt.crypt (password, salt)
	    self.pure = password

    def getCrypted(self):
	return self.crypt

class Desktop (SimpleConfigFile):
    def __init__ (self):
        SimpleConfigFile.__init__ (self)

    def set (self, desktop):
        self.info ['DESKTOP'] = desktop

class Language (SimpleConfigFile):
    def __init__ (self):
        self.info = {}

	if os.access("lang-table", os.R_OK):
	    f = open("lang-table", "r")
	elif os.access("/etc/lang-table", os.R_OK):
	    f = open("/etc/lang-table", "r")
	else:
	    f = open("/usr/lib/anaconda/lang-table", "r")

	lines = f.readlines ()
	f.close()
	self.langs = {}
	self.font = {}
	self.map = {}

	for line in lines:
	    string.strip(line)
	    l = string.split(line)
	    self.langs[l[0]] = l[4]
	    self.font[l[0]] = l[2]
	    self.map[l[0]] = l[3]
	
        # kickstart needs this
        self.abbrevMap = {}
        for (key, value) in self.langs.items ():
            self.abbrevMap[value] = key

	self.setByAbbrev("en_US")

    def available (self):
        return self.langs

    def setByAbbrev(self, lang):
	self.set(self.abbrevMap[lang])
    
    def set (self, lang):
        self.lang = self.langs[lang]
        self.info["LANG"] = self.langs[lang]
        os.environ["LANG"] = self.langs[lang]

	if self.font[lang] != "None":
	    self.info["SYSFONT"] = self.font[lang]
	    self.info["SYSFONTACM"] = self.map[lang]
	else:
            if self.info.has_key("SYSFONT"):
                del self.info["SYSFONT"]
            if self.info.has_key("SYSFONTACM"):
                del self.info["SYSFONTACM"]

	rpm.addMacro("_install_langs", self.langs[lang]);
        os.environ["LINGUAS"] = self.langs[lang]
        
    def get (self):
	return self.lang

    def getFont (self, lang):
	return self.font[lang]

class Authentication:
    def __init__ (self):
        self.useShadow = 1
        self.useMD5 = 1
        self.useNIS = 0
        self.domain = ""
        self.useBroadcast = 1
        self.server = ""

class InstSyslog:
    def __init__ (self, root, log):
        self.pid = os.fork ()
        if not self.pid:
            if os.access ("./anaconda", os.X_OK):
                path = "./anaconda"
            elif os.access ("/usr/bin/anaconda.real", os.X_OK):
                path = "/usr/bin/anaconda.real"
            else:
                path = "/usr/bin/anaconda"
            os.execv (path, ("syslogd", "--syslogd", root, log))

    def __del__ (self):
        os.kill (self.pid, 15)
        
class ToDo:
    def __init__(self, intf, method, rootPath, setupFilesystems = 1,
		 installSystem = 1, mouse = None, instClass = None, x = None,
		 expert = 0, serial = 0, reconfigOnly = 0, test = 0,
		 extraModules = []):
	self.intf = intf
	self.method = method
	self.hdList = None
	self.comps = None
	self.instPath = rootPath
	self.setupFilesystems = setupFilesystems
	self.installSystem = installSystem
        self.language = Language ()
	self.serial = serial
	self.fstab = None
        self.reconfigOnly = reconfigOnly
        self.log = LogFile (serial, reconfigOnly, test)
        self.network = Network ()
        self.rootpassword = Password ()
        self.extraModules = extraModules
        if mouse:
            self.mouse = Mouse (xmouseType = mouse)
        else:
            self.mouse = Mouse ()
        self.keyboard = Keyboard ()
        self.auth = Authentication ()
        self.desktop = Desktop ()
        self.ddruidReadOnly = 0
        self.bootdisk = 1

	# liloDevice, liloLinear, liloAppend are initialized form the
	# default install class
        arch = iutil.getArch ()
        self.lilo = LiloConfiguration()
	if arch == "sparc":
	    self.silo = SiloInstall (self.serial)
        elif arch == "alpha":
            self.milo = MiloInstall (self)
	self.timezone = None
        self.upgrade = 0
	self.ddruidAlreadySaved = 0
        self.initlevel = 3
	self.expert = expert
        self.progressWindow = None
	self.fdDevice = None
	if (not instClass):
	    raise TypeError, "installation class expected"
        if x:
            self.x = x
            self.x.setMouse (self.mouse)
        else:
            self.x = XF86Config (mouse = self.mouse)

	# This absolutely, positively MUST BE LAST
	self.setClass(instClass)

    def setFdDevice(self):
	if self.fdDevice:
	    return
	self.fdDevice = "/dev/fd0"
	if iutil.getArch() == "sparc":
	    try:
		f = open(self.fdDevice, "r")
	    except IOError, (errnum, msg):
		if errno.errorcode[errnum] == 'ENXIO':
		    self.fdDevice = "/dev/fd1"
	    else:
		f.close()

    def writeTimezone(self):
	if (self.timezone):
	    (timezone, asUtc, asArc) = self.timezone
            try:
                iutil.copyFile(self.instPath + "/usr/share/zoneinfo/" + timezone, 
                               self.instPath + "/etc/localtime")
            except OSError, (errno, msg):
                self.intf.messageWindow(_("Error"),
                                        _("Error copying file: %s") % msg)
	else:
	    asUtc = 0
	    asArc = 0

	f = open(self.instPath + "/etc/sysconfig/clock", "w")
	f.write('ZONE="%s"\n' % timezone)
	f.write("UTC=")
	if (asUtc):
	    f.write("true\n")
	else:
	    f.write("false\n")

	f.write("ARC=")
	if (asArc):
	    f.write("true\n")
	else:
	    f.write("false\n")
	f.close()

    def getTimezoneInfo(self):
	return self.timezone

    def setTimezoneInfo(self, timezone, asUtc = 0, asArc = 0):
	self.timezone = (timezone, asUtc, asArc)

    def addMount(self, device, location, fsystem, reformat = 1):
        if fsystem == "swap":
            ufs = 0
            try:
                isys.makeDevInode(device, '/tmp/' + device)
            except:
                pass
            try:
                ufs = isys.checkUFS ('/tmp/' + device)
            except:
                pass
            if not ufs:
                location = "swap"
                reformat = 1
        self.mounts[location] = (device, fsystem, reformat)

    def writeLanguage(self):
	f = open(self.instPath + "/etc/sysconfig/i18n", "w")
	f.write(str (self.language))
	f.close()

    def writeMouse(self):
	if self.serial: return
	f = open(self.instPath + "/etc/sysconfig/mouse", "w")
	f.write(str (self.mouse))
	f.close()
	self.mouse.makeLink(self.instPath)

    def writeDesktop(self):
	f = open(self.instPath + "/etc/sysconfig/desktop", "w")
	f.write(str (self.desktop))
	f.close()

    def writeKeyboard(self):
	if self.serial: return
	f = open(self.instPath + "/etc/sysconfig/keyboard", "w")
	f.write(str (self.keyboard))
	f.close()

    def needBootdisk (self):
	if self.bootdisk or self.fstab.rootOnLoop(): return 1

    def makeBootdisk (self):
	# this is faster then waiting on mkbootdisk to fail
	self.setFdDevice()
	device = self.fdDevice[5:]
	file = "/tmp/floppy"
	isys.makeDevInode(device, file)
	try:
	    fd = os.open(file, os.O_RDONLY)
	except:
            raise RuntimeError, "boot disk creation failed"
	os.close(fd)

	kernel = self.hdList['kernel']
        kernelTag = "-%s-%s" % (kernel['version'], kernel['release'])

        w = self.intf.waitWindow (_("Creating"), _("Creating boot disk..."))
	self.setFdDevice ()
        rc = iutil.execWithRedirect("/sbin/mkbootdisk",
                                    [ "/sbin/mkbootdisk",
                                      "--noprompt",
                                      "--device",
                                      self.fdDevice,
                                      kernelTag[1:] ],
                                    stdout = None, stderr = None, 
				    searchPath = 1, root = self.instPath)
        w.pop()
        if rc:
            raise RuntimeError, "boot disk creation failed"

    def freeHeaderList(self):
	if (self.hdList):
	    self.hdList = None

    def getHeaderList(self):
	if (not self.hdList):
	    w = self.intf.waitWindow(_("Reading"),
                                     _("Reading package information..."))
	    self.hdList = self.method.readHeaders()
	    w.pop()
	return self.hdList

    def getCompsList(self):
	if (not self.comps):
	    self.getHeaderList()
	    self.comps = self.method.readComps(self.hdList)
	    for comp in self.comps:
		if comp.selected:
		    comp.select (1)
	self.comps['Base'].select(1)

	self.updateInstClassComps()
            
	return self.comps

    def updateInstClassComps(self):
	# don't load it just for this
	if (not self.comps): return
	group = self.instClass.getGroups()
	packages = self.instClass.getPackages()
	if (group == None and packages == None): return 0
	for n in self.comps.keys():
	    self.comps[n].unselect(0)

	self.comps['Base'].select(1)
	if group:
	    for n in group:
		self.comps[n].select(1)

	if packages:
	    for n in packages:
		self.selectPackage(n)

	if self.x.server:
	    self.selectPackage('XFree86-' + self.x.server)

    def selectPackage(self, package):
	if not self.hdList.packages.has_key(package):
	    str = "package %s is not available" % (package,)
	    raise ValueError, str
	self.hdList.packages[package].selected = 1

    def writeNetworkConfig (self):
        # /etc/sysconfig/network-scripts/ifcfg-*
        for dev in self.network.netdevices.values ():
            device = dev.get ("device")
            f = open (self.instPath + "/etc/sysconfig/network-scripts/ifcfg-" + device, "w")
            f.write (str (dev))
            f.close ()

        # /etc/sysconfig/network

        f = open (self.instPath + "/etc/sysconfig/network", "w")
        f.write ("NETWORKING=yes\n"
                 "HOSTNAME=")
	if self.network.hostname:
	    f.write(self.network.hostname + "\n")
	else:
	    f.write("localhost.localdomain" + "\n")
	if self.network.gateway:
	    f.write("GATEWAY=" + self.network.gateway + "\n")
        f.close ()

        # /etc/hosts
        f = open (self.instPath + "/etc/hosts", "w")
        localline = "127.0.0.1\t\t"

        self.log ("self.network.hostname = %s", self.network.hostname)

	ip = self.network.lookupHostname()

	# If the hostname is not resolvable, tie it to 127.0.0.1
	if not ip and self.network.hostname != "localhost.localdomain":
	    localline = localline + self.network.hostname + " "
	    l = string.split(self.network.hostname, ".")
	    if len(l) > 1:
		localline = localline + l[0] + " "
                
	localline = localline + "localhost.localdomain localhost\n"
        f.write (localline)

	if ip:
	    f.write ("%s\t\t%s\n" % (ip, self.network.hostname))

	# If the hostname was not looked up, but typed in by the user,
	# domain might not be computed, so do it now.
	if self.network.domains == [ "localdomain" ] or not self.network.domains:
	    if '.' in self.network.hostname:
		# chop off everything before the leading '.'
		domain = self.network.hostname[(string.find(self.network.hostname, '.') + 1):]
		self.network.domains = [ domain ]

        # /etc/resolv.conf
        f = open (self.instPath + "/etc/resolv.conf", "w")

	if self.network.domains != [ 'localdomain' ] and self.network.domains:
	    f.write ("search " + string.joinfields (self.network.domains, ' ') 
			+ "\n")

        for ns in self.network.nameservers ():
            if ns:
                f.write ("nameserver " + ns + "\n")

        f.close ()

    def writeRootPassword (self):
	pure = self.rootpassword.getPure()
	if pure:
	    self.setPassword("root", pure)
	else:
	    # we need to splice in an already crypted password for kickstart
	    f = open (self.instPath + "/etc/passwd", "r")
	    lines = f.readlines ()
	    f.close ()
	    index = 0
	    for line in lines:
		if line[0:4] == "root":
		    entry = string.splitfields (line, ':')
		    entry[1] = self.rootpassword.getCrypted ()
		    lines[index] = string.joinfields (entry, ':')
		    break
		index = index + 1
	    f = open (self.instPath + "/etc/passwd", "w")
	    f.writelines (lines)
	    f.close ()

    def setupAuthentication (self):
        args = [ "/usr/sbin/authconfig", "--kickstart", "--nostart" ]
        if self.auth.useShadow:
            args.append ("--useshadow")
        if self.auth.useMD5:
            args.append ("--enablemd5")
        if self.auth.useNIS:
            args.append ("--enablenis")
            args.append ("--nisdomain")
            args.append (self.auth.domain)
            if not self.auth.useBroadcast:
                args.append ("--nisserver")
                args.append (self.auth.server)
        iutil.execWithRedirect(args[0], args,
                              stdout = None, stderr = None, searchPath = 1,
                              root = self.instPath)

    def copyConfModules (self):
        try:
            inf = open ("/tmp/conf.modules", "r")
        except:
            pass
        else:
            out = open (self.instPath + "/etc/conf.modules", "a")
            out.write (inf.read ())

    def verifyDeps (self):
	self.getCompsList()
        if self.upgrade:
            self.fstab.mountFilesystems (self.instPath)
            db = rpm.opendb (0, self.instPath)
            ts = rpm.TransactionSet(self.instPath, db)
        else:
            ts = rpm.TransactionSet()
            
        self.comps['Base'].select (1)

	for p in self.hdList.packages.values ():
            if p.selected:
                ts.add(p.h, (p.h, p.h[rpm.RPMTAG_NAME]))
            else:
                ts.add(p.h, (p.h, p.h[rpm.RPMTAG_NAME]), "a")

	ts.order()
        deps = ts.depcheck()
        rc = []
        if deps:
            for ((name, version, release),
                 (reqname, reqversion),
                 flags, suggest, sense) in deps:
                if sense == rpm.RPMDEP_SENSE_REQUIRES:
                    if suggest:
                        (header, sugname) = suggest
                    else:
                        sugname = _("no suggestion")
                    if not (name, sugname) in rc:
                        rc.append ((name, sugname))

        del ts
        if self.upgrade:
            del db
            self.fstab.umountFilesystems (self.instPath)            
        return rc

    def selectDeps (self, deps):
        if deps:
            for (who, dep) in deps:
                if dep != _("no suggestion"):
                    self.hdList[dep].selected = 1

    def upgradeFindRoot (self):
        rootparts = []
        if not self.setupFilesystems: return [ self.instPath ]
        win = self.intf.waitWindow (_("Searching"),
                                    _("Searching for Red Hat Linux installations..."))
        
        drives = self.fstab.driveList()
	mdList = raid.startAllRaid(drives)

	for dev in mdList:
            if fstab.isValidExt2 (dev):
                isys.makeDevInode(dev, '/tmp/' + dev)
                try:
                    isys.mount('/tmp/' + dev, '/mnt/sysimage')
                except SystemError, (errno, msg):
                    self.intf.messageWindow(_("Error"),
                                            _("Error mounting ext2 filesystem on %s: %s") % (dev, msg))
                    continue
                if os.access ('/mnt/sysimage/etc/fstab', os.R_OK):
                    rootparts.append (dev)
                isys.umount('/mnt/sysimage')
                os.remove ('/tmp/' + dev)

	raid.stopAllRaid(mdList)
	
        for drive in drives:
            isys.makeDevInode(drive, '/tmp/' + drive)
            
            try:
                table = _balkan.readTable ('/tmp/' + drive)
            except SystemError:
                pass
            else:
                for i in range (len (table)):
                    (type, sector, size) = table[i]
                    # 2 is ext2 in balkan speek
                    if size and type == 2:
			# for RAID arrays of format c0d0p1
			if drive [:3] == "rd/" or drive [:4] == "ida/":
                            dev = drive + 'p' + str (i + 1)
			else:
                            dev = drive + str (i + 1)
                        isys.makeDevInode(dev, '/tmp/' + dev)
                        try:
                            isys.mount('/tmp/' + dev, '/mnt/sysimage')
                        except SystemError, (errno, msg):
                            self.intf.messageWindow(_("Error"),
                                                    _("Error mounting ext2 filesystem on %s: %s") % (dev, msg))
                            continue
                        if os.access ('/mnt/sysimage/etc/fstab', os.R_OK):
                            rootparts.append (dev)
                        isys.umount('/mnt/sysimage')
                        os.remove ('/tmp/' + dev)
            os.remove ('/tmp/' + drive)
        win.pop ()
        return rootparts

    def upgradeFindPackages (self, root):
        win = self.intf.waitWindow (_("Finding"),
                                    _("Finding packages to upgrade..."))
        if self.setupFilesystems:
            isys.makeDevInode(root, '/tmp/' + root)
	    mdList = raid.startAllRaid(self.fstab.driveList())
            isys.mount('/tmp/' + root, '/mnt/sysimage')
	    fstab.readFstab('/mnt/sysimage/etc/fstab', self.fstab)
            isys.umount('/mnt/sysimage')        
	    raid.stopAllRaid(mdList)
            self.fstab.mountFilesystems (self.instPath)
	    self.fstab.turnOnSwap(formatSwap = 0)
        self.getCompsList ()
	self.getHeaderList ()

        packages = rpm.findUpgradeSet (self.hdList.hdlist, self.instPath)

        # unselect all packages
        for package in self.hdList.packages.values ():
            package.selected = 0

        # always upgrade all packages in Base package group
	self.comps['Base'].select(1)

        hasX = 0
        hasgmc = 0
        # turn on the packages in the upgrade set
        for package in packages:
            self.hdList[package[rpm.RPMTAG_NAME]].selected = 1
            if package[rpm.RPMTAG_NAME] == "XFree86":
                hasX = 1
            if package[rpm.RPMTAG_NAME] == "gmc":
                hasgmc = 1

        self.dbpath = "/var/lib/anaconda-rebuilddb" + str(int(time.time()))
        rpm.addMacro("_rebuilddbpath", self.dbpath);

        # now, set the system clock so the timestamps will be right:
        iutil.setClock (self.instPath)
        
        # and rebuild the database so we can run the dependency problem
        # sets against the on disk db
        rc = rpm.rebuilddb (self.instPath)
        if rc:
            self.intf.messageWindow(_("Error"),
                                    _("Rebuild of RPM database failed. "
                                      "You may be out of disk space?"))
            raise RuntimeError, "Rebuild of RPM database failed."

        # open up the database to check dependencies
        rpm.addMacro("_dbpath", self.dbpath);
        db = rpm.opendb (0, self.instPath)

        # if we have X but not gmc, we need to turn on GNOME.  We only
        # want to turn on packages we don't have installed already, though.
        if hasX and not hasgmc:
            self.log ("Has X but not GNOME")
            for package in self.comps['GNOME'].items.keys ():
                rec = db.findbyname (package.name)
                if not rec:
                    self.log ("GNOME: Adding %s", package)
                    self.comps['GNOME'].items[package].selected = 1
            
        del db
        self.fstab.umountFilesystems (self.instPath)

        # new package dependency fixup
        deps = self.verifyDeps ()

        for (name, suggest) in deps:
            self.log ("Upgrade Dependency: %s needs %s, automatically added.", name, suggest)
        self.selectDeps (deps)
        win.pop ()

    def rpmError (todo):
        todo.instLog.write (rpm.errorString () + "\n")

    def getClass(todo):
	return todo.instClass

    def setClass(todo, instClass):
	todo.instClass = instClass
	todo.hostname = todo.instClass.getHostname()
	todo.updateInstClassComps()
	( useShadow, useMd5, useNIS, nisDomain, nisBroadcast,
		      nisServer) = todo.instClass.getAuthentication()
        todo.auth.useShadow = useShadow
        todo.auth.useMD5 = useMd5
        todo.auth.useNIS = useNIS
        todo.auth.domain = nisDomain
        todo.auth.useBroadcast = nisBroadcast
        todo.auth.server = nisServer
	todo.timezone = instClass.getTimezoneInfo()
	todo.bootdisk = todo.instClass.getMakeBootdisk()
	todo.zeroMbr = todo.instClass.zeroMbr
	(where, linear, append) = todo.instClass.getLiloInformation()

        arch = iutil.getArch ()
	if arch == "i386":	
	    todo.lilo.setDevice(where)
	    todo.lilo.setLinear(linear)
	    todo.lilo.setAppend(append)
 	elif arch == "sparc":
	    todo.silo.setDevice(where)
	    todo.silo.setAppend(append)

	for (mntpoint, (dev, fstype, reformat)) in todo.instClass.fstab:
	    todo.addMount(dev, mntpoint, fstype, reformat)

	todo.users = []
	if todo.instClass.rootPassword:
	    todo.rootpassword.set(todo.instClass.rootPassword,
			      isCrypted = todo.instClass.rootPasswordCrypted)
	if todo.instClass.language:
	    todo.language.setByAbbrev(todo.instClass.language)
	if todo.instClass.keyboard:
	    todo.keyboard.set(todo.instClass.keyboard)
            if todo.instClass.keyboard != "us":
                xkb = todo.keyboard.getXKB ()
                if xkb:
                    apply (todo.x.setKeyboard, xkb)

	(bootProto, ip, netmask, gateway, nameserver) = \
		todo.instClass.getNetwork()
	if bootProto:
	    todo.network.gateway = gateway
	    todo.network.primaryNS = nameserver

	    devices = todo.network.available ()
	    if (devices and bootProto):
		list = devices.keys ()
		list.sort()
		dev = devices[list[0]]
                dev.set (("bootproto", bootProto))
                if bootProto == "static":
                    if (ip):
                        dev.set (("ipaddr", ip))
                    if (netmask):
                        dev.set (("netmask", netmask))

	if (todo.instClass.x):
	    todo.x = todo.instClass.x

	if (todo.instClass.mouse):
	    (type, device, emulateThreeButtons) = todo.instClass.mouse
	    todo.mouse.set(type, emulateThreeButtons, thedev = device)
            todo.x.setMouse(todo.mouse)
            
        if todo.instClass.desktop:
            todo.desktop.set (todo.instClass.desktop)

    def getPartitionWarningText(self):
	return self.instClass.clearPartText

    # List of (accountName, fullName, password) tupes
    def setUserList(todo, users):
	todo.users = users

    def getUserList(todo):
	return todo.users

    def setPassword(todo, account, password):
	devnull = os.open("/dev/null", os.O_RDWR)

	argv = [ "/usr/bin/passwd", "--stdin", account ]
	p = os.pipe()
	os.write(p[1], password + "\n")
	iutil.execWithRedirect(argv[0], argv, root = todo.instPath, 
			       stdin = p[0], stdout = devnull)
	os.close(p[0])
	os.close(p[1])
	os.close(devnull)

    def createAccounts(todo):
	if not todo.users: return

	for (account, name, password) in todo.users:
	    devnull = os.open("/dev/null", os.O_RDWR)

	    argv = [ "/usr/sbin/useradd", account ]
	    iutil.execWithRedirect(argv[0], argv, root = todo.instPath,
				   stdout = devnull)

	    argv = [ "/usr/bin/chfn", "-f", name, account]
	    iutil.execWithRedirect(argv[0], argv, root = todo.instPath,
				   stdout = devnull)
        
	    todo.setPassword(account, password)

	    os.close(devnull)

    def createCdrom(self):
        self.log ("making cd-rom links")
	list = isys.cdromList()
	count = 0
	for device in list:
	    cdname = "cdrom"
	    if (count):
		cdname = "%s%d" % (cdname, count)
	    count = count + 1

            self.log ("creating cdrom link for" + device)
            try:
                os.stat(self.instPath + "/dev/" + cdname)
                self.log ("link exists, removing")
                os.unlink(self.instPath + "/dev/" + cdname)
            except OSError:
                pass
	    os.symlink(device, self.instPath + "/dev/" + cdname)
	    mntpoint = "/mnt/" + cdname
	    self.fstab.addMount(cdname, mntpoint, "iso9660")

    def createRemovable(self, rType):
	devDict = isys.floppyDriveDict()

	d = isys.hardDriveDict()
	for item in d.keys():
	    devDict[item] = d[item]

	list = devDict.keys()
	list.sort()

	count = 0
	for device in list:
	    descript = devDict[device]
	    if string.find(string.upper(descript), string.upper(rType)) == -1:
		continue

	    self.log ("found %s disk, creating link", rType)

	    try:
		os.stat(self.instPath + "/dev/%s" % rType)
		self.log ("link exists, removing")
		os.unlink(self.instPath + "/dev/%s" % rType)
	    except OSError:
		pass
	    # the 4th partition of zip/jaz disks is the one that usually
	    # contains the DOS filesystem.  We'll guess at using that
	    # one, it is a sane default.
	    device = device + "4";
	    os.symlink(device, self.instPath + "/dev/%s" % rType)
	    mntpoint = "/mnt/%s" % rType
	    self.fstab.addMount(rType, mntpoint, "auto")

    def setDefaultRunlevel (self):
        inittab = open (self.instPath + '/etc/inittab', 'r')
        lines = inittab.readlines ()
        inittab.close ()
        inittab = open (self.instPath + '/etc/inittab', 'w')        
        for line in lines:
            if len (line) > 3 and line[:3] == "id:":
                fields = string.split (line, ':')
                fields[1] = str (self.initlevel)
                line = string.join (fields, ':')
            inittab.write (line)
        inittab.close ()
        
    def instCallback(self, what, amount, total, h, intf):
        if (what == rpm.RPMCALLBACK_TRANS_START):
            # step 6 is the bulk of the transaction set
            # processing time
            if amount == 6:
                self.progressWindow = \
                   self.intf.progressWindow (_("Processing"),
                                             _("Preparing to install..."),
                                             total)
        if (what == rpm.RPMCALLBACK_TRANS_PROGRESS):
            if self.progressWindow:
                self.progressWindow.set (amount)
                
        if (what == rpm.RPMCALLBACK_TRANS_STOP and self.progressWindow):
            self.progressWindow.pop ()

        if (what == rpm.RPMCALLBACK_INST_OPEN_FILE):
            intf.setPackage(h)
            intf.setPackageScale(0, 1)
            self.instLog.write (self.modeText % (h[rpm.RPMTAG_NAME],))
            self.instLog.flush ()
            fn = self.method.getFilename(h)
            self.rpmFD = os.open(fn, os.O_RDONLY)
            fn = self.method.unlinkFilename(fn)
            return self.rpmFD
        elif (what == rpm.RPMCALLBACK_INST_PROGRESS):
            if total:
                intf.setPackageScale(amount, total)
        elif (what == rpm.RPMCALLBACK_INST_CLOSE_FILE):
            os.close (self.rpmFD)
            intf.completePackage(h)
        else:
            pass

    def copyExtraModules(self):
	kernelVersions = []
	
	if (self.hdList.has_key('kernel-smp') and 
	    self.hdList['kernel-smp'].selected):
	    version = (self.hdList['kernel-smp']['version'] + "-" +
		       self.hdList['kernel-smp']['release'] + "smp")
	    kernelVersions.append(version)

	version = (self.hdList['kernel']['version'] + "-" +
		   self.hdList['kernel']['release'])
	kernelVersions.append(version)

        for (path, subdir, name) in self.extraModules:
	    pattern = ""
	    names = ""
	    for n in kernelVersions:
		pattern = pattern + " " + n + "/" + name + ".o"
		names = names + " " + name + ".o"
	    command = ("cd %s/lib/modules; gunzip < %s/modules.cgz | " +
			"%s/bin/cpio  --quiet -iumd %s") % \
		(self.instPath, path, self.instPath, pattern)
	    self.log("running: '%s'" % (command, ))
	    os.system(command)

	    for n in kernelVersions:
		fromFile = "%s/lib/modules/%s/%s.o" % (self.instPath, n, name)
		to = "%s/lib/modules/%s/%s/%s.o" % (self.instPath, n, 
							subdir, name)

		if (os.access(fromFile, os.R_OK)):
		    self.log("copying %s to %s" % (fromFile, to))
		    os.rename(fromFile, to)
		else:
		    self.log("missing DD module %s (this may be okay)" % 
				fromFile)

    def depmodModules(self):
	kernelVersions = []
	
	if (self.hdList.has_key('kernel-smp') and 
	    self.hdList['kernel-smp'].selected):
	    version = (self.hdList['kernel-smp']['version'] + "-" +
		       self.hdList['kernel-smp']['release'] + "smp")
	    kernelVersions.append(version)

	version = (self.hdList['kernel']['version'] + "-" +
		   self.hdList['kernel']['release'])
	kernelVersions.append(version)

        for version in kernelVersions:
	    iutil.execWithRedirect ("/sbin/depmod",
                                    [ "/sbin/depmod", "-a", version ],
                                    root = self.instPath, stderr = '/dev/null')

    def writeConfiguration(self):
        self.writeLanguage ()
        self.writeMouse ()
        self.writeKeyboard ()
        self.writeNetworkConfig ()
        self.setupAuthentication ()
        self.writeRootPassword ()
        self.createAccounts ()
        self.writeTimezone()

    def doInstall(self):
	# make sure we have the header list and comps file
	self.getHeaderList()
	self.getCompsList()

        arch = iutil.getArch ()

        if arch == "alpha":
            # if were're on alpha with ARC console, set the clock
            # so that our installed files won't be in the future
            if onMILO ():
                args = ("clock", "-A", "-s")
                try:
                    iutil.execWithRedirect('/usr/sbin/clock', args)
                except:
                    pass

	# this is NICE and LATE. It lets kickstart/server/workstation
	# installs detect this properly
	if (self.hdList.has_key('kernel-smp') and isys.smpAvailable()):
	    self.hdList['kernel-smp'].selected = 1

	# we *always* need a kernel installed
	if (self.hdList.has_key('kernel')):
	    self.hdList['kernel'].selected = 1

        # if NIS is configured, install ypbind and dependencies:
        if self.auth.useNIS:
            self.hdList['ypbind'].selected = 1
            self.hdList['yp-tools'].selected = 1
            self.hdList['portmap'].selected = 1

        if self.x.server:
            self.selectPackage ('XFree86-' + self.x.server)

        # make sure that all comps that include other comps are
        # selected (i.e. - recurse down the selected comps and turn
        # on the children
        if self.setupFilesystems:
            if not self.upgrade:
		if (self.ddruidAlreadySaved):
		    self.fstab.makeFilesystems ()
		else:
		    self.fstab.savePartitions ()
		    self.fstab.makeFilesystems ()
		    self.fstab.turnOnSwap()

            self.fstab.mountFilesystems (self.instPath)

        if self.upgrade and self.dbpath:
            # move the rebuilt db into place.
            os.rename (self.instPath + "/var/lib/rpm",
                       self.instPath + "/var/lib/rpm-old")
            os.rename (self.instPath + self.dbpath,
                       self.instPath + "/var/lib/rpm")
            rpm.addMacro ("_dbpath", "%{_var}/lib/rpm")
            iutil.rmrf (self.instPath + "/var/lib/rpm-old")
            # flag this so we only do it once.
            self.dbpath = None

        self.method.targetFstab (self.fstab)

	if not self.installSystem: 
	    return

	for i in [ '/var', '/var/lib', '/var/lib/rpm', '/tmp', '/dev' ]:
	    try:
	        os.mkdir(self.instPath + i)
	    except os.error, (errno, msg):
                # self.intf.messageWindow("Error", "Error making directory %s: %s" % (i, msg))
                pass

        # XXX in case we started out in Upgrade land, we need to
        # reset this macro to point to the right place.
        rpm.addMacro ("_dbpath", "%{_var}/lib/rpm")
	db = rpm.opendb(1, self.instPath)
	ts = rpm.TransactionSet(self.instPath, db)

        total = 0
	totalSize = 0

        if self.upgrade:
            how = "u"
        else:
            how = "i"
        
	for p in self.hdList.selected():
	    ts.add(p.h, p.h, how)
	    total = total + 1
	    totalSize = totalSize + p.h[rpm.RPMTAG_SIZE]

	ts.order()

        if self.upgrade:
            logname = '/tmp/upgrade.log'
        else:
            logname = '/tmp/install.log'
            
	self.instLog = open(self.instPath + logname, "w+")
	syslog = InstSyslog (self.instPath, self.instPath + logname)

	ts.scriptFd = self.instLog.fileno ()
	# the transaction set dup()s the file descriptor and will close the
	# dup'd when we go out of scope

	p = self.intf.packageProgressWindow(total, totalSize)

        if self.upgrade:
            self.modeText = _("Upgrading %s.\n")
        else:
            self.modeText = _("Installing %s.\n")

        oldError = rpm.errorSetCallback (self.rpmError)

        problems = ts.run(0, ~rpm.RPMPROB_FILTER_DISKSPACE,
                          self.instCallback, p)
        
        if problems:
            needed = {}
            size = 12
            for (descr, (type, mount, need)) in problems:
                idx = string.find (mount, "/mnt/sysimage")
                if idx != -1:
                    # 13 chars in /mnt/sysimage
                    mount = mount[13:]

                if needed.has_key (mount) and needed[mount] < need:
                    needed[mount] = need
                else:
                    needed[mount] = need
                    
            probs = _("You don't appear to have enough disk space to install "
                      "the packages you've selected. You need more space on the "
                      "following filesystems:\n\n")
            probs = probs + ("%-15s %s\n") % (_("Mount Point"), _("Space Needed"))
                    
            for (mount, need) in needed.items ():
                if need > (1024*1024):
                    need = (need + 1024 * 1024 - 1) / (1024 * 1024)
                    suffix = "M"
                else:
                    need = (need + 1023) / 1024
                    suffix = "k"

                prob = "%-15s %d %c\n" % (mount, need, suffix)
                probs = probs + prob
                
            self.intf.messageWindow (_("Disk Space"), probs)

	    del ts
	    del db
	    self.instLog.close()
	    del syslog

	    self.fstab.umountFilesystems(self.instPath)
            
            rpm.errorSetCallback (oldError)
            return 1

        # This should close the RPM database so that you can
        # do RPM ops in the chroot in a %post ks script
        del ts
        del db
        rpm.errorSetCallback (oldError)
        
        self.method.filesDone ()
        
        del p

        self.instLog.close ()

        w = self.intf.waitWindow(_("Post Install"), 
                                 _("Performing post install configuration..."))

        if not self.upgrade:
	    self.createCdrom()
	    self.createRemovable("zip")
	    self.createRemovable("jaz")
	    self.copyExtraModules()
	    self.setFdDevice()
            self.fstab.write (self.instPath, fdDevice = self.fdDevice)
            self.writeConfiguration ()
            self.writeDesktop ()
	    if (self.instClass.defaultRunlevel):
		self.initlevel = self.instClass.defaultRunlevel
		self.setDefaultRunlevel ()
            
            # pcmcia is supported only on i386 at the moment
            if arch == "i386":
                pcmcia.createPcmciaConfig(self.instPath + "/etc/sysconfig/pcmcia")
            self.copyConfModules ()
            if not self.x.skip and self.x.server:
		if self.x.server[0:3] == 'Sun':
                    try:
                        os.unlink(self.instPath + "/etc/X11/X")
                    except:
                        pass
		    script = open(self.instPath + "/etc/X11/X","w")
		    script.write("#!/bin/bash\n")
		    script.write("exec /usr/X11R6/bin/Xs%s -fp unix/:-1 $@\n" % self.x.server[1:])
		    script.close()
		    os.chmod(self.instPath + "/etc/X11/X", 0755)
		else:
                    if os.access (self.instPath + "/etc/X11/X", os.R_OK):
                        os.rename (self.instPath + "/etc/X11/X",
                                   self.instPath + "/etc/X11/X.rpmsave")
		    os.symlink ("../../usr/X11R6/bin/XF86_" + self.x.server,
				self.instPath + "/etc/X11/X")
		self.x.write (self.instPath + "/etc/X11/XF86Config")
            self.setDefaultRunlevel ()
            # go ahead and depmod modules on alpha, as rtc modprobe
            # will complain loudly if we don't do it now.
            if arch == "alpha":
                self.depmodModules()
                
            # blah.  If we're on a serial mouse, and we have X, we need to
            # close the mouse device, then run kudzu, then open it again.

            # turn it off
            mousedev = None

            # XXX currently Bad Things (X async reply) happen when doing
            # Mouse Magic on Sparc (Mach64, specificly)
            if os.environ.has_key ("DISPLAY") and not arch == "sparc":
                import xmouse
                try:
                    mousedev = xmouse.get()[0]
                except RuntimeError:
                    pass
            if mousedev:
                try:
                    os.rename (mousedev, "/dev/disablemouse")
                except OSError:
                    pass
                try:
                    xmouse.reopen()
                except RuntimeError:
                    pass
            argv = [ "/usr/sbin/kudzu", "-q" ]
	    devnull = os.open("/dev/null", os.O_RDWR)
	    iutil.execWithRedirect(argv[0], argv, root = self.instPath,
				   stdout = devnull)
            # turn it back on            
            if mousedev:
                try:
                    os.rename ("/dev/disablemouse", mousedev)
                except OSError:
                    pass
                try:
                    xmouse.reopen()
                except RuntimeError:
                    pass
        
        # XXX make me "not test mode"
        if self.setupFilesystems:
	    if arch == "sparc":
		self.silo.install (self.fstab, self.instPath, self.hdList, 
				   self.upgrade)
	    elif arch == "i386":
		self.lilo.install (self.fstab, self.instPath, self.hdList, 
				   self.upgrade)
	    elif arch == "alpha":
		self.milo.write ()
	    else:
		raise RuntimeError, "What kind of machine is this, anyway?!"

	if self.instClass.postScript:
	    scriptRoot = "/"
	    if self.instClass.postInChroot:
		scriptRoot = self.instPath	

	    path = scriptRoot + "/tmp/ks-script"

	    f = open(path, "w")
	    f.write("#!/bin/sh\n\n")
	    f.write(self.instClass.postScript)
	    f.close()
	    os.chmod(path, 0700)

	    if self.serial:
		messages = "/tmp/ks-script.log"
	    else:
		messages = "/dev/tty3"
	    iutil.execWithRedirect ("/bin/sh", ["/bin/sh", 
		    "/tmp/ks-script" ], stdout = messages,
		    stderr = messages, root = scriptRoot)
				    
	    os.unlink(path)

        del syslog
        
        w.pop ()

