debuggers.hg

view tools/pygrub/src/pygrub @ 21048:87ed57b6e4cd

pygrub: Fix grub2 support when config is on a separate /boot partition

Signed-off-by: David Markey <david@dmarkey.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Mar 05 14:39:25 2010 +0000 (2010-03-05)
parents d0b431ede24b
children 5bc70100b514
line source
1 #! /usr/bin/env python
2 #
3 # pygrub - simple python-based bootloader for Xen
4 #
5 # Copyright 2005-2006 Red Hat, Inc.
6 # Jeremy Katz <katzj@redhat.com>
7 #
8 # This software may be freely redistributed under the terms of the GNU
9 # general public license.
10 #
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.
14 #
16 import os, sys, string, struct, tempfile, re
17 import copy
18 import logging
19 import platform
21 import curses, _curses, curses.wrapper, curses.textpad, curses.ascii
22 import getopt
24 import fsimage
25 import grub.GrubConf
26 import grub.LiloConf
27 import grub.ExtLinuxConf
29 PYGRUB_VER = 0.6
31 def enable_cursor(ison):
32 if ison:
33 val = 2
34 else:
35 val = 0
37 try:
38 curses.curs_set(val)
39 except _curses.error:
40 pass
42 def is_disk_image(file):
43 fd = os.open(file, os.O_RDONLY)
44 buf = os.read(fd, 512)
45 os.close(fd)
47 if len(buf) >= 512 and \
48 struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,):
49 return True
50 return False
52 def get_active_partition(file):
53 """Find the offset for the start of the first active partition "
54 "in the disk image file."""
56 fd = os.open(file, os.O_RDONLY)
57 buf = os.read(fd, 512)
58 for poff in (446, 462, 478, 494): # partition offsets
59 # active partition has 0x80 as the first byte
60 if struct.unpack("<c", buf[poff:poff+1]) == ('\x80',):
61 return buf[poff:poff+16]
63 # if there's not a partition marked as active, fall back to
64 # the first partition
65 return buf[446:446+16]
67 SECTOR_SIZE=512
68 DK_LABEL_LOC=1
69 DKL_MAGIC=0xdabe
70 V_ROOT=0x2
72 def get_solaris_slice(file, offset):
73 """Find the root slice in a Solaris VTOC."""
75 fd = os.open(file, os.O_RDONLY)
76 os.lseek(fd, offset + (DK_LABEL_LOC * SECTOR_SIZE), 0)
77 buf = os.read(fd, 512)
78 if struct.unpack("<H", buf[508:510])[0] != DKL_MAGIC:
79 raise RuntimeError, "Invalid disklabel magic"
81 nslices = struct.unpack("<H", buf[30:32])[0]
83 for i in range(nslices):
84 sliceoff = 72 + 12 * i
85 slicetag = struct.unpack("<H", buf[sliceoff:sliceoff+2])[0]
86 slicesect = struct.unpack("<L", buf[sliceoff+4:sliceoff+8])[0]
87 if slicetag == V_ROOT:
88 return slicesect * SECTOR_SIZE
90 raise RuntimeError, "No root slice found"
92 def get_fs_offset_gpt(file):
93 fd = os.open(file, os.O_RDONLY)
94 # assume the first partition is an EFI system partition.
95 os.lseek(fd, SECTOR_SIZE * 2, 0)
96 buf = os.read(fd, 512)
97 return struct.unpack("<Q", buf[32:40])[0] * SECTOR_SIZE
99 FDISK_PART_SOLARIS=0xbf
100 FDISK_PART_SOLARIS_OLD=0x82
101 FDISK_PART_GPT=0xee
103 def get_fs_offset(file):
104 if not is_disk_image(file):
105 return 0
107 partbuf = get_active_partition(file)
108 if len(partbuf) == 0:
109 raise RuntimeError, "Unable to find active partition on disk"
111 offset = struct.unpack("<L", partbuf[8:12])[0] * SECTOR_SIZE
113 type = struct.unpack("<B", partbuf[4:5])[0]
115 if type == FDISK_PART_SOLARIS or type == FDISK_PART_SOLARIS_OLD:
116 offset += get_solaris_slice(file, offset)
118 if type == FDISK_PART_GPT:
119 offset = get_fs_offset_gpt(file)
121 return offset
123 class GrubLineEditor(curses.textpad.Textbox):
124 def __init__(self, screen, startx, starty, line = ""):
125 screen.addstr(startx, starty, "> ")
126 screen.noutrefresh()
127 win = curses.newwin(1, 74, startx, starty + 2)
128 curses.textpad.Textbox.__init__(self, win)
130 self.line = list(line)
131 self.pos = len(line)
132 self.cancelled = False
133 self.show_text()
135 def show_text(self):
136 """Show the text. One of our advantages over standard textboxes
137 is that we can handle lines longer than the window."""
139 self.win.erase()
140 p = self.pos
141 off = 0
142 while p > 70:
143 p -= 55
144 off += 55
146 l = self.line[off:off+70]
147 self.win.addstr(0, 0, string.join(l, ("")))
148 if self.pos > 70:
149 self.win.addch(0, 0, curses.ACS_LARROW)
151 self.win.move(0, p)
153 def do_command(self, ch):
154 # we handle escape as well as moving the line around, so have
155 # to override some of the default handling
157 self.lastcmd = ch
158 if ch == 27: # esc
159 self.cancelled = True
160 return 0
161 elif curses.ascii.isprint(ch):
162 self.line.insert(self.pos, chr(ch))
163 self.pos += 1
164 elif ch == curses.ascii.SOH: # ^a
165 self.pos = 0
166 elif ch in (curses.ascii.STX,curses.KEY_LEFT):
167 if self.pos > 0:
168 self.pos -= 1
169 elif ch in (curses.ascii.BS,curses.KEY_BACKSPACE):
170 if self.pos > 0:
171 self.pos -= 1
172 if self.pos < len(self.line):
173 self.line.pop(self.pos)
174 elif ch == curses.ascii.EOT: # ^d
175 if self.pos < len(self.line):
176 self.line.pop(self.pos)
177 elif ch == curses.ascii.ENQ: # ^e
178 self.pos = len(self.line)
179 elif ch in (curses.ascii.ACK, curses.KEY_RIGHT):
180 if self.pos < len(self.line):
181 self.pos +=1
182 elif ch == curses.ascii.VT: # ^k
183 self.line = self.line[:self.pos]
184 else:
185 return curses.textpad.Textbox.do_command(self, ch)
186 self.show_text()
187 return 1
189 def edit(self):
190 curses.doupdate()
191 r = curses.textpad.Textbox.edit(self)
192 if self.cancelled:
193 return None
194 return string.join(self.line, "")
197 class Grub:
198 def __init__(self, file, fs = None):
199 self.screen = None
200 self.entry_win = None
201 self.text_win = None
202 if file:
203 self.read_config(file, fs)
205 def draw_main_windows(self):
206 if self.screen is None: #only init stuff once
207 self.screen = curses.initscr()
208 self.screen.timeout(1000)
209 if hasattr(curses, 'use_default_colors'):
210 try:
211 curses.use_default_colors()
212 except:
213 pass # Not important if we can't use colour
214 enable_cursor(False)
215 self.entry_win = curses.newwin(10, 74, 2, 1)
216 self.text_win = curses.newwin(10, 70, 12, 5)
217 curses.def_prog_mode()
219 curses.reset_prog_mode()
220 self.screen.erase()
222 # create basic grub screen with a box of entries and a textbox
223 self.screen.addstr(1, 4, "pyGRUB version %s" %(PYGRUB_VER,))
224 self.entry_win.box()
225 self.screen.noutrefresh()
227 def fill_entry_list(self):
228 self.entry_win.erase()
229 self.entry_win.box()
231 maxy = self.entry_win.getmaxyx()[0]-3 # maxy - 2 for the frame + index
232 if self.selected_image > self.start_image + maxy:
233 self.start_image = self.selected_image
234 if self.selected_image < self.start_image:
235 self.start_image = self.selected_image
237 for y in range(self.start_image, len(self.cf.images)):
238 i = self.cf.images[y]
239 if y > self.start_image + maxy:
240 break
241 if y == self.selected_image:
242 self.entry_win.attron(curses.A_REVERSE)
243 self.entry_win.addstr(y + 1 - self.start_image, 2, i.title.expandtabs().ljust(70))
244 if y == self.selected_image:
245 self.entry_win.attroff(curses.A_REVERSE)
246 self.entry_win.noutrefresh()
248 def edit_entry(self, origimg):
249 def draw():
250 self.draw_main_windows()
252 self.text_win.addstr(0, 0, "Use the U and D keys to select which entry is highlighted.")
253 self.text_win.addstr(1, 0, "Press 'b' to boot, 'e' to edit the selected command in the")
254 self.text_win.addstr(2, 0, "boot sequence, 'c' for a command-line, 'o' to open a new line")
255 self.text_win.addstr(3, 0, "after ('O' for before) the selected line, 'd' to remove the")
256 self.text_win.addstr(4, 0, "selected line, or escape to go back to the main menu.")
257 self.text_win.addch(0, 8, curses.ACS_UARROW)
258 self.text_win.addch(0, 14, curses.ACS_DARROW)
259 (y, x) = self.text_win.getmaxyx()
260 self.text_win.move(y - 1, x - 1)
261 self.text_win.noutrefresh()
263 curline = 0
264 img = copy.deepcopy(origimg)
265 while 1:
266 draw()
267 self.entry_win.erase()
268 self.entry_win.box()
269 for idx in range(0, len(img.lines)):
270 # current line should be highlighted
271 if idx == curline:
272 self.entry_win.attron(curses.A_REVERSE)
274 # trim the line
275 l = img.lines[idx].expandtabs().ljust(70)
276 if len(l) > 70:
277 l = l[:69] + ">"
279 self.entry_win.addstr(idx + 1, 2, l)
280 if idx == curline:
281 self.entry_win.attroff(curses.A_REVERSE)
282 self.entry_win.noutrefresh()
283 curses.doupdate()
285 c = self.screen.getch()
286 if c in (ord('q'), 27): # 27 == esc
287 break
288 elif c == curses.KEY_UP:
289 curline -= 1
290 elif c == curses.KEY_DOWN:
291 curline += 1
292 elif c == ord('b'):
293 self.isdone = True
294 break
295 elif c == ord('e'):
296 l = self.edit_line(img.lines[curline])
297 if l is not None:
298 img.set_from_line(l, replace = curline)
299 elif c == ord('d'):
300 img.lines.pop(curline)
301 elif c == ord('o'):
302 img.lines.insert(curline+1, "")
303 curline += 1
304 elif c == ord('O'):
305 img.lines.insert(curline, "")
306 elif c == ord('c'):
307 self.command_line_mode()
308 if self.isdone:
309 return
311 # bound at the top and bottom
312 if curline < 0:
313 curline = 0
314 elif curline >= len(img.lines):
315 curline = len(img.lines) - 1
317 if self.isdone:
318 # Fix to allow pygrub command-line editing in Lilo bootloader (used by IA64)
319 if platform.machine() == 'ia64':
320 origimg.reset(img.lines, img.path)
321 else:
322 origimg.reset(img.lines)
324 def edit_line(self, line):
325 self.screen.erase()
326 self.screen.addstr(1, 2, "[ Minimal BASH-like line editing is supported. ")
327 self.screen.addstr(2, 2, " ESC at any time cancels. ENTER at any time accepts your changes. ]")
328 self.screen.noutrefresh()
330 t = GrubLineEditor(self.screen, 5, 2, line)
331 enable_cursor(True)
332 ret = t.edit()
333 if ret:
334 return ret
335 return None
337 def command_line_mode(self):
338 self.screen.erase()
339 self.screen.addstr(1, 2, "[ Minimal BASH-like line editing is supported. ESC at any time ")
340 self.screen.addstr(2, 2, " exits. Typing 'boot' will boot with your entered commands. ] ")
341 self.screen.noutrefresh()
343 y = 5
344 lines = []
345 while 1:
346 t = GrubLineEditor(self.screen, y, 2)
347 enable_cursor(True)
348 ret = t.edit()
349 if ret:
350 if ret in ("quit", "return"):
351 break
352 elif ret != "boot":
353 y += 1
354 lines.append(ret)
355 continue
357 # if we got boot, then we want to boot the entered image
358 img = grub.GrubConf.GrubImage(lines)
359 self.cf.add_image(img)
360 self.selected_image = len(self.cf.images) - 1
361 self.isdone = True
362 break
364 # else, we cancelled and should just go back
365 break
367 def read_config(self, fn, fs = None):
368 """Read the given file to parse the config. If fs = None, then
369 we're being given a raw config file rather than a disk image."""
371 if not os.access(fn, os.R_OK):
372 raise RuntimeError, "Unable to access %s" %(fn,)
374 if platform.machine() == 'ia64':
375 cfg_list = map(lambda x: (x,grub.LiloConf.LiloConfigFile),
376 # common distributions
377 ["/efi/debian/elilo.conf", "/efi/gentoo/elilo.conf",
378 "/efi/redflag/elilo.conf", "/efi/redhat/elilo.conf",
379 "/efi/SuSE/elilo.conf",] +
380 # fallbacks
381 ["/efi/boot/elilo.conf", "/elilo.conf",])
382 else:
383 cfg_list = map(lambda x: (x,grub.GrubConf.GrubConfigFile),
384 ["/boot/grub/menu.lst", "/boot/grub/grub.conf",
385 "/grub/menu.lst", "/grub/grub.conf"]) + \
386 map(lambda x: (x,grub.GrubConf.Grub2ConfigFile),
387 ["/boot/grub/grub.cfg", "/grub/grub.cfg"]) + \
388 map(lambda x: (x,grub.ExtLinuxConf.ExtLinuxConfigFile),
389 ["/boot/isolinux/isolinux.cfg",
390 "/boot/extlinux.conf"])
392 if not fs:
393 # set the config file and parse it
394 self.cf.filename = fn
395 self.cf.parse()
396 return
398 for f,parser in cfg_list:
399 if fs.file_exists(f):
400 print >>sys.stderr, "Using %s to parse %s" % (parser,f)
401 self.cf = parser()
402 self.cf.filename = f
403 break
404 if self.__dict__.get('cf', None) is None:
405 raise RuntimeError, "couldn't find bootloader config file in the image provided."
406 f = fs.open_file(self.cf.filename)
407 buf = f.read()
408 del f
409 self.cf.parse(buf)
411 def run(self):
412 timeout = int(self.cf.timeout)
414 self.selected_image = self.cf.default
415 self.isdone = False
416 while not self.isdone:
417 self.run_main(timeout)
418 timeout = -1
420 return self.selected_image
422 def run_main(self, timeout = -1):
423 def draw():
424 # set up the screen
425 self.draw_main_windows()
427 if not self.cf.hasPassword() or self.cf.hasPasswordAccess():
428 self.text_win.addstr(0, 0, "Use the U and D keys to select which entry is highlighted.")
429 self.text_win.addstr(1, 0, "Press enter to boot the selected OS, 'e' to edit the")
430 self.text_win.addstr(2, 0, "commands before booting, 'a' to modify the kernel arguments ")
431 self.text_win.addstr(3, 0, "before booting, or 'c' for a command line.")
433 else:
434 self.text_win.addstr(0, 0, "Use the U and D keys to select which entry is highlighted.")
435 self.text_win.addstr(1, 0, "Press enter to boot the selected OS or `p` to enter a")
436 self.text_win.addstr(2, 0, "password to unlock the next set of features.")
438 self.text_win.addch(0, 8, curses.ACS_UARROW)
439 self.text_win.addch(0, 14, curses.ACS_DARROW)
440 (y, x) = self.text_win.getmaxyx()
441 self.text_win.move(y - 1, x - 1)
442 self.text_win.noutrefresh()
444 # now loop until we hit the timeout or get a go from the user
445 mytime = 0
446 self.start_image = 0
447 while (timeout == -1 or mytime < int(timeout)):
448 draw()
449 if timeout != -1 and mytime != -1:
450 self.screen.addstr(20, 5, "Will boot selected entry in %2d seconds"
451 %(int(timeout) - mytime))
452 else:
453 self.screen.addstr(20, 5, " " * 80)
454 self.fill_entry_list()
455 curses.doupdate()
457 c = self.screen.getch()
458 if c == -1:
459 # Timed out waiting for a keypress
460 if mytime != -1:
461 mytime += 1
462 if mytime >= int(timeout):
463 self.isdone = True
464 break
465 else:
466 # received a keypress: stop the timer
467 mytime = -1
468 self.screen.timeout(-1)
470 # handle keypresses
471 if c == ord('c') and self.cf.hasPasswordAccess():
472 self.command_line_mode()
473 break
474 elif c == ord('a') and self.cf.hasPasswordAccess():
475 # find the kernel line, edit it and then boot
476 img = self.cf.images[self.selected_image]
477 for line in img.lines:
478 if line.startswith("kernel"):
479 l = self.edit_line(line)
480 if l is not None:
481 img.set_from_line(l, replace = True)
482 self.isdone = True
483 break
484 break
485 elif c == ord('e') and self.cf.hasPasswordAccess():
486 img = self.cf.images[self.selected_image]
487 self.edit_entry(img)
488 break
489 elif c == ord('p') and self.cf.hasPassword():
490 self.text_win.addstr(6, 1, "Password: ")
491 pwd = self.text_win.getstr(6, 8)
492 if not self.cf.checkPassword(pwd):
493 self.text_win.addstr(6, 1, "Password: ")
494 if self.cf.passExc is not None:
495 self.text_win.addstr(7, 0, "Exception: %s"
496 % self.cf.passExc)
497 else:
498 self.text_win.addstr(7, 0, "Failed!")
499 self.cf.setPasswordAccess( False )
500 else:
501 self.cf.setPasswordAccess( True )
502 break
503 elif c in (curses.KEY_ENTER, ord('\n'), ord('\r')):
504 self.isdone = True
505 break
506 elif c == curses.KEY_UP:
507 self.selected_image -= 1
508 elif c == curses.KEY_DOWN:
509 self.selected_image += 1
510 # elif c in (ord('q'), 27): # 27 == esc
511 # self.selected_image = -1
512 # self.isdone = True
513 # break
515 # bound at the top and bottom
516 if self.selected_image < 0:
517 self.selected_image = 0
518 elif self.selected_image >= len(self.cf.images):
519 self.selected_image = len(self.cf.images) - 1
521 def get_entry_idx(cf, entry):
522 # first, see if the given entry is numeric
523 try:
524 idx = string.atoi(entry)
525 return idx
526 except ValueError:
527 pass
529 # it's not, now check the labels for a match
530 for i in range(len(cf.images)):
531 if entry == cf.images[i].title:
532 return i
534 return None
536 def run_grub(file, entry, fs, arg):
537 global g
538 global sel
540 def run_main(scr, *args):
541 global sel
542 global g
543 sel = g.run()
545 g = Grub(file, fs)
546 if interactive:
547 curses.wrapper(run_main)
548 else:
549 sel = g.cf.default
551 # set the entry to boot as requested
552 if entry is not None:
553 idx = get_entry_idx(g.cf, entry)
554 if idx is not None and idx > 0 and idx < len(g.cf.images):
555 sel = idx
557 if sel == -1:
558 print "No kernel image selected!"
559 sys.exit(1)
561 try:
562 img = g.cf.images[sel]
563 except IndexError:
564 img = g.cf.images[0]
566 grubcfg = { "kernel": None, "ramdisk": None, "args": None }
568 grubcfg["kernel"] = img.kernel[1]
569 if img.initrd:
570 grubcfg["ramdisk"] = img.initrd[1]
571 if img.args:
572 grubcfg["args"] = img.args + " " + arg
574 return grubcfg
576 # If nothing has been specified, look for a Solaris domU. If found, perform the
577 # necessary tweaks.
578 def sniff_solaris(fs, cfg):
579 if not fs.file_exists("/platform/i86xpv/kernel/unix"):
580 return cfg
582 # darned python
583 longmode = (sys.maxint != 2147483647L)
584 if not longmode:
585 longmode = os.uname()[4] == "x86_64"
586 if not longmode:
587 if (os.access("/usr/bin/isainfo", os.R_OK) and
588 os.popen("/usr/bin/isainfo -b").read() == "64\n"):
589 longmode = True
591 if not cfg["kernel"]:
592 cfg["kernel"] = "/platform/i86xpv/kernel/unix"
593 cfg["ramdisk"] = "/platform/i86pc/boot_archive"
594 if longmode:
595 cfg["kernel"] = "/platform/i86xpv/kernel/amd64/unix"
596 cfg["ramdisk"] = "/platform/i86pc/amd64/boot_archive"
598 # Unpleasant. Typically we'll have 'root=foo -k' or 'root=foo /kernel -k',
599 # and we need to maintain Xen properties (root= and ip=) and the kernel
600 # before any user args.
602 xenargs = ""
603 userargs = ""
605 if not cfg["args"]:
606 cfg["args"] = cfg["kernel"]
607 else:
608 for arg in cfg["args"].split():
609 if re.match("^root=", arg) or re.match("^ip=", arg):
610 xenargs += arg + " "
611 elif arg != cfg["kernel"]:
612 userargs += arg + " "
613 cfg["args"] = xenargs + " " + cfg["kernel"] + " " + userargs
615 return cfg
617 def sniff_netware(fs, cfg):
618 if not fs.file_exists("/nwserver/xnloader.sys"):
619 return cfg
621 if not cfg["kernel"]:
622 cfg["kernel"] = "/nwserver/xnloader.sys"
624 return cfg
626 if __name__ == "__main__":
627 sel = None
629 def usage():
630 print >> sys.stderr, "Usage: %s [-q|--quiet] [-i|--interactive] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] <image>" %(sys.argv[0],)
632 try:
633 opts, args = getopt.gnu_getopt(sys.argv[1:], 'qih::',
634 ["quiet", "interactive", "help", "output=",
635 "entry=", "kernel=", "ramdisk=", "args=",
636 "isconfig"])
637 except getopt.GetoptError:
638 usage()
639 sys.exit(1)
641 if len(args) < 1:
642 usage()
643 sys.exit(1)
644 file = args[0]
646 output = None
647 entry = None
648 interactive = True
649 isconfig = False
651 # what was passed in
652 incfg = { "kernel": None, "ramdisk": None, "args": "" }
653 # what grub or sniffing chose
654 chosencfg = { "kernel": None, "ramdisk": None, "args": None }
655 # what to boot
656 bootcfg = { "kernel": None, "ramdisk": None, "args": None }
658 for o, a in opts:
659 if o in ("-q", "--quiet"):
660 interactive = False
661 elif o in ("-i", "--interactive"):
662 interactive = True
663 elif o in ("-h", "--help"):
664 usage()
665 sys.exit()
666 elif o in ("--output",):
667 output = a
668 elif o in ("--kernel",):
669 incfg["kernel"] = a
670 elif o in ("--ramdisk",):
671 incfg["ramdisk"] = a
672 elif o in ("--args",):
673 incfg["args"] = a
674 elif o in ("--entry",):
675 entry = a
676 # specifying the entry to boot implies non-interactive
677 interactive = False
678 elif o in ("--isconfig",):
679 isconfig = True
681 if output is None or output == "-":
682 fd = sys.stdout.fileno()
683 else:
684 fd = os.open(output, os.O_WRONLY)
686 # debug
687 if isconfig:
688 chosencfg = run_grub(file, entry, fs, incfg["args"])
689 print " kernel: %s" % chosencfg["kernel"]
690 if img.initrd:
691 print " initrd: %s" % chosencfg["ramdisk"]
692 print " args: %s" % chosencfg["args"]
693 sys.exit(0)
695 # if boot filesystem is set then pass to fsimage.open
696 bootfsargs = '"%s"' % incfg["args"]
697 bootfsgroup = re.findall('zfs-bootfs=(.*?)[\s\,\"]', bootfsargs)
698 if bootfsgroup:
699 fs = fsimage.open(file, get_fs_offset(file), bootfsgroup[0])
700 else:
701 fs = fsimage.open(file, get_fs_offset(file))
703 chosencfg = sniff_solaris(fs, incfg)
705 if not chosencfg["kernel"]:
706 chosencfg = sniff_netware(fs, incfg)
708 if not chosencfg["kernel"]:
709 chosencfg = run_grub(file, entry, fs, incfg["args"])
711 data = fs.open_file(chosencfg["kernel"]).read()
712 (tfd, bootcfg["kernel"]) = tempfile.mkstemp(prefix="boot_kernel.",
713 dir="/var/run/xend/boot")
714 os.write(tfd, data)
715 os.close(tfd)
717 if chosencfg["ramdisk"]:
718 data = fs.open_file(chosencfg["ramdisk"],).read()
719 (tfd, bootcfg["ramdisk"]) = tempfile.mkstemp(prefix="boot_ramdisk.",
720 dir="/var/run/xend/boot")
721 os.write(tfd, data)
722 os.close(tfd)
723 else:
724 initrd = None
726 sxp = "linux (kernel %s)" % bootcfg["kernel"]
727 if bootcfg["ramdisk"]:
728 sxp += "(ramdisk %s)" % bootcfg["ramdisk"]
729 if chosencfg["args"]:
730 zfsinfo = fsimage.getbootstring(fs)
731 if zfsinfo is None:
732 sxp += "(args \"%s\")" % chosencfg["args"]
733 else:
734 e = re.compile("zfs-bootfs=[\w\-\.\:@/]+" )
735 (chosencfg["args"],count) = e.subn(zfsinfo, chosencfg["args"])
736 if count == 0:
737 chosencfg["args"] += " -B %s" % zfsinfo
738 sxp += "(args \"%s\")" % (chosencfg["args"])
740 sys.stdout.flush()
741 os.write(fd, sxp)