[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH] Guest boot loader support [2/2]



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@xxxxxxxxxx>

Jeremy


------------------------------------------------------------------------

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
# 2005/04/13 23:02:35-04:00 katzj@xxxxxxxxxxxxxx # 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@xxxxxxxxxxxxxx +255 -0
# # tools/pygrub/src/fsys/ext2/test.py
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +15 -0
# # tools/pygrub/src/fsys/ext2/ext2module.c
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +332 -0
# # tools/pygrub/src/fsys/ext2/__init__.py
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +38 -0
# # tools/pygrub/src/fsys/__init__.py
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +61 -0
# # tools/pygrub/src/GrubConf.py
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +229 -0
# # tools/pygrub/setup.py
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +25 -0
# # tools/pygrub/Makefile
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +18 -0
# # tools/pygrub/src/pygrub
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +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@xxxxxxxxxxxxxx +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@xxxxxxxxxxxxxx +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@xxxxxxxxxxxxxx +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@xxxxxxxxxxxxxx +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@xxxxxxxxxxxxxx +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@xxxxxxxxxxxxxx +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@xxxxxxxxxxxxxx +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/Makefile
# # tools/Makefile
#   2005/04/13 23:02:33-04:00 katzj@xxxxxxxxxxxxxx +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@xxxxxxxxxx',
+      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@xxxxxxxxxx>
+#
+# 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@xxxxxxxxxx>
+#
+# 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@xxxxxxxxxx>
+#
+# 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@xxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxx>
+#
+# 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@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.