summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2008-11-19 16:58:23 +0000
committerDaniel P. Berrange <berrange@redhat.com>2008-11-19 16:58:23 +0000
commit714bef5b6a1278972aaa4b733ec27b3cf8edbce0 (patch)
tree3d1c4c14ddb27651f11dffc1675674167dc474b5 /src
parentAdd a virFreeCallback to event loop APIs (diff)
downloadlibvirt-714bef5b6a1278972aaa4b733ec27b3cf8edbce0.tar.gz
libvirt-714bef5b6a1278972aaa4b733ec27b3cf8edbce0.tar.bz2
libvirt-714bef5b6a1278972aaa4b733ec27b3cf8edbce0.zip
Add an initial User Mode Linux Driver
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am11
-rw-r--r--src/domain_conf.c47
-rw-r--r--src/domain_conf.h1
-rw-r--r--src/driver.h3
-rw-r--r--src/qemu_conf.c3
-rw-r--r--src/uml_conf.c404
-rw-r--r--src/uml_conf.h74
-rw-r--r--src/uml_driver.c1676
-rw-r--r--src/uml_driver.h32
-rw-r--r--src/virterror.c4
10 files changed, 2231 insertions, 24 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3e6e72639..b2c7e3f6e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -117,6 +117,10 @@ QEMU_DRIVER_SOURCES = \
qemu_conf.c qemu_conf.h \
qemu_driver.c qemu_driver.h
+UML_DRIVER_SOURCES = \
+ uml_conf.c uml_conf.h \
+ uml_driver.c uml_driver.h
+
NETWORK_DRIVER_SOURCES = \
network_driver.h network_driver.c
@@ -210,6 +214,12 @@ noinst_LTLIBRARIES += libvirt_driver_lxc.la
libvirt_driver_lxc_la_SOURCES = $(LXC_DRIVER_SOURCES)
endif
+if WITH_UML
+noinst_LTLIBRARIES += libvirt_driver_uml.la
+# Stateful, so linked to daemon instead
+#libvirt_la_LIBADD += libvirt_driver_uml.la
+libvirt_driver_uml_la_SOURCES = $(UML_DRIVER_SOURCES)
+endif
if WITH_NETWORK
noinst_LTLIBRARIES += libvirt_driver_network.la
@@ -247,6 +257,7 @@ EXTRA_DIST += \
$(XEN_DRIVER_SOURCES) \
$(QEMU_DRIVER_SOURCES) \
$(LXC_DRIVER_SOURCES) \
+ $(UML_DRIVER_SOURCES) \
$(OPENVZ_DRIVER_SOURCES) \
$(NETWORK_DRIVER_SOURCES) \
$(STORAGE_DRIVER_SOURCES) \
diff --git a/src/domain_conf.c b/src/domain_conf.c
index 7a5e5019e..39e60d459 100644
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -86,7 +86,8 @@ VIR_ENUM_IMPL(virDomainDiskBus, VIR_DOMAIN_DISK_BUS_LAST,
"scsi",
"virtio",
"xen",
- "usb")
+ "usb",
+ "uml")
VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST,
"mount",
@@ -610,7 +611,8 @@ virDomainDiskDefParseXML(virConnectPtr conn,
!STRPREFIX((const char *)target, "hd") &&
!STRPREFIX((const char *)target, "sd") &&
!STRPREFIX((const char *)target, "vd") &&
- !STRPREFIX((const char *)target, "xvd")) {
+ !STRPREFIX((const char *)target, "xvd") &&
+ !STRPREFIX((const char *)target, "ubd")) {
virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Invalid harddisk device name: %s"), target);
goto error;
@@ -634,6 +636,8 @@ virDomainDiskDefParseXML(virConnectPtr conn,
def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO;
else if (STRPREFIX(target, "xvd"))
def->bus = VIR_DOMAIN_DISK_BUS_XEN;
+ else if (STRPREFIX(target, "ubd"))
+ def->bus = VIR_DOMAIN_DISK_BUS_UML;
else
def->bus = VIR_DOMAIN_DISK_BUS_IDE;
}
@@ -1879,13 +1883,16 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
}
if (STREQ(def->os.type, "xen") ||
- STREQ(def->os.type, "hvm")) {
+ STREQ(def->os.type, "hvm") ||
+ STREQ(def->os.type, "uml")) {
def->os.kernel = virXPathString(conn, "string(./os/kernel[1])", ctxt);
def->os.initrd = virXPathString(conn, "string(./os/initrd[1])", ctxt);
def->os.cmdline = virXPathString(conn, "string(./os/cmdline[1])", ctxt);
def->os.root = virXPathString(conn, "string(./os/root[1])", ctxt);
def->os.loader = virXPathString(conn, "string(./os/loader[1])", ctxt);
+ }
+ if (STREQ(def->os.type, "hvm")) {
/* analysis of the boot devices */
if ((n = virXPathNodeSet(conn, "./os/boot", ctxt, &nodes)) < 0) {
virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
@@ -2016,32 +2023,30 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
}
VIR_FREE(nodes);
- /*
- * If no serial devices were listed, then look for console
- * devices which is the legacy syntax for the same thing
- */
- if (def->nserials == 0) {
- if ((node = virXPathNode(conn, "./devices/console[1]", ctxt)) != NULL) {
- virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
- node);
- if (!chr)
- goto error;
+ if ((node = virXPathNode(conn, "./devices/console[1]", ctxt)) != NULL) {
+ virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
+ node);
+ if (!chr)
+ goto error;
- chr->dstPort = 0;
- /*
- * For HVM console actually created a serial device
- * while for non-HVM it was a parvirt console
- */
- if (STREQ(def->os.type, "hvm")) {
+ chr->dstPort = 0;
+ /*
+ * For HVM console actually created a serial device
+ * while for non-HVM it was a parvirt console
+ */
+ if (STREQ(def->os.type, "hvm")) {
+ if (def->nserials != 0) {
+ virDomainChrDefFree(chr);
+ } else {
if (VIR_ALLOC_N(def->serials, 1) < 0) {
virDomainChrDefFree(chr);
goto no_memory;
}
def->nserials = 1;
def->serials[0] = chr;
- } else {
- def->console = chr;
}
+ } else {
+ def->console = chr;
}
}
diff --git a/src/domain_conf.h b/src/domain_conf.h
index 88fd6d5da..084c44852 100644
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -75,6 +75,7 @@ enum virDomainDiskBus {
VIR_DOMAIN_DISK_BUS_VIRTIO,
VIR_DOMAIN_DISK_BUS_XEN,
VIR_DOMAIN_DISK_BUS_USB,
+ VIR_DOMAIN_DISK_BUS_UML,
VIR_DOMAIN_DISK_BUS_LAST
};
diff --git a/src/driver.h b/src/driver.h
index 8962affb8..c79df9584 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -17,7 +17,8 @@ typedef enum {
VIR_DRV_QEMU = 3,
VIR_DRV_REMOTE = 4,
VIR_DRV_OPENVZ = 5,
- VIR_DRV_LXC = 6
+ VIR_DRV_LXC = 6,
+ VIR_DRV_UML = 7,
} virDrvNo;
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index 0e3b95988..c66cef9e3 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -56,7 +56,8 @@ VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST,
"scsi",
"virtio",
"xen",
- "usb")
+ "usb",
+ "uml")
#define qemudLog(level, msg...) fprintf(stderr, msg)
diff --git a/src/uml_conf.c b/src/uml_conf.c
new file mode 100644
index 000000000..674830d28
--- /dev/null
+++ b/src/uml_conf.c
@@ -0,0 +1,404 @@
+/*
+ * uml_conf.c: UML driver configuration
+ *
+ * Copyright (C) 2006, 2007, 2008 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include <dirent.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <sys/utsname.h>
+
+#if HAVE_NUMACTL
+#include <numa.h>
+#endif
+
+#include "uml_conf.h"
+#include "uuid.h"
+#include "buf.h"
+#include "conf.h"
+#include "util.h"
+#include "memory.h"
+#include "verify.h"
+
+
+#define umlLog(level, msg...) fprintf(stderr, msg)
+
+
+
+#if HAVE_NUMACTL
+#define MAX_CPUS 4096
+#define MAX_CPUS_MASK_SIZE (sizeof(unsigned long))
+#define MAX_CPUS_MASK_LEN (MAX_CPUS / MAX_CPUS_MASK_SIZE)
+#define MAX_CPUS_MASK_BYTES (MAX_CPUS / 8)
+
+#define MASK_CPU_ISSET(mask, cpu) \
+ (((mask)[((cpu) / MAX_CPUS_MASK_SIZE)] >> ((cpu) % MAX_CPUS_MASK_SIZE)) & 1)
+
+static int
+umlCapsInitNUMA(virCapsPtr caps)
+{
+ int n, i;
+ unsigned long *mask = NULL;
+ int ncpus;
+ int *cpus = NULL;
+ int ret = -1;
+
+ if (numa_available() < 0)
+ return 0;
+
+ if (VIR_ALLOC_N(mask, MAX_CPUS_MASK_LEN) < 0)
+ goto cleanup;
+
+ for (n = 0 ; n <= numa_max_node() ; n++) {
+ if (numa_node_to_cpus(n, mask, MAX_CPUS_MASK_BYTES) < 0)
+ goto cleanup;
+
+ for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
+ if (MASK_CPU_ISSET(mask, i))
+ ncpus++;
+
+ if (VIR_ALLOC_N(cpus, ncpus) < 0)
+ goto cleanup;
+
+ for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
+ if (MASK_CPU_ISSET(mask, i))
+ cpus[ncpus++] = i;
+
+ if (virCapabilitiesAddHostNUMACell(caps,
+ n,
+ ncpus,
+ cpus) < 0)
+ goto cleanup;
+
+ VIR_FREE(cpus);
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cpus);
+ VIR_FREE(mask);
+ return ret;
+}
+#else
+static int umlCapsInitNUMA(virCapsPtr caps ATTRIBUTE_UNUSED) { return 0; }
+#endif
+
+virCapsPtr umlCapsInit(void) {
+ struct utsname utsname;
+ virCapsPtr caps;
+ virCapsGuestPtr guest;
+
+ /* Really, this never fails - look at the man-page. */
+ uname (&utsname);
+
+ if ((caps = virCapabilitiesNew(utsname.machine,
+ 0, 0)) == NULL)
+ goto no_memory;
+
+ if (umlCapsInitNUMA(caps) < 0)
+ goto no_memory;
+
+ if ((guest = virCapabilitiesAddGuest(caps,
+ "uml",
+ utsname.machine,
+ STREQ(utsname.machine, "x86_64") ? 64 : 32,
+ NULL,
+ NULL,
+ 0,
+ NULL)) == NULL)
+ goto no_memory;
+
+ if (virCapabilitiesAddGuestDomain(guest,
+ "uml",
+ NULL,
+ NULL,
+ 0,
+ NULL) == NULL)
+ goto no_memory;
+
+ return caps;
+
+ no_memory:
+ virCapabilitiesFree(caps);
+ return NULL;
+}
+
+
+static char *
+umlBuildCommandLineChr(virConnectPtr conn,
+ virDomainChrDefPtr def,
+ const char *dev)
+{
+ char *ret;
+
+ switch (def->type) {
+ case VIR_DOMAIN_CHR_TYPE_NULL:
+ if (asprintf(&ret, "%s%d=null", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_PTY:
+ if (asprintf(&ret, "%s%d=pts", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_DEV:
+ if (asprintf(&ret, "%s%d=tty:%s", dev, def->dstPort,
+ def->data.file.path) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_STDIO:
+ if (asprintf(&ret, "%s%d=fd:0,fd:1", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_TCP:
+ if (def->data.tcp.listen != 1) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("only TCP listen is supported for chr device"));
+ return NULL;
+ }
+
+ if (asprintf(&ret, "%s%d=port:%s", dev, def->dstPort,
+ def->data.tcp.service) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_FILE:
+ case VIR_DOMAIN_CHR_TYPE_PIPE:
+ /* XXX could open the file/pipe & just pass the FDs */
+
+ case VIR_DOMAIN_CHR_TYPE_VC:
+ case VIR_DOMAIN_CHR_TYPE_UDP:
+ case VIR_DOMAIN_CHR_TYPE_UNIX:
+ default:
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unsupported chr device type %d"), def->type);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Constructs a argv suitable for launching uml with config defined
+ * for a given virtual machine.
+ */
+int umlBuildCommandLine(virConnectPtr conn,
+ struct uml_driver *driver ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ const char ***retargv,
+ const char ***retenv,
+ int **tapfds,
+ int *ntapfds) {
+ int i, j;
+ char memory[50];
+ struct utsname ut;
+ int qargc = 0, qarga = 0;
+ const char **qargv = NULL;
+ int qenvc = 0, qenva = 0;
+ const char **qenv = NULL;
+
+ uname(&ut);
+
+#define ADD_ARG_SPACE \
+ do { \
+ if (qargc == qarga) { \
+ qarga += 10; \
+ if (VIR_REALLOC_N(qargv, qarga) < 0) \
+ goto no_memory; \
+ } \
+ } while (0)
+
+#define ADD_ARG(thisarg) \
+ do { \
+ ADD_ARG_SPACE; \
+ qargv[qargc++] = thisarg; \
+ } while (0)
+
+#define ADD_ARG_LIT(thisarg) \
+ do { \
+ ADD_ARG_SPACE; \
+ if ((qargv[qargc++] = strdup(thisarg)) == NULL) \
+ goto no_memory; \
+ } while (0)
+
+#define ADD_ARG_PAIR(key,val) \
+ do { \
+ char *arg; \
+ ADD_ARG_SPACE; \
+ if (asprintf(&arg, "%s=%s", key, val) < 0) \
+ goto no_memory; \
+ qargv[qargc++] = arg; \
+ } while (0)
+
+
+#define ADD_ENV_SPACE \
+ do { \
+ if (qenvc == qenva) { \
+ qenva += 10; \
+ if (VIR_REALLOC_N(qenv, qenva) < 0) \
+ goto no_memory; \
+ } \
+ } while (0)
+
+#define ADD_ENV(thisarg) \
+ do { \
+ ADD_ENV_SPACE; \
+ qenv[qenvc++] = thisarg; \
+ } while (0)
+
+#define ADD_ENV_LIT(thisarg) \
+ do { \
+ ADD_ENV_SPACE; \
+ if ((qenv[qenvc++] = strdup(thisarg)) == NULL) \
+ goto no_memory; \
+ } while (0)
+
+#define ADD_ENV_COPY(envname) \
+ do { \
+ char *val = getenv(envname); \
+ char *envval; \
+ ADD_ENV_SPACE; \
+ if (val != NULL) { \
+ if (asprintf(&envval, "%s=%s", envname, val) < 0) \
+ goto no_memory; \
+ qenv[qenvc++] = envval; \
+ } \
+ } while (0)
+
+ snprintf(memory, sizeof(memory), "%luK", vm->def->memory);
+
+ ADD_ENV_LIT("LC_ALL=C");
+
+ ADD_ENV_COPY("LD_PRELOAD");
+ ADD_ENV_COPY("LD_LIBRARY_PATH");
+ ADD_ENV_COPY("PATH");
+ ADD_ENV_COPY("HOME");
+ ADD_ENV_COPY("USER");
+ ADD_ENV_COPY("LOGNAME");
+ ADD_ENV_COPY("TMPDIR");
+
+ ADD_ARG_LIT(vm->def->os.kernel);
+ //ADD_ARG_PAIR("con0", "fd:0,fd:1");
+ ADD_ARG_PAIR("mem", memory);
+ ADD_ARG_PAIR("umid", vm->def->name);
+
+ if (vm->def->os.root)
+ ADD_ARG_PAIR("root", vm->def->os.root);
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ virDomainDiskDefPtr disk = vm->def->disks[i];
+
+ if (!STRPREFIX(disk->dst, "ubd")) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unsupported disk type '%s'"), disk->dst);
+ goto error;
+ }
+
+ ADD_ARG_PAIR(disk->dst, disk->src);
+ }
+
+ for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
+ char *ret;
+ if (i == 0 && vm->def->console)
+ ret = umlBuildCommandLineChr(conn, vm->def->console, "con");
+ else
+ if (asprintf(&ret, "con%d=none", i) < 0)
+ goto no_memory;
+ ADD_ARG(ret);
+ }
+
+ for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
+ virDomainChrDefPtr chr = NULL;
+ char *ret;
+ for (j = 0 ; j < vm->def->nserials ; j++)
+ if (vm->def->serials[j]->dstPort == i)
+ chr = vm->def->serials[j];
+ if (chr)
+ ret = umlBuildCommandLineChr(conn, chr, "ssl");
+ else
+ if (asprintf(&ret, "ssl%d=none", i) < 0)
+ goto no_memory;
+ ADD_ARG(ret);
+ }
+
+ ADD_ARG(NULL);
+ ADD_ENV(NULL);
+
+ *retargv = qargv;
+ *retenv = qenv;
+ return 0;
+
+ no_memory:
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+ "%s", _("failed to allocate space for argv string"));
+ error:
+ if (tapfds &&
+ *tapfds) {
+ for (i = 0; i < *ntapfds; i++)
+ close((*tapfds)[i]);
+ VIR_FREE(*tapfds);
+ *ntapfds = 0;
+ }
+ if (qargv) {
+ for (i = 0 ; i < qargc ; i++)
+ VIR_FREE((qargv)[i]);
+ VIR_FREE(qargv);
+ }
+ if (qenv) {
+ for (i = 0 ; i < qenvc ; i++)
+ VIR_FREE((qenv)[i]);
+ VIR_FREE(qenv);
+ }
+ return -1;
+
+#undef ADD_ARG
+#undef ADD_ARG_LIT
+#undef ADD_ARG_SPACE
+#undef ADD_USBDISK
+#undef ADD_ENV
+#undef ADD_ENV_COPY
+#undef ADD_ENV_LIT
+#undef ADD_ENV_SPACE
+}
diff --git a/src/uml_conf.h b/src/uml_conf.h
new file mode 100644
index 000000000..1213df17c
--- /dev/null
+++ b/src/uml_conf.h
@@ -0,0 +1,74 @@
+/*
+ * config.h: VM configuration management
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#ifndef __UML_CONF_H
+#define __UML_CONF_H
+
+#include "internal.h"
+#include "bridge.h"
+#include "capabilities.h"
+#include "network_conf.h"
+#include "domain_conf.h"
+#include "virterror_internal.h"
+
+#define umlDebug(fmt, ...) do {} while(0)
+
+#define UML_CPUMASK_LEN CPU_SETSIZE
+
+#define UML_MAX_CHAR_DEVICE 16
+
+/* Main driver state */
+struct uml_driver {
+ unsigned int umlVersion;
+ int nextvmid;
+
+ virDomainObjList domains;
+
+ brControl *brctl;
+ char *configDir;
+ char *autostartDir;
+ char *logDir;
+ char *monitorDir;
+
+ int inotifyFD;
+ int inotifyWatch;
+
+ virCapsPtr caps;
+};
+
+
+#define umlReportError(conn, dom, net, code, fmt...) \
+ virReportErrorHelper(conn, VIR_FROM_UML, code, __FILE__, \
+ __FUNCTION__, __LINE__, fmt)
+
+virCapsPtr umlCapsInit (void);
+
+int umlBuildCommandLine (virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr dom,
+ const char ***retargv,
+ const char ***retenv,
+ int **tapfds,
+ int *ntapfds);
+
+#endif /* __UML_CONF_H */
diff --git a/src/uml_driver.c b/src/uml_driver.c
new file mode 100644
index 000000000..ac0c32c81
--- /dev/null
+++ b/src/uml_driver.c
@@ -0,0 +1,1676 @@
+/*
+ * uml_driver.c: core driver methods for managing UML guests
+ *
+ * Copyright (C) 2006, 2007, 2008 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <dirent.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+
+#if HAVE_NUMACTL
+#include <numa.h>
+#endif
+
+#include "uml_driver.h"
+#include "uml_conf.h"
+#include "c-ctype.h"
+#include "event.h"
+#include "buf.h"
+#include "util.h"
+#include "nodeinfo.h"
+#include "stats_linux.h"
+#include "capabilities.h"
+#include "memory.h"
+#include "uuid.h"
+#include "domain_conf.h"
+#include "datatypes.h"
+
+/* For storing short-lived temporary files. */
+#define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt"
+
+static int umlShutdown(void);
+
+/* umlDebug statements should be changed to use this macro instead. */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+#define umlLog(level, msg...) fprintf(stderr, msg)
+
+
+static int umlOpenMonitor(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr vm);
+static int umlReadPidFile(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr vm);
+
+static int umlSetCloseExec(int fd) {
+ int flags;
+ if ((flags = fcntl(fd, F_GETFD)) < 0)
+ goto error;
+ flags |= FD_CLOEXEC;
+ if ((fcntl(fd, F_SETFD, flags)) < 0)
+ goto error;
+ return 0;
+ error:
+ umlLog(UML_ERR,
+ "%s", _("Failed to set close-on-exec file descriptor flag\n"));
+ return -1;
+}
+
+static int umlStartVMDaemon(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr vm);
+
+static void umlShutdownVMDaemon(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr vm);
+
+
+static int umlMonitorCommand (virConnectPtr conn,
+ const struct uml_driver *driver,
+ const virDomainObjPtr vm,
+ const char *cmd,
+ char **reply);
+
+static struct uml_driver *uml_driver = NULL;
+
+
+static void
+umlAutostartConfigs(struct uml_driver *driver) {
+ unsigned int i;
+
+ for (i = 0 ; i < driver->domains.count ; i++) {
+ if (driver->domains.objs[i]->autostart &&
+ !virDomainIsActive(driver->domains.objs[i]) &&
+ umlStartVMDaemon(NULL, driver, driver->domains.objs[i]) < 0) {
+ virErrorPtr err = virGetLastError();
+ umlLog(UML_ERR, _("Failed to autostart VM '%s': %s\n"),
+ driver->domains.objs[i]->def->name, err->message);
+ }
+ }
+}
+
+
+static int
+umlIdentifyOneChrPTY(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr dom,
+ virDomainChrDefPtr def,
+ const char *dev)
+{
+ char *cmd;
+ char *res = NULL;
+ int retries = 0;
+ if (asprintf(&cmd, "config %s%d", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return -1;
+ }
+requery:
+ umlMonitorCommand(NULL, driver, dom, cmd, &res);
+
+ if (STRPREFIX(res, "pts:")) {
+ VIR_FREE(def->data.file.path);
+ if ((def->data.file.path = strdup(res + 4)) == NULL) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ VIR_FREE(res);
+ VIR_FREE(cmd);
+ return -1;
+ }
+ } else if (STRPREFIX(res, "pts")) {
+ /* It can take a while to startup, so retry for
+ upto 5 seconds */
+ /* XXX should do this in a better non-blocking
+ way somehow ...perhaps register a timer */
+ if (retries++ < 50) {
+ usleep(1000*10);
+ goto requery;
+ }
+ }
+
+ VIR_FREE(cmd);
+ VIR_FREE(res);
+ return 0;
+}
+
+static int
+umlIdentifyChrPTY(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr dom)
+{
+ int i;
+
+ if (dom->def->console &&
+ dom->def->console->type == VIR_DOMAIN_CHR_TYPE_PTY)
+ if (umlIdentifyOneChrPTY(conn, driver, dom,
+ dom->def->console, "con") < 0)
+ return -1;
+
+ for (i = 0 ; i < dom->def->nserials; i++)
+ if (dom->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PTY &&
+ umlIdentifyOneChrPTY(conn, driver, dom,
+ dom->def->serials[i], "ssl") < 0)
+ return -1;
+
+ return 0;
+}
+
+static void
+umlInotifyEvent(int watch,
+ int fd,
+ int events ATTRIBUTE_UNUSED,
+ void *data)
+{
+ char buf[1024];
+ struct inotify_event *e;
+ int got;
+ char *tmp, *name;
+ struct uml_driver *driver = data;
+ virDomainObjPtr dom;
+
+ if (watch != driver->inotifyWatch)
+ return;
+
+reread:
+ got = read(fd, buf, sizeof(buf));
+ if (got == -1) {
+ if (errno == EINTR)
+ goto reread;
+ return;
+ }
+
+ tmp = buf;
+ while (got) {
+ if (got < sizeof(struct inotify_event))
+ return; /* bad */
+
+ e = (struct inotify_event *)tmp;
+ tmp += sizeof(struct inotify_event);
+ got -= sizeof(struct inotify_event);
+
+ if (got < e->len)
+ return;
+
+ tmp += e->len;
+ got -= e->len;
+
+ name = (char *)&(e->name);
+
+ dom = virDomainFindByName(&driver->domains, name);
+
+ if (!dom) {
+ continue;
+ }
+
+ if (e->mask & IN_DELETE) {
+ if (!virDomainIsActive(dom)) {
+ continue;
+ }
+
+ dom->def->id = -1;
+ dom->pid = -1;
+ if (dom->newDef) {
+ virDomainDefFree(dom->def);
+ dom->def = dom->newDef;
+ }
+ dom->state = VIR_DOMAIN_SHUTOFF;
+ } else if (e->mask & (IN_CREATE | IN_MODIFY)) {
+ if (virDomainIsActive(dom)) {
+ continue;
+ }
+
+ if (umlReadPidFile(NULL, driver, dom) < 0) {
+ continue;
+ }
+
+ dom->def->id = driver->nextvmid++;
+ dom->state = VIR_DOMAIN_RUNNING;
+
+ if (umlOpenMonitor(NULL, driver, dom) < 0)
+ umlShutdownVMDaemon(NULL, driver, dom);
+
+ if (umlIdentifyChrPTY(NULL, driver, dom) < 0)
+ umlShutdownVMDaemon(NULL, driver, dom);
+ }
+ }
+}
+
+/**
+ * umlStartup:
+ *
+ * Initialization function for the Uml daemon
+ */
+static int
+umlStartup(void) {
+ uid_t uid = geteuid();
+ struct passwd *pw;
+ char *base = NULL;
+ char driverConf[PATH_MAX];
+
+ if (VIR_ALLOC(uml_driver) < 0)
+ return -1;
+
+ /* Don't have a dom0 so start from 1 */
+ uml_driver->nextvmid = 1;
+
+ if (!(pw = getpwuid(uid))) {
+ umlLog(UML_ERR, _("Failed to find user record for uid '%d': %s\n"),
+ uid, strerror(errno));
+ goto out_nouid;
+ }
+
+ if (!uid) {
+ if (asprintf(&uml_driver->logDir,
+ "%s/log/libvirt/uml", LOCAL_STATE_DIR) == -1)
+ goto out_of_memory;
+
+ if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL)
+ goto out_of_memory;
+ } else {
+ if (asprintf(&uml_driver->logDir,
+ "%s/.libvirt/uml/log", pw->pw_dir) == -1)
+ goto out_of_memory;
+
+ if (asprintf (&base, "%s/.libvirt", pw->pw_dir) == -1)
+ goto out_of_memory;
+ }
+
+ if (asprintf (&uml_driver->monitorDir,
+ "%s/.uml", pw->pw_dir) == -1)
+ goto out_of_memory;
+
+ /* Configuration paths are either ~/.libvirt/uml/... (session) or
+ * /etc/libvirt/uml/... (system).
+ */
+ if (snprintf (driverConf, sizeof(driverConf), "%s/uml.conf", base) == -1)
+ goto out_of_memory;
+ driverConf[sizeof(driverConf)-1] = '\0';
+
+ if (asprintf (&uml_driver->configDir, "%s/uml", base) == -1)
+ goto out_of_memory;
+
+ if (asprintf (&uml_driver->autostartDir, "%s/uml/autostart", base) == -1)
+ goto out_of_memory;
+
+ VIR_FREE(base);
+
+ if ((uml_driver->caps = umlCapsInit()) == NULL)
+ goto out_of_memory;
+
+
+ if ((uml_driver->inotifyFD = inotify_init()) < 0) {
+ umlLog(UML_ERR, "%s", _("cannot initialize inotify"));
+ goto out_nouid;
+ }
+
+ if (virFileMakePath(uml_driver->monitorDir) < 0) {
+ umlLog(UML_ERR, _("Failed to create monitor directory %s: %s"),
+ uml_driver->monitorDir, strerror(errno));
+ umlShutdown();
+ return -1;
+ }
+
+ if ((uml_driver->inotifyWatch =
+ inotify_add_watch(uml_driver->inotifyFD,
+ uml_driver->monitorDir,
+ IN_CREATE | IN_MODIFY | IN_DELETE)) < 0) {
+ umlShutdown();
+ return -1;
+ }
+
+ if (virEventAddHandle(uml_driver->inotifyFD, POLLIN,
+ umlInotifyEvent, uml_driver, NULL) < 0) {
+ umlShutdown();
+ return -1;
+ }
+
+ if (virDomainLoadAllConfigs(NULL,
+ uml_driver->caps,
+ &uml_driver->domains,
+ uml_driver->configDir,
+ uml_driver->autostartDir,
+ NULL, NULL) < 0) {
+ umlShutdown();
+ return -1;
+ }
+ umlAutostartConfigs(uml_driver);
+
+ return 0;
+
+ out_of_memory:
+ umlLog (UML_ERR,
+ "%s", _("umlStartup: out of memory\n"));
+ out_nouid:
+ VIR_FREE(base);
+ VIR_FREE(uml_driver);
+ return -1;
+}
+
+/**
+ * umlReload:
+ *
+ * Function to restart the Uml daemon, it will recheck the configuration
+ * files and update its state and the networking
+ */
+static int
+umlReload(void) {
+ if (!uml_driver)
+ return 0;
+
+ virDomainLoadAllConfigs(NULL,
+ uml_driver->caps,
+ &uml_driver->domains,
+ uml_driver->configDir,
+ uml_driver->autostartDir,
+ NULL, NULL);
+
+ umlAutostartConfigs(uml_driver);
+
+ return 0;
+}
+
+/**
+ * umlActive:
+ *
+ * Checks if the Uml daemon is active, i.e. has an active domain or
+ * an active network
+ *
+ * Returns 1 if active, 0 otherwise
+ */
+static int
+umlActive(void) {
+ unsigned int i;
+
+ if (!uml_driver)
+ return 0;
+
+ for (i = 0 ; i < uml_driver->domains.count ; i++)
+ if (virDomainIsActive(uml_driver->domains.objs[i]))
+ return 1;
+
+ /* Otherwise we're happy to deal with a shutdown */
+ return 0;
+}
+
+/**
+ * umlShutdown:
+ *
+ * Shutdown the Uml daemon, it will stop all active domains and networks
+ */
+static int
+umlShutdown(void) {
+ unsigned int i;
+
+ if (!uml_driver)
+ return -1;
+
+ virEventRemoveHandle(uml_driver->inotifyWatch);
+ close(uml_driver->inotifyFD);
+ virCapabilitiesFree(uml_driver->caps);
+
+ /* shutdown active VMs */
+ for (i = 0 ; i < uml_driver->domains.count ; i++) {
+ virDomainObjPtr dom = uml_driver->domains.objs[i];
+ if (virDomainIsActive(dom))
+ umlShutdownVMDaemon(NULL, uml_driver, dom);
+ if (!dom->persistent)
+ virDomainRemoveInactive(&uml_driver->domains,
+ dom);
+ }
+
+ virDomainObjListFree(&uml_driver->domains);
+
+ VIR_FREE(uml_driver->logDir);
+ VIR_FREE(uml_driver->configDir);
+ VIR_FREE(uml_driver->autostartDir);
+ VIR_FREE(uml_driver->monitorDir);
+
+ if (uml_driver->brctl)
+ brShutdown(uml_driver->brctl);
+
+ VIR_FREE(uml_driver);
+
+ return 0;
+}
+
+
+static int umlReadPidFile(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr vm)
+{
+ int rc = -1;
+ FILE *file;
+ char *pidfile = NULL;
+ int retries = 0;
+
+ vm->pid = -1;
+ if (asprintf(&pidfile, "%s/%s/pid",
+ driver->monitorDir, vm->def->name) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return -1;
+ }
+
+reopen:
+ if (!(file = fopen(pidfile, "r"))) {
+ if (errno == ENOENT &&
+ retries++ < 50) {
+ usleep(1000 * 100);
+ goto reopen;
+ }
+ goto cleanup;
+ }
+
+ if (fscanf(file, "%d", &vm->pid) != 1) {
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ if (fclose(file) < 0)
+ goto cleanup;
+
+ rc = 0;
+
+ cleanup:
+ if (rc != 0)
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("failed to read pid: %s: %s"),
+ pidfile, strerror(errno));
+ VIR_FREE(pidfile);
+ return rc;
+}
+
+static int umlMonitorAddress(virConnectPtr conn,
+ const struct uml_driver *driver,
+ virDomainObjPtr vm,
+ struct sockaddr_un *addr) {
+ char *sockname;
+
+ if (asprintf(&sockname, "%s/%s/mconsole",
+ driver->monitorDir, vm->def->name) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return -1;
+ }
+
+ memset(addr, 0, sizeof *addr);
+ addr->sun_family = AF_UNIX;
+ strncpy(addr->sun_path, sockname, sizeof(addr->sun_path)-1);
+ NUL_TERMINATE(addr->sun_path);
+ VIR_FREE(sockname);
+ return 0;
+}
+
+static int umlOpenMonitor(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr vm) {
+ struct sockaddr_un addr;
+ struct stat sb;
+ int retries = 0;
+
+ if (umlMonitorAddress(conn, driver, vm, &addr) < 0)
+ return -1;
+
+restat:
+ if (stat(addr.sun_path, &sb) < 0) {
+ if (errno == ENOENT &&
+ retries < 50) {
+ usleep(1000 * 100);
+ goto restat;
+ }
+ return -1;
+ }
+
+ if ((vm->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot open socket %s"), strerror(errno));
+ return -1;
+ }
+
+ memset(addr.sun_path, 0, sizeof addr.sun_path);
+ sprintf(addr.sun_path + 1, "%u", getpid());
+ if (bind(vm->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot bind socket %s"), strerror(errno));
+ close(vm->monitor);
+ vm->monitor = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#define MONITOR_MAGIC 0xcafebabe
+#define MONITOR_BUFLEN 512
+#define MONITOR_VERSION 2
+
+struct monitor_request {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t length;
+ char data[MONITOR_BUFLEN];
+};
+
+struct monitor_response {
+ uint32_t error;
+ uint32_t extra;
+ uint32_t length;
+ char data[MONITOR_BUFLEN];
+};
+
+
+static int umlMonitorCommand(virConnectPtr conn,
+ const struct uml_driver *driver,
+ const virDomainObjPtr vm,
+ const char *cmd,
+ char **reply)
+{
+ struct monitor_request req;
+ struct monitor_response res;
+ char *retdata = NULL;
+ int retlen = 0, ret = 0;
+ struct sockaddr_un addr;
+ unsigned int addrlen;
+
+ *reply = NULL;
+
+ if (umlMonitorAddress(conn, driver, vm, &addr) < 0)
+ return -1;
+
+ memset(&req, 0, sizeof(req));
+ req.magic = MONITOR_MAGIC;
+ req.version = MONITOR_VERSION;
+ req.length = strlen(cmd);
+ if (req.length > (MONITOR_BUFLEN-1)) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot send too long command %s: %s"),
+ cmd, strerror(EINVAL));
+ return -1;
+ }
+ strncpy(req.data, cmd, req.length);
+ req.data[req.length] = '\0';
+
+ if (sendto(vm->monitor, &req, sizeof req, 0,
+ (struct sockaddr *)&addr, sizeof addr) != (sizeof req)) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot send command %s: %s"),
+ cmd, strerror(errno));
+ return -1;
+ }
+
+ do {
+ addrlen = sizeof(addr);
+ if (recvfrom(vm->monitor, &res, sizeof res, 0,
+ (struct sockaddr *)&addr, &addrlen) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot read reply %s: %s"),
+ cmd, strerror(errno));
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(retdata, retlen + res.length) < 0) {
+ umlReportError(conn, NULL, NULL,
+ VIR_ERR_NO_MEMORY, NULL);
+ goto error;
+ }
+ memcpy(retdata + retlen, res.data, res.length);
+ retlen += res.length - 1;
+ retdata[retlen] = '\0';
+
+ if (res.error)
+ ret = -1;
+
+ } while (res.extra);
+
+ *reply = retdata;
+
+ return ret;
+
+error:
+ VIR_FREE(retdata);
+ return -1;
+}
+
+
+static int umlStartVMDaemon(virConnectPtr conn,
+ struct uml_driver *driver,
+ virDomainObjPtr vm) {
+ const char **argv = NULL, **tmp;
+ const char **progenv = NULL;
+ int i, ret, pid;
+ char *logfile;
+ int logfd = -1;
+ struct stat sb;
+ int *tapfds = NULL;
+ int ntapfds = 0;
+ fd_set keepfd;
+
+ FD_ZERO(&keepfd);
+
+ if (virDomainIsActive(vm)) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("VM is already active"));
+ return -1;
+ }
+
+ if (!vm->def->os.kernel) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("no kernel specified"));
+ return -1;
+ }
+ /* Make sure the binary we are about to try exec'ing exists.
+ * Technically we could catch the exec() failure, but that's
+ * in a sub-process so its hard to feed back a useful error
+ */
+ if (stat(vm->def->os.kernel, &sb) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Cannot find UML kernel %s: %s"),
+ vm->def->os.kernel, strerror(errno));
+ return -1;
+ }
+
+ if (virFileMakePath(driver->logDir) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot create log directory %s"),
+ driver->logDir);
+ return -1;
+ }
+
+ if (asprintf(&logfile, "%s/%s.log",
+ driver->logDir, vm->def->name) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return -1;
+ }
+
+ if ((logfd = open(logfile, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR)) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("failed to create logfile %s: %s"),
+ logfile, strerror(errno));
+ VIR_FREE(logfile);
+ return -1;
+ }
+ VIR_FREE(logfile);
+
+ if (umlSetCloseExec(logfd) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unable to set VM logfile close-on-exec flag %s"),
+ strerror(errno));
+ close(logfd);
+ return -1;
+ }
+
+ if (umlBuildCommandLine(conn, driver, vm,
+ &argv, &progenv,
+ &tapfds, &ntapfds) < 0) {
+ close(logfd);
+ return -1;
+ }
+
+ tmp = progenv;
+ while (*tmp) {
+ if (safewrite(logfd, *tmp, strlen(*tmp)) < 0)
+ umlLog(UML_WARN, _("Unable to write envv to logfile %d: %s\n"),
+ errno, strerror(errno));
+ if (safewrite(logfd, " ", 1) < 0)
+ umlLog(UML_WARN, _("Unable to write envv to logfile %d: %s\n"),
+ errno, strerror(errno));
+ tmp++;
+ }
+ tmp = argv;
+ while (*tmp) {
+ if (safewrite(logfd, *tmp, strlen(*tmp)) < 0)
+ umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"),
+ errno, strerror(errno));
+ if (safewrite(logfd, " ", 1) < 0)
+ umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"),
+ errno, strerror(errno));
+ tmp++;
+ }
+ if (safewrite(logfd, "\n", 1) < 0)
+ umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"),
+ errno, strerror(errno));
+
+ vm->monitor = -1;
+ vm->stdin_fd = -1;
+ vm->stdout_fd = vm->stderr_fd = logfd;
+
+ for (i = 0 ; i < ntapfds ; i++)
+ FD_SET(tapfds[i], &keepfd);
+
+ ret = virExec(conn, argv, progenv, &keepfd, &pid,
+ vm->stdin_fd, &vm->stdout_fd, &vm->stderr_fd,
+ VIR_EXEC_DAEMON);
+ close(logfd);
+
+ /* Cleanup intermediate proces */
+ if (waitpid(pid, NULL, 0) != pid)
+ umlLog(UML_WARN, _("failed to wait on process: %d: %s\n"),
+ pid, strerror(errno));
+
+ for (i = 0 ; argv[i] ; i++)
+ VIR_FREE(argv[i]);
+ VIR_FREE(argv);
+
+ for (i = 0 ; progenv[i] ; i++)
+ VIR_FREE(progenv[i]);
+ VIR_FREE(progenv);
+
+ if (tapfds) {
+ for (i = 0 ; i < ntapfds ; i++) {
+ close(tapfds[i]);
+ }
+ VIR_FREE(tapfds);
+ }
+
+ /* NB we don't mark it running here - we do that async
+ with inotify */
+
+ return ret;
+}
+
+static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
+ struct uml_driver *driver ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm)
+{
+ int ret;
+ if (!virDomainIsActive(vm) ||
+ vm->pid <= 1)
+ return;
+
+
+ kill(vm->pid, SIGTERM);
+
+ if (vm->monitor != -1)
+ close(vm->monitor);
+ vm->monitor = -1;
+
+ if ((ret = waitpid(vm->pid, NULL, 0)) != vm->pid) {
+ umlLog(UML_WARN,
+ _("Got unexpected pid %d != %d\n"),
+ ret, vm->pid);
+ }
+
+ vm->pid = -1;
+ vm->def->id = -1;
+ vm->state = VIR_DOMAIN_SHUTOFF;
+ VIR_FREE(vm->vcpupids);
+ vm->nvcpupids = 0;
+
+ if (vm->newDef) {
+ virDomainDefFree(vm->def);
+ vm->def = vm->newDef;
+ vm->def->id = -1;
+ vm->newDef = NULL;
+ }
+}
+
+
+static virDrvOpenStatus umlOpen(virConnectPtr conn,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED) {
+ uid_t uid = getuid();
+
+ if (uml_driver == NULL)
+ goto decline;
+
+ if (conn->uri != NULL) {
+ if (conn->uri->scheme == NULL || conn->uri->path == NULL)
+ goto decline;
+
+ if (STRNEQ (conn->uri->scheme, "uml"))
+ goto decline;
+
+ if (uid != 0) {
+ if (STRNEQ (conn->uri->path, "/session"))
+ goto decline;
+ } else { /* root */
+ if (STRNEQ (conn->uri->path, "/system") &&
+ STRNEQ (conn->uri->path, "/session"))
+ goto decline;
+ }
+ } else {
+ conn->uri = xmlParseURI(uid ? "uml:///session" : "uml:///system");
+ if (!conn->uri) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,NULL);
+ return VIR_DRV_OPEN_ERROR;
+ }
+ }
+
+ conn->privateData = uml_driver;
+
+ return VIR_DRV_OPEN_SUCCESS;
+
+ decline:
+ return VIR_DRV_OPEN_DECLINED;
+}
+
+static int umlClose(virConnectPtr conn) {
+ /*struct uml_driver *driver = (struct uml_driver *)conn->privateData;*/
+
+ conn->privateData = NULL;
+
+ return 0;
+}
+
+static const char *umlGetType(virConnectPtr conn ATTRIBUTE_UNUSED) {
+ return "UML";
+}
+
+static int umlGetNodeInfo(virConnectPtr conn,
+ virNodeInfoPtr nodeinfo) {
+ return virNodeInfoPopulate(conn, nodeinfo);
+}
+
+
+static char *umlGetCapabilities(virConnectPtr conn) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ char *xml;
+
+ if ((xml = virCapabilitiesFormatXML(driver->caps)) == NULL) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+ "%s", _("failed to allocate space for capabilities support"));
+ return NULL;
+ }
+
+ return xml;
+}
+
+
+#if HAVE_NUMACTL
+static int
+umlNodeGetCellsFreeMemory(virConnectPtr conn,
+ unsigned long long *freeMems,
+ int startCell,
+ int maxCells)
+{
+ int n, lastCell, numCells;
+
+ if (numa_available() < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT,
+ "%s", _("NUMA not supported on this host"));
+ return -1;
+ }
+ lastCell = startCell + maxCells - 1;
+ if (lastCell > numa_max_node())
+ lastCell = numa_max_node();
+
+ for (numCells = 0, n = startCell ; n <= lastCell ; n++) {
+ long long mem;
+ if (numa_node_size64(n, &mem) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Failed to query NUMA free memory"));
+ return -1;
+ }
+ freeMems[numCells++] = mem;
+ }
+ return numCells;
+}
+
+static unsigned long long
+umlNodeGetFreeMemory (virConnectPtr conn)
+{
+ unsigned long long freeMem = 0;
+ int n;
+ if (numa_available() < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT,
+ "%s", _("NUMA not supported on this host"));
+ return -1;
+ }
+
+ for (n = 0 ; n <= numa_max_node() ; n++) {
+ long long mem;
+ if (numa_node_size64(n, &mem) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Failed to query NUMA free memory"));
+ return -1;
+ }
+ freeMem += mem;
+ }
+
+ return freeMem;
+}
+
+#endif
+
+static int umlGetProcessInfo(unsigned long long *cpuTime, int pid) {
+ char proc[PATH_MAX];
+ FILE *pidinfo;
+ unsigned long long usertime, systime;
+
+ if (snprintf(proc, sizeof(proc), "/proc/%d/stat", pid) >= (int)sizeof(proc)) {
+ return -1;
+ }
+
+ if (!(pidinfo = fopen(proc, "r"))) {
+ /*printf("cannot read pid info");*/
+ /* VM probably shut down, so fake 0 */
+ *cpuTime = 0;
+ return 0;
+ }
+
+ if (fscanf(pidinfo, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu", &usertime, &systime) != 2) {
+ umlDebug("not enough arg");
+ return -1;
+ }
+
+ /* We got jiffies
+ * We want nanoseconds
+ * _SC_CLK_TCK is jiffies per second
+ * So calulate thus....
+ */
+ *cpuTime = 1000ull * 1000ull * 1000ull * (usertime + systime) / (unsigned long long)sysconf(_SC_CLK_TCK);
+
+ umlDebug("Got %llu %llu %llu", usertime, systime, *cpuTime);
+
+ fclose(pidinfo);
+
+ return 0;
+}
+
+
+static virDomainPtr umlDomainLookupByID(virConnectPtr conn,
+ int id) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ virDomainObjPtr vm = virDomainFindByID(&driver->domains, id);
+ virDomainPtr dom;
+
+ if (!vm) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) dom->id = vm->def->id;
+ return dom;
+}
+static virDomainPtr umlDomainLookupByUUID(virConnectPtr conn,
+ const unsigned char *uuid) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, uuid);
+ virDomainPtr dom;
+
+ if (!vm) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) dom->id = vm->def->id;
+ return dom;
+}
+static virDomainPtr umlDomainLookupByName(virConnectPtr conn,
+ const char *name) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ virDomainObjPtr vm = virDomainFindByName(&driver->domains, name);
+ virDomainPtr dom;
+
+ if (!vm) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) dom->id = vm->def->id;
+ return dom;
+}
+
+static int umlGetVersion(virConnectPtr conn, unsigned long *version) {
+ struct utsname ut;
+ int major, minor, micro;
+
+ uname(&ut);
+
+ if (sscanf(ut.release, "%u.%u.%u",
+ &major, &minor, &micro) != 3) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse version %s"), ut.release);
+ return -1;
+ }
+
+ *version = uml_driver->umlVersion;
+ return 0;
+}
+
+static char *
+umlGetHostname (virConnectPtr conn)
+{
+ int r;
+ char hostname[HOST_NAME_MAX+1], *str;
+
+ r = gethostname (hostname, HOST_NAME_MAX+1);
+ if (r == -1) {
+ umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
+ "%s", strerror (errno));
+ return NULL;
+ }
+ /* Caller frees this string. */
+ str = strdup (hostname);
+ if (str == NULL) {
+ umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
+ "%s", strerror (errno));
+ return NULL;
+ }
+ return str;
+}
+
+static int umlListDomains(virConnectPtr conn, int *ids, int nids) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ int got = 0, i;
+
+ for (i = 0 ; i < driver->domains.count && got < nids ; i++)
+ if (virDomainIsActive(driver->domains.objs[i]))
+ ids[got++] = driver->domains.objs[i]->def->id;
+
+ return got;
+}
+static int umlNumDomains(virConnectPtr conn) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ int n = 0, i;
+
+ for (i = 0 ; i < driver->domains.count ; i++)
+ if (virDomainIsActive(driver->domains.objs[i]))
+ n++;
+
+ return n;
+}
+static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml,
+ unsigned int flags ATTRIBUTE_UNUSED) {
+ virDomainDefPtr def;
+ virDomainObjPtr vm;
+ virDomainPtr dom;
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+
+ if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
+ return NULL;
+
+ vm = virDomainFindByName(&driver->domains, def->name);
+ if (vm) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+ _("domain '%s' is already defined"),
+ def->name);
+ virDomainDefFree(def);
+ return NULL;
+ }
+ vm = virDomainFindByUUID(&driver->domains, def->uuid);
+ if (vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ virUUIDFormat(def->uuid, uuidstr);
+ umlReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+ _("domain with uuid '%s' is already defined"),
+ uuidstr);
+ virDomainDefFree(def);
+ return NULL;
+ }
+
+ if (!(vm = virDomainAssignDef(conn,
+ &driver->domains,
+ def))) {
+ virDomainDefFree(def);
+ return NULL;
+ }
+
+ if (umlStartVMDaemon(conn, driver, vm) < 0) {
+ virDomainRemoveInactive(&driver->domains,
+ vm);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) dom->id = vm->def->id;
+ return dom;
+}
+
+
+static int umlDomainShutdown(virDomainPtr dom) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id);
+ char* info;
+
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("no domain with matching id %d"), dom->id);
+ return -1;
+ }
+
+#if 0
+ if (umlMonitorCommand(driver, vm, "system_powerdown", &info) < 0) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+ "%s", _("shutdown operation failed"));
+ return -1;
+ }
+#endif
+ VIR_FREE(info);
+ return 0;
+
+}
+
+
+static int umlDomainDestroy(virDomainPtr dom) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id);
+
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("no domain with matching id %d"), dom->id);
+ return -1;
+ }
+
+ umlShutdownVMDaemon(dom->conn, driver, vm);
+ if (!vm->persistent)
+ virDomainRemoveInactive(&driver->domains,
+ vm);
+
+ return 0;
+}
+
+
+static char *umlDomainGetOSType(virDomainPtr dom) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ char *type;
+
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ "%s", _("no domain with matching uuid"));
+ return NULL;
+ }
+
+ if (!(type = strdup(vm->def->os.type))) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_MEMORY,
+ "%s", _("failed to allocate space for ostype"));
+ return NULL;
+ }
+ return type;
+}
+
+/* Returns max memory in kb, 0 if error */
+static unsigned long umlDomainGetMaxMemory(virDomainPtr dom) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ virUUIDFormat(dom->uuid, uuidstr);
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ return 0;
+ }
+
+ return vm->def->maxmem;
+}
+
+static int umlDomainSetMaxMemory(virDomainPtr dom, unsigned long newmax) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ virUUIDFormat(dom->uuid, uuidstr);
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ return -1;
+ }
+
+ if (newmax < vm->def->memory) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+ "%s", _("cannot set max memory lower than current memory"));
+ return -1;
+ }
+
+ vm->def->maxmem = newmax;
+ return 0;
+}
+
+static int umlDomainSetMemory(virDomainPtr dom, unsigned long newmem) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ virUUIDFormat(dom->uuid, uuidstr);
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ return -1;
+ }
+
+ if (virDomainIsActive(vm)) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+ "%s", _("cannot set memory of an active domain"));
+ return -1;
+ }
+
+ if (newmem > vm->def->maxmem) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+ "%s", _("cannot set memory higher than max memory"));
+ return -1;
+ }
+
+ vm->def->memory = newmem;
+ return 0;
+}
+
+static int umlDomainGetInfo(virDomainPtr dom,
+ virDomainInfoPtr info) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ "%s", _("no domain with matching uuid"));
+ return -1;
+ }
+
+ info->state = vm->state;
+
+ if (!virDomainIsActive(vm)) {
+ info->cpuTime = 0;
+ } else {
+ if (umlGetProcessInfo(&(info->cpuTime), vm->pid) < 0) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, ("cannot read cputime for domain"));
+ return -1;
+ }
+ }
+
+ info->maxMem = vm->def->maxmem;
+ info->memory = vm->def->memory;
+ info->nrVirtCpu = vm->def->vcpus;
+ return 0;
+}
+
+
+static char *umlDomainDumpXML(virDomainPtr dom,
+ int flags ATTRIBUTE_UNUSED) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ "%s", _("no domain with matching uuid"));
+ return NULL;
+ }
+
+ return virDomainDefFormat(dom->conn,
+ (flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef ?
+ vm->newDef : vm->def,
+ flags);
+}
+
+
+static int umlListDefinedDomains(virConnectPtr conn,
+ char **const names, int nnames) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ int got = 0, i;
+
+ for (i = 0 ; i < driver->domains.count && got < nnames ; i++) {
+ if (!virDomainIsActive(driver->domains.objs[i])) {
+ if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+ "%s", _("failed to allocate space for VM name string"));
+ goto cleanup;
+ }
+ }
+ }
+
+ return got;
+
+ cleanup:
+ for (i = 0 ; i < got ; i++)
+ VIR_FREE(names[i]);
+ return -1;
+}
+
+static int umlNumDefinedDomains(virConnectPtr conn) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ int n = 0, i;
+
+ for (i = 0 ; i < driver->domains.count ; i++)
+ if (!virDomainIsActive(driver->domains.objs[i]))
+ n++;
+
+ return n;
+}
+
+
+static int umlDomainStart(virDomainPtr dom) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ "%s", _("no domain with matching uuid"));
+ return -1;
+ }
+
+ return umlStartVMDaemon(dom->conn, driver, vm);
+}
+
+
+static virDomainPtr umlDomainDefine(virConnectPtr conn, const char *xml) {
+ struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+ virDomainDefPtr def;
+ virDomainObjPtr vm;
+ virDomainPtr dom;
+
+ if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
+ return NULL;
+
+ if (!(vm = virDomainAssignDef(conn,
+ &driver->domains,
+ def))) {
+ virDomainDefFree(def);
+ return NULL;
+ }
+ vm->persistent = 1;
+
+ if (virDomainSaveConfig(conn,
+ driver->configDir,
+ vm->newDef ? vm->newDef : vm->def) < 0) {
+ virDomainRemoveInactive(&driver->domains,
+ vm);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) dom->id = vm->def->id;
+ return dom;
+}
+
+static int umlDomainUndefine(virDomainPtr dom) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ "%s", _("no domain with matching uuid"));
+ return -1;
+ }
+
+ if (virDomainIsActive(vm)) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("cannot delete active domain"));
+ return -1;
+ }
+
+ if (!vm->persistent) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("cannot undefine transient domain"));
+ return -1;
+ }
+
+ if (virDomainDeleteConfig(dom->conn, driver->configDir, driver->autostartDir, vm) < 0)
+ return -1;
+
+ virDomainRemoveInactive(&driver->domains,
+ vm);
+
+ return 0;
+}
+
+
+
+static int umlDomainGetAutostart(virDomainPtr dom,
+ int *autostart) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ "%s", _("no domain with matching uuid"));
+ return -1;
+ }
+
+ *autostart = vm->autostart;
+
+ return 0;
+}
+
+static int umlDomainSetAutostart(virDomainPtr dom,
+ int autostart) {
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ char *configFile = NULL, *autostartLink = NULL;
+ int ret = -1;
+
+ if (!vm) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ "%s", _("no domain with matching uuid"));
+ return -1;
+ }
+
+ if (!vm->persistent) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("cannot set autostart for transient domain"));
+ return -1;
+ }
+
+ autostart = (autostart != 0);
+
+ if (vm->autostart == autostart)
+ return 0;
+
+ if ((configFile = virDomainConfigFile(dom->conn, driver->configDir, vm->def->name)) == NULL)
+ goto cleanup;
+ if ((autostartLink = virDomainConfigFile(dom->conn, driver->autostartDir, vm->def->name)) == NULL)
+ goto cleanup;
+
+ if (autostart) {
+ int err;
+
+ if ((err = virFileMakePath(driver->autostartDir))) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot create autostart directory %s: %s"),
+ driver->autostartDir, strerror(err));
+ goto cleanup;
+ }
+
+ if (symlink(configFile, autostartLink) < 0) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to create symlink '%s to '%s': %s"),
+ autostartLink, configFile, strerror(errno));
+ goto cleanup;
+ }
+ } else {
+ if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to delete symlink '%s': %s"),
+ autostartLink, strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ vm->autostart = autostart;
+ ret = 0;
+
+cleanup:
+ VIR_FREE(configFile);
+ VIR_FREE(autostartLink);
+
+ return ret;
+}
+
+
+static int
+umlDomainBlockPeek (virDomainPtr dom,
+ const char *path,
+ unsigned long long offset, size_t size,
+ void *buffer,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+ virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ int fd, ret = -1, i;
+
+ if (!vm) {
+ umlReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("no domain with matching uuid"));
+ return -1;
+ }
+
+ if (!path || path[0] == '\0') {
+ umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+ _("NULL or empty path"));
+ return -1;
+ }
+
+ /* Check the path belongs to this domain. */
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ if (vm->def->disks[i]->src != NULL &&
+ STREQ (vm->def->disks[i]->src, path))
+ goto found;
+ }
+ umlReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+ _("invalid path"));
+ return -1;
+
+found:
+ /* The path is correct, now try to open it and get its size. */
+ fd = open (path, O_RDONLY);
+ if (fd == -1) {
+ umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
+ "%s", strerror (errno));
+ goto done;
+ }
+
+ /* Seek and read. */
+ /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should
+ * be 64 bits on all platforms.
+ */
+ if (lseek (fd, offset, SEEK_SET) == (off_t) -1 ||
+ saferead (fd, buffer, size) == (ssize_t) -1) {
+ umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
+ "%s", strerror (errno));
+ goto done;
+ }
+
+ ret = 0;
+ done:
+ if (fd >= 0) close (fd);
+ return ret;
+}
+
+
+
+static virDriver umlDriver = {
+ VIR_DRV_UML,
+ "UML",
+ LIBVIR_VERSION_NUMBER,
+ umlOpen, /* open */
+ umlClose, /* close */
+ NULL, /* supports_feature */
+ umlGetType, /* type */
+ umlGetVersion, /* version */
+ umlGetHostname, /* hostname */
+ NULL, /* URI */
+ NULL, /* getMaxVcpus */
+ umlGetNodeInfo, /* nodeGetInfo */
+ umlGetCapabilities, /* getCapabilities */
+ umlListDomains, /* listDomains */
+ umlNumDomains, /* numOfDomains */
+ umlDomainCreate, /* domainCreateXML */
+ umlDomainLookupByID, /* domainLookupByID */
+ umlDomainLookupByUUID, /* domainLookupByUUID */
+ umlDomainLookupByName, /* domainLookupByName */
+ NULL, /* domainSuspend */
+ NULL, /* domainResume */
+ umlDomainShutdown, /* domainShutdown */
+ NULL, /* domainReboot */
+ umlDomainDestroy, /* domainDestroy */
+ umlDomainGetOSType, /* domainGetOSType */
+ umlDomainGetMaxMemory, /* domainGetMaxMemory */
+ umlDomainSetMaxMemory, /* domainSetMaxMemory */
+ umlDomainSetMemory, /* domainSetMemory */
+ umlDomainGetInfo, /* domainGetInfo */
+ NULL, /* domainSave */
+ NULL, /* domainRestore */
+ NULL, /* domainCoreDump */
+ NULL, /* domainSetVcpus */
+ NULL, /* domainPinVcpu */
+ NULL, /* domainGetVcpus */
+ NULL, /* domainGetMaxVcpus */
+ umlDomainDumpXML, /* domainDumpXML */
+ umlListDefinedDomains, /* listDomains */
+ umlNumDefinedDomains, /* numOfDomains */
+ umlDomainStart, /* domainCreate */
+ umlDomainDefine, /* domainDefineXML */
+ umlDomainUndefine, /* domainUndefine */
+ NULL, /* domainAttachDevice */
+ NULL, /* domainDetachDevice */
+ umlDomainGetAutostart, /* domainGetAutostart */
+ umlDomainSetAutostart, /* domainSetAutostart */
+ NULL, /* domainGetSchedulerType */
+ NULL, /* domainGetSchedulerParameters */
+ NULL, /* domainSetSchedulerParameters */
+ NULL, /* domainMigratePrepare */
+ NULL, /* domainMigratePerform */
+ NULL, /* domainMigrateFinish */
+ NULL, /* domainBlockStats */
+ NULL, /* domainInterfaceStats */
+ umlDomainBlockPeek, /* domainBlockPeek */
+ NULL, /* domainMemoryPeek */
+#if HAVE_NUMACTL
+ umlNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */
+ umlNodeGetFreeMemory, /* getFreeMemory */
+#else
+ NULL, /* nodeGetCellsFreeMemory */
+ NULL, /* getFreeMemory */
+#endif
+ NULL, /* domainEventRegister */
+ NULL, /* domainEventUnregister */
+ NULL, /* domainMigratePrepare2 */
+ NULL, /* domainMigrateFinish2 */
+};
+
+
+static virStateDriver umlStateDriver = {
+ .initialize = umlStartup,
+ .cleanup = umlShutdown,
+ .reload = umlReload,
+ .active = umlActive,
+};
+
+int umlRegister(void) {
+ virRegisterDriver(&umlDriver);
+ virRegisterStateDriver(&umlStateDriver);
+ return 0;
+}
+
diff --git a/src/uml_driver.h b/src/uml_driver.h
new file mode 100644
index 000000000..2195410d6
--- /dev/null
+++ b/src/uml_driver.h
@@ -0,0 +1,32 @@
+/*
+ * uml_driver.h: user mode Linux driver
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#ifndef UML_DRIVER_H
+#define UML_DRIVER_H
+
+#include "internal.h"
+
+int umlRegister(void);
+
+#endif /* UML_DRIVER_H */
diff --git a/src/virterror.c b/src/virterror.c
index 15eb0a191..46a7d1542 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -310,7 +310,9 @@ virDefaultErrorFunc(virErrorPtr err)
case VIR_FROM_DOMAIN:
dom = "Domain Config ";
break;
-
+ case VIR_FROM_UML:
+ dom = "UML ";
+ break;
}
if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
domain = err->dom->name;