This patch is an update of http://www.redhat.com/archives/libvir-list/2008-October/msg00355.html providing a user mode linux driver. I've fixed a number of stupid bugs since the first posting, so its more reliable & less crash prone on startup. I also added a short driver doc page for the website. Even though this driver isn't finished (it still lacks networking), I'd like to get this info 0.5.0 release so it can at least be tried out by interested people. It shouldn't impact any users of existing drivers. configure.in | 8 docs/drvuml.html.in | 79 ++ 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 | 71 + src/uml_driver.c | 1671 ++++++++++++++++++++++++++++++++++++++++++++ src/uml_driver.h | 32 src/virterror.c | 4 15 files changed, 2321 insertions(+), 24 deletions(-) Daniel diff --git a/configure.in b/configure.in --- 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], @@ -252,6 +254,11 @@ if test "$with_qemu" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_QEMU], 1, [whether QEMU driver is enabled]) 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]) @@ -1098,6 +1105,7 @@ AC_MSG_NOTICE([ Xen: $with_xen]) 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 --- /dev/null +++ b/docs/drvuml.html.in @@ -0,0 +1,79 @@ +<html> + <body> + <h1>User Mode Linux driver</h1> + + <p> + 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. + </p> + + <h2>Connections to UML driver</h2> + + <p> + 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 + </p> + + <pre> + 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@xxxxxxxxxxx/system (remote access, SSH tunnelled) + </pre> + + <h2>Example XML configuration</h2> + + <p> + 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" + </p> + + <p> + 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". + </p> + + <p> + Once booted the primary console is connected toa PTY, and + thus accessible with "virsh console" or equivalent tools + </p> + + <pre> +<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> + </pre> + </body> +</html> diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h --- 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 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -97,6 +97,10 @@ endif 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 diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -66,6 +66,9 @@ #endif #ifdef WITH_LXC #include "lxc_driver.h" +#endif +#ifdef WITH_UML +#include "uml_driver.h" #endif #ifdef WITH_NETWORK #include "network_driver.h" @@ -749,6 +752,9 @@ static struct qemud_server *qemudInitial #endif #ifdef WITH_LXC lxcRegister(); +#endif +#ifdef WITH_UML + umlRegister(); #endif #ifdef WITH_NETWORK networkRegister(); diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -116,6 +116,10 @@ QEMU_DRIVER_SOURCES = \ 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 @@ libvirt_driver_lxc_la_SOURCES = $(LXC_DR 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 --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -86,7 +86,8 @@ VIR_ENUM_IMPL(virDomainDiskBus, VIR_DOMA "scsi", "virtio", "xen", - "usb") + "usb", + "uml") VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST, "mount", @@ -610,7 +611,8 @@ virDomainDiskDefParseXML(virConnectPtr c !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 c 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 virDomainDefParse } 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 virDomainDefParse } 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 --- 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 --- 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 --- a/src/qemu_conf.c +++ b/src/qemu_conf.c @@ -56,7 +56,8 @@ VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_ "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 --- /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@xxxxxxxxxx> + */ + +#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 < 16 ; 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 < 16 ; 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 --- /dev/null +++ b/src/uml_conf.h @@ -0,0 +1,71 @@ +/* + * 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@xxxxxxxxxx> + */ + +#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 + +/* 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; + + 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 --- /dev/null +++ b/src/uml_driver.c @@ -0,0 +1,1671 @@ +/* + * driver.c: core driver methods for managing qemu 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@xxxxxxxxxx> + */ + +#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 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; + +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 (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) < 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->inotifyFD); + 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 --- /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@xxxxxxxxxx> + */ + + +#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 --- 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; -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list