install boot files required for [U]EFI boot
[grml2usb.git] / grml2usb
index 3fbf2c7..4b706cc 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
@@ -77,8 +77,6 @@ parser.add_option("--grub", dest="grub", action="callback",
                   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",
@@ -479,107 +477,6 @@ menuentry "%(grml_flavour)s-forensic   - do not touch harddisks during hw recogn
        'flavour_filename': grml_flavour.replace('-', ''),
        'uid': UUID, '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/ bootid=%(uid)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/ bootid=%(uid)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 bootid=%(uid)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-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 bootid=%(uid)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-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 bootid=%(uid)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-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 bootid=%(uid)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-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 bootid=%(uid)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-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 bootid=%(uid)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-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 bootid=%(uid)s %(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('-', ''), 'uid': UUID,
-       '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/ bootid=%(uid)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('-', ''), 'uid': UUID,
-       'bootoptions': bootoptions, 'install_partition': install_partition } )
-
-
 def generate_isolinux_splash(grml_flavour):
     """Generate bootsplash for isolinux/syslinux
 
@@ -750,52 +647,7 @@ def install_bootloader(device):
             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:
-        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
@@ -823,10 +675,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)
-        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
@@ -854,18 +707,19 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     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)
@@ -880,39 +734,6 @@ def install_mir_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     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
 
@@ -1205,7 +1026,7 @@ def copy_grml_files(iso_mount, target):
     for myfile in copy_files:
         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)
+            logging.warn("Warning: file %s could not be found - can not install it", myfile)
         else:
             exec_rsync(grml_file, grml_target + myfile)
 
@@ -1215,7 +1036,7 @@ def copy_grml_files(iso_mount, target):
     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")
+            logging.warn("Warning: file %s could not be found - can not install it", myfile)
         else:
             exec_rsync(grml_file, grml_web_target + myfile)
 
@@ -1225,21 +1046,23 @@ def copy_grml_files(iso_mount, 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: myfile %s could not be found - can not install it")
+            logging.warn("Warning: file %s could not be found - can not install it", myfile)
         else:
             exec_rsync(grml_file, grml_webimg_target + myfile)
 
 
-def handle_addon_copy(filename, dst, iso_mount):
+def handle_addon_copy(filename, dst, iso_mount, ignore_errors=False):
     """handle copy of optional addons
 
     @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:
-        logging.warn("Warning: %s not found (that's fine if you don't need it)",  filename)
+        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)
 
@@ -1280,7 +1103,7 @@ def copy_addons(iso_mount, target):
     handle_addon_copy('memtest', addons, iso_mount)
 
     # gpxe.lkrn: got replaced by ipxe
-    handle_addon_copy('gpxe.lkrn', addons, iso_mount)
+    handle_addon_copy('gpxe.lkrn', addons, iso_mount, ignore_errors=True)
 
     # ipxe.lkrn
     handle_addon_copy('ipxe.lkrn', addons, iso_mount)
@@ -1327,10 +1150,18 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
     grub_target = target + '/boot/grub/'
     execute(mkdir, grub_target)
 
-
     logo = search_file('logo.16', iso_mount)
     exec_rsync(logo, syslinux_target + 'logo.16')
 
+    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)
@@ -1354,7 +1185,6 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
         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 expr in name, 'distri.cfg', \
@@ -1489,55 +1319,6 @@ def modify_grub_config(filename):
 
         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
 
@@ -1614,19 +1395,8 @@ def handle_grub_config(grml_flavour, device, target):
 
     grub_target = target + '/boot/grub/'
 
-    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?)")
-
-
     bootopt = get_bootoptions(grml_flavour)
 
-    # 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)
 
@@ -1882,7 +1652,7 @@ def install(image, device):
         register_tmpfile(iso_mountpoint)
         remove_image_mountpoint = True
         try:
-            mount(image, iso_mountpoint, ["-o", "loop", "-t", "iso9660"])
+            mount(image, iso_mountpoint, ["-o", "loop,ro", "-t", "iso9660"])
         except CriticalException, error:
             logging.critical("Fatal: %s", error)
             sys.exit(1)
@@ -1959,23 +1729,24 @@ def handle_mbr(device):
     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)
-        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:
-        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)
@@ -2139,10 +1910,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)
-        elif os.path.exists(device):
-            logging.critical("Fatal: installation on raw device not supported. (BIOS won't support it.)")
-            sys.exit(1)
-
 
     # provide upgrade path
     handle_compat_warning(device)
@@ -2155,7 +1922,11 @@ def main():
         install(iso, device)
 
     # install mbr
-    if not options.skipmbr and not os.path.isdir(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)