ia64/xen-unstable

view tools/pygrub/src/pygrub @ 8323:566395e5a14f

Minor tidy.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Mon Dec 12 16:32:50 2005 +0000 (2005-12-12)
parents d5775d0bcf1e
children 859c8d66b203
line source
1 #!/usr/bin/python
2 #
3 # pygrub - simple python-based bootloader for Xen
4 #
5 # Copyright 2005 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
17 import logging
19 import curses, _curses, curses.wrapper
20 import getopt
22 sys.path = [ '/usr/lib/python' ] + sys.path
24 import grub.GrubConf
25 import grub.fsys
27 PYGRUB_VER = 0.3
30 def draw_window():
31 stdscr = curses.initscr()
32 curses.use_default_colors()
33 try:
34 curses.curs_set(0)
35 except _curses.error:
36 pass
38 stdscr.addstr(1, 4, "pyGRUB version %s" %(PYGRUB_VER,))
40 win = curses.newwin(10, 74, 2, 1)
41 win.box()
42 win.refresh()
44 stdscr.addstr(12, 5, "Use the U and D keys to select which entry is highlighted.")
45 stdscr.addstr(13, 5, "Press enter to boot the selected OS. 'e' to edit the")
46 stdscr.addstr(14, 5, "commands before booting, 'a' to modify the kernel arguments ")
47 stdscr.addstr(15, 5, "before booting, or 'c' for a command line.")
48 stdscr.addch(12, 13, curses.ACS_UARROW)
49 stdscr.addch(12, 19, curses.ACS_DARROW)
50 (y, x) = stdscr.getmaxyx()
51 stdscr.move(y - 1, x - 1)
53 stdscr.refresh()
54 return (stdscr, win)
56 def fill_entries(win, cfg, selected):
57 y = 0
59 for i in cfg.images:
60 if (0, y) > win.getmaxyx():
61 break
62 if y == selected:
63 attr = curses.A_REVERSE
64 else:
65 attr = 0
66 win.addstr(y + 1, 2, i.title.ljust(70), attr)
67 y += 1
68 win.refresh()
70 def select(win, line):
71 win.attron(curses.A_REVERSE)
72 win.redrawln(line + 1, 1)
73 win.refresh()
75 def is_disk_image(file):
76 fd = os.open(file, os.O_RDONLY)
77 buf = os.read(fd, 512)
78 os.close(fd)
80 if len(buf) >= 512 and struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,):
81 return True
82 return False
84 SECTOR_SIZE=512
85 def get_active_offset(file):
86 """Find the offset for the start of the first active partition in the
87 disk image file."""
88 fd = os.open(file, os.O_RDONLY)
89 buf = os.read(fd, 512)
90 for poff in (446, 462, 478, 494): # partition offsets
91 # active partition has 0x80 as the first byte
92 if struct.unpack("<c", buf[poff:poff+1]) == ('\x80',):
93 return struct.unpack("<L", buf[poff+8:poff+12])[0] * SECTOR_SIZE
94 return -1
96 def get_config(fn):
97 if not os.access(fn, os.R_OK):
98 raise RuntimeError, "Unable to access %s" %(fn,)
100 cf = grub.GrubConf.GrubConfigFile()
102 offset = 0
103 if is_disk_image(fn):
104 offset = get_active_offset(fn)
105 if offset == -1:
106 raise RuntimeError, "Unable to find active partition on disk"
108 # open the image and read the grub config
109 fs = None
110 for fstype in grub.fsys.fstypes.values():
111 if fstype.sniff_magic(fn, offset):
112 fs = fstype.open_fs(fn, offset)
113 break
115 if fs is not None:
116 grubfile = None
117 for f in ("/boot/grub/menu.lst", "/boot/grub/grub.conf",
118 "/grub/menu.lst", "/grub/grub.conf"):
119 if fs.file_exist(f):
120 grubfile = f
121 break
122 if grubfile is None:
123 raise RuntimeError, "we couldn't find /boot/grub{menu.lst,grub.conf} " + \
124 "in the image provided. halt!"
125 f = fs.open_file(grubfile)
126 buf = f.read()
127 f.close()
128 fs.close()
129 # then parse the grub config
130 cf.parse(buf)
131 else:
132 # set the config file and parse it
133 cf.filename = fn
134 cf.parse()
136 return cf
138 def get_entry_idx(cf, entry):
139 # first, see if the given entry is numeric
140 try:
141 idx = string.atoi(entry)
142 return idx
143 except ValueError:
144 pass
146 # it's not, now check the labels for a match
147 for i in range(len(cf.images)):
148 if entry == cf.images[i].title:
149 return i
151 return None
153 def main(cf = None):
154 mytime = 0
156 (stdscr, win) = draw_window()
157 stdscr.timeout(1000)
158 selected = cf.default
160 while (mytime < int(cf.timeout)):
161 if cf.timeout != -1 and mytime != -1:
162 stdscr.addstr(20, 5, "Will boot selected entry in %2d seconds"
163 %(int(cf.timeout) - mytime))
164 else:
165 stdscr.addstr(20, 5, " " * 80)
167 fill_entries(win, cf, selected)
168 c = stdscr.getch()
169 if mytime != -1:
170 mytime += 1
171 # if c == ord('q'):
172 # selected = -1
173 # break
174 if c == ord('c'):
175 # FIXME: needs to go to command line mode
176 continue
177 elif c == ord('a'):
178 # FIXME: needs to go to append mode
179 continue
180 elif c == ord('e'):
181 # FIXME: needs to go to edit mode
182 continue
183 elif c in (curses.KEY_ENTER, ord('\n'), ord('\r')):
184 break
185 elif c == curses.KEY_UP:
186 mytime = -1
187 selected -= 1
188 elif c == curses.KEY_DOWN:
189 mytime = -1
190 selected += 1
191 else:
192 pass
194 # bound at the top and bottom
195 if selected < 0:
196 selected = 0
197 elif selected >= len(cf.images):
198 selected = len(cf.images) - 1
200 if selected >= 0:
201 return selected
203 if __name__ == "__main__":
204 sel = None
206 def run_main(scr, *args):
207 global sel
208 sel = main(cf)
210 def usage():
211 print >> sys.stderr, "Usage: %s [-q|--quiet] [--output=] [--entry=] <image>" %(sys.argv[0],)
213 try:
214 opts, args = getopt.gnu_getopt(sys.argv[1:], 'qh::',
215 ["quiet", "help", "output=", "entry="])
216 except getopt.GetoptError:
217 usage()
218 sys.exit(1)
220 if len(args) < 1:
221 usage()
222 sys.exit(1)
223 file = args[0]
225 output = None
226 entry = None
227 interactive = True
228 for o, a in opts:
229 if o in ("-q", "--quiet"):
230 interactive = False
231 elif o in ("-h", "--help"):
232 usage()
233 sys.exit()
234 elif o in ("--output",):
235 output = a
236 elif o in ("--entry",):
237 entry = a
238 # specifying the entry to boot implies non-interactive
239 interactive = False
241 if output is None or output == "-":
242 fd = sys.stdout.fileno()
243 else:
244 fd = os.open(output, os.O_WRONLY)
246 cf = get_config(file)
247 if interactive:
248 curses.wrapper(run_main)
249 else:
250 sel = cf.default
252 # set the entry to boot as requested
253 if entry is not None:
254 idx = get_entry_idx(cf, entry)
255 if idx is not None and idx > 0 and idx < len(cf.images):
256 sel = idx
258 img = cf.images[sel]
259 print "Going to boot %s" %(img.title)
260 print " kernel: %s" %(img.kernel[1],)
261 if img.initrd:
262 print " initrd: %s" %(img.initrd[1],)
264 offset = 0
265 if is_disk_image(file):
266 offset = get_active_offset(file)
267 if offset == -1:
268 raise RuntimeError, "Unable to find active partition on disk"
270 # read the kernel and initrd onto the hostfs
271 fs = None
272 for fstype in grub.fsys.fstypes.values():
273 if fstype.sniff_magic(file, offset):
274 fs = fstype.open_fs(file, offset)
275 break
277 if fs is None:
278 raise RuntimeError, "Unable to open filesystem"
280 kernel = fs.open_file(img.kernel[1],).read()
281 (tfd, fn) = tempfile.mkstemp(prefix="vmlinuz.")
282 os.write(tfd, kernel)
283 os.close(tfd)
284 sxp = "linux (kernel %s)" %(fn,)
286 if img.initrd:
287 initrd = fs.open_file(img.initrd[1],).read()
288 (tfd, fn) = tempfile.mkstemp(prefix="initrd.")
289 os.write(tfd, initrd)
290 os.close(tfd)
291 sxp += "(ramdisk %s)" %(fn,)
292 else:
293 initrd = None
294 sxp += "(args '%s')" %(img.args,)
296 sys.stdout.flush()
297 os.write(fd, sxp)