home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / usbcreator / backend.py < prev    next >
Encoding:
Python Source  |  2009-04-17  |  25.3 KB  |  602 lines

  1. # Copyright (C) 2008 Canonical Ltd.
  2.  
  3. # This program is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License version 3,
  5. # as published by the Free Software Foundation.
  6. #
  7. # This program is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10. # GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License
  13. # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  14.  
  15. import os
  16. import subprocess, sys
  17. import stat
  18. import shutil
  19. import gobject
  20. import dbus
  21. from dbus.mainloop.glib import DBusGMainLoop
  22. import time
  23. import tempfile
  24.  
  25. class Logger:
  26.     def __init__ (self):
  27.         if 'SUDO_USER' in os.environ:
  28.             log_file = '%s/.usb-creator.log' % \
  29.                 os.path.expanduser('~' + os.environ['SUDO_USER'])
  30.         else:
  31.             log_file = '%s/.usb-creator.log' % \
  32.                 os.path.expanduser('~')
  33.         self.fp = open(log_file, 'a')
  34.         self.save = sys.stderr
  35.         sys.stderr = self
  36.         self.new_line = 1
  37.         l = '\n-- Starting up at %s --\n' % time.strftime('%H:%M:%S')
  38.         self.fp.write(l)
  39.         self.fp.flush()
  40.         sys.stdout.write(l)
  41.         sys.stdout.flush()
  42.  
  43.     def __del__(self):
  44.         self.fp.close()
  45.         self.stderr = self.save
  46.  
  47.     def fileno(self):
  48.         return self.fp.fileno()
  49.  
  50.     def write(self, line):
  51.         if (self.new_line):
  52.             l = '[%s] ' % time.strftime('%H:%M:%S')
  53.             self.new_line = 0
  54.             l = l + line
  55.         else:
  56.             l = line
  57.  
  58.         self.fp.write(l)
  59.         self.fp.flush()
  60.         sys.stdout.write(l)
  61.         sys.stdout.flush()
  62.         if (line[-1] == '\n'):
  63.             self.new_line = 1
  64.  
  65. def popen(cmd):
  66.     print >>sys.stderr, str(cmd)
  67.     process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
  68.         stderr=sys.stderr, stdin=subprocess.PIPE)
  69.     res = process.communicate()
  70.     return process, res
  71.  
  72. def free_space(dev):
  73.     try:
  74.         stat = os.statvfs(dev)
  75.     except:
  76.         # This could get called in the event loop as we're shutting down, after
  77.         # we've unmounted filesystems.
  78.         return 0
  79.     return stat.f_bsize * stat.f_bavail
  80.  
  81. class Backend:
  82.     def __init__(self, frontend):
  83.         self.devices = {}
  84.         self.cds = {}
  85.         self.timeouts = {}
  86.         self.copy_timeout = 0
  87.         self.original_size = 0
  88.         self.progress_description = ''
  89.         self.frontend = frontend
  90.         self.logger = Logger()
  91.         DBusGMainLoop(set_as_default=True)
  92.         self.bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
  93.         hal_obj = self.bus.get_object('org.freedesktop.Hal',
  94.             '/org/freedesktop/Hal/Manager')
  95.         self.hal = dbus.Interface(hal_obj, 'org.freedesktop.Hal.Manager')
  96.         self.pipe = None
  97.  
  98.     def log(self, line):
  99.         # It might make more sense to just reassign stdout since we're logging
  100.         # everything anyway.
  101.         line = str(line) + '\n'
  102.         self.logger.write(line)
  103.  
  104.     def set_install_source(self, source):
  105.         # TODO: Make more consistent with install_target
  106.         self.install_source = source
  107.  
  108.     def set_install_target(self, target):
  109.         self.install_target = self.devices[target]
  110.  
  111.     def set_persistence_size(self, persist):
  112.         self.persistence_size = persist
  113.  
  114.     def format_device(self, device):
  115.         udi = self.hal.FindDeviceStringMatch('block.device', device)
  116.         udi = udi[0]
  117.         children = self.hal.FindDeviceStringMatch('info.parent', udi)
  118.         for child in children:
  119.             dev_obj = self.bus.get_object('org.freedesktop.Hal', child)
  120.             child = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
  121.             dev = str(child.GetProperty('block.device'))
  122.             popen(['umount', dev])
  123.         popen(['umount', device])
  124.  
  125.         # TODO: This could really use a progress dialog.
  126.         res = popen(['parted', '-s', device, 'mklabel', 'msdos'])
  127.         if res[0].returncode:
  128.             message = _('Unable to create a partition table:') + '\n' + str(res[1][0])
  129.             self.log(message)
  130.             self.frontend.notify(message)
  131.             return
  132.         res = popen(['parted', '-s', device, 'mkpartfs', 'primary', 'fat32', '0', '--', '-0'])
  133.         if res[0].returncode:
  134.             message = _('Unable to format device:') + '\n' + str(res[1][0])
  135.             self.log(message)
  136.             self.frontend.notify(message)
  137.         else:
  138.             self.devices.pop(device)
  139.             self.frontend.device_removed(device, source=False)
  140.     
  141.     def device_added(self, udi):
  142.         self.log('possibly adding: ' + str(udi))
  143.         dev_obj = self.bus.get_object("org.freedesktop.Hal", udi)
  144.         dev = dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
  145.         if dev.PropertyExists('volume.is_disc') and dev.GetProperty('volume.is_disc'):
  146.             if not dev.GetProperty('volume.disc.is_blank'):
  147.                 self.log('got a disc: %s' % dev.GetProperty('volume.label'))
  148.                 self.bus.add_signal_receiver(self.property_modified,
  149.                 'PropertyModified', 'org.freedesktop.Hal.Device',
  150.                 'org.freedesktop.Hal', udi, path_keyword='udi')
  151.         # Look for the volume first as it may not have appeared yet when you
  152.         # check the parent.
  153.         success = False
  154.         if dev.PropertyExists('block.is_volume') and dev.GetProperty('block.is_volume'):
  155.             if (dev.PropertyExists('storage.bus') and
  156.             dev.GetProperty('storage.bus') == 'usb') and \
  157.             dev.GetProperty('storage.removable'):
  158.                 if dev.GetProperty('volume.fstype') == 'vfat':
  159.                     success = True
  160.                 else:
  161.                     self.log('didnt add because not vfat')
  162.             else:
  163.                 p = dev.GetProperty('info.parent')
  164.                 dev_obj = self.bus.get_object('org.freedesktop.Hal', p)
  165.                 d = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
  166.                 if (d.PropertyExists('storage.bus') and
  167.                 d.GetProperty('storage.bus') == 'usb') and \
  168.                 d.GetProperty('storage.removable'):
  169.                     if dev.GetProperty('volume.fstype') == 'vfat':
  170.                         success = True
  171.             if success:
  172.                 self.add_device(dev)
  173.                 return
  174.         # Look for empty devices.
  175.         if self.IsStorageDevice(dev):
  176.             children = self.hal.FindDeviceStringMatch('info.parent', udi)
  177.             c = False
  178.             for child in children:
  179.                 dev_obj = self.bus.get_object('org.freedesktop.Hal', child)
  180.                 child = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
  181.                 if child.GetProperty('block.is_volume') and child.GetProperty('volume.fstype') == 'vfat':
  182.                     c = True
  183.                 self.log('children: ' + str(children))
  184.             if not c and dev.PropertyExists('storage.removable.media_size'):
  185.                 self.log('no children or children not vfat')
  186.                 device = str(dev.GetProperty('block.device'))
  187.                 self.devices[device] = {
  188.                     'label' : '',
  189.                     'fstype' : '',
  190.                     'uuid' : '',
  191.                     'mountpoint' : '',
  192.                     'udi' : udi,
  193.                     'free' : dev.GetProperty('storage.removable.media_size'),
  194.                     'capacity' : dev.GetProperty('storage.removable.media_size'),
  195.                     'device' : device
  196.                 }
  197.                 self.frontend.add_dest(device)
  198.  
  199.     def property_modified(self, num_changes, change_list, udi):
  200.         dev_obj = self.bus.get_object("org.freedesktop.Hal", udi)
  201.         dev = dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
  202.         if dev.PropertyExists('volume.is_disc') and dev.GetProperty('volume.is_disc'):
  203.             if not dev.GetProperty('volume.is_mounted'):
  204.                 return
  205.             mountpoint = dev.GetProperty('volume.mount_point')
  206.             # TODO: Is the most appropriate check?
  207.             if mountpoint and os.path.exists('%s/.disk/info' % mountpoint):
  208.                 add = False
  209.                 if not self.cds.has_key(udi):
  210.                     add = True
  211.                 self.cds[udi] = {
  212.                     'label' : str(dev.GetProperty('volume.label')),
  213.                     'uuid' : str(dev.GetProperty('volume.uuid')),
  214.                     'mountpoint' : mountpoint,
  215.                     'udi' : udi,
  216.                     'size' : dev.GetProperty('volume.size'),
  217.                     'filename' : '',
  218.                 }
  219.                 if add:
  220.                     self.frontend.add_source(udi)
  221.                 self.frontend.update_all_rows(None)
  222.         else:
  223.             mountpoint = str(dev.GetProperty('volume.mount_point'))
  224.             device = str(dev.GetProperty('block.device'))
  225.             self.devices[device] = {
  226.                 'label' : str(dev.GetProperty('volume.label')).replace(' ', '_'),
  227.                 'fstype' : str(dev.GetProperty('volume.fstype')),
  228.                 'uuid' : str(dev.GetProperty('volume.uuid')),
  229.                 'mountpoint' : mountpoint,
  230.                 'udi' : str(dev.GetProperty('info.udi')),
  231.                 'free' : mountpoint and free_space(mountpoint) or 0,
  232.                 'capacity' : dev.GetProperty('volume.size'),
  233.                 'device' : device
  234.             }
  235.             self.frontend.update_dest_row(device)
  236.         self.log('prop modified')
  237.         self.log('device_udi: %s' % udi)
  238.         self.log('num_changes: %d' % num_changes)
  239.         for c in change_list:
  240.             self.log('change: %s' % str(c[0]))
  241.  
  242.     def device_removed(self, udi):
  243.         d = None
  244.         for device in self.devices.itervalues():
  245.             if device['udi'] == udi:
  246.                 self.log('removing %s' % udi)
  247.                 d = device['device']
  248.                 break
  249.         if d:
  250.             self.devices.pop(d)
  251.             self.frontend.device_removed(d, source=False)
  252.             gobject.source_remove(self.timeouts[d])
  253.             self.timeouts.pop(d)
  254.         else:
  255.             # TODO: Ick.
  256.             d = None
  257.             for device in self.cds.itervalues():
  258.                 if device['udi'] == udi:
  259.                     self.log('removing %s' % udi)
  260.                     d = udi
  261.                     break
  262.             if d:
  263.                 self.cds.pop(d)
  264.                 self.frontend.device_removed(d, source=True)
  265.             
  266.     def add_device(self, dev):
  267.         # Remove parent device if present as this means there is not an empty
  268.         # partition table.
  269.         dev_obj = self.bus.get_object("org.freedesktop.Hal",
  270.             dev.GetProperty('info.parent'))
  271.         device = dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
  272.         d = device.GetProperty('block.device')
  273.         if self.devices.has_key(d):
  274.             self.devices.pop(d)
  275.             self.frontend.device_removed(d, source=False)
  276.  
  277.         udi = dev.GetProperty('info.udi')
  278.         mountpoint = str(dev.GetProperty('volume.mount_point'))
  279.         device = str(dev.GetProperty('block.device'))
  280.         self.devices[device] = {
  281.             'label' : str(dev.GetProperty('volume.label')).replace(' ', '_'),
  282.             'fstype' : str(dev.GetProperty('volume.fstype')),
  283.             'uuid' : str(dev.GetProperty('volume.uuid')),
  284.             'mountpoint' : mountpoint,
  285.             'udi' : str(dev.GetProperty('info.udi')),
  286.             'free' : mountpoint and free_space(mountpoint) or 0,
  287.             'capacity' : dev.GetProperty('volume.size'),
  288.             'device' : device
  289.         }
  290.         self.bus.add_signal_receiver(self.property_modified,
  291.         'PropertyModified', 'org.freedesktop.Hal.Device',
  292.         'org.freedesktop.Hal', udi, path_keyword='udi')
  293.         self.log('new device:\n%s' % self.devices[device])
  294.         self.frontend.add_dest(device)
  295.         def update_free(device):
  296.             dev = self.devices[device]
  297.             mp = dev['mountpoint']
  298.             free = dev['free']
  299.             if mp:
  300.                 dev['free'] = free_space(mp)
  301.                 if free != dev['free']:
  302.                     self.frontend.update_dest_row(device)
  303.             else:
  304.                 # TODO: Is here really the best place for this?
  305.                 fstype = dev['fstype']
  306.                 if not fstype:
  307.                     return True
  308.                 dev_obj = self.bus.get_object("org.freedesktop.Hal", str(dev['udi']))
  309.                 d = dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
  310.                 if d.GetProperty('volume.mount_point'):
  311.                     return True
  312.                 tmpdir = tempfile.mkdtemp()
  313.                 res = popen(['mount', '-t', fstype, device, tmpdir])
  314.                 if res[0].returncode:
  315.                     self.log('Error mounting %s: %s' % (device, res[1]))
  316.                     popen(['rmdir', tmpdir])
  317.                 else:
  318.                     dev['mountpoint'] = tmpdir
  319.             return True
  320.         self.timeouts[device] = gobject.timeout_add(2000, update_free, device)
  321.     
  322.     def detect_devices(self):
  323.         # TODO: Handle devices with empty partition tables.
  324.         # CDs
  325.         devices = self.hal.FindDeviceByCapability('volume')
  326.         for device in devices:
  327.             dev_obj = self.bus.get_object('org.freedesktop.Hal', device)
  328.             dev = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
  329.             if dev.PropertyExists('volume.is_disc') and dev.GetProperty('volume.is_disc'):
  330.                 if not dev.GetProperty('volume.disc.is_blank'):
  331.                     self.log('got a disc: %s' % dev.GetProperty('volume.label'))
  332.                     udi = dev.GetProperty('info.udi')
  333.                     if not dev.GetProperty('volume.is_mounted'):
  334.                         self.bus.add_signal_receiver(self.property_modified,
  335.                         'PropertyModified', 'org.freedesktop.Hal.Device',
  336.                         'org.freedesktop.Hal', udi, path_keyword='udi')
  337.                     else:
  338.                         mountpoint = dev.GetProperty('volume.mount_point')
  339.                         # TODO: Is the most appropriate check?
  340.                         if mountpoint and os.path.exists('%s/.disk/info' % mountpoint):
  341.                             self.cds[udi] = {
  342.                                 'label' : str(dev.GetProperty('volume.label')),
  343.                                 'uuid' : str(dev.GetProperty('volume.uuid')),
  344.                                 'mountpoint' : mountpoint,
  345.                                 'udi' : udi,
  346.                                 'size' : dev.GetProperty('volume.size'),
  347.                                 'filename' : '',
  348.                             }
  349.                             self.frontend.add_source(udi)
  350.         # USB disks
  351.         devices = self.hal.FindDeviceByCapability('storage')
  352.         for device in devices:
  353.             dev_obj = self.bus.get_object('org.freedesktop.Hal', device)
  354.             d = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
  355.             # Only check physical storage devices (not partitions)
  356.             if not self.IsStorageDevice(d):
  357.                 continue
  358.             # Check if this device has a filesystem but no partitions.
  359.             if d.GetProperty('block.is_volume'):
  360.                 if d.GetProperty('volume.fstype') == 'vfat':
  361.                     self.add_device(d) 
  362.             else:
  363.                 # If not, find all of this device's child partitions
  364.                 children = self.hal.FindDeviceStringMatch('info.parent', device)
  365.                 c = False
  366.                 for child in children:
  367.                     dev_obj = self.bus.get_object('org.freedesktop.Hal', child)
  368.                     child = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
  369.                     if child.GetProperty('block.is_volume'):
  370.                         if child.GetProperty('volume.fstype') == 'vfat':
  371.                             c = True
  372.                             self.add_device(child)
  373.                 # If no partitions were found, check for an empty partition table.
  374.                 if not c and d.PropertyExists('storage.removable.media_size'):
  375.                     udi = d.GetProperty('info.udi')
  376.                     device = str(d.GetProperty('block.device'))
  377.                     self.devices[device] = {
  378.                         'label' : '',
  379.                         'fstype' : '',
  380.                         'uuid' : '',
  381.                         'mountpoint' : '',
  382.                         'udi' : udi,
  383.                         'free' : d.GetProperty('storage.removable.media_size'),
  384.                         'capacity' : d.GetProperty('storage.removable.media_size'),
  385.                         'device' : device
  386.                     }
  387.                     self.frontend.add_dest(device)
  388.  
  389.         self.bus.add_signal_receiver(self.device_added,
  390.             "DeviceAdded",
  391.             "org.freedesktop.Hal.Manager",
  392.             "org.freedesktop.Hal",
  393.             "/org/freedesktop/Hal/Manager")
  394.         self.bus.add_signal_receiver(self.device_removed,
  395.             "DeviceRemoved",
  396.             "org.freedesktop.Hal.Manager",
  397.             "org.freedesktop.Hal",
  398.             "/org/freedesktop/Hal/Manager")
  399.  
  400.     def mount_iso(self, filename):
  401.         # HAL doesn't support loop mounted filesystems and indeed when manually
  402.         # mounted, the device does not appear in d-feet (searching the
  403.         # mountpoint).  So we have to just manually construct the addition to
  404.         # self.devices.
  405.         self.log('mounting %s' % filename)
  406.         tmpdir = tempfile.mkdtemp()
  407.         res = popen(['mount', '-t', 'iso9660', '-o', 'loop,ro', filename, tmpdir])
  408.         if res[0].returncode:
  409.             self.log('unable to mount %s to %s' % (filename, tmpdir))
  410.             self.log(res[1])
  411.             self.frontend.notify(_('Unable to mount the image %s.\n\n'
  412.                 'Please see ~/.usb-creator.log for more details') % filename)
  413.             return
  414.         fp = None
  415.         try:
  416.             fp = open('%s/.disk/info' % tmpdir)
  417.             line = fp.readline().strip('\0')
  418.             line = ' '.join(line.split(' ')[:2])
  419.             size = os.stat(filename).st_size
  420.             self.cds[filename] = { 'label' : line,
  421.                                    'uuid' : '',
  422.                                    'udi' : '',
  423.                                    'mountpoint' : '',
  424.                                    'filename' : filename,
  425.                                    'size' : size }
  426.             self.frontend.add_source(filename)
  427.         except Exception, e:
  428.             self.log('Unable to find %s/.disk/info, not using %s' %
  429.                 (tmpdir, filename))
  430.             self.log(str(e))
  431.             self.frontend.notify(_('This is not a desktop install CD and '
  432.                 'thus cannot be used by this application.'))
  433.             return
  434.         finally:
  435.             if fp:
  436.                 fp.close()
  437.             popen(['umount', tmpdir])
  438.             popen(['rmdir', tmpdir])
  439.  
  440.     def install_bootloader(self):
  441.         # TODO: Needs to be moved into the generic install routine.
  442.         
  443.         # Ugly, i18n.
  444.         self.frontend.progress(0, _('Installing the bootloader'))
  445.         import re
  446.         device = self.install_target['device']
  447.         udi = self.install_target['udi']
  448.         dev_obj = self.bus.get_object("org.freedesktop.Hal", udi)
  449.         dev = dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
  450.         dev_obj = self.bus.get_object("org.freedesktop.Hal", dev.GetProperty('info.parent'))
  451.         dev = dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
  452.         rootdev = dev.GetProperty('block.device')
  453.         tmp = device.lstrip(rootdev)
  454.         match = re.match('.*([0-9]+)$', tmp)
  455.         num = None
  456.         if match:
  457.             num = match.groups()[0]
  458.         self.log('Marking partition %s as active.' % num)
  459.         if not num:
  460.             self.frontend.failed(_('Unable to determine the partition number.'))
  461.  
  462.         # Install the bootloader to the MBR.
  463.         self.log('installing the bootloader to %s.' % rootdev)
  464.         process = popen(['dd', 'if=/usr/lib/syslinux/mbr.bin',
  465.             'of=%s' % rootdev, 'bs=446', 'count=1', 'conv=sync'])
  466.         bootloader_failed = _('Error installing the bootloader.')
  467.         if process[0].returncode:
  468.             self.frontend.failed(bootloader_failed)
  469.         self.log('installing the bootloader to %s.' % device)
  470.         args = ['syslinux']
  471.         if 'USBCREATOR_SAFE' in os.environ:
  472.             args.append('-s')
  473.         args.append(device)
  474.         process = popen(args)[0]
  475.         if process.returncode:
  476.             self.frontend.failed(bootloader_failed)
  477.         popen(['parted', '-s', rootdev, 'set', num, 'boot', 'on'])
  478.  
  479.     def copy_files(self):
  480.         tmpdir = ''
  481.         def _copy_files(source, target, persist):
  482.             cmd = ['/usr/share/usb-creator/install.py', '-s', '%s/.' % source,
  483.                 '-t', '%s' % target, '-p', '%d' % persist]
  484.             self.log(cmd)
  485.             self.pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
  486.                 stderr=sys.stderr, universal_newlines=True)
  487.             self.watch = gobject.io_add_watch(self.pipe.stdout,
  488.                      gobject.IO_IN | gobject.IO_HUP,
  489.                      self.data_available)
  490.             # Wait for the process to complete
  491.             gobject.child_watch_add(self.pipe.pid, self.on_end)
  492.  
  493.         source = self.cds[self.install_source]
  494.         if not source['udi']:
  495.             tmpdir = tempfile.mkdtemp()
  496.             popen(['mount', '-o', 'loop,ro', self.install_source, tmpdir])
  497.             source['mountpoint'] = tmpdir
  498.         elif not source['mountpoint']:
  499.             tmpdir = tempfile.mkdtemp()
  500.             popen(['mount', '-o', 'ro', self.install_source, tmpdir])
  501.             source['mountpoint'] = tmpdir
  502.         if not self.install_target['mountpoint']:
  503.             tmpdir = tempfile.mkdtemp()
  504.             popen(['mount', self.install_target['device'], tmpdir])
  505.             self.install_target['mountpoint'] = tmpdir
  506.  
  507.         # We don't care about updating the main window anymore.
  508.         for timeout in self.timeouts.itervalues():
  509.             gobject.source_remove(timeout)
  510.         self.timeouts = {}
  511.         self.bus.remove_signal_receiver(self.property_modified)
  512.         # Remove files we're going to copy.
  513.         t = self.install_target['mountpoint']
  514.         for obj in os.listdir(self.cds[self.install_source]['mountpoint']):
  515.             obj = os.path.join(t, obj)
  516.             popen(['rm', '-rf', obj])
  517.         popen(['rm', os.path.join(t, 'casper-rw')])
  518.         self.original_size = free_space(self.install_target['mountpoint'])
  519.         _copy_files(source['mountpoint'], self.install_target['mountpoint'],
  520.             self.persistence_size)
  521.  
  522.     def shutdown(self):
  523.         self.log('Forcing shutdown of the install process.')
  524.         import signal
  525.         try:
  526.             if self.pipe:
  527.                 os.kill(self.pipe.pid, signal.SIGTERM)
  528.         except OSError:
  529.             pass
  530.         source = self.cds[self.install_source]
  531.         if source['mountpoint'].startswith('/tmp'):
  532.             self.log('Unmounting source volume.')
  533.             popen(['umount', source['mountpoint']])
  534.             popen(['rmdir', source['mountpoint']])
  535.         if self.install_target['mountpoint'].startswith('/tmp'):
  536.             self.log('Unmounting target volume.')
  537.             popen(['umount', self.install_target['mountpoint']])
  538.             popen(['rmdir', self.install_target['mountpoint']])
  539.  
  540.     def quit(self):
  541.         for dev in self.devices:
  542.             mp = self.devices[dev]['mountpoint']
  543.             if mp and mp.startswith('/tmp'):
  544.                 popen(['umount', mp])
  545.                 popen(['rmdir', mp])
  546.  
  547.     def on_end(self, pid, error_code):
  548.         source = self.cds[self.install_source]
  549.         if source['mountpoint'].startswith('/tmp'):
  550.             self.log('Unmounting source volume.')
  551.             popen(['umount', source['mountpoint']])
  552.             popen(['rmdir', source['mountpoint']])
  553.         if self.install_target['mountpoint'].startswith('/tmp'):
  554.             self.log('Unmounting target volume.')
  555.             popen(['umount', self.install_target['mountpoint']])
  556.             popen(['rmdir', self.install_target['mountpoint']])
  557.         self.log('Install command exited with code: %d' % error_code)
  558.         if error_code != 0:
  559.             self.frontend.failed()
  560.         # TODO: Needed?
  561.         #gobject.source_remove(self.watch)
  562.         self.frontend.finished()
  563.  
  564.     def copy_progress(self):
  565.         now = free_space(self.install_target['mountpoint'])
  566.         source = float(self.cds[self.install_source]['size'])
  567.         source = source + self.persistence_size
  568.         per = ((self.original_size - now) / source) * 100
  569.         self.frontend.progress(per, self.progress_description)
  570.         return True
  571.  
  572.     def data_available(self, source, condition):
  573.         text = source.readline()
  574.         if len(text) > 0:
  575.             self.progress_description = text.strip('\n')
  576.             if not self.copy_timeout:
  577.                 self.copy_timeout = gobject.timeout_add(2000, self.copy_progress)
  578.             return True
  579.         else:
  580.             if self.copy_timeout:
  581.                 gobject.source_remove(self.copy_timeout)
  582.             return False
  583.  
  584.     def IsStorageDevice(self, d):
  585.         # We only care about usb devices currently
  586.         if d.GetProperty('storage.bus') not in ['usb', 'mmc']:
  587.             return False
  588.         
  589.         # Card readers are not removable, so nix this test.
  590.         #if d.GetProperty('storage.removable') == False:
  591.         #    return False
  592.  
  593.         # Known good drive types
  594.         drive_types = ['disk',
  595.                        'sd_mmc',
  596.                        'smart_media',
  597.                        'memory_stick',
  598.                        'compact_flash']
  599.         if d.GetProperty('storage.drive_type') in drive_types:
  600.             return True
  601.  
  602.