# HG changeset patch
# User Ian Jackson <Ian.Jackson@xxxxxxxxxxxxx>
# Date 1279122338 -3600
# Node ID bd3e5bc7235b1c5456b6bc1f972ddf6273c41011
# Parent cb1ca77e4b77728d7c76d8524644f6d47f868b30
libxl, xl: support running bootloader (e.g. pygrub) in domain 0
Much of the bootloader interaction (including the Solaris and NetBSD
portability bits) are translated pretty much directly from the python
in tools/python/xen/xend/XendBootloader.py
Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxx>
---
tools/libxl/Makefile | 4
tools/libxl/libxl.h | 21 +
tools/libxl/libxl_bootloader.c | 449 +++++++++++++++++++++++++++++++++++++++++
tools/libxl/xl_cmdimpl.c | 22 ++
4 files changed, 493 insertions(+), 3 deletions(-)
diff -r cb1ca77e4b77 -r bd3e5bc7235b tools/libxl/Makefile
--- a/tools/libxl/Makefile Wed Jul 14 16:44:18 2010 +0100
+++ b/tools/libxl/Makefile Wed Jul 14 16:45:38 2010 +0100
@@ -15,9 +15,9 @@ CFLAGS += -I. -fPIC
CFLAGS += -I. -fPIC
CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) $(CFLAGS_libxenstore)
$(CFLAGS_libblktapctl)
-LIBS = $(LDFLAGS_libxenctrl) $(LDFLAGS_libxenguest) $(LDFLAGS_libxenstore)
$(LDFLAGS_libblktapctl)
+LIBS = $(LDFLAGS_libxenctrl) $(LDFLAGS_libxenguest) $(LDFLAGS_libxenstore)
$(LDFLAGS_libblktapctl) -lutil
-LIBXL_OBJS-y = osdeps.o libxl_paths.o
+LIBXL_OBJS-y = osdeps.o libxl_paths.o libxl_bootloader.o
LIBXL_OBJS = flexarray.o libxl.o libxl_dom.o libxl_exec.o libxl_xshelp.o
libxl_device.o libxl_internal.o xenguest.o libxl_utils.o $(LIBXL_OBJS-y)
AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h
diff -r cb1ca77e4b77 -r bd3e5bc7235b tools/libxl/libxl.h
--- a/tools/libxl/libxl.h Wed Jul 14 16:44:18 2010 +0100
+++ b/tools/libxl/libxl.h Wed Jul 14 16:45:38 2010 +0100
@@ -132,7 +132,9 @@ typedef struct {
} hvm;
struct {
uint32_t slack_memkb;
- const char *cmdline;
+ const char *bootloader;
+ const char *bootloader_args;
+ char *cmdline;
libxl_file_reference ramdisk;
const char *features;
} pv;
@@ -329,6 +331,23 @@ int libxl_file_reference_map(struct libx
int libxl_file_reference_map(struct libxl_ctx *ctx, libxl_file_reference *f);
int libxl_file_reference_unmap(struct libxl_ctx *ctx, libxl_file_reference *f);
+/*
+ * Run the configured bootloader for a PV domain and update
+ * info->kernel, info->u.pv.ramdisk and info->u.pv.cmdline as
+ * appropriate (any initial values present in these fields must have
+ * been allocated with malloc).
+ *
+ * Is a NOP on non-PV domains or those with no bootloader configured.
+ *
+ * Users should call libxl_file_reference_unmap on the kernel and
+ * ramdisk to cleanup or rely on libxl_domain_{build,restore} to do
+ * it.
+ */
+int libxl_run_bootloader(struct libxl_ctx *ctx,
+ libxl_domain_build_info *info,
+ libxl_device_disk *disk,
+ uint32_t domid);
+
char *libxl_uuid2string(struct libxl_ctx *ctx, uint8_t uuid[16]);
/* 0 means ERROR_ENOMEM, which we have logged */
diff -r cb1ca77e4b77 -r bd3e5bc7235b tools/libxl/libxl_bootloader.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/libxl/libxl_bootloader.c Wed Jul 14 16:45:38 2010 +0100
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2010 Citrix Ltd.
+ * Author Ian Campbell <ian.campbell@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h"
+
+#include <string.h>
+#include <pty.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "libxl.h"
+#include "libxl_internal.h"
+
+#include "flexarray.h"
+
+#define XENCONSOLED_BUF_SIZE 16
+#define BOOTLOADER_BUF_SIZE 1024
+
+static char **make_bootloader_args(struct libxl_ctx *ctx,
+ libxl_domain_build_info *info,
+ uint32_t domid,
+ const char *fifo, const char *disk)
+{
+ flexarray_t *args;
+ int nr = 0;
+
+ args = flexarray_make(1, 1);
+ if (!args)
+ return NULL;
+
+ flexarray_set(args, nr++, (char *)info->u.pv.bootloader);
+
+ if (info->kernel.path)
+ flexarray_set(args, nr++, libxl_sprintf(ctx, "--kernel=%s",
info->kernel.path));
+ if (info->u.pv.ramdisk.path)
+ flexarray_set(args, nr++, libxl_sprintf(ctx, "--ramdisk=%s",
info->u.pv.ramdisk.path));
+ if (info->u.pv.cmdline && *info->u.pv.cmdline != '\0')
+ flexarray_set(args, nr++, libxl_sprintf(ctx, "--args=%s",
info->u.pv.cmdline));
+
+ flexarray_set(args, nr++, libxl_sprintf(ctx, "--output=%s", fifo));
+ flexarray_set(args, nr++, "--output-format=simple0");
+ flexarray_set(args, nr++, libxl_sprintf(ctx, "--output-directory=%s",
"/var/run/libxl/"));
+
+ if (info->u.pv.bootloader_args) {
+ char *saveptr;
+ /* Operate on a duplicate since strtok modifes the argument */
+ char *dup = libxl_strdup(ctx, info->u.pv.bootloader_args);
+ char *t = strtok_r(dup, " \t\n", &saveptr);
+ do {
+ flexarray_set(args, nr++, t);
+ } while ((t = strtok_r(NULL, " \t\n", &saveptr)));
+ }
+
+ flexarray_set(args, nr++, strdup(disk));
+
+ /* Sentinal for execv */
+ flexarray_set(args, nr++, NULL);
+
+ return (char **) flexarray_contents(args); /* Frees args */
+}
+
+static int open_xenconsoled_pty(int *master, int *slave, char *slave_path,
size_t slave_path_len)
+{
+ struct termios termattr;
+ int ret;
+
+ ret = openpty(master, slave, NULL, NULL, NULL);
+ if (ret < 0)
+ return -1;
+
+ ret = ttyname_r(*slave, slave_path, slave_path_len);
+ if (ret == -1) {
+ close(*master);
+ close(*slave);
+ *master = *slave = -1;
+ return -1;
+ }
+
+ /*
+ * On Solaris, the pty master side will get cranky if we try
+ * to write to it while there is no slave. To work around this,
+ * keep the slave descriptor open until we're done. Set it
+ * to raw terminal parameters, otherwise it will echo back
+ * characters, which will confuse the I/O loop below.
+ * Furthermore, a raw master pty device has no terminal
+ * semantics on Solaris, so don't try to set any attributes
+ * for it.
+ */
+#if !defined(__sun__) && !defined(__NetBSD__)
+ tcgetattr(*master, &termattr);
+ cfmakeraw(&termattr);
+ tcsetattr(*master, TCSANOW, &termattr);
+
+ close(*slave);
+ *slave = -1;
+#else
+ tcgetattr(*slave, &termattr);
+ cfmakeraw(&termattr);
+ tcsetattr(*slave, TCSANOW, &termattr);
+#endif
+
+ fcntl(*master, F_SETFL, O_NDELAY);
+
+ return 0;
+}
+
+static pid_t fork_exec_bootloader(int *master, char *arg0, char **args)
+{
+ struct termios termattr;
+ pid_t pid = forkpty(master, NULL, NULL, NULL);
+ if (pid == -1)
+ return -1;
+ else if (pid == 0) {
+ setenv("TERM", "vt100", 1);
+ libxl_exec(-1, -1, -1, arg0, args);
+ return -1;
+ }
+
+ /*
+ * On Solaris, the master pty side does not have terminal semantics,
+ * so don't try to set any attributes, as it will fail.
+ */
+#if !defined(__sun__)
+ tcgetattr(*master, &termattr);
+ cfmakeraw(&termattr);
+ tcsetattr(*master, TCSANOW, &termattr);
+#endif
+
+ fcntl(*master, F_SETFL, O_NDELAY);
+
+ return pid;
+}
+
+/*
+ * filedescriptors:
+ * fifo_fd - bootstring output from the bootloader
+ * xenconsoled_fd - input/output from/to xenconsole
+ * bootloader_fd - input/output from/to pty that controls the bootloader
+ * The filedescriptors are NDELAY, so it's ok to try to read
+ * bigger chunks than may be available, to keep e.g. curses
+ * screen redraws in the bootloader efficient. xenconsoled_fd is the side that
+ * gets xenconsole input, which will be keystrokes, so a small number
+ * is sufficient. bootloader_fd is pygrub output, which will be curses screen
+ * updates, so a larger number (1024) is appropriate there.
+ *
+ * For writeable descriptors, only include them in the set for select
+ * if there is actual data to write, otherwise this would loop too fast,
+ * eating up CPU time.
+ */
+static char * bootloader_interact(struct libxl_ctx *ctx, int xenconsoled_fd,
int bootloader_fd, int fifo_fd)
+{
+ int ret;
+
+ size_t nr_out = 0, size_out = 0;
+ char *output = NULL;
+
+ /* input from xenconsole. read on xenconsoled_fd write to bootloader_fd */
+ int xenconsoled_prod = 0, xenconsoled_cons = 0;
+ char xenconsoled_buf[XENCONSOLED_BUF_SIZE];
+ /* output from bootloader. read on bootloader_fd write to xenconsoled_fd */
+ int bootloader_prod = 0, bootloader_cons = 0;
+ char bootloader_buf[BOOTLOADER_BUF_SIZE];
+
+ while(1) {
+ fd_set wsel, rsel;
+ int nfds;
+
+ if (xenconsoled_prod == xenconsoled_cons)
+ xenconsoled_prod = xenconsoled_cons = 0;
+ if (bootloader_prod == bootloader_cons)
+ bootloader_prod = bootloader_cons = 0;
+
+ FD_ZERO(&rsel);
+ FD_SET(fifo_fd, &rsel);
+ nfds = fifo_fd + 1;
+ if (xenconsoled_prod == 0 || (xenconsoled_prod < BOOTLOADER_BUF_SIZE
&& xenconsoled_cons == 0)) {
+ FD_SET(xenconsoled_fd, &rsel);
+ nfds = xenconsoled_fd + 1 > nfds ? xenconsoled_fd + 1 : nfds;
+ }
+ if (bootloader_prod == 0 || (bootloader_prod < BOOTLOADER_BUF_SIZE &&
bootloader_cons == 0)) {
+ FD_SET(bootloader_fd, &rsel);
+ nfds = bootloader_fd + 1 > nfds ? bootloader_fd + 1 : nfds;
+ }
+
+ FD_ZERO(&wsel);
+ if (bootloader_prod != bootloader_cons) {
+ FD_SET(xenconsoled_fd, &wsel);
+ nfds = xenconsoled_fd + 1 > nfds ? xenconsoled_fd + 1 : nfds;
+ }
+ if (xenconsoled_prod != xenconsoled_cons) {
+ FD_SET(bootloader_fd, &wsel);
+ nfds = bootloader_fd + 1 > nfds ? bootloader_fd + 1 : nfds;
+ }
+
+ ret = select(nfds, &rsel, &wsel, NULL, NULL);
+ if (ret < 0)
+ goto out_err;
+
+ /* Input from xenconsole, read xenconsoled_fd, write bootloader_fd */
+ if (FD_ISSET(xenconsoled_fd, &rsel)) {
+ ret = read(xenconsoled_fd, &xenconsoled_buf[xenconsoled_prod],
XENCONSOLED_BUF_SIZE - xenconsoled_prod);
+ if (ret < 0 && errno != EIO && errno != EAGAIN)
+ goto out_err;
+ if (ret > 0)
+ xenconsoled_prod += ret;
+ }
+ if (FD_ISSET(bootloader_fd, &wsel)) {
+ ret = write(bootloader_fd, &xenconsoled_buf[xenconsoled_cons],
xenconsoled_prod - xenconsoled_cons);
+ if (ret < 0 && errno != EIO && errno != EAGAIN)
+ goto out_err;
+ if (ret > 0)
+ xenconsoled_cons += ret;
+ }
+
+ /* Input from bootloader, read bootloader_fd, write xenconsoled_fd */
+ if (FD_ISSET(bootloader_fd, &rsel)) {
+ ret = read(bootloader_fd, &bootloader_buf[bootloader_prod],
BOOTLOADER_BUF_SIZE - bootloader_prod);
+ if (ret < 0 && errno != EIO && errno != EAGAIN)
+ goto out_err;
+ if (ret > 0)
+ bootloader_prod += ret;
+ }
+ if (FD_ISSET(xenconsoled_fd, &wsel)) {
+ ret = write(xenconsoled_fd, &bootloader_buf[bootloader_cons],
bootloader_prod - bootloader_cons);
+ if (ret < 0 && errno != EIO && errno != EAGAIN)
+ goto out_err;
+ if (ret > 0)
+ bootloader_cons += ret;
+ }
+
+ if (FD_ISSET(fifo_fd, &rsel)) {
+ if (size_out - nr_out < 256) {
+ char *temp;
+ size_t new_size = size_out == 0 ? 32 : size_out * 2;
+
+ temp = realloc(output, new_size);
+ if (temp == NULL)
+ goto out_err;
+ output = temp;
+ memset(output + size_out, new_size - size_out, 0);
+ size_out = new_size;
+ }
+
+ ret = read(fifo_fd, output + nr_out, size_out - nr_out);
+ if (ret > 0)
+ nr_out += ret;
+ if (ret == 0)
+ break;
+ }
+ }
+
+ libxl_ptr_add(ctx, output);
+ return output;
+
+out_err:
+ free(output);
+ return NULL;
+}
+
+static void parse_bootloader_result(struct libxl_ctx *ctx,
+ libxl_domain_build_info *info,
+ const char *o)
+{
+ while (*o != '\0') {
+ if (strncmp("kernel ", o, strlen("kernel ")) == 0) {
+ free(info->kernel.path);
+ info->kernel.path = strdup(o + strlen("kernel "));
+ libxl_file_reference_map(ctx, &info->kernel);
+ unlink(info->kernel.path);
+ } else if (strncmp("ramdisk ", o, strlen("ramdisk ")) == 0) {
+ free(info->u.pv.ramdisk.path);
+ info->u.pv.ramdisk.path = strdup(o + strlen("ramdisk "));
+ libxl_file_reference_map(ctx, &info->u.pv.ramdisk);
+ unlink(info->u.pv.ramdisk.path);
+ } else if (strncmp("args ", o, strlen("args ")) == 0) {
+ free(info->u.pv.cmdline);
+ info->u.pv.cmdline = strdup(o + strlen("args "));
+ }
+
+ o = o + strlen(o) + 1;
+ }
+}
+
+int libxl_run_bootloader(struct libxl_ctx *ctx,
+ libxl_domain_build_info *info,
+ libxl_device_disk *disk,
+ uint32_t domid)
+{
+ int ret;
+
+ char *fifo = NULL;
+ const char *diskpath = NULL;
+ char **args = NULL;
+
+ char tempdir_template[] = "/var/run/libxl/bl.XXXXXX";
+ char *tempdir;
+
+ char *dom_console_xs_path;
+ char dom_console_slave_tty_path[PATH_MAX];
+
+ int xenconsoled_fd = -1, xenconsoled_slave = -1;
+ int bootloader_fd = -1, fifo_fd = -1;
+
+ int blrc;
+ pid_t pid;
+ char *blout;
+
+ struct stat st_buf;
+
+ if (info->hvm || !info->u.pv.bootloader)
+ return 0;
+
+ if (!disk)
+ return ERROR_INVAL;
+
+ ret = mkdir("/var/run/libxl/", S_IRWXU);
+ if (ret < 0 && errno != EEXIST)
+ return ERROR_FAIL;
+
+ ret = stat("/var/run/libxl/", &st_buf);
+ if (ret < 0)
+ return ERROR_FAIL;
+
+ if (!S_ISDIR(st_buf.st_mode))
+ return ERROR_FAIL;
+
+ tempdir = mkdtemp(tempdir_template);
+ if (tempdir == NULL)
+ return ERROR_FAIL;
+
+ ret = asprintf(&fifo, "%s/fifo", tempdir);
+ if (ret < 0) {
+ ret = ERROR_FAIL;
+ fifo = NULL;
+ goto out;
+ }
+
+ ret = mkfifo(fifo, 0600);
+ if (ret < 0) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ diskpath = libxl_device_disk_local_attach(ctx, disk);
+ if (!diskpath) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ args = make_bootloader_args(ctx, info, domid, fifo, diskpath);
+ if (args == NULL) {
+ ret = ERROR_NOMEM;
+ goto out;
+ }
+
+ /*
+ * We need to present the bootloader's tty as a pty slave that xenconsole
+ * can access. Since the bootloader itself needs a pty slave,
+ * we end up with a connection like this:
+ *
+ * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
+ *
+ * where we copy characters between the two master fds, as well as
+ * listening on the bootloader's fifo for the results.
+ */
+ ret = open_xenconsoled_pty(&xenconsoled_fd, &xenconsoled_slave,
+ &dom_console_slave_tty_path[0],
+ sizeof(dom_console_slave_tty_path));
+ if (ret < 0) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ dom_console_xs_path = libxl_sprintf(ctx, "%s/serial/0/tty",
libxl_xs_get_dompath(ctx, domid));
+ libxl_xs_write(ctx, XBT_NULL, dom_console_xs_path,
dom_console_slave_tty_path);
+
+ pid = fork_exec_bootloader(&bootloader_fd, (char *)info->u.pv.bootloader,
args);
+ if (pid < 0) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ while (1) {
+ fifo_fd = open(fifo, O_RDONLY);
+ if (fifo_fd > -1)
+ break;
+
+ if (errno == EINTR)
+ continue;
+
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ fcntl(fifo_fd, F_SETFL, O_NDELAY);
+
+ blout = bootloader_interact(ctx, xenconsoled_fd, bootloader_fd, fifo_fd);
+ if (blout == NULL) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ pid = waitpid(pid, &blrc, 0);
+ if (pid == -1 || (pid > 0 && WIFEXITED(blrc) && WEXITSTATUS(blrc) != 0)) {
+ ret = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl_device_disk_local_detach(ctx, disk);
+
+ parse_bootloader_result(ctx, info, blout);
+
+ ret = 0;
+out:
+ if (fifo_fd > -1)
+ close(fifo_fd);
+ if (bootloader_fd > -1)
+ close(bootloader_fd);
+ if (xenconsoled_fd > -1)
+ close(xenconsoled_fd);
+ if (xenconsoled_slave > -1)
+ close(xenconsoled_fd);
+
+ if (fifo) {
+ unlink(fifo);
+ free(fifo);
+ }
+
+ rmdir(tempdir);
+
+ free(args);
+
+ return ret;
+}
+
diff -r cb1ca77e4b77 -r bd3e5bc7235b tools/libxl/xl_cmdimpl.c
--- a/tools/libxl/xl_cmdimpl.c Wed Jul 14 16:44:18 2010 +0100
+++ b/tools/libxl/xl_cmdimpl.c Wed Jul 14 16:45:38 2010 +0100
@@ -362,6 +362,12 @@ static void printf_info(int domid,
printf("\t(max_memkb %d)\n", b_info->max_memkb);
printf("\t(target_memkb %d)\n", b_info->target_memkb);
printf("\t(nomigrate %d)\n", b_info->disable_migrate);
+
+ if (!c_info->hvm && b_info->u.pv.bootloader) {
+ printf("\t(bootloader %s)\n", b_info->u.pv.bootloader);
+ if (b_info->u.pv.bootloader_args)
+ printf("\t(bootloader_args %s)\n", b_info->u.pv.bootloader_args);
+ }
printf("\t(image\n");
if (c_info->hvm) {
@@ -598,6 +604,16 @@ static void parse_config_data(const char
if ((root || extra) && !cmdline) {
fprintf(stderr, "Failed to allocate memory for cmdline\n");
+ exit(1);
+ }
+
+ if (!xlu_cfg_get_string (config, "bootloader", &buf))
+ b_info->u.pv.bootloader = strdup(buf);
+ if (!xlu_cfg_get_string (config, "bootloader_args", &buf))
+ b_info->u.pv.bootloader_args = strdup(buf);
+
+ if (!b_info->u.pv.bootloader && !b_info->kernel.path) {
+ fprintf(stderr, "Neither kernel nor bootloader specified\n");
exit(1);
}
@@ -1159,6 +1175,12 @@ start:
goto error_out;
}
+ ret = libxl_run_bootloader(&ctx, &info2, num_disks > 0 ? &disks[0] : NULL,
domid);
+ if (ret) {
+ fprintf(stderr, "failed to run bootloader: %d\n", ret);
+ goto error_out;
+ }
+
if (!restore_file || !need_daemon) {
if (dm_info.saved_state) {
free(dm_info.saved_state);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|