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("--initrd", dest="initrd", action="store", type="string",
50 help="install specified initrd instead of the default [TODO]")
51 parser.add_option("--kernel", dest="kernel", action="store", type="string",
52 help="install specified kernel instead of the default [TODO]")
53 parser.add_option("--lilo-binary", dest="lilobin", action="store", type="string",
54 help="lilo executable to be used for installing MBR")
55 parser.add_option("--mbr-manager", dest="mbrmgr", action="store_true",
56 help="enable interactive boot manager menu in MBR")
57 parser.add_option("--quiet", dest="quiet", action="store_true",
58 help="do not output anything but just errors on console")
59 parser.add_option("--skip-addons", dest="skipaddons", action="store_true",
60 help="do not install /boot/addons/ files")
61 parser.add_option("--skip-mbr", dest="skipmbr", action="store_true",
62 help="do not install a master boot record (MBR) on the device")
63 parser.add_option("--syslinux", dest="syslinux", action="store_true",
64 help="install syslinux bootloader instead of grub")
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
276 set root=(hd0,%(install_partition)s)
277 linux /boot/addons/bsd4grml/ldbsd.com
280 menuentry "Boot OS of first partition on first disk" {
285 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
288 def generate_flavour_specific_grub2_config(grml_flavour, install_partition, bootoptions):
289 """Generate grub2 configuration for use via grub.cfg
293 @grml_flavour: name of grml flavour the configuration should be generated for"""
295 local_datestamp = DATESTAMP
298 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
299 menuentry "%(grml_flavour)s" {
300 set root=(hd0,%(install_partition)s)
301 linux /boot/release/%(grml_flavour)s/linux26 apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
302 initrd /boot/release/%(grml_flavour)s/initrd.gz
305 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
306 menuentry "%(grml_flavour)s2ram" {
307 set root=(hd0,%(install_partition)s)
308 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
309 initrd /boot/release/%(grml_flavour)s/initrd.gz
312 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
313 menuentry "%(grml_flavour)s-debug" {
314 set root=(hd0,%(install_partition)s)
315 linux /boot/release/%(grml_flavour)s/linux26
316 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
319 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
320 menuentry "%(grml_flavour)s-x" {
321 set root=(hd0,%(install_partition)s)
322 linux /boot/release/%(grml_flavour)s/linux26
323 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
326 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
327 menuentry "%(grml_flavour)s-nofb" {
328 set root=(hd0,%(install_partition)s)
329 linux /boot/release/%(grml_flavour)s/linux26
330 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
333 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
334 menuentry "%(grml_flavour)s-failsafe" {
335 set root=(hd0,%(install_partition)s)
336 linux /boot/release/%(grml_flavour)s/linux26
337 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
340 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
341 menuentry "%(grml_flavour)s-forensic" {
342 set root=(hd0,%(install_partition)s)
343 linux /boot/release/%(grml_flavour)s/linux26
344 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
347 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
348 menuentry "%(grml_flavour)s-serial" {
349 set root=(hd0,%(install_partition)s)
350 linux /boot/release/%(grml_flavour)s/linux26
351 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
354 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
357 def generate_flavour_specific_grub1_config(grml_flavour, install_partition, bootoptions):
358 """Generate grub1 configuration for use via menu.lst
362 @grml_flavour: name of grml flavour the configuration should be generated for"""
364 local_datestamp = DATESTAMP
367 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
368 title %(grml_flavour)s
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 %(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)s2ram
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 toram=%(grml_flavour)s %(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-debug
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 debug boot=live initcall_debug%(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-x
384 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
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-nofb
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 video=ofonly %(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-failsafe
394 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
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-forensic
399 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
400 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
402 ## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
403 title %(grml_flavour)s-serial
404 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
405 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
407 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
410 def generate_main_grub1_config(grml_flavour, install_partition, bootoptions):
411 """Generate grub1 configuration for use via menu.lst
413 @grml_flavour: name of grml flavour the configuration should be generated for"""
415 local_datestamp = DATESTAMP
418 ## main grub1 configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
421 # color red/blue green/black
422 splashimage=(hd0,%(install_partition)s)/boot/grub/splash.xpm.gz
427 title %(grml_flavour)s - Default boot (using 1024x768 framebuffer)
428 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
429 initrd (hd0,%(install_partition)s)/boot/release/%(grml_flavour)s/initrd.gz
431 title Memory test (memtest86+)
432 kernel (hd0,%(install_partition)s)/boot/addons/memtest
434 title Grub - all in one image
435 kernel (hd0,%(install_partition)s)/boot/addons/memdisk
436 initrd (hd0,%(install_partition)s)/boot/addons/allinone.img
439 kernel (hd0,%(install_partition)s)/boot/addons/memdisk
440 initrd (hd0,%(install_partition)s)/boot/addons/balder10.imz
443 kernel (hd0,%(install_partition)s)/boot/addons/bsd4grml/ldbsd.com
445 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions, 'install_partition': install_partition } )
448 def generate_isolinux_splash(grml_flavour):
449 """Generate bootsplash for isolinux/syslinux
451 @grml_flavour: name of grml flavour the configuration should be generated for"""
453 # TODO: adjust last bootsplash line (the one following the "Some information and boot ...")
455 grml_name = grml_flavour
458 \ f17
\f\18/boot/syslinux/logo.16
460 Some information and boot options available via keys F2 - F10. http://grml.org/
462 """ % {'grml_name': grml_name} )
465 def generate_main_syslinux_config(grml_flavour, bootoptions):
466 """Generate main configuration for use in syslinux.cfg
468 @grml_flavour: name of grml flavour the configuration should be generated for
469 @bootoptions: bootoptions that should be used as a default"""
471 local_datestamp = DATESTAMP
474 ## main syslinux configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
475 # use this to control the bootup via a serial port
480 DISPLAY /boot/syslinux/boot.msg
481 F1 /boot/syslinux/boot.msg
490 F10 /boot/syslinux/f10
491 ## end of main configuration
493 ## global configuration
494 # the default option (using %(grml_flavour)s)
496 KERNEL /boot/release/%(grml_flavour)s/linux26
497 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
501 KERNEL /boot/addons/memtest
506 KERNEL /boot/addons/memdisk
507 APPEND initrd=/boot/addons/allinone.img
512 KERNEL /boot/addons/memdisk
513 APPEND initrd=/boot/addons/balder10.imz
518 KERNEL /boot/addons/bsd4grml/ldbsd.com
520 ## end of global configuration
521 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions} )
524 def generate_flavour_specific_syslinux_config(grml_flavour, bootoptions):
525 """Generate flavour specific configuration for use in syslinux.cfg
527 @grml_flavour: name of grml flavour the configuration should be generated for
528 @bootoptions: bootoptions that should be used as a default"""
530 local_datestamp = DATESTAMP
534 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
535 LABEL %(grml_flavour)s
536 KERNEL /boot/release/%(grml_flavour)s/linux26
537 APPEND initrd=/boot/release/%(grml_flavour)s/initrd.gz apm=power-off boot=live nomce vga=791 quiet module=%(grml_flavour)s %(bootoptions)s
539 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
540 LABEL %(grml_flavour)s2ram
541 KERNEL /boot/release/%(grml_flavour)s/linux26
542 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
544 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
545 LABEL %(grml_flavour)s-debug
546 KERNEL /boot/release/%(grml_flavour)s/linux26
547 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
549 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
550 LABEL %(grml_flavour)s-x
551 KERNEL /boot/release/%(grml_flavour)s/linux26
552 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
554 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
555 LABEL %(grml_flavour)s-nofb
556 KERNEL /boot/release/%(grml_flavour)s/linux26
557 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
559 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
560 LABEL %(grml_flavour)s-failsafe
561 KERNEL /boot/release/%(grml_flavour)s/linux26
562 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
564 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
565 LABEL %(grml_flavour)s-forensic
566 KERNEL /boot/release/%(grml_flavour)s/linux26
567 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
569 # flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
570 LABEL %(grml_flavour)s-serial
571 KERNEL /boot/release/%(grml_flavour)s/linux26
572 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
573 """ % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp, 'bootoptions': bootoptions} )
576 def install_grub(device):
577 """Install grub on specified device.
579 @mntpoint: mountpoint of device where grub should install its files to
580 @device: partition where grub should be installed to"""
583 logging.info("Would execute grub-install [--root-directory=mount_point] %s now.", device)
585 device_mountpoint = tempfile.mkdtemp()
586 register_tmpfile(device_mountpoint)
588 mount(device, device_mountpoint, "")
589 logging.debug("grub-install --recheck --no-floppy --root-directory=%s %s", device_mountpoint, device)
590 proc = subprocess.Popen(["grub-install", "--recheck", "--no-floppy", "--root-directory=%s" % device_mountpoint, device], stdout=file(os.devnull, "r+"))
592 if proc.returncode != 0:
593 raise Exception("error executing grub-install")
594 except CriticalException, error:
595 logging.critical("Fatal: %s" % error)
600 unmount(device_mountpoint, "")
601 os.rmdir(device_mountpoint)
602 unregister_tmpfile(device_mountpoint)
605 def install_syslinux(device):
606 """Install syslinux on specified device.
608 @device: partition where syslinux should be installed to"""
611 logging.info("Would install syslinux as bootloader on %s", device)
614 # syslinux -d boot/isolinux /dev/sdb1
615 logging.info("Installing syslinux as bootloader")
616 logging.debug("syslinux -d boot/syslinux %s" % device)
617 proc = subprocess.Popen(["syslinux", "-d", "boot/syslinux", device])
619 if proc.returncode != 0:
620 raise CriticalException("Error executing syslinux (either try --fat16 or --grub?)")
623 def install_bootloader(device):
624 """Install bootloader on specified device.
626 @device: partition where bootloader should be installed to"""
628 # by default we use grub, so install syslinux only on request
631 install_syslinux(device)
632 except CriticalException, error:
633 logging.critical("Fatal: %s" % error)
639 except CriticalException, error:
640 logging.critical("Fatal: %s" % error)
645 def install_lilo_mbr(lilo, device):
648 # to support -A for extended partitions:
649 logging.info("Activating partitions in MBR via lilo")
650 logging.debug("%s -S /dev/null -M %s ext" % (lilo, device))
651 proc = subprocess.Popen([lilo, "-S", "/dev/null", "-M", device, "ext"])
653 if proc.returncode != 0:
654 raise Exception("error executing lilo")
656 # activate partition:
657 logging.debug("%s -S /dev/null -A %s 1" % (lilo, device))
658 proc = subprocess.Popen([lilo, "-S", "/dev/null", "-A", device, "1"])
660 if proc.returncode != 0:
661 raise Exception("error executing lilo")
664 def install_syslinux_mbr(device):
667 # lilo's mbr is broken, use the one from syslinux instead:
668 if not os.path.isfile("/usr/lib/syslinux/mbr.bin"):
669 raise Exception("/usr/lib/syslinux/mbr.bin can not be read")
671 logging.info("Installing syslinux MBR")
672 logging.debug("cat /usr/lib/syslinux/mbr.bin > %s" % device)
674 # TODO -> use Popen instead?
675 retcode = subprocess.call("cat /usr/lib/syslinux/mbr.bin > "+ device, shell=True)
677 logging.critical("Error copying MBR to device (%s)" % retcode)
678 except OSError, error:
679 logging.critical("Execution failed:", error)
682 def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
683 """Installs an MBR to a device.
685 Retrieve the partition table from "device", install an MBR from
686 the "mbrtemplate" file, set the "partition" (0..3) active, and
687 install the result back to "device".
689 "device" may be the name of a file assumed to be a hard disc
690 (or USB stick) image, or something like "/dev/sdb". "partition"
691 must be a number between 0 and 3, inclusive. "mbrtemplate" must
692 be a valid MBR file of at least 440 (439 if ismirbsdmbr) bytes.
694 If "ismirbsdmbr", the partitions' active flags are not changed.
695 Instead, the MBR's default value is set accordingly.
698 logging.info("Installing default MBR")
700 if not os.path.isfile(mbrtemplate):
701 logging.critical("Error: %s can not be read." % mbrtemplate)
702 raise CriticalException("Error installing MBR (either try --syslinux-mbr or install missing file?)")
704 if (partition < 0) or (partition > 3):
705 raise ValueError("partition must be between 0 and 3")
712 tmpf = tempfile.NamedTemporaryFile()
714 logging.debug("executing: dd if='%s' of='%s' bs=512 count=1" % (device, tmpf.name))
715 proc = subprocess.Popen(["dd", "if=%s" % device, "of=%s" % tmpf.name, "bs=512", "count=1"], stderr=file(os.devnull, "r+"))
717 if proc.returncode != 0:
718 raise Exception("error executing dd (first run)")
720 logging.debug("executing: dd if=%s of=%s bs=%s count=1 conv=notrunc" % (mbrtemplate, tmpf.name, nmbrbytes))
721 proc = subprocess.Popen(["dd", "if=%s" % mbrtemplate, "of=%s" % tmpf.name, "bs=%s" % nmbrbytes, "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
723 if proc.returncode != 0:
724 raise Exception("error executing dd (second run)")
726 mbrcode = tmpf.file.read(512)
727 if len(mbrcode) < 512:
728 raise EOFError("MBR size (%d) < 512" % len(mbrcode))
731 mbrcode = mbrcode[0:439] + chr(partition) + \
732 mbrcode[440:510] + "\x55\xAA"
734 actives = ["\x00", "\x00", "\x00", "\x00"]
735 actives[partition] = "\x80"
736 mbrcode = mbrcode[0:446] + actives[0] + \
737 mbrcode[447:462] + actives[1] + \
738 mbrcode[463:478] + actives[2] + \
739 mbrcode[479:494] + actives[3] + \
740 mbrcode[495:510] + "\x55\xAA"
744 tmpf.file.write(mbrcode)
747 logging.debug("executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc" % (tmpf.name, device))
748 proc = subprocess.Popen(["dd", "if=%s" % tmpf.name, "of=%s" % device, "bs=512", "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
750 if proc.returncode != 0:
751 raise Exception("error executing dd (third run)")
755 def handle_syslinux_mbr(device):
756 """Install syslinux master boot record on given device
758 @device: device where MBR should be installed to"""
760 if not is_writeable(device):
761 raise IOError("device not writeable for user")
763 # try to use system's lilo
767 # otherwise fall back to our static version
768 from platform import architecture
769 if architecture()[0] == '64bit':
770 lilo = '/usr/share/grml2usb/lilo/lilo.static.amd64'
772 lilo = '/usr/share/grml2usb/lilo/lilo.static.i386'
773 # finally prefer a specified lilo executable
775 lilo = options.lilobin
778 raise Exception("lilo executable can not be execute")
781 logging.info("Would install MBR running lilo and using syslinux.")
784 install_lilo_mbr(lilo, device)
785 install_syslinux_mbr(device)
788 def is_writeable(device):
789 """Check if the device is writeable for the current user
791 @device: partition where bootloader should be installed to"""
795 #raise Exception("no device for checking write permissions")
797 if not os.path.exists(device):
800 return os.access(device, os.W_OK) and os.access(device, os.R_OK)
803 def mount(source, target, mount_options):
804 """Mount specified source on given target
806 @source: name of device/ISO that should be mounted
807 @target: directory where the ISO should be mounted to
808 @options: mount specific options"""
810 # note: options.dryrun does not work here, as we have to
811 # locate files and identify the grml flavour
813 for x in file('/proc/mounts').readlines():
814 if x.startswith(source):
815 raise CriticalException("Error executing mount: %s already mounted - please unmount before invoking grml2usb" % source)
817 logging.debug("mount %s %s %s" % (mount_options, source, target))
818 proc = subprocess.Popen(["mount"] + list(mount_options) + [source, target])
820 if proc.returncode != 0:
821 raise CriticalException("Error executing mount")
823 logging.debug("register_mountpoint(%s)" % target)
824 register_mountpoint(target)
827 def unmount(target, unmount_options):
828 """Unmount specified target
830 @target: target where something is mounted on and which should be unmounted
831 @options: options for umount command"""
833 # make sure we unmount only already mounted targets
834 target_unmount = False
835 mounts = open('/proc/mounts').readlines()
836 mountstring = re.compile(".*%s.*" % re.escape(target))
838 if re.match(mountstring, line):
839 target_unmount = True
841 if not target_unmount:
842 logging.debug("%s not mounted anymore" % target)
844 logging.debug("umount %s %s" % (list(unmount_options), target))
845 proc = subprocess.Popen(["umount"] + list(unmount_options) + [target])
847 if proc.returncode != 0:
848 raise Exception("Error executing umount")
850 logging.debug("unregister_mountpoint(%s)" % target)
851 unregister_mountpoint(target)
854 def check_for_usbdevice(device):
855 """Check whether the specified device is a removable USB device
857 @device: device name, like /dev/sda1 or /dev/sda
860 usbdevice = re.match(r'/dev/(.*?)\d*$', device).group(1)
861 usbdevice = os.path.realpath('/sys/class/block/' + usbdevice + '/removable')
862 if os.path.isfile(usbdevice):
863 is_usb = open(usbdevice).readline()
870 def check_for_fat(partition):
871 """Check whether specified partition is a valid VFAT/FAT16 filesystem
873 @partition: device name of partition"""
876 udev_info = subprocess.Popen(["/lib/udev/vol_id", "-t", partition],
877 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
878 filesystem = udev_info.communicate()[0].rstrip()
880 if udev_info.returncode == 2:
881 raise CriticalException("Failed to read device %s"
882 " (wrong UID/permissions or device not present?)" % partition)
884 if options.syslinux and filesystem != "vfat":
885 raise CriticalException("Partition %s does not contain a FAT16 filesystem. (Use --fat16 or run mkfs.vfat %s)" % (partition, partition))
888 raise CriticalException("Sorry, /lib/udev/vol_id not available.")
891 def mkdir(directory):
892 """Simple wrapper around os.makedirs to get shell mkdir -p behaviour"""
894 # just silently pass as it's just fine it the directory exists
895 if not os.path.isdir(directory):
897 os.makedirs(directory)
898 # pylint: disable-msg=W0704
903 def copy_system_files(grml_flavour, iso_mount, target):
906 squashfs = search_file(grml_flavour + '.squashfs', iso_mount)
908 logging.critical("Fatal: squashfs file not found")
910 squashfs_target = target + '/live/'
911 execute(mkdir, squashfs_target)
912 # use install(1) for now to make sure we can write the files afterwards as normal user as well
913 logging.debug("cp %s %s" % (squashfs, target + '/live/' + grml_flavour + '.squashfs'))
914 proc = subprocess.Popen(["install", "--mode=664", squashfs, squashfs_target + grml_flavour + ".squashfs"])
917 filesystem_module = search_file('filesystem.module', iso_mount)
918 if filesystem_module is None:
919 logging.critical("Fatal: filesystem.module not found")
921 logging.debug("cp %s %s" % (filesystem_module, squashfs_target + grml_flavour + '.module'))
922 proc = subprocess.Popen(["install", "--mode=664", filesystem_module,
923 squashfs_target + grml_flavour + '.module'])
926 release_target = target + '/boot/release/' + grml_flavour
927 execute(mkdir, release_target)
929 kernel = search_file('linux26', iso_mount)
931 logging.critical("Fatal kernel not found")
933 logging.debug("cp %s %s" % (kernel, release_target + '/linux26'))
934 proc = subprocess.Popen(["install", "--mode=664", kernel, release_target + '/linux26'])
937 initrd = search_file('initrd.gz', iso_mount)
939 logging.critical("Fatal: initrd not found")
941 logging.debug("cp %s %s" % (initrd, release_target + '/initrd.gz'))
942 proc = subprocess.Popen(["install", "--mode=664", initrd, release_target + '/initrd.gz'])
946 def copy_grml_files(iso_mount, target):
949 grml_target = target + '/grml/'
950 execute(mkdir, grml_target)
952 for myfile in 'grml-cheatcodes.txt', 'grml-version', 'LICENSE.txt', 'md5sums', 'README.txt':
953 grml_file = search_file(myfile, iso_mount)
954 if grml_file is None:
955 logging.warn("Warning: myfile %s could not be found - can not install it", myfile)
957 logging.debug("cp %s %s" % (grml_file, grml_target + grml_file))
958 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_target + myfile])
961 grml_web_target = grml_target + '/web/'
962 execute(mkdir, grml_web_target)
964 for myfile in 'index.html', 'style.css':
965 grml_file = search_file(myfile, iso_mount)
966 if grml_file is None:
967 logging.warn("Warning: myfile %s could not be found - can not install it")
969 logging.debug("cp %s %s" % (grml_file, grml_web_target + grml_file))
970 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_web_target + myfile])
973 grml_webimg_target = grml_web_target + '/images/'
974 execute(mkdir, grml_webimg_target)
976 for myfile in 'button.png', 'favicon.png', 'linux.jpg', 'logo.png':
977 grml_file = search_file(myfile, iso_mount)
978 if grml_file is None:
979 logging.warn("Warning: myfile %s could not be found - can not install it")
981 logging.debug("cp %s %s" % (grml_file, grml_webimg_target + grml_file))
982 proc = subprocess.Popen(["install", "--mode=664", grml_file, grml_webimg_target + myfile])
986 def copy_addons(iso_mount, target):
988 addons = target + '/boot/addons/'
989 execute(mkdir, addons)
991 # grub all-in-one image
992 allinoneimg = search_file('allinone.img', iso_mount)
993 if allinoneimg is None:
994 logging.warn("Warning: allinone.img not found - can not install it")
996 logging.debug("cp %s %s" % (allinoneimg, addons + '/allinone.img'))
997 proc = subprocess.Popen(["install", "--mode=664", allinoneimg, addons + 'allinone.img'])
1001 bsdimg = search_file('bsd4grml', iso_mount)
1003 logging.warn("Warning: bsd4grml not found - can not install it")
1005 logging.debug("cp -a %s %s" % (bsdimg, addons + '/'))
1006 proc = subprocess.Popen(["cp", "-a", bsdimg, addons + '/'])
1010 balderimg = search_file('balder10.imz', iso_mount)
1011 if balderimg is None:
1012 logging.warn("Warning: balder10.imz not found - can not install it")
1014 logging.debug("cp %s %s" % (balderimg, addons + '/balder10.imz'))
1015 proc = subprocess.Popen(["install", "--mode=664", balderimg, addons + 'balder10.imz'])
1019 memdiskimg = search_file('memdisk', iso_mount)
1020 if memdiskimg is None:
1021 logging.warn("Warning: memdisk not found - can not install it")
1023 logging.debug("cp %s %s" % (memdiskimg, addons + '/memdisk'))
1024 proc = subprocess.Popen(["install", "--mode=664", memdiskimg, addons + 'memdisk'])
1028 memtestimg = search_file('memtest', iso_mount)
1029 if memtestimg is None:
1030 logging.warn("Warning: memtest not found - can not install it")
1032 logging.debug("cp %s %s" % (memtestimg, addons + '/memtest'))
1033 proc = subprocess.Popen(["install", "--mode=664", memtestimg, addons + 'memtest'])
1037 def copy_bootloader_files(iso_mount, target):
1040 syslinux_target = target + '/boot/syslinux/'
1041 execute(mkdir, syslinux_target)
1043 logo = search_file('logo.16', iso_mount)
1044 logging.debug("cp %s %s" % (logo, syslinux_target + 'logo.16'))
1045 proc = subprocess.Popen(["install", "--mode=664", logo, syslinux_target + 'logo.16'])
1048 for ffile in 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10':
1049 bootsplash = search_file(ffile, iso_mount)
1050 logging.debug("cp %s %s" % (bootsplash, syslinux_target + ffile))
1051 proc = subprocess.Popen(["install", "--mode=664", bootsplash, syslinux_target + ffile])
1054 grub_target = target + '/boot/grub/'
1055 execute(mkdir, grub_target)
1057 if not os.path.isfile("/usr/share/grml2usb/grub/splash.xpm.gz"):
1058 logging.critical("Error: /usr/share/grml2usb/grub/splash.xpm.gz can not be read.")
1061 logging.debug("cp /usr/share/grml2usb/grub/splash.xpm.gz %s" % grub_target + 'splash.xpm.gz')
1062 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/splash.xpm.gz',
1063 grub_target + 'splash.xpm.gz'])
1066 # grml splash in grub
1067 if os.path.isfile("/usr/share/grml2usb/grub/grml.png"):
1068 logging.debug("cp /usr/share/grml2usb/grub/grml.png to %s" % grub_target + 'grml.png')
1069 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grml2usb/grub/grml.png',
1070 grub_target + 'grml.png'])
1073 # font file for graphical bootsplash in grub
1074 if os.path.isfile("/usr/share/grub/ascii.pff"):
1075 logging.debug("cp /usr/share/grub/ascii.pff to %s" % grub_target + 'ascii.pff')
1076 proc = subprocess.Popen(["install", "--mode=664", '/usr/share/grub/ascii.pff',
1077 grub_target + 'ascii.pff'])
1081 def install_iso_files(grml_flavour, iso_mount, device, target):
1082 """Copy files from ISO on given target"""
1085 # * make sure grml_flavour, iso_mount, target are set when the function is called, otherwise raise exception
1086 # * provide alternative search_file() if file information is stored in a config.ini file?
1087 # * catch "install: .. No space left on device" & CO
1090 logging.info("Would copy files to %s", iso_mount)
1092 elif not options.bootloaderonly:
1093 logging.info("Copying files. This might take a while....")
1094 copy_system_files(grml_flavour, iso_mount, target)
1095 copy_grml_files(iso_mount, target)
1097 if not options.skipaddons:
1098 copy_addons(iso_mount, target)
1100 if not options.copyonly:
1101 copy_bootloader_files(iso_mount, target)
1103 if not options.dryrun:
1104 handle_bootloader_config(grml_flavour, device, target)
1106 # make sure we sync filesystems before returning
1107 proc = subprocess.Popen(["sync"])
1111 def uninstall_files(device):
1112 """Get rid of all grml files on specified device
1114 @device: partition where grml2usb files should be removed from"""
1117 logging.critical("TODO: uninstalling files from %s not yet implement, sorry." % device)
1120 def identify_grml_flavour(mountpath):
1121 """Get name of grml flavour
1123 @mountpath: path where the grml ISO is mounted to
1124 @return: name of grml-flavour"""
1126 version_file = search_file('grml-version', mountpath)
1128 if version_file == "":
1129 logging.critical("Error: could not find grml-version file.")
1133 tmpfile = open(version_file, 'r')
1134 grml_info = tmpfile.readline()
1135 grml_flavour = re.match(r'[\w-]*', grml_info).group()
1139 logging.critical("Unexpected error:", sys.exc_info()[0])
1145 def handle_grub1_config(grml_flavour, install_partition, grub_target, bootopt):
1149 grub1_cfg = grub_target + 'menu.lst'
1150 logging.debug("Creating grub1 configuration file (menu.lst)")
1152 # install main configuration only *once*, no matter how many ISOs we have:
1153 if os.path.isfile(grub1_cfg):
1154 string = open(grub1_cfg).readline()
1155 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1156 if not re.match(main_identifier, string):
1157 grub1_config_file = open(grub1_cfg, 'w')
1158 # logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1159 grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
1160 grub1_config_file.close()
1162 grub1_config_file = open(grub1_cfg, 'w')
1163 grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
1164 grub1_config_file.close()
1166 grub_flavour_config = True
1167 if os.path.isfile(grub1_cfg):
1168 string = open(grub1_cfg).readlines()
1169 # logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1170 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1172 if flavour.match(line):
1173 grub_flavour_config = False
1175 if grub_flavour_config:
1176 grub1_config_file = open(grub1_cfg, 'a')
1177 grub1_config_file.write(generate_flavour_specific_grub1_config(grml_flavour, install_partition, bootopt))
1178 grub1_config_file.close( )
1181 def handle_grub2_config(grml_flavour, install_partition, grub_target, bootopt):
1185 grub2_cfg = grub_target + 'grub.cfg'
1186 logging.debug("Creating grub2 configuration file (grub.lst)")
1188 # install main configuration only *once*, no matter how many ISOs we have:
1189 if os.path.isfile(grub2_cfg):
1190 string = open(grub2_cfg).readline()
1191 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1192 if not re.match(main_identifier, string):
1193 grub2_config_file = open(grub2_cfg, 'w')
1194 logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1195 grub2_config_file.write(generate_main_grub2_config(grml_flavour, install_partition, bootopt))
1196 grub2_config_file.close()
1198 grub2_config_file = open(grub2_cfg, 'w')
1199 grub2_config_file.write(generate_main_grub2_config(grml_flavour, install_partition, bootopt))
1200 grub2_config_file.close()
1202 grub_flavour_config = True
1203 if os.path.isfile(grub2_cfg):
1204 string = open(grub2_cfg).readlines()
1205 logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1206 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1208 if flavour.match(line):
1209 grub_flavour_config = False
1211 if grub_flavour_config:
1212 grub2_config_file = open(grub2_cfg, 'a')
1213 grub2_config_file.write(generate_flavour_specific_grub2_config(grml_flavour, install_partition, bootopt))
1214 grub2_config_file.close( )
1217 def handle_grub_config(grml_flavour, device, target):
1220 logging.debug("Generating grub configuration")
1222 grub_target = target + '/boot/grub/'
1223 execute(mkdir, grub_target)
1225 # we have to adjust root() inside grub configuration
1226 if device[-1:].isdigit():
1227 install_grub1_partition = int(device[-1:]) - 1
1228 install_grub2_partition = device[-1:]
1230 # do NOT write "None" in kernel cmdline
1231 if options.bootoptions is None:
1234 bootopt = options.bootoptions
1237 handle_grub1_config(grml_flavour, install_grub1_partition, grub_target, bootopt)
1239 handle_grub2_config(grml_flavour, install_grub2_partition, grub_target, bootopt)
1242 def handle_syslinux_config(grml_flavour, target):
1246 # do NOT write "None" in kernel cmdline
1247 if options.bootoptions is None:
1250 bootopt = options.bootoptions
1252 logging.info("Generating syslinux configuration")
1253 syslinux_target = target + '/boot/syslinux/'
1254 # should be present via copy_bootloader_files(), but make sure it exits:
1255 execute(mkdir, syslinux_target)
1256 syslinux_cfg = syslinux_target + 'syslinux.cfg'
1258 # install main configuration only *once*, no matter how many ISOs we have:
1259 if os.path.isfile(syslinux_cfg):
1260 string = open(syslinux_cfg).readline()
1261 main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
1262 if not re.match(main_identifier, string):
1263 syslinux_config_file = open(syslinux_cfg, 'w')
1264 logging.info("Note: grml flavour %s is being installed as the default booting system." % grml_flavour)
1265 syslinux_config_file.write(generate_main_syslinux_config(grml_flavour, bootopt))
1266 syslinux_config_file.close()
1268 syslinux_config_file = open(syslinux_cfg, 'w')
1269 syslinux_config_file.write(generate_main_syslinux_config(grml_flavour, bootopt))
1270 syslinux_config_file.close()
1272 # install flavour specific configuration only *once* as well
1273 # kind of ugly - I'm pretty sure this could be smoother...
1274 syslinux_flavour_config = True
1275 if os.path.isfile(syslinux_cfg):
1276 string = open(syslinux_cfg).readlines()
1277 logging.info("Note: you can boot flavour %s using '%s' on the commandline." % (grml_flavour, grml_flavour))
1278 flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
1280 if flavour.match(line):
1281 syslinux_flavour_config = False
1283 if syslinux_flavour_config:
1284 syslinux_config_file = open(syslinux_cfg, 'a')
1285 syslinux_config_file.write(generate_flavour_specific_syslinux_config(grml_flavour, bootopt))
1286 syslinux_config_file.close( )
1288 logging.debug("Generating isolinux/syslinux splash %s" % syslinux_target + 'boot.msg')
1289 isolinux_splash = open(syslinux_target + 'boot.msg', 'w')
1290 isolinux_splash.write(generate_isolinux_splash(grml_flavour))
1291 isolinux_splash.close( )
1294 def handle_bootloader_config(grml_flavour, device, target):
1297 if options.syslinux:
1298 handle_syslinux_config(grml_flavour, target)
1300 handle_grub_config(grml_flavour, device, target)
1303 def handle_iso(iso, device):
1304 """Main logic for mounting ISOs and copying files.
1306 @iso: full path to the ISO that should be installed to the specified device
1307 @device: partition where the specified ISO should be installed to"""
1309 logging.info("Using ISO %s" % iso)
1311 if os.path.isdir(iso):
1312 logging.critical("TODO: /live/image handling not yet implemented - sorry") # TODO
1315 iso_mountpoint = tempfile.mkdtemp()
1316 register_tmpfile(iso_mountpoint)
1317 remove_iso_mountpoint = True
1319 if not os.path.isfile(iso):
1320 logging.critical("Fatal: specified ISO %s could not be read" % iso)
1325 mount(iso, iso_mountpoint, ["-o", "loop", "-t", "iso9660"])
1326 except CriticalException, error:
1327 logging.critical("Fatal: %s" % error)
1330 if os.path.isdir(device):
1331 logging.info("Specified target is a directory, not mounting therefor.")
1332 device_mountpoint = device
1333 remove_device_mountpoint = False
1336 device_mountpoint = tempfile.mkdtemp()
1337 register_tmpfile(device_mountpoint)
1338 remove_device_mountpoint = True
1340 mount(device, device_mountpoint, "")
1341 except CriticalException, error:
1342 logging.critical("Fatal: %s" % error)
1347 grml_flavour = identify_grml_flavour(iso_mountpoint)
1348 logging.info("Identified grml flavour \"%s\"." % grml_flavour)
1349 install_iso_files(grml_flavour, iso_mountpoint, device, device_mountpoint)
1351 logging.critical("Fatal: a critical error happend during execution (not a grml ISO?), giving up")
1354 if os.path.isdir(iso_mountpoint) and remove_iso_mountpoint:
1355 unmount(iso_mountpoint, "")
1356 os.rmdir(iso_mountpoint)
1357 unregister_tmpfile(iso_mountpoint)
1358 if remove_device_mountpoint:
1359 unmount(device_mountpoint, "")
1360 if os.path.isdir(device_mountpoint):
1361 os.rmdir(device_mountpoint)
1362 unregister_tmpfile(device_mountpoint)
1365 def handle_mbr(device):
1369 # if not options.mbr:
1370 # logging.info("You are NOT using the --mbr option. Consider using it if your device does not boot.")
1372 # make sure we install MBR on /dev/sdX and not /dev/sdX#
1374 # make sure we have syslinux available
1376 if not options.skipmbr:
1377 if not which("syslinux") and not options.copyonly and not options.dryrun:
1378 logging.critical('Sorry, syslinux not available. Exiting.')
1379 logging.critical('Please install syslinux or consider using the --grub option.')
1382 if not options.skipmbr:
1383 if device[-1:].isdigit():
1384 mbr_device = re.match(r'(.*?)\d*$', device).group(1)
1385 partition_number = int(device[-1:]) - 1
1388 if options.syslinuxmbr:
1389 handle_syslinux_mbr(mbr_device)
1392 install_mir_mbr('/usr/share/grml2usb/mbr/mbrmgr', mbr_device, partition_number, True)
1394 install_mir_mbr('/usr/share/grml2usb/mbr/mbrldr', mbr_device, partition_number, True)
1395 except IOError, error:
1396 logging.critical("Execution failed: %s", error)
1398 except Exception, error:
1399 logging.critical("Execution failed: %s", error)
1403 def handle_vfat(device):
1406 # make sure we have mkfs.vfat available
1407 if options.fat16 and not options.force:
1408 if not which("mkfs.vfat") and not options.copyonly and not options.dryrun:
1409 logging.critical('Sorry, mkfs.vfat not available. Exiting.')
1410 logging.critical('Please make sure to install dosfstools.')
1413 # make sure the user is aware of what he is doing
1414 f = raw_input("Are you sure you want to format the specified partition with fat16? y/N ")
1415 if f == "y" or f == "Y":
1416 logging.info("Note: you can skip this question using the option --force")
1419 except CriticalException, error:
1420 logging.critical("Execution failed: %s", error)
1425 # check for vfat filesystem
1426 if device is not None and not os.path.isdir(device):
1428 check_for_fat(device)
1429 except CriticalException, error:
1430 logging.critical("Execution failed: %s", error)
1433 if not check_for_usbdevice(device):
1434 print "Warning: the specified device %s does not look like a removable usb device." % device
1435 f = raw_input("Do you really want to continue? y/N ")
1436 if f == "y" or f == "Y":
1442 def handle_compat_warning(device):
1445 # make sure we can replace old grml2usb script and warn user when using old way of life:
1446 if device.startswith("/mnt/external") or device.startswith("/mnt/usb") and not options.force:
1447 print "Warning: the semantics of grml2usb has changed."
1448 print "Instead of using grml2usb /path/to/iso %s you might" % device
1449 print "want to use grml2usb /path/to/iso /dev/... instead."
1450 print "Please check out the grml2usb manpage for details."
1451 f = raw_input("Do you really want to continue? y/N ")
1452 if f == "y" or f == "Y":
1458 def handle_logging():
1462 FORMAT = "%(asctime)-15s %(message)s"
1463 logging.basicConfig(level=logging.DEBUG, format=FORMAT)
1465 FORMAT = "Critical: %(message)s"
1466 logging.basicConfig(level=logging.CRITICAL, format=FORMAT)
1468 FORMAT = "Info: %(message)s"
1469 logging.basicConfig(level=logging.INFO, format=FORMAT)
1472 def handle_bootloader(device):
1474 # Install bootloader only if not using the --copy-only option
1475 if options.copyonly:
1476 logging.info("Not installing bootloader and its files as requested via option copyonly.")
1478 install_bootloader(device)
1482 """Main function [make pylint happy :)]"""
1485 print os.path.basename(sys.argv[0]) + " " + PROG_VERSION
1489 parser.error("invalid usage")
1494 # make sure we have the appropriate permissions
1498 logging.info("Running in simulation mode as requested via option dry-run.")
1500 # specified arguments
1501 device = args[len(args) - 1]
1502 isos = args[0:len(args) - 1]
1504 if device[-1:].isdigit():
1505 if int(device[-1:]) > 4:
1506 logging.critical("Fatal: installation on partition number >4 not supported. (As the BIOS won't support it.)")
1509 logging.critical("Fatal: installation on raw device not supported. (As the BIOS won't support it.)")
1512 # provide upgrade path
1513 handle_compat_warning(device)
1515 # check for vfat partition
1518 # main operation (like installing files)
1520 handle_iso(iso, device)
1525 handle_bootloader(device)
1527 # finally be politely :)
1528 logging.info("Finished execution of grml2usb (%s). Have fun with your grml system." % PROG_VERSION)
1531 if __name__ == "__main__":
1534 except KeyboardInterrupt:
1535 logging.info("Received KeyboardInterrupt")
1538 ## END OF FILE #################################################################
1539 # vim:foldmethod=indent expandtab ai ft=python tw=120 fileencoding=utf-8