All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Guest boot loader support
@ 2005-04-25 22:50 Jeremy Katz
  2005-04-26  9:32 ` aq
  2005-04-26 20:11 ` Jacob Gorm Hansen
  0 siblings, 2 replies; 17+ messages in thread
From: Jeremy Katz @ 2005-04-25 22:50 UTC (permalink / raw)
  To: xen-devel@lists.xensource.com

[-- Attachment #1: Type: text/plain, Size: 1351 bytes --]

Attached is an updated version of the patch to add boot loader support
for guest domains.  

Changes from the initial set of patches:
* Per Ian's request, adds an option to specify booting a specific kernel
from the host domain (you can specify it either with the title of the
boot loader config entry or with the grub 0-based index)
* Reduces some of the code duplication for finding out what the virtual
disk to boot off of is
* Improved error handling to use the standard Xen logging and error
facilities

At this point, I think that we handle the use cases where a user just
wants to have everything specified from within the guest domain,
creating a domain with xm create and wanting to be able to interactively
select a kernel and asking for a specific entry from the grub.conf to be
booted when creating a domain.

Future things (which I think are fine to do after the initial pass is
committed) include
* Reworking to use the planned new console infrastructure [1]
* Handling whole disks
* More filesystem support -- the code is generic and has all the hooks,
I just haven't delved deep enough into any of the other fs libraries
* Further improvements for the menu interface

Signed-off-by: Jeremy Katz <katzj@redhat.com>

Jeremy

[1] And I foolishly told Ian I'd start a discussion about this last week
and still haven't.  Hopefully tonight.

[-- Attachment #2: xen-bootloader.patch --]
[-- Type: text/x-patch, Size: 45444 bytes --]

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2005/04/25 18:25:47-04:00 katzj@bree.local.net 
#   Add passing of booting a specific entry by label or by index
# 
# tools/python/xen/xm/create.py
#   2005/04/25 18:25:45-04:00 katzj@bree.local.net +6 -2
#   allow specifying the default entry to boot
# 
# tools/python/xen/xend/XendBootloader.py
#   2005/04/25 18:25:45-04:00 katzj@bree.local.net +9 -3
#   support passing the default entry to boot
# 
# tools/pygrub/src/pygrub
#   2005/04/25 18:25:45-04:00 katzj@bree.local.net +35 -20
#   support passing an entry to boot either by label or by index.  also
#   remove some dead code and fix a few minor bugs
# 
# ChangeSet
#   2005/04/14 12:16:27-04:00 katzj@bree.local.net 
#   Minor fixes based on feedback from Mike Wray
# 
# tools/python/xen/xm/create.py
#   2005/04/14 12:16:25-04:00 katzj@bree.local.net +2 -3
#   Use method in blkif to do uname -> file conversion
# 
# tools/python/xen/xend/server/blkif.py
#   2005/04/14 12:16:25-04:00 katzj@bree.local.net +9 -0
#   Add method to convert from a uname to a real file
# 
# tools/python/xen/xend/XendDomainInfo.py
#   2005/04/14 12:16:25-04:00 katzj@bree.local.net +6 -8
#   Make lack of a disk a fatal error.  Use the method in blkif
#   for going from a disk uname to a filename
# 
# tools/python/xen/xend/XendBootloader.py
#   2005/04/14 12:16:25-04:00 katzj@bree.local.net +14 -4
#   Improve error handling.  Log errors and raise an error on a problem
#   instead of just printing to stderr and exiting
# 
# ChangeSet
#   2005/04/13 23:02:35-04:00 katzj@bree.local.net 
#   Add pygrub, a simple boot loader for use to read a boot loader
#   config out of a domU's filesystem and then read kernels out as well.
#   Currently only has support for ext[23] via libext2fs, but it should
#   be easy to add other filesystems.
#   
#   Still needs work to complete the interface and to support full disk
#   images instead of just partitions.
# 
# tools/pygrub/src/pygrub
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +255 -0
# 
# tools/pygrub/src/fsys/ext2/test.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +15 -0
# 
# tools/pygrub/src/fsys/ext2/ext2module.c
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +332 -0
# 
# tools/pygrub/src/fsys/ext2/__init__.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +38 -0
# 
# tools/pygrub/src/fsys/__init__.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +61 -0
# 
# tools/pygrub/src/GrubConf.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +229 -0
# 
# tools/pygrub/setup.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +25 -0
# 
# tools/pygrub/Makefile
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +18 -0
# 
# tools/pygrub/src/pygrub
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/pygrub
# 
# tools/pygrub/src/fsys/ext2/test.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/ext2/test.py
# 
# tools/pygrub/src/fsys/ext2/ext2module.c
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/ext2/ext2module.c
# 
# tools/pygrub/src/fsys/ext2/__init__.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/ext2/__init__.py
# 
# tools/pygrub/src/fsys/__init__.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/fsys/__init__.py
# 
# tools/pygrub/src/GrubConf.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/GrubConf.py
# 
# tools/pygrub/setup.py
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/setup.py
# 
# tools/pygrub/Makefile
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/Makefile
# 
# tools/Makefile
#   2005/04/13 23:02:33-04:00 katzj@bree.local.net +1 -0
#   Add pygrub subdirectory
# 
# ChangeSet
#   2005/04/13 23:01:42-04:00 katzj@bree.local.net 
#   Add framework for running a bootloader from within xend and xm. To
#   specify the executable to run, set in your domain config like
#     bootloader="/usr/bin/pygrub"
#   
#   The bootloader will get executed on both domain creation and domain
#   reboot to get the default kernel/initrd specified in the bootloader
#   config for the domain.  If you auto-connect the console on domain
#   creation, then your bootloader will be run in an interactive mode.
# 
# BitKeeper/etc/logging_ok
#   2005/04/13 23:01:42-04:00 katzj@bree.local.net +1 -0
#   Logging to logging@openlogging.org accepted
# 
# tools/python/xen/xend/XendBootloader.py
#   2005/04/13 23:01:37-04:00 katzj@bree.local.net +79 -0
# 
# tools/python/xen/xm/create.py
#   2005/04/13 23:01:37-04:00 katzj@bree.local.net +29 -2
#   Run bootloader on domain creation.  If console is being connected,
#   then run in an interactive mode, else just go with the default.
# 
# tools/python/xen/xend/XendDomainInfo.py
#   2005/04/13 23:01:37-04:00 katzj@bree.local.net +41 -1
#   Add running of a boot loader from xend on domain reboot.  Runs in
#   an uninteractive mode to get the default kernel/initrd for the
#   domain.  Also removes any temporary kernels created by the 
#   bootloader.
# 
# tools/python/xen/xend/XendBootloader.py
#   2005/04/13 23:01:37-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/python/xen/xend/XendBootloader.py
# 
diff -Nru a/tools/Makefile b/tools/Makefile
--- a/tools/Makefile	2005-04-25 18:49:05 -04:00
+++ b/tools/Makefile	2005-04-25 18:49: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-25 18:49: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-25 18:49:05 -04:00
@@ -0,0 +1,25 @@
+from distutils.core import setup, Extension
+import os
+
+extra_compile_args  = [ "-fno-strict-aliasing", "-Wall", "-Werror" ]
+
+# in a perfect world, we'd figure out the fsys modules dynamically
+ext2 = Extension("grub.fsys.ext2._pyext2",
+                 extra_compile_args = extra_compile_args,
+                 libraries = ["ext2fs"],
+                 sources = ["src/fsys/ext2/ext2module.c"])
+
+setup(name='pygrub',
+      version='0.1',
+      description='Boot loader that looks a lot like grub for Xen',
+      author='Jeremy Katz',
+      author_email='katzj@redhat.com',
+      license='GPL',
+      package_dir={'grub': 'src'},
+      scripts = ["src/pygrub"],
+      packages=['grub',
+                'grub.fsys',
+                'grub.fsys.ext2'],
+      ext_modules = [ext2]
+      )
+               
diff -Nru a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/tools/pygrub/src/GrubConf.py	2005-04-25 18:49:05 -04:00
@@ -0,0 +1,229 @@
+#
+# GrubConf.py - Simple grub.conf parsing
+#
+# Copyright 2005 Red Hat, Inc.
+# Jeremy Katz <katzj@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+import os, sys
+import logging
+
+def grub_split(s, maxsplit = -1):
+    """Split a grub option screen separated with either '=' or whitespace."""
+    eq = s.find('=')
+    if eq == -1:
+        return s.split(None, maxsplit)
+
+    # see which of a space or tab is first
+    sp = s.find(' ')
+    tab = s.find('\t')
+    if (tab != -1 and tab < sp) or (tab != -1 and sp == -1):
+        sp = tab
+
+    if eq != -1 and eq < sp or (eq != -1 and sp == -1):
+        return s.split('=', maxsplit)
+    else:
+        return s.split(None, maxsplit)
+
+def get_path(s):
+    """Returns a tuple of (GrubDiskPart, path) corresponding to string."""
+    if not s.startswith('('):
+        return (None, s)
+    idx = s.find(')')
+    if idx == -1:
+        raise ValueError, "Unable to find matching ')'"
+    d = s[:idx]
+    return (GrubDiskPart(d), s[idx + 1:])
+
+class GrubDiskPart(object):
+    def __init__(self, str):
+        if str.find(',') != -1:
+            (self.disk, self.part) = str.split(",", 2)
+        else:
+            self.disk = str
+            self.part = None
+
+    def __repr__(self):
+        if self.part is not None:
+            return "d%dp%d" %(self.disk, self.part)
+        else:
+            return "d%d" %(self,disk,)
+
+    def get_disk(self):
+        return self._disk
+    def set_disk(self, val):
+        val = val.replace("(", "").replace(")", "")
+        self._disk = int(val[2:])
+    disk = property(get_disk, set_disk)
+
+    def get_part(self):
+        return self._part
+    def set_part(self, val):
+        if val is None:
+            self._part = val
+            return
+        val = val.replace("(", "").replace(")", "")
+        self._part = int(val)
+    part = property(get_part, set_part)
+
+class GrubImage(object):
+    def __init__(self, lines):
+        self._root = self._initrd = self._kernel = self._args = None
+        for l in lines:
+            (com, arg) = grub_split(l, 1)
+
+            if self.commands.has_key(com):
+                if self.commands[com] is not None:
+                    exec("%s = r\"%s\"" %(self.commands[com], arg.strip()))
+                else:
+                    logging.info("Ignored image directive %s" %(com,))
+            else:
+                logging.warning("Unknown image directive %s" %(com,))
+
+    def __repr__(self):
+        return ("title: %s\n" 
+                "  root: %s\n"
+                "  kernel: %s\n"
+                "  args: %s\n"
+                "  initrd: %s" %(self.title, self.root, self.kernel,
+                                   self.args, self.initrd))
+
+    def set_root(self, val):
+        self._root = GrubDiskPart(val)
+    def get_root(self):
+        return self._root
+    root = property(get_root, set_root)
+
+    def set_kernel(self, val):
+        if val.find(" ") == -1:
+            self._kernel = get_path(val)
+            self._args = None
+            return
+        (kernel, args) = val.split(None, 1)
+        self._kernel = get_path(kernel)
+        self._args = args
+    def get_kernel(self):
+        return self._kernel
+    def get_args(self):
+        return self._args
+    kernel = property(get_kernel, set_kernel)
+    args = property(get_args)
+
+    def set_initrd(self, val):
+        self._initrd = get_path(val)
+    def get_initrd(self):
+        return self._initrd
+    initrd = property(get_initrd, set_initrd)
+
+    # set up command handlers
+    commands = { "title": "self.title",
+                 "root": "self.root",
+                 "rootnoverify": "self.root",
+                 "kernel": "self.kernel",
+                 "initrd": "self.initrd",
+                 "chainloader": None,
+                 "module": None}
+        
+
+class GrubConfigFile(object):
+    def __init__(self, fn = None):
+        self.filename = fn
+        self.images = []
+        self.timeout = -1
+
+        if fn is not None:
+            self.parse()
+
+    def parse(self, buf = None):
+        if buf is None:
+            if self.filename is None:
+                raise ValueError, "No config file defined to parse!"
+
+            f = open(self.filename, 'r')
+            lines = f.readlines()
+            f.close()
+        else:
+            lines = buf.split("\n")
+
+        img = []
+        for l in lines:
+            l = l.strip()
+            # skip blank lines
+            if len(l) == 0:
+                continue
+            # skip comments
+            if l.startswith('#'):
+                continue
+            # new image
+            if l.startswith("title"):
+                if len(img) > 0:
+                    self.images.append(GrubImage(img))
+                img = [l]
+                continue
+                
+            if len(img) > 0:
+                img.append(l)
+                continue
+
+            try:
+                (com, arg) = grub_split(l, 1)
+            except ValueError:
+                com = l
+                arg = ""
+
+            if self.commands.has_key(com):
+                if self.commands[com] is not None:
+                    exec("%s = r\"%s\"" %(self.commands[com], arg.strip()))
+                else:
+                    logging.info("Ignored directive %s" %(com,))
+            else:
+                logging.warning("Unknown directive %s" %(com,))
+                
+        if len(img) > 0:
+            self.images.append(GrubImage(img))
+
+    def _get_default(self):
+        return self._default
+    def _set_default(self, val):
+        if val == "saved":
+            self._default = -1
+        else:
+            self._default = int(val)
+
+        if self._default < 0:
+            raise ValueError, "default must be positive number"
+    default = property(_get_default, _set_default)
+
+    def set_splash(self, val):
+        self._splash = get_path(val)
+    def get_splash(self):
+        return self._splash
+    splash = property(get_splash, set_splash)
+
+    # set up command handlers
+    commands = { "default": "self.default",
+                 "timeout": "self.timeout",
+                 "fallback": "self.fallback",
+                 "hiddenmenu": "self.hiddenmenu",
+                 "splashimage": "self.splash",
+                 "password": "self.password" }
+    for c in ("bootp", "color", "device", "dhcp", "hide", "ifconfig",
+              "pager", "partnew", "parttype", "rarp", "serial",
+              "setkey", "terminal", "terminfo", "tftpserver", "unhide"):
+        commands[c] = None
+    del c
+
+
+if __name__ == "__main__":
+    if sys.argv < 2:
+        raise RuntimeError, "Need a grub.conf to read"
+    g = GrubConfigFile(sys.argv[1])
+    for i in g.images:
+        print i #, i.title, i.root, i.kernel, i.args, i.initrd
diff -Nru a/tools/pygrub/src/fsys/__init__.py b/tools/pygrub/src/fsys/__init__.py
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/tools/pygrub/src/fsys/__init__.py	2005-04-25 18:49:05 -04:00
@@ -0,0 +1,61 @@
+#
+# Copyright 2005 Red Hat, Inc.
+# Jeremy Katz <katzj@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+import os
+import sys
+
+fstypes = {}
+
+def register_fstype(x):
+    if x.name in fstypes.keys():
+        return
+    fstypes[x.name] = x
+
+class FileSystemType(object):
+    """A simple representation for a file system that gives a fs name
+    and a method for sniffing a file to see if it's of the given fstype."""
+    def __init__(self):
+        self.name = ""
+
+    def sniff_magic(self, fn, offset = 0):
+        """Look at the filesystem at fn for the appropriate magic starting at
+        offset offset."""
+        raise RuntimeError, "sniff_magic not implemented"
+
+    def open_fs(self, fn, offset = 0):
+        """Open the given filesystem and return a filesystem object."""
+        raise RuntimeError, "open_fs not implemented"
+
+class FileSystem(object):
+    def open(self, name, flags = 0, block_size = 0):
+        """Open the fsys on name with given flags and block_size."""
+        raise RuntimeError, "open not implemented"
+
+    def close(self):
+        """Close the fsys."""
+        raise RuntimeError, "close not implemented"
+
+    def open_file(self, file, flags = None):
+        """Open the file 'name' with the given flags.  The returned object
+        should look similar to a native file object."""
+        raise RuntimeError, "open_file not implemented"
+    
+
+
+mydir = sys.modules['grub.fsys'].__path__[0]
+for f in os.listdir(mydir):
+    if not os.path.isdir("%s/%s" %(mydir, f)):
+        continue
+    try:
+        exec "import grub.fsys.%s" %(f,)        
+    except ImportError, e:
+        pass
diff -Nru a/tools/pygrub/src/fsys/ext2/__init__.py b/tools/pygrub/src/fsys/ext2/__init__.py
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/tools/pygrub/src/fsys/ext2/__init__.py	2005-04-25 18:49:05 -04:00
@@ -0,0 +1,38 @@
+# Copyright 2005 Red Hat, Inc.
+# Jeremy Katz <katzj@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+from grub.fsys import register_fstype, FileSystemType
+from _pyext2 import *
+
+import os, struct
+
+class Ext2FileSystemType(FileSystemType):
+    def __init__(self):
+        FileSystemType.__init__(self)
+        self.name = "ext2"
+
+    def sniff_magic(self, fn, offset = 0):
+        fd = os.open(fn, os.O_RDONLY)
+        os.lseek(fd, offset, 0)
+        buf = os.read(fd, 2048)
+        
+        if len(buf) > 1082 and \
+               struct.unpack("<H", buf[1080:1082]) == (0xef53,):
+            return True
+        return False
+
+    def open_fs(self, fn, offset = 0):
+        if not self.sniff_magic(fn, offset):
+            raise ValueError, "Not an ext2 filesystem"
+        return Ext2Fs(fn)
+
+register_fstype(Ext2FileSystemType())
+
diff -Nru a/tools/pygrub/src/fsys/ext2/ext2module.c b/tools/pygrub/src/fsys/ext2/ext2module.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/tools/pygrub/src/fsys/ext2/ext2module.c	2005-04-25 18:49:05 -04:00
@@ -0,0 +1,332 @@
+/*
+ * ext2module.c - simple python binding for libext2fs
+ *
+ * Copyright 2005 Red Hat, Inc.
+ * Jeremy Katz <katzj@redhat.com>
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * general public license.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <Python.h>
+
+#include <ext2fs/ext2fs.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#if (PYTHON_API_VERSION >= 1011)
+#define PY_PAD 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L
+#else
+#define PY_PAD 0L,0L,0L,0L
+#endif
+
+
+/* global error object */
+PyObject *Ext2Error;
+
+typedef struct _Ext2Fs Ext2Fs;
+struct _Ext2Fs {
+    PyObject_HEAD;
+    ext2_filsys fs;
+};
+
+typedef struct _Ext2File Ext2File;
+struct _Ext2File {
+    PyObject_HEAD;
+    ext2_file_t file;
+};
+
+/* ext2 file object */
+
+static PyObject *
+ext2_file_close (Ext2File *file, PyObject *args)
+{
+    if (file->file != NULL)
+        ext2fs_file_close(file->file);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+ext2_file_read (Ext2File *file, PyObject *args)
+{
+    int err, size = 0;
+    size_t n, total = 0;
+    PyObject * buffer = NULL;
+
+    if (file->file == NULL) {
+        PyErr_SetString(PyExc_ValueError, "Cannot read from closed file");
+        return NULL;
+    }
+
+    if (!PyArg_ParseTuple(args, "|i", &size))
+        return NULL;
+
+    buffer = PyString_FromStringAndSize((char *) NULL, (size) ? size : 4096);
+    if (buffer == NULL)
+        return buffer;
+ 
+    while (1) {
+        err = ext2fs_file_read(file->file, PyString_AS_STRING(buffer) + total, 
+                               (size) ? size : 4096, &n);
+        if (err) {
+            if (buffer != NULL) { Py_DECREF(buffer); }
+            Py_DECREF(buffer);
+            PyErr_SetString(PyExc_ValueError, "read error");
+            return NULL;
+        }
+
+        total += n;
+        if (n == 0)
+            break;
+
+        if (size && size == total)
+            break;
+
+        if (!size) {
+            _PyString_Resize(&buffer, total + 4096);
+        }
+    }
+
+    _PyString_Resize(&buffer, total);
+    return buffer;
+}
+
+static void
+ext2_file_dealloc (Ext2File * file)
+{
+    if (file->file != NULL)
+        ext2fs_file_close(file->file);
+    PyMem_DEL(file);
+}
+
+static struct PyMethodDef Ext2FileMethods[] = {
+        { "close",
+          (PyCFunction) ext2_file_close,
+          METH_VARARGS, NULL },
+        { "read",
+          (PyCFunction) ext2_file_read,
+          METH_VARARGS, NULL },
+	{ NULL, NULL, 0, NULL }	
+};
+
+static PyObject *
+ext2_file_getattr (Ext2File * file, char * name)
+{
+        return Py_FindMethod (Ext2FileMethods, (PyObject *) file, name);
+}
+
+static char Ext2FileType__doc__[] = "This is the ext2 filesystem object";
+PyTypeObject Ext2FileType = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,				/* ob_size */
+	"Ext2File",			/* tp_name */
+	sizeof(Ext2File),		/* tp_size */
+	0,				/* tp_itemsize */
+	(destructor) ext2_file_dealloc, 	/* tp_dealloc */
+	0,				/* tp_print */
+	(getattrfunc) ext2_file_getattr, 	/* tp_getattr */
+	0,				/* tp_setattr */
+	0,				/* tp_compare */
+	0,				/* tp_repr */
+	0,				/* tp_as_number */
+	0,	 			/* tp_as_sequence */
+	0,				/* tp_as_mapping */
+	0,           			/* tp_hash */
+	0,                		/* tp_call */
+	0,                    		/* tp_str */
+	0,				/* tp_getattro */
+	0,				/* tp_setattro */
+	0,				/* tp_as_buffer */
+	0L,	       			/* tp_flags */
+	Ext2FileType__doc__,
+	PY_PAD
+};
+
+static PyObject *
+ext2_file_open (Ext2Fs *fs, char * name, int flags)
+{
+    int err;
+    ext2_file_t f;
+    ext2_ino_t ino;
+    Ext2File * file;
+
+    file = (Ext2File *) PyObject_NEW(Ext2File, &Ext2FileType);
+    file->file = NULL;
+
+    err = ext2fs_namei_follow(fs->fs, EXT2_ROOT_INO, EXT2_ROOT_INO, name, &ino);
+    if (err) {
+        PyErr_SetString(PyExc_ValueError, "unable to open file");
+        return NULL;
+    }
+
+    err = ext2fs_file_open(fs->fs, ino, flags, &f);
+    if (err) {
+        PyErr_SetString(PyExc_ValueError, "unable to open file");
+        return NULL;
+    }
+
+    file->file = f;
+    return (PyObject *) file;
+}
+
+/* ext2fs object */
+
+static PyObject *
+ext2_fs_close (Ext2Fs *fs, PyObject *args)
+{
+    if (fs->fs != NULL)
+        ext2fs_close(fs->fs);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+ext2_fs_open (Ext2Fs *fs, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "name", "flags", "superblock", 
+                              "block_size", NULL };
+    char * name;
+    int flags = 0, superblock = 0, err;
+    unsigned int block_size = 0;
+    ext2_filsys efs;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iii", kwlist, 
+                                     &name, &flags, &superblock, &block_size))
+                                     return NULL;
+
+    if (fs->fs != NULL) {
+        PyErr_SetString(PyExc_ValueError, "already have an fs object");
+        return NULL;
+    }
+
+    err = ext2fs_open(name, flags, superblock, block_size, 
+                      unix_io_manager, &efs);
+    if (err) {
+        PyErr_SetString(PyExc_ValueError, "unable to open file");
+        return NULL;
+    }
+
+    fs->fs = efs;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+ext2_fs_open_file (Ext2Fs *fs, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "name", "flags", NULL };
+    char * name;
+    int flags = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist, 
+                                     &name, &flags))
+                                     return NULL;
+
+    return ext2_file_open(fs, name, flags);
+}
+
+static void
+ext2_fs_dealloc (Ext2Fs * fs)
+{
+    if (fs->fs != NULL)
+        ext2fs_close(fs->fs);
+    PyMem_DEL(fs);
+}
+
+static struct PyMethodDef Ext2FsMethods[] = {
+        { "close",
+          (PyCFunction) ext2_fs_close,
+          METH_VARARGS, NULL },
+        { "open",
+          (PyCFunction) ext2_fs_open,
+          METH_VARARGS|METH_KEYWORDS, NULL },
+        { "open_file",
+          (PyCFunction) ext2_fs_open_file,
+          METH_VARARGS|METH_KEYWORDS, NULL },
+	{ NULL, NULL, 0, NULL }	
+};
+
+static PyObject *
+ext2_fs_getattr (Ext2Fs * fs, char * name)
+{
+        return Py_FindMethod (Ext2FsMethods, (PyObject *) fs, name);
+}
+
+static char Ext2FsType__doc__[] = "This is the ext2 filesystem object";
+PyTypeObject Ext2FsType = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,				/* ob_size */
+	"Ext2Fs",			/* tp_name */
+	sizeof(Ext2Fs),		/* tp_size */
+	0,				/* tp_itemsize */
+	(destructor) ext2_fs_dealloc, 	/* tp_dealloc */
+	0,				/* tp_print */
+	(getattrfunc) ext2_fs_getattr, 	/* tp_getattr */
+	0,				/* tp_setattr */
+	0,				/* tp_compare */
+	0,				/* tp_repr */
+	0,				/* tp_as_number */
+	0,	 			/* tp_as_sequence */
+	0,				/* tp_as_mapping */
+	0,           			/* tp_hash */
+	0,                		/* tp_call */
+	0,                    		/* tp_str */
+	0,				/* tp_getattro */
+	0,				/* tp_setattro */
+	0,				/* tp_as_buffer */
+	0L,	       			/* tp_flags */
+	Ext2FsType__doc__,
+	PY_PAD
+};
+
+static PyObject *
+ext2_fs_new(PyObject *o, PyObject *args, PyObject *kwargs) 
+{
+    static char *kwlist[] = { "name", "flags", "superblock", 
+                              "block_size", NULL };
+    char * name;
+    int flags = 0, superblock = 0;
+    unsigned int block_size = 0;
+    Ext2Fs *pfs;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iii", kwlist, 
+                                     &name, &flags, &superblock, &block_size))
+        return NULL;
+
+    pfs = (Ext2Fs *) PyObject_NEW(Ext2Fs, &Ext2FsType);
+    if (pfs == NULL)
+        return NULL;
+    pfs->fs = NULL;
+
+    if (!ext2_fs_open(pfs, 
+                      Py_BuildValue("siii", name, flags, superblock, block_size),
+                      NULL))
+        return NULL;
+
+    return (PyObject *)pfs;
+}
+
+
+static struct PyMethodDef Ext2ModuleMethods[] = {
+    { "Ext2Fs", (PyCFunction) ext2_fs_new, METH_VARARGS|METH_KEYWORDS, NULL },
+    { NULL, NULL, 0, NULL }
+};
+
+
+void init_pyext2(void) {
+    PyObject *m, *d;
+
+    m = Py_InitModule("_pyext2", Ext2ModuleMethods);
+    d = PyModule_GetDict(m);
+
+    /*    o = PyObject_NEW(PyObject, yExt2FsConstructorType);
+    PyDict_SetItemString(d, "PyExt2Fs", o);
+    Py_DECREF(o);*/
+                      
+}
diff -Nru a/tools/pygrub/src/fsys/ext2/test.py b/tools/pygrub/src/fsys/ext2/test.py
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/tools/pygrub/src/fsys/ext2/test.py	2005-04-25 18:49: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-25 18:49:05 -04:00
@@ -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 -Nru a/tools/python/xen/xend/XendBootloader.py b/tools/python/xen/xend/XendBootloader.py
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/tools/python/xen/xend/XendBootloader.py	2005-04-25 18:49:05 -04:00
@@ -0,0 +1,95 @@
+#
+# XendBootloader.py - Framework to run a boot loader for picking the kernel
+#
+# 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, 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', pin.val]]
+    config.append(['bootloader', blexec])
+    return config
+
diff -Nru a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py	2005-04-25 18:49:05 -04:00
+++ b/tools/python/xen/xend/XendDomainInfo.py	2005-04-25 18:49:05 -04:00
@@ -20,7 +20,8 @@
 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
+from xen.xend.server import channel, blkif
+from xen.xend.XendBootloader import bootloader
 
 import sxp
 
@@ -323,6 +324,7 @@
         self.image_handler = None
         self.is_vmx = 0
         self.vcpus = 1
+        self.bootloader = None
 
     def setdom(self, dom):
         """Set the domain id.
@@ -458,6 +460,7 @@
 
             self.find_image_handler()
             self.init_domain()
+            self.configure_bootloader()
             self.configure_console()
             self.configure_backends()
             self.construct_image()
@@ -795,7 +798,7 @@
                       	ramdisk        = ramdisk,
                       	flags          = flags)
 	else:
-        	log.warning('building dom with %d vcpus', self.vcpus)
+        	log.warning('building dom with %d vcpus' %(self.vcpus,))
         	err = buildfn(dom            = dom,
                	      	image          = kernel,
                       	control_evtchn = self.console.getRemotePort(),
@@ -803,6 +806,14 @@
                       	ramdisk        = ramdisk,
                       	flags          = flags,
                       	vcpus          = self.vcpus)
+
+        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 err != 0:
             raise VmError('Building domain failed: type=%s dom=%d err=%d'
                           % (ostype, dom, err))
@@ -961,6 +972,13 @@
             maxmem = self.memory
         xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
 
+    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.
         """
@@ -1034,6 +1052,24 @@
         try:
             self.restart_check()
             self.restart_state = STATE_RESTART_BOOTING
+            # if we're restarting with a bootloader, we need to run it
+            if self.bootloader:
+                # 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 = blkif.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)
+                else:
+                    s = "(vm %s)" %(sxp.to_string(blcfg[0]),)
+                    self.config = sxp.merge(sxp.from_string(s), self.config)
             d = self.construct(self.config)
         finally:
             self.restart_state = None
@@ -1163,6 +1199,7 @@
     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
 
@@ -1377,6 +1414,7 @@
 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 -Nru a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py
--- a/tools/python/xen/xend/server/blkif.py	2005-04-25 18:49:05 -04:00
+++ b/tools/python/xen/xend/server/blkif.py	2005-04-25 18:49:05 -04:00
@@ -96,6 +96,15 @@
                 '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
+
 class BlkifBackendController(controller.BackendController):
     """ Handler for the 'back-end' channel to a block device driver domain.
     """
diff -Nru a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py
--- a/tools/python/xen/xm/create.py	2005-04-25 18:49:05 -04:00
+++ b/tools/python/xen/xm/create.py	2005-04-25 18:49:05 -04:00
@@ -10,6 +10,8 @@
 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 @@
           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.")
@@ -375,6 +385,21 @@
     config_devs.append(['device_model', device_model])
     config_devs.append(['device_config', device_config])
 
+def run_bootloader(config, vals):
+    if not os.access(vals.bootloader, os.X_OK):
+        print >> sys.stderr, "Bootloader isn't executable"
+        sys.exit(1)
+    if len(vals.disk) < 1:
+        print >> sys.stderr, "No disks configured and boot loader requested"
+        sys.exit(1)
+    (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.extend(blcfg)
+
 def make_config(vals):
     """Create the domain configuration.
     """
@@ -396,8 +421,11 @@
         config.append(['restart', vals.restart])
     if vals.console:
         config.append(['console', vals.console])
-    
-    configure_image(config, vals)
+
+    if vals.bootloader:
+        run_bootloader(config, vals)
+    else:
+        configure_image(config, vals)
     config_devs = []
     configure_disks(config_devs, vals)
     configure_pci(config_devs, vals)
@@ -405,6 +433,7 @@
     configure_usb(config_devs, vals)
     configure_vmx(config_devs, vals)
     config += config_devs
+
     return config
 
 def preprocess_disk(opts, vals):
@@ -588,6 +617,7 @@
         if not opts.getopt('name') and opts.getopt('defconfig'):
             opts.setopt('name', os.path.basename(opts.getopt('defconfig')))
         config = make_config(opts.vals)
+
     if opts.vals.dryrun:
         PrettyPrint.prettyprint(config)
     else:

[-- Attachment #3: Type: text/plain, Size: 138 bytes --]

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

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2005-04-29  3:06 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-04-25 22:50 [PATCH] Guest boot loader support Jeremy Katz
2005-04-26  9:32 ` aq
2005-04-26  9:54   ` aq
2005-04-26 17:01   ` Jeremy Katz
2005-04-26 19:37     ` aq
2005-04-26 19:48       ` Mark Williamson
2005-04-26 20:24         ` Hollis Blanchard
2005-04-26 20:47           ` Mark Williamson
2005-04-26 21:02             ` aq
2005-04-27  0:30               ` Mark Williamson
2005-04-27  3:14                 ` aq
2005-04-29  3:06             ` Jeremy Katz
2005-04-26 20:49           ` aq
2005-04-26 19:58     ` aq
2005-04-26 20:11 ` Jacob Gorm Hansen
2005-04-26 20:21   ` Anthony Liguori
2005-04-26 20:39     ` Jacob Gorm Hansen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.