~rodsmith/refind/master

447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
1
#!/usr/bin/env python3
2
3
"""
4
Set rEFInd as the default boot loader, using Linux's efibootmgr tool.
5
6
Copyright (c) 2016 Roderick W. Smith
7
8
Authors:
9
  Roderick W. Smith <rodsmith@rodsbooks.com>
10
11
This program is free software: you can redistribute it and/or modify
12
it under the terms of the GNU General Public License version 3, or
13
(at your option) any later version, as published by the Free Software
14
Foundation.
15
16
This program is distributed in the hope that it will be useful,
17
but WITHOUT ANY WARRANTY; without even the implied warranty of
18
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
GNU General Public License for more details.
20
21
You should have received a copy of the GNU General Public License
22
along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
"""
24
25
import os
448 by srs5694
Improvements to refind-mkdefault, including packaging files.
26
import shlex
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
27
import shutil
28
import sys
29
30
from subprocess import Popen, PIPE
31
from argparse import ArgumentParser
32
33
34
def discover_data():
35
    """Extract boot entry and boot order information.
36
37
    :returns:
38
        boot_entries, boot_order
39
    """
40
    command = "efibootmgr -v"
448 by srs5694
Improvements to refind-mkdefault, including packaging files.
41
    bootinfo_bytes = (Popen(shlex.split(command), stdout=PIPE)
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
42
                      .communicate()[0])
43
    bootinfo = (bootinfo_bytes.decode(encoding="utf-8", errors="ignore")
44
                .splitlines())
45
    boot_entries = {}
46
    boot_order = []
47
    if len(bootinfo) > 1:
48
        for s in bootinfo:
49
            if "BootOrder" in s:
50
                try:
51
                    boot_order = s.split(":")[1].replace(" ", "").split(",")
52
                except IndexError:
53
                    pass
54
            else:
55
                # On Boot#### lines, #### is characters 4-8....
56
                hex_value = s[4:8]
57
                # ....and the description starts at character 10
58
                name = s[10:]
59
                try:
60
                    # In normal efibootmgr output, only Boot#### entries
61
                    # have characters 4-8 that can be interpreted as
62
                    # hex values, so this will harmlessly error out on all
63
                    # but Boot#### entries....
64
                    int(hex_value, 16)
65
                    boot_entries[hex_value] = name
66
                except ValueError:
67
                    pass
68
    return boot_entries, boot_order
69
70
71
def add_unordered_entry(boot_entries, boot_order, label):
72
    """Find a rEFInd boot_entry and add it to the boot_order list.
73
74
    Run if the boot_order list includes no rEFInd entry, in the
75
    hopes of finding an existing rEFInd boot_entry that can be
76
    used.
77
    :param boot_entries:
78
        Dictionary of boot entries, with string (hex-encoded number) as
79
        key and description as value
80
    :param boot_order:
81
        List of boot numbers as strings, in boot order
82
    :param label:
83
        String used to identify rEFInd entry in efibootmgr output
84
    :returns:
85
        True if an entry was added, False otherwise
86
    """
87
    added = False
88
    for boot_num, description in boot_entries.items():
89
        if label.lower() in description.lower():
90
            print("Adding Boot{} from boot options list.".format(boot_num))
91
            boot_order.insert(0, boot_num)
92
            added = True
93
    return added
94
95
96
def set_refind_first(boot_entries, boot_order, label):
97
    """Adjust boot_order so that rEFInd is first.
98
99
    :param boot_entries:
100
        Dictionary of boot entries, with string (hex-encoded number) as
101
        key and description as value
102
    :param boot_order:
103
        List of boot numbers as strings, in boot order
104
    :param label:
105
        String used to identify rEFInd entry in efibootmgr output
106
    :returns:
453 by srs5694
Misc small changes, mostly to refind-mkdefault
107
        * -1 if order already OK
108
        * 0 if order adjusted
109
        * 3 if label was not found in available entries
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
110
    """
111
    first_refind_number = i = -1
453 by srs5694
Misc small changes, mostly to refind-mkdefault
112
    retval = 0
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
113
    found_first_refind = ""
114
    show_multiple_warning = True
115
    for entry in boot_order:
116
        i += 1
117
        if label.lower() in boot_entries[entry].lower():
118
            if found_first_refind:
119
                if show_multiple_warning:
120
                    print("Found multiple {} entries! The earliest in the boot order will be made".format(label))
121
                    print("the default, but this may not be what you want. Manually checking with")
122
                    print("efibootmgr is advisable!\n")
123
                    show_multiple_warning = False
124
            else:
125
                found_first_refind = entry
126
                first_refind_number = i
127
    if first_refind_number == -1:
453 by srs5694
Misc small changes, mostly to refind-mkdefault
128
        if not add_unordered_entry(boot_entries, boot_order, label):
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
129
            print("{} was not found in the boot options list!".format(label))
130
            print("You should create a {} entry with efibootmgr or by re-installing".format(label))
131
            print("(with refind-install, for example)")
453 by srs5694
Misc small changes, mostly to refind-mkdefault
132
            retval = 3
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
133
    elif first_refind_number == 0:
134
        print("{} is already the first entry".format(label))
453 by srs5694
Misc small changes, mostly to refind-mkdefault
135
        retval = -1
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
136
    elif first_refind_number > 0:
137
        del boot_order[first_refind_number]
138
        boot_order.insert(0, found_first_refind)
139
140
        print("{} is not the first boot entry; adjusting....".format(label))
453 by srs5694
Misc small changes, mostly to refind-mkdefault
141
    return retval
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
142
143
144
def save_changes(boot_order):
145
    """Save an altered boot_order.
146
448 by srs5694
Improvements to refind-mkdefault, including packaging files.
147
    :param boot_order:
148
        List of boot numbers as strings, in boot order
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
149
    :returns:
453 by srs5694
Misc small changes, mostly to refind-mkdefault
150
        0 if there were no problems, 1 otherwise
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
151
    """
453 by srs5694
Misc small changes, mostly to refind-mkdefault
152
    retval = 0
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
153
    order_string = ",".join(boot_order)
154
    command = "efibootmgr -o {}".format(order_string)
155
    print("Setting a boot order of {}".format(order_string))
156
    try:
448 by srs5694
Improvements to refind-mkdefault, including packaging files.
157
        Popen(shlex.split(command), stdout=PIPE).communicate()[0]
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
158
    except:
159
        print("An error occurred setting the new boot order!")
453 by srs5694
Misc small changes, mostly to refind-mkdefault
160
        retval = 1
161
    return retval
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
162
163
164
def main():
165
    """Set rEFInd as the default boot option."""
166
    description = "Sets rEFInd as the default EFI boot option"
167
    parser = ArgumentParser(description=description)
168
    parser.add_argument("-L", "--label",
169
                        default="rEFInd",
448 by srs5694
Improvements to refind-mkdefault, including packaging files.
170
                        help=("The label used to identify rEFInd (default=rEFInd)"))
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
171
    args = parser.parse_args()
172
173
    if sys.platform != "linux":
174
        print("This program is useful only under Linux; exiting!")
455 by srs5694
Tweaked refind-mkdefault return values for some error conditions.
175
        return(4)
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
176
    if shutil.which("efibootmgr") is None:
177
        print("The efibootmgr utility is not installed; exiting!")
455 by srs5694
Tweaked refind-mkdefault return values for some error conditions.
178
        return(4)
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
179
    if not os.geteuid() == 0:
448 by srs5694
Improvements to refind-mkdefault, including packaging files.
180
        print("This program must be run as root (or via sudo); exiting!")
455 by srs5694
Tweaked refind-mkdefault return values for some error conditions.
181
        return(4)
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
182
183
    retval = 0
184
    boot_entries, boot_order = discover_data()
185
    if boot_entries == {}:
448 by srs5694
Improvements to refind-mkdefault, including packaging files.
186
        print("No EFI boot entries are available. This may indicate a firmware problem.")
453 by srs5694
Misc small changes, mostly to refind-mkdefault
187
        retval = 2
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
188
    if boot_order == []:
189
        print("The EFI BootOrder variable is not available. This may indicate a firmware")
190
        print("problem.")
453 by srs5694
Misc small changes, mostly to refind-mkdefault
191
    if (retval == 0):
192
        changed = set_refind_first(boot_entries, boot_order, args.label)
193
        if (changed == 0):
194
            retval = save_changes(boot_order)
195
        else:
196
            print("No changes saved.")
197
            if changed > 0:
198
                retval = changed
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
199
    else:
200
        print("No changes saved.")
201
    return(retval)
202
705 by Rod Smith
Trivial reformatting plus convert some old rEFIt log lines into new rEFInd LOG() calls
203
447 by srs5694
Added refind-mkdefault script to reset rEFInd as the default boot
204
if __name__ == '__main__':
205
    sys.exit(main())