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()) |