diff options
author | mjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com> | 2005-04-28 12:27:47 +0000 |
---|---|---|
committer | mjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com> | 2005-04-28 12:27:47 +0000 |
commit | 51a9f171a5de469910dcf8a4fc1f34e042e17fac (patch) | |
tree | 6f4594dc095baf69fef1a058aa669acd2bd28c2d | |
parent | b4ac7763beeabbc040395b860560d92014ce5005 (diff) | |
download | xen-51a9f171a5de469910dcf8a4fc1f34e042e17fac.tar.gz xen-51a9f171a5de469910dcf8a4fc1f34e042e17fac.tar.bz2 xen-51a9f171a5de469910dcf8a4fc1f34e042e17fac.zip |
bitkeeper revision 1.1327.2.13 (4270d6c35WpxFmhdkREjmSvk82s-Bg)
Merge Jeremy's pygrub bootloader patch.
Signed-off-by: Jeremy Katz <katzj@redhat.com>
Signed-off-by: Mike Wray <mike.wray@hp.com>
-rw-r--r-- | .rootkeys | 10 | ||||
-rw-r--r-- | tools/Makefile | 1 | ||||
-rw-r--r-- | tools/pygrub/Makefile | 18 | ||||
-rw-r--r-- | tools/pygrub/setup.py | 25 | ||||
-rw-r--r-- | tools/pygrub/src/GrubConf.py | 229 | ||||
-rw-r--r-- | tools/pygrub/src/__init__.py | 0 | ||||
-rw-r--r-- | tools/pygrub/src/fsys/__init__.py | 61 | ||||
-rw-r--r-- | tools/pygrub/src/fsys/ext2/__init__.py | 38 | ||||
-rw-r--r-- | tools/pygrub/src/fsys/ext2/ext2module.c | 332 | ||||
-rw-r--r-- | tools/pygrub/src/fsys/ext2/test.py | 15 | ||||
-rw-r--r-- | tools/pygrub/src/pygrub | 270 | ||||
-rw-r--r-- | tools/python/xen/xend/XendBootloader.py | 94 | ||||
-rw-r--r-- | tools/python/xen/xend/XendDomainInfo.py | 40 | ||||
-rwxr-xr-x | tools/python/xen/xend/server/blkif.py | 9 | ||||
-rw-r--r-- | tools/python/xen/xm/create.py | 61 | ||||
-rw-r--r-- | tools/xfrd/xfrd.c | 4 |
16 files changed, 1190 insertions, 17 deletions
@@ -799,6 +799,15 @@ 4107986eMWVdBoz4tXYoOscpN_BCYg tools/misc/xensv 4056f5155QYZdsk-1fLdjsZPFTnlhg tools/misc/xensymoops 40cf2937dqM1jWW87O5OoOYND8leuA tools/misc/xm +4270cc81g3nSNYCZ1ryCMDEbLtMtbQ tools/pygrub/Makefile +4270cc81EIl7NyaS3Av6IPRk2c2a6Q tools/pygrub/setup.py +4270cc81t7eNCDp4Bhbh58p1CNxaCQ tools/pygrub/src/GrubConf.py +4270d6c2fWF4r5-zF1pSuAFwUZS0aA tools/pygrub/src/__init__.py +4270cc81CzKMiujDPWcaYhu709vGXw tools/pygrub/src/fsys/__init__.py +4270cc81RTIiq9si0dI4YRTRE4KRMw tools/pygrub/src/fsys/ext2/__init__.py +4270cc81YCYa4pexivBD2NdLE2F_Pg tools/pygrub/src/fsys/ext2/ext2module.c +4270cc81o4BL5e8Cs87aSi8EXA5NtQ tools/pygrub/src/fsys/ext2/test.py +4270cc81TS6L_tEO6wSp5wcURcpldQ tools/pygrub/src/pygrub 40c9c468icGyC5RAF1bRKsCXPDCvsA tools/python/Makefile 40ffc44dOwe1CcYXGCkYHdG_NxcccA tools/python/logging/logging-0.4.9.2/PKG-INFO 40ffc44dpqpgqgrnLfR70PsiBc3liA tools/python/logging/logging-0.4.9.2/README.txt @@ -896,6 +905,7 @@ 40c9c468Um_qc66OQeLEceIz1pgD5g tools/python/xen/xend/EventServer.py 40c9c468QJTEuk9g4qHxGpmIi70PEQ tools/python/xen/xend/PrettyPrint.py 40e15b7eeQxWE_hUPB2YTgM9fsZ1PQ tools/python/xen/xend/Vifctl.py +4270cc81xbweGYhsM4326N3dX1bGHQ tools/python/xen/xend/XendBootloader.py 40c9c4688m3eqnC8fhLu1APm36VOVA tools/python/xen/xend/XendClient.py 40c9c468t6iIKTjwuYoe-UMCikDcOQ tools/python/xen/xend/XendConsole.py 40c9c468WnXs6eOUSff23IIGI4kMfQ tools/python/xen/xend/XendDB.py diff --git a/tools/Makefile b/tools/Makefile index da2fe19a72..9f4a601d02 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -11,6 +11,7 @@ SUBDIRS += python SUBDIRS += xfrd SUBDIRS += xcs SUBDIRS += ioemu +SUBDIRS += pygrub .PHONY: all install clean check check_clean diff --git a/tools/pygrub/Makefile b/tools/pygrub/Makefile new file mode 100644 index 0000000000..a676cdf0e9 --- /dev/null +++ b/tools/pygrub/Makefile @@ -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 --git a/tools/pygrub/setup.py b/tools/pygrub/setup.py new file mode 100644 index 0000000000..193c160a3b --- /dev/null +++ b/tools/pygrub/setup.py @@ -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 --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py new file mode 100644 index 0000000000..3603b72243 --- /dev/null +++ b/tools/pygrub/src/GrubConf.py @@ -0,0 +1,229 @@ +# +# GrubConf.py - Simple grub.conf parsing +# +# Copyright 2005 Red Hat, Inc. +# Jeremy Katz <katzj@redhat.com> +# +# This software may be freely redistributed under the terms of the GNU +# general public license. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os, sys +import logging + +def grub_split(s, maxsplit = -1): + """Split a grub option screen separated with either '=' or whitespace.""" + eq = s.find('=') + if eq == -1: + return s.split(None, maxsplit) + + # see which of a space or tab is first + sp = s.find(' ') + tab = s.find('\t') + if (tab != -1 and tab < sp) or (tab != -1 and sp == -1): + sp = tab + + if eq != -1 and eq < sp or (eq != -1 and sp == -1): + return s.split('=', maxsplit) + else: + return s.split(None, maxsplit) + +def get_path(s): + """Returns a tuple of (GrubDiskPart, path) corresponding to string.""" + if not s.startswith('('): + return (None, s) + idx = s.find(')') + if idx == -1: + raise ValueError, "Unable to find matching ')'" + d = s[:idx] + return (GrubDiskPart(d), s[idx + 1:]) + +class GrubDiskPart(object): + def __init__(self, str): + if str.find(',') != -1: + (self.disk, self.part) = str.split(",", 2) + else: + self.disk = str + self.part = None + + def __repr__(self): + if self.part is not None: + return "d%dp%d" %(self.disk, self.part) + else: + return "d%d" %(self,disk,) + + def get_disk(self): + return self._disk + def set_disk(self, val): + val = val.replace("(", "").replace(")", "") + self._disk = int(val[2:]) + disk = property(get_disk, set_disk) + + def get_part(self): + return self._part + def set_part(self, val): + if val is None: + self._part = val + return + val = val.replace("(", "").replace(")", "") + self._part = int(val) + part = property(get_part, set_part) + +class GrubImage(object): + def __init__(self, lines): + self._root = self._initrd = self._kernel = self._args = None + for l in lines: + (com, arg) = grub_split(l, 1) + + if self.commands.has_key(com): + if self.commands[com] is not None: + exec("%s = r\"%s\"" %(self.commands[com], arg.strip())) + else: + logging.info("Ignored image directive %s" %(com,)) + else: + logging.warning("Unknown image directive %s" %(com,)) + + def __repr__(self): + return ("title: %s\n" + " root: %s\n" + " kernel: %s\n" + " args: %s\n" + " initrd: %s" %(self.title, self.root, self.kernel, + self.args, self.initrd)) + + def set_root(self, val): + self._root = GrubDiskPart(val) + def get_root(self): + return self._root + root = property(get_root, set_root) + + def set_kernel(self, val): + if val.find(" ") == -1: + self._kernel = get_path(val) + self._args = None + return + (kernel, args) = val.split(None, 1) + self._kernel = get_path(kernel) + self._args = args + def get_kernel(self): + return self._kernel + def get_args(self): + return self._args + kernel = property(get_kernel, set_kernel) + args = property(get_args) + + def set_initrd(self, val): + self._initrd = get_path(val) + def get_initrd(self): + return self._initrd + initrd = property(get_initrd, set_initrd) + + # set up command handlers + commands = { "title": "self.title", + "root": "self.root", + "rootnoverify": "self.root", + "kernel": "self.kernel", + "initrd": "self.initrd", + "chainloader": None, + "module": None} + + +class GrubConfigFile(object): + def __init__(self, fn = None): + self.filename = fn + self.images = [] + self.timeout = -1 + + if fn is not None: + self.parse() + + def parse(self, buf = None): + if buf is None: + if self.filename is None: + raise ValueError, "No config file defined to parse!" + + f = open(self.filename, 'r') + lines = f.readlines() + f.close() + else: + lines = buf.split("\n") + + img = [] + for l in lines: + l = l.strip() + # skip blank lines + if len(l) == 0: + continue + # skip comments + if l.startswith('#'): + continue + # new image + if l.startswith("title"): + if len(img) > 0: + self.images.append(GrubImage(img)) + img = [l] + continue + + if len(img) > 0: + img.append(l) + continue + + try: + (com, arg) = grub_split(l, 1) + except ValueError: + com = l + arg = "" + + if self.commands.has_key(com): + if self.commands[com] is not None: + exec("%s = r\"%s\"" %(self.commands[com], arg.strip())) + else: + logging.info("Ignored directive %s" %(com,)) + else: + logging.warning("Unknown directive %s" %(com,)) + + if len(img) > 0: + self.images.append(GrubImage(img)) + + def _get_default(self): + return self._default + def _set_default(self, val): + if val == "saved": + self._default = -1 + else: + self._default = int(val) + + if self._default < 0: + raise ValueError, "default must be positive number" + default = property(_get_default, _set_default) + + def set_splash(self, val): + self._splash = get_path(val) + def get_splash(self): + return self._splash + splash = property(get_splash, set_splash) + + # set up command handlers + commands = { "default": "self.default", + "timeout": "self.timeout", + "fallback": "self.fallback", + "hiddenmenu": "self.hiddenmenu", + "splashimage": "self.splash", + "password": "self.password" } + for c in ("bootp", "color", "device", "dhcp", "hide", "ifconfig", + "pager", "partnew", "parttype", "rarp", "serial", + "setkey", "terminal", "terminfo", "tftpserver", "unhide"): + commands[c] = None + del c + + +if __name__ == "__main__": + if sys.argv < 2: + raise RuntimeError, "Need a grub.conf to read" + g = GrubConfigFile(sys.argv[1]) + for i in g.images: + print i #, i.title, i.root, i.kernel, i.args, i.initrd diff --git a/tools/pygrub/src/__init__.py b/tools/pygrub/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/pygrub/src/__init__.py diff --git a/tools/pygrub/src/fsys/__init__.py b/tools/pygrub/src/fsys/__init__.py new file mode 100644 index 0000000000..6d76301deb --- /dev/null +++ b/tools/pygrub/src/fsys/__init__.py @@ -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 --git a/tools/pygrub/src/fsys/ext2/__init__.py b/tools/pygrub/src/fsys/ext2/__init__.py new file mode 100644 index 0000000000..ff8f7af48f --- /dev/null +++ b/tools/pygrub/src/fsys/ext2/__init__.py @@ -0,0 +1,38 @@ +# Copyright 2005 Red Hat, Inc. +# Jeremy Katz <katzj@redhat.com> +# +# This software may be freely redistributed under the terms of the GNU +# general public license. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +from grub.fsys import register_fstype, FileSystemType +from _pyext2 import * + +import os, struct + +class Ext2FileSystemType(FileSystemType): + def __init__(self): + FileSystemType.__init__(self) + self.name = "ext2" + + def sniff_magic(self, fn, offset = 0): + fd = os.open(fn, os.O_RDONLY) + os.lseek(fd, offset, 0) + buf = os.read(fd, 2048) + + if len(buf) > 1082 and \ + struct.unpack("<H", buf[1080:1082]) == (0xef53,): + return True + return False + + def open_fs(self, fn, offset = 0): + if not self.sniff_magic(fn, offset): + raise ValueError, "Not an ext2 filesystem" + return Ext2Fs(fn) + +register_fstype(Ext2FileSystemType()) + diff --git a/tools/pygrub/src/fsys/ext2/ext2module.c b/tools/pygrub/src/fsys/ext2/ext2module.c new file mode 100644 index 0000000000..30cfd043ae --- /dev/null +++ b/tools/pygrub/src/fsys/ext2/ext2module.c @@ -0,0 +1,332 @@ +/* + * ext2module.c - simple python binding for libext2fs + * + * Copyright 2005 Red Hat, Inc. + * Jeremy Katz <katzj@redhat.com> + * + * This software may be freely redistributed under the terms of the GNU + * general public license. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <Python.h> + +#include <ext2fs/ext2fs.h> +#include <stdlib.h> +#include <stdio.h> + +#if (PYTHON_API_VERSION >= 1011) +#define PY_PAD 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L +#else +#define PY_PAD 0L,0L,0L,0L +#endif + + +/* global error object */ +PyObject *Ext2Error; + +typedef struct _Ext2Fs Ext2Fs; +struct _Ext2Fs { + PyObject_HEAD; + ext2_filsys fs; +}; + +typedef struct _Ext2File Ext2File; +struct _Ext2File { + PyObject_HEAD; + ext2_file_t file; +}; + +/* ext2 file object */ + +static PyObject * +ext2_file_close (Ext2File *file, PyObject *args) +{ + if (file->file != NULL) + ext2fs_file_close(file->file); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +ext2_file_read (Ext2File *file, PyObject *args) +{ + int err, size = 0; + size_t n, total = 0; + PyObject * buffer = NULL; + + if (file->file == NULL) { + PyErr_SetString(PyExc_ValueError, "Cannot read from closed file"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "|i", &size)) + return NULL; + + buffer = PyString_FromStringAndSize((char *) NULL, (size) ? size : 4096); + if (buffer == NULL) + return buffer; + + while (1) { + err = ext2fs_file_read(file->file, PyString_AS_STRING(buffer) + total, + (size) ? size : 4096, &n); + if (err) { + if (buffer != NULL) { Py_DECREF(buffer); } + Py_DECREF(buffer); + PyErr_SetString(PyExc_ValueError, "read error"); + return NULL; + } + + total += n; + if (n == 0) + break; + + if (size && size == total) + break; + + if (!size) { + _PyString_Resize(&buffer, total + 4096); + } + } + + _PyString_Resize(&buffer, total); + return buffer; +} + +static void +ext2_file_dealloc (Ext2File * file) +{ + if (file->file != NULL) + ext2fs_file_close(file->file); + PyMem_DEL(file); +} + +static struct PyMethodDef Ext2FileMethods[] = { + { "close", + (PyCFunction) ext2_file_close, + METH_VARARGS, NULL }, + { "read", + (PyCFunction) ext2_file_read, + METH_VARARGS, NULL }, + { NULL, NULL, 0, NULL } +}; + +static PyObject * +ext2_file_getattr (Ext2File * file, char * name) +{ + return Py_FindMethod (Ext2FileMethods, (PyObject *) file, name); +} + +static char Ext2FileType__doc__[] = "This is the ext2 filesystem object"; +PyTypeObject Ext2FileType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "Ext2File", /* tp_name */ + sizeof(Ext2File), /* tp_size */ + 0, /* tp_itemsize */ + (destructor) ext2_file_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) ext2_file_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0L, /* tp_flags */ + Ext2FileType__doc__, + PY_PAD +}; + +static PyObject * +ext2_file_open (Ext2Fs *fs, char * name, int flags) +{ + int err; + ext2_file_t f; + ext2_ino_t ino; + Ext2File * file; + + file = (Ext2File *) PyObject_NEW(Ext2File, &Ext2FileType); + file->file = NULL; + + err = ext2fs_namei_follow(fs->fs, EXT2_ROOT_INO, EXT2_ROOT_INO, name, &ino); + if (err) { + PyErr_SetString(PyExc_ValueError, "unable to open file"); + return NULL; + } + + err = ext2fs_file_open(fs->fs, ino, flags, &f); + if (err) { + PyErr_SetString(PyExc_ValueError, "unable to open file"); + return NULL; + } + + file->file = f; + return (PyObject *) file; +} + +/* ext2fs object */ + +static PyObject * +ext2_fs_close (Ext2Fs *fs, PyObject *args) +{ + if (fs->fs != NULL) + ext2fs_close(fs->fs); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +ext2_fs_open (Ext2Fs *fs, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "name", "flags", "superblock", + "block_size", NULL }; + char * name; + int flags = 0, superblock = 0, err; + unsigned int block_size = 0; + ext2_filsys efs; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iii", kwlist, + &name, &flags, &superblock, &block_size)) + return NULL; + + if (fs->fs != NULL) { + PyErr_SetString(PyExc_ValueError, "already have an fs object"); + return NULL; + } + + err = ext2fs_open(name, flags, superblock, block_size, + unix_io_manager, &efs); + if (err) { + PyErr_SetString(PyExc_ValueError, "unable to open file"); + return NULL; + } + + fs->fs = efs; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +ext2_fs_open_file (Ext2Fs *fs, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "name", "flags", NULL }; + char * name; + int flags = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, + &name, &flags)) + return NULL; + + return ext2_file_open(fs, name, flags); +} + +static void +ext2_fs_dealloc (Ext2Fs * fs) +{ + if (fs->fs != NULL) + ext2fs_close(fs->fs); + PyMem_DEL(fs); +} + +static struct PyMethodDef Ext2FsMethods[] = { + { "close", + (PyCFunction) ext2_fs_close, + METH_VARARGS, NULL }, + { "open", + (PyCFunction) ext2_fs_open, + METH_VARARGS|METH_KEYWORDS, NULL }, + { "open_file", + (PyCFunction) ext2_fs_open_file, + METH_VARARGS|METH_KEYWORDS, NULL }, + { NULL, NULL, 0, NULL } +}; + +static PyObject * +ext2_fs_getattr (Ext2Fs * fs, char * name) +{ + return Py_FindMethod (Ext2FsMethods, (PyObject *) fs, name); +} + +static char Ext2FsType__doc__[] = "This is the ext2 filesystem object"; +PyTypeObject Ext2FsType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "Ext2Fs", /* tp_name */ + sizeof(Ext2Fs), /* tp_size */ + 0, /* tp_itemsize */ + (destructor) ext2_fs_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) ext2_fs_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0L, /* tp_flags */ + Ext2FsType__doc__, + PY_PAD +}; + +static PyObject * +ext2_fs_new(PyObject *o, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "name", "flags", "superblock", + "block_size", NULL }; + char * name; + int flags = 0, superblock = 0; + unsigned int block_size = 0; + Ext2Fs *pfs; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iii", kwlist, + &name, &flags, &superblock, &block_size)) + return NULL; + + pfs = (Ext2Fs *) PyObject_NEW(Ext2Fs, &Ext2FsType); + if (pfs == NULL) + return NULL; + pfs->fs = NULL; + + if (!ext2_fs_open(pfs, + Py_BuildValue("siii", name, flags, superblock, block_size), + NULL)) + return NULL; + + return (PyObject *)pfs; +} + + +static struct PyMethodDef Ext2ModuleMethods[] = { + { "Ext2Fs", (PyCFunction) ext2_fs_new, METH_VARARGS|METH_KEYWORDS, NULL }, + { NULL, NULL, 0, NULL } +}; + + +void init_pyext2(void) { + PyObject *m, *d; + + m = Py_InitModule("_pyext2", Ext2ModuleMethods); + d = PyModule_GetDict(m); + + /* o = PyObject_NEW(PyObject, yExt2FsConstructorType); + PyDict_SetItemString(d, "PyExt2Fs", o); + Py_DECREF(o);*/ + +} diff --git a/tools/pygrub/src/fsys/ext2/test.py b/tools/pygrub/src/fsys/ext2/test.py new file mode 100644 index 0000000000..eeb79506ee --- /dev/null +++ b/tools/pygrub/src/fsys/ext2/test.py @@ -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 --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub new file mode 100644 index 0000000000..4439092625 --- /dev/null +++ b/tools/pygrub/src/pygrub @@ -0,0 +1,270 @@ +#!/usr/bin/python +# +# pygrub - simple python-based bootloader for Xen +# +# Copyright 2005 Red Hat, Inc. +# Jeremy Katz <katzj@redhat.com> +# +# This software may be freely redistributed under the terms of the GNU +# general public license. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os, sys, string, struct, tempfile +import logging + +import curses, _curses, curses.wrapper +import getopt + +import grub.GrubConf +import grub.fsys + +PYGRUB_VER = 0.02 + + +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_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 get_entry_idx(cf, entry): + # first, see if the given entry is numeric + try: + idx = string.atoi(entry) + return idx + except ValueError: + pass + + # it's not, now check the labels for a match + for i in range(len(cf.images)): + if entry == cf.images[i].title: + return i + + return None + +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 >> sys.stderr, "Usage: %s [-q|--quiet] [--output=] [--entry=] <image>" %(sys.argv[0],) + + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], 'qh::', + ["quiet", "help", "output=", "entry="]) + except getopt.GetoptError: + usage() + sys.exit(1) + + if len(args) < 1: + usage() + sys.exit(1) + file = args[0] + + output = None + entry = 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 ("--output",): + output = a + elif o in ("--entry",): + entry = a + # specifying the entry to boot implies non-interactive + interactive = False + + 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 + + # set the entry to boot as requested + if entry is not None: + idx = get_entry_idx(cf, entry) + if idx is not None and idx > 0 and idx < len(cf.images): + sel = idx + + 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,) + + sys.stdout.flush() + os.write(fd, sxp) + diff --git a/tools/python/xen/xend/XendBootloader.py b/tools/python/xen/xend/XendBootloader.py new file mode 100644 index 0000000000..09a50f8e8c --- /dev/null +++ b/tools/python/xen/xend/XendBootloader.py @@ -0,0 +1,94 @@ +# +# XendBootloader.py - Framework to run a boot loader for picking the kernel +# +# 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, select +import sxp + +from XendLogging import log +from XendError import VmError + +BL_FIFO = "/var/lib/xen/xenbl" + +def bootloader(blexec, disk, quiet = 0, vcpus = None, entry = None): + """Run the boot loader executable on the given disk and return a + config image. + @param blexec Binary to use as the boot loader + @param disk Disk to run the boot loader on. + @param quiet Run in non-interactive mode, just booting the default. + @param vcpus Number of vcpus for the domain. + @param entry Default entry to boot.""" + + if not os.access(blexec, os.X_OK): + msg = "Bootloader isn't executable" + log.error(msg) + raise VmError(msg) + if not os.access(disk, os.R_OK): + msg = "Disk isn't accessible" + log.error(msg) + raise VmError(msg) + + os.mkfifo(BL_FIFO, 0600) + + child = os.fork() + if (not child): + args = [ blexec ] + if quiet: + args.append("-q") + args.append("--output=%s" %(BL_FIFO,)) + if entry is not None: + args.append("--entry=%s" %(entry,)) + args.append(disk) + + try: + os.execvp(args[0], args) + except OSError, e: + print e + pass + os._exit(1) + + while 1: + try: + r = os.open(BL_FIFO, os.O_RDONLY) + except OSError, e: + if e.errno == 4: + continue + break + ret = "" + while 1: + select.select([r], [], []) + s = os.read(r, 1024) + ret = ret + s + if len(s) == 0: + break + + (pid, status) = os.waitpid(child, 0) + os.close(r) + os.unlink(BL_FIFO) + + if len(ret) == 0: + msg = "Boot loader didn't return any data!" + log.error(msg) + raise VmError, msg + + pin = sxp.Parser() + pin.input(ret) + pin.input_eof() + + config_image = pin.val + if vcpus and sxp.child_value(config_image, "vcpus") is None: + config_image.append(['vcpus', vcpus]) + + config = ['image', config_image] + return config + diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index c5f7ae3d99..36d1cb887d 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -16,11 +16,13 @@ import xen.lowlevel.xc; xc = xen.lowlevel.xc.new() import xen.util.ip from xen.util.ip import _readline, _readlines from xen.xend.server import channel, controller +from xen.xend.server.blkif import blkdev_uname_to_file from server.channel import channelFactory import server.SrvDaemon; xend = server.SrvDaemon.instance() from server import messages +from xen.xend.XendBootloader import bootloader import sxp from XendLogging import log from XendError import VmError @@ -294,6 +296,7 @@ class XendDomainInfo: self.image_handler = None self.is_vmx = False self.vcpus = 1 + self.bootloader = None def setdom(self, dom): """Set the domain id. @@ -496,6 +499,7 @@ class XendDomainInfo: self.find_image_handler() self.init_domain() self.register_domain() + self.configure_bootloader() # Create domain devices. self.configure_backends() @@ -674,6 +678,13 @@ class XendDomainInfo: memory = memory * 1024 + self.pgtable_size(memory) dom = xc.domain_create(dom= dom, mem_kb= memory, cpu= cpu, cpu_weight= cpu_weight) + if self.bootloader: + try: + if kernel: os.unlink(kernel) + if ramdisk: os.unlink(ramdisk) + except OSError, e: + log.warning('unable to unlink kernel/ramdisk: %s' %(e,)) + if dom <= 0: raise VmError('Creating domain failed: name=%s memory=%d' % (self.name, memory)) @@ -854,6 +865,13 @@ class XendDomainInfo: self.config.remove(['device', dev_config]) self.deleteDevice(type, dev.getId()) + def configure_bootloader(self): + """Configure boot loader. + """ + bl = sxp.child_value(self.config, "bootloader") + if bl is not None: + self.bootloader = bl + def configure_console(self): """Configure the vm console port. """ @@ -931,10 +949,30 @@ class XendDomainInfo: self.state = STATE_VM_OK self.restart_check() self.restart_state = STATE_RESTART_BOOTING + if self.bootloader: + self.config = self.bootloader_config() self.construct(self.config) finally: self.restart_state = None + def bootloader_config(self): + # if we're restarting with a bootloader, we need to run it + # FIXME: this assumes the disk is the first device and + # that we're booting from the first disk + blcfg = None + # FIXME: this assumes that we want to use the first disk + dev = sxp.child_value(self.config, "device") + if dev: + disk = sxp.child_value(dev, "uname") + fn = blkdev_uname_to_file(disk) + blcfg = bootloader(self.bootloader, fn, 1, self.vcpus) + if blcfg is None: + msg = "Had a bootloader specified, but can't find disk" + log.error(msg) + raise VmError(msg) + config = sxp.merge(['vm', blconfig ], self.config) + return config + def configure_backends(self): """Set configuration flags if the vm is a backend for netif or blkif. Configure the backends to use for vbd and vif if specified. @@ -1071,6 +1109,7 @@ def vm_image_linux(vm, image): if args: cmdline += " " + args ramdisk = sxp.child_value(image, "ramdisk", '') + log.debug("creating linux domain with cmdline: %s" %(cmdline,)) vm.create_domain("linux", kernel, ramdisk, cmdline) return vm @@ -1169,6 +1208,7 @@ add_config_handler('image', vm_field_ignore) add_config_handler('device', vm_field_ignore) add_config_handler('backend', vm_field_ignore) add_config_handler('vcpus', vm_field_ignore) +add_config_handler('bootloader', vm_field_ignore) # Register other config handlers. add_config_handler('maxmem', vm_field_maxmem) diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py index 10ce6c9eac..6be499d64e 100755 --- a/tools/python/xen/xend/server/blkif.py +++ b/tools/python/xen/xend/server/blkif.py @@ -71,6 +71,15 @@ def blkdev_segment(name): 'type' : 'Disk' } return val +def blkdev_uname_to_file(uname): + """Take a blkdev uname and return the corresponding filename.""" + fn = None + if uname.find(":") != -1: + (typ, fn) = uname.split(":") + if typ == "phy" and not fn.startswith("/dev/"): + fn = "/dev/%s" %(fn,) + return fn + def mount_mode(name): mode = None name = expand_dev_name(name) diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index 82fd0b7a22..5b775d8d57 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -10,6 +10,8 @@ import socket from xen.xend import sxp from xen.xend import PrettyPrint from xen.xend.XendClient import server, XendError +from xen.xend.XendBootloader import bootloader +from xen.xend.server import blkif from xen.util import console_client @@ -94,6 +96,14 @@ gopts.var('name', val='NAME', fn=set_value, default=None, use="Domain name. Must be unique.") +gopts.var('bootloader', val='FILE', + fn=set_value, default=None, + use="Path to bootloader.") + +gopts.var('bootentry', val='NAME', + fn=set_value, default=None, + use="Entry to boot via boot loader") + gopts.var('kernel', val='FILE', fn=set_value, default=None, use="Path to kernel image.") @@ -252,7 +262,7 @@ def strip(pre, s): else: return s -def configure_image(config, vals): +def configure_image(opts, config, vals): """Create the image config. """ config_image = [ vals.builder ] @@ -272,7 +282,7 @@ def configure_image(config, vals): config.append(['image', config_image ]) -def configure_disks(config_devs, vals): +def configure_disks(opts, config_devs, vals): """Create the config for disks (virtual block devices). """ for (uname, dev, mode, backend) in vals.disk: @@ -284,14 +294,14 @@ def configure_disks(config_devs, vals): config_vbd.append(['backend', backend]) config_devs.append(['device', config_vbd]) -def configure_pci(config_devs, vals): +def configure_pci(opts, config_devs, vals): """Create the config for pci devices. """ for (bus, dev, func) in vals.pci: config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]] config_devs.append(['device', config_pci]) -def configure_usb(config_devs, vals): +def configure_usb(opts, config_devs, vals): for path in vals.usb: config_usb = ['usb', ['path', path]] config_devs.append(['device', config_usb]) @@ -315,7 +325,7 @@ def randomMAC(): random.randint(0x00, 0xff) ] return ':'.join(map(lambda x: "%02x" % x, mac)) -def configure_vifs(config_devs, vals): +def configure_vifs(opts, config_devs, vals): """Create the config for virtual network interfaces. """ vifs = vals.vif @@ -357,7 +367,7 @@ def configure_vifs(config_devs, vals): config_vif.append(['ip', ip]) config_devs.append(['device', config_vif]) -def configure_vfr(config, vals): +def configure_vfr(opts, config, vals): if not vals.ipaddr: return config_vfr = ['vfr'] idx = 0 # No way of saying which IP is for which vif? @@ -365,7 +375,7 @@ def configure_vfr(config, vals): config_vfr.append(['vif', ['id', idx], ['ip', ip]]) config.append(config_vfr) -def configure_vmx(config_devs, vals): +def configure_vmx(opts, config_devs, vals): """Create the config for VMX devices. """ memmap = vals.memmap @@ -375,7 +385,21 @@ def configure_vmx(config_devs, vals): config_devs.append(['device_model', device_model]) config_devs.append(['device_config', device_config]) -def make_config(vals): +def run_bootloader(opts, config, vals): + if not os.access(vals.bootloader, os.X_OK): + opts.err("Bootloader isn't executable") + if len(vals.disk) < 1: + opts.err("No disks configured and boot loader requested") + (uname, dev, mode, backend) = vals.disk[0] + file = blkif.blkdev_uname_to_file(uname) + + blcfg = bootloader(vals.bootloader, file, not vals.console_autoconnect, + vals.vcpus, vals.blentry) + + config.append(['bootloader', vals.bootloader]) + config.append(blcfg) + +def make_config(opts, vals): """Create the domain configuration. """ @@ -396,15 +420,19 @@ def make_config(vals): config.append(['restart', vals.restart]) if vals.console: config.append(['console', vals.console]) - - configure_image(config, vals) + + if vals.bootloader: + run_bootloader(opts, config, vals) + else: + configure_image(opts, config, vals) config_devs = [] - configure_disks(config_devs, vals) - configure_pci(config_devs, vals) - configure_vifs(config_devs, vals) - configure_usb(config_devs, vals) - configure_vmx(config_devs, vals) + configure_disks(opts, config_devs, vals) + configure_pci(opts, config_devs, vals) + configure_vifs(opts, config_devs, vals) + configure_usb(opts, config_devs, vals) + configure_vmx(opts, config_devs, vals) config += config_devs + return config def preprocess_disk(opts, vals): @@ -587,7 +615,8 @@ def main(argv): preprocess(opts, opts.vals) if not opts.getopt('name') and opts.getopt('defconfig'): opts.setopt('name', os.path.basename(opts.getopt('defconfig'))) - config = make_config(opts.vals) + config = make_config(opts, opts.vals) + if opts.vals.dryrun: PrettyPrint.prettyprint(config) else: diff --git a/tools/xfrd/xfrd.c b/tools/xfrd/xfrd.c index be91098dbb..89353e0e3b 100644 --- a/tools/xfrd/xfrd.c +++ b/tools/xfrd/xfrd.c @@ -49,7 +49,7 @@ #include "select.h" #define MODULE_NAME "XFRD" - +#define DEBUG 1 #include "debug.h" /* @@ -1226,7 +1226,9 @@ int main(int argc, char *argv[]){ int err = 0; int key = 0; int long_index = 0; +#ifndef DEBUG static const char * LOGFILE = "/var/log/xfrd.log"; +#endif #ifndef DEBUG freopen(LOGFILE, "w+", stdout); |