grml2usb: print backtrace when --verbose is given
[grml2usb.git] / grml2usb
index 5108b9f..9c6c8c5 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
 """
@@ -7,7 +7,7 @@ grml2usb
 
 This script installs a Grml system (either a running system or ISO[s]) to a USB device
 
-:copyright: (c) 2009, 2010, 2011 by Michael Prokop <mika@grml.org>
+:copyright: (c) 2009-2019 by Michael Prokop <mika@grml.org>
 :license: GPL v2 or any later version
 :bugreports: http://grml.org/bugs/
 
@@ -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,11 +67,12 @@ 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\
+USAGE = "Usage: %prog [options] <[ISO[s] | /run/live/medium]> </dev/sdX#>\n\
 \n\
 %prog installs Grml ISO[s] to an USB device to be able to boot from it.\n\
-Make sure you have at least one Grml ISO or a running Grml system (/lib/live/mount/medium),\n\
+Make sure you have at least one Grml ISO or a running Grml system (/run/live/medium),\n\
 grub or syslinux and root access.\n\
 \n\
 Run %prog --help for usage hints, further information via: man grml2usb"
@@ -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
     """
@@ -314,7 +315,7 @@ def get_defaults_file(iso_mount, flavour, name):
     return ('', '')
 
 
-def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin', lst_return=False):
+def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin', lst_return=False, required=False):
     """Given a search path, find file
 
     @filename: name of file to search for
@@ -343,6 +344,10 @@ def search_file(filename, search_path='/bin' + os.pathsep + '/usr/bin', lst_retu
                 retval.append(os.path.abspath(os.path.join(current_dir, filename)))
                 if not lst_return:
                     break
+
+    if required and not retval:
+        raise CriticalException("Required file %s not found in %s" % (filename, search_path))
+
     if lst_return:
         return retval
     elif retval:
@@ -379,9 +384,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 +396,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")
@@ -522,7 +527,7 @@ def install_grub(device):
                                          "--no-floppy", "--target=i386-pc",
                                          "--root-directory=%s" % device_mountpoint,
                                          opt, grub_device],
-                                        stdout=file(os.devnull, "r+"))
+                                        stdout=open(os.devnull, "r+"))
                 proc.wait()
                 if proc.returncode == 0:
                     break
@@ -631,7 +636,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)")
@@ -639,7 +644,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)")
@@ -650,16 +655,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()
@@ -670,7 +675,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)")
@@ -707,7 +712,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")
@@ -1107,7 +1112,7 @@ 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)
+    logo = search_file('logo.16', iso_mount, required=True)
     exec_rsync(logo, syslinux_target + 'logo.16')
 
     bootx64_efi = search_file('bootx64.efi', iso_mount)
@@ -1158,7 +1163,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)
 
 
@@ -1226,7 +1231,7 @@ def identify_grml_flavour(mountpath):
     version_files = search_file('grml-version', mountpath, lst_return=True)
 
     if not version_files:
-        if mountpath.startswith("/lib/live/mount/medium"):
+        if mountpath.startswith("/run/live/medium"):
             logging.critical("Error: could not find grml-version file.")
             logging.critical("Looks like your system is running from RAM but required files are not available.")
             logging.critical("Please either boot without toram=... or use boot option toram instead of toram=...")
@@ -1299,6 +1304,10 @@ def handle_grub_config(grml_flavour, device, target):
                 if shortname in filename:
                     line = live_media_path_re.sub('', line)
                     line = line.rstrip() + ' live-media-path=/live/%s/ ' % (grml_flavour)
+                if bootopt.strip():
+                    line = line.replace(' {} '.format(bootopt.strip()), ' ')
+                    if line.endswith(bootopt):
+                        line = line[:-len(bootopt)]
                 line = line.rstrip() + r' bootid=%s %s ' % (UUID, bootopt)
                 for regex in remove_regexes:
                     line = regex.sub(' ', line)
@@ -1307,7 +1316,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"""
 
@@ -1627,7 +1636,7 @@ def install(image, device):
 def install_grml(mountpoint, device):
     """Main logic for copying files of the currently running Grml system.
 
-    @mountpoint: directory where currently running live system resides (usually /lib/live/mount/medium)
+    @mountpoint: directory where currently running live system resides (usually /run/live/medium)
     @device: partition where the specified ISO should be installed to"""
 
     device_mountpoint = device
@@ -1813,7 +1822,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
     """
@@ -1923,6 +1932,8 @@ def main():
 
     except Exception as error:
         logging.critical("Fatal: %s", str(error))
+        if options.verbose:
+            logging.exception("Exception:")
         sys.exit(1)