Copy complete Grml flavour directory instead of explicit specifing files
[grml2usb.git] / grml2usb
index 6d0f311..b3e25df 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
@@ -1,31 +1,53 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
+# pylint: disable-msg=C0302
 """
 grml2usb
 ~~~~~~~~
 
 This script installs a grml system (either a running system or ISO[s]) to a USB device
 
 """
 grml2usb
 ~~~~~~~~
 
 This script installs a grml system (either a running system or ISO[s]) to a USB device
 
-:copyright: (c) 2009 by Michael Prokop <mika@grml.org>
+:copyright: (c) 2009, 2010, 2011 by Michael Prokop <mika@grml.org>
 :license: GPL v2 or any later version
 :bugreports: http://grml.org/bugs/
 
 """
 
 :license: GPL v2 or any later version
 :bugreports: http://grml.org/bugs/
 
 """
 
-# from __future__ import with_statement
 from optparse import OptionParser
 from inspect import isroutine, isclass
 import datetime, logging, os, re, subprocess, sys, tempfile, time, os.path
 import fileinput
 import glob
 from optparse import OptionParser
 from inspect import isroutine, isclass
 import datetime, logging, os, re, subprocess, sys, tempfile, time, os.path
 import fileinput
 import glob
+import uuid
+import struct
+
+# The line following this line is patched by debian/rules and tarball.sh.
+PROG_VERSION='***UNRELEASED***'
 
 # global variables
 
 # global variables
-PROG_VERSION = "0.9.14"
 MOUNTED = set()  # register mountpoints
 TMPFILES = set() # register tmpfiles
 DATESTAMP = time.mktime(datetime.datetime.now().timetuple()) # unique identifier for syslinux.cfg
 GRML_FLAVOURS = set() # which flavours are being installed?
 MOUNTED = set()  # register mountpoints
 TMPFILES = set() # register tmpfiles
 DATESTAMP = time.mktime(datetime.datetime.now().timetuple()) # unique identifier for syslinux.cfg
 GRML_FLAVOURS = set() # which flavours are being installed?
-global GRML_DEFAULT
+GRML_DEFAULT = None
+UUID = None
+SYSLINUX_LIBS = "/usr/lib/syslinux/"
+
+def syslinux_warning(option, opt, value, opt_parser):
+    """A helper function for printing a warning about deprecated option
+    """
+    # pylint: disable-msg=W0613
+    sys.stderr.write("Note: the --syslinux option is deprecated as syslinux "
+                     "is grml2usb's default. Continuing anyway.\n")
+    setattr(opt_parser.values, option.dest, True)
+
+# if grub option is set, unset syslinux option
+def grub_option(option, opt, value, opt_parser):
+    """A helper function adjusting other option values
+    """
+    # pylint: disable-msg=W0613
+    setattr(opt_parser.values, option.dest, True)
+    setattr(opt_parser.values, 'syslinux', False)
 
 # cmdline parsing
 USAGE = "Usage: %prog [options] <[ISO[s] | /live/image]> </dev/sdX#>\n\
 
 # cmdline parsing
 USAGE = "Usage: %prog [options] <[ISO[s] | /live/image]> </dev/sdX#>\n\
@@ -37,9 +59,10 @@ grub or syslinux and root access.\n\
 Run %prog --help for usage hints, further information via: man grml2usb"
 
 # pylint: disable-msg=C0103
 Run %prog --help for usage hints, further information via: man grml2usb"
 
 # pylint: disable-msg=C0103
+# pylint: disable-msg=W0603
 parser = OptionParser(usage=USAGE)
 parser.add_option("--bootoptions", dest="bootoptions",
 parser = OptionParser(usage=USAGE)
 parser.add_option("--bootoptions", dest="bootoptions",
-                  action="store", type="string",
+                  action="append", type="string",
                   help="use specified bootoptions as default")
 parser.add_option("--bootloader-only", dest="bootloaderonly", action="store_true",
                   help="do not copy files but just install a bootloader")
                   help="use specified bootoptions as default")
 parser.add_option("--bootloader-only", dest="bootloaderonly", action="store_true",
                   help="do not copy files but just install a bootloader")
@@ -51,12 +74,11 @@ parser.add_option("--fat16", dest="fat16", action="store_true",
                   help="format specified partition with FAT16")
 parser.add_option("--force", dest="force", action="store_true",
                   help="force any actions requiring manual interaction")
                   help="format specified partition with FAT16")
 parser.add_option("--force", dest="force", action="store_true",
                   help="force any actions requiring manual interaction")
-parser.add_option("--grub", dest="grub", action="store_true",
+parser.add_option("--grub", dest="grub", action="callback",
+                  callback=grub_option,
                   help="install grub bootloader instead of (default) syslinux")
 parser.add_option("--grub-mbr", dest="grubmbr", action="store_true",
                   help="install grub into MBR instead of (default) PBR")
                   help="install grub bootloader instead of (default) syslinux")
 parser.add_option("--grub-mbr", dest="grubmbr", action="store_true",
                   help="install grub into MBR instead of (default) PBR")
-parser.add_option("--lilo-binary", dest="lilobin",  action="store", type="string",
-                  help="lilo executable to be used for installing MBR")
 parser.add_option("--mbr-menu", dest="mbrmenu", action="store_true",
                   help="enable interactive boot menu in MBR")
 parser.add_option("--quiet", dest="quiet", action="store_true",
 parser.add_option("--mbr-menu", dest="mbrmenu", action="store_true",
                   help="enable interactive boot menu in MBR")
 parser.add_option("--quiet", dest="quiet", action="store_true",
@@ -71,7 +93,8 @@ parser.add_option("--skip-mbr", dest="skipmbr", action="store_true",
                   help="do not install a master boot record (MBR) on the device")
 parser.add_option("--skip-syslinux-config", dest="skipsyslinuxconfig", action="store_true",
                   help="skip generation of syslinux configuration files")
                   help="do not install a master boot record (MBR) on the device")
 parser.add_option("--skip-syslinux-config", dest="skipsyslinuxconfig", action="store_true",
                   help="skip generation of syslinux configuration files")
-parser.add_option("--syslinux", dest="syslinux", action="store_true",
+parser.add_option("--syslinux", dest="syslinux", action="callback", default=True,
+                  callback=syslinux_warning,
                   help="install syslinux bootloader (deprecated as it's the default)")
 parser.add_option("--syslinux-mbr", dest="syslinuxmbr", action="store_true",
                   help="install syslinux master boot record (MBR) instead of default")
                   help="install syslinux bootloader (deprecated as it's the default)")
 parser.add_option("--syslinux-mbr", dest="syslinuxmbr", action="store_true",
                   help="install syslinux master boot record (MBR) instead of default")
@@ -101,7 +124,7 @@ class CriticalException(Exception):
 # arrays, e.g. in MBR creation.
 
 
 # arrays, e.g. in MBR creation.
 
 
-def array2string(a):
+def array2string(*a):
     """Convert a list of integers [0;255] to a string."""
     return struct.pack("%sB" % len(a), *a)
 
     """Convert a list of integers [0;255] to a string."""
     return struct.pack("%sB" % len(a), *a)
 
@@ -122,6 +145,8 @@ def cleanup():
     try:
         for device in MOUNTED:
             unmount(device, "")
     try:
         for device in MOUNTED:
             unmount(device, "")
+        for tmpfile in TMPFILES:
+            os.unlink(tmpfile)
     # ignore: RuntimeError: Set changed size during iteration
     except RuntimeError:
         logging.debug('caught expection RuntimeError, ignoring')
     # ignore: RuntimeError: Set changed size during iteration
     except RuntimeError:
         logging.debug('caught expection RuntimeError, ignoring')
@@ -129,7 +154,7 @@ def cleanup():
 
 def register_tmpfile(path):
     """
 
 def register_tmpfile(path):
     """
-    TODO - not implemented yet
+    register tmpfile
     """
 
     TMPFILES.add(path)
     """
 
     TMPFILES.add(path)
@@ -137,11 +162,13 @@ def register_tmpfile(path):
 
 def unregister_tmpfile(path):
     """
 
 def unregister_tmpfile(path):
     """
-    TODO - not implemented yet
+    remove registered tmpfile
     """
 
     """
 
-    if path in TMPFILES:
+    try:
         TMPFILES.remove(path)
         TMPFILES.remove(path)
+    except KeyError:
+        pass
 
 
 def register_mountpoint(target):
 
 
 def register_mountpoint(target):
@@ -178,7 +205,6 @@ def execute(f, *exec_arguments):
     the command (default) or when using --dry-run commandline option
     just displays what would be executed."""
     # usage: execute(subprocess.Popen, (["ls", "-la"]))
     the command (default) or when using --dry-run commandline option
     just displays what would be executed."""
     # usage: execute(subprocess.Popen, (["ls", "-la"]))
-    # TODO: doesn't work for proc = execute(subprocess.Popen...() -> any ideas?
     if options.dryrun:
         # pylint: disable-msg=W0141
         logging.debug('dry-run only: %s(%s)', get_function_name(f), ', '.join(map(repr, exec_arguments)))
     if options.dryrun:
         # pylint: disable-msg=W0141
         logging.debug('dry-run only: %s(%s)', get_function_name(f), ', '.join(map(repr, exec_arguments)))
@@ -212,6 +238,17 @@ def which(program):
     return None
 
 
     return None
 
 
+def get_defaults_file(iso_mount, flavour, name):
+    """get the default file for syslinux
+    """
+    bootloader_dirs = ['/boot/isolinux/', '/boot/syslinux/']
+    for directory in bootloader_dirs:
+        for name in name, \
+        "%s_%s" % (get_flavour_filename(flavour), name):
+            if os.path.isfile(iso_mount + directory + name):
+                return (directory, name)
+    return ('','')
+
 def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin'):
     """Given a search path, find file
 
 def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin'):
     """Given a search path, find file
 
@@ -220,10 +257,22 @@ def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin'):
     file_found = 0
     paths = search_path.split(os.pathsep)
     current_dir = '' # make pylint happy :)
     file_found = 0
     paths = search_path.split(os.pathsep)
     current_dir = '' # make pylint happy :)
+
+    def match_file(cwd):
+        """Helper function ffor testing if specified file exists in cwd
+
+        @cwd: current working directory
+        """
+        return  os.path.exists(os.path.join(cwd, filename))
+
     for path in paths:
     for path in paths:
+        current_dir = path
+        if match_file(current_dir):
+            file_found = 1
+            break
         # pylint: disable-msg=W0612
         for current_dir, directories, files in os.walk(path):
         # pylint: disable-msg=W0612
         for current_dir, directories, files in os.walk(path):
-            if os.path.exists(os.path.join(current_dir, filename)):
+            if match_file(current_dir):
                 file_found = 1
                 break
     if file_found:
                 file_found = 1
                 break
     if file_found:
@@ -255,258 +304,11 @@ def mkfs_fat16(device):
         raise CriticalException("error executing mkfs.vfat")
 
 
         raise CriticalException("error executing mkfs.vfat")
 
 
-def generate_main_grub2_config(grml_flavour, bootoptions):
-    """Generate grub2 configuration for use via grub.cfg
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## main grub2 configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
-set default=0
-set timeout=10
-
-insmod fat
-
-if loadfont /boot/grub/ascii.pf2 ; then
-   insmod png
-   set gfxmode=640x480
-   insmod gfxterm
-   insmod vbe
-   if terminal_output.gfxterm ; then true ; else
-    # For backward compatibility with versions of terminal.mod that don't
-    # understand terminal_output
-    terminal gfxterm
-   fi
-fi
-
-if background_image /boot/grub/grml.png ; then
-  set color_normal=black/black
-  set color_highlight=red/black
-else
-  set menu_color_normal=white/black
-  set menu_color_highlight=black/yellow
-fi
-
-menuentry "%(grml_flavour)s (default)" {
-    set gfxpayload=1024x768x16,1024x768
-    linux   /boot/release/%(flavour_filename)s/linux26 apm=power-off quiet boot=live nomce live-media-path=/live/%(grml_flavour)s/ %(bootoptions)s
-    initrd  /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-menuentry "Memory test (memtest86+)" {
-    linux16   /boot/addons/memtest
-}
-
-menuentry "Boot Grub (all in one image)" {
-    linux   /boot/addons/memdisk
-    initrd  /boot/addons/allinone.img
-}
-
-menuentry "Boot FreeDOS" {
-    linux   /boot/addons/memdisk
-    initrd  /boot/addons/balder10.imz
-}
-
-menuentry "Boot MirOS bsd4grml" {
-    multiboot /boot/addons/bsd4grml/ldbsd.com
-    module    /boot/addons/bsd4grml/bsd.rd
-    module    /boot/addons/bsd4grml/boot.1
-    module    /boot/addons/bsd4grml/boot.2
-    module    /boot/addons/bsd4grml/boot.3
-    module    /boot/addons/bsd4grml/boot.4
-    module    /boot/addons/bsd4grml/boot.5
-    module    /boot/addons/bsd4grml/boot.6
-    module    /boot/addons/bsd4grml/boot.cfg
-}
-
-menuentry "Boot OS of first partition on first disk" {
-    set root=(hd0,1)
-    chainloader +1
-}
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-        'flavour_filename': grml_flavour.replace('-', ''),
-        'bootoptions': bootoptions } )
-
-
-def generate_flavour_specific_grub2_config(grml_flavour, bootoptions):
-    """Generate grub2 configuration for use via grub.cfg
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s            - boot in default mode" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-persistent - enable persistency feature" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet persistent live-media-path=/live/%(grml_flavour)s/ %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s2ram        - copy compressed grml file to RAM" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ toram=%(grml_flavour)s.squashfs %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-debug      - enable debugging options" {
-    set gfxpayload=1024x768x16,1024x768
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ debug initcall_debug%(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-x          - start X Window System" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ startx=wm-ng %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-nofb       - disable framebuffer" {
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal video=ofonly %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-failsafe   - disable hardware detection" {
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal noautoconfig atapicd noapic noacpi acpi=off nomodules nofirewire noudev nousb nohotplug noapm nopcmcia nosmp maxcpus=0 noscsi noagp nodma ide=nodma noswap nofstab nosound nogpm nosyslog nodhcp nocpu nodisc nomodem xmodule=vesa noraid nolvm noresume selinux=0 edd=off pci=nomsi %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-menuentry "%(grml_flavour)s-forensic   - do not touch harddisks during hw recognition" {
-    set gfxpayload=1024x768x16,1024x768
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ nofstab noraid nolvm noautoconfig noswap raid=noautodetect forensic readonly %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''),
-       'bootoptions': bootoptions } )
-
-
-def generate_flavour_specific_grub1_config(grml_flavour, install_partition, bootoptions):
-    """Generate grub1 configuration for use via menu.lst
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @install_partition: partition number for use in (hd0,X)
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-persistent
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet persistent live-media-path=/live/%(grml_flavour)s/ %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s2ram
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ toram=%(grml_flavour)s.squashfs %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-debug
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ debug initcall_debug%(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-x
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ startx=wm-ng %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-nofb
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal video=ofonly %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-failsafe
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal noautoconfig atapicd noapic noacpi acpi=off nomodules nofirewire noudev nousb nohotplug noapm nopcmcia nosmp maxcpus=0 noscsi noagp nodma ide=nodma noswap nofstab nosound nogpm nosyslog nodhcp nocpu nodisc nomodem xmodule=vesa noraid nolvm noresume selinux=0 edd=off pci=nomsi %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-forensic
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce vga=791 quiet live-media-path=/live/%(grml_flavour)s/ nofstab noraid nolvm noautoconfig noswap raid=noautodetect forensic readonly %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-## flavour specific configuration for %(grml_flavour)s [grml2usb for %(grml_flavour)s: %(local_datestamp)s]
-title %(grml_flavour)s-serial
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce quiet live-media-path=/live/%(grml_flavour)s/ vga=normal video=vesafb:off console=tty1 console=ttyS0,9600n8 %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''),
-       'bootoptions': bootoptions, 'install_partition': install_partition } )
-
-
-def generate_main_grub1_config(grml_flavour, install_partition, bootoptions):
-    """Generate grub1 configuration for use via menu.lst
-
-    @grml_flavour: name of grml flavour the configuration should be generated for"""
-
-    local_datestamp = DATESTAMP
-
-    return("""\
-## main grub1 configuration - generated by grml2usb [main config generated at: %(local_datestamp)s]
-# misc options:
-timeout 30
-# color red/blue green/black
-splashimage=(hd0,%(install_partition)s)/boot/grub/splash.xpm.gz
-foreground  = 000000
-background  = FFCC33
-
-# define entries:
-title %(grml_flavour)s  - Default boot (using 1024x768 framebuffer)
-kernel (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/linux26 apm=power-off vga=791 quiet boot=live nomce live-media-path=/live/%(grml_flavour)s/ %(bootoptions)s
-initrd (hd0,%(install_partition)s)/boot/release/%(flavour_filename)s/initrd.gz
-
-title Memory test (memtest86+)
-kernel (hd0,%(install_partition)s)/boot/addons/memtest
-
-title Grub - all in one image
-kernel (hd0,%(install_partition)s)/boot/addons/memdisk
-initrd (hd0,%(install_partition)s)/boot/addons/allinone.img
-
-title FreeDOS
-kernel (hd0,%(install_partition)s)/boot/addons/memdisk
-initrd (hd0,%(install_partition)s)/boot/addons/balder10.imz
-
-title MirOS BSD
-kernel (hd0,%(install_partition)s)/boot/addons/bsd4grml/ldbsd.com
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''),
-       'bootoptions': bootoptions, 'install_partition': install_partition } )
-
-
 def generate_isolinux_splash(grml_flavour):
     """Generate bootsplash for isolinux/syslinux
 
     @grml_flavour: name of grml flavour the configuration should be generated for"""
 
 def generate_isolinux_splash(grml_flavour):
     """Generate bootsplash for isolinux/syslinux
 
     @grml_flavour: name of grml flavour the configuration should be generated for"""
 
-    # TODO: adjust last bootsplash line (the one following the "Some information and boot ...")
-
     grml_name = grml_flavour
 
     return("""\
     grml_name = grml_flavour
 
     return("""\
@@ -517,12 +319,12 @@ Some information and boot options available via keys F2 - F10. http://grml.org/
 """ % {'grml_name': grml_name} )
 
 
 """ % {'grml_name': grml_name} )
 
 
-def generate_main_syslinux_config(*args):
+def generate_main_syslinux_config(*arg):
     """Generate main configuration for use in syslinux.cfg
 
     """Generate main configuration for use in syslinux.cfg
 
-    @*args: just for backward compatibility"""
-
-    local_datestamp = DATESTAMP
+    @*arg: just for backward compatibility"""
+    # pylint: disable-msg=W0613
+    # remove warning about unused arg
 
     return("""\
 label -
 
     return("""\
 label -
@@ -593,7 +395,6 @@ def install_grub(device):
                 mount(device, device_mountpoint, "")
 
                 # If using --grub-mbr then make sure we install grub in MBR instead of PBR
                 mount(device, device_mountpoint, "")
 
                 # If using --grub-mbr then make sure we install grub in MBR instead of PBR
-                # Thanks to grub2. NOT.
                 if options.grubmbr:
                     logging.debug("Using option --grub-mbr ...")
                     if device[-1:].isdigit():
                 if options.grubmbr:
                     logging.debug("Using option --grub-mbr ...")
                     if device[-1:].isdigit():
@@ -604,15 +405,22 @@ def install_grub(device):
                     grub_device = device
 
                 logging.info("Installing grub as bootloader")
                     grub_device = device
 
                 logging.info("Installing grub as bootloader")
-                logging.debug("grub-install --recheck --no-floppy --root-directory=%s %s",
-                    device_mountpoint, grub_device)
-                proc = subprocess.Popen(["grub-install", "--recheck", "--no-floppy",
-                    "--root-directory=%s" % device_mountpoint, grub_device], stdout=file(os.devnull, "r+"))
-                proc.wait()
+                for opt in ["", "--force" ]:
+                    logging.debug("grub-install --recheck %s --no-floppy --root-directory=%s %s",
+                                  opt, device_mountpoint, grub_device)
+                    proc = subprocess.Popen(["grub-install", "--recheck", opt, "--no-floppy",
+                                             "--root-directory=%s" % device_mountpoint, grub_device],
+                                            stdout=file(os.devnull, "r+"))
+                    proc.wait()
+                    if proc.returncode == 0:
+                        break
+
                 if proc.returncode != 0:
                     # raise Exception("error executing grub-install")
                 if proc.returncode != 0:
                     # raise Exception("error executing grub-install")
-                    logging.critical("Fatal: error executing grub-install (please check the grml2usb FAQ or drop the --grub option)")
-                    logging.critical("Note:  if using grub2 consider using the --grub-mbr option because grub2's PBR feature is broken.")
+                    logging.critical("Fatal: error executing grub-install "
+                                     + "(please check the grml2usb FAQ or drop the --grub option)")
+                    logging.critical("Note:  if using grub2 consider using "
+                                     + "the --grub-mbr option as grub considers PBR problematic.")
                     cleanup()
                     sys.exit(1)
             except CriticalException, error:
                     cleanup()
                     sys.exit(1)
             except CriticalException, error:
@@ -650,21 +458,13 @@ def install_bootloader(device):
     @device: partition where bootloader should be installed to"""
 
     # by default we use grub, so install syslinux only on request
     @device: partition where bootloader should be installed to"""
 
     # by default we use grub, so install syslinux only on request
-    if options.syslinux:
-        logging.info("Note: the --syslinux option is deprecated as syslinux is grml2usb's default. Continuing anyway.")
-
     if options.grub:
     if options.grub:
-        if not which("grub-install"):
-            logging.critical("Fatal: grub-install not available (please install the grub package or use the --syslinux option)")
+        try:
+            install_grub(device)
+        except CriticalException, error:
+            logging.critical("Fatal: %s", error)
             cleanup()
             sys.exit(1)
             cleanup()
             sys.exit(1)
-        else:
-            try:
-                install_grub(device)
-            except CriticalException, error:
-                logging.critical("Fatal: %s", error)
-                cleanup()
-                sys.exit(1)
     else:
         try:
             install_syslinux(device)
     else:
         try:
             install_syslinux(device)
@@ -674,53 +474,7 @@ def install_bootloader(device):
             sys.exit(1)
 
 
             sys.exit(1)
 
 
-def execute_lilo(lilo, device):
-    """execute lilo for activating the partitions in the MBR
-
-    @lilo: path of lilo executable
-    @device: device where lilo should be executed on"""
-
-    # to support -A for extended partitions:
-    logging.info("Activating partitions in MBR via lilo")
-    logging.debug("%s -S /dev/null -M %s ext", lilo, device)
-    proc = subprocess.Popen([lilo, "-S", "/dev/null", "-M", device, "ext"])
-    proc.wait()
-    if proc.returncode != 0:
-        raise Exception("error executing lilo")
-
-    # activate partition:
-    logging.debug("%s -S /dev/null -A %s 1", lilo, device)
-    proc = subprocess.Popen([lilo, "-S", "/dev/null", "-A", device, "1"])
-    proc.wait()
-    if proc.returncode != 0:
-        raise Exception("error executing lilo")
-
-
-def install_syslinux_mbr(device):
-    """install syslinux's master boot record (MBR) on the specified device
-
-    @device: device where MBR of syslinux should be installed to"""
-
-    # make sure we have syslinux available
-    if not which("syslinux") and not options.copyonly:
-        raise Exception("syslinux not available (either install it or consider using the --grub option)")
-
-    # lilo's mbr is broken, use the one from syslinux instead:
-    if not os.path.isfile("/usr/lib/syslinux/mbr.bin"):
-        raise Exception("/usr/lib/syslinux/mbr.bin can not be read")
-
-    logging.info("Installing syslinux MBR")
-    logging.debug("cat /usr/lib/syslinux/mbr.bin > %s", device)
-    try:
-        # TODO -> use Popen instead?
-        retcode = subprocess.call("cat /usr/lib/syslinux/mbr.bin > "+ device, shell=True)
-        if retcode < 0:
-            logging.critical("Error copying MBR to device (%s)", retcode)
-    except OSError, error:
-        logging.critical("Execution failed: %s", error)
-
-
-def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
+def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     """install 'mbr' master boot record (MBR) on a device
 
     Retrieve the partition table from "device", install an MBR from the
     """install 'mbr' master boot record (MBR) on a device
 
     Retrieve the partition table from "device", install an MBR from the
@@ -748,10 +502,11 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
 
     if not os.path.isfile(mbrtemplate):
         logging.critical("Error: %s can not be read.", mbrtemplate)
 
     if not os.path.isfile(mbrtemplate):
         logging.critical("Error: %s can not be read.", mbrtemplate)
-        raise CriticalException("Error installing MBR (either try --syslinux-mbr or install missing file?)")
+        raise CriticalException("Error installing MBR (either try --syslinux-mbr or install missing file \"%s\"?)" % mbrtemplate)
 
 
-    if (partition < 0) or (partition > 3):
-        raise ValueError("partition must be between 0 and 3")
+    if partition is not None and ((partition < 0) or (partition > 3)):
+        logging.warn("Cannot activate partition %d" % partition)
+        partition = None
 
     if ismirbsdmbr:
         nmbrbytes = 439
 
     if ismirbsdmbr:
         nmbrbytes = 439
@@ -761,15 +516,16 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     tmpf = tempfile.NamedTemporaryFile()
 
     logging.debug("executing: dd if='%s' of='%s' bs=512 count=1", device, tmpf.name)
     tmpf = tempfile.NamedTemporaryFile()
 
     logging.debug("executing: dd if='%s' of='%s' bs=512 count=1", device, tmpf.name)
-    proc = subprocess.Popen(["dd", "if=%s" % device, "of=%s" % tmpf.name, "bs=512", "count=1"], stderr=file(os.devnull, "r+"))
+    proc = subprocess.Popen(["dd", "if=%s" % device, "of=%s" % tmpf.name, "bs=512", "count=1"],
+                            stderr=file(os.devnull, "r+"))
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (first run)")
 
     logging.debug("executing: dd if=%s of=%s bs=%s count=1 conv=notrunc", mbrtemplate,
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (first run)")
 
     logging.debug("executing: dd if=%s of=%s bs=%s count=1 conv=notrunc", mbrtemplate,
-        tmpf.name, nmbrbytes)
+                  tmpf.name, nmbrbytes)
     proc = subprocess.Popen(["dd", "if=%s" % mbrtemplate, "of=%s" % tmpf.name, "bs=%s" % nmbrbytes,
     proc = subprocess.Popen(["dd", "if=%s" % mbrtemplate, "of=%s" % tmpf.name, "bs=%s" % nmbrbytes,
-        "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
+                             "count=1", "conv=notrunc"], stderr=file(os.devnull, "r+"))
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (second run)")
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (second run)")
@@ -778,18 +534,19 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     if len(mbrcode) < 512:
         raise EOFError("MBR size (%d) < 512" % len(mbrcode))
 
     if len(mbrcode) < 512:
         raise EOFError("MBR size (%d) < 512" % len(mbrcode))
 
-    if ismirbsdmbr:
-        mbrcode = mbrcode[0:439] + chr(partition) + \
-          mbrcode[440:510] + "\x55\xAA"
-    else:
-        actives = ["\x00", "\x00", "\x00", "\x00"]
-        actives[partition] = "\x80"
-        mbrcode = mbrcode[0:446] + actives[0] + \
-          mbrcode[447:462] + actives[1] + \
-          mbrcode[463:478] + actives[2] + \
-          mbrcode[479:494] + actives[3] + \
-          mbrcode[495:510] + "\x55\xAA"
-
+    if partition is not None:
+        if ismirbsdmbr:
+            mbrcode = mbrcode[0:439] + chr(partition) + \
+                    mbrcode[440:510] + "\x55\xAA"
+        else:
+            actives = ["\x00", "\x00", "\x00", "\x00"]
+            actives[partition] = "\x80"
+            mbrcode = mbrcode[0:446] + actives[0] + \
+                    mbrcode[447:462] + actives[1] + \
+                    mbrcode[463:478] + actives[2] + \
+                    mbrcode[479:494] + actives[3] + \
+                    mbrcode[495:510] + "\x55\xAA"
+    
     tmpf.file.seek(0)
     tmpf.file.truncate()
     tmpf.file.write(mbrcode)
     tmpf.file.seek(0)
     tmpf.file.truncate()
     tmpf.file.write(mbrcode)
@@ -797,46 +554,13 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
 
     logging.debug("executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc", tmpf.name, device)
     proc = subprocess.Popen(["dd", "if=%s" % tmpf.name, "of=%s" % device, "bs=512", "count=1",
 
     logging.debug("executing: dd if='%s' of='%s' bs=512 count=1 conv=notrunc", tmpf.name, device)
     proc = subprocess.Popen(["dd", "if=%s" % tmpf.name, "of=%s" % device, "bs=512", "count=1",
-                            "conv=notrunc"], stderr=file(os.devnull, "r+"))
+                             "conv=notrunc"], stderr=file(os.devnull, "r+"))
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (third run)")
     del tmpf
 
 
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (third run)")
     del tmpf
 
 
-def handle_syslinux_mbr(device):
-    """Install syslinux master boot record on given device
-
-    @device: device where MBR should be installed to"""
-
-    if not is_writeable(device):
-        raise IOError("device not writeable for user")
-
-    # try to use system's lilo
-    if which("lilo"):
-        lilo = which("lilo")
-    else:
-        # otherwise fall back to our static version
-        from platform import architecture
-        if architecture()[0] == '64bit':
-            lilo = GRML2USB_BASE + '/lilo/lilo.static.amd64'
-        else:
-            lilo = GRML2USB_BASE + '/lilo/lilo.static.i386'
-    # finally prefer a specified lilo executable
-    if options.lilobin:
-        lilo = options.lilobin
-
-    if not is_exe(lilo):
-        raise Exception("lilo executable can not be execute")
-
-    if options.dryrun:
-        logging.info("Would install MBR running lilo and using syslinux.")
-        return 0
-
-    execute_lilo(lilo, device)
-    install_syslinux_mbr(device)
-
-
 def is_writeable(device):
     """Check if the device is writeable for the current user
 
 def is_writeable(device):
     """Check if the device is writeable for the current user
 
@@ -864,7 +588,8 @@ def mount(source, target, mount_options):
 
     for x in file('/proc/mounts').readlines():
         if x.startswith(source):
 
     for x in file('/proc/mounts').readlines():
         if x.startswith(source):
-            raise CriticalException("Error executing mount: %s already mounted - please unmount before invoking grml2usb" % source)
+            raise CriticalException("Error executing mount: %s already mounted - " % source
+                                    + "please unmount before invoking grml2usb")
 
     if os.path.isdir(source):
         logging.debug("Source %s is not a device, therefore not mounting.", source)
 
     if os.path.isdir(source):
         logging.debug("Source %s is not a device, therefore not mounting.", source)
@@ -933,17 +658,19 @@ def check_for_fat(partition):
 
     @partition: device name of partition"""
 
 
     @partition: device name of partition"""
 
+    if not os.access(partition, os.R_OK):
+        raise CriticalException("Failed to read device %s"
+                " (wrong UID/permissions or device/directory not present?)" % partition)
+
     try:
         udev_info = subprocess.Popen(["/sbin/blkid", "-s", "TYPE", "-o", "value", partition],
     try:
         udev_info = subprocess.Popen(["/sbin/blkid", "-s", "TYPE", "-o", "value", partition],
-                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+                                     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         filesystem = udev_info.communicate()[0].rstrip()
 
         filesystem = udev_info.communicate()[0].rstrip()
 
-        if udev_info.returncode == 2:
-            raise CriticalException("Failed to read device %s"
-                                    " (wrong UID/permissions or device/directory not present?)" % partition)
-
-        if options.syslinux and filesystem != "vfat":
-            raise CriticalException("Partition %s does not contain a FAT16 filesystem. (Use --fat16 or run mkfs.vfat %s)" % (partition, partition))
+        if filesystem != "vfat":
+            raise CriticalException(
+                    "Partition %s does not contain a FAT16 filesystem. "
+                    "(Use --fat16 or run mkfs.vfat %s)" % (partition, partition))
 
     except OSError:
         raise CriticalException("Sorry, /sbin/blkid not available (install e2fsprogs?)")
 
     except OSError:
         raise CriticalException("Sorry, /sbin/blkid not available (install e2fsprogs?)")
@@ -980,6 +707,40 @@ def exec_rsync(source, target):
         sys.exit(1)
 
 
         sys.exit(1)
 
 
+def write_uuid(target_file):
+    """Generates an returns uuid and write it to the specified file
+
+    @target_file: filename to write the uuid to
+    """
+
+    fileh = open(target_file, 'w')
+    uid = str(uuid.uuid4())
+    fileh.write(uid)
+    fileh.close()
+    return uid
+
+
+def get_uuid(target):
+    """Get the uuid of the specified target. Will generate an uuid if none exist.
+
+    @target: directory/mountpoint containing the grml layout
+    """
+
+    conf_target = target + "/conf/"
+    uuid_file_name = conf_target + "/bootid.txt"
+    if os.path.isdir(conf_target):
+        if os.path.isfile(uuid_file_name):
+            uuid_file = open(uuid_file_name, 'r')
+            uid = uuid_file.readline().strip()
+            uuid_file.close()
+            return uid
+        else:
+            return write_uuid(uuid_file_name)
+    else:
+        execute(mkdir, conf_target)
+        return write_uuid(uuid_file_name)
+
+
 def copy_system_files(grml_flavour, iso_mount, target):
     """copy grml's main files (like squashfs, kernel and initrd) to a given target
 
 def copy_system_files(grml_flavour, iso_mount, target):
     """copy grml's main files (like squashfs, kernel and initrd) to a given target
 
@@ -989,73 +750,117 @@ def copy_system_files(grml_flavour, iso_mount, target):
 
     squashfs = search_file(grml_flavour + '.squashfs', iso_mount)
     if squashfs is None:
 
     squashfs = search_file(grml_flavour + '.squashfs', iso_mount)
     if squashfs is None:
-        logging.critical("Fatal: squashfs file not found")
+        logging.critical("Fatal: squashfs file not found"
+        ", please check that your iso is not corrupt")
         raise CriticalException("error locating squashfs file")
     else:
         squashfs_target = target + '/live/' + grml_flavour + '/'
         execute(mkdir, squashfs_target)
     exec_rsync(squashfs, squashfs_target + grml_flavour + '.squashfs')
 
         raise CriticalException("error locating squashfs file")
     else:
         squashfs_target = target + '/live/' + grml_flavour + '/'
         execute(mkdir, squashfs_target)
     exec_rsync(squashfs, squashfs_target + grml_flavour + '.squashfs')
 
-    filesystem_module = search_file('filesystem.module', iso_mount)
+    for prefix in grml_flavour + "/", "":
+        filesystem_module = search_file(prefix + 'filesystem.module', iso_mount)
+        if filesystem_module:
+            break
     if filesystem_module is None:
         logging.critical("Fatal: filesystem.module not found")
         raise CriticalException("error locating filesystem.module file")
     else:
         exec_rsync(filesystem_module, squashfs_target + 'filesystem.module')
 
     if filesystem_module is None:
         logging.critical("Fatal: filesystem.module not found")
         raise CriticalException("error locating filesystem.module file")
     else:
         exec_rsync(filesystem_module, squashfs_target + 'filesystem.module')
 
-    release_target = target + '/boot/release/' + grml_flavour.replace('-', '')
-    execute(mkdir, release_target)
+    kernel = search_file('vmlinuz', iso_mount)
+    if kernel is None:
+        # compat for releases < 2011.12
+        kernel = search_file('linux26', iso_mount)
 
 
-    kernel = search_file('linux26', iso_mount)
     if kernel is None:
     if kernel is None:
-        logging.critical("Fatal kernel not found")
+        logging.critical("Fatal: kernel not found")
         raise CriticalException("error locating kernel file")
         raise CriticalException("error locating kernel file")
-    else:
-        exec_rsync(kernel, release_target + '/linux26')
 
 
-    initrd = search_file('initrd.gz', iso_mount)
-    if initrd is None:
-        logging.critical("Fatal: initrd not found")
-        raise CriticalException("error locating initrd file")
-    else:
-        exec_rsync(initrd, release_target + '/initrd.gz')
+    source = os.path.dirname(kernel) + '/'
+    dest = target + '/' + os.path.dirname(kernel).replace(iso_mount,'') + '/'
+    execute(mkdir, dest)
+    exec_rsync(source, dest)
 
 
 
 
-def copy_grml_files(iso_mount, target):
+def update_grml_versions(iso_mount, target):
+    """Update the grml version file on a cd
+    Returns true if version was updated successfully,
+    False if grml-version does not exist yet on the mountpoint
+
+    @iso_mount: string of the iso mount point
+    @target: path of the target mount point
+    """
+    grml_target = target + '/grml/'
+    target_grml_version_file = search_file('grml-version', grml_target)
+    if target_grml_version_file:
+        iso_grml_version_file = search_file('grml-version', iso_mount)
+        if not iso_grml_version_file:
+            logging.warn("Warning: %s could not be found - can not install it", iso_grml_version_file)
+            return False
+        try:
+            # read the flavours from the iso image
+            iso_versions = {}
+            iso_file = open(iso_grml_version_file, 'r')
+            for line in iso_file:
+                iso_versions[get_flavour(line)] = line.strip()
+
+            # update the existing flavours on the target
+            for line in fileinput.input([target_grml_version_file], inplace=1):
+                flavour = get_flavour(line)
+                if flavour in iso_versions.keys():
+                    print iso_versions.pop(flavour)
+                else:
+                    print line.strip()
+            fileinput.close()
+
+            target_file = open(target_grml_version_file, 'a')
+            # add the new flavours from the current iso
+            for flavour in iso_versions:
+                target_file.write("%s\n" % iso_versions[flavour])
+        except IOError:
+            logging.warn("Warning: Could not write file")
+        finally:
+            iso_file.close()
+            target_file.close()
+        return True
+    else:
+        return False
+
+def copy_grml_files(grml_flavour, iso_mount, target):
     """copy some minor grml files to a given target
 
     """copy some minor grml files to a given target
 
+    @grml_flavour: the current grml_flavour
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
     grml_target = target + '/grml/'
     execute(mkdir, grml_target)
 
     @iso_mount: path where a grml ISO is mounted on
     @target: path where grml's main files should be copied to"""
 
     grml_target = target + '/grml/'
     execute(mkdir, grml_target)
 
-    for myfile in 'grml-cheatcodes.txt', 'grml-version', 'LICENSE.txt', 'md5sums', 'README.txt':
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: myfile %s could not be found - can not install it", myfile)
-        else:
-            exec_rsync(grml_file, grml_target + myfile)
-
-    grml_web_target = grml_target + '/web/'
-    execute(mkdir, grml_web_target)
+    grml_prefixe = ["GRML", "grml"]
+    for prefix in grml_prefixe:
+      filename = "{0}/{1}/{2}".format(iso_mount, prefix, grml_flavour)
+      if os.path.exists(filename):
+        exec_rsync(filename, grml_target)
+        break
+    else:
+      logging.warn("Warning: could not find flavour directory for %s ", grml_flavour)
 
 
-    for myfile in 'index.html', 'style.css':
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: myfile %s could not be found - can not install it")
-        else:
-            exec_rsync(grml_file, grml_web_target + myfile)
 
 
-    grml_webimg_target = grml_web_target + '/images/'
-    execute(mkdir, grml_webimg_target)
+def handle_addon_copy(filename, dst, iso_mount, ignore_errors=False):
+    """handle copy of optional addons
 
 
-    for myfile in 'button.png', 'favicon.png', 'linux.jpg', 'logo.png':
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: myfile %s could not be found - can not install it")
-        else:
-            exec_rsync(grml_file, grml_webimg_target + myfile)
+    @filename: filename of the addon
+    @dst: destination directory
+    @iso_mount: location of the iso mount
+    @ignore_errors: don't report missing files
+    """
+    file_location = search_file(filename, iso_mount)
+    if file_location is None:
+        if not ignore_errors:
+            logging.warn("Warning: %s not found (that's fine if you don't need it)",  filename)
+    else:
+        exec_rsync(file_location, dst)
 
 
 def copy_addons(iso_mount, target):
 
 
 def copy_addons(iso_mount, target):
@@ -1068,25 +873,12 @@ def copy_addons(iso_mount, target):
     execute(mkdir, addons)
 
     # grub all-in-one image
     execute(mkdir, addons)
 
     # grub all-in-one image
-    allinoneimg = search_file('allinone.img', iso_mount)
-    if allinoneimg is None:
-        logging.warn("Warning: allinone.img not found (that's fine if you don't need it)")
-    else:
-        exec_rsync(allinoneimg, addons + 'allinone.img')
+    handle_addon_copy('allinone.img', addons, iso_mount)
 
     # bsd imag
 
     # bsd imag
-    bsdimg = search_file('bsd4grml', iso_mount)
-    if bsdimg is None:
-        logging.warn("Warning: bsd4grml not found (that's fine if you don't need it)")
-    else:
-        exec_rsync(bsdimg, addons + '/')
+    handle_addon_copy('bsd4grml', addons, iso_mount)
 
 
-    # freedos image
-    balderimg = search_file('balder10.imz', iso_mount)
-    if balderimg is None:
-        logging.warn("Warning: balder10.imz not found (that's fine if you don't need it)")
-    else:
-        exec_rsync(balderimg, addons + 'balder10.imz')
+    handle_addon_copy('balder10.imz', addons, iso_mount)
 
     # install hdt and pci.ids only when using syslinux (grub doesn't support it)
     if options.syslinux:
 
     # install hdt and pci.ids only when using syslinux (grub doesn't support it)
     if options.syslinux:
@@ -1101,81 +893,116 @@ def copy_addons(iso_mount, target):
             exec_rsync(picids, addons + '/pci.ids')
 
     # memdisk image
             exec_rsync(picids, addons + '/pci.ids')
 
     # memdisk image
-    memdiskimg = search_file('memdisk', iso_mount)
-    if memdiskimg is None:
-        logging.warn("Warning: memdisk not found (that's fine if you don't need it)")
-    else:
-        exec_rsync(memdiskimg, addons + 'memdisk')
+    handle_addon_copy('memdisk', addons, iso_mount)
 
     # memtest86+ image
 
     # memtest86+ image
-    memtestimg = search_file('memtest', iso_mount)
-    if memtestimg is None:
-        logging.warn("Warning: memtest not found (that's fine if you don't need it)")
-    else:
-        exec_rsync(memtestimg, addons + 'memtest')
+    handle_addon_copy('memtest', addons, iso_mount)
+
+    # gpxe.lkrn: got replaced by ipxe
+    handle_addon_copy('gpxe.lkrn', addons, iso_mount, ignore_errors=True)
+
+    # ipxe.lkrn
+    handle_addon_copy('ipxe.lkrn', addons, iso_mount)
+
+def glob_and_copy(filepattern, dst):
+    """Glob on specified filepattern and copy the result to dst
+
+    @filepattern: globbing pattern
+    @dst: target directory
+    """
+    for name in glob.glob(filepattern):
+        copy_if_exist(name, dst)
+
+def search_and_copy(filename, search_path, dst):
+    """Search for the specified filename at searchpath and copy it to dst
+
+    @filename: filename to look for
+    @search_path: base search file
+    @dst: destionation to copy the file to
+    """
+    file_location = search_file(filename, search_path)
+    copy_if_exist(file_location, dst)
+
+def copy_if_exist(filename, dst):
+    """Copy filename to dst if filename is set.
 
 
+    @filename: a filename
+    @dst: dst file
+    """
+    if filename and (os.path.isfile(filename) or os.path.isdir(filename)):
+        exec_rsync(filename, dst)
 
 
-def copy_bootloader_files(iso_mount, target):
-    """copy grml's bootloader files to a given target
+def copy_bootloader_files(iso_mount, target, grml_flavour):
+    """Copy grml's bootloader files to a given target
 
     @iso_mount: path where a grml ISO is mounted on
 
     @iso_mount: path where a grml ISO is mounted on
-    @target: path where grml's main files should be copied to"""
+    @target: path where grml's main files should be copied to
+    @grml_flavour: name of the current processed grml_flavour
+    """
 
     syslinux_target = target + '/boot/syslinux/'
     execute(mkdir, syslinux_target)
 
 
     syslinux_target = target + '/boot/syslinux/'
     execute(mkdir, syslinux_target)
 
+    grub_target = target + '/boot/grub/'
+    execute(mkdir, grub_target)
+
     logo = search_file('logo.16', iso_mount)
     exec_rsync(logo, syslinux_target + 'logo.16')
 
     logo = search_file('logo.16', iso_mount)
     exec_rsync(logo, syslinux_target + 'logo.16')
 
-    for ffile in ['f%d' % number for number in range(1,11) ]:
-        bootsplash = search_file(ffile, iso_mount)
-        exec_rsync(bootsplash, syslinux_target + ffile)
+    bootx64_efi = search_file('bootx64.efi', iso_mount)
+    if bootx64_efi:
+        mkdir(target + '/efi/boot/')
+        exec_rsync(bootx64_efi, target + '/efi/boot/bootx64.efi')
+
+    efi_img = search_file('efi.img', iso_mount)
+    if efi_img:
+        mkdir(target + '/boot/')
+        exec_rsync(efi_img, target + '/boot/efi.img')
+
+    for ffile in ['f%d' % number for number in range(1, 11) ]:
+        search_and_copy(ffile, iso_mount, syslinux_target + ffile)
+
+    loopback_cfg = search_file("loopback.cfg", iso_mount)
+    if loopback_cfg:
+        directory = os.path.dirname(loopback_cfg)
+        directory = directory.replace(iso_mount, "")
+        mkdir(os.path.join(target, directory))
+        exec_rsync(loopback_cfg, target + os.path.sep + directory)
 
     # avoid the "file is read only, overwrite anyway (y/n) ?" question
     # of mtools by syslinux ("mmove -D o -D O s:/ldlinux.sys $target_file")
     if os.path.isfile(syslinux_target + 'ldlinux.sys'):
         os.unlink(syslinux_target + 'ldlinux.sys')
 
 
     # avoid the "file is read only, overwrite anyway (y/n) ?" question
     # of mtools by syslinux ("mmove -D o -D O s:/ldlinux.sys $target_file")
     if os.path.isfile(syslinux_target + 'ldlinux.sys'):
         os.unlink(syslinux_target + 'ldlinux.sys')
 
-    if not search_file('default.cfg', iso_mount + '/boot/isolinux/'):
+    (source_dir, name) = get_defaults_file(iso_mount, grml_flavour, "default.cfg")
+    (source_dir, defaults_file) = get_defaults_file(iso_mount, grml_flavour, "grml.cfg")
+
+    if not source_dir:
         logging.critical("Fatal: file default.cfg could not be found.")
         logging.critical("Note:  this grml2usb version requires an ISO generated by grml-live >=0.9.24 ...")
         logging.critical("       ... either use grml releases >=2009.10 or switch to an older grml2usb version.")
         logging.critical("Fatal: file default.cfg could not be found.")
         logging.critical("Note:  this grml2usb version requires an ISO generated by grml-live >=0.9.24 ...")
         logging.critical("       ... either use grml releases >=2009.10 or switch to an older grml2usb version.")
-        logging.critical("       Please visit http://grml.org/grml2usb/#grml2usb-compat for further information.")
         raise
 
         raise
 
-    for filename in 'default.cfg', 'distri.cfg', \
-                    'grml.cfg', 'grml.png', 'hd.cfg', 'isolinux.cfg', 'isolinux.bin', \
-                    'isoprompt.cfg', 'options.cfg', \
-                    'prompt.cfg', 'vesamenu.c32', 'vesamenu.cfg', 'grml.png':
-        path = search_file(filename, iso_mount + '/boot/isolinux/')
-        exec_rsync(path, syslinux_target + filename)
+    if not os.path.exists(iso_mount + '/boot/grub/footer.cfg'):
+        logging.warning("Warning: Grml releases older than 2011.12 support only one flavour in grub.")
 
 
-    # copy the addons_*.cfg file to the new syslinux directory
-    for filename in glob.glob(iso_mount + '/boot/isolinux/' + 'addon*.cfg'):
-        exec_rsync(filename, syslinux_target)
+    for expr in name, 'distri.cfg', \
+        defaults_file, 'grml.png', 'hd.cfg', 'isolinux.cfg', 'isolinux.bin', \
+        'isoprompt.cfg', 'options.cfg', \
+        'prompt.cfg', 'vesamenu.cfg', 'grml.png', '*.c32':
+        glob_and_copy(iso_mount + source_dir + expr, syslinux_target)
 
 
-    path = search_file('hidden.cfg', iso_mount + '/boot/isolinux/')
-    exec_rsync(path, syslinux_target + "new_" + 'hidden.cfg')
+    for filename in glob.glob1(syslinux_target, "*.c32"):
+        copy_if_exist(os.path.join(SYSLINUX_LIBS, filename), syslinux_target)
 
 
 
 
-    grub_target = target + '/boot/grub/'
-    execute(mkdir, grub_target)
-
-    if not os.path.isfile(GRML2USB_BASE + "/grub/splash.xpm.gz"):
-        logging.critical("Error: %s/grub/splash.xpm.gz can not be read.", GRML2USB_BASE)
-        logging.critical("Please make sure you've installed the grml2usb (Debian) package!")
-        raise
-    else:
-        exec_rsync(GRML2USB_BASE + '/grub/splash.xpm.gz', grub_target + 'splash.xpm.gz')
-
-    # grml splash in grub
-    if os.path.isfile(GRML2USB_BASE + "/grub/grml.png"):
-        exec_rsync(GRML2USB_BASE + '/grub/grml.png', grub_target + 'grml.png')
+    # copy the addons_*.cfg file to the new syslinux directory
+    glob_and_copy(iso_mount + source_dir + 'addon*.cfg', syslinux_target)
 
 
-    # font file for graphical bootsplash in grub
-    if os.path.isfile("/usr/share/grub/ascii.pf2"):
-        exec_rsync('/usr/share/grub/ascii.pf2', grub_target + 'ascii.pf2')
+    search_and_copy('hidden.cfg', iso_mount + source_dir, syslinux_target + "new_" + 'hidden.cfg')
 
 
+    # copy all grub files from ISO
+    glob_and_copy(iso_mount + '/boot/grub/*', grub_target)
 
 def install_iso_files(grml_flavour, iso_mount, device, target):
     """Copy files from ISO to given target
 
 def install_iso_files(grml_flavour, iso_mount, device, target):
     """Copy files from ISO to given target
@@ -1185,30 +1012,27 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
     @device: device/partition where bootloader should be installed to
     @target: path where grml's main files should be copied to"""
 
     @device: device/partition where bootloader should be installed to
     @target: path where grml's main files should be copied to"""
 
-    # TODO => several improvements:
-    # * make sure grml_flavour, iso_mount, target are set when the function is called, otherwise raise exception
-    # * provide alternative search_file() if file information is stored in a config.ini file?
-    # * catch "install: .. No space left on device" & CO
-
+    global GRML_DEFAULT
+    GRML_DEFAULT = GRML_DEFAULT or grml_flavour
     if options.dryrun:
         return 0
     elif not options.bootloaderonly:
         logging.info("Copying files. This might take a while....")
         try:
             copy_system_files(grml_flavour, iso_mount, target)
     if options.dryrun:
         return 0
     elif not options.bootloaderonly:
         logging.info("Copying files. This might take a while....")
         try:
             copy_system_files(grml_flavour, iso_mount, target)
-            copy_grml_files(iso_mount, target)
+            copy_grml_files(grml_flavour, iso_mount, target)
         except CriticalException, error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
     if not options.skipaddons:
         except CriticalException, error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
     if not options.skipaddons:
-        if grml_flavour.endswith('-small'):
-            logging.info("Note: grml-small doesn't provide any addons, not installing them therefore.")
+        if not search_file('addons', iso_mount):
+            logging.info("Could not find addons, therefore not installing.")
         else:
             copy_addons(iso_mount, target)
 
     if not options.copyonly:
         else:
             copy_addons(iso_mount, target)
 
     if not options.copyonly:
-        copy_bootloader_files(iso_mount, target)
+        copy_bootloader_files(iso_mount, target, grml_flavour)
 
         if not options.dryrun:
             handle_bootloader_config(grml_flavour, device, target)
 
         if not options.dryrun:
             handle_bootloader_config(grml_flavour, device, target)
@@ -1218,14 +1042,10 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
     proc.wait()
 
 
     proc.wait()
 
 
-def uninstall_files(device):
-    """Get rid of all grml files on specified device
-
-    @device: partition where grml2usb files should be removed from"""
-
-    # TODO - not implemented yet
-    logging.critical("TODO: uninstalling files from %s not yet implement, sorry.", device)
-
+def get_flavour(flavour_str):
+    """Returns the flavour of a grml version string
+    """
+    return re.match(r'[\w-]*', flavour_str).group()
 
 def identify_grml_flavour(mountpath):
     """Get name of grml flavour
 
 def identify_grml_flavour(mountpath):
     """Get name of grml flavour
@@ -1239,134 +1059,36 @@ def identify_grml_flavour(mountpath):
         logging.critical("Error: could not find grml-version file.")
         raise
 
         logging.critical("Error: could not find grml-version file.")
         raise
 
+    flavours = []
+    tmpfile = None
     try:
         tmpfile = open(version_file, 'r')
     try:
         tmpfile = open(version_file, 'r')
-        grml_info = tmpfile.readline()
-        grml_flavour = re.match(r'[\w-]*', grml_info).group()
-    except TypeError:
+        for line in tmpfile.readlines():
+            flavours.append(get_flavour(line))
+    except TypeError, e:
         raise
     except Exception, e:
         logging.critical("Unexpected error: %s", e)
         raise
         raise
     except Exception, e:
         logging.critical("Unexpected error: %s", e)
         raise
+    finally:
+        if tmpfile:
+            tmpfile.close()
 
 
-    return grml_flavour
-
-
-def modify_grub_config(filename):
-    if options.removeoption:
-        regexe = []
-        for regex in options.removeoption:
-            regexe.append(re.compile(r'%s' % regex))
-
-        option_re = re.compile(r'(.*/boot/release/.*linux26.*)')
-
-        for line in fileinput.input(filename, inplace=1):
-            if regexe and option_re.search(line):
-                for regex in regexe:
-                    line = regex.sub(' ', line)
-
-            sys.stdout.write(line)
-
-        fileinput.close()
-
-def handle_grub1_config(grml_flavour, install_partition, grub_target, bootopt):
-    """Main handler for generating grub1 configuration
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @install_partition: partition number for use in (hd0,X)
-    @grub_target: path of grub's configuration files
-    @bootoptions: additional bootoptions that should be used by default"""
-
-    # grub1 config
-    grub1_cfg = grub_target + 'menu.lst'
-    logging.debug("Creating grub1 configuration file (menu.lst)")
-
-    # install main configuration only *once*, no matter how many ISOs we have:
-    if os.path.isfile(grub1_cfg):
-        string = open(grub1_cfg).readline()
-        main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
-        if not re.match(main_identifier, string):
-            grub1_config_file = open(grub1_cfg, 'w')
-            grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
-            grub1_config_file.close()
-    else:
-        grub1_config_file = open(grub1_cfg, 'w')
-        grub1_config_file.write(generate_main_grub1_config(grml_flavour, install_partition, bootopt))
-        grub1_config_file.close()
-
-    grub_flavour_config = True
-    if os.path.isfile(grub1_cfg):
-        string = open(grub1_cfg).readlines()
-        flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
-        for line in string:
-            if flavour.match(line):
-                grub_flavour_config = False
-
-    if grub_flavour_config:
-        grub1_config_file = open(grub1_cfg, 'a')
-        grub1_config_file.write(generate_flavour_specific_grub1_config(grml_flavour, install_partition, bootopt))
-        grub1_config_file.close()
-
-    modify_grub_config(grub1_cfg)
-
-    # make sure grub.conf isn't a symlink but a plain file instead,
-    # otherwise it will break on FAT16 filesystems
-    # this works around grub-install of (at least) Fedora 10
-    if os.path.isfile(grub1_cfg):
-        grubconf = grub_target + 'grub.conf'
-        if not os.path.islink(grubconf):
-            import shutil
-            shutil.copyfile(grub1_cfg, grub_target + 'grub.conf')
-
-def handle_grub2_config(grml_flavour, grub_target, bootopt):
-    """Main handler for generating grub2 configuration
-
-    @grml_flavour: name of grml flavour the configuration should be generated for
-    @grub_target: path of grub's configuration files
-    @bootoptions: additional bootoptions that should be used by default"""
+    return flavours
 
 
-    # grub2 config
-    grub2_cfg = grub_target + 'grub.cfg'
-    logging.debug("Creating grub2 configuration file (grub.lst)")
 
 
-    global GRML_DEFAULT
+def get_bootoptions(grml_flavour):
+    """Returns bootoptions for specific flavour
 
 
-    # install main configuration only *once*, no matter how many ISOs we have:
-    grub_flavour_is_default = False
-    if os.path.isfile(grub2_cfg):
-        string = open(grub2_cfg).readline()
-        main_identifier = re.compile(".*main config generated at: %s.*" % re.escape(str(DATESTAMP)))
-        if not re.match(main_identifier, string):
-            grub2_config_file = open(grub2_cfg, 'w')
-            GRML_DEFAULT = grml_flavour
-            grub_flavour_is_default = True
-            grub2_config_file.write(generate_main_grub2_config(grml_flavour, bootopt))
-            grub2_config_file.close()
+    @grml_flavour: name of the grml_flavour
+    """
+    # do NOT write "None" in kernel cmdline
+    if not options.bootoptions:
+        bootopt = ""
     else:
     else:
-        grub2_config_file = open(grub2_cfg, 'w')
-        GRML_DEFAULT = grml_flavour
-        grub_flavour_is_default = True
-        grub2_config_file.write(generate_main_grub2_config(grml_flavour, bootopt))
-        grub2_config_file.close()
-
-    # install flavour specific configuration only *once* as well
-    grub_flavour_config = True
-    if os.path.isfile(grub2_cfg):
-        string = open(grub2_cfg).readlines()
-        flavour = re.compile("grml2usb for %s: %s" % (re.escape(grml_flavour), re.escape(str(DATESTAMP))))
-        for line in string:
-            if flavour.match(line):
-                grub_flavour_config = False
-
-    if grub_flavour_config:
-        grub2_config_file = open(grub2_cfg, 'a')
-        # display only if the grml flavour isn't the default
-        if not grub_flavour_is_default:
-            GRML_FLAVOURS.add(grml_flavour)
-        grub2_config_file.write(generate_flavour_specific_grub2_config(grml_flavour, bootopt))
-        grub2_config_file.close()
-
-    modify_grub_config(grub2_cfg)
+        bootopt = " ".join(options.bootoptions)
+    bootopt = bootopt.replace("%flavour", grml_flavour)
+    return bootopt
 
 
 def handle_grub_config(grml_flavour, device, target):
 
 
 def handle_grub_config(grml_flavour, device, target):
@@ -1376,29 +1098,35 @@ def handle_grub_config(grml_flavour, device, target):
     @device: device/partition where grub should be installed to
     @target: path of grub's configuration files"""
 
     @device: device/partition where grub should be installed to
     @target: path of grub's configuration files"""
 
-    logging.debug("Generating grub configuration")
+    global UUID
+
+    logging.debug("Updating grub configuration")
 
     grub_target = target + '/boot/grub/'
 
     grub_target = target + '/boot/grub/'
-    execute(mkdir, grub_target)
 
 
-    if os.path.isdir(device):
-        install_grub1_partition = None
-    else:
-        if device[-1:].isdigit():
-            install_grub1_partition = int(device[-1:]) - 1
-        else:
-            raise CriticalException("error validating partition schema (raw device?)")
+    bootid_re = re.compile("bootid=[\w_-]+")
+    live_media_path_re = re.compile("live-media-path=[\w_/-]+")
 
 
-    # do NOT write "None" in kernel cmdline
-    if options.bootoptions is None:
-        bootopt = ""
-    else:
-        bootopt = options.bootoptions
+    bootopt = get_bootoptions(grml_flavour)
+
+    remove_regexes = []
+    option_re = re.compile(r'(.*/boot/.*(linux26|vmlinuz).*)')
+
+    if options.removeoption:
+        for regex in options.removeoption:
+            remove_regexes.append(re.compile(regex))
 
 
-    # write menu.lst
-    handle_grub1_config(grml_flavour, install_grub1_partition, grub_target, bootopt)
-    # write grub.cfg
-    handle_grub2_config(grml_flavour, grub_target, bootopt)
+    for filename in glob.glob(grub_target + '*.cfg'):
+        for line in fileinput.input(filename, inplace=1):
+            line = line.rstrip("\r\n")
+            if option_re.search(line):
+                line = bootid_re.sub('', line)
+                line = live_media_path_re.sub('', line)
+                line = line.rstrip() + r' live-media-path=/live/%s/ bootid=%s %s ' % (grml_flavour, UUID, bootopt)
+                for regex in remove_regexes:
+                    line = regex.sub(' ', line)
+            print line
+        fileinput.close()
 
 
 def initial_syslinux_config(target):
 
 
 def initial_syslinux_config(target):
@@ -1412,7 +1140,7 @@ def initial_syslinux_config(target):
         return
     data = open(filename, "w")
     data.write(generate_main_syslinux_config())
         return
     data = open(filename, "w")
     data.write(generate_main_syslinux_config())
-    data.close
+    data.close()
 
     filename = target + "hiddens.cfg"
     data = open(filename, "w")
 
     filename = target + "hiddens.cfg"
     data = open(filename, "w")
@@ -1420,6 +1148,11 @@ def initial_syslinux_config(target):
     data.close()
 
 def add_entry_if_not_present(filename, entry):
     data.close()
 
 def add_entry_if_not_present(filename, entry):
+    """Write entry into filename if entry is not already in the file
+
+    @filanme: name of the file
+    @entry: data to write to the file
+    """
     data = open(filename, "a+")
     for line in data:
         if line == entry:
     data = open(filename, "a+")
     for line in data:
         if line == entry:
@@ -1429,49 +1162,69 @@ def add_entry_if_not_present(filename, entry):
 
     data.close()
 
 
     data.close()
 
+def get_flavour_filename(flavour):
+    """Generate a iso9960 save filename out of the specified flavour
 
 
+    @flavour: grml flavour
+    """
+    return flavour.replace('-', '_')
 
 def adjust_syslinux_bootoptions(src, flavour):
 
 def adjust_syslinux_bootoptions(src, flavour):
-    append_re = re.compile("^(\s*append.*)$", re.I)
-    boot_re = re.compile("/boot/([a-zA-Z0-9_]+/)+([a-zA-Z0-9._]+)")
+    """Adjust existing bootoptions of specified syslinux config to
+    grml2usb specific ones, e.g. change the location of the kernel...
+
+    @src: config file to alter
+    @flavour: grml flavour
+    """
+
+    append_re = re.compile("^(\s*append.*/boot/.*)$", re.I)
     # flavour_re = re.compile("(label.*)(grml\w+)")
     default_re = re.compile("(default.cfg)")
     # flavour_re = re.compile("(label.*)(grml\w+)")
     default_re = re.compile("(default.cfg)")
+    bootid_re = re.compile("bootid=[\w_-]+")
+    live_media_path_re = re.compile("live-media-path=[\w_/-]+")
 
 
-    # do NOT write "None" in kernel cmdline
-    if options.bootoptions is None:
-        bootopt = ""
-    else:
-        bootopt = options.bootoptions
+    bootopt = get_bootoptions(flavour)
 
     regexe = []
     option_re = None
     if options.removeoption:
 
     regexe = []
     option_re = None
     if options.removeoption:
-        option_re = re.compile(r'/boot/release/.*/initrd.gz')
+        option_re = re.compile(r'/boot/.*/(initrd.gz|initrd.img)')
 
         for regex in options.removeoption:
             regexe.append(re.compile(r'%s' % regex))
 
     for line in fileinput.input(src, inplace=1):
 
         for regex in options.removeoption:
             regexe.append(re.compile(r'%s' % regex))
 
     for line in fileinput.input(src, inplace=1):
-        line = boot_re.sub(r'/boot/release/%s/\2 ' % flavour.replace('-', ''), line)
         # line = flavour_re.sub(r'\1 %s-\2' % flavour, line)
         line = default_re.sub(r'%s-\1' % flavour, line)
         # line = flavour_re.sub(r'\1 %s-\2' % flavour, line)
         line = default_re.sub(r'%s-\1' % flavour, line)
+        line = bootid_re.sub('', line)
+        line = live_media_path_re.sub('', line)
         line = append_re.sub(r'\1 live-media-path=/live/%s/ ' % flavour, line)
         line = append_re.sub(r'\1 boot=live %s ' % bootopt, line)
         line = append_re.sub(r'\1 live-media-path=/live/%s/ ' % flavour, line)
         line = append_re.sub(r'\1 boot=live %s ' % bootopt, line)
+        line = append_re.sub(r'\1 %s=%s ' % ("bootid", UUID), line)
         if option_re and option_re.search(line):
             for regex in regexe:
                 line = regex.sub(' ', line)
         sys.stdout.write(line)
     fileinput.close()
 
         if option_re and option_re.search(line):
             for regex in regexe:
                 line = regex.sub(' ', line)
         sys.stdout.write(line)
     fileinput.close()
 
-def adjust_labels(src, flavour):
+def adjust_labels(src, replacement):
+    """Adjust the specified labels in the syslinux config file src with
+    specified replacement
+    """
     label_re = re.compile("^(\s*label\s*) ([a-zA-Z0-9_-]+)", re.I)
     for line in fileinput.input(src, inplace=1):
     label_re = re.compile("^(\s*label\s*) ([a-zA-Z0-9_-]+)", re.I)
     for line in fileinput.input(src, inplace=1):
-        line = label_re.sub(r'\1 %s-\2' % flavour, line)
+        line = label_re.sub(replacement, line)
         sys.stdout.write(line)
     fileinput.close()
 
 
 def add_syslinux_entry(filename, grml_flavour):
         sys.stdout.write(line)
     fileinput.close()
 
 
 def add_syslinux_entry(filename, grml_flavour):
+    """Add includes for a specific grml_flavour to the specified filename
+
+    @filename: syslinux config file
+    @grml_flavour: grml flavour to add
+    """
+
     entry_filename = "option_%s.cfg" % grml_flavour
     entry = "include %s\n" % entry_filename
 
     entry_filename = "option_%s.cfg" % grml_flavour
     entry = "include %s\n" % entry_filename
 
@@ -1483,18 +1236,28 @@ def add_syslinux_entry(filename, grml_flavour):
     data.close()
 
 def modify_filenames(grml_flavour, target, filenames):
     data.close()
 
 def modify_filenames(grml_flavour, target, filenames):
+    """Replace the standard filenames with the new ones
+
+    @grml_flavour: grml-flavour strin
+    @target: directory where the files are located
+    @filenames: list of filenames to alter
+    """
     grml_filename = grml_flavour.replace('-', '_')
     for filename in filenames:
         old_filename = "%s/%s" % (target, filename)
         new_filename = "%s/%s_%s" % (target, grml_filename, filename)
         os.rename(old_filename, new_filename)
     grml_filename = grml_flavour.replace('-', '_')
     for filename in filenames:
         old_filename = "%s/%s" % (target, filename)
         new_filename = "%s/%s_%s" % (target, grml_filename, filename)
         os.rename(old_filename, new_filename)
-        adjust_syslinux_bootoptions(new_filename, grml_flavour)
 
 
 def remove_default_entry(filename):
 
 
 def remove_default_entry(filename):
+    """Remove the default entry from specified syslinux file
+
+    @filename: syslinux config file
+    """
     default_re = re.compile("^(\s*menu\s*default\s*)$", re.I)
     for line in fileinput.input(filename, inplace=1):
     default_re = re.compile("^(\s*menu\s*default\s*)$", re.I)
     for line in fileinput.input(filename, inplace=1):
-        if default_re.match(line): continue
+        if default_re.match(line):
+            continue
         sys.stdout.write(line)
     fileinput.close()
 
         sys.stdout.write(line)
     fileinput.close()
 
@@ -1505,22 +1268,14 @@ def handle_syslinux_config(grml_flavour, target):
     @grml_flavour: name of grml flavour the configuration should be generated for
     @target: path of syslinux's configuration files"""
 
     @grml_flavour: name of grml flavour the configuration should be generated for
     @target: path of syslinux's configuration files"""
 
-    # do NOT write "None" in kernel cmdline
-    if options.bootoptions is None:
-        bootopt = ""
-    else:
-        bootopt = options.bootoptions
-
     logging.debug("Generating syslinux configuration")
     syslinux_target = target + '/boot/syslinux/'
     # should be present via  copy_bootloader_files(), but make sure it exits:
     execute(mkdir, syslinux_target)
     syslinux_cfg = syslinux_target + 'syslinux.cfg'
 
     logging.debug("Generating syslinux configuration")
     syslinux_target = target + '/boot/syslinux/'
     # should be present via  copy_bootloader_files(), but make sure it exits:
     execute(mkdir, syslinux_target)
     syslinux_cfg = syslinux_target + 'syslinux.cfg'
 
-    global GRML_DEFAULT
 
     # install main configuration only *once*, no matter how many ISOs we have:
 
     # install main configuration only *once*, no matter how many ISOs we have:
-    syslinux_flavour_is_default = False
     syslinux_config_file = open(syslinux_cfg, 'w')
     syslinux_config_file.write("TIMEOUT 300\n")
     syslinux_config_file.write("include vesamenu.cfg\n")
     syslinux_config_file = open(syslinux_cfg, 'w')
     syslinux_config_file.write("TIMEOUT 300\n")
     syslinux_config_file.write("include vesamenu.cfg\n")
@@ -1531,24 +1286,25 @@ def handle_syslinux_config(grml_flavour, target):
     prompt_name.close()
 
     initial_syslinux_config(syslinux_target)
     prompt_name.close()
 
     initial_syslinux_config(syslinux_target)
-    modify_filenames(grml_flavour, syslinux_target, ['grml.cfg', 'default.cfg'])
+    flavour_filename = grml_flavour.replace('-', '_')
+
+    if search_file('default.cfg', syslinux_target):
+        modify_filenames(grml_flavour, syslinux_target, ['grml.cfg', 'default.cfg'])
 
     filename = search_file("new_hidden.cfg", syslinux_target)
 
 
 
     filename = search_file("new_hidden.cfg", syslinux_target)
 
 
-    flavour_filename = grml_flavour.replace('-', '_')
     # process hidden file
     if not search_file("hidden.cfg", syslinux_target):
         new_hidden = syslinux_target + "hidden.cfg"
         os.rename(filename, new_hidden)
         adjust_syslinux_bootoptions(new_hidden, grml_flavour)
     else:
     # process hidden file
     if not search_file("hidden.cfg", syslinux_target):
         new_hidden = syslinux_target + "hidden.cfg"
         os.rename(filename, new_hidden)
         adjust_syslinux_bootoptions(new_hidden, grml_flavour)
     else:
-        new_hidden =  "%s_hidden.cfg" % (flavour_filename)
-        new_hidden_file =  "%s/%s" % (syslinux_target, new_hidden)
+        new_hidden_file =  "%s/%s_hidden.cfg" % (syslinux_target, flavour_filename)
         os.rename(filename, new_hidden_file)
         os.rename(filename, new_hidden_file)
-        adjust_labels(new_hidden_file, flavour_filename)
-        adjust_syslinux_bootoptions(new_hidden_file, flavour_filename)
-        entry = 'include %s\n' % new_hidden
+        adjust_labels(new_hidden_file, r'\1 %s-\2' % grml_flavour)
+        adjust_syslinux_bootoptions(new_hidden_file, grml_flavour)
+        entry = 'include %s_hidden.cfg\n' % flavour_filename
         add_entry_if_not_present("%s/hiddens.cfg" % syslinux_target, entry)
 
 
         add_entry_if_not_present("%s/hiddens.cfg" % syslinux_target, entry)
 
 
@@ -1556,9 +1312,21 @@ def handle_syslinux_config(grml_flavour, target):
     new_default = "%s_default.cfg" % (flavour_filename)
     entry = 'include %s\n' % new_default
     defaults_file = '%s/defaults.cfg' % syslinux_target
     new_default = "%s_default.cfg" % (flavour_filename)
     entry = 'include %s\n' % new_default
     defaults_file = '%s/defaults.cfg' % syslinux_target
+    new_default_with_path = "%s/%s" % (syslinux_target, new_default)
+    new_grml_cfg = "%s/%s_grml.cfg" % (syslinux_target, flavour_filename)
 
     if os.path.isfile(defaults_file):
 
     if os.path.isfile(defaults_file):
-        remove_default_entry('%s/%s_default.cfg' % (syslinux_target, flavour_filename))
+
+        # remove default menu entry in menu
+        remove_default_entry(new_default_with_path)
+
+        # adjust all labels for additional isos
+        adjust_labels(new_default_with_path, r'\1 %s' % grml_flavour)
+        adjust_labels(new_grml_cfg, r'\1 %s-\2' % grml_flavour)
+
+    # always adjust bootoptions
+    adjust_syslinux_bootoptions(new_default_with_path, grml_flavour)
+    adjust_syslinux_bootoptions(new_grml_cfg, grml_flavour)
 
     add_entry_if_not_present("%s/defaults.cfg" % syslinux_target, entry)
 
 
     add_entry_if_not_present("%s/defaults.cfg" % syslinux_target, entry)
 
@@ -1572,6 +1340,8 @@ def handle_bootloader_config(grml_flavour, device, target):
     @device: device/partition where bootloader should be installed to
     @target: path of bootloader's configuration files"""
 
     @device: device/partition where bootloader should be installed to
     @target: path of bootloader's configuration files"""
 
+    global UUID
+    UUID = get_uuid(target)
     if options.skipsyslinuxconfig:
         logging.info("Skipping generation of syslinux configuration as requested.")
     else:
     if options.skipsyslinuxconfig:
         logging.info("Skipping generation of syslinux configuration as requested.")
     else:
@@ -1591,111 +1361,87 @@ def handle_bootloader_config(grml_flavour, device, target):
             sys.exit(1)
 
 
             sys.exit(1)
 
 
-def handle_dir(live_image, device):
-    """Main logic for copying files of the currently running grml system.
-
-    @live_image: directory where currently running live system resides (usually /live/image)
-    @device: partition where the specified ISO should be installed to"""
 
 
-    logging.info("Using %s as install base", live_image)
+def install(image, device):
+    """Install a grml image to the specified device
 
 
-    if os.path.isdir(device):
-        logging.info("Specified target is a directory, therefore not mounting.")
-        device_mountpoint = device
-        remove_device_mountpoint = False
+    @image: directory or is file
+    @device: partition or directory to install the device
+    """
+    iso_mountpoint = image
+    remove_image_mountpoint = False
+    if os.path.isdir(image):
+        logging.info("Using %s as install base", image)
     else:
     else:
-        device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
-        register_tmpfile(device_mountpoint)
-        remove_device_mountpoint = True
+        logging.info("Using ISO %s", image)
+        iso_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
+        register_tmpfile(iso_mountpoint)
+        remove_image_mountpoint = True
         try:
         try:
-            mount(device, device_mountpoint, "")
+            mount(image, iso_mountpoint, ["-o", "loop,ro", "-t", "iso9660"])
         except CriticalException, error:
             logging.critical("Fatal: %s", error)
         except CriticalException, error:
             logging.critical("Fatal: %s", error)
-            cleanup()
             sys.exit(1)
 
     try:
             sys.exit(1)
 
     try:
-        try:
-            grml_flavour = identify_grml_flavour(live_image)
-            logging.info("Identified grml flavour \"%s\".", grml_flavour)
-            install_iso_files(grml_flavour, live_image, device, device_mountpoint)
-        except TypeError:
-            logging.critical("Fatal: a critical error happend during execution (not a grml ISO?), giving up")
-            sys.exit(1)
+        install_grml(iso_mountpoint, device)
     finally:
     finally:
-        if remove_device_mountpoint:
+        if remove_image_mountpoint:
             try:
             try:
-                unmount(device_mountpoint, "")
-                if os.path.isdir(device_mountpoint):
-                    os.rmdir(device_mountpoint)
-                    unregister_tmpfile(device_mountpoint)
+                remove_mountpoint(iso_mountpoint)
             except CriticalException, error:
                 logging.critical("Fatal: %s", error)
                 cleanup()
 
 
             except CriticalException, error:
                 logging.critical("Fatal: %s", error)
                 cleanup()
 
 
-def handle_iso(iso, device):
-    """Main logic for mounting ISOs and copying files.
 
 
-    @iso: full path to the ISO that should be installed to the specified device
-    @device: partition where the specified ISO should be installed to"""
-
-    logging.info("Using ISO %s", iso)
-
-    iso_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
-    register_tmpfile(iso_mountpoint)
-    remove_iso_mountpoint = True
-
-    if not os.path.isfile(iso):
-        logging.critical("Fatal: specified ISO %s could not be read", iso)
-        cleanup()
-        sys.exit(1)
+def install_grml(mountpoint, device):
+    """Main logic for copying files of the currently running grml system.
 
 
-    try:
-        mount(iso, iso_mountpoint, ["-o", "loop", "-t", "iso9660"])
-    except CriticalException, error:
-        logging.critical("Fatal: %s", error)
-        sys.exit(1)
+    @mountpoin: directory where currently running live system resides (usually /live/image)
+    @device: partition where the specified ISO should be installed to"""
 
 
+    device_mountpoint = device
     if os.path.isdir(device):
     if os.path.isdir(device):
-        logging.info("Specified target is a directory, therefore not mounting.")
-        device_mountpoint = device
+        logging.info("Specified device is not a directory, therefore not mounting.")
         remove_device_mountpoint = False
         remove_device_mountpoint = False
-        # skip_mbr = True
     else:
         device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
         register_tmpfile(device_mountpoint)
         remove_device_mountpoint = True
         try:
     else:
         device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
         register_tmpfile(device_mountpoint)
         remove_device_mountpoint = True
         try:
-            mount(device, device_mountpoint, "")
+            check_for_fat(device)
+            mount(device, device_mountpoint, ['-o', 'utf8,iocharset=iso8859-1'])
         except CriticalException, error:
         except CriticalException, error:
-            logging.critical("Fatal: %s", error)
-            cleanup()
-            sys.exit(1)
-
-    try:
-        try:
-            grml_flavour = identify_grml_flavour(iso_mountpoint)
-            logging.info("Identified grml flavour \"%s\".", grml_flavour)
-            install_iso_files(grml_flavour, iso_mountpoint, device, device_mountpoint)
-        except TypeError:
-            logging.critical("Fatal: a critical error happend during execution (not a grml ISO?), giving up")
-            sys.exit(1)
-    finally:
-        if os.path.isdir(iso_mountpoint) and remove_iso_mountpoint:
-            unmount(iso_mountpoint, "")
-            os.rmdir(iso_mountpoint)
-            unregister_tmpfile(iso_mountpoint)
-        if remove_device_mountpoint:
             try:
             try:
-                unmount(device_mountpoint, "")
-                if os.path.isdir(device_mountpoint):
-                    os.rmdir(device_mountpoint)
-                    unregister_tmpfile(device_mountpoint)
+                mount(device, device_mountpoint, "")
             except CriticalException, error:
                 logging.critical("Fatal: %s", error)
             except CriticalException, error:
                 logging.critical("Fatal: %s", error)
-                cleanup()
+                raise
+    try:
+        grml_flavours = identify_grml_flavour(mountpoint)
+        for flavour in set(grml_flavours):
+            if not flavour:
+                logging.warning("No valid flavour found, please check your iso")
+            logging.info("Identified grml flavour \"%s\".", flavour)
+            install_iso_files(flavour, mountpoint, device, device_mountpoint)
+            GRML_FLAVOURS.add(flavour)
+    finally:
+        if remove_device_mountpoint:
+            remove_mountpoint(device_mountpoint)
 
 
+def remove_mountpoint(mountpoint):
+    """remove a registred mountpoint
+    """
+
+    try:
+        unmount(mountpoint, "")
+        if os.path.isdir(mountpoint):
+            os.rmdir(mountpoint)
+            unregister_tmpfile(mountpoint)
+    except CriticalException, error:
+        logging.critical("Fatal: %s", error)
+        cleanup()
 
 def handle_mbr(device):
     """Main handler for installing master boot record (MBR)
 
 def handle_mbr(device):
     """Main handler for installing master boot record (MBR)
@@ -1709,23 +1455,24 @@ def handle_mbr(device):
     if device[-1:].isdigit():
         mbr_device = re.match(r'(.*?)\d*$', device).group(1)
         partition_number = int(device[-1:]) - 1
     if device[-1:].isdigit():
         mbr_device = re.match(r'(.*?)\d*$', device).group(1)
         partition_number = int(device[-1:]) - 1
-        skip_install_mir_mbr = False
+    else:
+        logging.warn("Could not detect partition number, not activating partition")
+        partition_number = None
 
     # if we get e.g. /dev/loop1 as device we don't want to put the MBR
     # into /dev/loop of course, therefore use /dev/loop1 as mbr_device
     if mbr_device == "/dev/loop":
         mbr_device = device
         logging.info("Detected loop device - using %s as MBR device therefore", mbr_device)
 
     # if we get e.g. /dev/loop1 as device we don't want to put the MBR
     # into /dev/loop of course, therefore use /dev/loop1 as mbr_device
     if mbr_device == "/dev/loop":
         mbr_device = device
         logging.info("Detected loop device - using %s as MBR device therefore", mbr_device)
-        skip_install_mir_mbr = True
+
+    mbrcode = GRML2USB_BASE + '/mbr/mbrldr'
+    if options.syslinuxmbr:
+        mbrcode = '/usr/lib/syslinux/mbr.bin'
+    elif options.mbrmenu:
+        mbrcode = GRML2USB_BASE + '/mbr/mbrldr'
 
     try:
 
     try:
-        if options.syslinuxmbr:
-            handle_syslinux_mbr(mbr_device)
-        elif not skip_install_mir_mbr:
-            if options.mbrmenu:
-                install_mir_mbr(GRML2USB_BASE + '/mbr/mbrmgr', mbr_device, partition_number, True)
-            else:
-                install_mir_mbr(GRML2USB_BASE + '/mbr/mbrldr', mbr_device, partition_number, False)
+        install_mbr(mbrcode, mbr_device, partition_number, True)
     except IOError, error:
         logging.critical("Execution failed: %s", error)
         sys.exit(1)
     except IOError, error:
         logging.critical("Execution failed: %s", error)
         sys.exit(1)
@@ -1746,35 +1493,30 @@ def handle_vfat(device):
             logging.critical('Please make sure to install dosfstools.')
             sys.exit(1)
 
             logging.critical('Please make sure to install dosfstools.')
             sys.exit(1)
 
-        exec_mkfs = False
         if options.force:
             print "Forcing mkfs.fat16 on %s as requested via option --force." % device
         if options.force:
             print "Forcing mkfs.fat16 on %s as requested via option --force." % device
-            exec_mkfs = True
         else:
             # make sure the user is aware of what he is doing
             f = raw_input("Are you sure you want to format the specified partition with fat16? y/N ")
             if f == "y" or f == "Y":
                 logging.info("Note: you can skip this question using the option --force")
         else:
             # make sure the user is aware of what he is doing
             f = raw_input("Are you sure you want to format the specified partition with fat16? y/N ")
             if f == "y" or f == "Y":
                 logging.info("Note: you can skip this question using the option --force")
-                exec_mkfs = True
-
-        if exec_mkfs:
-            try:
-                mkfs_fat16(device)
-            except CriticalException, error:
-                logging.critical("Execution failed: %s", error)
+            else:
                 sys.exit(1)
                 sys.exit(1)
-        else:
+        try:
+            mkfs_fat16(device)
+        except CriticalException, error:
+            logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
     # check for vfat filesystem
             sys.exit(1)
 
     # check for vfat filesystem
-    if device is not None and not os.path.isdir(device):
+    if device is not None and not os.path.isdir(device) and options.syslinux:
         try:
             check_for_fat(device)
         except CriticalException, error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
         try:
             check_for_fat(device)
         except CriticalException, error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
 
-    if not os.path.isdir(device) and not check_for_usbdevice(device):
+    if not os.path.isdir(device) and not check_for_usbdevice(device) and not options.force:
         print "Warning: the specified device %s does not look like a removable usb device." % device
         f = raw_input("Do you really want to continue? y/N ")
         if f == "y" or f == "Y":
         print "Warning: the specified device %s does not look like a removable usb device." % device
         f = raw_input("Do you really want to continue? y/N ")
         if f == "y" or f == "Y":
@@ -1832,6 +1574,39 @@ def handle_bootloader(device):
         install_bootloader(device)
 
 
         install_bootloader(device)
 
 
+def check_options(opts):
+    """Check compability of provided user opts
+
+    @opts option dict from OptionParser
+    """
+    if opts.grubmbr and not opts.grub:
+        logging.critical("Error: --grub-mbr requires --grub option.")
+        sys.exit(1)
+
+
+def check_programs():
+    """check if all needed programs are installed"""
+    if options.grub:
+        if not which("grub-install"):
+            logging.critical("Fatal: grub-install not available (please install the "
+                             + "grub package or drop the --grub option)")
+            sys.exit(1)
+
+    if options.syslinux:
+        if not which("syslinux"):
+            logging.critical("Fatal: syslinux not available (please install the "
+                             + "syslinux package or use the --grub option)")
+            sys.exit(1)
+
+    if not which("rsync"):
+        logging.critical("Fatal: rsync not available, can not continue - sorry.")
+        sys.exit(1)
+
+def load_loop():
+    """Runs modprobe loop and throws away it's output"""
+    proc = subprocess.Popen(["modprobe", "loop"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    proc.wait()
+
 def main():
     """Main function [make pylint happy :)]"""
 
 def main():
     """Main function [make pylint happy :)]"""
 
@@ -1848,11 +1623,17 @@ def main():
     # make sure we have the appropriate permissions
     check_uid_root()
 
     # make sure we have the appropriate permissions
     check_uid_root()
 
+    check_options(options)
+
+    load_loop()
+
     logging.info("Executing grml2usb version %s", PROG_VERSION)
 
     if options.dryrun:
         logging.info("Running in simulation mode as requested via option dry-run.")
 
     logging.info("Executing grml2usb version %s", PROG_VERSION)
 
     if options.dryrun:
         logging.info("Running in simulation mode as requested via option dry-run.")
 
+    check_programs()
+
     # specified arguments
     device = args[len(args) - 1]
     isos = args[0:len(args) - 1]
     # specified arguments
     device = args[len(args) - 1]
     isos = args[0:len(args) - 1]
@@ -1862,14 +1643,6 @@ def main():
             if int(device[-1:]) > 4 or device[-2:].isdigit():
                 logging.critical("Fatal: installation on partition number >4 not supported. (BIOS won't support it.)")
                 sys.exit(1)
             if int(device[-1:]) > 4 or device[-2:].isdigit():
                 logging.critical("Fatal: installation on partition number >4 not supported. (BIOS won't support it.)")
                 sys.exit(1)
-        else:
-            if os.path.exists(device):
-                logging.critical("Fatal: installation on raw device not supported. (BIOS won't support it.)")
-                sys.exit(1)
-
-    if not which("rsync"):
-        logging.critical("Fatal: rsync not available, can not continue - sorry.")
-        sys.exit(1)
 
     # provide upgrade path
     handle_compat_warning(device)
 
     # provide upgrade path
     handle_compat_warning(device)
@@ -1879,15 +1652,15 @@ def main():
 
     # main operation (like installing files)
     for iso in isos:
 
     # main operation (like installing files)
     for iso in isos:
-        if os.path.isdir(iso):
-            handle_dir(iso, device)
-        else:
-            handle_iso(iso, device)
+        install(iso, device)
 
     # install mbr
 
     # install mbr
-    if not os.path.isdir(device):
-        if not options.skipmbr:
-            handle_mbr(device)
+    is_superfloppy = not device[-1:].isdigit()
+    if is_superfloppy:
+        logging.info("Detected superfloppy format - not installing MBR")
+
+    if not options.skipmbr and not os.path.isdir(device) and not is_superfloppy:
+        handle_mbr(device)
 
     handle_bootloader(device)
 
 
     handle_bootloader(device)