]> code.delx.au - refind/blob - refind-mkdefault
Added documentation on refind-mkdefault script (bootcoup.html and
[refind] / refind-mkdefault
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
26 import shlex
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"
41 bootinfo_bytes = (Popen(shlex.split(command), stdout=PIPE)
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:
107 True if order adjusted, False otherwise
108 """
109 first_refind_number = i = -1
110 changed_order = False
111 found_first_refind = ""
112 show_multiple_warning = True
113 for entry in boot_order:
114 i += 1
115 if label.lower() in boot_entries[entry].lower():
116 if found_first_refind:
117 if show_multiple_warning:
118 print("Found multiple {} entries! The earliest in the boot order will be made".format(label))
119 print("the default, but this may not be what you want. Manually checking with")
120 print("efibootmgr is advisable!\n")
121 show_multiple_warning = False
122 else:
123 found_first_refind = entry
124 first_refind_number = i
125 if first_refind_number == -1:
126 if add_unordered_entry(boot_entries, boot_order, label):
127 changed_order = True
128 else:
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)")
132 elif first_refind_number == 0:
133 print("{} is already the first entry".format(label))
134 elif first_refind_number > 0:
135 del boot_order[first_refind_number]
136 boot_order.insert(0, found_first_refind)
137 changed_order = True
138
139 print("{} is not the first boot entry; adjusting....".format(label))
140 return changed_order
141
142
143 def save_changes(boot_order):
144 """Save an altered boot_order.
145
146 :param boot_order:
147 List of boot numbers as strings, in boot order
148 :returns:
149 True if there were no problems, False otherwise
150 """
151 order_string = ",".join(boot_order)
152 command = "efibootmgr -o {}".format(order_string)
153 print("Setting a boot order of {}".format(order_string))
154 try:
155 Popen(shlex.split(command), stdout=PIPE).communicate()[0]
156 except:
157 print("An error occurred setting the new boot order!")
158
159
160 def main():
161 """Set rEFInd as the default boot option."""
162 description = "Sets rEFInd as the default EFI boot option"
163 parser = ArgumentParser(description=description)
164 parser.add_argument("-L", "--label",
165 default="rEFInd",
166 help=("The label used to identify rEFInd (default=rEFInd)"))
167 args = parser.parse_args()
168
169 if sys.platform != "linux":
170 print("This program is useful only under Linux; exiting!")
171 return(1)
172 if shutil.which("efibootmgr") is None:
173 print("The efibootmgr utility is not installed; exiting!")
174 return(1)
175 if not os.geteuid() == 0:
176 print("This program must be run as root (or via sudo); exiting!")
177 return(1)
178
179 problems = False
180 retval = 0
181 boot_entries, boot_order = discover_data()
182 if boot_entries == {}:
183 problems = True
184 print("No EFI boot entries are available. This may indicate a firmware problem.")
185 if boot_order == []:
186 problems = True
187 print("The EFI BootOrder variable is not available. This may indicate a firmware")
188 print("problem.")
189 if (not problems and
190 set_refind_first(boot_entries, boot_order, args.label)):
191 save_changes(boot_order)
192 else:
193 if problems:
194 retval = 1
195 print("No changes saved.")
196 return(retval)
197
198 if __name__ == '__main__':
199 sys.exit(main())