From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mike Wray Subject: Re: [PATCH] Guest boot loader support [2/2] Date: Thu, 14 Apr 2005 13:28:47 +0100 Message-ID: <425E61FF.8080809@hpl.hp.com> References: <1113448377.5078.51.camel@bree.local.net> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1113448377.5078.51.camel@bree.local.net> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: Jeremy Katz Cc: Ian Pratt , xen-devel@lists.xensource.com List-Id: xen-devel@lists.xenproject.org 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 > > 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 > +# > +# 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 > +# > +# 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 > +# > +# 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(" + 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 > + * > + * 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 > + > +#include > +#include > +#include > + > +#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 > +# > +# 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(" + 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=] " %(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