2 # -*- coding: utf-8 -*-
7 This script installs a grml system (either a running system or ISO[s]) to a USB device
9 :copyright: (c) 2009 by Michael Prokop <mika@grml.org>
10 :license: GPL v2 or any later version
11 :bugreports: http://grml.org/bugs/
15 from __future__ import with_statement
16 from optparse import OptionParser
17 from inspect import isroutine, isclass
18 import datetime, logging, os, re, subprocess, sys, tempfile, time
21 PROG_VERSION = "0.9.2(pre2)"
22 MOUNTED = set() # register mountpoints
23 TMPFILES = set() # register tmpfiles
24 DATESTAMP = time.mktime(datetime.datetime.now().timetuple()) # unique identifier for syslinux.cfg
27 USAGE = "Usage: %prog [options] <[ISO[s] | /live/image]> </dev/sdX#>\n\
29 %prog installs a grml ISO to an USB device to be able to boot from it.\n\
30 Make sure you have at least one grml ISO or a running grml system (/live/image),\n\
31 syslinux (just run 'aptitude install syslinux' on Debian-based systems)\n\
32 and root access. Further information can be found in: man grml2usb"
34 # pylint: disable-msg=C0103
35 parser = OptionParser(usage=USAGE)
36 parser.add_option("--bootoptions", dest="bootoptions",
37 action="store", type="string",
38 help="use specified bootoptions as default")
39 parser.add_option("--bootloader-only", dest="bootloaderonly", action="store_true",
40 help="do not copy files but just install a bootloader")
41 parser.add_option("--copy-only", dest="copyonly", action="store_true",
42 help="copy files only but do not install bootloader")
43 parser.add_option("--dry-run", dest="dryrun", action="store_true",
44 help="avoid executing commands")
45 parser.add_option("--fat16", dest="fat16", action="store_true",
46 help="format specified partition with FAT16")
47 parser.add_option("--force", dest="force", action="store_true",
48 help="force any actions requiring manual interaction")
49 parser.add_option("--grub", dest="grub", action="store_true",
50 help="install grub bootloader instead of syslinux")
51 parser.add_option("--initrd", dest="initrd", action="store", type="string",
52 help="install specified initrd instead of the default [TODO]")
53 parser.add_option("--kernel", dest="kernel", action="store", type="string",
54 help="install specified kernel instead of the default [TODO]")
55 parser.add_option("--lilo", dest="lilo", action="store", type="string",
56 help="lilo executable to be used for installing MBR")
57 parser.add_option("--mbr-manager", dest="mbrmgr", action="store_true",
58 help="enable boot manager menu in MBR")
59 parser.add_option("--quiet", dest="quiet", action="store_true",
60 help="do not output anything but just errors on console")
61 parser.add_option("--skip-addons", dest="skipaddons", action="store_true",
62 help="do not install /boot/addons/ files")
63 parser.add_option("--skip-mbr", dest="skipmbr", action="store_true",
64 help="do not install a master boot record (MBR) on the device")
65 parser.add_option("--syslinux-mbr", dest="syslinuxmbr", action="store_true",
66 help="install syslinux master boot record (MBR) instead of default")
67 parser.add_option("--squashfs", dest="squashfs", action="store", type="string",
68 help="install specified squashfs file instead of the default [TODO]")
69 parser.add_option("--uninstall", dest="uninstall", action="store_true",
70 help="remove grml ISO files from specified device [TODO]")
71 parser.add_option("--verbose", dest="verbose", action="store_true",
72 help="enable verbose mode")
73 parser.add_option("-v", "--version", dest="version", action="store_true",
74 help="display version and exit")
75 (options, args) = parser.parse_args()
78 class CriticalException(Exception):
79 """Throw critical exception if the exact error is not known but fatal."
81 @Exception: message"""
86 """Cleanup function to make sure there aren't any mounted devices left behind.
89 logging.info("Cleaning up before exiting...")
90 proc = subprocess.Popen(["sync"])
94 for device in MOUNTED:
96 # ignore: RuntimeError: Set changed size during iteration
98 logging.debug('caught expection RuntimeError, ignoring')
101 def register_tmpfile(path):
108 def unregister_tmpfile(path):
113 TMPFILES.remove(path)
116 def register_mountpoint(target):
123 def unregister_mountpoint(target):
127 if target in MOUNTED:
128 MOUNTED.remove(target)
131 def get_function_name(obj):
132 """Helper function for use in execute() to retrive name of a function
134 @obj: the function object
136 if not (isroutine(obj) or isclass(obj)):
138 return obj.__module__ + '.' + obj.__name__
141 def execute(f, *exec_arguments):
142 """Wrapper for executing a command. Either really executes
143 the command (default) or when using --dry-run commandline option
144 just displays what would be executed."""
145 # usage: execute(subprocess.Popen, (["ls", "-la"]))
146 # TODO: doesn't work for proc = execute(subprocess.Popen...() -> any ideas?
148 # pylint: disable-msg=W0141
149 logging.debug('dry-run only: %s(%s)' % (get_function_name(f), ', '.join(map(repr, exec_arguments))))
151 # pylint: disable-msg=W0142
152 return f(*exec_arguments)
156 """Check whether a given file can be executed
158 @fpath: full path to file
160 return os.path.exists(fpath) and os.access(fpath, os.X_OK)
164 """Check whether a given program is available in PATH
166 @program: name of executable"""
167 fpath = os.path.split(program)[0]
172 for path in os.environ["PATH"].split(os.pathsep):
173 exe_file = os.path.join(path, program)
180 def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin'):
181 """Given a search path, find file
183 @filename: name of file to search for
184 @search_path: path where searching for the specified filename"""
186 paths = search_path.split(os.pathsep)
187 current_dir = '' # make pylint happy :)
189 # pylint: disable-msg=W0612
190 for current_dir, directories, files in os.walk(path):
191 if os.path.exists(os.path.join(current_dir, filename)):
195 return os.path.abspath(os.path.join(current_dir, filename))
200 def check_uid_root():
201 """Check for root permissions"""
202 if not os.geteuid()==0:
203 sys.exit("Error: please run this script with uid 0 (root).")
206 def mkfs_fat16(device):
207 """Format specified device with VFAT/FAT16 filesystem.
209 @device: partition that should be formated"""
211 # syslinux -d boot/isolinux /dev/sdb1
212 logging.info("Formating partition with fat16 filesystem")
213 logging.debug("mkfs.vfat -F 16 %s" % device)
214 proc = subprocess.Popen(["mkfs.vfat", "-F", "16", device])
216 if proc.returncode != 0:
217 raise CriticalException("error executing mkfs.vfat")
220 def generate_main_grub2_config(grml_flavour, install_partition, bootoptions):
221 """Generate grub2 configuration for use via grub.cfg
225 @grml_flavour: name of grml flavour the configuration should be generated for"""
227 local_datestamp = DATESTAMP
230 ## main grub2 configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
236 if font (hd0,%(install_partition)s)/boot/grub/ascii.pff ; then
244 if background_image (hd0,%(install_partition)s)/boot/grub/grml.png ; then
245 set color_normal=black/black
246 set color_highlight=red/black
248 set menu_color_normal=white/black
249 set menu_color_highlight=black/yellow
252 menuentry "%(grml_flavour)s (default)" {
253 set root=(hd0,%(install_partition)s)
254 linux /boot/release/%(grml_flavour)s/linux26 apm=power-off lang=us vga=791 quiet boot=live nomce module=%(grml_flavour)s %(bootoptions)s
255 initrd /boot/release/%(grml_flavour)s/initrd.gz
258 menuentry "Memory test (memtest86+)" {
259 set root=(hd0,%(install_partition)s)
260 linux /boot/addons/memtest
263 menuentry "Grub - all in one image" {
264 set root=(hd0,%(install_partition)s)
265 linux /boot/addons/memdisk
266 initrd /boot/addons/allinone.img
269 menuentry "FreeDOS" {
270 set root=(hd0,%(install_partition)s)
271 linux /boot/addons/memdisk
272 initrd /boot/addons/balder10.imz
275 menuentry "Boot OS of first partition on first disk" {
280 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
283 def generate_flavour_specific_grub2_config(grml_flavour, install_partition, bootoptions):
284 """Generate grub2 configuration for use via grub.cfg
288 @grml_flavour: name of grml flavour the configuration should be generated for"""
290 local_datestamp = DATESTAMP
293 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
294 menuentry "%(grml_flavour)s" {
295 set root=(hd0,%(install_partition)s)
296 linux /boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
297 initrd /boot/release/%(grml_flavour)s/initrd.gz
300 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
301 menuentry "%(grml_flavour)s2ram" {
302 set root=(hd0,%(install_partition)s)
303 linux /boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s toram=%(grml_flavour)s %(bootoptions)s
304 initrd /boot/release/%(grml_flavour)s/initrd.gz
307 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
308 menuentry "%(grml_flavour)s-debug" {
309 set root=(hd0,%(install_partition)s)
310 linux /boot/release/%(grml_flavour)s/linux26
311 initrd /boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s debug boot=live initcall_debug%(bootoptions)s
314 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
315 menuentry "%(grml_flavour)s-x" {
316 set root=(hd0,%(install_partition)s)
317 linux /boot/release/%(grml_flavour)s/linux26
318 initrd /boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s startx=wm-ng %(bootoptions)s
321 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
322 menuentry "%(grml_flavour)s-nofb" {
323 set root=(hd0,%(install_partition)s)
324 linux /boot/release/%(grml_flavour)s/linux26
325 initrd /boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal video=ofonly %(bootoptions)s
328 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
329 menuentry "%(grml_flavour)s-failsafe" {
330 set root=(hd0,%(install_partition)s)
331 linux /boot/release/%(grml_flavour)s/linux26
332 initrd /boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal lang=us boot=live noautoconfig atapicd noacpi acpi=off nomodules nofirewire noudev nousb nohotplug noapm nopcmcia maxcpus=1 noscsi noagp nodma ide=nodma noswap nofstab nosound nogpm nosyslog nodhcp nocpu nodisc nomodem xmodule=vesa noraid nolvm %(bootoptions)s
335 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
336 menuentry "%(grml_flavour)s-forensic" {
337 set root=(hd0,%(install_partition)s)
338 linux /boot/release/%(grml_flavour)s/linux26
339 initrd /boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s nofstab noraid nolvm noautoconfig noswap raid=noautodetect %(bootoptions)s
342 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
343 menuentry "%(grml_flavour)s-serial" {
344 set root=(hd0,%(install_partition)s)
345 linux /boot/release/%(grml_flavour)s/linux26
346 initrd /boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal video=vesafb:off console=tty1 console=ttyS0,9600n8 %(bootoptions)s
349 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
352 def generate_grub1_config(grml_flavour, install_partition, bootoptions):
353 """Generate grub1 configuration for use via menu.lst
355 @grml_flavour: name of grml flavour the configuration should be generated for"""
357 local_datestamp = DATESTAMP
362 # color red/blue green/black
363 splashimage=/boot/grub/splash.xpm.gz
367 # root=(hd0,%(install_partition)s)
370 title %(grml_flavour)s - Default boot (using 1024x768 framebuffer)
371 kernel /boot/release/%(grml_flavour)s/linux26 apm=power-off lang=us vga=791 quiet boot=live nomce module=%(grml_flavour)s
372 initrd /boot/release/%(grml_flavour)s/initrd.gz
374 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
377 def generate_isolinux_splash(grml_flavour):
378 """Generate bootsplash for isolinux/syslinux
380 @grml_flavour: name of grml flavour the configuration should be generated for"""
382 # TODO: adjust last bootsplash line (the one following the "Some information and boot ...")
384 grml_name = grml_flavour
387 \ f17
\f\18/boot/syslinux/logo.16
389 Some information and boot options available via keys F2 - F10. http://grml.org/
391 """ % {'grml_name': grml_name} )
394 def generate_main_syslinux_config(grml_flavour, bootoptions):
395 """Generate main configuration for use in syslinux.cfg
397 @grml_flavour: name of grml flavour the configuration should be generated for
398 @bootoptions: bootoptions that should be used as a default"""
400 local_datestamp = DATESTAMP
403 ## main syslinux configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
404 # use this to control the bootup via a serial port
409 DISPLAY /boot/syslinux/boot.msg
410 F1 /boot/syslinux/boot.msg
419 F10 /boot/syslinux/f10
420 ## end of main configuration
422 ## global configuration
423 # the default option (using %(grml_flavour)s)
425 KERNEL /boot/release/%(grml_flavour)s/linux26
426 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
430 KERNEL /boot/addons/memtest
435 KERNEL /boot/addons/memdisk
436 APPEND initrd=/boot/addons/allinone.img
441 KERNEL /boot/addons/memdisk
442 APPEND initrd=/boot/addons/balder10.imz
444 ## end of global configuration
445 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions} )
448 def generate_flavour_specific_syslinux_config(grml_flavour, bootoptions):
449 """Generate flavour specific configuration for use in syslinux.cfg
451 @grml_flavour: name of grml flavour the configuration should be generated for
452 @bootoptions: bootoptions that should be used as a default"""
454 local_datestamp = DATESTAMP
458 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
459 LABEL %(grml_flavour)s
460 KERNEL /boot/release/%(grml_flavour)s/linux26
461 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
463 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
464 LABEL %(grml_flavour)s2ram
465 KERNEL /boot/release/%(grml_flavour)s/linux26
466 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s toram=%(grml_flavour)s %(bootoptions)s
468 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
469 LABEL %(grml_flavour)s-debug
470 KERNEL /boot/release/%(grml_flavour)s/linux26
471 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s debug boot=live initcall_debug%(bootoptions)s
473 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
474 LABEL %(grml_flavour)s-x
475 KERNEL /boot/release/%(grml_flavour)s/linux26
476 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s startx=wm-ng %(bootoptions)s
478 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
479 LABEL %(grml_flavour)s-nofb
480 KERNEL /boot/release/%(grml_flavour)s/linux26
481 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal video=ofonly %(bootoptions)s
483 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
484 LABEL %(grml_flavour)s-failsafe
485 KERNEL /boot/release/%(grml_flavour)s/linux26
486 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal lang=us boot=live noautoconfig atapicd noacpi acpi=off nomodules nofirewire noudev nousb nohotplug noapm nopcmcia maxcpus=1 noscsi noagp nodma ide=nodma noswap nofstab nosound nogpm nosyslog nodhcp nocpu nodisc nomodem xmodule=vesa noraid nolvm %(bootoptions)s
488 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
489 LABEL %(grml_flavour)s-forensic
490 KERNEL /boot/release/%(grml_flavour)s/linux26
491 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s nofstab noraid nolvm noautoconfig noswap raid=noautodetect %(bootoptions)s
493 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
494 LABEL %(grml_flavour)s-serial
495 KERNEL /boot/release/%(grml_flavour)s/linux26
496 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal video=vesafb:off console=tty1 console=ttyS0,9600n8 %(bootoptions)s
497 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions} )
500 def install_grub(device):
501 """Install grub on specified device.
503 @mntpoint: mountpoint of device where grub should install its files to
504 @device: partition where grub should be installed to"""
507 logging.info("Would execute grub-install [--root-directory=mount_point] %s now.", device)
509 device_mountpoint = tempfile.mkdtemp()
510 register_tmpfile(device_mountpoint)
512 mount(device, device_mountpoint, "")
513 logging.debug("grub-install --root-directory=%s %s", device_mountpoint, device)
514 proc = subprocess.Popen(["grub-install", "--root-directory=%s" % device_mountpoint, device], stdout=file(os.devnull, "r+"))
516 if proc.returncode != 0:
517 raise Exception("error executing grub-install")
518 except CriticalException, error:
519 logging.critical("Fatal: %s" % error)
524 unmount(device_mountpoint, "")
525 os.rmdir(device_mountpoint)
526 unregister_tmpfile(device_mountpoint)
529 def install_syslinux(device):
530 """Install syslinux on specified device.
532 @device: partition where syslinux should be installed to"""
535 logging.info("Would install syslinux as bootloader on %s", device)
538 # syslinux -d boot/isolinux /dev/sdb1
539 logging.info("Installing syslinux as bootloader")
540 logging.debug("syslinux -d boot/syslinux %s" % device)
541 proc = subprocess.Popen(["syslinux", "-d", "boot/syslinux", device])
543 if proc.returncode != 0:
544 raise CriticalException("Error executing syslinux (either try --fat16 or --grub?)")
547 def install_bootloader(device):
548 """Install bootloader on specified device.
550 @device: partition where bootloader should be installed to"""
552 # Install bootloader on the device (/dev/sda),
553 # not on the partition itself (/dev/sda1)?
554 #if partition[-1:].isdigit():
555 # device = re.match(r'(.*?)\d*$', partition).group(1)
563 install_syslinux(device)
564 except CriticalException, error:
565 logging.critical("Fatal: %s" % error)
570 def install_lilo_mbr(lilo, device):
573 # to support -A for extended partitions:
574 logging.info("Activating partitions in MBR via lilo")
575 logging.debug("%s -S /dev/null -M %s ext" % (lilo, device))
576 proc = subprocess.Popen([lilo, "-S", "/dev/null", "-M", device, "ext"])
578 if proc.returncode != 0:
579 raise Exception("error executing lilo")
581 # activate partition:
582 logging.debug("%s -S /dev/null -A %s 1" % (lilo, device))
583 proc = subprocess.Popen([lilo, "-S", "/dev/null", "-A", device, "1"])
585 if proc.returncode != 0:
586 raise Exception("error executing lilo")
589 def install_syslinux_mbr(device):
592 # lilo's mbr is broken, use the one from syslinux instead:
593 if not os.path.isfile("/usr/lib/syslinux/mbr.bin"):
594 raise Exception("/usr/lib/syslinux/mbr.bin can not be read")
596 logging.info("Installing syslinux MBR")
597 logging.debug("cat /usr/lib/syslinux/mbr.bin > %s" % device)
599 # TODO -> use Popen instead?
600 retcode = subprocess.call("cat /usr/lib/syslinux/mbr.bin > "+ device, shell=True)
602 logging.critical("Error copying MBR to device (%s)" % retcode)
603 except OSError, error:
604 logging.critical("Execution failed:", error)
607 def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
608 """Installs an MBR to a device.
610 Retrieve the partition table from "device", install an MBR from
611 the "mbrtemplate" file, set the "partition" (0..3) active, and
612 install the result back to "device".
614 "device" may be the name of a file assumed to be a hard disc
615 (or USB stick) image, or something like "/dev/sdb". "partition"
616 must be a number between 0 and 3, inclusive. "mbrtemplate" must
617 be a valid MBR file of at least 440 (439 if ismirbsdmbr) bytes.
619 If "ismirbsdmbr", the partitions' active flags are not changed.
620 Instead, the MBR's default value is set accordingly.
623 logging.info("Installing default MBR")
625 if not os.path.isfile(mbrtemplate):
626 logging.critical("Error: %s can not be read." % mbrtemplate)
627 raise CriticalException("Error installing MBR (either try --syslinux-mbr or install missing file?)")
629 if (partition < 0) or (partition > 3):
630 raise ValueError("partition must be between 0 and 3")
637 tmpf = tempfile.NamedTemporaryFile()
639 logging.debug("executing: dd if='%s' of='%s' bs=512 count=1" % (device, tmpf.name))
640 proc = subprocess.Popen(["dd", "if=%s" % device, "of=%s" % tmpf.name, "bs=512", "count=1"], stderr=file(os.devnull, "r+"))
642 if proc.returncode != 0:
643 raise Exception("error executing dd (first run)")
645 logging.debug("executing: dd if=%s of=%s bs=%s count=1 conv=notrunc" % (mbrtemplate, tmpf.name, nmbrbytes))
646 proc = subprocess.Popen(["dd", "if=%s" % mbrtemplate, "of=%s" % tmpf.name, "bs=%s" % nmbrbytes, "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
648 if proc.returncode != 0:
649 raise Exception("error executing dd (second run)")
651 mbrcode = tmpf.file.read(512)
652 if len(mbrcode) < 512:
653 raise EOFError("MBR size (%d) < 512" % len(mbrcode))
656 mbrcode = mbrcode[0:439] + chr(partition) + \
657 mbrcode[440:510] + "\x55\xAA"
659 actives = ["\x00", "\x00", "\x00", "\x00"]
660 actives[partition] = "\x80"
661 mbrcode = mbrcode[0:446] + actives[0] + \
662 mbrcode[447:462] + actives[1] + \
663 mbrcode[463:478] + actives[2] + \
664 mbrcode[479:494] + actives[3] + \
665 mbrcode[495:510] + "\x55\xAA"
669 tmpf.file.write(mbrcode)
672 logging.debug("executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc" % (tmpf.name, device))
673 proc = subprocess.Popen(["dd", "if=%s" % tmpf.name, "of=%s" % device, "bs=512", "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
675 if proc.returncode != 0:
676 raise Exception("error executing dd (third run)")
680 def handle_syslinux_mbr(device):
681 """Install syslinux master boot record on given device
683 @device: device where MBR should be installed to"""
685 if not is_writeable(device):
686 raise IOError("device not writeable for user")
688 # try to use system's lilo
692 # otherwise fall back to our static version
693 from platform import architecture
694 if architecture()[0] == '64bit':
695 lilo = '/usr/share/grml2usb/lilo/lilo.static.amd64'
697 lilo = '/usr/share/grml2usb/lilo/lilo.static.i386'
698 # finally prefer a specified lilo executable
703 raise Exception("lilo executable can not be execute")
706 logging.info("Would install MBR running lilo and using syslinux.")
709 install_lilo_mbr(lilo, device)
710 install_syslinux_mbr(device)
713 def is_writeable(device):
714 """Check if the device is writeable for the current user
716 @device: partition where bootloader should be installed to"""
720 #raise Exception("no device for checking write permissions")
722 if not os.path.exists(device):
725 return os.access(device, os.W_OK) and os.access(device, os.R_OK)
728 def mount(source, target, mount_options):
729 """Mount specified source on given target
731 @source: name of device/ISO that should be mounted
732 @target: directory where the ISO should be mounted to
733 @options: mount specific options"""
735 # note: options.dryrun does not work here, as we have to
736 # locate files and identify the grml flavour
738 for x in file('/proc/mounts').readlines():
739 if x.startswith(source):
740 raise CriticalException("Error executing mount: %s already mounted - please unmount before invoking grml2usb" % source)
742 logging.debug("mount %s %s %s" % (mount_options, source, target))
743 proc = subprocess.Popen(["mount"] + list(mount_options) + [source, target])
745 if proc.returncode != 0:
746 raise CriticalException("Error executing mount")
748 logging.debug("register_mountpoint(%s)" % target)
749 register_mountpoint(target)
752 def unmount(target, unmount_options):
753 """Unmount specified target
755 @target: target where something is mounted on and which should be unmounted
756 @options: options for umount command"""
758 # make sure we unmount only already mounted targets
759 target_unmount = False
760 mounts = open('/proc/mounts').readlines()
761 mountstring = re.compile(".*%s.*" % re.escape(target))
763 if re.match(mountstring, line):
764 target_unmount = True
766 if not target_unmount:
767 logging.debug("%s not mounted anymore" % target)
769 logging.debug("umount %s %s" % (list(unmount_options), target))
770 proc = subprocess.Popen(["umount"] + list(unmount_options) + [target])
772 if proc.returncode != 0:
773 raise Exception("Error executing umount")
775 logging.debug("unregister_mountpoint(%s)" % target)
776 unregister_mountpoint(target)
779 def check_for_usbdevice(device):
780 """Check whether the specified device is a removable USB device
782 @device: device name, like /dev/sda1 or /dev/sda
785 usbdevice = re.match(r'/dev/(.*?)\d*$', device).group(1)
786 usbdevice = os.path.realpath('/sys/class/block/' + usbdevice + '/removable')
787 if os.path.isfile(usbdevice):
788 is_usb = open(usbdevice).readline()
795 def check_for_fat(partition):
796 """Check whether specified partition is a valid VFAT/FAT16 filesystem
798 @partition: device name of partition"""
801 udev_info = subprocess.Popen(["/lib/udev/vol_id", "-t", partition],
802 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
803 filesystem = udev_info.communicate()[0].rstrip()
805 if udev_info.returncode == 2:
806 raise CriticalException("Failed to read device %s"
807 " (wrong UID/permissions or device not present?)" % partition)
809 if filesystem != "vfat":
810 raise CriticalException("Partition %s does not contain a FAT16 filesystem. (Use --fat16 or run mkfs.vfat %s)" % (partition, partition))
813 raise CriticalException("Sorry, /lib/udev/vol_id not available.")
816 def mkdir(directory):
817 """Simple wrapper around os.makedirs to get shell mkdir -p behaviour"""
819 # just silently pass as it's just fine it the directory exists
820 if not os.path.isdir(directory):
822 os.makedirs(directory)
823 # pylint: disable-msg=W0704
828 def copy_system_files(grml_flavour, iso_mount, target):
831 squashfs = search_file(grml_flavour + '.squashfs', iso_mount)
833 logging.critical("Fatal: squashfs file not found")
835 squashfs_target = target + '/live/'
836 execute(mkdir, squashfs_target)
837 # use install(1) for now to make sure we can write the files afterwards as normal user as well
838 logging.debug("cp %s %s" % (squashfs, target + '/live/' + grml_flavour + '.squashfs'))
839 proc = subprocess.Popen(["install", "--mode=664", squashfs, squashfs_target + grml_flavour + ".squashfs"])
842 filesystem_module = search_file('filesystem.module', iso_mount)
843 if filesystem_module is None:
844 logging.critical("Fatal: filesystem.module not found")
846 logging.debug("cp %s %s" % (filesystem_module, squashfs_target + grml_flavour + '.module'))
847 proc = subprocess.Popen(["install", "--mode=664", filesystem_module,
848 squashfs_target + grml_flavour + '.module'])
851 release_target = target + '/boot/release/' + grml_flavour
852 execute(mkdir, release_target)
854 kernel = search_file('linux26', iso_mount)
856 logging.critical("Fatal kernel not found")
858 logging.debug("cp %s %s" % (kernel, release_target + '/linux26'))
859 proc = subprocess.Popen(["install", "--mode=664", kernel, release_target + '/linux26'])
862 initrd = search_file('initrd.gz', iso_mount)
864 logging.critical("Fatal: initrd not found")
866 logging.debug("cp %s %s" % (initrd, release_target + '/initrd.gz'))
867 proc = subprocess.Popen(["install", "--mode=664", initrd, release_target + '/initrd.gz'])
871 def copy_grml_files(iso_mount, target):
874 grml_target = target + '/grml/'
875 execute(mkdir, grml_target)
877 for myfile in 'grml-cheatcodes.txt', 'grml-version', 'LICENSE.txt', 'md5sums', 'README.txt':
878 grml_file = search_file(myfile, iso_mount)
879 if grml_file is None:
880 logging.warn("Warning: myfile %s could not be found - can not install it", myfile)
882 logging.debug("cp %s %s" % (grml_file, grml_target + grml_file))
883 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_target + myfile])
886 grml_web_target = grml_target + '/web/'
887 execute(mkdir, grml_web_target)
889 for myfile in 'index.html', 'style.css':
890 grml_file = search_file(myfile, iso_mount)
891 if grml_file is None:
892 logging.warn("Warning: myfile %s could not be found - can not install it")
894 logging.debug("cp %s %s" % (grml_file, grml_web_target + grml_file))
895 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_web_target + myfile])
898 grml_webimg_target = grml_web_target + '/images/'
899 execute(mkdir, grml_webimg_target)
901 for myfile in 'button.png', 'favicon.png', 'linux.jpg', 'logo.png':
902 grml_file = search_file(myfile, iso_mount)
903 if grml_file is None:
904 logging.warn("Warning: myfile %s could not be found - can not install it")
906 logging.debug("cp %s %s" % (grml_file, grml_webimg_target + grml_file))
907 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_webimg_target + myfile])
911 def copy_addons(iso_mount, target):
913 addons = target + '/boot/addons/'
914 execute(mkdir, addons)
916 # grub all-in-one image
917 allinoneimg = search_file('allinone.img', iso_mount)
918 if allinoneimg is None:
919 logging.warn("Warning: allinone.img not found - can not install it")
921 logging.debug("cp %s %s" % (allinoneimg, addons + '/allinone.img'))
922 proc = subprocess.Popen(["install", "--mode=664", allinoneimg, addons + 'allinone.img'])
926 balderimg = search_file('balder10.imz', iso_mount)
927 if balderimg is None:
928 logging.warn("Warning: balder10.imz not found - can not install it")
930 logging.debug("cp %s %s" % (balderimg, addons + '/balder10.imz'))
931 proc = subprocess.Popen(["install", "--mode=664", balderimg, addons + 'balder10.imz'])
935 memdiskimg = search_file('memdisk', iso_mount)
936 if memdiskimg is None:
937 logging.warn("Warning: memdisk not found - can not install it")
939 logging.debug("cp %s %s" % (memdiskimg, addons + '/memdisk'))
940 proc = subprocess.Popen(["install", "--mode=664", memdiskimg, addons + 'memdisk'])
944 memtestimg = search_file('memtest', iso_mount)
945 if memtestimg is None:
946 logging.warn("Warning: memtest not found - can not install it")
948 logging.debug("cp %s %s" % (memtestimg, addons + '/memtest'))
949 proc = subprocess.Popen(["install", "--mode=664", memtestimg, addons + 'memtest'])
953 def copy_bootloader_files(iso_mount, target):
956 syslinux_target = target + '/boot/syslinux/'
957 execute(mkdir, syslinux_target)
959 logo = search_file('logo.16', iso_mount)
960 logging.debug("cp %s %s" % (logo, syslinux_target + 'logo.16'))
961 proc = subprocess.Popen(["install", "--mode=664", logo, syslinux_target + 'logo.16'])
964 for ffile in 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10':
965 bootsplash = search_file(ffile, iso_mount)
966 logging.debug("cp %s %s" % (bootsplash, syslinux_target + ffile))
967 proc = subprocess.Popen(["install", "--mode=664", bootsplash, syslinux_target + ffile])
970 grub_target = target + '/boot/grub/'
971 execute(mkdir, grub_target)
973 if not os.path.isfile("/usr/share/grml2usb/grub/splash.xpm.gz"):
974 logging.critical("Error: /usr/share/grml2usb/grub/splash.xpm.gz can not be read.")
977 logging.debug("cp /usr/share/grml2usb/grub/splash.xpm.gz %s" % grub_target + 'splash.xpm.gz')
978 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/splash.xpm.gz',
979 grub_target + 'splash.xpm.gz'])
982 # grml splash in grub
983 if os.path.isfile("/usr/share/grml2usb/grub/grml.png"):
984 logging.debug("cp /usr/share/grml2usb/grub/grml.png to %s" % grub_target + 'grml.png')
985 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/grml.png',
986 grub_target + 'grml.png'])
989 # font file for graphical bootsplash in grub
990 if os.path.isfile("/usr/share/grub/ascii.pff"):
991 logging.debug("cp /usr/share/grub/ascii.pff to %s" % grub_target + 'ascii.pff')
992 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grub/ascii.pff',
993 grub_target + 'ascii.pff'])
996 if not os.path.isfile("/usr/share/grml2usb/grub/stage2_eltorito"):
997 logging.critical("Error: /usr/share/grml2usb/grub/stage2_eltorito can not be read.")
1000 logging.debug("cp /usr/share/grml2usb/grub/stage2_eltorito to %s" % grub_target + 'stage2_eltorito')
1001 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/stage2_eltorito',
1002 grub_target + 'stage2_eltorito'])
1006 def install_iso_files(grml_flavour, iso_mount, device, target):
1007 """Copy files from ISO on given target"""
1010 # * make sure grml_flavour, iso_mount, target are set when the function is called, otherwise raise exception
1011 # * provide alternative search_file() if file information is stored in a config.ini file?
1012 # * catch "install: .. No space left on device" & CO
1015 logging.info("Would copy files to %s", iso_mount)
1017 elif not options.bootloaderonly:
1018 logging.info("Copying files. This might take a while....")
1019 copy_system_files(grml_flavour, iso_mount, target)
1020 copy_grml_files(iso_mount, target)
1022 if not options.skipaddons:
1023 copy_addons(iso_mount, target)
1025 if not options.copyonly:
1026 copy_bootloader_files(iso_mount, target)
1028 if not options.dryrun:
1029 handle_bootloader_config(grml_flavour, device, target)
1031 # make sure we sync filesystems before returning
1032 proc = subprocess.Popen(["sync"])
1036 def uninstall_files(device):
1037 """Get rid of all grml files on specified device
1039 @device: partition where grml2usb files should be removed from"""
1042 logging.critical("TODO: uninstalling files from %s not yet implement, sorry." % device)
1045 def identify_grml_flavour(mountpath):
1046 """Get name of grml flavour
1048 @mountpath: path where the grml ISO is mounted to
1049 @return: name of grml-flavour"""
1051 version_file = search_file('grml-version', mountpath)
1053 if version_file == "":
1054 logging.critical("Error: could not find grml-version file.")
1058 tmpfile = open(version_file, 'r')
1059 grml_info = tmpfile.readline()
1060 grml_flavour = re.match(r'[\w-]*', grml_info).group()
1064 logging.critical("Unexpected error:", sys.exc_info()[0])
1070 def handle_grub_config(grml_flavour, device, target):
1073 logging.debug("Generating grub configuration")
1074 #with open("...", "w") as f:
1075 #f.write("bla bla bal")
1077 grub_target = target + '/boot/grub/'
1078 # should be present via copy_bootloader_files(), but make sure it exists:
1079 execute(mkdir, grub_target)
1080 # we have to adjust root() inside grub configuration
1081 if device[-1:].isdigit():
1082 install_partition = device[-1:]
1084 # do NOT write "None" in kernel cmdline
1085 if options.bootoptions is None:
1088 bootopt = options.bootoptions
1091 #logging.debug("Creating grub1 configuration file")
1092 #grub_config_file = open(grub_target + 'menu.lst', 'w')
1093 #grub_config_file.write(generate_grub1_config(grml_flavour, install_partition, bootopt))
1094 #grub_config_file.close()
1095 # TODO => generate_main_grub1_config() && generate_flavour_specific_grub1_config()
1098 grub2_cfg = grub_target + 'grub.cfg'
1099 logging.debug("Creating grub2 configuration file")
1101 # install main configuration only *once*, no matter how many ISOs we have:
1102 if os.path.isfile(grub2_cfg):
1103 string = open(grub2_cfg).readline()
1104 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1105 if not re.match(main_identifier, string):
1106 grub2_config_file = open(grub2_cfg, 'w')
1107 logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1108 grub2_config_file.write(generate_main_grub2_config(grml_flavour, install_partition, bootopt))
1109 grub2_config_file.close()
1111 grub2_config_file = open(grub2_cfg, 'w')
1112 grub2_config_file.write(generate_main_grub2_config(grml_flavour, install_partition, bootopt))
1113 grub2_config_file.close()
1115 grub_flavour_config = True
1116 if os.path.isfile(grub2_cfg):
1117 string = open(grub2_cfg).readlines()
1118 logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1119 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1121 if flavour.match(line):
1122 grub_flavour_config = False
1124 if grub_flavour_config:
1125 grub2_config_file = open(grub2_cfg, 'a')
1126 grub2_config_file.write(generate_flavour_specific_grub2_config(grml_flavour, install_partition, bootopt))
1127 grub2_config_file.close( )
1130 def handle_syslinux_config(grml_flavour, target):
1134 # do NOT write "None" in kernel cmdline
1135 if options.bootoptions is None:
1138 bootopt = options.bootoptions
1140 logging.info("Generating syslinux configuration")
1141 syslinux_target = target + '/boot/syslinux/'
1142 # should be present via copy_bootloader_files(), but make sure it exits:
1143 execute(mkdir, syslinux_target)
1144 syslinux_cfg = syslinux_target + 'syslinux.cfg'
1146 # install main configuration only *once*, no matter how many ISOs we have:
1147 if os.path.isfile(syslinux_cfg):
1148 string = open(syslinux_cfg).readline()
1149 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1150 if not re.match(main_identifier, string):
1151 syslinux_config_file = open(syslinux_cfg, 'w')
1152 logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1153 syslinux_config_file.write(generate_main_syslinux_config(grml_flavour, bootopt))
1154 syslinux_config_file.close()
1156 syslinux_config_file = open(syslinux_cfg, 'w')
1157 syslinux_config_file.write(generate_main_syslinux_config(grml_flavour, bootopt))
1158 syslinux_config_file.close()
1160 # install flavour specific configuration only *once* as well
1161 # kind of ugly - I'm pretty sure this could be smoother...
1162 syslinux_flavour_config = True
1163 if os.path.isfile(syslinux_cfg):
1164 string = open(syslinux_cfg).readlines()
1165 logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1166 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1168 if flavour.match(line):
1169 syslinux_flavour_config = False
1171 if syslinux_flavour_config:
1172 syslinux_config_file = open(syslinux_cfg, 'a')
1173 syslinux_config_file.write(generate_flavour_specific_syslinux_config(grml_flavour, bootopt))
1174 syslinux_config_file.close( )
1176 logging.debug("Generating isolinux/syslinux splash %s" % syslinux_target + 'boot.msg')
1177 isolinux_splash = open(syslinux_target + 'boot.msg', 'w')
1178 isolinux_splash.write(generate_isolinux_splash(grml_flavour))
1179 isolinux_splash.close( )
1182 def handle_bootloader_config(grml_flavour, device, target):
1186 handle_grub_config(grml_flavour, device, target)
1188 handle_syslinux_config(grml_flavour, target)
1191 def handle_iso(iso, device):
1192 """Main logic for mounting ISOs and copying files.
1194 @iso: full path to the ISO that should be installed to the specified device
1195 @device: partition where the specified ISO should be installed to"""
1197 logging.info("Using ISO %s" % iso)
1199 if os.path.isdir(iso):
1200 logging.critical("TODO: /live/image handling not yet implemented - sorry") # TODO
1203 iso_mountpoint = tempfile.mkdtemp()
1204 register_tmpfile(iso_mountpoint)
1205 remove_iso_mountpoint = True
1207 if not os.path.isfile(iso):
1208 logging.critical("Fatal: specified ISO %s could not be read" % iso)
1213 mount(iso, iso_mountpoint, ["-o", "loop", "-t", "iso9660"])
1214 except CriticalException, error:
1215 logging.critical("Fatal: %s" % error)
1218 if os.path.isdir(device):
1219 logging.info("Specified target is a directory, not mounting therefor.")
1220 device_mountpoint = device
1221 remove_device_mountpoint = False
1224 device_mountpoint = tempfile.mkdtemp()
1225 register_tmpfile(device_mountpoint)
1226 remove_device_mountpoint = True
1228 mount(device, device_mountpoint, "")
1229 except CriticalException, error:
1230 logging.critical("Fatal: %s" % error)
1235 grml_flavour = identify_grml_flavour(iso_mountpoint)
1236 logging.info("Identified grml flavour \"%s\"." % grml_flavour)
1237 install_iso_files(grml_flavour, iso_mountpoint, device, device_mountpoint)
1239 logging.critical("Fatal: a critical error happend during execution (not a grml ISO?), giving up")
1242 if os.path.isdir(iso_mountpoint) and remove_iso_mountpoint:
1243 unmount(iso_mountpoint, "")
1244 os.rmdir(iso_mountpoint)
1245 unregister_tmpfile(iso_mountpoint)
1246 if remove_device_mountpoint:
1247 unmount(device_mountpoint, "")
1248 if os.path.isdir(device_mountpoint):
1249 os.rmdir(device_mountpoint)
1250 unregister_tmpfile(device_mountpoint)
1253 def handle_mbr(device):
1257 # if not options.mbr:
1258 # logging.info("You are NOT using the --mbr option. Consider using it if your device does not boot.")
1260 # make sure we install MBR on /dev/sdX and not /dev/sdX#
1262 # make sure we have syslinux available
1264 if not options.skipmbr:
1265 if not which("syslinux") and not options.copyonly and not options.dryrun:
1266 logging.critical('Sorry, syslinux not available. Exiting.')
1267 logging.critical('Please install syslinux or consider using the --grub option.')
1270 if not options.skipmbr:
1271 if device[-1:].isdigit():
1272 mbr_device = re.match(r'(.*?)\d*$', device).group(1)
1273 partition_number = int(device[-1:]) - 1
1276 if options.syslinuxmbr:
1277 handle_syslinux_mbr(mbr_device)
1280 install_mir_mbr('/usr/share/grml2usb/mbr/mbrmgr', mbr_device, partition_number, True)
1282 install_mir_mbr('/usr/share/grml2usb/mbr/mbrldr', mbr_device, partition_number, True)
1283 except IOError, error:
1284 logging.critical("Execution failed: %s", error)
1286 except Exception, error:
1287 logging.critical("Execution failed: %s", error)
1291 def handle_vfat(device):
1294 # make sure we have mkfs.vfat available
1295 if options.fat16 and not options.force:
1296 if not which("mkfs.vfat") and not options.copyonly and not options.dryrun:
1297 logging.critical('Sorry, mkfs.vfat not available. Exiting.')
1298 logging.critical('Please make sure to install dosfstools.')
1301 # make sure the user is aware of what he is doing
1302 f = raw_input("Are you sure you want to format the specified partition with fat16? y/N ")
1303 if f == "y" or f == "Y":
1304 logging.info("Note: you can skip this question using the option --force")
1307 except CriticalException, error:
1308 logging.critical("Execution failed: %s", error)
1313 # check for vfat filesystem
1314 if device is not None and not os.path.isdir(device):
1316 check_for_fat(device)
1317 except CriticalException, error:
1318 logging.critical("Execution failed: %s", error)
1321 if not check_for_usbdevice(device):
1322 print "Warning: the specified device %s does not look like a removable usb device." % device
1323 f = raw_input("Do you really want to continue? y/N ")
1324 if f == "y" or f == "Y":
1330 def handle_compat_warning(device):
1333 # make sure we can replace old grml2usb script and warn user when using old way of life:
1334 if device.startswith("/mnt/external") or device.startswith("/mnt/usb") and not options.force:
1335 print "Warning: the semantics of grml2usb has changed."
1336 print "Instead of using grml2usb /path/to/iso %s you might" % device
1337 print "want to use grml2usb /path/to/iso /dev/... instead."
1338 print "Please check out the grml2usb manpage for details."
1339 f = raw_input("Do you really want to continue? y/N ")
1340 if f == "y" or f == "Y":
1346 def handle_logging():
1350 FORMAT = "%(asctime)-15s %(message)s"
1351 logging.basicConfig(level=logging.DEBUG, format=FORMAT)
1353 FORMAT = "Critical: %(message)s"
1354 logging.basicConfig(level=logging.CRITICAL, format=FORMAT)
1356 FORMAT = "Info: %(message)s"
1357 logging.basicConfig(level=logging.INFO, format=FORMAT)
1360 def handle_bootloader(device):
1362 # Install bootloader only if not using the --copy-only option
1363 if options.copyonly:
1364 logging.info("Not installing bootloader and its files as requested via option copyonly.")
1366 install_bootloader(device)
1370 """Main function [make pylint happy :)]"""
1373 print os.path.basename(sys.argv[0]) + " " + PROG_VERSION
1377 parser.error("invalid usage")
1382 # make sure we have the appropriate permissions
1386 logging.info("Running in simulation mode as requested via option dry-run.")
1388 # specified arguments
1389 device = args[len(args) - 1]
1390 isos = args[0:len(args) - 1]
1392 if device[-1:].isdigit():
1393 if int(device[-1:]) > 4:
1394 logging.critical("Fatal: installation on partition number >4 not supported. (As the BIOS won't support it.)")
1397 logging.critical("Fatal: installation on raw device not supported. (As the BIOS won't support it.)")
1400 # provide upgrade path
1401 handle_compat_warning(device)
1403 # check for vfat partition
1406 # main operation (like installing files)
1408 handle_iso(iso, device)
1413 handle_bootloader(device)
1415 # finally be politely :)
1416 logging.info("Finished execution of grml2usb (%s). Have fun with your grml system." % PROG_VERSION)
1419 if __name__ == "__main__":
1422 except KeyboardInterrupt:
1423 logging.info("Received KeyboardInterrupt")
1426 ## END OF FILE #################################################################
1427 # vim:foldmethod=indent expandtab ai ft=python tw=120 fileencoding=utf-8