home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / share / ubiquity / install.py < prev    next >
Encoding:
Python Source  |  2006-08-30  |  47.3 KB  |  1,290 lines

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Copyright (C) 2005 Javier Carranza and others for Guadalinex
  5. # Copyright (C) 2005, 2006 Canonical Ltd.
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  
  21. import sys
  22. import os
  23. import platform
  24. import errno
  25. import stat
  26. import re
  27. import textwrap
  28. import shutil
  29. import subprocess
  30. import time
  31. import struct
  32. import socket
  33. import fcntl
  34. import traceback
  35. import debconf
  36. import apt_pkg
  37. from apt.package import Package
  38. from apt.cache import Cache
  39. from apt.progress import FetchProgress, InstallProgress
  40.  
  41. sys.path.insert(0, '/usr/lib/ubiquity')
  42.  
  43. from ubiquity import misc
  44. from ubiquity.components import language_apply, apt_setup, timezone_apply, \
  45.                                 clock_setup, kbd_chooser_apply, \
  46.                                 usersetup_apply, hw_detect, check_kernels
  47.  
  48. class DebconfFetchProgress(FetchProgress):
  49.     """An object that reports apt's fetching progress using debconf."""
  50.  
  51.     def __init__(self, db, title, info_starting, info):
  52.         FetchProgress.__init__(self)
  53.         self.db = db
  54.         self.title = title
  55.         self.info_starting = info_starting
  56.         self.info = info
  57.         self.old_capb = None
  58.         self.eta = 0.0
  59.  
  60.     def start(self):
  61.         self.db.progress('START', 0, 100, self.title)
  62.         if self.info_starting is not None:
  63.             self.db.progress('INFO', self.info_starting)
  64.         self.old_capb = self.db.capb()
  65.         capb_list = self.old_capb.split()
  66.         capb_list.append('progresscancel')
  67.         self.db.capb(' '.join(capb_list))
  68.  
  69.     # TODO cjwatson 2006-02-27: implement updateStatus
  70.  
  71.     def pulse(self):
  72.         FetchProgress.pulse(self)
  73.         try:
  74.             self.db.progress('SET', int(self.percent))
  75.         except debconf.DebconfError:
  76.             return False
  77.         if self.eta != 0.0:
  78.             time_str = "%d:%02d" % divmod(int(self.eta), 60)
  79.             self.db.subst(self.info, 'TIME', time_str)
  80.             try:
  81.                 self.db.progress('INFO', self.info)
  82.             except debconf.DebconfError:
  83.                 return False
  84.         return True
  85.  
  86.     def stop(self):
  87.         if self.old_capb is not None:
  88.             self.db.capb(self.old_capb)
  89.             self.old_capb = None
  90.             self.db.progress('STOP')
  91.  
  92. class DebconfInstallProgress(InstallProgress):
  93.     """An object that reports apt's installation progress using debconf."""
  94.  
  95.     def __init__(self, db, title, info, error=None):
  96.         InstallProgress.__init__(self)
  97.         self.db = db
  98.         self.title = title
  99.         self.info = info
  100.         self.error_template = error
  101.         self.started = False
  102.         # InstallProgress uses a non-blocking status fd; our run()
  103.         # implementation doesn't need that, and in fact we spin unless the
  104.         # fd is blocking.
  105.         flags = fcntl.fcntl(self.statusfd.fileno(), fcntl.F_GETFL)
  106.         fcntl.fcntl(self.statusfd.fileno(), fcntl.F_SETFL,
  107.                     flags & ~os.O_NONBLOCK)
  108.  
  109.     def startUpdate(self):
  110.         self.db.progress('START', 0, 100, self.title)
  111.         self.started = True
  112.  
  113.     def error(self, pkg, errormsg):
  114.         if self.error_template is not None:
  115.             self.db.subst(self.error_template, 'PACKAGE', pkg)
  116.             self.db.subst(self.error_template, 'MESSAGE', errormsg)
  117.             self.db.input('critical', self.error_template)
  118.             self.db.go()
  119.  
  120.     def statusChange(self, pkg, percent, status):
  121.         self.percent = percent
  122.         self.status = status
  123.         self.db.progress('SET', int(percent))
  124.         self.db.subst(self.info, 'DESCRIPTION', status)
  125.         self.db.progress('INFO', self.info)
  126.  
  127.     def updateInterface(self):
  128.         # TODO cjwatson 2006-02-28: InstallProgress.updateInterface doesn't
  129.         # give us a handy way to spot when percentages/statuses change and
  130.         # aren't pmerror/pmconffile, so we have to reimplement it here.
  131.         if self.statusfd is None:
  132.             return False
  133.         try:
  134.             while not self.read.endswith("\n"):
  135.                 r = os.read(self.statusfd.fileno(),1)
  136.                 if not r:
  137.                     return False
  138.                 self.read += r
  139.         except OSError, (err,errstr):
  140.             print errstr
  141.         if self.read.endswith("\n"):
  142.             s = self.read
  143.             (status, pkg, percent, status_str) = s.split(":", 3)
  144.             if status == "pmerror":
  145.                 self.error(pkg, status_str)
  146.             elif status == "pmconffile":
  147.                 # we get a string like this:
  148.                 # 'current-conffile' 'new-conffile' useredited distedited
  149.                 match = re.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(status_str)
  150.                 if match:
  151.                     self.conffile(match.group(1), match.group(2))
  152.             else:
  153.                 self.statusChange(pkg, float(percent), status_str.strip())
  154.             self.read = ""
  155.         return True
  156.  
  157.     def run(self, pm):
  158.         # Create a subprocess to deal with turning apt status messages into
  159.         # debconf protocol messages.
  160.         child_pid = self.fork()
  161.         if child_pid == 0:
  162.             # child
  163.             os.close(self.writefd)
  164.             try:
  165.                 while self.updateInterface():
  166.                     pass
  167.             except (KeyboardInterrupt, SystemExit):
  168.                 pass # we're going to exit anyway
  169.             except:
  170.                 traceback.print_exc(file=sys.stderr)
  171.             os._exit(0)
  172.  
  173.         self.statusfd.close()
  174.  
  175.         # Redirect stdout to stderr to avoid it interfering with our
  176.         # debconf protocol stream.
  177.         saved_stdout = os.dup(1)
  178.         os.dup2(2, 1)
  179.  
  180.         # Make sure all packages are installed non-interactively. We
  181.         # don't have enough passthrough magic here to deal with any
  182.         # debconf questions they might ask.
  183.         saved_environ_keys = ('DEBIAN_FRONTEND', 'DEBIAN_HAS_FRONTEND',
  184.                               'DEBCONF_USE_CDEBCONF')
  185.         saved_environ = {}
  186.         for key in saved_environ_keys:
  187.             if key in os.environ:
  188.                 saved_environ[key] = os.environ[key]
  189.         os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
  190.         if 'DEBIAN_HAS_FRONTEND' in os.environ:
  191.             del os.environ['DEBIAN_HAS_FRONTEND']
  192.         if 'DEBCONF_USE_CDEBCONF' in os.environ:
  193.             # Probably not a good idea to use this in /target too ...
  194.             del os.environ['DEBCONF_USE_CDEBCONF']
  195.  
  196.         res = pm.ResultFailed
  197.         try:
  198.             res = pm.DoInstall(self.writefd)
  199.         finally:
  200.             # Reap the status-to-debconf subprocess.
  201.             os.close(self.writefd)
  202.             while True:
  203.                 try:
  204.                     (pid, status) = os.waitpid(child_pid, 0)
  205.                     if pid != child_pid:
  206.                         break
  207.                     if os.WIFEXITED(status) or os.WIFSIGNALED(status):
  208.                         break
  209.                 except OSError:
  210.                     break
  211.  
  212.             # Put back stdout.
  213.             os.dup2(saved_stdout, 1)
  214.             os.close(saved_stdout)
  215.  
  216.             # Put back the environment.
  217.             for key in saved_environ_keys:
  218.                 if key in saved_environ:
  219.                     os.environ[key] = saved_environ[key]
  220.                 elif key in os.environ:
  221.                     del os.environ[key]
  222.  
  223.         return res
  224.  
  225.     def finishUpdate(self):
  226.         if self.started:
  227.             self.db.progress('STOP')
  228.             self.started = False
  229.  
  230. class InstallStepError(Exception):
  231.     """Raised when an install step fails.
  232.  
  233.     Attributes:
  234.         message -- message returned with exception
  235.  
  236.     """
  237.  
  238.     def __init__(self, message):
  239.         Exception.__init__(self, message)
  240.         self.message = message
  241.  
  242. class Install:
  243.  
  244.     def __init__(self):
  245.         """Initial attributes."""
  246.  
  247.         if os.path.isdir('/rofs'):
  248.             self.source = '/rofs'
  249.         elif os.path.isdir('/UNIONFS'):
  250.             # Klaus Knopper says this may not actually work very well
  251.             # because it'll copy the WHOLE WORLD (~12GB).
  252.             self.source = '/UNIONFS'
  253.         else:
  254.             self.source = '/source'
  255.         self.target = '/target'
  256.         self.unionfs = False
  257.         self.kernel_version = platform.release()
  258.         self.db = debconf.Debconf()
  259.  
  260.         apt_pkg.InitConfig()
  261.         apt_pkg.Config.Set("Dir", "/target")
  262.         apt_pkg.Config.Set("Dir::State::status", "/target/var/lib/dpkg/status")
  263.         apt_pkg.Config.Set("APT::GPGV::TrustedKeyring",
  264.                            "/target/etc/apt/trusted.gpg")
  265.         apt_pkg.Config.Set("Acquire::gpgv::Options::",
  266.                            "--ignore-time-conflict")
  267.         apt_pkg.Config.Set("DPkg::Options::", "--root=/target")
  268.         # We don't want apt-listchanges or dpkg-preconfigure, so just clear
  269.         # out the list of pre-installation hooks.
  270.         apt_pkg.Config.Clear("DPkg::Pre-Install-Pkgs")
  271.         apt_pkg.InitSystem()
  272.  
  273.     def excepthook(self, exctype, excvalue, exctb):
  274.         """Crash handler. Dump the traceback to a file so that it can be
  275.         read by the caller."""
  276.  
  277.         if (issubclass(exctype, KeyboardInterrupt) or
  278.             issubclass(exctype, SystemExit)):
  279.             return
  280.  
  281.         tbtext = ''.join(traceback.format_exception(exctype, excvalue, exctb))
  282.         print >>sys.stderr, "Exception during installation:"
  283.         print >>sys.stderr, tbtext
  284.         tbfile = open('/var/lib/ubiquity/install.trace', 'w')
  285.         print >>tbfile, tbtext
  286.         tbfile.close()
  287.  
  288.         sys.exit(1)
  289.  
  290.     def run(self):
  291.         """Run the install stage: copy everything to the target system, then
  292.         configure it as necessary."""
  293.  
  294.         self.db.progress('START', 0, 100, 'ubiquity/install/title')
  295.         self.db.progress('INFO', 'ubiquity/install/mounting_source')
  296.  
  297.         try:
  298.             if self.source == '/source':
  299.                 self.mount_source()
  300.  
  301.             self.db.progress('SET', 1)
  302.             self.db.progress('REGION', 1, 75)
  303.             self.copy_all()
  304.  
  305.             self.db.progress('SET', 75)
  306.             self.db.progress('INFO', 'ubiquity/install/cleanup')
  307.             if self.source == '/source':
  308.                 self.umount_source()
  309.  
  310.             self.db.progress('SET', 76)
  311.             self.db.progress('REGION', 76, 77)
  312.             self.run_target_config_hooks()
  313.  
  314.             self.db.progress('SET', 77)
  315.             self.db.progress('REGION', 77, 78)
  316.             self.db.progress('INFO', 'ubiquity/install/locales')
  317.             self.configure_locales()
  318.  
  319.             self.db.progress('SET', 78)
  320.             self.db.progress('REGION', 78, 79)
  321.             self.db.progress('INFO', 'ubiquity/install/network')
  322.             self.configure_network()
  323.  
  324.             self.db.progress('SET', 79)
  325.             self.db.progress('REGION', 79, 80)
  326.             self.db.progress('INFO', 'ubiquity/install/apt')
  327.             self.configure_apt()
  328.  
  329.             self.db.progress('SET', 80)
  330.             self.db.progress('REGION', 80, 84)
  331.             # Ignore failures from language pack installation.
  332.             try:
  333.                 self.install_language_packs()
  334.             except InstallStepError:
  335.                 pass
  336.             except IOError:
  337.                 pass
  338.             except SystemError:
  339.                 pass
  340.  
  341.             self.db.progress('SET', 84)
  342.             self.db.progress('REGION', 84, 85)
  343.             self.db.progress('INFO', 'ubiquity/install/timezone')
  344.             self.configure_timezone()
  345.  
  346.             self.db.progress('SET', 85)
  347.             self.db.progress('REGION', 85, 87)
  348.             self.db.progress('INFO', 'ubiquity/install/keyboard')
  349.             self.configure_keyboard()
  350.  
  351.             self.db.progress('SET', 87)
  352.             self.db.progress('REGION', 87, 88)
  353.             self.db.progress('INFO', 'ubiquity/install/user')
  354.             self.configure_user()
  355.  
  356.             self.db.progress('SET', 88)
  357.             self.db.progress('REGION', 88, 92)
  358.             self.db.progress('INFO', 'ubiquity/install/hardware')
  359.             self.configure_hardware()
  360.  
  361.             self.db.progress('SET', 92)
  362.             self.db.progress('REGION', 92, 93)
  363.             self.remove_unusable_kernels()
  364.  
  365.             self.db.progress('SET', 93)
  366.             self.db.progress('REGION', 93, 94)
  367.             self.db.progress('INFO', 'ubiquity/install/bootloader')
  368.             self.configure_bootloader()
  369.  
  370.             self.db.progress('SET', 94)
  371.             self.db.progress('REGION', 94, 99)
  372.             self.db.progress('INFO', 'ubiquity/install/removing')
  373.             self.remove_extras()
  374.  
  375.             self.db.progress('SET', 99)
  376.             self.db.progress('INFO', 'ubiquity/install/log_files')
  377.             self.copy_logs()
  378.  
  379.             self.cleanup()
  380.  
  381.             self.db.progress('SET', 100)
  382.         finally:
  383.             try:
  384.                 self.db.progress('STOP')
  385.             except (KeyboardInterrupt, SystemExit):
  386.                 raise
  387.             except:
  388.                 pass
  389.  
  390.  
  391.     def copy_all(self):
  392.         """Core copy process. This is the most important step of this
  393.         stage. It clones live filesystem into a local partition in the
  394.         selected hard disk."""
  395.  
  396.         files = []
  397.         total_size = 0
  398.  
  399.         self.db.progress('START', 0, 100, 'ubiquity/install/title')
  400.         self.db.progress('INFO', 'ubiquity/install/scanning')
  401.  
  402.         # Obviously doing os.walk() twice is inefficient, but I'd rather not
  403.         # suck the list into ubiquity's memory, and I'm guessing that the
  404.         # kernel's dentry cache will avoid most of the slowness anyway.
  405.         walklen = 0
  406.         for entry in os.walk(self.source):
  407.             walklen += 1
  408.         walkpos = 0
  409.         walkprogress = 0
  410.  
  411.         for dirpath, dirnames, filenames in os.walk(self.source):
  412.             walkpos += 1
  413.             if int(float(walkpos) / walklen * 10) != walkprogress:
  414.                 walkprogress = int(float(walkpos) / walklen * 10)
  415.                 self.db.progress('SET', walkprogress)
  416.  
  417.             sourcepath = dirpath[len(self.source) + 1:]
  418.  
  419.             for name in dirnames + filenames:
  420.                 relpath = os.path.join(sourcepath, name)
  421.                 fqpath = os.path.join(self.source, dirpath, name)
  422.  
  423.                 total_size += os.lstat(fqpath).st_size
  424.                 files.append(relpath)
  425.  
  426.         self.db.progress('SET', 10)
  427.         self.db.progress('INFO', 'ubiquity/install/copying')
  428.  
  429.         # Progress bar handling:
  430.         # We sample progress every half-second (assuming time.time() gives
  431.         # us sufficiently good granularity) and use the average of progress
  432.         # over the last minute or so to decide how much time remains. We
  433.         # don't bother displaying any progress for the first ten seconds in
  434.         # order to allow things to settle down, and we only update the "time
  435.         # remaining" indicator at most every two seconds after that.
  436.  
  437.         copy_progress = 0
  438.         copied_size, counter = 0, 0
  439.         directory_times = []
  440.         time_start = time.time()
  441.         times = [(time_start, copied_size)]
  442.         long_enough = False
  443.         time_last_update = time_start
  444.  
  445.         old_umask = os.umask(0)
  446.         for path in files:
  447.             sourcepath = os.path.join(self.source, path)
  448.             targetpath = os.path.join(self.target, path)
  449.             st = os.lstat(sourcepath)
  450.             mode = stat.S_IMODE(st.st_mode)
  451.             if stat.S_ISLNK(st.st_mode):
  452.                 if not os.path.lexists(targetpath):
  453.                     linkto = os.readlink(sourcepath)
  454.                     os.symlink(linkto, targetpath)
  455.             elif stat.S_ISDIR(st.st_mode):
  456.                 if not os.path.isdir(targetpath):
  457.                     os.mkdir(targetpath, mode)
  458.             elif stat.S_ISCHR(st.st_mode):
  459.                 os.mknod(targetpath, stat.S_IFCHR | mode, st.st_rdev)
  460.             elif stat.S_ISBLK(st.st_mode):
  461.                 os.mknod(targetpath, stat.S_IFBLK | mode, st.st_rdev)
  462.             elif stat.S_ISFIFO(st.st_mode):
  463.                 os.mknod(targetpath, stat.S_IFIFO | mode)
  464.             elif stat.S_ISSOCK(st.st_mode):
  465.                 os.mknod(targetpath, stat.S_IFSOCK | mode)
  466.             elif stat.S_ISREG(st.st_mode):
  467.                 if not os.path.exists(targetpath):
  468.                     shutil.copyfile(sourcepath, targetpath)
  469.  
  470.             copied_size += st.st_size
  471.             os.lchown(targetpath, st.st_uid, st.st_gid)
  472.             if not stat.S_ISLNK(st.st_mode):
  473.                 os.chmod(targetpath, mode)
  474.             if stat.S_ISDIR(st.st_mode):
  475.                 directory_times.append((targetpath, st.st_atime, st.st_mtime))
  476.             # os.utime() sets timestamp of target, not link
  477.             elif not stat.S_ISLNK(st.st_mode):
  478.                 os.utime(targetpath, (st.st_atime, st.st_mtime))
  479.  
  480.             if int((copied_size * 90) / total_size) != copy_progress:
  481.                 copy_progress = int((copied_size * 90) / total_size)
  482.                 self.db.progress('SET', 10 + copy_progress)
  483.  
  484.             time_now = time.time()
  485.             if (time_now - times[-1][0]) >= 0.5:
  486.                 times.append((time_now, copied_size))
  487.                 if not long_enough and time_now - times[0][0] >= 10:
  488.                     long_enough = True
  489.                 if long_enough and time_now - time_last_update >= 2:
  490.                     time_last_update = time_now
  491.                     while (time_now - times[0][0] > 60 and
  492.                            time_now - times[1][0] >= 60):
  493.                         times.pop(0)
  494.                     speed = ((times[-1][1] - times[0][1]) /
  495.                              (times[-1][0] - times[0][0]))
  496.                     time_remaining = int((total_size - copied_size) / speed)
  497.                     time_str = "%d:%02d" % divmod(time_remaining, 60)
  498.                     self.db.subst('ubiquity/install/copying_time',
  499.                                   'TIME', time_str)
  500.                     self.db.progress('INFO', 'ubiquity/install/copying_time')
  501.  
  502.         # Apply timestamps to all directories now that the items within them
  503.         # have been copied.
  504.         for dirtime in directory_times:
  505.             (directory, atime, mtime) = dirtime
  506.             os.utime(directory, (atime, mtime))
  507.  
  508.         os.umask(old_umask)
  509.  
  510.         self.db.progress('SET', 100)
  511.         self.db.progress('STOP')
  512.  
  513.  
  514.     def copy_logs(self):
  515.         """copy log files into installed system."""
  516.  
  517.         target_dir = os.path.join(self.target, 'var/log/installer')
  518.         if not os.path.exists(target_dir):
  519.             os.makedirs(target_dir)
  520.  
  521.         for log_file in ('/var/log/installer/syslog', '/var/log/partman',
  522.                          '/var/log/installer/version'):
  523.             target_log_file = os.path.join(target_dir,
  524.                                            os.path.basename(log_file))
  525.             if not misc.ex('cp', '-a', log_file, target_log_file):
  526.                 misc.pre_log('error', 'Failed to copy installation log file')
  527.             os.chmod(target_log_file, stat.S_IRUSR | stat.S_IWUSR)
  528.  
  529.  
  530.     def mount_source(self):
  531.         """mounting loop system from cloop or squashfs system."""
  532.  
  533.         self.dev = ''
  534.         if not os.path.isdir(self.source):
  535.             try:
  536.                 os.mkdir(self.source)
  537.             except Exception, e:
  538.                 print e
  539.             misc.pre_log('info', 'mkdir %s' % self.source)
  540.  
  541.         # Autodetection on unionfs systems
  542.         for line in open('/proc/mounts'):
  543.             (device, fstype) = line.split()[1:3]
  544.             if fstype == 'squashfs' and os.path.exists(device):
  545.                 misc.ex('mount', '--bind', device, self.source)
  546.                 self.unionfs = True
  547.                 return
  548.  
  549.         # Manual Detection on non unionfs systems
  550.         fsfiles = ['/cdrom/casper/filesystem.cloop',
  551.                    '/cdrom/casper/filesystem.squashfs',
  552.                    '/cdrom/META/META.squashfs']
  553.  
  554.         for fsfile in fsfiles:
  555.             if os.path.isfile(fsfile):
  556.                 if os.path.splitext(fsfile)[1] == '.cloop':
  557.                     self.dev = '/dev/cloop1'
  558.                     break
  559.                 elif os.path.splitext(fsfile)[1] == '.squashfs':
  560.                     self.dev = '/dev/loop3'
  561.                     break
  562.  
  563.         if self.dev == '':
  564.             raise InstallStepError("No source device found")
  565.  
  566.         misc.ex('losetup', self.dev, file)
  567.         try:
  568.             misc.ex('mount', self.dev, self.source)
  569.         except Exception, e:
  570.             print e
  571.  
  572.  
  573.     def umount_source(self):
  574.         """umounting loop system from cloop or squashfs system."""
  575.  
  576.         if not misc.ex('umount', self.source):
  577.             raise InstallStepError("Failed to unmount source device")
  578.         if self.unionfs:
  579.             return
  580.         if not misc.ex('losetup', '-d', self.dev) and self.dev != '':
  581.             raise InstallStepError("Failed to detach loopback source device")
  582.  
  583.  
  584.     def run_target_config_hooks(self):
  585.         """Run hook scripts from /usr/lib/ubiquity/target-config. This allows
  586.         casper to hook into us and repeat bits of its configuration in the
  587.         target system."""
  588.  
  589.         hookdir = '/usr/lib/ubiquity/target-config'
  590.  
  591.         if os.path.isdir(hookdir):
  592.             # Exclude hooks containing '.', so that *.dpkg-* et al are avoided.
  593.             hooks = filter(lambda entry: '.' not in entry, os.listdir(hookdir))
  594.             self.db.progress('START', 0, len(hooks), 'ubiquity/install/title')
  595.             for hookentry in hooks:
  596.                 hook = os.path.join(hookdir, hookentry)
  597.                 if not os.access(hook, os.X_OK):
  598.                     self.db.progress('STEP', 1)
  599.                     continue
  600.                 self.db.subst('ubiquity/install/target_hook',
  601.                               'SCRIPT', hookentry)
  602.                 self.db.progress('INFO', 'ubiquity/install/target_hook')
  603.                 # Errors are ignored at present, although this may change.
  604.                 subprocess.call(hook)
  605.                 self.db.progress('STEP', 1)
  606.             self.db.progress('STOP')
  607.  
  608.  
  609.     def configure_locales(self):
  610.         """Apply locale settings to installed system."""
  611.         dbfilter = language_apply.LanguageApply(None)
  612.         ret = dbfilter.run_command(auto_process=True)
  613.         if ret != 0:
  614.             raise InstallStepError("LanguageApply failed with code %d" % ret)
  615.  
  616.  
  617.     def configure_apt(self):
  618.         """Configure /etc/apt/sources.list."""
  619.  
  620.         # Avoid clock skew causing gpg verification issues.
  621.         # This file will be left in place until the end of the install.
  622.         apt_conf_itc = open(os.path.join(
  623.             self.target, 'etc/apt/apt.conf.d/00IgnoreTimeConflict'), 'w')
  624.         print >>apt_conf_itc, ('Acquire::gpgv::Options {'
  625.                                ' "--ignore-time-conflict"; };')
  626.         apt_conf_itc.close()
  627.  
  628.         dbfilter = apt_setup.AptSetup(None)
  629.         ret = dbfilter.run_command(auto_process=True)
  630.         if ret != 0:
  631.             raise InstallStepError("AptSetup failed with code %d" % ret)
  632.  
  633.  
  634.     def get_cache_pkg(self, cache, pkg):
  635.         # work around broken has_key in python-apt 0.6.16
  636.         try:
  637.             return cache[pkg]
  638.         except KeyError:
  639.             return None
  640.  
  641.  
  642.     def record_installed(self, pkgs):
  643.         """Record which packages we've explicitly installed so that we don't
  644.         try to remove them later."""
  645.  
  646.         record_file = "/var/lib/ubiquity/apt-installed"
  647.         if not os.path.exists(os.path.dirname(record_file)):
  648.             os.makedirs(os.path.dirname(record_file))
  649.         record = open(record_file, "a")
  650.  
  651.         for pkg in pkgs:
  652.             print >>record, pkg
  653.  
  654.         record.close()
  655.  
  656.  
  657.     def mark_install(self, cache, pkg):
  658.         cachedpkg = self.get_cache_pkg(cache, pkg)
  659.         if cachedpkg is not None and not cachedpkg.isInstalled:
  660.             apt_error = False
  661.             try:
  662.                 cachedpkg.markInstall()
  663.             except SystemError:
  664.                 apt_error = True
  665.             if cache._depcache.BrokenCount > 0 or apt_error:
  666.                 cachedpkg.markKeep()
  667.                 assert cache._depcache.BrokenCount == 0
  668.  
  669.  
  670.     def install_language_packs(self):
  671.         langpacks = []
  672.         try:
  673.             langpack_db = self.db.get('base-config/language-packs')
  674.             langpacks = langpack_db.replace(',', '').split()
  675.         except debconf.DebconfError:
  676.             pass
  677.         if not langpacks:
  678.             try:
  679.                 langpack_db = self.db.get('pkgsel/language-packs')
  680.                 langpacks = langpack_db.replace(',', '').split()
  681.             except debconf.DebconfError:
  682.                 pass
  683.         if not langpacks:
  684.             try:
  685.                 langpack_db = self.db.get('localechooser/supported-locales')
  686.                 langpack_set = set()
  687.                 for locale in langpack_db.replace(',', '').split():
  688.                     langpack_set.add(locale.split('_')[0])
  689.                 langpacks = sorted(langpack_set)
  690.             except debconf.DebconfError:
  691.                 pass
  692.         if not langpacks:
  693.             langpack_db = self.db.get('debian-installer/locale')
  694.             langpacks = [langpack_db.split('_')[0]]
  695.         misc.pre_log('info',
  696.                      'keeping language packs for: %s' % ' '.join(langpacks))
  697.  
  698.         try:
  699.             lppatterns = self.db.get('pkgsel/language-pack-patterns').split()
  700.         except debconf.DebconfError:
  701.             return
  702.  
  703.         to_install = []
  704.         for lp in langpacks:
  705.             # Basic language packs, required to get localisation working at
  706.             # all. We install these almost unconditionally; if you want to
  707.             # get rid of even these, you can preseed pkgsel/language-packs
  708.             # to the empty string.
  709.             to_install.append('language-pack-%s' % lp)
  710.             # Other language packs, typically selected by preseeding.
  711.             for pattern in lppatterns:
  712.                 to_install.append(pattern.replace('$LL', lp))
  713.             # More extensive language support packages.
  714.             to_install.append('language-support-%s' % lp)
  715.         self.record_installed(to_install)
  716.  
  717.         self.db.progress('START', 0, 100, 'ubiquity/langpacks/title')
  718.  
  719.         self.db.progress('REGION', 0, 10)
  720.         fetchprogress = DebconfFetchProgress(
  721.             self.db, 'ubiquity/langpacks/title',
  722.             'ubiquity/install/apt_indices_starting',
  723.             'ubiquity/install/apt_indices')
  724.         cache = Cache()
  725.         try:
  726.             # update() returns False on failure and 0 on success. Madness!
  727.             if cache.update(fetchprogress) not in (0, True):
  728.                 fetchprogress.stop()
  729.                 self.db.progress('STOP')
  730.                 return
  731.         except IOError, e:
  732.             print >>sys.stderr, e
  733.             sys.stderr.flush()
  734.             self.db.progress('STOP')
  735.             raise
  736.         cache.open(None)
  737.         self.db.progress('SET', 10)
  738.  
  739.         self.db.progress('REGION', 10, 100)
  740.         fetchprogress = DebconfFetchProgress(
  741.             self.db, 'ubiquity/langpacks/title', None,
  742.             'ubiquity/langpacks/packages')
  743.         installprogress = DebconfInstallProgress(
  744.             self.db, 'ubiquity/langpacks/title', 'ubiquity/install/apt_info')
  745.  
  746.         for lp in to_install:
  747.             self.mark_install(cache, lp)
  748.         installed_pkgs = []
  749.         for pkg in cache.keys():
  750.             if (cache[pkg].markedInstall or cache[pkg].markedUpgrade or
  751.                 cache[pkg].markedReinstall or cache[pkg].markedDowngrade):
  752.                 installed_pkgs.append(pkg)
  753.         self.record_installed(installed_pkgs)
  754.  
  755.         try:
  756.             if not cache.commit(fetchprogress, installprogress):
  757.                 fetchprogress.stop()
  758.                 installprogress.finishUpdate()
  759.                 self.db.progress('STOP')
  760.                 return
  761.         except IOError, e:
  762.             print >>sys.stderr, e
  763.             sys.stderr.flush()
  764.             self.db.progress('STOP')
  765.             raise
  766.         except SystemError, e:
  767.             print >>sys.stderr, e
  768.             sys.stderr.flush()
  769.             self.db.progress('STOP')
  770.             raise
  771.         self.db.progress('SET', 100)
  772.  
  773.         self.db.progress('STOP')
  774.  
  775.  
  776.     def configure_timezone(self):
  777.         """Set timezone on installed system."""
  778.  
  779.         dbfilter = timezone_apply.TimezoneApply(None)
  780.         ret = dbfilter.run_command(auto_process=True)
  781.         if ret != 0:
  782.             raise InstallStepError("TimezoneApply failed with code %d" % ret)
  783.  
  784.         dbfilter = clock_setup.ClockSetup(None)
  785.         ret = dbfilter.run_command(auto_process=True)
  786.         if ret != 0:
  787.             raise InstallStepError("ClockSetup failed with code %d" % ret)
  788.  
  789.  
  790.     def configure_keyboard(self):
  791.         """Set keyboard in installed system."""
  792.  
  793.         try:
  794.             keymap = self.db.get('debian-installer/keymap')
  795.             self.set_debconf('debian-installer/keymap', keymap)
  796.         except debconf.DebconfError:
  797.             pass
  798.  
  799.         dbfilter = kbd_chooser_apply.KbdChooserApply(None)
  800.         ret = dbfilter.run_command(auto_process=True)
  801.         if ret != 0:
  802.             raise InstallStepError("KbdChooserApply failed with code %d" % ret)
  803.  
  804.  
  805.     def configure_user(self):
  806.         """create the user selected along the installation process
  807.         into the installed system. Default user from live system is
  808.         deleted and skel for this new user is copied to $HOME."""
  809.  
  810.         dbfilter = usersetup_apply.UserSetupApply(None)
  811.         ret = dbfilter.run_command(auto_process=True)
  812.         if ret != 0:
  813.             raise InstallStepError("UserSetupApply failed with code %d" % ret)
  814.  
  815.  
  816.     def get_resume_partition(self):
  817.         biggest_size = 0
  818.         biggest_partition = None
  819.         swaps = open('/proc/swaps')
  820.         for line in swaps:
  821.             words = line.split()
  822.             if words[1] != 'partition':
  823.                 continue
  824.             size = int(words[2])
  825.             if size > biggest_size:
  826.                 biggest_size = size
  827.                 biggest_partition = words[0]
  828.         swaps.close()
  829.         return biggest_partition
  830.  
  831.     def configure_hardware(self):
  832.         """reconfiguring several packages which depends on the
  833.         hardware system in which has been installed on and need some
  834.         automatic configurations to get work."""
  835.  
  836.         dbfilter = hw_detect.HwDetect(None, self.db)
  837.         ret = dbfilter.run_command(auto_process=True)
  838.         if ret != 0:
  839.             raise InstallStepError("HwDetect failed with code %d" % ret)
  840.  
  841.         self.db.progress('INFO', 'ubiquity/install/hardware')
  842.  
  843.         subprocess.call(['/usr/lib/ubiquity/debian-installer-utils'
  844.                          '/register-module.post-base-installer'])
  845.  
  846.         resume = self.get_resume_partition()
  847.         if resume is not None:
  848.             resume_uuid = None
  849.             try:
  850.                 resume_uuid = subprocess.Popen(
  851.                     ['vol_id', '-u', resume],
  852.                     stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
  853.             except OSError:
  854.                 pass
  855.             if resume_uuid:
  856.                 resume = "UUID=%s" % resume_uuid
  857.             if os.path.exists(os.path.join(self.target,
  858.                                            'etc/initramfs-tools/conf.d')):
  859.                 configdir = os.path.join(self.target,
  860.                                          'etc/initramfs-tools/conf.d')
  861.             elif os.path.exists(os.path.join(self.target,
  862.                                              'etc/mkinitramfs/conf.d')):
  863.                 configdir = os.path.join(self.target,
  864.                                          'etc/mkinitramfs/conf.d')
  865.             else:
  866.                 configdir = None
  867.             if configdir is not None:
  868.                 configfile = open(os.path.join(configdir, 'resume'), 'w')
  869.                 print >>configfile, "RESUME=%s" % resume
  870.                 configfile.close()
  871.  
  872.         self.chrex('mount', '-t', 'proc', 'proc', '/proc')
  873.         self.chrex('mount', '-t', 'sysfs', 'sysfs', '/sys')
  874.  
  875.         packages = ['linux-image-' + self.kernel_version,
  876.                     'linux-restricted-modules-' + self.kernel_version]
  877.  
  878.         try:
  879.             for package in packages:
  880.                 self.reconfigure(package)
  881.         finally:
  882.             self.chrex('umount', '/proc')
  883.             self.chrex('umount', '/sys')
  884.  
  885.  
  886.     def get_all_interfaces(self):
  887.         """Get all non-local network interfaces."""
  888.         ifs = []
  889.         ifs_file = open('/proc/net/dev')
  890.         # eat header
  891.         ifs_file.readline()
  892.         ifs_file.readline()
  893.  
  894.         for line in ifs_file:
  895.             name = re.match('(.*?(?::\d+)?):', line.strip()).group(1)
  896.             if name == 'lo':
  897.                 continue
  898.             ifs.append(name)
  899.  
  900.         ifs_file.close()
  901.         return ifs
  902.  
  903.  
  904.     def configure_network(self):
  905.         """Automatically configure the network.
  906.         
  907.         At present, the only thing the user gets to tweak in the UI is the
  908.         hostname. Some other things will be copied from the live filesystem,
  909.         so changes made there will be reflected in the installed system.
  910.         
  911.         Unfortunately, at present we have to duplicate a fair bit of netcfg
  912.         here, because it's hard to drive netcfg in a way that won't try to
  913.         bring interfaces up and down."""
  914.  
  915.         # TODO cjwatson 2006-03-30: just call netcfg instead of doing all
  916.         # this; requires a netcfg binary that doesn't bring interfaces up
  917.         # and down
  918.  
  919.         for path in ('/etc/network/interfaces', '/etc/resolv.conf'):
  920.             if os.path.exists(path):
  921.                 shutil.copy2(path, os.path.join(self.target, path[1:]))
  922.  
  923.         try:
  924.             hostname = self.db.get('netcfg/get_hostname')
  925.         except debconf.DebconfError:
  926.             hostname = ''
  927.         if hostname == '':
  928.             hostname = 'ubuntu'
  929.         fp = open(os.path.join(self.target, 'etc/hostname'), 'w')
  930.         print >>fp, hostname
  931.         fp.close()
  932.  
  933.         hosts = open(os.path.join(self.target, 'etc/hosts'), 'w')
  934.         print >>hosts, "127.0.0.1\tlocalhost"
  935.         print >>hosts, "127.0.1.1\t%s" % hostname
  936.         print >>hosts, textwrap.dedent("""\
  937.  
  938.             # The following lines are desirable for IPv6 capable hosts
  939.             ::1     ip6-localhost ip6-loopback
  940.             fe00::0 ip6-localnet
  941.             ff00::0 ip6-mcastprefix
  942.             ff02::1 ip6-allnodes
  943.             ff02::2 ip6-allrouters
  944.             ff02::3 ip6-allhosts""")
  945.         hosts.close()
  946.  
  947.         # TODO cjwatson 2006-03-30: from <bits/ioctls.h>; ugh, but no
  948.         # binding available
  949.         SIOCGIFHWADDR = 0x8927
  950.         # <net/if_arp.h>
  951.         ARPHRD_ETHER = 1
  952.  
  953.         if_names = {}
  954.         sock = socket.socket(socket.SOCK_DGRAM)
  955.         interfaces = self.get_all_interfaces()
  956.         for i in range(len(interfaces)):
  957.             if_names[interfaces[i]] = struct.unpack('H6s',
  958.                 fcntl.ioctl(sock.fileno(), SIOCGIFHWADDR,
  959.                             struct.pack('256s', interfaces[i]))[16:24])
  960.         sock.close()
  961.  
  962.         iftab = open(os.path.join(self.target, 'etc/iftab'), 'w')
  963.  
  964.         print >>iftab, textwrap.dedent("""\
  965.             # This file assigns persistent names to network interfaces.
  966.             # See iftab(5) for syntax.
  967.             """)
  968.  
  969.         for i in range(len(interfaces)):
  970.             dup = False
  971.             with_arp = False
  972.  
  973.             if_name = if_names[interfaces[i]]
  974.             if if_name is None or if_name[0] != ARPHRD_ETHER:
  975.                 continue
  976.  
  977.             for j in range(len(interfaces)):
  978.                 if i == j or if_names[interfaces[j]] is None:
  979.                     continue
  980.                 if if_name[1] != if_names[interfaces[j]][1]:
  981.                     continue
  982.  
  983.                 if if_names[interfaces[j]][0] == ARPHRD_ETHER:
  984.                     dup = True
  985.  
  986.             if dup:
  987.                 continue
  988.  
  989.             line = (interfaces[i] + " mac " +
  990.                     ':'.join(['%02x' % ord(if_name[1][c]) for c in range(6)]))
  991.             line += " arp %d" % if_name[0]
  992.             print >>iftab, line
  993.  
  994.         iftab.close()
  995.  
  996.  
  997.     def configure_bootloader(self):
  998.         """configuring and installing boot loader into installed
  999.         hardware system."""
  1000.  
  1001.         misc.ex('mount', '--bind', '/proc', self.target + '/proc')
  1002.         misc.ex('mount', '--bind', '/dev', self.target + '/dev')
  1003.  
  1004.         try:
  1005.             from ubiquity.components import grubinstaller
  1006.             dbfilter = grubinstaller.GrubInstaller(None)
  1007.             ret = dbfilter.run_command(auto_process=True)
  1008.             if ret != 0:
  1009.                 raise InstallStepError(
  1010.                     "GrubInstaller failed with code %d" % ret)
  1011.         except ImportError:
  1012.             try:
  1013.                 from ubiquity.components import yabootinstaller
  1014.                 dbfilter = yabootinstaller.YabootInstaller(None)
  1015.                 ret = dbfilter.run_command(auto_process=True)
  1016.                 if ret != 0:
  1017.                     raise InstallStepError(
  1018.                         "YabootInstaller failed with code %d" % ret)
  1019.             except ImportError:
  1020.                 raise InstallStepError("No bootloader installer found")
  1021.  
  1022.         misc.ex('umount', '-f', self.target + '/proc')
  1023.         misc.ex('umount', '-f', self.target + '/dev')
  1024.  
  1025.  
  1026.     def do_remove(self, to_remove, recursive=False):
  1027.         self.db.progress('START', 0, 5, 'ubiquity/install/title')
  1028.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  1029.  
  1030.         fetchprogress = DebconfFetchProgress(
  1031.             self.db, 'ubiquity/install/title',
  1032.             'ubiquity/install/apt_indices_starting',
  1033.             'ubiquity/install/apt_indices')
  1034.         cache = Cache()
  1035.  
  1036.         while True:
  1037.             removed = set()
  1038.             for pkg in to_remove:
  1039.                 cachedpkg = self.get_cache_pkg(cache, pkg)
  1040.                 if cachedpkg is not None and cachedpkg.isInstalled:
  1041.                     apt_error = False
  1042.                     try:
  1043.                         cachedpkg.markDelete(autoFix=False, purge=True)
  1044.                     except SystemError:
  1045.                         apt_error = True
  1046.                     if apt_error:
  1047.                         cachedpkg.markKeep()
  1048.                     elif cache._depcache.BrokenCount > 0:
  1049.                         # If we're recursively removing packages, or if all
  1050.                         # of the broken packages are in the set of packages
  1051.                         # to remove anyway, then go ahead and try to remove
  1052.                         # them too.
  1053.                         brokenpkgs = set()
  1054.                         for pkg in cache.keys():
  1055.                             if cache._depcache.IsInstBroken(cache._cache[pkg]):
  1056.                                 brokenpkgs.add(pkg)
  1057.                         broken_removed = set()
  1058.                         if recursive or brokenpkgs <= to_remove:
  1059.                             for pkg in brokenpkgs:
  1060.                                 cachedpkg2 = self.get_cache_pkg(cache, pkg)
  1061.                                 if cachedpkg2 is not None:
  1062.                                     broken_removed.add(pkg)
  1063.                                     try:
  1064.                                         cachedpkg2.markDelete(autoFix=False,
  1065.                                                               purge=True)
  1066.                                     except SystemError:
  1067.                                         apt_error = True
  1068.                                         break
  1069.                         if apt_error or cache._depcache.BrokenCount > 0:
  1070.                             # That didn't work. Revert all the removals we
  1071.                             # just tried.
  1072.                             for pkg in broken_removed:
  1073.                                 self.get_cache_pkg(cache, pkg).markKeep()
  1074.                             cachedpkg.markKeep()
  1075.                         else:
  1076.                             removed.add(pkg)
  1077.                             removed |= broken_removed
  1078.                     else:
  1079.                         removed.add(pkg)
  1080.                     assert cache._depcache.BrokenCount == 0
  1081.             if len(removed) == 0:
  1082.                 break
  1083.             to_remove -= removed
  1084.  
  1085.         self.db.progress('SET', 1)
  1086.         self.db.progress('REGION', 1, 5)
  1087.         fetchprogress = DebconfFetchProgress(
  1088.             self.db, 'ubiquity/install/title', None,
  1089.             'ubiquity/install/fetch_remove')
  1090.         installprogress = DebconfInstallProgress(
  1091.             self.db, 'ubiquity/install/title', 'ubiquity/install/apt_info',
  1092.             'ubiquity/install/apt_error_remove')
  1093.         try:
  1094.             if not cache.commit(fetchprogress, installprogress):
  1095.                 fetchprogress.stop()
  1096.                 installprogress.finishUpdate()
  1097.                 self.db.progress('STOP')
  1098.                 return
  1099.         except SystemError, e:
  1100.             print >>sys.stderr, e
  1101.             sys.stderr.flush()
  1102.             self.db.progress('STOP')
  1103.             raise
  1104.         self.db.progress('SET', 5)
  1105.  
  1106.         self.db.progress('STOP')
  1107.  
  1108.  
  1109.     def remove_unusable_kernels(self):
  1110.         """Remove unusable kernels; keeping them may cause us to be unable
  1111.         to boot."""
  1112.  
  1113.         self.db.progress('START', 0, 6, 'ubiquity/install/title')
  1114.  
  1115.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  1116.  
  1117.         # Check for kernel packages to remove.
  1118.         dbfilter = check_kernels.CheckKernels(None)
  1119.         dbfilter.run_command(auto_process=True)
  1120.  
  1121.         remove_kernels = set()
  1122.         if os.path.exists("/var/lib/ubiquity/remove-kernels"):
  1123.             for line in open("/var/lib/ubiquity/remove-kernels"):
  1124.                 remove_kernels.add(line.strip())
  1125.  
  1126.         if len(remove_kernels) == 0:
  1127.             self.db.progress('STOP')
  1128.             return
  1129.  
  1130.         self.db.progress('SET', 1)
  1131.         self.db.progress('REGION', 1, 5)
  1132.         try:
  1133.             self.do_remove(remove_kernels, recursive=True)
  1134.         except:
  1135.             self.db.progress('STOP')
  1136.             raise
  1137.         self.db.progress('SET', 5)
  1138.  
  1139.         # Now we need to fix up kernel symlinks. Depending on the
  1140.         # architecture, these may be in / or in /boot.
  1141.         bootdir = os.path.join(self.target, 'boot')
  1142.         if self.db.get('base-installer/kernel/linux/link_in_boot') == 'true':
  1143.             linkdir = bootdir
  1144.             linkprefix = ''
  1145.         else:
  1146.             linkdir = self.target
  1147.             linkprefix = 'boot'
  1148.  
  1149.         # Remove old symlinks. We'll set them up from scratch.
  1150.         re_symlink = re.compile('vmlinu[xz]|initrd.img$')
  1151.         for entry in os.listdir(linkdir):
  1152.             if re_symlink.match(entry) is not None:
  1153.                 filename = os.path.join(linkdir, entry)
  1154.                 if os.path.islink(filename):
  1155.                     os.unlink(filename)
  1156.         if linkdir != self.target:
  1157.             # Remove symlinks in /target too, which may have been created on
  1158.             # the live filesystem. This isn't necessary, but it may help
  1159.             # avoid confusion.
  1160.             for entry in os.listdir(self.target):
  1161.                 if re_symlink.match(entry) is not None:
  1162.                     filename = os.path.join(self.target, entry)
  1163.                     if os.path.islink(filename):
  1164.                         os.unlink(filename)
  1165.  
  1166.         # Create symlinks. Prefer our current kernel version if possible,
  1167.         # but if not (perhaps due to a customised live filesystem image),
  1168.         # it's better to create some symlinks than none at all.
  1169.         re_image = re.compile('(vmlinu[xz]|initrd.img)-')
  1170.         for entry in os.listdir(bootdir):
  1171.             match = re_image.match(entry)
  1172.             if match is not None:
  1173.                 imagetype = match.group(1)
  1174.                 linksrc = os.path.join(linkprefix, entry)
  1175.                 linkdst = os.path.join(linkdir, imagetype)
  1176.                 if os.path.exists(linkdst):
  1177.                     if entry.endswith('-' + self.kernel_version):
  1178.                         os.unlink(linkdst)
  1179.                     else:
  1180.                         continue
  1181.                 os.symlink(linksrc, linkdst)
  1182.  
  1183.         self.db.progress('SET', 6)
  1184.         self.db.progress('STOP')
  1185.  
  1186.  
  1187.     def remove_extras(self):
  1188.         """Try to remove packages that are needed on the live CD but not on
  1189.         the installed system."""
  1190.  
  1191.         # Looking through files for packages to remove is pretty quick, so
  1192.         # don't bother with a progress bar for that.
  1193.  
  1194.         # Check for packages specific to the live CD.
  1195.         if (os.path.exists("/cdrom/casper/filesystem.manifest-desktop") and
  1196.             os.path.exists("/cdrom/casper/filesystem.manifest")):
  1197.             desktop_packages = set()
  1198.             for line in open("/cdrom/casper/filesystem.manifest-desktop"):
  1199.                 if line.strip() != '' and not line.startswith('#'):
  1200.                     desktop_packages.add(line.split()[0])
  1201.             live_packages = set()
  1202.             for line in open("/cdrom/casper/filesystem.manifest"):
  1203.                 if line.strip() != '' and not line.startswith('#'):
  1204.                     live_packages.add(line.split()[0])
  1205.             difference = live_packages - desktop_packages
  1206.         else:
  1207.             difference = set()
  1208.  
  1209.         # Keep packages we explicitly installed.
  1210.         apt_installed = set()
  1211.         if os.path.exists("/var/lib/ubiquity/apt-installed"):
  1212.             for line in open("/var/lib/ubiquity/apt-installed"):
  1213.                 apt_installed.add(line.strip())
  1214.         difference -= apt_installed
  1215.  
  1216.         if len(difference) == 0:
  1217.             return
  1218.  
  1219.         # Don't worry about failures removing packages; it will be easier
  1220.         # for the user to sort them out with a graphical package manager (or
  1221.         # whatever) after installation than it will be to try to deal with
  1222.         # them automatically here.
  1223.         self.do_remove(difference)
  1224.  
  1225.  
  1226.     def cleanup(self):
  1227.         """Miscellaneous cleanup tasks."""
  1228.         os.unlink(os.path.join(
  1229.             self.target, 'etc/apt/apt.conf.d/00IgnoreTimeConflict'))
  1230.  
  1231.  
  1232.     def chrex(self, *args):
  1233.         """executes commands on chroot system (provided by *args)."""
  1234.  
  1235.         msg = ''
  1236.         for word in args:
  1237.             msg += str(word) + ' '
  1238.         if not misc.ex('chroot', self.target, *args):
  1239.             misc.pre_log('error', 'chroot ' + msg)
  1240.             return False
  1241.         return True
  1242.  
  1243.  
  1244.     def copy_debconf(self, package):
  1245.         """setting debconf database into installed system."""
  1246.  
  1247.         # TODO cjwatson 2006-02-25: unusable here now because we have a
  1248.         # running debconf frontend that's locked the database; fortunately
  1249.         # this isn't critical. We still need to think about how to handle
  1250.         # preseeding in general, though.
  1251.         targetdb = os.path.join(self.target, 'var/cache/debconf/config.dat')
  1252.  
  1253.         misc.ex('debconf-copydb', 'configdb', 'targetdb', '-p',
  1254.                 '^%s/' % package, '--config=Name:targetdb',
  1255.                 '--config=Driver:File','--config=Filename:' + targetdb)
  1256.  
  1257.  
  1258.     def set_debconf(self, question, value):
  1259.         dccomm = subprocess.Popen(['chroot', self.target,
  1260.                                    'debconf-communicate',
  1261.                                    '-fnoninteractive', 'ubiquity'],
  1262.                                   stdin=subprocess.PIPE,
  1263.                                   stdout=subprocess.PIPE, close_fds=True)
  1264.         dc = debconf.Debconf(read=dccomm.stdout, write=dccomm.stdin)
  1265.         dc.set(question, value)
  1266.         dc.fset(question, 'seen', 'true')
  1267.         dccomm.stdin.close()
  1268.         dccomm.wait()
  1269.  
  1270.  
  1271.     def reconfigure(self, package):
  1272.         """executes a dpkg-reconfigure into installed system to each
  1273.         package which provided by args."""
  1274.  
  1275.         self.chrex('dpkg-reconfigure', '-fnoninteractive', package)
  1276.  
  1277.  
  1278. if __name__ == '__main__':
  1279.     if not os.path.exists('/var/lib/ubiquity'):
  1280.         os.makedirs('/var/lib/ubiquity')
  1281.     if os.path.exists('/var/lib/ubiquity/install.trace'):
  1282.         os.unlink('/var/lib/ubiquity/install.trace')
  1283.  
  1284.     install = Install()
  1285.     sys.excepthook = install.excepthook
  1286.     install.run()
  1287.     sys.exit(0)
  1288.  
  1289. # vim:ai:et:sts=4:tw=80:sw=4:
  1290.