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_flavour_specific_grub1_config(grml_flavour, install_partition, bootoptions):
353 """Generate grub1 configuration for use via menu.lst
357 @grml_flavour: name of grml flavour the configuration should be generated for"""
359 local_datestamp = DATESTAMP
362 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
363 title %(grml_flavour)s
364 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
365 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
367 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
368 title %(grml_flavour)s2ram
369 kernel (hd0,%(install_partition)s)/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
370 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
372 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
373 title %(grml_flavour)s-debug
374 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s debug boot=live initcall_debug%(bootoptions)s
375 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
377 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
378 title %(grml_flavour)s-x
379 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s startx=wm-ng %(bootoptions)s
380 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
382 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
383 title %(grml_flavour)s-nofb
384 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal video=ofonly %(bootoptions)s
385 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
387 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
388 title %(grml_flavour)s-failsafe
389 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 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
390 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
392 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
393 title %(grml_flavour)s-forensic
394 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s nofstab noraid nolvm noautoconfig noswap raid=noautodetect %(bootoptions)s
395 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
397 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
398 title %(grml_flavour)s-serial
399 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce quiet module=%(grml_flavour)s vga=normal video=vesafb:off console=tty1 console=ttyS0,9600n8 %(bootoptions)s
400 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
402 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
405 def generate_main_grub1_config(grml_flavour, install_partition, bootoptions):
406 """Generate grub1 configuration for use via menu.lst
408 @grml_flavour: name of grml flavour the configuration should be generated for"""
410 local_datestamp = DATESTAMP
413 ## main grub1 configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
416 # color red/blue green/black
417 splashimage=(hd0,%(install_partition)s)/boot/grub/splash.xpm.gz
422 title %(grml_flavour)s - Default boot (using 1024x768 framebuffer)
423 kernel (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/linux26 apm=power-off lang=us vga=791 quiet boot=live nomce module=%(grml_flavour)s
424 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
426 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
429 def generate_isolinux_splash(grml_flavour):
430 """Generate bootsplash for isolinux/syslinux
432 @grml_flavour: name of grml flavour the configuration should be generated for"""
434 # TODO: adjust last bootsplash line (the one following the "Some information and boot ...")
436 grml_name = grml_flavour
439 \ f17
\f\18/boot/syslinux/logo.16
441 Some information and boot options available via keys F2 - F10. http://grml.org/
443 """ % {'grml_name': grml_name} )
446 def generate_main_syslinux_config(grml_flavour, bootoptions):
447 """Generate main configuration for use in syslinux.cfg
449 @grml_flavour: name of grml flavour the configuration should be generated for
450 @bootoptions: bootoptions that should be used as a default"""
452 local_datestamp = DATESTAMP
455 ## main syslinux configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
456 # use this to control the bootup via a serial port
461 DISPLAY /boot/syslinux/boot.msg
462 F1 /boot/syslinux/boot.msg
471 F10 /boot/syslinux/f10
472 ## end of main configuration
474 ## global configuration
475 # the default option (using %(grml_flavour)s)
477 KERNEL /boot/release/%(grml_flavour)s/linux26
478 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
482 KERNEL /boot/addons/memtest
487 KERNEL /boot/addons/memdisk
488 APPEND initrd=/boot/addons/allinone.img
493 KERNEL /boot/addons/memdisk
494 APPEND initrd=/boot/addons/balder10.imz
496 ## end of global configuration
497 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions} )
500 def generate_flavour_specific_syslinux_config(grml_flavour, bootoptions):
501 """Generate flavour specific configuration for use in syslinux.cfg
503 @grml_flavour: name of grml flavour the configuration should be generated for
504 @bootoptions: bootoptions that should be used as a default"""
506 local_datestamp = DATESTAMP
510 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
511 LABEL %(grml_flavour)s
512 KERNEL /boot/release/%(grml_flavour)s/linux26
513 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
515 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
516 LABEL %(grml_flavour)s2ram
517 KERNEL /boot/release/%(grml_flavour)s/linux26
518 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
520 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
521 LABEL %(grml_flavour)s-debug
522 KERNEL /boot/release/%(grml_flavour)s/linux26
523 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
525 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
526 LABEL %(grml_flavour)s-x
527 KERNEL /boot/release/%(grml_flavour)s/linux26
528 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
530 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
531 LABEL %(grml_flavour)s-nofb
532 KERNEL /boot/release/%(grml_flavour)s/linux26
533 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
535 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
536 LABEL %(grml_flavour)s-failsafe
537 KERNEL /boot/release/%(grml_flavour)s/linux26
538 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
540 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
541 LABEL %(grml_flavour)s-forensic
542 KERNEL /boot/release/%(grml_flavour)s/linux26
543 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
545 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
546 LABEL %(grml_flavour)s-serial
547 KERNEL /boot/release/%(grml_flavour)s/linux26
548 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
549 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions} )
552 def install_grub(device):
553 """Install grub on specified device.
555 @mntpoint: mountpoint of device where grub should install its files to
556 @device: partition where grub should be installed to"""
559 logging.info("Would execute grub-install [--root-directory=mount_point] %s now.", device)
561 device_mountpoint = tempfile.mkdtemp()
562 register_tmpfile(device_mountpoint)
564 mount(device, device_mountpoint, "")
565 logging.debug("grub-install --recheck --no-floppy --root-directory=%s %s", device_mountpoint, device)
566 proc = subprocess.Popen(["grub-install", "--recheck", "--no-floppy", "--root-directory=%s" % device_mountpoint, device], stdout=file(os.devnull, "r+"))
568 if proc.returncode != 0:
569 raise Exception("error executing grub-install")
570 except CriticalException, error:
571 logging.critical("Fatal: %s" % error)
576 unmount(device_mountpoint, "")
577 os.rmdir(device_mountpoint)
578 unregister_tmpfile(device_mountpoint)
581 def install_syslinux(device):
582 """Install syslinux on specified device.
584 @device: partition where syslinux should be installed to"""
587 logging.info("Would install syslinux as bootloader on %s", device)
590 # syslinux -d boot/isolinux /dev/sdb1
591 logging.info("Installing syslinux as bootloader")
592 logging.debug("syslinux -d boot/syslinux %s" % device)
593 proc = subprocess.Popen(["syslinux", "-d", "boot/syslinux", device])
595 if proc.returncode != 0:
596 raise CriticalException("Error executing syslinux (either try --fat16 or --grub?)")
599 def install_bootloader(device):
600 """Install bootloader on specified device.
602 @device: partition where bootloader should be installed to"""
604 # Install bootloader on the device (/dev/sda),
605 # not on the partition itself (/dev/sda1)?
606 #if partition[-1:].isdigit():
607 # device = re.match(r'(.*?)\d*$', partition).group(1)
615 install_syslinux(device)
616 except CriticalException, error:
617 logging.critical("Fatal: %s" % error)
622 def install_lilo_mbr(lilo, device):
625 # to support -A for extended partitions:
626 logging.info("Activating partitions in MBR via lilo")
627 logging.debug("%s -S /dev/null -M %s ext" % (lilo, device))
628 proc = subprocess.Popen([lilo, "-S", "/dev/null", "-M", device, "ext"])
630 if proc.returncode != 0:
631 raise Exception("error executing lilo")
633 # activate partition:
634 logging.debug("%s -S /dev/null -A %s 1" % (lilo, device))
635 proc = subprocess.Popen([lilo, "-S", "/dev/null", "-A", device, "1"])
637 if proc.returncode != 0:
638 raise Exception("error executing lilo")
641 def install_syslinux_mbr(device):
644 # lilo's mbr is broken, use the one from syslinux instead:
645 if not os.path.isfile("/usr/lib/syslinux/mbr.bin"):
646 raise Exception("/usr/lib/syslinux/mbr.bin can not be read")
648 logging.info("Installing syslinux MBR")
649 logging.debug("cat /usr/lib/syslinux/mbr.bin > %s" % device)
651 # TODO -> use Popen instead?
652 retcode = subprocess.call("cat /usr/lib/syslinux/mbr.bin > "+ device, shell=True)
654 logging.critical("Error copying MBR to device (%s)" % retcode)
655 except OSError, error:
656 logging.critical("Execution failed:", error)
659 def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
660 """Installs an MBR to a device.
662 Retrieve the partition table from "device", install an MBR from
663 the "mbrtemplate" file, set the "partition" (0..3) active, and
664 install the result back to "device".
666 "device" may be the name of a file assumed to be a hard disc
667 (or USB stick) image, or something like "/dev/sdb". "partition"
668 must be a number between 0 and 3, inclusive. "mbrtemplate" must
669 be a valid MBR file of at least 440 (439 if ismirbsdmbr) bytes.
671 If "ismirbsdmbr", the partitions' active flags are not changed.
672 Instead, the MBR's default value is set accordingly.
675 logging.info("Installing default MBR")
677 if not os.path.isfile(mbrtemplate):
678 logging.critical("Error: %s can not be read." % mbrtemplate)
679 raise CriticalException("Error installing MBR (either try --syslinux-mbr or install missing file?)")
681 if (partition < 0) or (partition > 3):
682 raise ValueError("partition must be between 0 and 3")
689 tmpf = tempfile.NamedTemporaryFile()
691 logging.debug("executing: dd if='%s' of='%s' bs=512 count=1" % (device, tmpf.name))
692 proc = subprocess.Popen(["dd", "if=%s" % device, "of=%s" % tmpf.name, "bs=512", "count=1"], stderr=file(os.devnull, "r+"))
694 if proc.returncode != 0:
695 raise Exception("error executing dd (first run)")
697 logging.debug("executing: dd if=%s of=%s bs=%s count=1 conv=notrunc" % (mbrtemplate, tmpf.name, nmbrbytes))
698 proc = subprocess.Popen(["dd", "if=%s" % mbrtemplate, "of=%s" % tmpf.name, "bs=%s" % nmbrbytes, "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
700 if proc.returncode != 0:
701 raise Exception("error executing dd (second run)")
703 mbrcode = tmpf.file.read(512)
704 if len(mbrcode) < 512:
705 raise EOFError("MBR size (%d) < 512" % len(mbrcode))
708 mbrcode = mbrcode[0:439] + chr(partition) + \
709 mbrcode[440:510] + "\x55\xAA"
711 actives = ["\x00", "\x00", "\x00", "\x00"]
712 actives[partition] = "\x80"
713 mbrcode = mbrcode[0:446] + actives[0] + \
714 mbrcode[447:462] + actives[1] + \
715 mbrcode[463:478] + actives[2] + \
716 mbrcode[479:494] + actives[3] + \
717 mbrcode[495:510] + "\x55\xAA"
721 tmpf.file.write(mbrcode)
724 logging.debug("executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc" % (tmpf.name, device))
725 proc = subprocess.Popen(["dd", "if=%s" % tmpf.name, "of=%s" % device, "bs=512", "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
727 if proc.returncode != 0:
728 raise Exception("error executing dd (third run)")
732 def handle_syslinux_mbr(device):
733 """Install syslinux master boot record on given device
735 @device: device where MBR should be installed to"""
737 if not is_writeable(device):
738 raise IOError("device not writeable for user")
740 # try to use system's lilo
744 # otherwise fall back to our static version
745 from platform import architecture
746 if architecture()[0] == '64bit':
747 lilo = '/usr/share/grml2usb/lilo/lilo.static.amd64'
749 lilo = '/usr/share/grml2usb/lilo/lilo.static.i386'
750 # finally prefer a specified lilo executable
755 raise Exception("lilo executable can not be execute")
758 logging.info("Would install MBR running lilo and using syslinux.")
761 install_lilo_mbr(lilo, device)
762 install_syslinux_mbr(device)
765 def is_writeable(device):
766 """Check if the device is writeable for the current user
768 @device: partition where bootloader should be installed to"""
772 #raise Exception("no device for checking write permissions")
774 if not os.path.exists(device):
777 return os.access(device, os.W_OK) and os.access(device, os.R_OK)
780 def mount(source, target, mount_options):
781 """Mount specified source on given target
783 @source: name of device/ISO that should be mounted
784 @target: directory where the ISO should be mounted to
785 @options: mount specific options"""
787 # note: options.dryrun does not work here, as we have to
788 # locate files and identify the grml flavour
790 for x in file('/proc/mounts').readlines():
791 if x.startswith(source):
792 raise CriticalException("Error executing mount: %s already mounted - please unmount before invoking grml2usb" % source)
794 logging.debug("mount %s %s %s" % (mount_options, source, target))
795 proc = subprocess.Popen(["mount"] + list(mount_options) + [source, target])
797 if proc.returncode != 0:
798 raise CriticalException("Error executing mount")
800 logging.debug("register_mountpoint(%s)" % target)
801 register_mountpoint(target)
804 def unmount(target, unmount_options):
805 """Unmount specified target
807 @target: target where something is mounted on and which should be unmounted
808 @options: options for umount command"""
810 # make sure we unmount only already mounted targets
811 target_unmount = False
812 mounts = open('/proc/mounts').readlines()
813 mountstring = re.compile(".*%s.*" % re.escape(target))
815 if re.match(mountstring, line):
816 target_unmount = True
818 if not target_unmount:
819 logging.debug("%s not mounted anymore" % target)
821 logging.debug("umount %s %s" % (list(unmount_options), target))
822 proc = subprocess.Popen(["umount"] + list(unmount_options) + [target])
824 if proc.returncode != 0:
825 raise Exception("Error executing umount")
827 logging.debug("unregister_mountpoint(%s)" % target)
828 unregister_mountpoint(target)
831 def check_for_usbdevice(device):
832 """Check whether the specified device is a removable USB device
834 @device: device name, like /dev/sda1 or /dev/sda
837 usbdevice = re.match(r'/dev/(.*?)\d*$', device).group(1)
838 usbdevice = os.path.realpath('/sys/class/block/' + usbdevice + '/removable')
839 if os.path.isfile(usbdevice):
840 is_usb = open(usbdevice).readline()
847 def check_for_fat(partition):
848 """Check whether specified partition is a valid VFAT/FAT16 filesystem
850 @partition: device name of partition"""
853 udev_info = subprocess.Popen(["/lib/udev/vol_id", "-t", partition],
854 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
855 filesystem = udev_info.communicate()[0].rstrip()
857 if udev_info.returncode == 2:
858 raise CriticalException("Failed to read device %s"
859 " (wrong UID/permissions or device not present?)" % partition)
861 if filesystem != "vfat":
862 raise CriticalException("Partition %s does not contain a FAT16 filesystem. (Use --fat16 or run mkfs.vfat %s)" % (partition, partition))
865 raise CriticalException("Sorry, /lib/udev/vol_id not available.")
868 def mkdir(directory):
869 """Simple wrapper around os.makedirs to get shell mkdir -p behaviour"""
871 # just silently pass as it's just fine it the directory exists
872 if not os.path.isdir(directory):
874 os.makedirs(directory)
875 # pylint: disable-msg=W0704
880 def copy_system_files(grml_flavour, iso_mount, target):
883 squashfs = search_file(grml_flavour + '.squashfs', iso_mount)
885 logging.critical("Fatal: squashfs file not found")
887 squashfs_target = target + '/live/'
888 execute(mkdir, squashfs_target)
889 # use install(1) for now to make sure we can write the files afterwards as normal user as well
890 logging.debug("cp %s %s" % (squashfs, target + '/live/' + grml_flavour + '.squashfs'))
891 proc = subprocess.Popen(["install", "--mode=664", squashfs, squashfs_target + grml_flavour + ".squashfs"])
894 filesystem_module = search_file('filesystem.module', iso_mount)
895 if filesystem_module is None:
896 logging.critical("Fatal: filesystem.module not found")
898 logging.debug("cp %s %s" % (filesystem_module, squashfs_target + grml_flavour + '.module'))
899 proc = subprocess.Popen(["install", "--mode=664", filesystem_module,
900 squashfs_target + grml_flavour + '.module'])
903 release_target = target + '/boot/release/' + grml_flavour
904 execute(mkdir, release_target)
906 kernel = search_file('linux26', iso_mount)
908 logging.critical("Fatal kernel not found")
910 logging.debug("cp %s %s" % (kernel, release_target + '/linux26'))
911 proc = subprocess.Popen(["install", "--mode=664", kernel, release_target + '/linux26'])
914 initrd = search_file('initrd.gz', iso_mount)
916 logging.critical("Fatal: initrd not found")
918 logging.debug("cp %s %s" % (initrd, release_target + '/initrd.gz'))
919 proc = subprocess.Popen(["install", "--mode=664", initrd, release_target + '/initrd.gz'])
923 def copy_grml_files(iso_mount, target):
926 grml_target = target + '/grml/'
927 execute(mkdir, grml_target)
929 for myfile in 'grml-cheatcodes.txt', 'grml-version', 'LICENSE.txt', 'md5sums', 'README.txt':
930 grml_file = search_file(myfile, iso_mount)
931 if grml_file is None:
932 logging.warn("Warning: myfile %s could not be found - can not install it", myfile)
934 logging.debug("cp %s %s" % (grml_file, grml_target + grml_file))
935 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_target + myfile])
938 grml_web_target = grml_target + '/web/'
939 execute(mkdir, grml_web_target)
941 for myfile in 'index.html', 'style.css':
942 grml_file = search_file(myfile, iso_mount)
943 if grml_file is None:
944 logging.warn("Warning: myfile %s could not be found - can not install it")
946 logging.debug("cp %s %s" % (grml_file, grml_web_target + grml_file))
947 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_web_target + myfile])
950 grml_webimg_target = grml_web_target + '/images/'
951 execute(mkdir, grml_webimg_target)
953 for myfile in 'button.png', 'favicon.png', 'linux.jpg', 'logo.png':
954 grml_file = search_file(myfile, iso_mount)
955 if grml_file is None:
956 logging.warn("Warning: myfile %s could not be found - can not install it")
958 logging.debug("cp %s %s" % (grml_file, grml_webimg_target + grml_file))
959 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_webimg_target + myfile])
963 def copy_addons(iso_mount, target):
965 addons = target + '/boot/addons/'
966 execute(mkdir, addons)
968 # grub all-in-one image
969 allinoneimg = search_file('allinone.img', iso_mount)
970 if allinoneimg is None:
971 logging.warn("Warning: allinone.img not found - can not install it")
973 logging.debug("cp %s %s" % (allinoneimg, addons + '/allinone.img'))
974 proc = subprocess.Popen(["install", "--mode=664", allinoneimg, addons + 'allinone.img'])
978 balderimg = search_file('balder10.imz', iso_mount)
979 if balderimg is None:
980 logging.warn("Warning: balder10.imz not found - can not install it")
982 logging.debug("cp %s %s" % (balderimg, addons + '/balder10.imz'))
983 proc = subprocess.Popen(["install", "--mode=664", balderimg, addons + 'balder10.imz'])
987 memdiskimg = search_file('memdisk', iso_mount)
988 if memdiskimg is None:
989 logging.warn("Warning: memdisk not found - can not install it")
991 logging.debug("cp %s %s" % (memdiskimg, addons + '/memdisk'))
992 proc = subprocess.Popen(["install", "--mode=664", memdiskimg, addons + 'memdisk'])
996 memtestimg = search_file('memtest', iso_mount)
997 if memtestimg is None:
998 logging.warn("Warning: memtest not found - can not install it")
1000 logging.debug("cp %s %s" % (memtestimg, addons + '/memtest'))
1001 proc = subprocess.Popen(["install", "--mode=664", memtestimg, addons + 'memtest'])
1005 def copy_bootloader_files(iso_mount, target):
1008 syslinux_target = target + '/boot/syslinux/'
1009 execute(mkdir, syslinux_target)
1011 logo = search_file('logo.16', iso_mount)
1012 logging.debug("cp %s %s" % (logo, syslinux_target + 'logo.16'))
1013 proc = subprocess.Popen(["install", "--mode=664", logo, syslinux_target + 'logo.16'])
1016 for ffile in 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10':
1017 bootsplash = search_file(ffile, iso_mount)
1018 logging.debug("cp %s %s" % (bootsplash, syslinux_target + ffile))
1019 proc = subprocess.Popen(["install", "--mode=664", bootsplash, syslinux_target + ffile])
1022 grub_target = target + '/boot/grub/'
1023 execute(mkdir, grub_target)
1025 if not os.path.isfile("/usr/share/grml2usb/grub/splash.xpm.gz"):
1026 logging.critical("Error: /usr/share/grml2usb/grub/splash.xpm.gz can not be read.")
1029 logging.debug("cp /usr/share/grml2usb/grub/splash.xpm.gz %s" % grub_target + 'splash.xpm.gz')
1030 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/splash.xpm.gz',
1031 grub_target + 'splash.xpm.gz'])
1034 # grml splash in grub
1035 if os.path.isfile("/usr/share/grml2usb/grub/grml.png"):
1036 logging.debug("cp /usr/share/grml2usb/grub/grml.png to %s" % grub_target + 'grml.png')
1037 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/grml.png',
1038 grub_target + 'grml.png'])
1041 # font file for graphical bootsplash in grub
1042 if os.path.isfile("/usr/share/grub/ascii.pff"):
1043 logging.debug("cp /usr/share/grub/ascii.pff to %s" % grub_target + 'ascii.pff')
1044 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grub/ascii.pff',
1045 grub_target + 'ascii.pff'])
1048 if not os.path.isfile("/usr/share/grml2usb/grub/stage2_eltorito"):
1049 logging.critical("Error: /usr/share/grml2usb/grub/stage2_eltorito can not be read.")
1052 logging.debug("cp /usr/share/grml2usb/grub/stage2_eltorito to %s" % grub_target + 'stage2_eltorito')
1053 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/stage2_eltorito',
1054 grub_target + 'stage2_eltorito'])
1058 def install_iso_files(grml_flavour, iso_mount, device, target):
1059 """Copy files from ISO on given target"""
1062 # * make sure grml_flavour, iso_mount, target are set when the function is called, otherwise raise exception
1063 # * provide alternative search_file() if file information is stored in a config.ini file?
1064 # * catch "install: .. No space left on device" & CO
1067 logging.info("Would copy files to %s", iso_mount)
1069 elif not options.bootloaderonly:
1070 logging.info("Copying files. This might take a while....")
1071 copy_system_files(grml_flavour, iso_mount, target)
1072 copy_grml_files(iso_mount, target)
1074 if not options.skipaddons:
1075 copy_addons(iso_mount, target)
1077 if not options.copyonly:
1078 copy_bootloader_files(iso_mount, target)
1080 if not options.dryrun:
1081 handle_bootloader_config(grml_flavour, device, target)
1083 # make sure we sync filesystems before returning
1084 proc = subprocess.Popen(["sync"])
1088 def uninstall_files(device):
1089 """Get rid of all grml files on specified device
1091 @device: partition where grml2usb files should be removed from"""
1094 logging.critical("TODO: uninstalling files from %s not yet implement, sorry." % device)
1097 def identify_grml_flavour(mountpath):
1098 """Get name of grml flavour
1100 @mountpath: path where the grml ISO is mounted to
1101 @return: name of grml-flavour"""
1103 version_file = search_file('grml-version', mountpath)
1105 if version_file == "":
1106 logging.critical("Error: could not find grml-version file.")
1110 tmpfile = open(version_file, 'r')
1111 grml_info = tmpfile.readline()
1112 grml_flavour = re.match(r'[\w-]*', grml_info).group()
1116 logging.critical("Unexpected error:", sys.exc_info()[0])
1122 def handle_grub1_config(grml_flavour, install_partition, grub_target, bootopt):
1126 grub1_cfg = grub_target + 'menu.lst'
1127 logging.debug("Creating grub1 configuration file (menu.lst)")
1129 # install main configuration only *once*, no matter how many ISOs we have:
1130 if os.path.isfile(grub1_cfg):
1131 string = open(grub1_cfg).readline()
1132 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1133 if not re.match(main_identifier, string):
1134 grub1_config_file = open(grub1_cfg, 'w')
1135 logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1136 grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
1137 grub1_config_file.close()
1139 grub1_config_file = open(grub1_cfg, 'w')
1140 grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
1141 grub1_config_file.close()
1143 grub_flavour_config = True
1144 if os.path.isfile(grub1_cfg):
1145 string = open(grub1_cfg).readlines()
1146 logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1147 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1149 if flavour.match(line):
1150 grub_flavour_config = False
1152 if grub_flavour_config:
1153 grub1_config_file = open(grub1_cfg, 'a')
1154 grub1_config_file.write(generate_flavour_specific_grub1_config(grml_flavour, install_partition, bootopt))
1155 grub1_config_file.close( )
1158 def handle_grub2_config(grml_flavour, install_partition, grub_target, bootopt):
1162 grub2_cfg = grub_target + 'grub.cfg'
1163 logging.debug("Creating grub2 configuration file (grub.lst)")
1165 # install main configuration only *once*, no matter how many ISOs we have:
1166 if os.path.isfile(grub2_cfg):
1167 string = open(grub2_cfg).readline()
1168 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1169 if not re.match(main_identifier, string):
1170 grub2_config_file = open(grub2_cfg, 'w')
1171 logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1172 grub2_config_file.write(generate_main_grub2_config(grml_flavour, install_partition, bootopt))
1173 grub2_config_file.close()
1175 grub2_config_file = open(grub2_cfg, 'w')
1176 grub2_config_file.write(generate_main_grub2_config(grml_flavour, install_partition, bootopt))
1177 grub2_config_file.close()
1179 grub_flavour_config = True
1180 if os.path.isfile(grub2_cfg):
1181 string = open(grub2_cfg).readlines()
1182 logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1183 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1185 if flavour.match(line):
1186 grub_flavour_config = False
1188 if grub_flavour_config:
1189 grub2_config_file = open(grub2_cfg, 'a')
1190 grub2_config_file.write(generate_flavour_specific_grub2_config(grml_flavour, install_partition, bootopt))
1191 grub2_config_file.close( )
1194 def handle_grub_config(grml_flavour, device, target):
1197 logging.debug("Generating grub configuration")
1199 grub_target = target + '/boot/grub/'
1200 execute(mkdir, grub_target)
1202 # we have to adjust root() inside grub configuration
1203 if device[-1:].isdigit():
1204 install_grub1_partition = int(device[-1:]) - 1
1205 install_grub2_partition = device[-1:]
1207 # do NOT write "None" in kernel cmdline
1208 if options.bootoptions is None:
1211 bootopt = options.bootoptions
1214 handle_grub1_config(grml_flavour, install_grub1_partition, grub_target, bootopt)
1216 handle_grub2_config(grml_flavour, install_grub2_partition, grub_target, bootopt)
1219 def handle_syslinux_config(grml_flavour, target):
1223 # do NOT write "None" in kernel cmdline
1224 if options.bootoptions is None:
1227 bootopt = options.bootoptions
1229 logging.info("Generating syslinux configuration")
1230 syslinux_target = target + '/boot/syslinux/'
1231 # should be present via copy_bootloader_files(), but make sure it exits:
1232 execute(mkdir, syslinux_target)
1233 syslinux_cfg = syslinux_target + 'syslinux.cfg'
1235 # install main configuration only *once*, no matter how many ISOs we have:
1236 if os.path.isfile(syslinux_cfg):
1237 string = open(syslinux_cfg).readline()
1238 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1239 if not re.match(main_identifier, string):
1240 syslinux_config_file = open(syslinux_cfg, 'w')
1241 logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1242 syslinux_config_file.write(generate_main_syslinux_config(grml_flavour, bootopt))
1243 syslinux_config_file.close()
1245 syslinux_config_file = open(syslinux_cfg, 'w')
1246 syslinux_config_file.write(generate_main_syslinux_config(grml_flavour, bootopt))
1247 syslinux_config_file.close()
1249 # install flavour specific configuration only *once* as well
1250 # kind of ugly - I'm pretty sure this could be smoother...
1251 syslinux_flavour_config = True
1252 if os.path.isfile(syslinux_cfg):
1253 string = open(syslinux_cfg).readlines()
1254 logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1255 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1257 if flavour.match(line):
1258 syslinux_flavour_config = False
1260 if syslinux_flavour_config:
1261 syslinux_config_file = open(syslinux_cfg, 'a')
1262 syslinux_config_file.write(generate_flavour_specific_syslinux_config(grml_flavour, bootopt))
1263 syslinux_config_file.close( )
1265 logging.debug("Generating isolinux/syslinux splash %s" % syslinux_target + 'boot.msg')
1266 isolinux_splash = open(syslinux_target + 'boot.msg', 'w')
1267 isolinux_splash.write(generate_isolinux_splash(grml_flavour))
1268 isolinux_splash.close( )
1271 def handle_bootloader_config(grml_flavour, device, target):
1275 handle_grub_config(grml_flavour, device, target)
1277 handle_syslinux_config(grml_flavour, target)
1280 def handle_iso(iso, device):
1281 """Main logic for mounting ISOs and copying files.
1283 @iso: full path to the ISO that should be installed to the specified device
1284 @device: partition where the specified ISO should be installed to"""
1286 logging.info("Using ISO %s" % iso)
1288 if os.path.isdir(iso):
1289 logging.critical("TODO: /live/image handling not yet implemented - sorry") # TODO
1292 iso_mountpoint = tempfile.mkdtemp()
1293 register_tmpfile(iso_mountpoint)
1294 remove_iso_mountpoint = True
1296 if not os.path.isfile(iso):
1297 logging.critical("Fatal: specified ISO %s could not be read" % iso)
1302 mount(iso, iso_mountpoint, ["-o", "loop", "-t", "iso9660"])
1303 except CriticalException, error:
1304 logging.critical("Fatal: %s" % error)
1307 if os.path.isdir(device):
1308 logging.info("Specified target is a directory, not mounting therefor.")
1309 device_mountpoint = device
1310 remove_device_mountpoint = False
1313 device_mountpoint = tempfile.mkdtemp()
1314 register_tmpfile(device_mountpoint)
1315 remove_device_mountpoint = True
1317 mount(device, device_mountpoint, "")
1318 except CriticalException, error:
1319 logging.critical("Fatal: %s" % error)
1324 grml_flavour = identify_grml_flavour(iso_mountpoint)
1325 logging.info("Identified grml flavour \"%s\"." % grml_flavour)
1326 install_iso_files(grml_flavour, iso_mountpoint, device, device_mountpoint)
1328 logging.critical("Fatal: a critical error happend during execution (not a grml ISO?), giving up")
1331 if os.path.isdir(iso_mountpoint) and remove_iso_mountpoint:
1332 unmount(iso_mountpoint, "")
1333 os.rmdir(iso_mountpoint)
1334 unregister_tmpfile(iso_mountpoint)
1335 if remove_device_mountpoint:
1336 unmount(device_mountpoint, "")
1337 if os.path.isdir(device_mountpoint):
1338 os.rmdir(device_mountpoint)
1339 unregister_tmpfile(device_mountpoint)
1342 def handle_mbr(device):
1346 # if not options.mbr:
1347 # logging.info("You are NOT using the --mbr option. Consider using it if your device does not boot.")
1349 # make sure we install MBR on /dev/sdX and not /dev/sdX#
1351 # make sure we have syslinux available
1353 if not options.skipmbr:
1354 if not which("syslinux") and not options.copyonly and not options.dryrun:
1355 logging.critical('Sorry, syslinux not available. Exiting.')
1356 logging.critical('Please install syslinux or consider using the --grub option.')
1359 if not options.skipmbr:
1360 if device[-1:].isdigit():
1361 mbr_device = re.match(r'(.*?)\d*$', device).group(1)
1362 partition_number = int(device[-1:]) - 1
1365 if options.syslinuxmbr:
1366 handle_syslinux_mbr(mbr_device)
1369 install_mir_mbr('/usr/share/grml2usb/mbr/mbrmgr', mbr_device, partition_number, True)
1371 install_mir_mbr('/usr/share/grml2usb/mbr/mbrldr', mbr_device, partition_number, True)
1372 except IOError, error:
1373 logging.critical("Execution failed: %s", error)
1375 except Exception, error:
1376 logging.critical("Execution failed: %s", error)
1380 def handle_vfat(device):
1383 # make sure we have mkfs.vfat available
1384 if options.fat16 and not options.force:
1385 if not which("mkfs.vfat") and not options.copyonly and not options.dryrun:
1386 logging.critical('Sorry, mkfs.vfat not available. Exiting.')
1387 logging.critical('Please make sure to install dosfstools.')
1390 # make sure the user is aware of what he is doing
1391 f = raw_input("Are you sure you want to format the specified partition with fat16? y/N ")
1392 if f == "y" or f == "Y":
1393 logging.info("Note: you can skip this question using the option --force")
1396 except CriticalException, error:
1397 logging.critical("Execution failed: %s", error)
1402 # check for vfat filesystem
1403 if device is not None and not os.path.isdir(device):
1405 check_for_fat(device)
1406 except CriticalException, error:
1407 logging.critical("Execution failed: %s", error)
1410 if not check_for_usbdevice(device):
1411 print "Warning: the specified device %s does not look like a removable usb device." % device
1412 f = raw_input("Do you really want to continue? y/N ")
1413 if f == "y" or f == "Y":
1419 def handle_compat_warning(device):
1422 # make sure we can replace old grml2usb script and warn user when using old way of life:
1423 if device.startswith("/mnt/external") or device.startswith("/mnt/usb") and not options.force:
1424 print "Warning: the semantics of grml2usb has changed."
1425 print "Instead of using grml2usb /path/to/iso %s you might" % device
1426 print "want to use grml2usb /path/to/iso /dev/... instead."
1427 print "Please check out the grml2usb manpage for details."
1428 f = raw_input("Do you really want to continue? y/N ")
1429 if f == "y" or f == "Y":
1435 def handle_logging():
1439 FORMAT = "%(asctime)-15s %(message)s"
1440 logging.basicConfig(level=logging.DEBUG, format=FORMAT)
1442 FORMAT = "Critical: %(message)s"
1443 logging.basicConfig(level=logging.CRITICAL, format=FORMAT)
1445 FORMAT = "Info: %(message)s"
1446 logging.basicConfig(level=logging.INFO, format=FORMAT)
1449 def handle_bootloader(device):
1451 # Install bootloader only if not using the --copy-only option
1452 if options.copyonly:
1453 logging.info("Not installing bootloader and its files as requested via option copyonly.")
1455 install_bootloader(device)
1459 """Main function [make pylint happy :)]"""
1462 print os.path.basename(sys.argv[0]) + " " + PROG_VERSION
1466 parser.error("invalid usage")
1471 # make sure we have the appropriate permissions
1475 logging.info("Running in simulation mode as requested via option dry-run.")
1477 # specified arguments
1478 device = args[len(args) - 1]
1479 isos = args[0:len(args) - 1]
1481 if device[-1:].isdigit():
1482 if int(device[-1:]) > 4:
1483 logging.critical("Fatal: installation on partition number >4 not supported. (As the BIOS won't support it.)")
1486 logging.critical("Fatal: installation on raw device not supported. (As the BIOS won't support it.)")
1489 # provide upgrade path
1490 handle_compat_warning(device)
1492 # check for vfat partition
1495 # main operation (like installing files)
1497 handle_iso(iso, device)
1502 handle_bootloader(device)
1504 # finally be politely :)
1505 logging.info("Finished execution of grml2usb (%s). Have fun with your grml system." % PROG_VERSION)
1508 if __name__ == "__main__":
1511 except KeyboardInterrupt:
1512 logging.info("Received KeyboardInterrupt")
1515 ## END OF FILE #################################################################
1516 # vim:foldmethod=indent expandtab ai ft=python tw=120 fileencoding=utf-8