Modify grub config files for the specified flavour instead of all grub config files
[grml2usb.git] / grml2usb
index e42deb6..5ab3bf2 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
@@ -21,8 +21,10 @@ import glob
 import uuid
 import struct
 
+# The line following this line is patched by debian/rules and tarball.sh.
+PROG_VERSION='***UNRELEASED***'
+
 # global variables
-PROG_VERSION = "0.9.31"
 MOUNTED = set()  # register mountpoints
 TMPFILES = set() # register tmpfiles
 DATESTAMP = time.mktime(datetime.datetime.now().timetuple()) # unique identifier for syslinux.cfg
@@ -302,181 +304,6 @@ def mkfs_fat16(device):
         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 boot=live nomce live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)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
-}
-
-if [ ${iso_path} ] ; then
-    # assume loopback.cfg boot
-    if [ -e /boot/addons/bsd4grml/loopback.0 ] ; then
-        # bsd4grml 20100815 and later
-        menuentry "Boot MirOS bsd4grml" {
-            multiboot /boot/addons/bsd4grml/ldbsd.com
-            module /boot/addons/bsd4grml/bsd.rd bsd
-            module /boot/addons/bsd4grml/loopback.0 boot.cfg
-            module /boot/addons/bsd4grml/loopback.1 boot.1
-            module /boot/addons/bsd4grml/loopback.2 boot.2
-            module /boot/addons/bsd4grml/loopback.3 boot.3
-            module /boot/addons/bsd4grml/loopback.4 boot.4
-            module /boot/addons/bsd4grml/loopback.5 boot.5
-            module /boot/addons/bsd4grml/loopback.6 boot.6
-        }
-    else
-        # old bsd4grml
-        menuentry "Boot MirOS bsd4grml" {
-            multiboot /boot/addons/bsd4grml/ldbsd.com
-            module /boot/addons/bsd4grml/bsd.rd bsd.rd
-            module /boot/addons/bsd4grml/boot.cfg boot.cfg
-            module /boot/addons/bsd4grml/boot.1 boot.1
-            module /boot/addons/bsd4grml/boot.2 boot.2
-            module /boot/addons/bsd4grml/boot.3 boot.3
-            module /boot/addons/bsd4grml/boot.4 boot.4
-            module /boot/addons/bsd4grml/boot.5 boot.5
-        }
-    fi
-else
-    # assume grub.cfg boot
-    menuentry "Boot MirOS bsd4grml" {
-        multiboot /boot/addons/bsd4grml/ldbsd.com
-        module /boot/addons/bsd4grml/bsd.rd bsd.rd
-        module /boot/addons/bsd4grml/boot.cfg boot.cfg
-        module /boot/addons/bsd4grml/boot.1 boot.1
-        module /boot/addons/bsd4grml/boot.2 boot.2
-        module /boot/addons/bsd4grml/boot.3 boot.3
-        module /boot/addons/bsd4grml/boot.4 boot.4
-        module /boot/addons/bsd4grml/boot.5 boot.5
-        module /boot/addons/bsd4grml/boot.6 boot.6
-    }
-fi
-
-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('-', ''),
-       'uid': UUID, '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 live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)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 - enable persistent mode" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce persistent live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)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 - copy Grml to RAM" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce live-media-path=/live/%(grml_flavour)s/ toram=%(grml_flavour)s.squashfs bootid=%(uid)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 - start X Window System" {
-    set gfxpayload=1024x768x16,1024x768
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce live-media-path=/live/%(grml_flavour)s/ startx bootid=%(uid)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 - disable framebuffer" {
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce live-media-path=/live/%(grml_flavour)s/ vga=normal video=ofonly bootid=%(uid)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 - disable Kernel Mode-Setting" {
-    linux  /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce live-media-path=/live/%(grml_flavour)s/ bootid=%(uid)s %(bootoptions)s radeon.modeset=0 i915.modeset=0 nouveau.modeset=0 nomodeset
-    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 mode" {
-    set gfxpayload=1024x768x16,1024x768
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce live-media-path=/live/%(grml_flavour)s/ nofstab noraid nolvm noautoconfig noswap raid=noautodetect forensic readonly bootid=%(uid)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 - enable debugging options" {
-    set gfxpayload=1024x768x16,1024x768
-    linux /boot/release/%(flavour_filename)s/linux26 apm=power-off boot=live nomce live-media-path=/live/%(grml_flavour)s/ debug bootid=%(uid)s initcall_debug %(bootoptions)s
-    initrd /boot/release/%(flavour_filename)s/initrd.gz
-}
-
-""" % {'grml_flavour': grml_flavour, 'local_datestamp': local_datestamp,
-       'flavour_filename': grml_flavour.replace('-', ''),
-       'uid': UUID, 'bootoptions': bootoptions } )
-
 def generate_isolinux_splash(grml_flavour):
     """Generate bootsplash for isolinux/syslinux
 
@@ -941,28 +768,24 @@ def copy_system_files(grml_flavour, iso_mount, target):
     else:
         exec_rsync(filesystem_module, squashfs_target + 'filesystem.module')
 
+    cleaned_flavour = re.sub(r'[,._-]', '', grml_flavour)
 
-    release_path = 'boot/release/' + grml_flavour.replace('-', '')
-    release_target = target + "/" + release_path
-    execute(mkdir, release_target)
-
-    prefix = ""
-    if os.path.isdir(iso_mount + '/boot/release'):
-        prefix = release_path + '/'
-
-    kernel = search_file(prefix + 'linux26', iso_mount)
-    if kernel is None:
-        logging.critical("Fatal kernel not found")
-        raise CriticalException("error locating kernel file")
+    if os.path.isdir(iso_mount + '/boot/' + cleaned_flavour):
+        exec_rsync(iso_mount + '/boot/' + cleaned_flavour, target + '/boot')
     else:
-        exec_rsync(kernel, release_target + '/linux26')
+        kernel = search_file('vmlinuz', iso_mount)
+        if kernel is None:
+            # compat for releases < 2011.12
+            kernel = search_file('linux26', iso_mount)
 
-    initrd = search_file(prefix + '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')
+        if kernel is None:
+            logging.critical("Fatal: kernel not found")
+            raise CriticalException("error locating kernel file")
+
+        source = os.path.dirname(kernel) + '/'
+        dest = target + '/' + os.path.dirname(kernel).replace(iso_mount,'') + '/'
+        execute(mkdir, dest)
+        exec_rsync(source, dest)
 
 
 def update_grml_versions(iso_mount, target):
@@ -1009,46 +832,24 @@ def update_grml_versions(iso_mount, target):
     else:
         return False
 
-def copy_grml_files(iso_mount, target):
+def copy_grml_files(grml_flavour, iso_mount, 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)
 
-    copy_files = [ 'grml-cheatcodes.txt', 'LICENSE.txt', 'md5sums', 'README.txt' ]
-    # handle grml-version
-    if not update_grml_versions(iso_mount, target):
-        copy_files.append('grml-version')
-
-    for myfile in copy_files:
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: file %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)
-
-    for myfile in 'index.html', 'style.css':
-        grml_file = search_file(myfile, iso_mount)
-        if grml_file is None:
-            logging.warn("Warning: file %s could not be found - can not install it", myfile)
-        else:
-            exec_rsync(grml_file, grml_web_target + myfile)
-
-    grml_webimg_target = grml_web_target + '/images/'
-    execute(mkdir, grml_webimg_target)
-
-    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: file %s could not be found - can not install it", myfile)
-        else:
-            exec_rsync(grml_file, grml_webimg_target + myfile)
+    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)
 
 
 def handle_addon_copy(filename, dst, iso_mount, ignore_errors=False):
@@ -1187,6 +988,9 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
         logging.critical("       ... either use grml releases >=2009.10 or switch to an older grml2usb version.")
         raise
 
+    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.")
+
     for expr in name, 'distri.cfg', \
         defaults_file, 'grml.png', 'hd.cfg', 'isolinux.cfg', 'isolinux.bin', \
         'isoprompt.cfg', 'options.cfg', \
@@ -1202,28 +1006,8 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
 
     search_and_copy('hidden.cfg', iso_mount + source_dir, syslinux_target + "new_" + 'hidden.cfg')
 
-
-    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
-    copy_if_exist(GRML2USB_BASE + "/grub/grml.png", grub_target + 'grml.png')
-
-    # font file for graphical bootsplash in grub
-    copy_if_exist('/usr/share/grub/ascii.pf2', grub_target + 'ascii.pf2')
-
-    # always copy grub content as it might be useful
-
-    glob_and_copy(iso_mount + '/boot/grub/*.mod', grub_target)
-    glob_and_copy(iso_mount + '/boot/grub/*.img', grub_target)
-    glob_and_copy(iso_mount + '/boot/grub/stage*', grub_target)
+    # 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
@@ -1241,7 +1025,7 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
         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)
@@ -1298,78 +1082,6 @@ def identify_grml_flavour(mountpath):
     return flavours
 
 
-def modify_grub_config(filename):
-    """Adjust bootoptions for a grub file
-
-    @filename: filename to modify
-    """
-    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_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"""
-
-    # grub2 config
-    grub2_cfg = grub_target + 'grub.cfg'
-    logging.debug("Creating grub2 configuration file (grub.cfg)")
-
-    global GRML_DEFAULT
-
-    # 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()
-    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)
-
-
 def get_bootoptions(grml_flavour):
     """Returns bootoptions for specific flavour
 
@@ -1391,14 +1103,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"""
 
-    logging.debug("Generating grub configuration")
+    global UUID
+
+    logging.debug("Updating grub configuration")
 
     grub_target = target + '/boot/grub/'
 
+    bootid_re = re.compile("bootid=[\w_-]+")
+    live_media_path_re = re.compile("live-media-path=[\w_/-]+")
+
     bootopt = get_bootoptions(grml_flavour)
 
-    # write grub.cfg
-    handle_grub2_config(grml_flavour, grub_target, bootopt)
+    remove_regexes = []
+    option_re = re.compile(r'(.*/boot/.*(linux26|vmlinuz).*)')
+
+    if options.removeoption:
+        for regex in options.removeoption:
+            remove_regexes.append(re.compile(regex))
+
+    for filename in glob.glob(grub_target + grml_flavour + '*.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):
@@ -1449,8 +1182,7 @@ def adjust_syslinux_bootoptions(src, flavour):
     @flavour: grml flavour
     """
 
-    append_re = re.compile("^(\s*append.*/boot/release.*)$", re.I)
-    boot_re = re.compile("/boot/([a-zA-Z0-9_]+/)+([a-zA-Z0-9._]+)")
+    append_re = re.compile("^(\s*append.*/boot/.*)$", re.I)
     # flavour_re = re.compile("(label.*)(grml\w+)")
     default_re = re.compile("(default.cfg)")
     bootid_re = re.compile("bootid=[\w_-]+")
@@ -1461,13 +1193,12 @@ def adjust_syslinux_bootoptions(src, flavour):
     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):
-        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 = bootid_re.sub('', line)
@@ -1510,7 +1241,7 @@ def add_syslinux_entry(filename, grml_flavour):
     data.close()
 
 def modify_filenames(grml_flavour, target, filenames):
-    """Replace the standarf filenames with the new ones
+    """Replace the standard filenames with the new ones
 
     @grml_flavour: grml-flavour strin
     @target: directory where the files are located
@@ -1587,7 +1318,7 @@ def handle_syslinux_config(grml_flavour, target):
     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)
+    new_grml_cfg = "%s/%s_grml.cfg" % (syslinux_target, flavour_filename)
 
     if os.path.isfile(defaults_file):
 
@@ -1876,6 +1607,11 @@ def check_programs():
         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 :)]"""
 
@@ -1894,6 +1630,8 @@ def main():
 
     check_options(options)
 
+    load_loop()
+
     logging.info("Executing grml2usb version %s", PROG_VERSION)
 
     if options.dryrun:
@@ -1902,7 +1640,7 @@ def main():
     check_programs()
 
     # specified arguments
-    device = args[len(args) - 1]
+    device = os.path.realpath(args[len(args) - 1])
     isos = args[0:len(args) - 1]
 
     if not os.path.isdir(device):