All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jeremy Katz <katzj@redhat.com>
To: aq <aquynh@gmail.com>
Cc: "xen-devel@lists.xensource.com" <xen-devel@lists.xensource.com>
Subject: Re: [PATCH] Guest boot loader support
Date: Tue, 26 Apr 2005 13:01:11 -0400	[thread overview]
Message-ID: <1114534871.18436.7.camel@bree.local.net> (raw)
In-Reply-To: <9cde8bff050426023247aa475b@mail.gmail.com>

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

On Tue, 2005-04-26 at 18:32 +0900, aq wrote:
> Jeremy, I tried it out with the latest xen-unstable got from the
> homepage and patch with xen-booloader.patch. There are few "hunk".
> What xen-unstable version are you using?

Bah, bitkeeper foils me again.  Rediffed to current -unstable attached.
Thanks for catching that.

> By the way, did you forget __init__.py in pygrub/src/ ? pygrub is a
> python package, and looks like we need it. At the moment compilation
> spit out an error "... __init__.py not found...".

It was there at one point... added it as well for this diff.

As for your other comment, the r quoting of the strings is to make sure
that the strings are interpreted as raw strings instead of trying to
handle escaping which helps in cases where you have "odd" things in your
grub.conf.

Jeremy

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

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2005/04/26 12:46:24-04:00 katzj@bree.local.net 
#   Add __init__.py.  Noticed by aq <aquynh@gmail.com>
# 
# tools/pygrub/src/__init__.py
#   2005/04/26 12:39:00-04:00 katzj@bree.local.net +0 -0
# 
# tools/pygrub/src/__init__.py
#   2005/04/26 12:38:59-04:00 katzj@bree.local.net +0 -0
#   BitKeeper file /home/katzj/cvs/xen/xen-pygrub/tools/pygrub/src/__init__.py
# 
# 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-26 12:54:29 -04:00
+++ b/tools/Makefile	2005-04-26 12:54:29 -04:00
@@ -13,6 +13,7 @@
 ifndef XEN_NO_IOEMU
 SUBDIRS += ioemu
 endif
+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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -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-26 12:54:29 -04:00
+++ b/tools/python/xen/xend/XendDomainInfo.py	2005-04-26 12:54:29 -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-26 12:54:29 -04:00
+++ b/tools/python/xen/xend/server/blkif.py	2005-04-26 12:54:29 -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-26 12:54:29 -04:00
+++ b/tools/python/xen/xm/create.py	2005-04-26 12:54:29 -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

  parent reply	other threads:[~2005-04-26 17:01 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1114534871.18436.7.camel@bree.local.net \
    --to=katzj@redhat.com \
    --cc=aquynh@gmail.com \
    --cc=xen-devel@lists.xensource.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.