efibootdude 0.0__py3-none-any.whl → 0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of efibootdude might be problematic. Click here for more details.
- efibootdude/main.py +65 -39
- {efibootdude-0.0.dist-info → efibootdude-0.2.dist-info}/METADATA +12 -5
- efibootdude-0.2.dist-info/RECORD +9 -0
- efibootdude-0.0.dist-info/RECORD +0 -9
- {efibootdude-0.0.dist-info → efibootdude-0.2.dist-info}/LICENSE +0 -0
- {efibootdude-0.0.dist-info → efibootdude-0.2.dist-info}/WHEEL +0 -0
- {efibootdude-0.0.dist-info → efibootdude-0.2.dist-info}/entry_points.txt +0 -0
- {efibootdude-0.0.dist-info → efibootdude-0.2.dist-info}/top_level.txt +0 -0
efibootdude/main.py
CHANGED
|
@@ -18,7 +18,7 @@ from types import SimpleNamespace
|
|
|
18
18
|
import subprocess
|
|
19
19
|
import traceback
|
|
20
20
|
import curses as cs
|
|
21
|
-
import xml.etree.ElementTree as ET
|
|
21
|
+
# import xml.etree.ElementTree as ET
|
|
22
22
|
from efibootdude.PowerWindow import Window, OptionSpinner
|
|
23
23
|
|
|
24
24
|
|
|
@@ -26,20 +26,21 @@ class EfiBootDude:
|
|
|
26
26
|
""" Main class for curses atop efibootmgr"""
|
|
27
27
|
singleton = None
|
|
28
28
|
|
|
29
|
-
def __init__(self):
|
|
29
|
+
def __init__(self, testfile=None):
|
|
30
30
|
# self.cmd_loop = CmdLoop(db=False) # just running as command
|
|
31
31
|
assert not EfiBootDude.singleton
|
|
32
32
|
EfiBootDude.singleton = self
|
|
33
|
+
self.testfile = testfile
|
|
33
34
|
|
|
34
35
|
spin = self.spin = OptionSpinner()
|
|
35
36
|
spin.add_key('help_mode', '? - toggle help screen', vals=[False, True])
|
|
36
37
|
spin.add_key('verbose', 'v - toggle verbose', vals=[False, True])
|
|
37
38
|
|
|
38
39
|
# FIXME: keys
|
|
39
|
-
other = '
|
|
40
|
+
other = 'tudrnmw*zqx'
|
|
40
41
|
other_keys = set(ord(x) for x in other)
|
|
41
42
|
other_keys.add(cs.KEY_ENTER)
|
|
42
|
-
|
|
43
|
+
other_keys.add(27) # ESCAPE
|
|
43
44
|
other_keys.add(10) # another form of ENTER
|
|
44
45
|
self.opts = spin.default_obj
|
|
45
46
|
|
|
@@ -88,35 +89,50 @@ class EfiBootDude:
|
|
|
88
89
|
|
|
89
90
|
def get_part_uuids(self):
|
|
90
91
|
""" Get all the Partition UUIDS"""
|
|
92
|
+
# uuids = {}
|
|
93
|
+
# with open('/run/blkid/blkid.tab', encoding='utf8') as fh:
|
|
94
|
+
# # sample: <device ... TYPE="vfat"
|
|
95
|
+
# # PARTUUID="25d2dea1-9f68-1644-91dd-4836c0b3a30a">/dev/nvme0n1p1</device>
|
|
96
|
+
# for xml_line in fh:
|
|
97
|
+
# element = ET.fromstring(xml_line)
|
|
98
|
+
# if 'PARTUUID' in element.attrib:
|
|
99
|
+
# device=element.text.strip()
|
|
100
|
+
# name = self.mounts.get(device, device)
|
|
101
|
+
# uuids[element.attrib['PARTUUID'].lower()] = name
|
|
102
|
+
# return uuids
|
|
103
|
+
|
|
91
104
|
uuids = {}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
partuuid_path = '/dev/disk/by-partuuid/'
|
|
106
|
+
|
|
107
|
+
if not os.path.exists(partuuid_path):
|
|
108
|
+
return uuids
|
|
109
|
+
for entry in os.listdir(partuuid_path):
|
|
110
|
+
full_path = os.path.join(partuuid_path, entry)
|
|
111
|
+
if os.path.islink(full_path):
|
|
112
|
+
device_path = os.path.realpath(full_path)
|
|
113
|
+
uuids[entry] = device_path
|
|
101
114
|
return uuids
|
|
102
115
|
|
|
103
116
|
@staticmethod
|
|
104
|
-
def
|
|
117
|
+
def extract_uuids(line):
|
|
105
118
|
""" Find uuid string in a line """
|
|
106
119
|
# Define the regex pattern for UUID (e.g., 25d2dea1-9f68-1644-91dd-4836c0b3a30a)
|
|
107
120
|
pattern = r'\b[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\b'
|
|
108
121
|
# Search for the pattern in the line
|
|
109
|
-
|
|
110
|
-
return
|
|
111
|
-
|
|
122
|
+
mats = re.findall(pattern, line, re.IGNORECASE)
|
|
123
|
+
return mats
|
|
112
124
|
|
|
113
125
|
def digest_boots(self):
|
|
114
126
|
""" Digest the output of 'efibootmgr'."""
|
|
115
127
|
# Define the command to run
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
lines = []
|
|
129
|
+
if self.testfile:
|
|
130
|
+
with open(self.testfile, 'r', encoding='utf-8') as fh:
|
|
131
|
+
lines = fh.readlines()
|
|
132
|
+
else:
|
|
133
|
+
command = 'efibootmgr'.split()
|
|
134
|
+
result = subprocess.run(command, stdout=subprocess.PIPE, text=True, check=True)
|
|
135
|
+
lines = result.stdout.splitlines()
|
|
120
136
|
rv = []
|
|
121
137
|
width1 = 0 # width of info1
|
|
122
138
|
label_wid = 0
|
|
@@ -141,8 +157,8 @@ class EfiBootDude:
|
|
|
141
157
|
)
|
|
142
158
|
|
|
143
159
|
mat = re.match(r'\bBoot([0-9a-f]+)\b(\*?)' # Boot0024*
|
|
144
|
-
+ r'\s+(\
|
|
145
|
-
+ r'\
|
|
160
|
+
+ r'\s+(\S.*\S|\S)\s*\t' # Linux Boot Manager
|
|
161
|
+
+ r'\s*(\S.*\S|\S)\s*$', # HD(4,GPT,cd15e3b1-...
|
|
146
162
|
line, re.IGNORECASE)
|
|
147
163
|
if not mat:
|
|
148
164
|
ns.ident = key
|
|
@@ -160,16 +176,19 @@ class EfiBootDude:
|
|
|
160
176
|
label_wid = max(label_wid, len(ns.label))
|
|
161
177
|
other = mat.group(4)
|
|
162
178
|
|
|
163
|
-
|
|
179
|
+
pat = r'(?:/?\b\w*\(|/)(\\[^/()]+)(?:$|[()/])'
|
|
180
|
+
mat = re.search(pat, other, re.IGNORECASE)
|
|
164
181
|
device, subpath = '', '' # e.g., /boot/efi, \EFI\UBUNTU\SHIMX64.EFI
|
|
165
182
|
if mat:
|
|
166
183
|
subpath = mat.group(1) + ' '
|
|
167
184
|
start, end = mat.span()
|
|
168
185
|
other = other[:start] + other[end:]
|
|
169
186
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
187
|
+
uuids = self.extract_uuids(other)
|
|
188
|
+
for uuid in uuids:
|
|
189
|
+
if uuid and uuid in self.uuids:
|
|
190
|
+
device = self.uuids[uuid]
|
|
191
|
+
break
|
|
173
192
|
|
|
174
193
|
if device:
|
|
175
194
|
ns.info1 = device
|
|
@@ -227,7 +246,8 @@ class EfiBootDude:
|
|
|
227
246
|
cmds.append(f'{prefix} --bootnum {ident} --label "{tag}"')
|
|
228
247
|
if self.mods.order:
|
|
229
248
|
orders = [ns.ident for ns in self.digests if ns.is_boot]
|
|
230
|
-
|
|
249
|
+
orders = ','.join(orders)
|
|
250
|
+
cmds.append(f'{prefix} --bootorder {orders}')
|
|
231
251
|
if self.mods.next:
|
|
232
252
|
cmds.append(f'{prefix} --bootnext {self.mods.next}')
|
|
233
253
|
if self.mods.timeout:
|
|
@@ -243,7 +263,7 @@ class EfiBootDude:
|
|
|
243
263
|
os.system('/bin/echo; /bin/echo')
|
|
244
264
|
|
|
245
265
|
for cmd in cmds:
|
|
246
|
-
os.system(f'set -x; {cmd}; /bin/echo " <<<ExitCode=$?>>>"')
|
|
266
|
+
os.system(f'(set -x; {cmd}); /bin/echo " <<<ExitCode=$?>>>"')
|
|
247
267
|
|
|
248
268
|
os.system(r'/bin/echo -e "\n\n===== Press ENTER for menu ====> \c"; read FOO')
|
|
249
269
|
self.reinit()
|
|
@@ -275,7 +295,7 @@ class EfiBootDude:
|
|
|
275
295
|
' * - toggle whether entry is active'
|
|
276
296
|
' m - modify - modify the value'
|
|
277
297
|
' w - write - write the changes',
|
|
278
|
-
'
|
|
298
|
+
' ESC - abandon changes and re-read boot state',
|
|
279
299
|
]
|
|
280
300
|
for line in lines:
|
|
281
301
|
self.win.put_body(line)
|
|
@@ -336,7 +356,6 @@ class EfiBootDude:
|
|
|
336
356
|
actions['m'] = 'modify'
|
|
337
357
|
if self.mods.dirty:
|
|
338
358
|
actions['w'] = 'write'
|
|
339
|
-
actions['f'] = 'fresh'
|
|
340
359
|
|
|
341
360
|
return actions
|
|
342
361
|
|
|
@@ -380,7 +399,7 @@ class EfiBootDude:
|
|
|
380
399
|
answer = 'y'
|
|
381
400
|
if self.mods.dirty:
|
|
382
401
|
answer = self.win.answer(
|
|
383
|
-
prompt='Enter "y" to abandon edits and exit
|
|
402
|
+
prompt='Enter "y" to abandon edits and exit')
|
|
384
403
|
if answer.strip().lower().startswith('y'):
|
|
385
404
|
self.win.stop_curses()
|
|
386
405
|
os.system('clear; stty sane')
|
|
@@ -394,7 +413,7 @@ class EfiBootDude:
|
|
|
394
413
|
seed = ns.label.split()[0]
|
|
395
414
|
while True:
|
|
396
415
|
answer = self.win.answer(
|
|
397
|
-
prompt='Enter timeout seconds or clear to abort
|
|
416
|
+
prompt='Enter timeout seconds or clear to abort',
|
|
398
417
|
seed=seed, width=80)
|
|
399
418
|
seed = answer = answer.strip()
|
|
400
419
|
if not answer:
|
|
@@ -452,7 +471,7 @@ class EfiBootDude:
|
|
|
452
471
|
if key == ord('t') and ns.is_boot:
|
|
453
472
|
seed = ns.label
|
|
454
473
|
while True:
|
|
455
|
-
answer = self.win.answer(prompt='Enter new label or clear to abort
|
|
474
|
+
answer = self.win.answer(prompt='Enter new label or clear to abort',
|
|
456
475
|
seed=seed, width=80)
|
|
457
476
|
seed = answer = answer.strip()
|
|
458
477
|
if not answer:
|
|
@@ -463,10 +482,13 @@ class EfiBootDude:
|
|
|
463
482
|
self.mods.dirty = True
|
|
464
483
|
break
|
|
465
484
|
|
|
466
|
-
if key ==
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
485
|
+
if key == 27: # ESC
|
|
486
|
+
if self.mods.dirty:
|
|
487
|
+
answer = self.win.answer(
|
|
488
|
+
prompt='Enter "y" to clear edits and refresh')
|
|
489
|
+
if answer.strip().lower().startswith('y'):
|
|
490
|
+
self.reinit()
|
|
491
|
+
else:
|
|
470
492
|
self.reinit()
|
|
471
493
|
return None
|
|
472
494
|
|
|
@@ -480,8 +502,12 @@ class EfiBootDude:
|
|
|
480
502
|
|
|
481
503
|
def main():
|
|
482
504
|
""" The program """
|
|
505
|
+
import argparse
|
|
506
|
+
parser = argparse.ArgumentParser()
|
|
507
|
+
parser.add_argument('testfile', nargs='?', default=None)
|
|
508
|
+
opts = parser.parse_args()
|
|
483
509
|
|
|
484
|
-
dude = EfiBootDude()
|
|
510
|
+
dude = EfiBootDude(testfile=opts.testfile)
|
|
485
511
|
dude.main_loop()
|
|
486
512
|
|
|
487
513
|
if __name__ == '__main__':
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: efibootdude
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2
|
|
4
4
|
Summary: A visual wrapper for efibootmgr
|
|
5
5
|
Author-email: Joe Defen <joedef@google.com>
|
|
6
6
|
License: MIT
|
|
@@ -32,21 +32,28 @@ boot menu and parameters while running Linux.
|
|
|
32
32
|
* setting boot entries active or inactive, and
|
|
33
33
|
* setting the boot menu timeout value (until it boots the default entry).
|
|
34
34
|
|
|
35
|
-
To be sure, there are many other esoteric uses of `efibootmanager
|
|
35
|
+
To be sure, there are many other esoteric uses of `efibootmanager` including adding
|
|
36
|
+
a new boot entry; for such needs, just use `efibootmgr` directly.
|
|
36
37
|
|
|
37
38
|
## Usage
|
|
38
|
-
After running `efibootdude
|
|
39
|
+
After running `efibootdude` and making some changes, you'll see a screen comparable to this:
|
|
40
|
+
|
|
39
41
|
.
|
|
42
|
+
|
|
40
43
|
At this point
|
|
41
44
|
* The current line starts with `>` and is highlighted.
|
|
42
|
-
* The top line shows actions for the current line
|
|
45
|
+
* The top line shows actions for the current line; type the underscored letter
|
|
46
|
+
to effect the action.
|
|
43
47
|
* Enter `?` for a more complete explanation of the keys, navigation keys, etc.
|
|
48
|
+
* **ALWAYS** view the help at least once if unfamiliar with this tool,
|
|
49
|
+
it navigation, and/or uncertain of keys not shown on top line.
|
|
44
50
|
* With this current line, we can:
|
|
45
51
|
* Use `u` or `d` to move it up or down in the boot order.
|
|
46
52
|
* Use `t` to relabel the boot entry.
|
|
47
53
|
* Use `r` to remove it.
|
|
48
54
|
* And so forth.
|
|
49
|
-
*
|
|
55
|
+
* Use the `ESC` key to abandon any changes and reload the boot information.
|
|
56
|
+
* When ready to write the changes to the BIOS, enter `w`.
|
|
50
57
|
* When writing the changes, `efibootdude` drops out of menu mode so you can
|
|
51
58
|
verify the underlying commands, error codes, and error messages.
|
|
52
59
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
efibootdude/PowerWindow.py,sha256=pQGXsAMeuiHn-vtEiVG0rgW1eCslh3ukC-VrPhH_j3k,28587
|
|
2
|
+
efibootdude/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
efibootdude/main.py,sha256=opKAafTuw9BpwZl-Q99BmMGVarsf_fEgY-aLjbpeZtA,18460
|
|
4
|
+
efibootdude-0.2.dist-info/LICENSE,sha256=qB9OdnyyF6WYHiEIXVm0rOSdcf8e2ctorrtWs6CC5lU,1062
|
|
5
|
+
efibootdude-0.2.dist-info/METADATA,sha256=HuHORtrDzLH0ItVDm_UbRXsRVRSgBtyBcVEkzODKNCY,3380
|
|
6
|
+
efibootdude-0.2.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
|
|
7
|
+
efibootdude-0.2.dist-info/entry_points.txt,sha256=3KZ_86ZSy4f-weuSruTlMGaQ96jZUm7mjyITVnjE184,54
|
|
8
|
+
efibootdude-0.2.dist-info/top_level.txt,sha256=BrMsK3JmOrVJUNkHX1dicqWrdjbm4itRtmXJMxte-VU,12
|
|
9
|
+
efibootdude-0.2.dist-info/RECORD,,
|
efibootdude-0.0.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
efibootdude/PowerWindow.py,sha256=pQGXsAMeuiHn-vtEiVG0rgW1eCslh3ukC-VrPhH_j3k,28587
|
|
2
|
-
efibootdude/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
efibootdude/main.py,sha256=xl6_bKWJVxzbN31SUWUAWWI5DH5F_iX5HpR-i23JjJ0,17609
|
|
4
|
-
efibootdude-0.0.dist-info/LICENSE,sha256=qB9OdnyyF6WYHiEIXVm0rOSdcf8e2ctorrtWs6CC5lU,1062
|
|
5
|
-
efibootdude-0.0.dist-info/METADATA,sha256=hNAKcQp31odNUlDLHVZ41ngqLq19Y0WuttYgXw08ZtY,2995
|
|
6
|
-
efibootdude-0.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
|
|
7
|
-
efibootdude-0.0.dist-info/entry_points.txt,sha256=3KZ_86ZSy4f-weuSruTlMGaQ96jZUm7mjyITVnjE184,54
|
|
8
|
-
efibootdude-0.0.dist-info/top_level.txt,sha256=BrMsK3JmOrVJUNkHX1dicqWrdjbm4itRtmXJMxte-VU,12
|
|
9
|
-
efibootdude-0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|