Increrased version number.
[grml2usb.git] / grml2usb
index 31cf05b..7591970 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
@@ -17,15 +17,26 @@ from optparse import OptionParser
 from inspect import isroutine, isclass
 import datetime, logging, os, re, subprocess, sys, tempfile, time, os.path
 import fileinput
 from inspect import isroutine, isclass
 import datetime, logging, os, re, subprocess, sys, tempfile, time, os.path
 import fileinput
+import glob
 
 # global variables
 
 # global variables
-PROG_VERSION = "0.9.14"
+PROG_VERSION = "0.9.19-pre1"
 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
 
 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
 
+def syslinux_warning(option, opt, value, parser):
+    sys.stderr.write("Note: the --syslinux option is deprecated as syslinux " +
+            "is grml2usb's default. Continuing anyway.\n")
+    setattr(parser.values, option.dest, True)
+
+# if grub option is set, unset syslinux option
+def grub_option(option, opt, value, parser):
+    setattr(parser.values, option.dest, True)
+    setattr(parser.values, 'syslinux', False)
+
 # cmdline parsing
 USAGE = "Usage: %prog [options] <[ISO[s] | /live/image]> </dev/sdX#>\n\
 \n\
 # cmdline parsing
 USAGE = "Usage: %prog [options] <[ISO[s] | /live/image]> </dev/sdX#>\n\
 \n\
@@ -50,7 +61,8 @@ 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")
@@ -70,7 +82,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")
@@ -649,9 +662,6 @@ 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 not which("grub-install"):
             logging.critical("Fatal: grub-install not available (please install the grub package or use the --syslinux option)")
     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)")
@@ -941,7 +951,7 @@ def check_for_fat(partition):
             raise CriticalException("Failed to read device %s"
                                     " (wrong UID/permissions or device/directory not present?)" % partition)
 
             raise CriticalException("Failed to read device %s"
                                     " (wrong UID/permissions or device/directory not present?)" % partition)
 
-        if options.syslinux and filesystem != "vfat":
+        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("Partition %s does not contain a FAT16 filesystem. (Use --fat16 or run mkfs.vfat %s)" % (partition, partition))
 
     except OSError:
@@ -1113,6 +1123,12 @@ def copy_addons(iso_mount, target):
     else:
         exec_rsync(memtestimg, addons + 'memtest')
 
     else:
         exec_rsync(memtestimg, addons + 'memtest')
 
+    # gpxe.lkrn
+    gpxeimg = search_file('gpxe.lkrn', iso_mount)
+    if gpxeimg is None:
+        logging.warn("Warning: gpxe.lkrn not found (that's fine if you don't need it)")
+    else:
+        exec_rsync(gpxeimg, addons + 'gpxe.lkrn')
 
 def copy_bootloader_files(iso_mount, target):
     """copy grml's bootloader files to a given target
 
 def copy_bootloader_files(iso_mount, target):
     """copy grml's bootloader files to a given target
@@ -1128,6 +1144,8 @@ def copy_bootloader_files(iso_mount, target):
 
     for ffile in ['f%d' % number for number in range(1,11) ]:
         bootsplash = search_file(ffile, iso_mount)
 
     for ffile in ['f%d' % number for number in range(1,11) ]:
         bootsplash = search_file(ffile, iso_mount)
+        if not bootsplash:
+            continue
         exec_rsync(bootsplash, syslinux_target + ffile)
 
     # avoid the "file is read only, overwrite anyway (y/n) ?" question
         exec_rsync(bootsplash, syslinux_target + ffile)
 
     # avoid the "file is read only, overwrite anyway (y/n) ?" question
@@ -1135,22 +1153,35 @@ def copy_bootloader_files(iso_mount, target):
     if os.path.isfile(syslinux_target + 'ldlinux.sys'):
         os.unlink(syslinux_target + 'ldlinux.sys')
 
     if os.path.isfile(syslinux_target + 'ldlinux.sys'):
         os.unlink(syslinux_target + 'ldlinux.sys')
 
-    if not search_file('default.cfg', iso_mount + '/boot/isolinux/'):
+    bootloader_dirs = ['/boot/isolinux/', '/boot/syslinux/']
+    source_dir = None
+    for dir in bootloader_dirs:
+        if glob.glob(iso_mount + dir + '*default.cfg'):
+            source_dir = dir
+            break
+    else:
         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
 
         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
 
-    for filename in 'addons.cfg', 'default.cfg', 'distri.cfg', \
-                    'grml.cfg', 'grml.png', 'hd.cfg', 'isolinux.cfg', 'isolinux.bin', \
+    for expr 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':
                     '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)
+        files = glob.glob(iso_mount + source_dir + expr)
+        for path in files:
+            filename = os.path.basename(path)
+            exec_rsync(path, syslinux_target + filename)
 
 
-    path = search_file('hidden.cfg', iso_mount + '/boot/isolinux/')
-    exec_rsync(path, syslinux_target + "new_" + 'hidden.cfg')
+    # copy the addons_*.cfg file to the new syslinux directory
+    for filename in glob.glob(iso_mount + source_dir + 'addon*.cfg'):
+        exec_rsync(filename, syslinux_target)
+
+    path = search_file('hidden.cfg', iso_mount + source_dir)
+    if path:
+        exec_rsync(path, syslinux_target + "new_" + 'hidden.cfg')
 
 
     grub_target = target + '/boot/grub/'
 
 
     grub_target = target + '/boot/grub/'
@@ -1171,6 +1202,15 @@ def copy_bootloader_files(iso_mount, target):
     if os.path.isfile("/usr/share/grub/ascii.pf2"):
         exec_rsync('/usr/share/grub/ascii.pf2', grub_target + 'ascii.pf2')
 
     if os.path.isfile("/usr/share/grub/ascii.pf2"):
         exec_rsync('/usr/share/grub/ascii.pf2', grub_target + 'ascii.pf2')
 
+    # always copy grub content as it might be useful
+    for file in glob.glob(iso_mount + '/boot/grub/*.mod'):
+        exec_rsync(file, grub_target)
+
+    for file in glob.glob(iso_mount + '/boot/grub/*.img'):
+        exec_rsync(file, grub_target)
+
+    for file in glob.glob(iso_mount + '/boot/grub/stage*'):
+        exec_rsync(file, 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
@@ -1186,6 +1226,8 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
     # * catch "install: .. No space left on device" & CO
 
     if options.dryrun:
     # * catch "install: .. No space left on device" & CO
 
     if options.dryrun:
+        global GRML_DEFAULT
+        GRML_DEFAULT = grml_flavour
         return 0
     elif not options.bootloaderonly:
         logging.info("Copying files. This might take a while....")
         return 0
     elif not options.bootloaderonly:
         logging.info("Copying files. This might take a while....")
@@ -1197,8 +1239,8 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
             sys.exit(1)
 
     if not options.skipaddons:
             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)
 
         else:
             copy_addons(iso_mount, target)
 
@@ -1458,10 +1500,10 @@ def adjust_syslinux_bootoptions(src, flavour):
         sys.stdout.write(line)
     fileinput.close()
 
         sys.stdout.write(line)
     fileinput.close()
 
-def adjust_labels(src, flavour):
+def adjust_labels(src, 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()
 
         sys.stdout.write(line)
     fileinput.close()
 
@@ -1526,7 +1568,8 @@ 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'])
+    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)
 
@@ -1541,8 +1584,8 @@ def handle_syslinux_config(grml_flavour, target):
         new_hidden =  "%s_hidden.cfg" % (flavour_filename)
         new_hidden_file =  "%s/%s" % (syslinux_target, new_hidden)
         os.rename(filename, new_hidden_file)
         new_hidden =  "%s_hidden.cfg" % (flavour_filename)
         new_hidden_file =  "%s/%s" % (syslinux_target, new_hidden)
         os.rename(filename, new_hidden_file)
-        adjust_labels(new_hidden_file, flavour_filename)
-        adjust_syslinux_bootoptions(new_hidden_file, flavour_filename)
+        adjust_labels(new_hidden_file, r'\1 %s-\2' % grml_flavour)
+        adjust_syslinux_bootoptions(new_hidden_file, grml_flavour)
         entry = 'include %s\n' % new_hidden
         add_entry_if_not_present("%s/hiddens.cfg" % syslinux_target, entry)
 
         entry = 'include %s\n' % new_hidden
         add_entry_if_not_present("%s/hiddens.cfg" % syslinux_target, entry)
 
@@ -1553,7 +1596,16 @@ def handle_syslinux_config(grml_flavour, target):
     defaults_file = '%s/defaults.cfg' % syslinux_target
 
     if os.path.isfile(defaults_file):
     defaults_file = '%s/defaults.cfg' % syslinux_target
 
     if os.path.isfile(defaults_file):
-        remove_default_entry('%s/%s_default.cfg' % (syslinux_target, flavour_filename))
+        new_default_with_path = "%s/%s" % (syslinux_target, new_default)
+        new_grml_cfg = "%s/%s_grml.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)
+
 
     add_entry_if_not_present("%s/defaults.cfg" % syslinux_target, entry)
 
 
     add_entry_if_not_present("%s/defaults.cfg" % syslinux_target, entry)
 
@@ -1662,11 +1714,15 @@ def handle_iso(iso, device):
         register_tmpfile(device_mountpoint)
         remove_device_mountpoint = True
         try:
         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:
+                mount(device, device_mountpoint, "")
+            except CriticalException, error:
+                logging.critical("Fatal: %s", error)
+                cleanup()
+                sys.exit(1)
 
     try:
         try:
 
     try:
         try:
@@ -1764,7 +1820,7 @@ def handle_vfat(device):
     # check for vfat filesystem
     if device is not None and not os.path.isdir(device):
         try:
     # check for vfat filesystem
     if device is not None and not os.path.isdir(device):
         try:
-            check_for_fat(device)
+            if options.syslinux: check_for_fat(device)
         except CriticalException, error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
         except CriticalException, error:
             logging.critical("Execution failed: %s", error)
             sys.exit(1)
@@ -1848,6 +1904,10 @@ def main():
     if options.dryrun:
         logging.info("Running in simulation mode as requested via option dry-run.")
 
     if options.dryrun:
         logging.info("Running in simulation mode as requested via option dry-run.")
 
+    if options.grubmbr and not options.grub:
+        logging.critical("Error: --grub-mbr requires --grub option.")
+        sys.exit(1)
+
     # 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]