]> code.delx.au - refind/blob - refind-mkdefault
Added refind-mkdefault script to reset rEFInd as the default boot
[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 shutil
27 import sys
28
29 from subprocess import Popen, PIPE
30 from argparse import ArgumentParser
31
32
33 def discover_data():
34 """Extract boot entry and boot order information.
35
36 :returns:
37 boot_entries, boot_order
38 """
39 command = "efibootmgr -v"
40 bootinfo_bytes = (Popen(command, stdout=PIPE, shell=True)
41 .communicate()[0])
42 bootinfo = (bootinfo_bytes.decode(encoding="utf-8", errors="ignore")
43 .splitlines())
44 boot_entries = {}
45 boot_order = []
46 if len(bootinfo) > 1:
47 for s in bootinfo:
48 if "BootOrder" in s:
49 try:
50 boot_order = s.split(":")[1].replace(" ", "").split(",")
51 except IndexError:
52 pass
53 else:
54 # On Boot#### lines, #### is characters 4-8....
55 hex_value = s[4:8]
56 # ....and the description starts at character 10
57 name = s[10:]
58 try:
59 # In normal efibootmgr output, only Boot#### entries
60 # have characters 4-8 that can be interpreted as
61 # hex values, so this will harmlessly error out on all
62 # but Boot#### entries....
63 int(hex_value, 16)
64 boot_entries[hex_value] = name
65 except ValueError:
66 pass
67 return boot_entries, boot_order
68
69
70 def add_unordered_entry(boot_entries, boot_order, label):
71 """Find a rEFInd boot_entry and add it to the boot_order list.
72
73 Run if the boot_order list includes no rEFInd entry, in the
74 hopes of finding an existing rEFInd boot_entry that can be
75 used.
76 :param boot_entries:
77 Dictionary of boot entries, with string (hex-encoded number) as
78 key and description as value
79 :param boot_order:
80 List of boot numbers as strings, in boot order
81 :param label:
82 String used to identify rEFInd entry in efibootmgr output
83 :returns:
84 True if an entry was added, False otherwise
85 """
86 added = False
87 for boot_num, description in boot_entries.items():
88 if label.lower() in description.lower():
89 print("Adding Boot{} from boot options list.".format(boot_num))
90 boot_order.insert(0, boot_num)
91 added = True
92 return added
93
94
95 def set_refind_first(boot_entries, boot_order, label):
96 """Adjust boot_order so that rEFInd is first.
97
98 :param boot_entries:
99 Dictionary of boot entries, with string (hex-encoded number) as
100 key and description as value
101 :param boot_order:
102 List of boot numbers as strings, in boot order
103 :param label:
104 String used to identify rEFInd entry in efibootmgr output
105 :returns:
106 True if order adjusted, False otherwise
107 """
108 first_refind_number = i = -1
109 changed_order = False
110 found_first_refind = ""
111 show_multiple_warning = True
112 for entry in boot_order:
113 i += 1
114 if label.lower() in boot_entries[entry].lower():
115 if found_first_refind:
116 if show_multiple_warning:
117 print("Found multiple {} entries! The earliest in the boot order will be made".format(label))
118 print("the default, but this may not be what you want. Manually checking with")
119 print("efibootmgr is advisable!\n")
120 show_multiple_warning = False
121 else:
122 found_first_refind = entry
123 first_refind_number = i
124 if first_refind_number == -1:
125 if add_unordered_entry(boot_entries, boot_order, label):
126 changed_order = True
127 else:
128 print("{} was not found in the boot options list!".format(label))
129 print("You should create a {} entry with efibootmgr or by re-installing".format(label))
130 print("(with refind-install, for example)")
131 elif first_refind_number == 0:
132 print("{} is already the first entry".format(label))
133 elif first_refind_number > 0:
134 del boot_order[first_refind_number]
135 boot_order.insert(0, found_first_refind)
136 changed_order = True
137
138 print("{} is not the first boot entry; adjusting....".format(label))
139 return changed_order
140
141
142 def save_changes(boot_order):
143 """Save an altered boot_order.
144
145 :returns:
146 True if there were no problems, False otherwise
147 """
148 order_string = ",".join(boot_order)
149 command = "efibootmgr -o {}".format(order_string)
150 print("Setting a boot order of {}".format(order_string))
151 try:
152 Popen(command, stdout=PIPE, shell=True).communicate()[0]
153 except:
154 print("An error occurred setting the new boot order!")
155
156
157 def main():
158 """Set rEFInd as the default boot option."""
159 description = "Sets rEFInd as the default EFI boot option"
160 parser = ArgumentParser(description=description)
161 parser.add_argument("-L", "--label",
162 default="rEFInd",
163 help=("The label used to identify rEFInd"))
164 args = parser.parse_args()
165
166 if sys.platform != "linux":
167 print("This program is useful only under Linux; exiting!")
168 return(1)
169 if shutil.which("efibootmgr") is None:
170 print("The efibootmgr utility is not installed; exiting!")
171 return(1)
172 if not os.geteuid() == 0:
173 print("You must be root to run this program")
174 return(1)
175
176 problems = False
177 retval = 0
178 boot_entries, boot_order = discover_data()
179 if boot_entries == {}:
180 problems = True
181 print("No EFI boot entries available. This may indicate a firmware problem.")
182 if boot_order == []:
183 problems = True
184 print("The EFI BootOrder variable is not available. This may indicate a firmware")
185 print("problem.")
186 if (boot_entries != {} and boot_order != [] and
187 set_refind_first(boot_entries, boot_order, args.label)):
188 save_changes(boot_order)
189 else:
190 if problems:
191 retval = 1
192 print("No changes saved.")
193 return(retval)
194
195 if __name__ == '__main__':
196 sys.exit(main())