#
# pylorax instroot module
# Install image and tree support data generation tool -- Python module.
#
# Copyright (C) 2008  Red Hat, Inc.
#
# 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 .
#
# Author(s): David Cantrell 
#
import os
import shutil
import sys
import pylorax
sys.path.insert(0, '/usr/share/yum-cli')
import yummain
# Create inst root from which stage 1 and stage 2 images are built.
def createInstRoot(yumconf=None, arch=None, treedir=None, updates=None):
    """createInstRoot(yumconf=None, arch=None, treedir=None, [updates=None])
    Create a instroot tree for the specified architecture.  The list of
    packages to install are specified in the /etc/lorax/packages and
    /etc/lorax/ARCH/packages files.
    yumconf is the path to the yum configuration create for this run of
    lorax.  arch is the platform name we are building for, treedir is
    the location where the instroot tree should go
    (will be treedir + '/install').  updates is an optional path to a
    directory of update RPM packages for the instroot.
    The yumconf, arch, and treedir parameters are all required.
    On success, this function returns True.  On failure, False is
    returned or the program is aborted immediately.
    """
    if yumconf is None or arch is None or treedir is None:
        return False
    # on 64-bit systems, make sure we use lib64 as the lib directory
    if arch.endswith('64') or arch == 's390x':
        libdir = 'lib64'
    else:
        libdir = 'lib'
    # the directory where the instroot will be created
    destdir = os.path.join(treedir, 'install')
    os.makedirs(destdir)
    # build a list of packages to install
    packages = set()
    packages_files = []
    packages_files.append(os.path.join(pylorax.conf['confdir'], 'packages'))
    packages_files.append(os.path.join(pylorax.conf['confdir'], arch, 'packages'))
    for pfile in packages_files:
        if os.path.isfile(pfile):
            f = open(pfile, 'r')
            for line in f.readlines():
                line = line.strip()
                if line.startswith('#') or line == '':
                    continue
                if line.startswith('-'):
                    try:
                        packages.remove(line[1:])
                    except KeyError:
                        pass
                else:
                    packages.add(line)
            f.close()
    packages = list(packages)
    packages.sort()
    # install the packages to the instroot
    if not installPackages(yumconf=yumconf, destdir=destdir, packages=packages):
        sys.stderr.write("ERROR: Could not install packages.\n")
        sys.exit(1)
    if not scrubInstRoot(destdir=destdir, libdir=libdir):
        sys.stderr.write("ERROR: Could not scrub instroot.\n")
        sys.exit(1)
    return True
# Call yummain to install the list of packages to destdir
def installPackages(yumconf=None, destdir=None, packages=None):
    """installPackages(yumconf=yumconf, destdir=destdir, packages=packages)
    Call yummain to install the list of packages.  All parameters are
    required.  yumconf is the yum configuration file created for this
    run of lorax.  destdir is the installroot that yum should install to.
    packages is a list of package names that yum should install.
    yum will resolve dependencies, so it is not necessary to list every
    package you want in the instroot.
    This function returns True on success, False on failre.
    """
    if yumconf is None or destdir is None or packages is None or packages == []:
        return False
    # build the list of arguments to pass to yum
    arglist = ['-c', yumconf]
    arglist.append("--installroot=%s" % (destdir,))
    arglist += ['install', '-y'] + packages
    # do some prep work on the destdir before calling yum
    os.makedirs(os.path.join(destdir, 'tmp'))
    os.makedirs(os.path.join(destdir, 'var', 'log'))
    os.makedirs(os.path.join(destdir, 'var', 'lib'))
    os.symlink(os.path.join(os.path.sep, 'tmp'), os.path.join(destdir, 'var', 'lib', 'xkb'))
    # XXX: sort through yum errcodes and return False for actual bad things
    # we care about
    errcode = yummain.user_main(arglist, exit_code=False)
    return True
# Scrub the instroot tree (remove files we don't want, modify settings, etc)
def scrubInstRoot(destdir=None, libdir='lib'):
    """scrubInstRoot(destdir=None, libdir='lib')
    Clean up the newly created instroot and make the tree more suitable to
    run the installer.
    destdir is the path to the instroot.  libdir is the subdirectory in
    /usr for libraries (either lib or lib64).
    """
    if destdir is None or not os.path.isdir(destdir):
        return False
    print
    # drop custom configuration files in to the instroot
    dogtailconf = os.path.join(pylorax.conf['datadir'], 'dogtail-%conf.xml')
    if os.path.isfile(dogtailconf):
        os.makedirs(os.path.join(destdir, '.gconf', 'desktop', 'gnome', 'interface'))
        f = open(os.path.join(destdir, '.gconf', 'desktop', '%gconf.xml'), 'w')
        f.close()
        f = open(os.path.join(destdir, '.gconf', 'desktop', 'gnome', '%gconf.xml'), 'w')
        f.close()
        dest = os.path.join(destdir, '.gconf', 'desktop', 'gnome', 'interface', '%gconf.xml')
        print "Installing %s..." % (os.path.join(os.path.sep, '.gconf', 'desktop', 'gnome', 'interface', '%gconf.xml'),)
        shutil.copy(dogtailconf, dest)
    # create selinux config
    if os.path.isfile(os.path.join(destdir, 'etc', 'selinux', 'targeted')):
        src = os.path.join(pylorax.conf['datadir'], 'selinux-config')
        if os.path.isfile(src):
            dest = os.path.join(destdir, 'etc', 'selinux', 'config')
            print "Installing %s..." % (os.path.join(os.path.sep, 'etc', 'selinux', 'config'),)
            shutil.copy(src, dest)
    # create libuser.conf
    src = os.path.join(pylorax.conf['datadir'], 'libuser.conf')
    dest = os.path.join(destdir, 'etc', 'libuser.conf')
    if os.path.isfile(src):
        print "Installing %s..." % (os.path.join(os.path.sep, 'etc', 'libuser.conf'),)
        shutil.copy(src, dest)
    # figure out the gtk+ theme to keep
    gtkrc = os.path.join(destdir, 'etc', 'gtk-2.0', 'gtkrc')
    gtk_theme_name = None
    gtk_icon_themes = []
    gtk_engine = None
    if os.path.isfile(gtkrc):
        print "\nReading %s..." % (os.path.join(os.path.sep, 'etc', 'gtk-2.0', 'gtkrc'),)
        f = open(gtkrc, 'r')
        lines = f.readlines()
        f.close()
        for line in lines:
            line = line.strip()
            if line.startswith('gtk-theme-name'):
                gtk_theme_name = line[line.find('=') + 1:].replace('"', '').strip()
                print "    gtk-theme-name: %s" % (gtk_theme_name,)
                # find the engine for this theme
                gtkrc = os.path.join(destdir, 'usr', 'share', 'themes', gtk_theme_name, 'gtk-2.0', 'gtkrc')
                if os.path.isfile(gtkrc):
                    f = open(gtkrc, 'r')
                    engine_lines = f.readlines()
                    f.close()
                    for engine_line in engine_lines:
                        engine_line = engine_line.strip()
                        if engine_line.find('engine') != -1:
                            gtk_engine = engine_line[engine_line.find('"') + 1:].replace('"', '')
                            print "    gtk-engine: %s" % (gtk_engine,)
                            break
            if line.startswith('gtk-icon-theme-name'):
                icon_theme = line[line.find('=') + 1:].replace('"', '').strip()
                gtk_icon_themes.append(icon_theme)
                # bring in all inherited themes
                while True:
                    icon_theme_index = os.path.join(destdir, 'usr', 'share', 'icons', icon_theme, 'index.theme')
                    if os.path.isfile(icon_theme_index):
                        inherits = False
                        f = open(icon_theme_index, 'r')
                        icon_lines = f.readlines()
                        f.close()
                        for icon_line in icon_lines:
                            icon_line = icon_line.strip()
                            if icon_line.startswith('Inherits='):
                                inherits = True
                                icon_theme = icon_line[icon_line.find('=') + 1:].replace('"', '')
                                print "    gtk-icon-theme-name: %s" % (icon_theme,)
                                gtk_icon_themes.append(icon_theme)
                                break
                        if not inherits:
                            break
                    else:
                        break
    theme_path = os.path.join(destdir, 'usr', 'share', 'themes')
    if os.path.isdir(theme_path):
        for theme in os.listdir(theme_path):
            if theme != gtk_theme_name:
                theme = os.path.join(theme_path, theme)
                shutil.rmtree(theme, ignore_errors=True)
    icon_path = os.path.join(destdir, 'usr', 'share', 'icons')
    if os.path.isdir(icon_path):
        for icon in os.listdir(icon_path):
            try:
                if gtk_icon_themes.index(icon):
                    continue
            except ValueError:
                icon = os.path.join(icon_path, icon)
                shutil.rmtree(icon, ignore_errors=True)
    tmp_path = os.path.join(destdir, 'usr', libdir, 'gtk-2.0')
    if os.path.isdir(tmp_path):
        for subdir in os.listdir(tmp_path):
            new_path = os.path.join(tmp_path, subdir, 'engines')
            if os.path.isdir(new_path):
                for engine in os.listdir(new_path):
                    if engine.find(gtk_engine) == -1:
                        tmp_engine = os.path.join(new_path, engine)
                        os.unlink(tmp_engine)
    return True