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,)