From 714bef5b6a1278972aaa4b733ec27b3cf8edbce0 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 19 Nov 2008 16:58:23 +0000 Subject: [PATCH] Add an initial User Mode Linux Driver --- ChangeLog | 17 + configure.in | 8 + docs/drvuml.html.in | 79 ++ docs/sitemap.html.in | 4 + include/libvirt/virterror.h | 1 + qemud/Makefile.am | 4 + qemud/qemud.c | 6 + src/Makefile.am | 11 + src/domain_conf.c | 47 +- src/domain_conf.h | 1 + src/driver.h | 3 +- src/qemu_conf.c | 3 +- src/uml_conf.c | 404 +++++++++ src/uml_conf.h | 74 ++ src/uml_driver.c | 1676 +++++++++++++++++++++++++++++++++++ src/uml_driver.h | 32 + src/virterror.c | 4 +- 17 files changed, 2350 insertions(+), 24 deletions(-) create mode 100644 docs/drvuml.html.in create mode 100644 src/uml_conf.c create mode 100644 src/uml_conf.h create mode 100644 src/uml_driver.c create mode 100644 src/uml_driver.h diff --git a/ChangeLog b/ChangeLog index 364fa0febb..71a511ec7a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +Wed Nov 19 16:51:00 GMT 2008 Daniel Berrange + + Add User Mode Linux driver + * src/uml_driver.c, src/uml_driver.h, src/uml_conf.c, + src/uml_conf.h: Core UML driver code + * src/domain_conf.c, src/domain_conf.h: Add UML disk + bus type. Fix paravirt console handling for non-HVM domains + * src/driver.h: Add UML driver number + * src/virterror.h: Add error code for UML driver + * src/qemu_conf.c: Add disk bus enum entry for UML + * src/Makefile.am: Build UML driver + * qemud/qemud.c: Initialize UML driver + * qemud/Makefile.am: Link to UML driver library + * docs/drvuml.html.in: Basic docs for the UML driver + * configure.in: Flag for enabling UML driver + * include/libvirt/virterror.h: Add constant for UML driver + Wed Nov 19 16:22:00 GMT 2008 Daniel Berrange Add virFreeCallback to event loop APIs. diff --git a/configure.in b/configure.in index 085cae6952..21931a131d 100644 --- a/configure.in +++ b/configure.in @@ -149,6 +149,8 @@ AC_ARG_WITH([xen], [ --with-xen add XEN support (on)],[],[with_xen=yes]) AC_ARG_WITH([qemu], [ --with-qemu add QEMU/KVM support (on)],[],[with_qemu=yes]) +AC_ARG_WITH([uml], +[ --with-uml add UML support (on)],[],[with_uml=yes]) AC_ARG_WITH([openvz], [ --with-openvz add OpenVZ support (on)],[],[with_openvz=yes]) AC_ARG_WITH([lxc], @@ -253,6 +255,11 @@ if test "$with_qemu" = "yes" ; then fi AM_CONDITIONAL([WITH_QEMU], [test "$with_qemu" = "yes"]) +if test "$with_uml" = "yes" ; then + AC_DEFINE_UNQUOTED([WITH_UML], 1, [whether UML driver is enabled]) +fi +AM_CONDITIONAL([WITH_UML], [test "$with_uml" = "yes"]) + if test "$with_test" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_TEST], 1, [whether Test driver is enabled]) fi @@ -1098,6 +1105,7 @@ AC_MSG_NOTICE([]) AC_MSG_NOTICE([ Xen: $with_xen]) AC_MSG_NOTICE([ Proxy: $with_xen_proxy]) AC_MSG_NOTICE([ QEMU: $with_qemu]) +AC_MSG_NOTICE([ UML: $with_uml]) AC_MSG_NOTICE([ OpenVZ: $with_openvz]) AC_MSG_NOTICE([ LXC: $with_lxc]) AC_MSG_NOTICE([ Test: $with_test]) diff --git a/docs/drvuml.html.in b/docs/drvuml.html.in new file mode 100644 index 0000000000..8f4429a49f --- /dev/null +++ b/docs/drvuml.html.in @@ -0,0 +1,79 @@ + + +

User Mode Linux driver

+ +

+ The UML driver for libvirt allows use and management of paravirtualized + guests built for User Mode Linux. UML requires no special support in + the host kernel, so can be used by any user of any linux system, provided + they have enough free RAM for their guest's needs, though there are + certain restrictions on network connectivity unless the adminstrator + has pre-created TAP devices. +

+ +

Connections to UML driver

+ +

+ The libvirt UML driver follows the QEMU driver in providing two + types of connection. There is one privileged instance per host, + which runs as root. This is called the "system" instance, and allows + full use of all host resources. Then, there is a per-user unprivileged + "session", instance. This has more restricted capabilities, and may + require the host administrator to setup certain resources ahead of + time to allow full integration with the network. Example connection + URIs are +

+ +
+    uml:///system                        (local access, system instance)
+    uml:///session                       (local access, session instance)
+    uml://example.com/system             (remote access, TLS/x509)
+    uml+tcp://example.com/system         (remote access, SASl/Kerberos)
+    uml+ssh://root@example.com/system    (remote access, SSH tunnelled)
+    
+ +

Example XML configuration

+ +

+ User mode Linux driver only supports directly kernel boot at + this time. A future driver enhancement may allow a paravirt + bootloader in a similar style to Xen's pygrub. For now though, + the UML kernel must be stored on the host and referenced + explicitly in the "os" element. Since UML is a paravirtualized + technology, the kernel "type" is set to "uml" +

+ +

+ There is not yet support for networking in the driver, but + disks can be specified in the usual libvirt manner. The main + variation is the target device naming scheme "ubd0", and + bus type of "uml". +

+ +

+ Once booted the primary console is connected toa PTY, and + thus accessible with "virsh console" or equivalent tools +

+ +
+<domain type='uml'>
+  <name>demo</name>
+  <uuid>b4433fc2-a22e-ffb3-0a3d-9c173b395800</uuid>
+  <memory>500000</memory>
+  <currentMemory>500000</currentMemory>
+  <vcpu>1</vcpu>
+  <os>
+    <type arch='x86_64'>uml</type>
+    <kernel>/home/berrange/linux-uml-2.6.26-x86_64</kernel>
+  </os>
+  <devices>
+    <disk type='file' device='disk'>
+      <source file='/home/berrange/FedoraCore6-AMD64-root_fs'/>
+      <target dev='ubd0' bus='uml'/>
+    </disk>
+    <console type='pty'/>
+  </devices>
+</domain>
+    
+ + diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in index 2b7ed164c8..0e801666bf 100644 --- a/docs/sitemap.html.in +++ b/docs/sitemap.html.in @@ -124,6 +124,10 @@ OpenVZ Driver for the OpenVZ container technology +
  • + UML + Driver for the User Mode Linux technology +
  • Storage Driver for the storage management APIs diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 8e24708e96..868f1a3b32 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -58,6 +58,7 @@ typedef enum { VIR_FROM_STORAGE, /* Error from storage driver */ VIR_FROM_NETWORK, /* Error from network config */ VIR_FROM_DOMAIN, /* Error from domain config */ + VIR_FROM_UML, /* Error at the UML driver */ } virErrorDomain; diff --git a/qemud/Makefile.am b/qemud/Makefile.am index 8d2dc5a582..b271796221 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -99,6 +99,10 @@ if WITH_LXC libvirtd_LDADD += ../src/libvirt_driver_lxc.la endif +if WITH_UML +libvirtd_LDADD += ../src/libvirt_driver_uml.la +endif + if WITH_STORAGE_DIR libvirtd_LDADD += ../src/libvirt_driver_storage.la endif diff --git a/qemud/qemud.c b/qemud/qemud.c index fd4f3c0625..bb5cfcdcc1 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -67,6 +67,9 @@ #ifdef WITH_LXC #include "lxc_driver.h" #endif +#ifdef WITH_UML +#include "uml_driver.h" +#endif #ifdef WITH_NETWORK #include "network_driver.h" #endif @@ -751,6 +754,9 @@ static struct qemud_server *qemudInitialize(int sigread) { #ifdef WITH_LXC lxcRegister(); #endif +#ifdef WITH_UML + umlRegister(); +#endif #ifdef WITH_NETWORK networkRegister(); #endif diff --git a/src/Makefile.am b/src/Makefile.am index 3e6e726394..b2c7e3f6ef 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 7a5e5019ec..39e60d4597 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 88fd6d5da9..084c44852d 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 8962affb88..c79df95848 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 0e3b959884..c66cef9e39 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 0000000000..674830d28a --- /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 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_NUMACTL +#include +#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 0000000000..1213df17ce --- /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 + */ + +#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 0000000000..ac0c32c813 --- /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 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_NUMACTL +#include +#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(¨_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(¨_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 (¨_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 (¨_driver->configDir, "%s/uml", base) == -1) + goto out_of_memory; + + if (asprintf (¨_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, + ¨_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, + ¨_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(¨_driver->domains, + dom); + } + + virDomainObjListFree(¨_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, µ) != 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(¨Driver); + virRegisterStateDriver(¨StateDriver); + return 0; +} + diff --git a/src/uml_driver.h b/src/uml_driver.h new file mode 100644 index 0000000000..2195410d61 --- /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 + */ + + +#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 15eb0a1911..46a7d15426 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; -- 2.39.5