Force usage with python2
[grml2usb.git] / grml2usb
index e465ebe..ca172ff 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 # -*- coding: utf-8 -*-
 # pylint: disable-msg=C0302
 """
@@ -42,7 +42,7 @@ GRML_FLAVOURS = set()  # which flavours are being installed?
 GRML_DEFAULT = None
 UUID = None
 SYSLINUX_LIBS = "/usr/lib/syslinux/"
-GPT_HEADER = "\x55\xaa\x45\x46\x49\x20\x50\x41\x52\x54"  # original GPT header
+GPT_HEADER = b"\x55\xaa\x45\x46\x49\x20\x50\x41\x52\x54"  # original GPT header
 GRUB_INSTALL = None
 
 RE_PARTITION = re.compile(r'([a-z/]*?)(\d+)$')
@@ -67,6 +67,7 @@ def grub_option(option, opt, value, opt_parser):
     setattr(opt_parser.values, option.dest, True)
     setattr(opt_parser.values, 'syslinux', False)
 
+
 # cmdline parsing
 USAGE = "Usage: %prog [options] <[ISO[s] | /lib/live/mount/medium]> </dev/sdX#>\n\
 \n\
@@ -245,7 +246,7 @@ def unregister_mountpoint(target):
 
 
 def get_function_name(obj):
-    """Helper function for use in execute() to retrive name of a function
+    """Helper function for use in execute() to retrieve name of a function
 
     @obj: the function object
     """
@@ -379,9 +380,9 @@ def check_boot_flag(device):
         if part.getFlag(parted.PARTITION_BOOT):
             logging.debug("bootflag is enabled on %s" % device)
             return
-    except HodorException, e:
+    except HodorException as e:
         logging.info("%s, falling back to old bootflag detection", e)
-    except ImportError, e:
+    except ImportError as e:
         logging.debug("could not import parted, falling back to old bootflag detection")
 
     with open(boot_dev, 'r') as image:
@@ -391,7 +392,7 @@ def check_boot_flag(device):
 
         if gpt_data == GPT_HEADER:
             logging.info("GPT detected, skipping bootflag check")
-        elif bootcode[6] == '\x80':
+        elif bootcode[6] == b"\x80":
             logging.debug("bootflag is enabled")
         else:
             logging.debug("bootflag is NOT enabled")
@@ -513,15 +514,16 @@ def install_grub(device):
             mount(device, device_mountpoint, "")
 
             logging.info("Installing grub as bootloader")
-            for opt in ["", "--force"]:
+            for opt in ["--", "--force"]:
                 set_rw(device)
                 set_rw(grub_device)
-                logging.debug("grub-install --recheck %s --no-floppy --root-directory=%s %s",
-                              opt, device_mountpoint, grub_device)
-                proc = subprocess.Popen([GRUB_INSTALL, "--recheck", opt,
+                logging.debug("%s --recheck --no-floppy --target=i386-pc --root-directory=%s %s %s",
+                              GRUB_INSTALL, device_mountpoint, opt, grub_device)
+                proc = subprocess.Popen([GRUB_INSTALL, "--recheck",
                                          "--no-floppy", "--target=i386-pc",
-                                         "--root-directory=%s" % device_mountpoint, grub_device],
-                                        stdout=file(os.devnull, "r+"))
+                                         "--root-directory=%s" % device_mountpoint,
+                                         opt, grub_device],
+                                        stdout=open(os.devnull, "r+"))
                 proc.wait()
                 if proc.returncode == 0:
                     break
@@ -630,7 +632,7 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
 
     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+"))
+                            stderr=open(os.devnull, "r+"))
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (first run)")
@@ -638,7 +640,7 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
     logging.debug("executing: dd if=%s of=%s bs=%s count=1 conv=notrunc", mbrtemplate,
                   tmpf.name, 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=open(os.devnull, "r+"))
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (second run)")
@@ -649,16 +651,16 @@ def install_mbr(mbrtemplate, device, partition, ismirbsdmbr=True):
 
     if partition is not None:
         if ismirbsdmbr:
-            mbrcode = mbrcode[0:439] + chr(partition) + \
-                    mbrcode[440:510] + "\x55\xAA"
+            mbrcode = mbrcode[0:439] + chr(partition).encode('latin-1') + \
+                    mbrcode[440:510] + b"\x55\xAA"
         else:
-            actives = ["\x00", "\x00", "\x00", "\x00"]
-            actives[partition] = "\x80"
+            actives = [b"\x00", b"\x00", b"\x00", b"\x00"]
+            actives[partition] = b"\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"
+                    mbrcode[495:510] + b"\x55\xAA"
 
     tmpf.file.seek(0)
     tmpf.file.truncate()
@@ -669,7 +671,7 @@ def install_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",
-                             "conv=notrunc"], stderr=file(os.devnull, "r+"))
+                             "conv=notrunc"], stderr=open(os.devnull, "r+"))
     proc.wait()
     if proc.returncode != 0:
         raise Exception("error executing dd (third run)")
@@ -706,7 +708,7 @@ def mount(source, target, mount_options):
     # note: options.dryrun does not work here, as we have to
     # locate files and identify the grml flavour
 
-    for x in file('/proc/mounts').readlines():
+    for x in open('/proc/mounts', 'r').readlines():
         if x.startswith(source):
             raise CriticalException("Error executing mount: %s already mounted - " % source +
                                     "please unmount before invoking grml2usb")
@@ -1118,6 +1120,7 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
     if efi_img:
         mkdir(target + '/boot/')
         exec_rsync(efi_img, target + '/boot/efi.img')
+        handle_secure_boot(target, efi_img)
 
     for ffile in ['f%d' % number for number in range(1, 11)]:
         search_and_copy(ffile, iso_mount, syslinux_target + ffile)
@@ -1156,7 +1159,7 @@ def copy_bootloader_files(iso_mount, target, grml_flavour):
     # copy all grub files from ISO
     glob_and_copy(iso_mount + '/boot/grub/*', grub_target)
 
-    # finally (after all GRUB files have been been installed) build static loopback.cfg
+    # finally (after all GRUB files have been installed) build static loopback.cfg
     build_loopbackcfg(target)
 
 
@@ -1274,6 +1277,7 @@ def handle_grub_config(grml_flavour, device, target):
     logging.debug("Updating grub configuration")
 
     grub_target = target + '/boot/grub/'
+    secureboot_target = target + '/EFI/ubuntu/'
 
     bootid_re = re.compile("bootid=[\w_-]+")
     live_media_path_re = re.compile("live-media-path=[\w_/-]+")
@@ -1288,7 +1292,7 @@ def handle_grub_config(grml_flavour, device, target):
             remove_regexes.append(re.compile(regex))
 
     shortname = get_shortname(grml_flavour)
-    for filename in glob.glob(grub_target + '*.cfg'):
+    for filename in glob.glob(grub_target + '*.cfg') + glob.glob(secureboot_target + '*.cfg'):
         for line in fileinput.input(filename, inplace=1):
             line = line.rstrip("\r\n")
             if option_re.search(line):
@@ -1304,7 +1308,7 @@ def handle_grub_config(grml_flavour, device, target):
 
 
 def initial_syslinux_config(target):
-    """Generates intial syslinux configuration
+    """Generates initial syslinux configuration
 
     @target path of syslinux's configuration files"""
 
@@ -1508,6 +1512,51 @@ def handle_syslinux_config(grml_flavour, target):
     add_syslinux_entry("%s/additional.cfg" % syslinux_target, flavour_filename)
 
 
+def handle_secure_boot(target, efi_img):
+    """Provide secure boot support by extracting files from /boot/efi.img
+
+    @target: path where grml's main files should be copied to
+    @efi_img: path to the efi.img file that includes the files for secure boot
+    """
+
+    mkdir(target + '/efi/boot/')
+    efi_mountpoint = tempfile.mkdtemp(prefix="grml2usb", dir=os.path.abspath(options.tmpdir))
+    logging.debug("efi_mountpoint = %s" % efi_mountpoint)
+    register_tmpfile(efi_mountpoint)
+
+    try:
+        logging.debug("mount(%s, %s, ['-o', 'ro', '-t', 'vfat']" % (efi_img, efi_mountpoint))
+        mount(efi_img, efi_mountpoint, ['-o', 'ro', '-t', 'vfat'])
+    except CriticalException as error:
+        logging.critical("Fatal: %s", error)
+        sys.exit(1)
+
+    ubuntu_cfg = search_file('grub.cfg', efi_mountpoint + '/EFI/ubuntu')
+    logging.debug("ubuntu_cfg = %s" % ubuntu_cfg)
+    if not ubuntu_cfg:
+        logging.info("No /EFI/ubuntu/grub.cfg found inside EFI image, looks like Secure Boot support is missing.")
+    else:
+        mkdir(target + '/efi/ubuntu')
+        logging.debug("exec_rsync(%s, %s + '/efi/ubuntu/grub.cfg')" % (ubuntu_cfg, target))
+        exec_rsync(ubuntu_cfg, target + '/efi/ubuntu/grub.cfg')
+
+        logging.debug("exec_rsync(%s + '/EFI/BOOT/grubx64.efi', %s + '/efi/boot/grubx64.efi')'" % (efi_mountpoint, target))
+        exec_rsync(efi_mountpoint + '/EFI/BOOT/grubx64.efi', target + '/efi/boot/grubx64.efi')
+
+        # NOTE - we're overwriting /efi/boot/bootx64.efi from copy_bootloader_files here
+        logging.debug("exec_rsync(%s + '/EFI/BOOT/bootx64.efi', %s + '/efi/boot/bootx64.efi')'" % (efi_mountpoint, target))
+        exec_rsync(efi_mountpoint + '/EFI/BOOT/bootx64.efi', target + '/efi/boot/bootx64.efi')
+
+    try:
+        unmount(efi_mountpoint, "")
+        logging.debug('Unmounted %s' % efi_mountpoint)
+        os.rmdir(efi_mountpoint)
+        logging.debug('Removed directory %s' % efi_mountpoint)
+    except StandardError:
+        logging.critical('RuntimeError while umount %s' % efi_mountpoint)
+        sys.exit(1)
+
+
 def handle_bootloader_config(grml_flavour, device, target):
     """Main handler for generating bootloader's configuration
 
@@ -1765,7 +1814,7 @@ def handle_bootloader(device):
 
 
 def check_options(opts):
-    """Check compability of provided user opts
+    """Check compatibility of provided user opts
 
     @opts option dict from OptionParser
     """