X-Git-Url: https://code.delx.au/refind/blobdiff_plain/70c59b783f94768bd5875f44be2808454d000f3e..33cc55e661add10c9fd3f3300936e3eb48570c8d:/refind-mkdefault?ds=sidebyside diff --git a/refind-mkdefault b/refind-mkdefault new file mode 100755 index 0000000..325d382 --- /dev/null +++ b/refind-mkdefault @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 + +""" +Set rEFInd as the default boot loader, using Linux's efibootmgr tool. + +Copyright (c) 2016 Roderick W. Smith + +Authors: + Roderick W. Smith + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License version 3, or +(at your option) any later version, as published by the Free Software +Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import os +import shutil +import sys + +from subprocess import Popen, PIPE +from argparse import ArgumentParser + + +def discover_data(): + """Extract boot entry and boot order information. + + :returns: + boot_entries, boot_order + """ + command = "efibootmgr -v" + bootinfo_bytes = (Popen(command, stdout=PIPE, shell=True) + .communicate()[0]) + bootinfo = (bootinfo_bytes.decode(encoding="utf-8", errors="ignore") + .splitlines()) + boot_entries = {} + boot_order = [] + if len(bootinfo) > 1: + for s in bootinfo: + if "BootOrder" in s: + try: + boot_order = s.split(":")[1].replace(" ", "").split(",") + except IndexError: + pass + else: + # On Boot#### lines, #### is characters 4-8.... + hex_value = s[4:8] + # ....and the description starts at character 10 + name = s[10:] + try: + # In normal efibootmgr output, only Boot#### entries + # have characters 4-8 that can be interpreted as + # hex values, so this will harmlessly error out on all + # but Boot#### entries.... + int(hex_value, 16) + boot_entries[hex_value] = name + except ValueError: + pass + return boot_entries, boot_order + + +def add_unordered_entry(boot_entries, boot_order, label): + """Find a rEFInd boot_entry and add it to the boot_order list. + + Run if the boot_order list includes no rEFInd entry, in the + hopes of finding an existing rEFInd boot_entry that can be + used. + :param boot_entries: + Dictionary of boot entries, with string (hex-encoded number) as + key and description as value + :param boot_order: + List of boot numbers as strings, in boot order + :param label: + String used to identify rEFInd entry in efibootmgr output + :returns: + True if an entry was added, False otherwise + """ + added = False + for boot_num, description in boot_entries.items(): + if label.lower() in description.lower(): + print("Adding Boot{} from boot options list.".format(boot_num)) + boot_order.insert(0, boot_num) + added = True + return added + + +def set_refind_first(boot_entries, boot_order, label): + """Adjust boot_order so that rEFInd is first. + + :param boot_entries: + Dictionary of boot entries, with string (hex-encoded number) as + key and description as value + :param boot_order: + List of boot numbers as strings, in boot order + :param label: + String used to identify rEFInd entry in efibootmgr output + :returns: + True if order adjusted, False otherwise + """ + first_refind_number = i = -1 + changed_order = False + found_first_refind = "" + show_multiple_warning = True + for entry in boot_order: + i += 1 + if label.lower() in boot_entries[entry].lower(): + if found_first_refind: + if show_multiple_warning: + print("Found multiple {} entries! The earliest in the boot order will be made".format(label)) + print("the default, but this may not be what you want. Manually checking with") + print("efibootmgr is advisable!\n") + show_multiple_warning = False + else: + found_first_refind = entry + first_refind_number = i + if first_refind_number == -1: + if add_unordered_entry(boot_entries, boot_order, label): + changed_order = True + else: + print("{} was not found in the boot options list!".format(label)) + print("You should create a {} entry with efibootmgr or by re-installing".format(label)) + print("(with refind-install, for example)") + elif first_refind_number == 0: + print("{} is already the first entry".format(label)) + elif first_refind_number > 0: + del boot_order[first_refind_number] + boot_order.insert(0, found_first_refind) + changed_order = True + + print("{} is not the first boot entry; adjusting....".format(label)) + return changed_order + + +def save_changes(boot_order): + """Save an altered boot_order. + + :returns: + True if there were no problems, False otherwise + """ + order_string = ",".join(boot_order) + command = "efibootmgr -o {}".format(order_string) + print("Setting a boot order of {}".format(order_string)) + try: + Popen(command, stdout=PIPE, shell=True).communicate()[0] + except: + print("An error occurred setting the new boot order!") + + +def main(): + """Set rEFInd as the default boot option.""" + description = "Sets rEFInd as the default EFI boot option" + parser = ArgumentParser(description=description) + parser.add_argument("-L", "--label", + default="rEFInd", + help=("The label used to identify rEFInd")) + args = parser.parse_args() + + if sys.platform != "linux": + print("This program is useful only under Linux; exiting!") + return(1) + if shutil.which("efibootmgr") is None: + print("The efibootmgr utility is not installed; exiting!") + return(1) + if not os.geteuid() == 0: + print("You must be root to run this program") + return(1) + + problems = False + retval = 0 + boot_entries, boot_order = discover_data() + if boot_entries == {}: + problems = True + print("No EFI boot entries available. This may indicate a firmware problem.") + if boot_order == []: + problems = True + print("The EFI BootOrder variable is not available. This may indicate a firmware") + print("problem.") + if (boot_entries != {} and boot_order != [] and + set_refind_first(boot_entries, boot_order, args.label)): + save_changes(boot_order) + else: + if problems: + retval = 1 + print("No changes saved.") + return(retval) + +if __name__ == '__main__': + sys.exit(main())