2
# GrubConf.py - Simple grub.conf parsing
4
# Copyright 2009 Citrix Systems Inc.
5
# Copyright 2005-2006 Red Hat, Inc.
6
# Jeremy Katz <katzj@redhat.com>
8
# This software may be freely redistributed under the terms of the GNU
9
# general public license.
11
# You should have received a copy of the GNU General Public License
12
# along with this program; if not, write to the Free Software
13
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
def grub_split(s, maxsplit = -1):
23
return s.split(None, maxsplit)
25
# see which of a space or tab is first
28
if (tab != -1 and tab < sp) or (tab != -1 and sp == -1):
31
if eq != -1 and eq < sp or (eq != -1 and sp == -1):
32
return s.split('=', maxsplit)
34
return s.split(None, maxsplit)
36
def grub_exact_split(s, num):
37
ret = grub_split(s, num - 1)
39
return ret + [""] * (num - len(ret))
43
"""Returns a tuple of (GrubDiskPart, path) corresponding to string."""
44
if not s.startswith('('):
48
raise ValueError, "Unable to find matching ')'"
50
return (GrubDiskPart(d), s[idx + 1:])
52
class GrubDiskPart(object):
53
def __init__(self, str):
54
if str.find(',') != -1:
55
(self.disk, self.part) = str.split(",", 2)
61
if self.part is not None:
62
return "d%dp%d" %(self.disk, self.part)
64
return "d%d" %(self.disk,)
68
def set_disk(self, val):
69
val = val.replace("(", "").replace(")", "")
70
self._disk = int(val[2:])
71
disk = property(get_disk, set_disk)
75
def set_part(self, val):
79
val = val.replace("(", "").replace(")", "")
81
part = property(get_part, set_part)
83
class _GrubImage(object):
84
def __init__(self, title, lines):
86
self.title = title.strip()
93
" initrd: %s\n" %(self.title, self.root, self.kernel,
94
self.args, self.initrd))
95
def _parse(self, lines):
96
map(self.set_from_line, lines)
98
def reset(self, lines):
99
self._root = self._initrd = self._kernel = self._args = None
103
def set_root(self, val):
104
self._root = GrubDiskPart(val)
107
root = property(get_root, set_root)
109
def set_kernel(self, val):
110
if val.find(" ") == -1:
111
self._kernel = get_path(val)
114
(kernel, args) = val.split(None, 1)
115
self._kernel = get_path(kernel)
117
def get_kernel(self):
121
kernel = property(get_kernel, set_kernel)
122
args = property(get_args)
124
def set_initrd(self, val):
125
self._initrd = get_path(val)
126
def get_initrd(self):
128
initrd = property(get_initrd, set_initrd)
130
class GrubImage(_GrubImage):
131
def __init__(self, title, lines):
132
_GrubImage.__init__(self, title, lines)
134
def set_from_line(self, line, replace = None):
135
(com, arg) = grub_exact_split(line, 2)
137
if self.commands.has_key(com):
138
if self.commands[com] is not None:
139
setattr(self, self.commands[com], arg.strip())
141
logging.info("Ignored image directive %s" %(com,))
143
logging.warning("Unknown image directive %s" %(com,))
145
# now put the line in the list of lines
147
self.lines.append(line)
149
self.lines.pop(replace)
150
self.lines.insert(replace, line)
152
# set up command handlers
153
commands = { "root": "root",
154
"rootnoverify": "root",
160
class _GrubConfigFile(object):
161
def __init__(self, fn = None):
166
self.passwordAccess = True
172
def parse(self, buf = None):
173
raise RuntimeError, "unimplemented parse function"
175
def hasPasswordAccess(self):
176
return self.passwordAccess
178
def setPasswordAccess(self, val):
179
self.passwordAccess = val
181
def hasPassword(self):
182
return hasattr(self, 'password')
184
def checkPassword(self, password):
185
# Always allow if no password defined in grub.conf
186
if not self.hasPassword():
189
pwd = getattr(self, 'password').split()
191
# We check whether password is in MD5 hash for comparison
192
if pwd[0] == '--md5':
195
if crypt.crypt(password, pwd[1]) == pwd[1]:
198
self.passExc = "Can't verify password: %s" % str(e)
201
# ... and if not, we compare it as a plain text
202
if pwd[0] == password:
208
(com, arg) = grub_exact_split(line, 2)
209
if self.commands.has_key(com):
210
if self.commands[com] is not None:
211
setattr(self, self.commands[com], arg.strip())
213
logging.info("Ignored directive %s" %(com,))
215
logging.warning("Unknown directive %s" %(com,))
217
def add_image(self, image):
218
self.images.append(image)
220
def _get_default(self):
222
def _set_default(self, val):
226
self._default = int(val)
228
if self._default < 0:
229
raise ValueError, "default must be positive number"
230
default = property(_get_default, _set_default)
232
def set_splash(self, val):
233
self._splash = get_path(val)
234
def get_splash(self):
236
splash = property(get_splash, set_splash)
238
# set up command handlers
239
commands = { "default": "default",
240
"timeout": "timeout",
241
"fallback": "fallback",
242
"hiddenmenu": "hiddenmenu",
243
"splashimage": "splash",
244
"password": "password" }
245
for c in ("bootp", "color", "device", "dhcp", "hide", "ifconfig",
246
"pager", "partnew", "parttype", "rarp", "serial",
247
"setkey", "terminal", "terminfo", "tftpserver", "unhide"):
251
class GrubConfigFile(_GrubConfigFile):
252
def __init__(self, fn = None):
253
_GrubConfigFile.__init__(self,fn)
255
def new_image(self, title, lines):
256
return GrubImage(title, lines)
258
def parse(self, buf = None):
260
if self.filename is None:
261
raise ValueError, "No config file defined to parse!"
263
f = open(self.filename, 'r')
264
lines = f.readlines()
267
lines = buf.split("\n")
277
if l.startswith('#'):
280
if l.startswith("title"):
282
self.add_image(GrubImage(title, img))
291
(com, arg) = grub_exact_split(l, 2)
292
if self.commands.has_key(com):
293
if self.commands[com] is not None:
294
setattr(self, self.commands[com], arg.strip())
296
logging.info("Ignored directive %s" %(com,))
298
logging.warning("Unknown directive %s" %(com,))
301
self.add_image(GrubImage(title, img))
303
if self.hasPassword():
304
self.setPasswordAccess(False)
306
def grub2_handle_set(arg):
307
(com,arg) = grub_split(arg,2)
309
m = re.match("([\"\'])(.*)\\1", arg)
314
class Grub2Image(_GrubImage):
315
def __init__(self, title, lines):
316
_GrubImage.__init__(self, title, lines)
318
def set_from_line(self, line, replace = None):
319
(com, arg) = grub_exact_split(line, 2)
322
(com,arg) = grub2_handle_set(arg)
324
if self.commands.has_key(com):
325
if self.commands[com] is not None:
326
setattr(self, self.commands[com], arg.strip())
328
logging.info("Ignored image directive %s" %(com,))
329
elif com.startswith('set:'):
332
logging.warning("Unknown image directive %s" %(com,))
334
# now put the line in the list of lines
336
self.lines.append(line)
338
self.lines.pop(replace)
339
self.lines.insert(replace, line)
341
commands = {'set:root': 'root',
348
class Grub2ConfigFile(_GrubConfigFile):
349
def __init__(self, fn = None):
350
_GrubConfigFile.__init__(self, fn)
352
def new_image(self, title, lines):
353
return Grub2Image(title, lines)
355
def parse(self, buf = None):
357
if self.filename is None:
358
raise ValueError, "No config file defined to parse!"
360
f = open(self.filename, 'r')
361
lines = f.readlines()
364
lines = buf.split("\n")
375
if l.startswith('#'):
378
# skip function declarations
379
if l.startswith('function'):
383
if l.startswith('}'):
388
title_match = re.match('^menuentry ["\'](.*)["\'] (.*){', l)
391
raise RuntimeError, "syntax error: cannot nest menuentry (%d %s)" % (len(img),img)
393
title = title_match.group(1)
396
if l.startswith("}"):
398
raise RuntimeError, "syntax error: closing brace without menuentry"
400
self.add_image(Grub2Image(title, img))
408
(com, arg) = grub_exact_split(l, 2)
411
(com,arg) = grub2_handle_set(arg)
413
if self.commands.has_key(com):
414
if self.commands[com] is not None:
415
setattr(self, self.commands[com], arg.strip())
417
logging.info("Ignored directive %s" %(com,))
418
elif com.startswith('set:'):
421
logging.warning("Unknown directive %s" %(com,))
424
raise RuntimeError, "syntax error: end of file with open menuentry(%d %s)" % (len(img),img)
426
if self.hasPassword():
427
self.setPasswordAccess(False)
429
commands = {'set:default': 'default',
431
'set:timeout': 'timeout',
441
if __name__ == "__main__":
443
raise RuntimeError, "Need a grub version (\"grub\" or \"grub2\") and a grub.conf or grub.cfg to read"
444
if sys.argv[1] == "grub":
445
g = GrubConfigFile(sys.argv[2])
446
elif sys.argv[1] == "grub2":
447
g = Grub2ConfigFile(sys.argv[2])
449
raise RuntimeError, "Unknown config type %s" % sys.argv[1]
451
print i #, i.title, i.root, i.kernel, i.args, i.initrd