ups, log.info() does not need a tuple
[grml2usb.git] / grml2usb
index 68565db..7f1f21d 100755 (executable)
--- a/grml2usb
+++ b/grml2usb
@@ -40,7 +40,11 @@ GRML_FLAVOURS = set()  # which flavours are being installed?
 GRML_DEFAULT = None
 UUID = None
 SYSLINUX_LIBS = "/usr/lib/syslinux/"
+GRUB_INSTALL = "grub-install"
 
+RE_PARTITION = re.compile(r'([a-z/]*?)(\d+)$')
+RE_P_PARTITION = re.compile(r'(.*?\d+)p(\d+)$')
+RE_LOOP_DEVICE = re.compile(r'/dev/loop\d+$')
 
 def syslinux_warning(option, opt, value, opt_parser):
     """A helper function for printing a warning about deprecated option
@@ -60,10 +64,10 @@ def grub_option(option, opt, value, opt_parser):
     setattr(opt_parser.values, 'syslinux', False)
 
 # cmdline parsing
-USAGE = "Usage: %prog [options] <[ISO[s] | /live/image]> </dev/sdX#>\n\
+USAGE = "Usage: %prog [options] <[ISO[s] | /lib/live/mount/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 (/live/image),\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\
 grub or syslinux and root access.\n\
 \n\
 Run %prog --help for usage hints, further information via: man grml2usb"
@@ -128,6 +132,12 @@ class CriticalException(Exception):
     @Exception: message"""
     pass
 
+class VerifyException(Exception):
+    """Throw critical exception if there is an fatal error when verifying something.
+
+    @Exception: message"""
+    pass
+
 
 # The following two functions help to operate on strings as
 # array (list) of bytes (octets). In Python 3000, the bytes
@@ -305,6 +315,20 @@ def check_uid_root():
         sys.exit("Error: please run this script with uid 0 (root).")
 
 
+def check_boot_flag(device):
+    boot_dev, x = get_device_from_partition(device)
+
+    with open(boot_dev, 'r') as image:
+        data = image.read(512)
+        bootcode = data[440:]
+        if bootcode[6] == '\x80':
+            logging.debug("bootflag is enabled")
+        else:
+            logging.debug("bootflag is NOT enabled")
+            raise VerifyException("Device %s does not have the bootflag set. "
+                "Please enable it to be able to boot." % boot_dev)
+
+
 def mkfs_fat16(device):
     """Format specified device with VFAT/FAT16 filesystem.
 
@@ -403,7 +427,7 @@ def install_grub(device):
     @device: partition where grub should be installed to"""
 
     if options.dryrun:
-        logging.info("Would execute grub-install [--root-directory=mount_point] %s now.", device)
+        logging.info("Would execute %s [--root-directory=mount_point] %s now.", GRUB_INSTALL, device)
     else:
         device_mountpoint = tempfile.mkdtemp(prefix="grml2usb")
         register_tmpfile(device_mountpoint)
@@ -414,18 +438,15 @@ def install_grub(device):
                 # If using --grub-mbr then make sure we install grub in MBR instead of PBR
                 if options.grubmbr:
                     logging.debug("Using option --grub-mbr ...")
-                    if device[-1:].isdigit():
-                        grub_device = re.match(r'(.*?)\d*$', device).group(1)
-                    else:
-                        grub_device = device
+                    grub_device, x = get_device_from_partition(device)
                 else:
                     grub_device = device
 
                 logging.info("Installing grub as bootloader")
                 for opt in ["", "--force"]:
-                    logging.debug("grub-install --recheck %s --no-floppy --root-directory=%s %s",
-                                  opt, device_mountpoint, grub_device)
-                    proc = subprocess.Popen(["grub-install", "--recheck", opt, "--no-floppy",
+                    logging.debug("%s --recheck %s --no-floppy --root-directory=%s %s",
+                                  GRUB_INSTALL, opt, device_mountpoint, grub_device)
+                    proc = subprocess.Popen([GRUB_INSTALL, "--recheck", opt, "--no-floppy",
                                              "--root-directory=%s" % device_mountpoint, grub_device],
                                             stdout=file(os.devnull, "r+"))
                     proc.wait()
@@ -433,9 +454,9 @@ def install_grub(device):
                         break
 
                 if proc.returncode != 0:
-                    # raise Exception("error executing grub-install")
-                    logging.critical("Fatal: error executing grub-install "
-                                     + "(please check the grml2usb FAQ or drop the --grub option)")
+                    # raise Exception("error executing %s" % (GRUB_INSTALL))
+                    logging.critical("Fatal: error executing %s "
+                                     + "(please check the grml2usb FAQ or drop the --grub option)" % (GRUB_INSTALL))
                     logging.critical("Note:  if using grub2 consider using "
                                      + "the --grub-mbr option as grub considers PBR problematic.")
                     cleanup()
@@ -1093,6 +1114,19 @@ def install_iso_files(grml_flavour, iso_mount, device, target):
     proc.wait()
 
 
+def get_device_from_partition(partition):
+    device = partition
+    partition_number = None
+    if partition[-1].isdigit() and not RE_LOOP_DEVICE.match(partition):
+        m = RE_P_PARTITION.match(partition)
+        if not m:
+            m = RE_PARTITION.match(partition)
+        if m:
+            device = m.group(1)
+            partition_number = int(m.group(2)) - 1
+    return (device, partition_number)
+
+
 def get_flavour(flavour_str):
     """Returns the flavour of a grml version string
     """
@@ -1108,7 +1142,7 @@ def identify_grml_flavour(mountpath):
     version_files = search_file('grml-version', mountpath, lst_return=True)
 
     if not version_files:
-        if mountpath.startswith("/live/image"):
+        if mountpath.startswith("/lib/live/mount/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=...")
@@ -1439,7 +1473,7 @@ def install(image, device):
         logging.info("Using %s as install base", image)
     else:
         logging.info("Using ISO %s", image)
-        iso_mountpoint = tempfile.mkdtemp(prefix="grml2usb", dir=options.tmpdir)
+        iso_mountpoint = tempfile.mkdtemp(prefix="grml2usb", dir=os.path.abspath(options.tmpdir))
         register_tmpfile(iso_mountpoint)
         remove_image_mountpoint = True
         try:
@@ -1462,7 +1496,7 @@ def install(image, device):
 def install_grml(mountpoint, device):
     """Main logic for copying files of the currently running grml system.
 
-    @mountpoin: directory where currently running live system resides (usually /live/image)
+    @mountpoint: directory where currently running live system resides (usually /lib/live/mount/medium)
     @device: partition where the specified ISO should be installed to"""
 
     device_mountpoint = device
@@ -1475,7 +1509,11 @@ def install_grml(mountpoint, device):
         remove_device_mountpoint = True
         try:
             check_for_fat(device)
+            check_boot_flag(device)
             mount(device, device_mountpoint, ['-o', 'utf8,iocharset=iso8859-1'])
+        except VerifyException, error:
+            logging.critical("Fatal: %s", error)
+            raise
         except CriticalException, error:
             try:
                 mount(device, device_mountpoint, "")
@@ -1518,12 +1556,9 @@ def handle_mbr(device):
         logging.info("Would install MBR")
         return 0
 
-    if device[-1:].isdigit():
-        mbr_device = re.match(r'(.*?)\d*$', device).group(1)
-        partition_number = int(device[-1:]) - 1
-    else:
+    mbr_device, partition_number = get_device_from_partition(device)
+    if not partition_number:
         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
@@ -1651,12 +1686,16 @@ def check_options(opts):
 
 
 def check_programs():
+    global GRUB_INSTALL
     """check if all needed programs are installed"""
     if options.grub:
-        if not which("grub-install"):
-            logging.critical("Fatal: grub-install not available (please install the "
-                             + "grub package or drop the --grub option)")
-            sys.exit(1)
+        if not which(GRUB_INSTALL):
+            if which("grub2-install"):  # Fedora workaround
+                GRUB_INSTALL="grub2-install"
+            else:
+                logging.critical("Fatal: grub-install not available (please install the "
+                                 + "grub package or drop the --grub option)")
+                sys.exit(1)
 
     if options.syslinux:
         if not which("syslinux"):
@@ -1671,6 +1710,11 @@ def check_programs():
 
 def load_loop():
     """Runs modprobe loop and throws away it's output"""
+    if not which("modprobe"):
+        logging.critical("Fatal: modprobe not available, can not continue - sorry.")
+        logging.critical("Hint: is /sbin missing in PATH?")
+        sys.exit(1)
+
     proc = subprocess.Popen(["modprobe", "loop"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     proc.wait()