Package liveusb :: Module creator
[hide private]
[frames] | no frames]

Source Code for Module liveusb.creator

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright © 2008  Red Hat, Inc. All rights reserved. 
  4  # 
  5  # This copyrighted material is made available to anyone wishing to use, modify, 
  6  # copy, or redistribute it subject to the terms and conditions of the GNU 
  7  # General Public License v.2.  This program is distributed in the hope that it 
  8  # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the 
  9  # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 10  # See the GNU General Public License for more details.  You should have 
 11  # received a copy of the GNU General Public License along with this program; if 
 12  # not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 
 13  # Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are 
 14  # incorporated in the source code or documentation are not subject to the GNU 
 15  # General Public License and may only be used or replicated with the express 
 16  # permission of Red Hat, Inc. 
 17  # 
 18  # Author(s): Luke Macken <lmacken@redhat.com> 
 19   
 20  """ 
 21  Our main LiveUSBCreator module. 
 22   
 23  This contains the LiveUSBCreator parent class, which is an abstract interface 
 24  that provides platform-independent methods.  Platform specific implementations 
 25  include the LinuxLiveUSBCreator and the WindowsLiveUSBCreator. 
 26  """ 
 27   
 28  import subprocess 
 29  import tempfile 
 30  import logging 
 31  import shutil 
 32  import sha 
 33  import os 
 34  import re 
 35   
 36  from StringIO import StringIO 
 37  from datetime import datetime 
 38  from stat import ST_SIZE 
 39   
 40  from liveusb.releases import releases 
 41  from liveusb import _ 
 42   
 43   
44 -class LiveUSBError(Exception):
45 """ A generic error message that is thrown by the LiveUSBCreator """
46 47
48 -class LiveUSBCreator(object):
49 """ An OS-independent parent class for Live USB Creators """ 50 51 iso = None # the path to our live image 52 label = "FEDORA" # if one doesn't already exist 53 fstype = None # the format of our usb stick 54 drives = {} # {device: {'label': label, 'mount': mountpoint}} 55 overlay = 0 # size in mb of our persisten overlay 56 dest = None # the mount point of of our selected drive 57 uuid = None # the uuid of our selected drive 58 pids = [] # a list of pids of all of our subprocesses 59 output = StringIO() # log subprocess output in case of errors 60 totalsize = 0 # the total size of our overlay + iso 61 isosize = 0 # the size of the selected iso 62 _drive = None # mountpoint of the currently selected drive 63 mb_per_sec = 0 # how many megabytes per second we can write 64 log = None 65 66 drive = property(fget=lambda self: self.drives[self._drive], 67 fset=lambda self, d: self._set_drive(d)) 68
69 - def __init__(self, opts):
70 self.opts = opts 71 self._setup_logger()
72
73 - def _setup_logger(self):
74 self.log = logging.getLogger(__name__) 75 level = logging.INFO 76 if self.opts.verbose: 77 level = logging.DEBUG 78 self.log.setLevel(level) 79 handler = logging.StreamHandler() 80 handler.setLevel(level) 81 formatter = logging.Formatter("[%(module)s:%(lineno)s] %(message)s") 82 handler.setFormatter(formatter) 83 self.log.addHandler(handler)
84
85 - def detect_removable_drives(self):
86 """ This method should populate self.drives with removable devices """ 87 raise NotImplementedError
88
89 - def verify_filesystem(self):
90 """ 91 Verify the filesystem of our device, setting the volume label 92 if necessary. If something is not right, this method throws a 93 LiveUSBError. 94 """ 95 raise NotImplementedError
96
97 - def get_free_bytes(self, drive=None):
98 """ Return the number of free bytes on a given drive. 99 100 If drive is None, then use the currently selected device. 101 """ 102 raise NotImplementedError
103
104 - def extract_iso(self):
105 """ Extract the LiveCD ISO to the USB drive """ 106 raise NotImplementedError
107
108 - def verify_iso_md5(self):
109 """ Verify the MD5 checksum of the ISO """ 110 raise NotImplementedError
111
112 - def install_bootloader(self):
113 """ Install the bootloader to our device. 114 115 Platform-specific classes inheriting from the LiveUSBCreator are 116 expected to implement this method to install the bootloader to the 117 specified device using syslinux. This specific implemention is 118 platform independent and performs sanity checking along with adding 119 OLPC support. 120 """ 121 if not os.path.exists(os.path.join(self.dest, 'isolinux')): 122 raise LiveUSBError('extract_iso must be run before ' 123 'install_bootloader') 124 if self.opts.xo: 125 self._setup_olpc()
126
127 - def _setup_olpc(self):
128 """ Install the Open Firmware configuration for the OLPC. 129 130 This method will make the selected device bootable on the OLPC. It 131 does this by installing a /boot/olpc.fth open firmware configuration 132 file that enables booting off of USB and SD cards on the XO. 133 """ 134 self.log.info(_('Setting up OLPC boot file...')) 135 args = [] 136 137 # Grab the kernel arguments from our syslinux configuration 138 cfg = file(os.path.join(self.dest, 'isolinux', 'syslinux.cfg')) 139 for line in cfg.readlines(): 140 if 'append' in line: 141 args.extend([arg for arg in line.split()[1:] 142 if not arg.startswith('initrd')]) 143 break 144 cfg.close() 145 146 from liveusb.olpc import ofw_config 147 if not os.path.exists(os.path.join(self.dest, 'boot')): 148 os.mkdir(os.path.join(self.dest, 'boot')) 149 olpc_cfg = file(os.path.join(self.dest, 'boot', 'olpc.fth'), 'w') 150 olpc_cfg.write(ofw_config % ' '.join(args)) 151 olpc_cfg.close() 152 self.log.debug('Wrote %s' % olpc_cfg.name)
153
154 - def terminate(self):
155 """ Terminate any subprocesses that we have spawned """ 156 raise NotImplementedError
157
158 - def mount_device(self):
159 """ Mount self.drive, setting the mount point to self.mount """ 160 raise NotImplementedError
161
162 - def unmount_device(self):
163 """ Unmount the device mounted at self.mount """ 164 raise NotImplementedError
165
166 - def popen(self, cmd, **kwargs):
167 """ A wrapper method for running subprocesses. 168 169 This method handles logging of the command and it's output, and keeps 170 track of the pids in case we need to kill them. If something goes 171 wrong, an error log is written out and a LiveUSBError is thrown. 172 173 @param cmd: The commandline to execute. Either a string or a list. 174 @param kwargs: Extra arguments to pass to subprocess.Popen 175 """ 176 self.log.debug(cmd) 177 self.output.write(cmd) 178 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 179 stderr=subprocess.PIPE, stdin=subprocess.PIPE, 180 shell=True, **kwargs) 181 self.pids.append(proc.pid) 182 out, err = proc.communicate() 183 self.output.write(out + '\n' + err + '\n') 184 if proc.returncode: 185 self.write_log() 186 raise LiveUSBError(_("There was a problem executing the following " 187 "command: `%s`\nA more detailed error log has " 188 "been written to 'liveusb-creator.log'" % cmd)) 189 return proc
190
191 - def verify_iso_sha1(self, progress=None):
192 """ Verify the SHA1 checksum of our ISO if it is in our release list """ 193 self.log.info(_("Verifying SHA1 of LiveCD image...")) 194 if not progress: 195 class DummyProgress: 196 def set_max_progress(self, value): pass 197 def update_progress(self, value): pass
198 progress = DummyProgress() 199 release = self.get_release_from_iso() 200 if release: 201 progress.set_max_progress(self.isosize / 1024) 202 checksum = sha.new() 203 isofile = file(self.iso, 'rb') 204 bytes = 1024**2 205 total = 0 206 while bytes: 207 data = isofile.read(bytes) 208 checksum.update(data) 209 bytes = len(data) 210 total += bytes 211 progress.update_progress(total / 1024) 212 if checksum.hexdigest() == release['sha1']: 213 return True 214 else: 215 self.log.info(_("Error: The SHA1 of your Live CD is " 216 "invalid. You can run this program with " 217 "the --noverify argument to bypass this " 218 "verification check.")) 219 return False 220 else: 221 self.log.debug(_('Unknown ISO, skipping checksum verification'))
222
223 - def check_free_space(self):
224 """ Make sure there is enough space for the LiveOS and overlay """ 225 freebytes = self.get_free_bytes() 226 self.log.debug('freebytes = %d' % freebytes) 227 self.isosize = os.stat(self.iso)[ST_SIZE] 228 self.log.debug('isosize = %d' % self.isosize) 229 overlaysize = self.overlay * 1024**2 230 self.log.debug('overlaysize = %d' % overlaysize) 231 self.totalsize = overlaysize + self.isosize 232 if self.totalsize > freebytes: 233 raise LiveUSBError(_("Not enough free space on device." + 234 "\n%dMB ISO + %dMB overlay > %dMB free space" % 235 (self.isosize/1024**2, self.overlay, 236 freebytes/1024**2)))
237
238 - def create_persistent_overlay(self):
239 if self.overlay: 240 self.log.info(_("Creating") + " %sMB " % self.overlay + 241 _("persistent overlay")) 242 if self.fstype == 'vfat': 243 # vfat apparently can't handle sparse files 244 self.popen('dd if=/dev/zero of=%s count=%d bs=1M' 245 % (self.get_overlay(), self.overlay)) 246 else: 247 self.popen('dd if=/dev/zero of=%s count=1 bs=1M seek=%d' 248 % (self.get_overlay(), self.overlay))
249
250 - def update_configs(self):
251 """ Generate our syslinux.cfg """ 252 isolinux = file(os.path.join(self.dest, "isolinux", "isolinux.cfg"),'r') 253 syslinux = file(os.path.join(self.dest, "isolinux", "syslinux.cfg"),'w') 254 usblabel = self.uuid and 'UUID=' + self.uuid or 'LABEL=' + self.label 255 for line in isolinux.readlines(): 256 if "CDLABEL" in line: 257 line = re.sub("CDLABEL=[^ ]*", usblabel, line) 258 line = re.sub("rootfstype=[^ ]*", 259 "rootfstype=%s" % self.fstype, 260 line) 261 if self.overlay and "liveimg" in line: 262 line = line.replace("liveimg", "liveimg overlay=" + usblabel) 263 line = line.replace(" ro ", " rw ") 264 if self.opts.kernel_args: 265 line = line.replace("liveimg", "liveimg %s" % 266 ' '.join(self.opts.kernel_args.split(','))) 267 syslinux.write(line) 268 isolinux.close() 269 syslinux.close()
270
271 - def delete_liveos(self):
272 """ Delete the existing LiveOS """ 273 self.log.info(_('Removing existing Live OS')) 274 for path in [self.get_liveos(), 275 os.path.join(self.dest + os.path.sep, 'syslinux'), 276 os.path.join(self.dest + os.path.sep, 'isolinux')]: 277 if os.path.exists(path): 278 self.log.debug("Deleting " + path) 279 try: 280 shutil.rmtree(path) 281 except OSError, e: 282 raise LiveUSBError(_("Unable to remove previous LiveOS: " 283 "%s" % str(e)))
284
285 - def write_log(self):
286 """ Write out our subprocess stdout/stderr to a log file """ 287 out = file('liveusb-creator.log', 'a') 288 out.write(self.output.getvalue()) 289 out.close()
290
291 - def existing_liveos(self):
292 return os.path.exists(self.get_liveos())
293
294 - def get_liveos(self):
295 return os.path.join(self.dest + os.path.sep, "LiveOS")
296
297 - def existing_overlay(self):
298 return os.path.exists(self.get_overlay())
299
300 - def get_overlay(self):
301 return os.path.join(self.get_liveos(), 302 'overlay-%s-%s' % (self.label, self.uuid or ''))
303
304 - def get_release_from_iso(self):
305 """ If the ISO is for a known release, return it. """ 306 isoname = os.path.basename(self.iso) 307 for release in releases: 308 if os.path.basename(release['url']) == isoname: 309 return release
310
311 - def _set_drive(self, drive):
312 if not self.drives.has_key(drive): 313 raise LiveUSBError(_("Cannot find device %s" % drive)) 314 self.log.debug("%s selected: %s" % (drive, self.drives[drive])) 315 self._drive = drive 316 self.uuid = self.drives[drive]['uuid'] 317 self.fstype = self.drives[drive]['fstype']
318
319 - def get_proxies(self):
320 """ Return a dictionary of proxy settings """ 321 return None
322 323
324 -class LinuxLiveUSBCreator(LiveUSBCreator):
325 326 bus = None # the dbus.SystemBus 327 hal = None # an org.freedesktop.Hal.Manager dbus.Interface 328
329 - def detect_removable_drives(self):
330 """ Detect all removable USB storage devices using HAL via D-Bus """ 331 import dbus 332 self.drives = {} 333 self.bus = dbus.SystemBus() 334 hal_obj = self.bus.get_object("org.freedesktop.Hal", 335 "/org/freedesktop/Hal/Manager") 336 self.hal = dbus.Interface(hal_obj, "org.freedesktop.Hal.Manager") 337 338 devices = [] 339 if self.opts.force: 340 devices = self.hal.FindDeviceStringMatch('block.device', 341 self.opts.force) 342 else: 343 devices = self.hal.FindDeviceByCapability("storage") 344 345 for device in devices: 346 dev = self._get_device(device) 347 if self.opts.force or dev.GetProperty("storage.bus") == "usb" and \ 348 dev.GetProperty("storage.removable"): 349 if dev.GetProperty("block.is_volume"): 350 self._add_device(dev) 351 continue 352 else: # iterate over children looking for a volume 353 children = self.hal.FindDeviceStringMatch("info.parent", 354 device) 355 for child in children: 356 child = self._get_device(child) 357 if child.GetProperty("block.is_volume"): 358 self._add_device(child, parent=dev) 359 break 360 361 if not len(self.drives): 362 raise LiveUSBError(_("Unable to find any USB drives"))
363
364 - def _add_device(self, dev, parent=None):
365 mount = str(dev.GetProperty('volume.mount_point')) 366 device = str(dev.GetProperty('block.device')) 367 self.drives[device] = { 368 'label' : str(dev.GetProperty('volume.label')).replace(' ', '_'), 369 'fstype' : str(dev.GetProperty('volume.fstype')), 370 'uuid' : str(dev.GetProperty('volume.uuid')), 371 'mount' : mount, 372 'udi' : dev, 373 'unmount' : False, 374 'free' : mount and self.get_free_bytes(mount) / 1024**2 or None, 375 'device' : device, 376 'parent' : parent.GetProperty('block.device') 377 }
378
379 - def mount_device(self):
380 """ Mount our device with HAL if it is not already mounted """ 381 import dbus 382 self.dest = self.drive['mount'] 383 if not self.dest: 384 if not self.fstype: 385 raise LiveUSBError(_("Filesystem for %s unknown!" % 386 self.drive['device'])) 387 try: 388 self.log.debug("Calling %s.Mount('', %s, [], ...)" % ( 389 self.drive['udi'], self.fstype)) 390 self.drive['udi'].Mount('', self.fstype, [], 391 dbus_interface='org.freedesktop.Hal.Device.Volume') 392 self.drive['unmount'] = True 393 except dbus.exceptions.DBusException, e: 394 if e.get_dbus_name() == \ 395 'org.freedesktop.Hal.Device.Volume.AlreadyMounted': 396 self.log.debug('Device already mounted') 397 except Exception, e: 398 raise LiveUSBError(_("Unable to mount device: %s" % str(e))) 399 device = self.hal.FindDeviceStringMatch('block.device', 400 self.drive['device']) 401 device = self._get_device(device[0]) 402 self.dest = device.GetProperty('volume.mount_point') 403 self.log.debug("Mounted %s to %s " % (self.drive['device'], 404 self.dest)) 405 self.drive['mount'] = self.dest 406 else: 407 self.log.debug("Using existing mount: %s" % self.dest)
408
409 - def unmount_device(self):
410 """ Unmount our device if we mounted it to begin with """ 411 import dbus 412 try: 413 unmount = self.drive.get('unmount') 414 except KeyError: # the device has been removed 415 return 416 if self.dest and unmount: 417 self.log.debug("Unmounting %s from %s" % (self.drive['device'], 418 self.dest)) 419 try: 420 self.drive['udi'].Unmount([], 421 dbus_interface='org.freedesktop.Hal.Device.Volume') 422 except dbus.exceptions.DBusException, e: 423 raise 424 self.log.warning("Unable to unmount device: %s" % str(e)) 425 return 426 self.drive['unmount'] = False 427 self.drive['mount'] = None 428 if os.path.exists(self.dest): 429 self.log.error("Mount %s exists after unmounting" % self.dest) 430 #shutil.rmtree(self.dest) too agressive? 431 self.dest = None
432
433 - def verify_filesystem(self):
434 self.log.info(_("Verifying filesystem...")) 435 if self.fstype not in ('vfat', 'msdos', 'ext2', 'ext3'): 436 if not self.fstype: 437 raise LiveUSBError(_("Unknown filesystem for %s. Your device " 438 "may need to be reformatted.")) 439 else: 440 raise LiveUSBError(_("Unsupported filesystem: %s" % 441 self.fstype)) 442 if self.drive['label']: 443 self.label = self.drive['label'] 444 else: 445 self.log.info("Setting %s label to %s" % (self.drive['device'], 446 self.label)) 447 try: 448 if self.fstype in ('vfat', 'msdos'): 449 try: 450 self.popen('/sbin/dosfslabel %s %s' % ( 451 self.drive['device'], self.label)) 452 except LiveUSBError: 453 # dosfslabel returns an error code even upon success 454 pass 455 else: 456 self.popen('/sbin/e2label %s %s' % (self.drive['device'], 457 self.label)) 458 except LiveUSBError, e: 459 self.log.error("Unable to change volume label: %s" % str(e)) 460 self.label = None
461
462 - def extract_iso(self):
463 """ Extract self.iso to self.dest """ 464 self.log.info(_("Extracting live image to USB device...")) 465 tmpdir = tempfile.mkdtemp() 466 self.popen('mount -o loop,ro "%s" %s' % (self.iso, tmpdir)) 467 tmpliveos = os.path.join(tmpdir, 'LiveOS') 468 try: 469 if not os.path.isdir(tmpliveos): 470 raise LiveUSBError(_("Unable to find LiveOS on ISO")) 471 liveos = os.path.join(self.dest, 'LiveOS') 472 if not os.path.exists(liveos): 473 os.mkdir(liveos) 474 for img in ('squashfs.img', 'osmin.img'): 475 start = datetime.now() 476 self.popen("cp %s '%s'" % (os.path.join(tmpliveos, img), 477 os.path.join(liveos, img))) 478 delta = datetime.now() - start 479 if delta.seconds: 480 self.mb_per_sec = (self.isosize / delta.seconds) / 1024**2 481 self.log.info(_("Wrote to device at") + " %d MB/sec" % 482 self.mb_per_sec) 483 isolinux = os.path.join(self.dest, 'isolinux') 484 if not os.path.exists(isolinux): 485 os.mkdir(isolinux) 486 self.popen("cp %s/* '%s'" % (os.path.join(tmpdir, 'isolinux'), 487 isolinux)) 488 finally: 489 self.popen('umount %s' % tmpdir)
490
491 - def install_bootloader(self):
492 """ Run syslinux to install the bootloader on our devices """ 493 LiveUSBCreator.install_bootloader(self) 494 self.log.info(_("Installing bootloader...")) 495 shutil.move(os.path.join(self.dest, "isolinux"), 496 os.path.join(self.dest, "syslinux")) 497 os.unlink(os.path.join(self.dest, "syslinux", "isolinux.cfg")) 498 self.popen('syslinux%s%s -d %s %s' % (self.opts.force and ' -f' or ' ', 499 self.opts.safe and ' -s' or ' ', 500 os.path.join(self.dest, 'syslinux'), self.drive['device']))
501
502 - def get_free_bytes(self, device=None):
503 """ Return the number of available bytes on our device """ 504 import statvfs 505 device = device and device or self.dest 506 stat = os.statvfs(device) 507 return stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL]
508
509 - def _get_device(self, udi):
510 """ Return a dbus Interface to a specific HAL device UDI """ 511 import dbus 512 dev_obj = self.bus.get_object("org.freedesktop.Hal", udi) 513 return dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
514
515 - def terminate(self):
516 import signal 517 self.log.info("Cleaning up...") 518 for pid in self.pids: 519 try: 520 os.kill(pid, signal.SIGHUP) 521 self.log.debug("Killed process %d" % pid) 522 except OSError: 523 pass 524 self.unmount_device()
525
526 - def verify_iso_md5(self):
527 """ Verify the ISO md5sum. 528 529 At the moment this is Linux specific, until we port checkisomd5 530 to Windows. 531 """ 532 self.log.info(_('Verifying ISO MD5 checksum')) 533 try: 534 self.popen('checkisomd5 "%s"' % self.iso) 535 except LiveUSBError: 536 return False 537 return True
538
539 - def get_proxies(self):
540 """ Return the proxy settings. 541 542 At the moment this implementation only works on KDE, and should 543 eventually be expanded to support other platforms as well. 544 """ 545 try: 546 from PyQt4 import QtCore 547 except ImportError: 548 self.log.warning("PyQt4 module not installed; skipping KDE " 549 "proxy detection") 550 return 551 kioslaverc = QtCore.QDir.homePath() + '/.kde/share/config/kioslaverc' 552 if not QtCore.QFile.exists(kioslaverc): 553 return {} 554 settings = QtCore.QSettings(kioslaverc, QtCore.QSettings.IniFormat) 555 settings.beginGroup('Proxy Settings') 556 proxies = {} 557 # check for KProtocolManager::ManualProxy (the only one we support) 558 if settings.value('ProxyType').toInt()[0] == 1: 559 httpProxy = settings.value('httpProxy').toString() 560 if httpProxy != '': 561 proxies['http'] = httpProxy 562 ftpProxy = settings.value('ftpProxy').toString() 563 if ftpProxy != '': 564 proxies['ftp'] = ftpProxy 565 return proxies
566 567
568 -class WindowsLiveUSBCreator(LiveUSBCreator):
569
570 - def detect_removable_drives(self):
571 import win32file, win32api 572 self.drives = {} 573 for drive in [l + ':' for l in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']: 574 if win32file.GetDriveType(drive) == win32file.DRIVE_REMOVABLE or \ 575 drive == self.opts.force: 576 try: 577 vol = win32api.GetVolumeInformation(drive) 578 label = vol[0] 579 except: 580 label = None 581 self.drives[drive] = { 582 'label': label, 583 'mount': drive, 584 'uuid': self._get_device_uuid(drive), 585 'free': self.get_free_bytes(drive) / 1024**2, 586 'fstype': 'vfat', 587 'device': drive, 588 } 589 if not len(self.drives): 590 raise LiveUSBError(_("Unable to find any removable devices"))
591
592 - def verify_filesystem(self):
593 import win32api, win32file, pywintypes 594 self.log.info(_("Verifying filesystem...")) 595 try: 596 vol = win32api.GetVolumeInformation(self.drive['device']) 597 except Exception, e: 598 raise LiveUSBError(_("Make sure your USB key is plugged in and " 599 "formatted with the FAT filesystem")) 600 if vol[-1] not in ('FAT32', 'FAT'): 601 raise LiveUSBError(_("Unsupported filesystem: %s\nPlease backup " 602 "and format your USB key with the FAT " 603 "filesystem." % vol[-1])) 604 self.fstype = 'vfat' 605 if vol[0] == '': 606 try: 607 win32file.SetVolumeLabel(self.drive['device'], self.label) 608 self.log.debug("Set %s label to %s" % (self.drive['device'], 609 self.label)) 610 except pywintypes.error, e: 611 self.log.warning("Unable to SetVolumeLabel: " + str(e)) 612 self.label = None 613 else: 614 self.label = vol[0].replace(' ', '_')
615
616 - def get_free_bytes(self, device=None):
617 """ Return the number of free bytes on our selected drive """ 618 import win32file 619 device = device and device or self.drive['device'] 620 try: 621 (spc, bps, fc, tc) = win32file.GetDiskFreeSpace(device) 622 except Exception, e: 623 self.log.error("Problem determining free space: %s" % str(e)) 624 return 0 625 return fc * (spc * bps) # free-clusters * bytes per-cluster
626
627 - def extract_iso(self):
628 """ Extract our ISO with 7-zip directly to the USB key """ 629 self.log.info(_("Extracting live image to USB device...")) 630 start = datetime.now() 631 self.popen('7z x "%s" -x![BOOT] -y -o%s' % ( 632 self.iso, self.drive['device'])) 633 delta = datetime.now() - start 634 if delta.seconds: 635 self.mb_per_sec = (self.isosize / delta.seconds) / 1024**2 636 self.log.info(_("Wrote to device at") + " %d MB/sec" % 637 self.mb_per_sec)
638
639 - def install_bootloader(self):
640 """ Run syslinux to install the bootloader on our device """ 641 LiveUSBCreator.install_bootloader(self) 642 self.log.info(_("Installing bootloader")) 643 device = self.drive['device'] 644 if os.path.isdir(os.path.join(device + os.path.sep, "syslinux")): 645 syslinuxdir = os.path.join(device + os.path.sep, "syslinux") 646 # Python for Windows is unable to delete read-only files, and some 647 # may exist here if the LiveUSB stick was created in Linux 648 for f in os.listdir(syslinuxdir): 649 os.chmod(os.path.join(syslinuxdir, f), 0777) 650 shutil.rmtree(syslinuxdir) 651 shutil.move(os.path.join(device + os.path.sep, "isolinux"), 652 os.path.join(device + os.path.sep, "syslinux")) 653 os.unlink(os.path.join(device + os.path.sep, "syslinux", 654 "isolinux.cfg")) 655 self.popen('syslinux%s%s -m -a -d %s %s' % (self.opts.force and ' -f' 656 or ' ', self.opts.safe and ' -s' or ' ', 657 os.path.join(device + os.path.sep, 'syslinux'), device))
658
659 - def _get_device_uuid(self, drive):
660 """ Return the UUID of our selected drive """ 661 uuid = None 662 try: 663 import win32com.client 664 uuid = win32com.client.Dispatch("WbemScripting.SWbemLocator") \ 665 .ConnectServer(".", "root\cimv2") \ 666 .ExecQuery("Select VolumeSerialNumber from " 667 "Win32_LogicalDisk where Name = '%s'" % 668 drive)[0].VolumeSerialNumber 669 if uuid == 'None': 670 uuid = None 671 else: 672 uuid = uuid[:4] + '-' + uuid[4:] 673 self.log.debug("Found UUID %s for %s" % (uuid, drive)) 674 except Exception, e: 675 self.log.warning("Exception while fetching UUID: %s" % str(e)) 676 return uuid
677
678 - def popen(self, cmd, **kwargs):
679 import win32process 680 if isinstance(cmd, basestring): 681 cmd = cmd.split() 682 tool = os.path.join('tools', '%s.exe' % cmd[0]) 683 if not os.path.exists(tool): 684 raise LiveUSBError(_("Cannot find") + ' %s. ' % (cmd[0]) + 685 _("Make sure to extract the entire " 686 "liveusb-creator zip file before " 687 "running this program.")) 688 return LiveUSBCreator.popen(self, ' '.join([tool] + cmd[1:]), 689 creationflags=win32process.CREATE_NO_WINDOW, 690 **kwargs)
691
692 - def terminate(self):
693 """ Terminate any subprocesses that we have spawned """ 694 import win32api, win32con, pywintypes 695 for pid in self.pids: 696 try: 697 handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 698 False, pid) 699 self.log.debug("Terminating process %s" % pid) 700 win32api.TerminateProcess(handle, -2) 701 win32api.CloseHandle(handle) 702 except pywintypes.error: 703 pass
704
705 - def mount_device(self):
706 self.dest = self.drive['mount']
707
708 - def unmount_device(self):
709 pass
710
711 - def get_proxies(self):
712 proxies = {} 713 try: 714 import _winreg as winreg 715 settings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 716 'Software\\Microsoft\\Windows' 717 '\\CurrentVersion\\Internet Settings') 718 proxy = winreg.QueryValueEx(settings, "ProxyEnable")[0] 719 if proxy: 720 server = str(winreg.QueryValueEx(settings, 'ProxyServer')[0]) 721 if ';' in server: 722 for p in server.split(';'): 723 protocol, address = p.split('=') 724 proxies[protocol] = '%s://%s' % (protocol, address) 725 else: 726 proxies['http'] = 'http://%s' % server 727 proxies['ftp'] = 'ftp://%s' % server 728 settings.Close() 729 except Exception, e: 730 self.log.warning('Unable to detect proxy settings: %s' % str(e)) 731 self.log.debug('Using proxies: %s' % proxies) 732 return proxies
733
734 - def verify_iso_md5(self):
735 """ Verify the ISO md5sum. 736 737 At the moment this is Linux-only, until we port checkisomd5 to Windows. 738 """ 739 pass
740