This adds pygrub, an implementation of a bootloader for domU. It reads from the guest filesystem (using libext2fs) and provides a grub-like interface for selecting the kernel/initrd/kernel args for booting your domU. TODO list off the top of my head: * Support filesystems other than ext[23] * Support whole disk images and not just a partition image * Implement command line editing/append mode * Possibly the full command line stuff * Password support * Other missing important bits? Even with that, it''s still quite functional. Signed-off-by: Jeremy Katz <katzj@redhat.com> Jeremy _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Christian Limpach
2005-Apr-14 06:03 UTC
Re: [Xen-devel] [PATCH] Guest boot loader support [2/2]
On 4/13/05, Jeremy Katz <katzj@redhat.com> wrote:> This adds pygrub, an implementation of a bootloader for domU. It reads > from the guest filesystem (using libext2fs) and provides a grub-like > interface for selecting the kernel/initrd/kernel args for booting your > domU. > > TODO list off the top of my head: > * Support filesystems other than ext[23]Maybe you could use the filesystem reading code from grub? It seems to support most filesystems, only question is whether it can be reused outside of grub in a userspace application... christian _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
On Wed, 2005-04-13 at 23:03 -0700, Christian Limpach wrote:> On 4/13/05, Jeremy Katz <katzj@redhat.com> wrote: > > This adds pygrub, an implementation of a bootloader for domU. It reads > > from the guest filesystem (using libext2fs) and provides a grub-like > > interface for selecting the kernel/initrd/kernel args for booting your > > domU. > > > > TODO list off the top of my head: > > * Support filesystems other than ext[23] > > Maybe you could use the filesystem reading code from grub? It seems > to support most filesystems, only question is whether it can be reused > outside of grub in a userspace application...The code in grub itself is pretty crufty just because it depends on nothing more than what a BIOS can give you. There''s also a fair bit of grub specific code for handling things like the tab completion in the shell interspersed in the code. So at this point, it just seems that using the standard libraries that are available for the various filesystems is the cleanest . Granted, I could potentially change my mind after looking at libreiserfs and libxfs ;-) Jeremy _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jeremy Katz wrote:> This adds pygrub, an implementation of a bootloader for domU. It reads > from the guest filesystem (using libext2fs) and provides a grub-like > interface for selecting the kernel/initrd/kernel args for booting your > domU. > > TODO list off the top of my head: > * Support filesystems other than ext[23] > * Support whole disk images and not just a partition image > * Implement command line editing/append mode > * Possibly the full command line stuff > * Password support > * Other missing important bits? > > Even with that, it''s still quite functional.I may be wrong, but it looks like the code printing out the sxpr is missing some brackets. See below (almost at the end). Mike> > Signed-off-by: Jeremy Katz <katzj@redhat.com> > > Jeremy > > > ------------------------------------------------------------------------ > > # This is a BitKeeper generated diff -Nru style patch. > # > # ChangeSet > # 2005/04/13 23:02:35-04:00 katzj@bree.local.net > # Add pygrub, a simple boot loader for use to read a boot loader > # config out of a domU''s filesystem and then read kernels out as well. > # Currently only has support for ext[23] via libext2fs, but it should > # be easy to add other filesystems. > # > # Still needs work to complete the interface and to support full disk > # images instead of just partitions. > # > # tools/pygrub/src/pygrub > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +255 -0 > # > # tools/pygrub/src/fsys/ext2/test.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +15 -0 > # > # tools/pygrub/src/fsys/ext2/ext2module.c > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +332 -0 > # > # tools/pygrub/src/fsys/ext2/__init__.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +38 -0 > # > # tools/pygrub/src/fsys/__init__.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +61 -0 > # > # tools/pygrub/src/GrubConf.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +229 -0 > # > # tools/pygrub/setup.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +25 -0 > # > # tools/pygrub/Makefile > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +18 -0 > # > # tools/pygrub/src/pygrub > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/pygrub > # > # tools/pygrub/src/fsys/ext2/test.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/ext2/test.py > # > # tools/pygrub/src/fsys/ext2/ext2module.c > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/ext2/ext2module.c > # > # tools/pygrub/src/fsys/ext2/__init__.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/ext2/__init__.py > # > # tools/pygrub/src/fsys/__init__.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/__init__.py > # > # tools/pygrub/src/GrubConf.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/GrubConf.py > # > # tools/pygrub/setup.py > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/setup.py > # > # tools/pygrub/Makefile > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0 > # BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/Makefile > # > # tools/Makefile > # 2005/04/13 23:02:33-04:00 katzj@bree.local.net +1 -0 > # Add pygrub subdirectory > # > diff -Nru a/tools/Makefile b/tools/Makefile > --- a/tools/Makefile 2005-04-13 23:04:05 -04:00 > +++ b/tools/Makefile 2005-04-13 23:04:05 -04:00 > @@ -11,6 +11,7 @@ > SUBDIRS += xfrd > SUBDIRS += xcs > SUBDIRS += ioemu > +SUBDIRS += pygrub > > .PHONY: all install clean check check_clean > > diff -Nru a/tools/pygrub/Makefile b/tools/pygrub/Makefile > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/Makefile 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,18 @@ > + > +XEN_ROOT = ../.. > +include $(XEN_ROOT)/tools/Rules.mk > + > +all: build > +build: > + CFLAGS="$(CFLAGS)" python setup.py build > + > +ifndef XEN_PYTHON_NATIVE_INSTALL > +install: all > + CFLAGS="$(CFLAGS)" python setup.py install --home="$(DESTDIR)/usr" > +else > +install: all > + CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)" > +endif > + > +clean: > + rm -rf build *.pyc *.pyo *.o *.a *~ > diff -Nru a/tools/pygrub/setup.py b/tools/pygrub/setup.py > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/setup.py 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,25 @@ > +from distutils.core import setup, Extension > +import os > + > +extra_compile_args = [ "-fno-strict-aliasing", "-Wall", "-Werror" ] > + > +# in a perfect world, we''d figure out the fsys modules dynamically > +ext2 = Extension("grub.fsys.ext2._pyext2", > + extra_compile_args = extra_compile_args, > + libraries = ["ext2fs"], > + sources = ["src/fsys/ext2/ext2module.c"]) > + > +setup(name=''pygrub'', > + version=''0.1'', > + description=''Boot loader that looks a lot like grub for Xen'', > + author=''Jeremy Katz'', > + author_email=''katzj@redhat.com'', > + license=''GPL'', > + package_dir={''grub'': ''src''}, > + scripts = ["src/pygrub"], > + packages=[''grub'', > + ''grub.fsys'', > + ''grub.fsys.ext2''], > + ext_modules = [ext2] > + ) > + > diff -Nru a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/src/GrubConf.py 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,229 @@ > +# > +# GrubConf.py - Simple grub.conf parsing > +# > +# Copyright 2005 Red Hat, Inc. > +# Jeremy Katz <katzj@redhat.com> > +# > +# This software may be freely redistributed under the terms of the GNU > +# general public license. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > +# > + > +import os, sys > +import logging > + > +def grub_split(s, maxsplit = -1): > + """Split a grub option screen separated with either ''='' or whitespace.""" > + eq = s.find(''='') > + if eq == -1: > + return s.split(None, maxsplit) > + > + # see which of a space or tab is first > + sp = s.find('' '') > + tab = s.find(''\t'') > + if (tab != -1 and tab < sp) or (tab != -1 and sp == -1): > + sp = tab > + > + if eq != -1 and eq < sp or (eq != -1 and sp == -1): > + return s.split(''='', maxsplit) > + else: > + return s.split(None, maxsplit) > + > +def get_path(s): > + """Returns a tuple of (GrubDiskPart, path) corresponding to string.""" > + if not s.startswith(''(''): > + return (None, s) > + idx = s.find('')'') > + if idx == -1: > + raise ValueError, "Unable to find matching '')''" > + d = s[:idx] > + return (GrubDiskPart(d), s[idx + 1:]) > + > +class GrubDiskPart(object): > + def __init__(self, str): > + if str.find('','') != -1: > + (self.disk, self.part) = str.split(",", 2) > + else: > + self.disk = str > + self.part = None > + > + def __repr__(self): > + if self.part is not None: > + return "d%dp%d" %(self.disk, self.part) > + else: > + return "d%d" %(self,disk,) > + > + def get_disk(self): > + return self._disk > + def set_disk(self, val): > + val = val.replace("(", "").replace(")", "") > + self._disk = int(val[2:]) > + disk = property(get_disk, set_disk) > + > + def get_part(self): > + return self._part > + def set_part(self, val): > + if val is None: > + self._part = val > + return > + val = val.replace("(", "").replace(")", "") > + self._part = int(val) > + part = property(get_part, set_part) > + > +class GrubImage(object): > + def __init__(self, lines): > + self._root = self._initrd = self._kernel = self._args = None > + for l in lines: > + (com, arg) = grub_split(l, 1) > + > + if self.commands.has_key(com): > + if self.commands[com] is not None: > + exec("%s = r\"%s\"" %(self.commands[com], arg.strip())) > + else: > + logging.info("Ignored image directive %s" %(com,)) > + else: > + logging.warning("Unknown image directive %s" %(com,)) > + > + def __repr__(self): > + return ("title: %s\n" > + " root: %s\n" > + " kernel: %s\n" > + " args: %s\n" > + " initrd: %s" %(self.title, self.root, self.kernel, > + self.args, self.initrd)) > + > + def set_root(self, val): > + self._root = GrubDiskPart(val) > + def get_root(self): > + return self._root > + root = property(get_root, set_root) > + > + def set_kernel(self, val): > + if val.find(" ") == -1: > + self._kernel = get_path(val) > + self._args = None > + return > + (kernel, args) = val.split(None, 1) > + self._kernel = get_path(kernel) > + self._args = args > + def get_kernel(self): > + return self._kernel > + def get_args(self): > + return self._args > + kernel = property(get_kernel, set_kernel) > + args = property(get_args) > + > + def set_initrd(self, val): > + self._initrd = get_path(val) > + def get_initrd(self): > + return self._initrd > + initrd = property(get_initrd, set_initrd) > + > + # set up command handlers > + commands = { "title": "self.title", > + "root": "self.root", > + "rootnoverify": "self.root", > + "kernel": "self.kernel", > + "initrd": "self.initrd", > + "chainloader": None, > + "module": None} > + > + > +class GrubConfigFile(object): > + def __init__(self, fn = None): > + self.filename = fn > + self.images = [] > + self.timeout = -1 > + > + if fn is not None: > + self.parse() > + > + def parse(self, buf = None): > + if buf is None: > + if self.filename is None: > + raise ValueError, "No config file defined to parse!" > + > + f = open(self.filename, ''r'') > + lines = f.readlines() > + f.close() > + else: > + lines = buf.split("\n") > + > + img = [] > + for l in lines: > + l = l.strip() > + # skip blank lines > + if len(l) == 0: > + continue > + # skip comments > + if l.startswith(''#''): > + continue > + # new image > + if l.startswith("title"): > + if len(img) > 0: > + self.images.append(GrubImage(img)) > + img = [l] > + continue > + > + if len(img) > 0: > + img.append(l) > + continue > + > + try: > + (com, arg) = grub_split(l, 1) > + except ValueError: > + com = l > + arg = "" > + > + if self.commands.has_key(com): > + if self.commands[com] is not None: > + exec("%s = r\"%s\"" %(self.commands[com], arg.strip())) > + else: > + logging.info("Ignored directive %s" %(com,)) > + else: > + logging.warning("Unknown directive %s" %(com,)) > + > + if len(img) > 0: > + self.images.append(GrubImage(img)) > + > + def _get_default(self): > + return self._default > + def _set_default(self, val): > + if val == "saved": > + self._default = -1 > + else: > + self._default = int(val) > + > + if self._default < 0: > + raise ValueError, "default must be positive number" > + default = property(_get_default, _set_default) > + > + def set_splash(self, val): > + self._splash = get_path(val) > + def get_splash(self): > + return self._splash > + splash = property(get_splash, set_splash) > + > + # set up command handlers > + commands = { "default": "self.default", > + "timeout": "self.timeout", > + "fallback": "self.fallback", > + "hiddenmenu": "self.hiddenmenu", > + "splashimage": "self.splash", > + "password": "self.password" } > + for c in ("bootp", "color", "device", "dhcp", "hide", "ifconfig", > + "pager", "partnew", "parttype", "rarp", "serial", > + "setkey", "terminal", "terminfo", "tftpserver", "unhide"): > + commands[c] = None > + del c > + > + > +if __name__ == "__main__": > + if sys.argv < 2: > + raise RuntimeError, "Need a grub.conf to read" > + g = GrubConfigFile(sys.argv[1]) > + for i in g.images: > + print i #, i.title, i.root, i.kernel, i.args, i.initrd > diff -Nru a/tools/pygrub/src/fsys/__init__.py b/tools/pygrub/src/fsys/__init__.py > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/src/fsys/__init__.py 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,61 @@ > +# > +# Copyright 2005 Red Hat, Inc. > +# Jeremy Katz <katzj@redhat.com> > +# > +# This software may be freely redistributed under the terms of the GNU > +# general public license. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > +# > + > +import os > +import sys > + > +fstypes = {} > + > +def register_fstype(x): > + if x.name in fstypes.keys(): > + return > + fstypes[x.name] = x > + > +class FileSystemType(object): > + """A simple representation for a file system that gives a fs name > + and a method for sniffing a file to see if it''s of the given fstype.""" > + def __init__(self): > + self.name = "" > + > + def sniff_magic(self, fn, offset = 0): > + """Look at the filesystem at fn for the appropriate magic starting at > + offset offset.""" > + raise RuntimeError, "sniff_magic not implemented" > + > + def open_fs(self, fn, offset = 0): > + """Open the given filesystem and return a filesystem object.""" > + raise RuntimeError, "open_fs not implemented" > + > +class FileSystem(object): > + def open(self, name, flags = 0, block_size = 0): > + """Open the fsys on name with given flags and block_size.""" > + raise RuntimeError, "open not implemented" > + > + def close(self): > + """Close the fsys.""" > + raise RuntimeError, "close not implemented" > + > + def open_file(self, file, flags = None): > + """Open the file ''name'' with the given flags. The returned object > + should look similar to a native file object.""" > + raise RuntimeError, "open_file not implemented" > + > + > + > +mydir = sys.modules[''grub.fsys''].__path__[0] > +for f in os.listdir(mydir): > + if not os.path.isdir("%s/%s" %(mydir, f)): > + continue > + try: > + exec "import grub.fsys.%s" %(f,) > + except ImportError, e: > + pass > diff -Nru a/tools/pygrub/src/fsys/ext2/__init__.py b/tools/pygrub/src/fsys/ext2/__init__.py > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/src/fsys/ext2/__init__.py 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,38 @@ > +# Copyright 2005 Red Hat, Inc. > +# Jeremy Katz <katzj@redhat.com> > +# > +# This software may be freely redistributed under the terms of the GNU > +# general public license. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > +# > + > +from grub.fsys import register_fstype, FileSystemType > +from _pyext2 import * > + > +import os, struct > + > +class Ext2FileSystemType(FileSystemType): > + def __init__(self): > + FileSystemType.__init__(self) > + self.name = "ext2" > + > + def sniff_magic(self, fn, offset = 0): > + fd = os.open(fn, os.O_RDONLY) > + os.lseek(fd, offset, 0) > + buf = os.read(fd, 2048) > + > + if len(buf) > 1082 and \ > + struct.unpack("<H", buf[1080:1082]) == (0xef53,): > + return True > + return False > + > + def open_fs(self, fn, offset = 0): > + if not self.sniff_magic(fn, offset): > + raise ValueError, "Not an ext2 filesystem" > + return Ext2Fs(fn) > + > +register_fstype(Ext2FileSystemType()) > + > diff -Nru a/tools/pygrub/src/fsys/ext2/ext2module.c b/tools/pygrub/src/fsys/ext2/ext2module.c > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/src/fsys/ext2/ext2module.c 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,332 @@ > +/* > + * ext2module.c - simple python binding for libext2fs > + * > + * Copyright 2005 Red Hat, Inc. > + * Jeremy Katz <katzj@redhat.com> > + * > + * This software may be freely redistributed under the terms of the GNU > + * general public license. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > +#include <Python.h> > + > +#include <ext2fs/ext2fs.h> > +#include <stdlib.h> > +#include <stdio.h> > + > +#if (PYTHON_API_VERSION >= 1011) > +#define PY_PAD 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L > +#else > +#define PY_PAD 0L,0L,0L,0L > +#endif > + > + > +/* global error object */ > +PyObject *Ext2Error; > + > +typedef struct _Ext2Fs Ext2Fs; > +struct _Ext2Fs { > + PyObject_HEAD; > + ext2_filsys fs; > +}; > + > +typedef struct _Ext2File Ext2File; > +struct _Ext2File { > + PyObject_HEAD; > + ext2_file_t file; > +}; > + > +/* ext2 file object */ > + > +static PyObject * > +ext2_file_close (Ext2File *file, PyObject *args) > +{ > + if (file->file != NULL) > + ext2fs_file_close(file->file); > + Py_INCREF(Py_None); > + return Py_None; > +} > + > +static PyObject * > +ext2_file_read (Ext2File *file, PyObject *args) > +{ > + int err, size = 0; > + size_t n, total = 0; > + PyObject * buffer = NULL; > + > + if (file->file == NULL) { > + PyErr_SetString(PyExc_ValueError, "Cannot read from closed file"); > + return NULL; > + } > + > + if (!PyArg_ParseTuple(args, "|i", &size)) > + return NULL; > + > + buffer = PyString_FromStringAndSize((char *) NULL, (size) ? size : 4096); > + if (buffer == NULL) > + return buffer; > + > + while (1) { > + err = ext2fs_file_read(file->file, PyString_AS_STRING(buffer) + total, > + (size) ? size : 4096, &n); > + if (err) { > + if (buffer != NULL) { Py_DECREF(buffer); } > + Py_DECREF(buffer); > + PyErr_SetString(PyExc_ValueError, "read error"); > + return NULL; > + } > + > + total += n; > + if (n == 0) > + break; > + > + if (size && size == total) > + break; > + > + if (!size) { > + _PyString_Resize(&buffer, total + 4096); > + } > + } > + > + _PyString_Resize(&buffer, total); > + return buffer; > +} > + > +static void > +ext2_file_dealloc (Ext2File * file) > +{ > + if (file->file != NULL) > + ext2fs_file_close(file->file); > + PyMem_DEL(file); > +} > + > +static struct PyMethodDef Ext2FileMethods[] = { > + { "close", > + (PyCFunction) ext2_file_close, > + METH_VARARGS, NULL }, > + { "read", > + (PyCFunction) ext2_file_read, > + METH_VARARGS, NULL }, > + { NULL, NULL, 0, NULL } > +}; > + > +static PyObject * > +ext2_file_getattr (Ext2File * file, char * name) > +{ > + return Py_FindMethod (Ext2FileMethods, (PyObject *) file, name); > +} > + > +static char Ext2FileType__doc__[] = "This is the ext2 filesystem object"; > +PyTypeObject Ext2FileType = { > + PyObject_HEAD_INIT(&PyType_Type) > + 0, /* ob_size */ > + "Ext2File", /* tp_name */ > + sizeof(Ext2File), /* tp_size */ > + 0, /* tp_itemsize */ > + (destructor) ext2_file_dealloc, /* tp_dealloc */ > + 0, /* tp_print */ > + (getattrfunc) ext2_file_getattr, /* tp_getattr */ > + 0, /* tp_setattr */ > + 0, /* tp_compare */ > + 0, /* tp_repr */ > + 0, /* tp_as_number */ > + 0, /* tp_as_sequence */ > + 0, /* tp_as_mapping */ > + 0, /* tp_hash */ > + 0, /* tp_call */ > + 0, /* tp_str */ > + 0, /* tp_getattro */ > + 0, /* tp_setattro */ > + 0, /* tp_as_buffer */ > + 0L, /* tp_flags */ > + Ext2FileType__doc__, > + PY_PAD > +}; > + > +static PyObject * > +ext2_file_open (Ext2Fs *fs, char * name, int flags) > +{ > + int err; > + ext2_file_t f; > + ext2_ino_t ino; > + Ext2File * file; > + > + file = (Ext2File *) PyObject_NEW(Ext2File, &Ext2FileType); > + file->file = NULL; > + > + err = ext2fs_namei_follow(fs->fs, EXT2_ROOT_INO, EXT2_ROOT_INO, name, &ino); > + if (err) { > + PyErr_SetString(PyExc_ValueError, "unable to open file"); > + return NULL; > + } > + > + err = ext2fs_file_open(fs->fs, ino, flags, &f); > + if (err) { > + PyErr_SetString(PyExc_ValueError, "unable to open file"); > + return NULL; > + } > + > + file->file = f; > + return (PyObject *) file; > +} > + > +/* ext2fs object */ > + > +static PyObject * > +ext2_fs_close (Ext2Fs *fs, PyObject *args) > +{ > + if (fs->fs != NULL) > + ext2fs_close(fs->fs); > + Py_INCREF(Py_None); > + return Py_None; > +} > + > +static PyObject * > +ext2_fs_open (Ext2Fs *fs, PyObject *args, PyObject *kwargs) > +{ > + static char *kwlist[] = { "name", "flags", "superblock", > + "block_size", NULL }; > + char * name; > + int flags = 0, superblock = 0, err; > + unsigned int block_size = 0; > + ext2_filsys efs; > + > + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iii", kwlist, > + &name, &flags, &superblock, &block_size)) > + return NULL; > + > + if (fs->fs != NULL) { > + PyErr_SetString(PyExc_ValueError, "already have an fs object"); > + return NULL; > + } > + > + err = ext2fs_open(name, flags, superblock, block_size, > + unix_io_manager, &efs); > + if (err) { > + PyErr_SetString(PyExc_ValueError, "unable to open file"); > + return NULL; > + } > + > + fs->fs = efs; > + > + Py_INCREF(Py_None); > + return Py_None; > +} > + > +static PyObject * > +ext2_fs_open_file (Ext2Fs *fs, PyObject *args, PyObject *kwargs) > +{ > + static char *kwlist[] = { "name", "flags", NULL }; > + char * name; > + int flags = 0; > + > + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, > + &name, &flags)) > + return NULL; > + > + return ext2_file_open(fs, name, flags); > +} > + > +static void > +ext2_fs_dealloc (Ext2Fs * fs) > +{ > + if (fs->fs != NULL) > + ext2fs_close(fs->fs); > + PyMem_DEL(fs); > +} > + > +static struct PyMethodDef Ext2FsMethods[] = { > + { "close", > + (PyCFunction) ext2_fs_close, > + METH_VARARGS, NULL }, > + { "open", > + (PyCFunction) ext2_fs_open, > + METH_VARARGS|METH_KEYWORDS, NULL }, > + { "open_file", > + (PyCFunction) ext2_fs_open_file, > + METH_VARARGS|METH_KEYWORDS, NULL }, > + { NULL, NULL, 0, NULL } > +}; > + > +static PyObject * > +ext2_fs_getattr (Ext2Fs * fs, char * name) > +{ > + return Py_FindMethod (Ext2FsMethods, (PyObject *) fs, name); > +} > + > +static char Ext2FsType__doc__[] = "This is the ext2 filesystem object"; > +PyTypeObject Ext2FsType = { > + PyObject_HEAD_INIT(&PyType_Type) > + 0, /* ob_size */ > + "Ext2Fs", /* tp_name */ > + sizeof(Ext2Fs), /* tp_size */ > + 0, /* tp_itemsize */ > + (destructor) ext2_fs_dealloc, /* tp_dealloc */ > + 0, /* tp_print */ > + (getattrfunc) ext2_fs_getattr, /* tp_getattr */ > + 0, /* tp_setattr */ > + 0, /* tp_compare */ > + 0, /* tp_repr */ > + 0, /* tp_as_number */ > + 0, /* tp_as_sequence */ > + 0, /* tp_as_mapping */ > + 0, /* tp_hash */ > + 0, /* tp_call */ > + 0, /* tp_str */ > + 0, /* tp_getattro */ > + 0, /* tp_setattro */ > + 0, /* tp_as_buffer */ > + 0L, /* tp_flags */ > + Ext2FsType__doc__, > + PY_PAD > +}; > + > +static PyObject * > +ext2_fs_new(PyObject *o, PyObject *args, PyObject *kwargs) > +{ > + static char *kwlist[] = { "name", "flags", "superblock", > + "block_size", NULL }; > + char * name; > + int flags = 0, superblock = 0; > + unsigned int block_size = 0; > + Ext2Fs *pfs; > + > + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iii", kwlist, > + &name, &flags, &superblock, &block_size)) > + return NULL; > + > + pfs = (Ext2Fs *) PyObject_NEW(Ext2Fs, &Ext2FsType); > + if (pfs == NULL) > + return NULL; > + pfs->fs = NULL; > + > + if (!ext2_fs_open(pfs, > + Py_BuildValue("siii", name, flags, superblock, block_size), > + NULL)) > + return NULL; > + > + return (PyObject *)pfs; > +} > + > + > +static struct PyMethodDef Ext2ModuleMethods[] = { > + { "Ext2Fs", (PyCFunction) ext2_fs_new, METH_VARARGS|METH_KEYWORDS, NULL }, > + { NULL, NULL, 0, NULL } > +}; > + > + > +void init_pyext2(void) { > + PyObject *m, *d; > + > + m = Py_InitModule("_pyext2", Ext2ModuleMethods); > + d = PyModule_GetDict(m); > + > + /* o = PyObject_NEW(PyObject, yExt2FsConstructorType); > + PyDict_SetItemString(d, "PyExt2Fs", o); > + Py_DECREF(o);*/ > + > +} > diff -Nru a/tools/pygrub/src/fsys/ext2/test.py b/tools/pygrub/src/fsys/ext2/test.py > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/src/fsys/ext2/test.py 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,15 @@ > +#!/usr/bin/python > + > + > +import _pyext2 > +import struct, os, sys > + > +fs = _pyext2.Ext2Fs("test.img") > + > +f = fs.open_file("/boot/vmlinuz-2.6.11-1.1177_FC4") > +buf = f.read() > +o = open("vmlinuz", "wb+") > +o.write(buf) > +o.close() > + > +f.close() > diff -Nru a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub > --- /dev/null Wed Dec 31 16:00:00 196900 > +++ b/tools/pygrub/src/pygrub 2005-04-13 23:04:05 -04:00 > @@ -0,0 +1,255 @@ > +#!/usr/bin/python > +# > +# pygrub - simple python-based bootloader for Xen > +# > +# Copyright 2005 Red Hat, Inc. > +# Jeremy Katz <katzj@redhat.com> > +# > +# This software may be freely redistributed under the terms of the GNU > +# general public license. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > +# > + > +import os, sys, struct, tempfile > +import logging > + > +import curses, _curses, curses.wrapper > +import getopt > + > +import grub.GrubConf > +import grub.fsys > + > +PYGRUB_VER = 0.01 > + > + > +def draw_window(): > + stdscr = curses.initscr() > + curses.use_default_colors() > + try: > + curses.curs_set(0) > + except _curses.error: > + pass > + > + stdscr.addstr(1, 4, "pyGRUB version %s" %(PYGRUB_VER,)) > + > + win = curses.newwin(10, 74, 2, 1) > + win.box() > + win.refresh() > + > + stdscr.addstr(12, 5, "Use the U and D keys to select which entry is highlighted.") > + stdscr.addstr(13, 5, "Press enter to boot the selected OS. ''e'' to edit the") > + stdscr.addstr(14, 5, "commands before booting, ''a'' to modify the kernel arguments ") > + stdscr.addstr(15, 5, "before booting, or ''c'' for a command line.") > + stdscr.addch(12, 13, curses.ACS_UARROW) > + stdscr.addch(12, 19, curses.ACS_DARROW) > + (y, x) = stdscr.getmaxyx() > + stdscr.move(y - 1, x - 1) > + > + stdscr.refresh() > + return (stdscr, win) > + > +def fill_entries(win, cfg, selected): > + y = 0 > + > + for i in cfg.images: > + if (0, y) > win.getmaxyx(): > + break > + if y == selected: > + attr = curses.A_REVERSE > + else: > + attr = 0 > + win.addstr(y + 1, 2, i.title.ljust(70), attr) > + y += 1 > + win.refresh() > + > +def select(win, line): > + win.attron(curses.A_REVERSE) > + win.redrawln(line + 1, 1) > + win.refresh() > + > +def is_ext2_image(file): > + fd = os.open(file, os.O_RDONLY) > + buf = os.read(fd, 2048) > + os.close(fd) > + > + if len(buf) > 1082 and struct.unpack("<H", buf[1080:1082]) == (0xef53,): > + return True > + return False > + > +def is_disk_image(file): > + fd = os.open(file, os.O_RDONLY) > + buf = os.read(fd, 512) > + os.close(fd) > + > + if len(buf) >= 512 and struct.unpack("H", buf[0x1fe: 0x200]) == (0xaaff): > + return True > + return False > + > + > +def get_config(fn): > + if not os.access(fn, os.R_OK): > + raise RuntimeError, "Unable to access %s" %(fn,) > + > + cf = grub.GrubConf.GrubConfigFile() > + > + if is_disk_image(fn): > + raise RuntimeError, "appears to be a full disk image... unable to handle this yet" > + > + # open the image and read the grub config > + fs = None > + for fstype in grub.fsys.fstypes.values(): > + if fstype.sniff_magic(fn): > + fs = fstype.open_fs(fn) > + break > + > + if fs is not None: > + f = fs.open_file("/boot/grub/grub.conf") > + buf = f.read() > + f.close() > + fs.close() > + # then parse the grub config > + cf.parse(buf) > + else: > + # set the config file and parse it > + cf.filename = fn > + cf.parse() > + > + return cf > + > + > +def main(cf = None): > + mytime = 0 > + > + (stdscr, win) = draw_window() > + stdscr.timeout(1000) > + selected = cf.default > + > + while (mytime < int(cf.timeout)): > + if cf.timeout != -1 and mytime != -1: > + stdscr.addstr(20, 5, "Will boot selected entry in %2d seconds" > + %(int(cf.timeout) - mytime)) > + else: > + stdscr.addstr(20, 5, " " * 80) > + > + fill_entries(win, cf, selected) > + c = stdscr.getch() > + if mytime != -1: > + mytime += 1 > + if c == ord(''q''): > + selected = -1 > + break > + elif c == ord(''c''): > + # FIXME: needs to go to command line mode > + continue > + elif c == ord(''a''): > + # FIXME: needs to go to append mode > + continue > + elif c == ord(''e''): > + # FIXME: needs to go to edit mode > + continue > + elif c in (curses.KEY_ENTER, ord(''\n''), ord(''\r'')): > + break > + elif c == curses.KEY_UP: > + mytime = -1 > + selected -= 1 > + elif c == curses.KEY_DOWN: > + mytime = -1 > + selected += 1 > + else: > + pass > + > + # bound at the top and bottom > + if selected < 0: > + selected = 0 > + elif selected >= len(cf.images): > + selected = len(cf.images) - 1 > + > + if selected >= 0: > + return selected > + > +if __name__ == "__main__": > + sel = None > + > + def run_main(scr, *args): > + global sel > + sel = main(cf) > + > + def usage(): > + print "Usage: %s [-q|--quiet] [-o|--output=] <image>" %(sys.argv[0],) > + > + try: > + opts, args = getopt.getopt(sys.argv[1:], ''qh:o:'', > + ["quiet", "help", "output"]) > + except getopt.GetoptError: > + usage() > + sys.exit(1) > + > + if len(args) < 1: > + usage() > + sys.exit(1) > + print args > + file = args[0] > + > + output = None > + interactive = True > + for o, a in opts: > + if o in ("-q", "--quiet"): > + interactive = False > + elif o in ("-h", "--help"): > + usage() > + sys.exit() > + elif o in ("-o", "--output"): > + output = a > + > + if output is None or output == "-": > + fd = sys.stdout.fileno() > + else: > + fd = os.open(output, os.O_WRONLY) > + > + cf = get_config(file) > + if interactive: > + curses.wrapper(run_main) > + else: > + sel = cf.default > + > + img = cf.images[sel] > + print "Going to boot %s" %(img.title) > + print " kernel: %s" %(img.kernel[1],) > + if img.initrd: > + print " initrd: %s" %(img.initrd[1],) > + > + if is_disk_image(file): > + raise RuntimeError, "unable to handle full disk images yet" > + > + # read the kernel and initrd onto the hostfs > + fs = None > + for fstype in grub.fsys.fstypes.values(): > + if fstype.sniff_magic(file): > + fs = fstype.open_fs(file) > + break > + > + if fs is None: > + raise RuntimeError, "Unable to open filesystem" > + > + kernel = fs.open_file(img.kernel[1],).read() > + (tfd, fn) = tempfile.mkstemp(prefix="vmlinuz.") > + os.write(tfd, kernel) > + os.close(tfd) > + sxp = "linux (kernel %s)" %(fn,) > + > + if img.initrd: > + initrd = fs.open_file(img.initrd[1],).read() > + (tfd, fn) = tempfile.mkstemp(prefix="initrd.") > + os.write(tfd, initrd) > + os.close(tfd) > + sxp += "(ramdisk %s)" %(fn,) > + else: > + initrd = None > + sxp += "(args ''%s'')" %(img.args,)It looks like the code above is missing some brackets in the output: shouldn''t it be writing "(linux ..." instead of "linux ...", and adding a closing ")"? It might be easier if you used lists for this: val = [''linux'', [''kernel'', fn]] val.append([''ramdisk'', fn]) val.append([''args'', img.args]) os.write(fd, sxp.to_string(val)) This will take care of the brackets, and any string quoting needed. BTW, I used ''val'' to avoid colliding with the ''sxp'' module.> + > + sys.stdout.flush() > + os.write(fd, sxp) > + > > > ------------------------------------------------------------------------ > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xensource.com > http://lists.xensource.com/xen-devel_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
On Thu, 2005-04-14 at 13:28 +0100, Mike Wray wrote:> It looks like the code above is missing some brackets in the output: > shouldn''t it be writing "(linux ..." instead of "linux ...", > and adding a closing ")"?Adding them causes problems with nesting inside the image. This is actually tied into the bit where I go to a string and back in the other patch. Given the way things are being passed around, we do actually hae to do the slightly round-about way instead of the most obvious.> It might be easier if you used lists for this: > > val = [''linux'', [''kernel'', fn]] > val.append([''ramdisk'', fn]) > val.append([''args'', img.args]) > os.write(fd, sxp.to_string(val)) > > This will take care of the brackets, and any string quoting needed.But adds a dependency on the sxp module. As it stands now, pygrub is pretty independent of any xen code. The biggest problem with adding such a dep is the path handling for where the sxp module lives. I guess I could be convinced if you really think it''s better Jeremy _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel