* [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
* Re: [PATCH] Guest boot loader support
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 20:11 ` Jacob Gorm Hansen
1 sibling, 2 replies; 17+ messages in thread
From: aq @ 2005-04-26 9:32 UTC (permalink / raw)
To: Jeremy Katz; +Cc: xen-devel@lists.xensource.com
On 4/26/05, Jeremy Katz <katzj@redhat.com> wrote:
> 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.
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?
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...".
Quick fix:
touch tools/pygrub/src/__init__.py
then no more complain.
regards,
aq
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 9:32 ` aq
@ 2005-04-26 9:54 ` aq
2005-04-26 17:01 ` Jeremy Katz
1 sibling, 0 replies; 17+ messages in thread
From: aq @ 2005-04-26 9:54 UTC (permalink / raw)
To: Jeremy Katz; +Cc: xen-devel
[-- Attachment #1: Type: text/plain, Size: 1156 bytes --]
On 4/26/05, aq <aquynh@gmail.com> wrote:
> On 4/26/05, Jeremy Katz <katzj@redhat.com> wrote:
> > 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
> >
Jeremy, I looked at your code, and find few lines like this:
exec("%s = r\"%s\"" %(self.commands[com], arg.strip()))
Looks like you dont need the above "r". Then the code should be:
exec("%s = \"%s\"" %(self.commands[com], arg.strip()))
Here is a small patch (against your patch) to remove those typos.
$ diffstat booloader1.patch
xen-bootloader2.patch | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
Signed-off-by: Nguyen Anh Quynh <aquynh@gmail.com>
[-- Attachment #2: booloader1.patch --]
[-- Type: application/octet-stream, Size: 1001 bytes --]
--- xen-bootloader.patch 2005-04-26 18:47:36.437028000 +0900
+++ xen-bootloader2.patch 2005-04-26 18:49:01.687028000 +0900
@@ -147,7 +147,7 @@ diff -Nru a/tools/pygrub/src/GrubConf.py
+
+ if self.commands.has_key(com):
+ if self.commands[com] is not None:
-+ exec("%s = r\"%s\"" %(self.commands[com], arg.strip()))
++ exec("%s = \"%s\"" %(self.commands[com], arg.strip()))
+ else:
+ logging.info("Ignored image directive %s" %(com,))
+ else:
@@ -246,7 +246,7 @@ diff -Nru a/tools/pygrub/src/GrubConf.py
+
+ if self.commands.has_key(com):
+ if self.commands[com] is not None:
-+ exec("%s = r\"%s\"" %(self.commands[com], arg.strip()))
++ exec("%s = \"%s\"" %(self.commands[com], arg.strip()))
+ else:
+ logging.info("Ignored directive %s" %(com,))
+ 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
* Re: [PATCH] Guest boot loader support
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:58 ` aq
1 sibling, 2 replies; 17+ messages in thread
From: Jeremy Katz @ 2005-04-26 17:01 UTC (permalink / raw)
To: aq; +Cc: xen-devel@lists.xensource.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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 17:01 ` Jeremy Katz
@ 2005-04-26 19:37 ` aq
2005-04-26 19:48 ` Mark Williamson
2005-04-26 19:58 ` aq
1 sibling, 1 reply; 17+ messages in thread
From: aq @ 2005-04-26 19:37 UTC (permalink / raw)
To: Jeremy Katz; +Cc: xen-devel@lists.xensource.com
On 4/27/05, Jeremy Katz <katzj@redhat.com> wrote:
> 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.
ok, i see the point.
other question: pygrub::get_config(fn) tries to read
"/boot/grub/grub.conf". Normally this should be "/boot/grub/menu.lst".
I wonder which distro are you working on?
I expect this code will be in -unstable soon, so we can work on the
same code base. sending patch would be easier.
thank you,
aq
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 19:37 ` aq
@ 2005-04-26 19:48 ` Mark Williamson
2005-04-26 20:24 ` Hollis Blanchard
0 siblings, 1 reply; 17+ messages in thread
From: Mark Williamson @ 2005-04-26 19:48 UTC (permalink / raw)
To: xen-devel, aq
> ok, i see the point.
>
> other question: pygrub::get_config(fn) tries to read
> "/boot/grub/grub.conf". Normally this should be "/boot/grub/menu.lst".
> I wonder which distro are you working on?
I'm betting he's using RedHat :-)
My FC3 box has menu.lst as a symlink to grub.conf. Grub seems to be happy
with either of these names. Painful experience suggests that if both files
are present, then "menu.lst" will "win".
Jeremy: for distros that just have menu.lst, could you search for this too?
Perhaps it's worth implementing a grub-style precedence for menu.lst when
present and grub.conf if not?
Cheers,
Mark
> I expect this code will be in -unstable soon, so we can work on the
> same code base. sending patch would be easier.
>
> thank you,
> aq
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xensource.com
> http://lists.xensource.com/xen-devel
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 17:01 ` Jeremy Katz
2005-04-26 19:37 ` aq
@ 2005-04-26 19:58 ` aq
1 sibling, 0 replies; 17+ messages in thread
From: aq @ 2005-04-26 19:58 UTC (permalink / raw)
To: Jeremy Katz; +Cc: xen-devel@lists.xensource.com
On 4/27/05, Jeremy Katz <katzj@redhat.com> wrote:
> 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
>
I checked the new patch. src/__init__.py is not in yet?
$ diffstat xen-bootloader-2.patch
Makefile | 1
pygrub/Makefile | 18 ++
pygrub/setup.py | 25 ++
pygrub/src/GrubConf.py | 229 ++++++++++++++++++++++++++
pygrub/src/fsys/__init__.py | 61 ++++++
pygrub/src/fsys/ext2/__init__.py | 38 ++++
pygrub/src/fsys/ext2/ext2module.c | 332 ++++++++++++++++++++++++++++++++++++++
pygrub/src/fsys/ext2/test.py | 15 +
pygrub/src/pygrub | 270 ++++++++++++++++++++++++++++++
python/xen/xend/XendBootloader.py | 95 ++++++++++
python/xen/xend/XendDomainInfo.py | 42 ++++
python/xen/xend/server/blkif.py | 9 +
python/xen/xm/create.py | 34 +++
13 files changed, 1165 insertions(+), 4 deletions(-)
when i use diff to produce the patch, if the src/__init__.py is empty,
diff would ignore that file, too. so the trick is to create
__init__.py with a space inside ;-)
regards,
aq
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-25 22:50 [PATCH] Guest boot loader support Jeremy Katz
2005-04-26 9:32 ` aq
@ 2005-04-26 20:11 ` Jacob Gorm Hansen
2005-04-26 20:21 ` Anthony Liguori
1 sibling, 1 reply; 17+ messages in thread
From: Jacob Gorm Hansen @ 2005-04-26 20:11 UTC (permalink / raw)
To: Jeremy Katz; +Cc: xen-devel
Jeremy Katz wrote:
> Attached is an updated version of the patch to add boot loader support
> for guest domains.
In relation to this, did anyone consider porting Grub to run inside a domU?
Jacob
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 20:11 ` Jacob Gorm Hansen
@ 2005-04-26 20:21 ` Anthony Liguori
2005-04-26 20:39 ` Jacob Gorm Hansen
0 siblings, 1 reply; 17+ messages in thread
From: Anthony Liguori @ 2005-04-26 20:21 UTC (permalink / raw)
To: xen-devel
On Tue, 2005-04-26 at 15:11, Jacob Gorm Hansen wrote:
> Jeremy Katz wrote:
> > Attached is an updated version of the patch to add boot loader support
> > for guest domains.
>
> In relation to this, did anyone consider porting Grub to run inside a domU?
While it seems appealing, a large amount of the grub code is device
drivers, real mode boot strapping code, etc.
You'd probably get very little useful code from grub in porting it to
domU.
Regards,
> Jacob
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xensource.com
> http://lists.xensource.com/xen-devel
--
Anthony Liguori
Linux Technology Center (LTC) - IBM Austin
E-mail: aliguori@us.ibm.com
Phone: (512) 838-1208
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 19:48 ` Mark Williamson
@ 2005-04-26 20:24 ` Hollis Blanchard
2005-04-26 20:47 ` Mark Williamson
2005-04-26 20:49 ` aq
0 siblings, 2 replies; 17+ messages in thread
From: Hollis Blanchard @ 2005-04-26 20:24 UTC (permalink / raw)
To: Mark Williamson; +Cc: xen-devel
Mark Williamson wrote:
>>ok, i see the point.
>>
>>other question: pygrub::get_config(fn) tries to read
>>"/boot/grub/grub.conf". Normally this should be "/boot/grub/menu.lst".
>>I wonder which distro are you working on?
>
> My FC3 box has menu.lst as a symlink to grub.conf. Grub seems to be happy
> with either of these names. Painful experience suggests that if both files
> are present, then "menu.lst" will "win".
GRUB only looks for menu.lst; distros create symlinks because that's a
terrible name for a config file. In other words, no code should be
opening grub.conf...
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 20:21 ` Anthony Liguori
@ 2005-04-26 20:39 ` Jacob Gorm Hansen
0 siblings, 0 replies; 17+ messages in thread
From: Jacob Gorm Hansen @ 2005-04-26 20:39 UTC (permalink / raw)
To: Anthony Liguori; +Cc: xen-devel
Anthony Liguori wrote:
> On Tue, 2005-04-26 at 15:11, Jacob Gorm Hansen wrote:
>
>>Jeremy Katz wrote:
>>
>>>Attached is an updated version of the patch to add boot loader support
>>>for guest domains.
>>
>>In relation to this, did anyone consider porting Grub to run inside a domU?
>
>
> While it seems appealing, a large amount of the grub code is device
> drivers, real mode boot strapping code, etc.
The interesting things you would get from Grub would be the
filesystem-decoding stuff, the elf decoder, the two-stage loader, and
the nice and well-tested config-file parsing, in a format that lots of
users already understand. Tftp and bootp/dhcp support could also be nice
in some scenarios.
And by having the bootloader in the domU you would get stronger security
as well.
Jacob
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 20:24 ` Hollis Blanchard
@ 2005-04-26 20:47 ` Mark Williamson
2005-04-26 21:02 ` aq
2005-04-29 3:06 ` Jeremy Katz
2005-04-26 20:49 ` aq
1 sibling, 2 replies; 17+ messages in thread
From: Mark Williamson @ 2005-04-26 20:47 UTC (permalink / raw)
To: Hollis Blanchard; +Cc: Mark Williamson, xen-devel
> GRUB only looks for menu.lst; distros create symlinks because that's a
> terrible name for a config file. In other words, no code should be
> opening grub.conf...
OK, I've investigated this using my sacrificial FC3 laptop ;-) I'd not fully
investigated this before.
Redhat's grub looks for grub.conf and not menu.lst. Standard grub looks for
menu.lst.
If the loader is to be flexible for different domU distros, it'd be nice to
look for both, in one order or the other.
Cheers,
Mark
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 20:24 ` Hollis Blanchard
2005-04-26 20:47 ` Mark Williamson
@ 2005-04-26 20:49 ` aq
1 sibling, 0 replies; 17+ messages in thread
From: aq @ 2005-04-26 20:49 UTC (permalink / raw)
To: Hollis Blanchard; +Cc: Mark Williamson, xen-devel
[-- Attachment #1: Type: text/plain, Size: 1155 bytes --]
On 4/27/05, Hollis Blanchard <hollisb@us.ibm.com> wrote:
> Mark Williamson wrote:
> >>ok, i see the point.
> >>
> >>other question: pygrub::get_config(fn) tries to read
> >>"/boot/grub/grub.conf". Normally this should be "/boot/grub/menu.lst".
> >>I wonder which distro are you working on?
> >
> > My FC3 box has menu.lst as a symlink to grub.conf. Grub seems to be happy
> > with either of these names. Painful experience suggests that if both files
> > are present, then "menu.lst" will "win".
>
> GRUB only looks for menu.lst; distros create symlinks because that's a
> terrible name for a config file. In other words, no code should be
> opening grub.conf...
>
ok, so here is a quick patch to fix the "/boot/grub/grub.conf"
problem. Please apply this after applying the last Jeremy's patch.
list of changes:
- make a dummy file tools/pygrub/src/__init__.py
- open and parse /boot/grub/menu.lst instead of /boot/grub/grub.conf
$ diffstat xen-bootloader.aq.patch
__init__.py | 1 +
pygrub | 7 +++++--
2 files changed, 6 insertions(+), 2 deletions(-)
Signed-off-by: Nguyen Anh Quynh <aquynh@gmail.com>
[-- Attachment #2: xen-bootloader.aq.patch --]
[-- Type: application/octet-stream, Size: 1245 bytes --]
diff -Nurp xen-unstable.27.3.org/tools/pygrub/src/__init__.py xen-unstable.27.3/tools/pygrub/src/__init__.py
--- xen-unstable.27.3.org/tools/pygrub/src/__init__.py 1970-01-01 09:00:00.000000000 +0900
+++ xen-unstable.27.3/tools/pygrub/src/__init__.py 2005-04-27 04:50:12.000000000 +0900
@@ -0,0 +1 @@
+
diff -Nurp xen-unstable.27.3.org/tools/pygrub/src/pygrub xen-unstable.27.3/tools/pygrub/src/pygrub
--- xen-unstable.27.3.org/tools/pygrub/src/pygrub 2005-04-27 05:32:43.000000000 +0900
+++ xen-unstable.27.3/tools/pygrub/src/pygrub 2005-04-27 05:43:48.000000000 +0900
@@ -13,7 +13,7 @@
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
-import os, sys, string, struct, tempfile
+import os, os.path, sys, string, struct, tempfile
import logging
import curses, _curses, curses.wrapper
@@ -97,7 +97,10 @@ def get_config(fn):
break
if fs is not None:
- f = fs.open_file("/boot/grub/grub.conf")
+ if not os.path.isfile("/boot/grub/menu.lst"):
+ raise RuntimeError, "we cannot find /boot/grub/menu.lst" + \
+ "in the image provided. halt!"
+ f = fs.open_file("/boot/grub/menu.lst")
buf = f.read()
f.close()
fs.close()
[-- 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
* Re: [PATCH] Guest boot loader support
2005-04-26 20:47 ` Mark Williamson
@ 2005-04-26 21:02 ` aq
2005-04-27 0:30 ` Mark Williamson
2005-04-29 3:06 ` Jeremy Katz
1 sibling, 1 reply; 17+ messages in thread
From: aq @ 2005-04-26 21:02 UTC (permalink / raw)
Cc: Mark Williamson, xen-devel
[-- Attachment #1: Type: text/plain, Size: 1056 bytes --]
On 4/27/05, Mark Williamson <mark.williamson@cl.cam.ac.uk> wrote:
> > GRUB only looks for menu.lst; distros create symlinks because that's a
> > terrible name for a config file. In other words, no code should be
> > opening grub.conf...
>
> OK, I've investigated this using my sacrificial FC3 laptop ;-) I'd not fully
> investigated this before.
>
> Redhat's grub looks for grub.conf and not menu.lst. Standard grub looks for
> menu.lst.
>
> If the loader is to be flexible for different domU distros, it'd be nice to
> look for both, in one order or the other.
>
in that case, this patch is better for you, Mark ;-). Please apply
this on top of Jeremy's patch.
list of changes:
- make a dummy file tools/pygrub/src/__init__.py
- tools/src/pygrub looks for either /boot/grub/menu.lst or
/boot/grub/grub.conf, in that order.
$ diffstat xen-bootloader.aq2.patch
__init__.py | 1 +
pygrub | 11 +++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
Signed-off-by: Nguyen Anh Quynh <aquynh@gmail.com>
[-- Attachment #2: xen-bootloader.aq2.patch --]
[-- Type: application/octet-stream, Size: 1403 bytes --]
diff -Nurp xen-unstable.27.3.org/tools/pygrub/src/__init__.py xen-unstable.27.3/tools/pygrub/src/__init__.py
--- xen-unstable.27.3.org/tools/pygrub/src/__init__.py 1970-01-01 09:00:00.000000000 +0900
+++ xen-unstable.27.3/tools/pygrub/src/__init__.py 2005-04-27 04:50:12.000000000 +0900
@@ -0,0 +1 @@
+
diff -Nurp xen-unstable.27.3.org/tools/pygrub/src/pygrub xen-unstable.27.3/tools/pygrub/src/pygrub
--- xen-unstable.27.3.org/tools/pygrub/src/pygrub 2005-04-27 05:32:43.000000000 +0900
+++ xen-unstable.27.3/tools/pygrub/src/pygrub 2005-04-27 05:57:43.000000000 +0900
@@ -13,7 +13,7 @@
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
-import os, sys, string, struct, tempfile
+import os, os.path, sys, string, struct, tempfile
import logging
import curses, _curses, curses.wrapper
@@ -97,7 +97,14 @@ def get_config(fn):
break
if fs is not None:
- f = fs.open_file("/boot/grub/grub.conf")
+ if os.path.isfile("/boot/grub/menu.lst"):
+ grubfile = "/boot/grub/menu.lst"
+ elif os.path.isfile("/boot/grub/grub.conf"):
+ grubfile = "/boot/grub/grub.conf"
+ else:
+ raise RuntimeError, "we cannot find /boot/grub/{menu.lst,grub.conf} " + \
+ "in the image provided. halt!"
+ f = fs.open_file(grubfile)
buf = f.read()
f.close()
fs.close()
[-- 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
* Re: [PATCH] Guest boot loader support
2005-04-26 21:02 ` aq
@ 2005-04-27 0:30 ` Mark Williamson
2005-04-27 3:14 ` aq
0 siblings, 1 reply; 17+ messages in thread
From: Mark Williamson @ 2005-04-27 0:30 UTC (permalink / raw)
To: aq; +Cc: Mark Williamson, xen-devel
Thanks for the patch.
I haven't looked at Jeremy's code but won't the os.path.isfile() break when
the kernel (and the boot options) are being loaded from within a domain's
filesystem, rather than dom0?
Cheers,
Mark
On Tuesday 26 April 2005 22:02, aq wrote:
> On 4/27/05, Mark Williamson <mark.williamson@cl.cam.ac.uk> wrote:
> > > GRUB only looks for menu.lst; distros create symlinks because that's a
> > > terrible name for a config file. In other words, no code should be
> > > opening grub.conf...
> >
> > OK, I've investigated this using my sacrificial FC3 laptop ;-) I'd not
> > fully investigated this before.
> >
> > Redhat's grub looks for grub.conf and not menu.lst. Standard grub looks
> > for menu.lst.
> >
> > If the loader is to be flexible for different domU distros, it'd be nice
> > to look for both, in one order or the other.
>
> in that case, this patch is better for you, Mark ;-). Please apply
> this on top of Jeremy's patch.
>
> list of changes:
> - make a dummy file tools/pygrub/src/__init__.py
> - tools/src/pygrub looks for either /boot/grub/menu.lst or
> /boot/grub/grub.conf, in that order.
>
> $ diffstat xen-bootloader.aq2.patch
> __init__.py | 1 +
> pygrub | 11 +++++++++--
> 2 files changed, 10 insertions(+), 2 deletions(-)
>
> Signed-off-by: Nguyen Anh Quynh <aquynh@gmail.com>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-27 0:30 ` Mark Williamson
@ 2005-04-27 3:14 ` aq
0 siblings, 0 replies; 17+ messages in thread
From: aq @ 2005-04-27 3:14 UTC (permalink / raw)
To: Mark Williamson, Jeremy Katz; +Cc: xen-devel
On 4/27/05, Mark Williamson <mark.williamson@cl.cam.ac.uk> wrote:
> Thanks for the patch.
>
> I haven't looked at Jeremy's code but won't the os.path.isfile() break when
> the kernel (and the boot options) are being loaded from within a domain's
> filesystem, rather than dom0?
ah you right here. my bad mistake. so we need another patch for this
problem. Perhaps the abstract of filesystem should be extended for
this case also. I am working on it.
thank you,
aq
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] Guest boot loader support
2005-04-26 20:47 ` Mark Williamson
2005-04-26 21:02 ` aq
@ 2005-04-29 3:06 ` Jeremy Katz
1 sibling, 0 replies; 17+ messages in thread
From: Jeremy Katz @ 2005-04-29 3:06 UTC (permalink / raw)
To: Mark Williamson; +Cc: xen-devel@lists.xensource.com
On Tue, 2005-04-26 at 21:47 +0100, Mark Williamson wrote:
> > GRUB only looks for menu.lst; distros create symlinks because that's a
> > terrible name for a config file. In other words, no code should be
> > opening grub.conf...
>
> OK, I've investigated this using my sacrificial FC3 laptop ;-) I'd not fully
> investigated this before.
>
> Redhat's grub looks for grub.conf and not menu.lst. Standard grub looks for
> menu.lst.
>
> If the loader is to be flexible for different domU distros, it'd be nice to
> look for both, in one order or the other.
Yeah, oops... I forgot about that. I've been doing grub.conf so long
now that my mind forgets that Okuji didn't want to take the patch for
upstream grub at times.
Just checking both is the easy answer and aq has already sent the patch
to do so (thanks!)
Jeremy
^ 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.