[libvirt] libvirt-qpid
by Ian Main
Howdy folks!
So I've been working on a qpid interface to libvirt. Here's the description
I've come up with:
libvirt-qpid provides an interface with libvirt using QMF (qpid modeling
framework) which utilizes the AMQP protocol. The Advanced Message Queuing
Protocol (AMQP) is an open standard application layer protocol providing
reliable transport of messages.
QMF provides a modeling framework layer on top of qpid. This interface
allows you to manage hosts, domains, pools etc. as a set of objects with
properties and methods.
The source code repo:
http://git.et.redhat.com/?p=libvirt-qpid.git;a=summary
So I'd like to hear any feedback you guys might have. This file in particular
describes the API that is used to interact with libvirt.
http://git.et.redhat.com/?p=libvirt-qpid.git;a=blob_plain;f=src/libvirt-s...
My TODO list looks like this:
- Not sure on camel case or underscores. Original libvirt API uses
camel case, I went with underscores as I was originally basing it off
the ruby bindings.
- Need rc scripts, daemonization, better reconnect/error handling for
both qpid and libvirt. I think it should attempt to reconnect until
successful whenever it gets disconnected. We may need an extra status
property for this state as well.
- Return libvirt error codes? (it's just text msgs right now with error
status bit).
- Make it less chatty, but log stuff
If you wish to try it out, I've made rpms for fedora 9 x86_64 and i386.
They are in the ovirt repo, which you can add to yum using:
rpm -ivh http://ovirt.org/repos/ovirt/9/ovirt-release-LATEST.fc9.noarch.rpm
Once you have that set up, 'yum install libvirt-qpid python-qpid', and then
run (each in their own terminals):
qpidd --auth no
libvirt-qpid (as root to auth with libvirt)
qpid-tool
qpid-tool provides an interface to qpid and allows you to view/manipulate the
qpid models/objects. Here is an example of how it looks using qpid-tool:
$ qpid-tool
Management Tool for QPID
qpid: list
Management Object Types:
ObjectType Active Deleted
============================================
com.redhat.libvirt:domain 2 0
com.redhat.libvirt:node 1 0
qpid: list node
Objects of type com.redhat.libvirt:node
ID Created Destroyed Index
=================================
101 00:02:00 -
qpid: show 101
Object of type com.redhat.libvirt:node: (last sample time: 17:47:54)
Type Element 101
==============================================
property hostname vpro.mains.net
property uri qemu:///system
property libvirt_version 0.4.5
property api_version 0.4.5
property hypervisor_version 0.9.1
property hypervisor_type QEMU
qpid: list domain
Objects of type com.redhat.libvirt:domain
ID Created Destroyed Index
=================================
102 00:02:05 - 101
103 00:02:05 - 101
qpid: show 102
Object of type com.redhat.libvirt:domain: (last sample time: 17:48:09)
Type Element 102
=================================================================
property uuid 662958f8-12c5-baad-2252-4d2126e0971f
property name ovirt-appliance
property node 101
property state shutoff
property num_vcpus 1
property active false
statistic maximum_memory 786432
statistic memory 786432
statistic cpu_time 0
qpid: call 102 create
Call Result: create 0 (OK) {}
qpid: show 102
Object of type com.redhat.libvirt:domain: (last sample time: 17:48:24)
Type Element 102
=================================================================
property uuid 662958f8-12c5-baad-2252-4d2126e0971f
property name ovirt-appliance
property node 101
property state running
property num_vcpus 1
property active true
statistic maximum_memory 786432
statistic memory 786432
statistic cpu_time 0
qpid:
16 years, 2 months
[libvirt] Suggestion on commit rules when fixing breakages
by Daniel Veillard
As the number of compilation options and platform grows, it gets
more difficult for a commiter to always ensure one chunk of code
won't give a problem in a different situation. To try to lower the
cost of maintaining the protability I would suggest the following
rule for commit:
- if a recently commited patch breaks compilation on a platform
or for a given driver then it's fine to commit a minimal fix
directly without getting the review feedback first
- similary if make check or make syntax-chek breaks, if there is
an obvious fix, it's fine to commit immediately
Note that this would remove the need to send the patch to the list
anyway (or tell what the fix was if trivial). This doesn't either
remove the rule that 'make check syntax-check' should pass before
commiting anything, and obviously the existing review process is still
needed t for anything which is not a trivial fix breaking make or
checks.
I guess it makes sense to minimize disruption for those working on
head and lower the time needed to get those fix in for those who catch
and fix them ;-)
Opinions ?
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
16 years, 2 months
[libvirt] [PATCH] Thu Oct 16 17:07:10 +0200 Jim Meyering <meyering@redhat.com>
by Jim Meyering
The output of a failed po-check rule (part of "make syntax-check)
was unnecessarily cryptic. Here's the fix that I've already applied
in upstream coreutils:
build: when po-check fails, say why and suggest a fix
* Makefile.maint (po-check): Before, when this check failed, it just
spat out a diff mentioning two temporary files. Now, it tells you
what's wrong and suggests a fix with a patch using the name of the
affected file (rather than temporary file names) in the diff output.
diff --git a/Makefile.maint b/Makefile.maint
index 03800f8..4920112 100644
--- a/Makefile.maint
+++ b/Makefile.maint
@@ -466,8 +466,12 @@ m4-check:
&& { echo 'Makefile.maint: quote the first arg to AC_DEFUN' 1>&2; \
exit 1; } || :
+fix_po_file_diag = \
+'you have changed the set of files with translatable diagnostics;\n\
+apply the above patch\n'
+
# Verify that all source files using _() are listed in po/POTFILES.in.
-# FIXME: don't hard-code file names below; use a more general mechanism.
+po_file = po/POTFILES.in
po-check:
@if test -f po/POTFILES.in; then \
grep -E -v '^(#|$$)' po/POTFILES.in \
@@ -482,13 +486,14 @@ po-check:
*.[ch]) \
base=`expr " $$file" : ' \(.*\)\..'`; \
{ test -f $$base.l || test -f $$base.y; } && continue;; \
- *) continue;; \
+ *) continue;; \
esac; \
files="$$files $$file"; \
done; \
grep -E -l '\b(N?_|gettext *)\([^)"]*("|$$)' $$files \
| sort -u > $@-2; \
- diff -u $@-1 $@-2 || exit 1; \
+ diff -u -L $(po_file) -L $(po_file) $@-1 $@-2 \
+ || { printf '$(ME): '$(fix_po_file_diag) 1>&2; exit 1; }; \
rm -f $@-1 $@-2; \
fi
--
1.6.0.2.98.gc82e
16 years, 2 months
[libvirt] [PATCH]: Export the 'label' on block devices
by Chris Lalancette
To support LVM partitioning in oVirt, one of the things we need is the ability
to tell what kind of label is currently on a block device. Here, a 'label' is
used in the same sense that it is used in parted; namely, it defines which kind
of partition table is on the disk, whether it be DOS, LVM2, SUN, BSD, etc. Note
that this is different than the partition type; those are things like Linux,
FAT16, FAT32, etc.
This actually turns out to be fairly easy to implement; there are really only a
few labels that are in common use, and they all have an easy signature to
recognize (but see comments in the code about pc98). This patch implements
label detection on block devices in virStorageBackendUpdateVolInfoFD, and hooks
it up to the iSCSI backend so it works there.
To keep code duplication down, I moved some of the enum's from
storage_backend_disk.c into a common place. Note, however, that there is a
slight semantic change because of this. Previously, if no label was found on a
disk in storage_backend_disk.c, it would always return "dos" as the label type.
That's not actually true, though; if it's a completely zeroed disk, for
instance, it really just has label type of 'unknown'. This patch changes to the
new semantic of 'unknown' for label types we don't understand. I don't think
this will be a huge issue for compatibility, but there could be something I'm
missing.
Otherwise, this patch has been tested by me to work, and now when you do:
# virsh vol-dumpxml --pool iscsitest lun-1
you'll get:
<volume>
...
<target>
...
<format type='dos' />
Which should be sufficient for oVirt to do it's detection.
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
16 years, 2 months
[libvirt] PATCH: Experimental User Mode Linux driver for libvirt
by Daniel P. Berrange
This patch implements a driver supporting User Mode Linux guests. User mode
linux is a kind of paravirtualized kernel which runs on a plain Linux host.
It requires no elevated privileges at all, except for some of the network
integration. It is a pretty straightforward thing to invoke, so I figured
it would be easy to write a driver to support it. I was right :-)
As with the QEMU driver, there are two styles of invocation
uml:///system - a single privileged, per host driver instance as root
uml:///session - a per-user unprivileged instance
The user mode linux docs recommend against running as root, so naturally
I recommend use of uml:///session, though my testing is so far with
uml:///system.
It supports the bare minimum neccessary for a libvirt driver, namely start,
stop, define, create, destroy, undefine against domains. For configuration
it supports setting of memory limit, virtual disk devices, and paravirt
console devices - good enough to get 'virsh console' working over a PTY,
and dump XML config.
Like QEMU, UML has an admin monitor console. It is a little different
though, using a UNIX domain socket, in datagram mode, sending command
strings back & forth. This actaully makes it a little easier to deal
with in libvirt. User mode linux has a uml_mconsole command line tool
to interact with it, but I directly implement the socket support in
libvirt instead, so this driver does not (yet) have any dependancy on
the UML utilities.
The driver is largely a clone of the QEMU driver, replacing the bit which
builds the command line argv, and all code dealing with the QEMU monitor.
Since the monitor socket is datagram based, we can't make use of it for
detecting VM shutdown as we do with QEMU/LXC. So instead, I make use of
inotify, to monitor $HOME/.uml which is populated witha directory for
each VM. When we see a directory created, libvirt marks the corresponding
VM as running, finds it PID & probes the monitor for the PTY config. When
we see a directory deleted, libvirt makes the VM as shutoff and frees any
resources its holding. This actually works very nicely & simply.
An example config I'm using for testing is
<domain type='uml'>
<name>demo</name>
<uuid>b4433fc2-a22e-ffb3-0a3d-9c173b395800</uuid>
<memory>500000</memory>
<os>
<type arch='x86_64'>uml</type>
<kernel>/home/berrange/linux</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'>
<target port='0'/>
</console>
<serial type='tcp'>
<source mode='bind' host='localhost' service='9000'/>
<target port='0'/>
</serial>
<serial type='pty'>
<target port='3'/>
</serial>
</devices>
</domain>
The user mode linux kernel, has the device emulator built-in, so there's no
need to specify the device emulator path explicitly. The kernel I built
straight from latest 2.6.27 sources, and the root filesystem I downloaded
from the UML website. I've been testing with latest libvirt CVS, on a
Fedora 9 x86_64 host.
If you save that config to a file 'demo.xml', you ought to be able todo
virsh --connect uml:///system define demo.xml
virsh --connect uml:///system start demo
virsh --connect uml:///system console demo
Though there is still a small race whereby we can ask for the PTY device
before its been created, causing console to fail.
If things go wrong /var/log/libvirt/uml/$NAME.log contains log messages,
The configuration is stored in /etc/libvirt/uml/$NAME.xml, or in
$HOME/.libvirt/uml/$NAME.xml, and you can also query stuff with mconsole
by doing 'uml_mconsole $NAME'
This isn't quite ready to merge, but I thought I'd send it here to make
people aware of it.
configure.in | 8
include/libvirt/virterror.h | 1
src/Makefile.am | 9
src/domain_conf.c | 47 -
src/domain_conf.h | 1
src/driver.h | 3
src/libvirt.c | 6
src/qemu_conf.c | 3
src/remote_internal.c | 16
src/uml_conf.c | 404 ++++++++++
src/uml_conf.h | 72 +
src/uml_driver.c | 1771 ++++++++++++++++++++++++++++++++++++++++++++
src/uml_driver.h | 34
src/virterror.c | 4
14 files changed, 2355 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],
@@ -261,6 +263,11 @@ if test "$with_qemu" = "yes" ; then
LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_QEMU"
fi
AM_CONDITIONAL([WITH_QEMU], [test "$with_qemu" = "yes"])
+
+if test "$with_uml" = "yes" ; then
+ LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_UML"
+fi
+AM_CONDITIONAL([WITH_UML], [test "$with_uml" = "yes"])
if test "$with_test" = "yes" ; then
LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_TEST"
@@ -1083,6 +1090,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/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/src/Makefile.am b/src/Makefile.am
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -107,6 +107,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
@@ -178,6 +182,10 @@ libvirt_la_SOURCES += $(QEMU_DRIVER_SOUR
libvirt_la_SOURCES += $(QEMU_DRIVER_SOURCES)
endif
+if WITH_UML
+libvirt_la_SOURCES += $(UML_DRIVER_SOURCES)
+endif
+
if WITH_LXC
libvirt_la_SOURCES += $(LXC_DRIVER_SOURCES)
endif
@@ -201,6 +209,7 @@ EXTRA_DIST += \
$(REMOTE_DRIVER_SOURCES) \
$(XEN_DRIVER_SOURCES) \
$(QEMU_DRIVER_SOURCES) \
+ $(UML_DRIVER_SOURCES) \
$(LXC_DRIVER_SOURCES) \
$(OPENVZ_DRIVER_SOURCES) \
$(NETWORK_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;
}
@@ -1900,13 +1904,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,
@@ -2036,32 +2043,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
@@ -73,6 +73,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
@@ -22,7 +22,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/libvirt.c b/src/libvirt.c
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -50,6 +50,9 @@
#ifdef WITH_QEMU
#include "qemu_driver.h"
#endif
+#ifdef WITH_UML
+#include "uml_driver.h"
+#endif
#ifdef WITH_OPENVZ
#include "openvz_driver.h"
#endif
@@ -289,6 +292,9 @@ virInitialize(void)
#ifdef WITH_LIBVIRTD
#ifdef WITH_QEMU
if (qemudRegister() == -1) return -1;
+#endif
+#ifdef WITH_UML
+ if (umlRegister() == -1) return -1;
#endif
#ifdef WITH_OPENVZ
if (openvzRegister() == -1) return -1;
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -55,7 +55,8 @@ VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_
"scsi",
"virtio",
"xen",
- "usb")
+ "usb",
+ "uml")
#define qemudLog(level, msg...) fprintf(stderr, msg)
diff --git a/src/remote_internal.c b/src/remote_internal.c
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -757,6 +757,22 @@ remoteOpen (virConnectPtr conn,
#if WITH_QEMU
if (uri &&
uri->scheme && STREQ (uri->scheme, "qemu") &&
+ (!uri->server || STREQ (uri->server, "")) &&
+ uri->path) {
+ if (STREQ (uri->path, "/system")) {
+ rflags |= VIR_DRV_OPEN_REMOTE_UNIX;
+ } else if (STREQ (uri->path, "/session")) {
+ rflags |= VIR_DRV_OPEN_REMOTE_UNIX;
+ if (getuid() > 0) {
+ rflags |= VIR_DRV_OPEN_REMOTE_USER;
+ rflags |= VIR_DRV_OPEN_REMOTE_AUTOSTART;
+ }
+ }
+ }
+#endif
+#if WITH_UML
+ if (uri &&
+ uri->scheme && STREQ (uri->scheme, "uml") &&
(!uri->server || STREQ (uri->server, "")) &&
uri->path) {
if (STREQ (uri->path, "/system")) {
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(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include <dirent.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <sys/utsname.h>
+
+#if HAVE_NUMACTL
+#include <numa.h>
+#endif
+
+#include "uml_conf.h"
+#include "uuid.h"
+#include "buf.h"
+#include "conf.h"
+#include "util.h"
+#include "memory.h"
+#include "verify.h"
+
+
+#define umlLog(level, msg...) fprintf(stderr, msg)
+
+
+
+#if HAVE_NUMACTL
+#define MAX_CPUS 4096
+#define MAX_CPUS_MASK_SIZE (sizeof(unsigned long))
+#define MAX_CPUS_MASK_LEN (MAX_CPUS / MAX_CPUS_MASK_SIZE)
+#define MAX_CPUS_MASK_BYTES (MAX_CPUS / 8)
+
+#define MASK_CPU_ISSET(mask, cpu) \
+ (((mask)[((cpu) / MAX_CPUS_MASK_SIZE)] >> ((cpu) % MAX_CPUS_MASK_SIZE)) & 1)
+
+static int
+umlCapsInitNUMA(virCapsPtr caps)
+{
+ int n, i;
+ unsigned long *mask = NULL;
+ int ncpus;
+ int *cpus = NULL;
+ int ret = -1;
+
+ if (numa_available() < 0)
+ return 0;
+
+ if (VIR_ALLOC_N(mask, MAX_CPUS_MASK_LEN) < 0)
+ goto cleanup;
+
+ for (n = 0 ; n <= numa_max_node() ; n++) {
+ if (numa_node_to_cpus(n, mask, MAX_CPUS_MASK_BYTES) < 0)
+ goto cleanup;
+
+ for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
+ if (MASK_CPU_ISSET(mask, i))
+ ncpus++;
+
+ if (VIR_ALLOC_N(cpus, ncpus) < 0)
+ goto cleanup;
+
+ for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
+ if (MASK_CPU_ISSET(mask, i))
+ cpus[ncpus++] = i;
+
+ if (virCapabilitiesAddHostNUMACell(caps,
+ n,
+ ncpus,
+ cpus) < 0)
+ goto cleanup;
+
+ VIR_FREE(cpus);
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cpus);
+ VIR_FREE(mask);
+ return ret;
+}
+#else
+static int umlCapsInitNUMA(virCapsPtr caps ATTRIBUTE_UNUSED) { return 0; }
+#endif
+
+virCapsPtr umlCapsInit(void) {
+ struct utsname utsname;
+ virCapsPtr caps;
+ virCapsGuestPtr guest;
+
+ /* Really, this never fails - look at the man-page. */
+ uname (&utsname);
+
+ if ((caps = virCapabilitiesNew(utsname.machine,
+ 0, 0)) == NULL)
+ goto no_memory;
+
+ if (umlCapsInitNUMA(caps) < 0)
+ goto no_memory;
+
+ if ((guest = virCapabilitiesAddGuest(caps,
+ "uml",
+ utsname.machine,
+ STREQ(utsname.machine, "x86_64") ? 64 : 32,
+ NULL,
+ NULL,
+ 0,
+ NULL)) == NULL)
+ goto no_memory;
+
+ if (virCapabilitiesAddGuestDomain(guest,
+ "uml",
+ NULL,
+ NULL,
+ 0,
+ NULL) == NULL)
+ goto no_memory;
+
+ return caps;
+
+ no_memory:
+ virCapabilitiesFree(caps);
+ return NULL;
+}
+
+
+static char *
+umlBuildCommandLineChr(virConnectPtr conn,
+ virDomainChrDefPtr def,
+ const char *dev)
+{
+ char *ret;
+
+ switch (def->type) {
+ case VIR_DOMAIN_CHR_TYPE_NULL:
+ if (asprintf(&ret, "%s%d=null", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_PTY:
+ if (asprintf(&ret, "%s%d=pts", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_DEV:
+ if (asprintf(&ret, "%s%d=tty:%s", dev, def->dstPort,
+ def->data.file.path) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_STDIO:
+ if (asprintf(&ret, "%s%d=fd:0,fd:1", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_TCP:
+ if (def->data.tcp.listen != 1) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("only TCP listen is supported for chr device"));
+ return NULL;
+ }
+
+ if (asprintf(&ret, "%s%d=port:%s", dev, def->dstPort,
+ def->data.tcp.service) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return NULL;
+ }
+ break;
+
+ case VIR_DOMAIN_CHR_TYPE_FILE:
+ case VIR_DOMAIN_CHR_TYPE_PIPE:
+ /* XXX could open the file/pipe & just pass the FDs */
+
+ case VIR_DOMAIN_CHR_TYPE_VC:
+ case VIR_DOMAIN_CHR_TYPE_UDP:
+ case VIR_DOMAIN_CHR_TYPE_UNIX:
+ default:
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unsupported chr device type %d"), def->type);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Constructs a argv suitable for launching uml with config defined
+ * for a given virtual machine.
+ */
+int umlBuildCommandLine(virConnectPtr conn,
+ struct uml_driver *driver ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ const char ***retargv,
+ const char ***retenv,
+ int **tapfds,
+ int *ntapfds) {
+ int i, j;
+ char memory[50];
+ struct utsname ut;
+ int qargc = 0, qarga = 0;
+ const char **qargv = NULL;
+ int qenvc = 0, qenva = 0;
+ const char **qenv = NULL;
+
+ uname(&ut);
+
+#define ADD_ARG_SPACE \
+ do { \
+ if (qargc == qarga) { \
+ qarga += 10; \
+ if (VIR_REALLOC_N(qargv, qarga) < 0) \
+ goto no_memory; \
+ } \
+ } while (0)
+
+#define ADD_ARG(thisarg) \
+ do { \
+ ADD_ARG_SPACE; \
+ qargv[qargc++] = thisarg; \
+ } while (0)
+
+#define ADD_ARG_LIT(thisarg) \
+ do { \
+ ADD_ARG_SPACE; \
+ if ((qargv[qargc++] = strdup(thisarg)) == NULL) \
+ goto no_memory; \
+ } while (0)
+
+#define ADD_ARG_PAIR(key,val) \
+ do { \
+ char *arg; \
+ ADD_ARG_SPACE; \
+ if (asprintf(&arg, "%s=%s", key, val) < 0) \
+ goto no_memory; \
+ qargv[qargc++] = arg; \
+ } while (0)
+
+
+#define ADD_ENV_SPACE \
+ do { \
+ if (qenvc == qenva) { \
+ qenva += 10; \
+ if (VIR_REALLOC_N(qenv, qenva) < 0) \
+ goto no_memory; \
+ } \
+ } while (0)
+
+#define ADD_ENV(thisarg) \
+ do { \
+ ADD_ENV_SPACE; \
+ qenv[qenvc++] = thisarg; \
+ } while (0)
+
+#define ADD_ENV_LIT(thisarg) \
+ do { \
+ ADD_ENV_SPACE; \
+ if ((qenv[qenvc++] = strdup(thisarg)) == NULL) \
+ goto no_memory; \
+ } while (0)
+
+#define ADD_ENV_COPY(envname) \
+ do { \
+ char *val = getenv(envname); \
+ char *envval; \
+ ADD_ENV_SPACE; \
+ if (val != NULL) { \
+ if (asprintf(&envval, "%s=%s", envname, val) < 0) \
+ goto no_memory; \
+ qenv[qenvc++] = envval; \
+ } \
+ } while (0)
+
+ snprintf(memory, sizeof(memory), "%luK", vm->def->memory);
+
+ ADD_ENV_LIT("LC_ALL=C");
+
+ ADD_ENV_COPY("LD_PRELOAD");
+ ADD_ENV_COPY("LD_LIBRARY_PATH");
+ ADD_ENV_COPY("PATH");
+ ADD_ENV_COPY("HOME");
+ ADD_ENV_COPY("USER");
+ ADD_ENV_COPY("LOGNAME");
+ ADD_ENV_COPY("TMPDIR");
+
+ ADD_ARG_LIT(vm->def->os.kernel);
+ //ADD_ARG_PAIR("con0", "fd:0,fd:1");
+ ADD_ARG_PAIR("mem", memory);
+ ADD_ARG_PAIR("umid", vm->def->name);
+
+ if (vm->def->os.root)
+ ADD_ARG_PAIR("root", vm->def->os.root);
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ virDomainDiskDefPtr disk = vm->def->disks[i];
+
+ if (!STRPREFIX(disk->dst, "ubd")) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unsupported disk type '%s'"), disk->dst);
+ goto error;
+ }
+
+ ADD_ARG_PAIR(disk->dst, disk->src);
+ }
+
+ for (i = 0 ; i < 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,72 @@
+/*
+ * 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(a)redhat.com>
+ */
+
+#ifndef __UML_CONF_H
+#define __UML_CONF_H
+
+#include <config.h>
+
+#include "internal.h"
+#include "bridge.h"
+#include "capabilities.h"
+#include "network_conf.h"
+#include "domain_conf.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,1771 @@
+/*
+ * 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(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <dirent.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+
+#if HAVE_NUMACTL
+#include <numa.h>
+#endif
+
+#include "uml_driver.h"
+#include "uml_conf.h"
+#include "c-ctype.h"
+#include "event.h"
+#include "buf.h"
+#include "util.h"
+#include "nodeinfo.h"
+#include "stats_linux.h"
+#include "capabilities.h"
+#include "memory.h"
+#include "uuid.h"
+#include "domain_conf.h"
+
+/* 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;
+}
+
+#if 0
+static int umlSetNonBlock(int fd) {
+ int flags;
+ if ((flags = fcntl(fd, F_GETFL)) < 0)
+ goto error;
+ flags |= O_NONBLOCK;
+ if ((fcntl(fd, F_SETFL, flags)) < 0)
+ goto error;
+ return 0;
+ error:
+ umlLog(UML_ERR,
+ "%s", _("Failed to set non-blocking file descriptor flag\n"));
+ return -1;
+}
+#endif
+
+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;
+ if (asprintf(&cmd, "config %s%d", dev, def->dstPort) < 0) {
+ umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return -1;
+ }
+ umlMonitorCommand(NULL, driver, dom, cmd, &res);
+ VIR_FREE(cmd);
+
+ if (STRPREFIX(res, "pts:")) {
+ fprintf(stderr, "Got it %s\n", res + 4);
+ 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);
+ return -1;
+ }
+ }
+ 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;
+
+ umlLog(UML_INFO, _("got inotify event\n"));
+
+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) {
+ umlLog (UML_ERR, _("cannot find domain '%s'\n"), name);
+ continue;
+ }
+
+ if (e->mask & IN_DELETE) {
+ umlLog (UML_INFO, _("got delete event %s\n"), name);
+ if (!virDomainIsActive(dom)) {
+ umlLog (UML_ERR, _("domain '%s' not active\n"),
+ name);
+ 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)) {
+ umlLog (UML_INFO, _("got create/change event '%s'\n"), name);
+ if (virDomainIsActive(dom)) {
+ umlLog (UML_ERR, _("domain '%s' already active as pid %d\n"),
+ name, dom->pid);
+ continue;
+ }
+
+ if (umlReadPidFile(NULL, driver, dom) < 0) {
+ umlLog (UML_ERR, _("cannot read PID for domain '%s'\n"), name);
+ continue;
+ }
+ umlLog (UML_INFO, _("started domain '%s'"), name);
+ 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 (inotify_add_watch(uml_driver->inotifyFD,
+ uml_driver->monitorDir,
+ IN_CREATE | IN_MODIFY | IN_DELETE) < 0) {
+ umlLog(UML_ERR, _("cannot add watch on %s"),
+ uml_driver->monitorDir);
+ 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) < 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);
+
+ 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;
+
+ umlLog(UML_INFO, "got UML pid %d\n", vm->pid);
+ 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;
+
+ umlLog(UML_INFO, _("Shutting down VM '%d' '%s'\n"),
+ vm->pid, vm->def->name);
+
+ 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;
+ }
+}
+
+
+
+/**
+ * umlProbe:
+ *
+ * Probe for the availability of the uml driver, assume the
+ * presence of Uml emulation if the binaries are installed
+ */
+static const char *umlProbe(void)
+{
+ if (!access("/usr/bin/uml_mconsole", R_OK | X_OK))
+ return NULL;
+
+ if (getuid() == 0) {
+ return("uml:///system");
+ } else {
+ return("uml:///session");
+ }
+}
+
+static virDrvOpenStatus umlOpen(virConnectPtr conn,
+ xmlURIPtr uri,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED) {
+ uid_t uid = getuid();
+
+ if (uml_driver == NULL)
+ goto decline;
+
+ if (uri == NULL || uri->scheme == NULL || uri->path == NULL)
+ goto decline;
+
+ if (STRNEQ (uri->scheme, "uml"))
+ goto decline;
+
+ if (uid != 0) {
+ if (STRNEQ (uri->path, "/session"))
+ goto decline;
+ } else { /* root */
+ if (STRNEQ (uri->path, "/system") &&
+ STRNEQ (uri->path, "/session"))
+ goto decline;
+ }
+
+ 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;
+}
+
+#if 0
+static char *umlEscape(const char *in, int shell)
+{
+ int len = 0;
+ int i, j;
+ char *out;
+
+ /* To pass through the UML monitor, we need to use escape
+ sequences: \r, \n, \", \\
+
+ To pass through both UML + the shell, we need to escape
+ the single character ' as the five characters '\\''
+ */
+
+ for (i = 0; in[i] != '\0'; i++) {
+ switch(in[i]) {
+ case '\r':
+ case '\n':
+ case '"':
+ case '\\':
+ len += 2;
+ break;
+ case '\'':
+ if (shell)
+ len += 5;
+ else
+ len += 1;
+ break;
+ default:
+ len += 1;
+ break;
+ }
+ }
+
+ if (VIR_ALLOC_N(out, len + 1) < 0)
+ return NULL;
+
+ for (i = j = 0; in[i] != '\0'; i++) {
+ switch(in[i]) {
+ case '\r':
+ out[j++] = '\\';
+ out[j++] = 'r';
+ break;
+ case '\n':
+ out[j++] = '\\';
+ out[j++] = 'n';
+ break;
+ case '"':
+ case '\\':
+ out[j++] = '\\';
+ out[j++] = in[i];
+ break;
+ case '\'':
+ if (shell) {
+ out[j++] = '\'';
+ out[j++] = '\\';
+ out[j++] = '\\';
+ out[j++] = '\'';
+ out[j++] = '\'';
+ } else {
+ out[j++] = in[i];
+ }
+ break;
+ default:
+ out[j++] = in[i];
+ break;
+ }
+ }
+ out[j] = '\0';
+
+ return out;
+}
+
+static char *umlEscapeMonitorArg(const char *in)
+{
+ return umlEscape(in, 0);
+}
+
+static char *umlEscapeShellArg(const char *in)
+{
+ return umlEscape(in, 1);
+}
+#endif
+
+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,
+ umlProbe, /* probe */
+ 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
+};
+
+
+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,34 @@
+/*
+ * 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(a)redhat.com>
+ */
+
+
+#ifndef UML_DRIVER_H
+#define UML_DRIVER_H
+
+#include <config.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 :|
16 years, 2 months
[libvirt] Committed a fix for building with -Werror
by Chris Lalancette
I just committed this:
+Thu Oct 16 15:41:00 CEST 2008 Chris Lalancette <clalance(a)redhat.com>
+ * Compiling with -Werror showed a possible use before initialization
+ in src/qemu_driver.c. Make sure to initialize the olddisk ptr to
+ NULL.
+
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.135
diff -u -r1.135 qemu_driver.c
--- src/qemu_driver.c 13 Oct 2008 16:46:28 -0000 1.135
+++ src/qemu_driver.c 16 Oct 2008 13:42:17 -0000
@@ -2401,6 +2401,7 @@
return -1;
}
+ origdisk = NULL;
newdisk = dev->data.disk;
for (i = 0 ; i < vm->def->ndisks ; i++) {
if (vm->def->disks[i]->bus == newdisk->bus &&
--
Chris Lalancette
16 years, 2 months
[libvirt] fix "make syntax-check" vs. new .ico file with trailing TAB
by Jim Meyering
One of the new (binary) .ico files has a TAB just before a newline,
and that triggers a "make syntax-check" failure.
This gives all *.ico files a free pass.
(also sorted the file)
>From 11096480dfb54fc179b3854d3f81739f2d2ef273 Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering(a)redhat.com>
Date: Thu, 16 Oct 2008 15:05:16 +0200
Subject: [PATCH] build: exempt *.ico files from the trailing blank check
* .x-sc_trailing_blank: Add \.ico$ to the list.
---
.x-sc_trailing_blank | 5 +++--
ChangeLog | 5 +++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/.x-sc_trailing_blank b/.x-sc_trailing_blank
index 8b3ec89..de3369e 100644
--- a/.x-sc_trailing_blank
+++ b/.x-sc_trailing_blank
@@ -1,6 +1,7 @@
-\.png$
\.fig$
\.gif$
-^NEWS$
+\.ico$
+\.png$
^ChangeLog$
+^NEWS$
^docs/.*
diff --git a/ChangeLog b/ChangeLog
index 2aff530..322f837 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Thu Oct 16 15:04:36 +0200 Jim Meyering <meyering(a)redhat.com>
+
+ build: exempt *.ico files from the trailing blank check
+ * .x-sc_trailing_blank: Add \.ico$ to the list.
+
Wed Oct 16 14:03:00 CEST 2008 Chris Lalancette <clalance(a)redhat.com>
* "make syntax-check" was complaining that network_driver.c was
--
1.6.0.2.98.gc82e
16 years, 2 months
[libvirt] Libvirt support for migration in KVM
by Shanmuga Rajan
Hi
I have tested migration function VirDomain object by migrating with in
localhost(for testing purpose).
System details are
KVM hypervisor.
Intel VT 64 bit centos.
libvirt 0.4.6. (compiled from source)
vs i am running is Win 2003 64 bit...
Host is also 64 bit....
but when i tried to use the migrate function of virDomain object. it
throws the following error....
libvirt.libvirtError: virDomainMigrate() failed this function is not
supported by the hypervisor: virDomainMigrate
i read in kvm wiki that they do support migration....
Can anyone point out the problem....
here comes the python code....
>>> vsobj.migrate(conn,libvirt.VIR_MIGRATE_LIVE,None,conn.getURI(),0)
libvir: error : this function is not supported by the hypervisor:
virDomainMigrate
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/lib64/python2.4/site-packages/libvirt.py", line 301, in migrate
if ret is None:raise libvirtError('virDomainMigrate() failed', dom=self)
libvirt.libvirtError: virDomainMigrate() failed this function is not
supported by the hypervisor: virDomainMigrate
>>>
Thanks for any suggestion
-- Shan
16 years, 2 months