#!/usr/bin/python

'''
Installer

This is a installer program for a Ubuntu or Metadistros Live system.
This is the main program, but there are also a couple of libraries to
help it to work, such as the frontend.
The way it works is simple. It detects the frontend to use, then
load the module for that frontend. After that, it makes some calls
through the frontend in order to get the info necessary to install.

Once it has the info, partitioning, format, copy the distro to the disk
and configure everything.
'''

import sys
import os
import errno
import fcntl
import shutil
import syslog
import atexit
import optparse

sys.path.insert(0, '/usr/lib/ubiquity')

from ubiquity import misc

VERSION = '@VERSION@'
TARGET = '/target'
LOCKFILE = '/var/lock/ubiquity'
lock = None

def install(frontend=None):
    '''install(frontend=None) -> none
    
    Get the type of frontend to use and load the module for that.
    If frontend is None, defaults to the first of gtkui and kde-ui that
    exists.
    '''
    if frontend is None:
        frontends = ['gtkui', 'kde-ui']
    else:
        frontends = [frontend]
    mod = __import__('ubiquity.frontend', globals(), locals(), frontends)
    for f in frontends:
        if hasattr(mod, f):
            ui = getattr(mod, f)
            break
    else:
        raise AttributeError, ('No frontend available; tried %s' %
                               ', '.join(frontends))

    unmount_target()
    distro = misc.distribution().lower()
    wizard = ui.Wizard(distro)
    ret = wizard.run()
    copy_debconf()
    unmount_target()
    if ret == 10:
        wizard.do_reboot()

def copy_debconf():
    """Copy a few important questions into the installed system."""
    targetdb = '/target/var/cache/debconf/config.dat'
    for q in ('^console-setup/',):
        misc.ex('debconf-copydb', 'configdb', 'targetdb', '-p', q,
                '--config=Name:targetdb', '--config=Driver:File',
                '--config=Filename:%s' % targetdb)

def unmount_target():
    paths = []
    mounts = open('/proc/mounts')
    for line in mounts:
        path = line.split(' ')[1]
        if path == '/target' or path.startswith('/target/'):
            paths.append(path)
    mounts.close()
    paths.sort()
    paths.reverse()
    for path in paths:
        misc.ex('umount', path)

def prepend_path(directory):
    if 'PATH' in os.environ and os.environ['PATH'] != '':
        os.environ['PATH'] = '%s:%s' % (directory, os.environ['PATH'])
    else:
        os.environ['PATH'] = directory

def release_lock():
    global lock
    try:
        os.unlink(LOCKFILE)
    except OSError:
        pass
    if lock is not None:
        lock.close()
        lock = None

def acquire_lock():
    global lock
    lock = open(LOCKFILE, 'w')
    try:
        fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError, e:
        if e.errno in (errno.EACCES, errno.EAGAIN, errno.EWOULDBLOCK):
            print "Ubiquity is already running!"
            sys.exit(1)
        raise
    atexit.register(release_lock)
    fcntl.fcntl(lock, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
    print >>lock, os.getpid()
    lock.flush()
    os.fsync(lock.fileno())

def main():
    usage = '%prog [options] [frontend]'
    parser = optparse.OptionParser(usage=usage, version=VERSION)
    parser.set_defaults(
        debug=('UBIQUITY_DEBUG' in os.environ),
        cdebconf=False,
        new_partitioner=True,
        migration_assistant=True)
    parser.add_option('-d', '--debug', dest='debug', action='store_true',
                      help='debug mode (warning: passwords will be logged!)')
    parser.add_option('--cdebconf', dest='cdebconf', action='store_true',
                      help='use cdebconf instead of debconf (experimental)')
    parser.add_option('--old-partitioner', dest='new_partitioner',
                      action='store_false',
                      help='force use of old advanced partitioner')
    parser.add_option('--new-partitioner', dest='new_partitioner',
                      action='store_true',
                      help='use new advanced partitioner (experimental)')
    parser.add_option('--no-migration-assistant', dest='migration_assistant',
                      action='store_false',
                      help='disable Migration Assistant')
    (options, args) = parser.parse_args()

    if options.debug:
        os.environ['UBIQUITY_DEBUG'] = '1'

    if options.cdebconf:
        # Note that this needs to be set before DebconfCommunicate is
        # imported by anything.
        os.environ['DEBCONF_USE_CDEBCONF'] = '1'
        prepend_path('/usr/lib/cdebconf')
    prepend_path('/usr/lib/ubiquity/compat')

    if options.new_partitioner:
        os.environ['UBIQUITY_NEW_PARTITIONER'] = '1'
    else:
        try:
            del os.environ['UBIQUITY_NEW_PARTITIONER']
        except KeyError:
            pass

    if options.migration_assistant:
        os.environ['UBIQUITY_MIGRATION_ASSISTANT'] = '1'

    if 'UBIQUITY_NEW_PARTITIONER' not in os.environ:
        try:
            del os.environ['UBIQUITY_MIGRATION_ASSISTANT']
        except KeyError:
            pass

    acquire_lock()

    if not os.path.exists('/var/log/installer'):
        os.makedirs('/var/log/installer')
    syslog.openlog('ubiquity', syslog.LOG_NOWAIT | syslog.LOG_PID)

    syslog.syslog("Ubiquity %s" % VERSION)
    version_file = open('/var/log/installer/version', 'w')
    print >>version_file, 'ubiquity %s' % VERSION
    version_file.close()

    if 'UBIQUITY_DEBUG' in os.environ:
        if 'UBIQUITY_DEBUG_CORE' not in os.environ:
            os.environ['UBIQUITY_DEBUG_CORE'] = '1'
        if 'DEBCONF_DEBUG' not in os.environ:
            os.environ['DEBCONF_DEBUG'] = 'developer|filter'
        # The frontend should take care of displaying a helpful message if
        # we are being run without root privileges.
        try:
            sys.stderr = open('/var/log/installer/debug', 'a', 1)
            os.dup2(sys.stderr.fileno(), 2)
            print >>sys.stderr, "Ubiquity %s" % VERSION
        except IOError, err:
            if err.errno != errno.EACCES:
                raise

    # Default to enabling internal (non-debconf) debugging.
    if 'UBIQUITY_DEBUG_CORE' not in os.environ:
        os.environ['UBIQUITY_DEBUG_CORE'] = '1'

    # Clean up old state.
    if os.path.exists("/var/lib/ubiquity/apt-installed"):
        os.unlink("/var/lib/ubiquity/apt-installed")
    if os.path.exists("/var/lib/ubiquity/remove-kernels"):
        os.unlink("/var/lib/ubiquity/remove-kernels")
    shutil.rmtree("/var/lib/partman", ignore_errors=True)

    if args:
        install(args[0])
    else:
        install()

if __name__ == '__main__':
    main()

# vim:ai:et:sts=4:tw=80:sw=4:
