-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# pylint: disable-msg=C0302
"""
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/
"""
-from __future__ import print_function
+
from optparse import OptionParser
from inspect import isroutine, isclass
import datetime
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"
try:
unmount(device, "")
logging.debug('Unmounted %s' % device)
- except StandardError:
+ except Exception:
logging.debug('RuntimeError while umount %s, ignoring' % device)
for tmppath in TMPFILES.copy():
os.unlink(tmppath)
logging.debug('temporary file %s deleted' % tmppath)
unregister_tmpfile(tmppath)
- except StandardError:
+ except Exception:
msg = 'RuntimeError while removing temporary %s, ignoring'
logging.debug(msg % tmppath)
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
"""
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
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:
except ImportError as e:
logging.debug("could not import parted, falling back to old bootflag detection")
- with open(boot_dev, 'r') as image:
+ with open(boot_dev, 'rb') as image:
data = image.read(520)
bootcode = data[440:]
gpt_data = bootcode[70:80]
try:
udev_info = subprocess.Popen(["/sbin/blkid", "-s", "TYPE", "-o", "value", partition],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
filesystem = udev_info.communicate()[0].rstrip()
if filesystem != "vfat":
# update the existing flavours on the target
for line in fileinput.input([target_grml_version_file], inplace=1):
flavour = get_flavour(line)
- if flavour in iso_versions.keys():
+ if flavour in list(iso_versions.keys()):
print(iso_versions.pop(flavour))
else:
print(line.strip())
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)
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)
# 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)
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=...")
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_/-]+")
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):
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)
def initial_syslinux_config(target):
- """Generates intial syslinux configuration
+ """Generates initial syslinux configuration
@target path of syslinux's configuration files"""
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 Exception:
+ 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
if options.force or os.path.exists(os.path.join(image, 'live')):
logging.info("Using %s as install base", image)
else:
- q = raw_input("%s does not look like a Grml system. "
+ q = input("%s does not look like a Grml system. "
"Do you really want to use this image? y/N " % image)
if q.lower() == 'y':
logging.info("Using %s as install base", image)
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
print("Forcing mkfs.fat16 on %s as requested via option --force." % device)
else:
# make sure the user is aware of what he is doing
- f = raw_input("Are you sure you want to format the specified partition with fat16? y/N ")
+ f = input("Are you sure you want to format the specified partition with fat16? y/N ")
if f == "y" or f == "Y":
logging.info("Note: you can skip this question using the option --force")
else:
if not os.path.isdir(device) and not check_for_usbdevice(device) and not options.force:
print("Warning: the specified device %s does not look like a removable usb device." % device)
- f = raw_input("Do you really want to continue? y/N ")
+ f = input("Do you really want to continue? y/N ")
if f.lower() != "y":
sys.exit(1)
print("Instead of using grml2usb /path/to/iso %s you might" % device)
print("want to use grml2usb /path/to/iso /dev/... instead.")
print("Please check out the grml2usb manpage for details.")
- f = raw_input("Do you really want to continue? y/N ")
+ f = input("Do you really want to continue? y/N ")
if f.lower() != "y":
sys.exit(1)
def check_options(opts):
- """Check compability of provided user opts
+ """Check compatibility of provided user opts
@opts option dict from OptionParser
"""
def load_loop():
- """Runs modprobe loop and throws away it's output"""
+ """Runs modprobe loop and throws away its 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()
+ proc.communicate()
def main():
except Exception as error:
logging.critical("Fatal: %s", str(error))
+ if options.verbose:
+ logging.exception("Exception:")
sys.exit(1)