Devel
Threads by month
- ----- 2026 -----
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
November 2008
- 45 participants
- 99 discussions
I am working on a x86_64 machine. I expected 'make install' to install to
libvirt.so to /usr/lib64. But it installs to /usr/lib. Is this a bug or
is it the expected behavior?
Kenneth Nagin
2
1
Hi,
I had been running kvm in a previous version of Ubuntu with the
following command line:
/usr/bin/kvm
-hda /mnt/kvm/vm-images-work/nanobsd.img
-net nic,vlan=26,macaddr=52:54:00:12:34:26 -net tap,vlan=26,ifname=tap26
-net nic,vlan=27,macaddr=52:54:00:12:34:27 -net tap,vlan=27,ifname=tap27
-net nic,vlan=28,macaddr=52:54:00:12:34:28 -net tap,vlan=28,ifname=tap28
-m 640 -vnc :26 -usbdevice tablet
The vm shows couple if lines in console and then redirects rest of the
output to serial console. I can see that in ctrl-alt-3 of vncviewer
ctrl-alt-2 gives access to emulation console and 4 gives access to
parallel console.
After upgrading to latest Intrepid Ibex I decided to move to using
virsh to manage my vms. For the above vm I am using the following xml
file.
<domain type='kvm'>
<name>nanobsd</name>
<memory>262144</memory>
<currentMemory>262144</currentMemory>
<vcpu>1</vcpu>
<os>
<type>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
</features>
<clock offset='localtime'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/kvm</emulator>
<disk type='file' device='disk'>
<source file='/mnt/kvm/vm-images-work/nanotest.img'/>
<target dev='hda' bus='ide'/>
</disk>
<interface type='bridge'>
<mac address='52:54:00:12:34:31'/>
<source bridge='br0'/>
</interface>
<interface type='bridge'>
<mac address='52:54:00:12:34:32'/>
<source bridge='br0'/>
</interface>
<interface type='bridge'>
<mac address='52:54:00:12:34:33'/>
<source bridge='br0'/>
</interface>
<serial type='vc'>
<target port='2'/>
</serial>
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<graphics type='vnc' port='5931' listen='192.168.3.3'/>
</devices>
</domain>
Which produces the following command line.
/usr/bin/kvm -S -M pc -m 256 -smp 1 -name nanobsd -monitor pty
-localtime -boot c
-drive file=/mnt/kvm/vm-images-work/nanotest.img,if=ide,index=0,boot=on
-net nic,macaddr=52:54:00:12:34:31,vlan=0 -net
tap,fd=28,script=,vlan=0,ifname=vnet5
-net nic,macaddr=52:54:00:12:34:32,vlan=1 -net
tap,fd=29,script=,vlan=1,ifname=vnet6
-net nic,macaddr=52:54:00:12:34:33,vlan=2 -net
tap,fd=30,script=,vlan=2,ifname=vnet7
-serial vc -parallel none -usb -vnc 192.168.3.3:31
Problem with this configuration is that the screen freezes after
couple of minuets of booting, I am not sure if this is the problem
with the serial console or actual execution. I can see the serial
console in ctrl-alt-2, but the window size changes. The image works
perfectly in normal PC as well as if I use the previous command line.
I guess I am missing some thing here, but I would be very happy if I
can get the old behaviour back :)
raj
3
4
20 Nov '08
While starting to think about Windows compability, I realized the newly
exposed API for registering an external EventImpl is not adequate.
Currently it's assuming 32-bit unix fds. But Windows uses a pointer
(HANDLE) here. So we need to generalize this interface so it can be
implemented for 64-bit Windows. The attached patch does this. (I'm
sure it conflicts with work Dan B is doing, so I'm hoping he'll just
incorporate this into his changes.)
Dave
include/libvirt/libvirt.h.in | 24 +++++++++++++++---------
src/event.c | 18 +++++++++---------
2 files changed, 24 insertions(+), 18 deletions(-)
3
7
[libvirt] PATCH: Change signature of virEventAddHandle to allow multiple calls per FD
by Daniel P. Berrange 19 Nov '08
by Daniel P. Berrange 19 Nov '08
19 Nov '08
As discussed previously, this patch changes the semantics of the public
API for dealing with file handle watches. Previously we would track the
watch based on the file handle number directly. With this change, the
virEventAddHandle method returns an integer 'watch' number. This watch
number is required when unregistering or updating a watch. The watch is
also passed into the callback when an event occurrs. This allows for
multiple watches to be registered against the same file descriptor.
There was quite alot of fallout from this patch requiring many callers
to be updated to comply with the new semantics.
examples/domain-events/events-c/event-test.c | 7 +-
examples/domain-events/events-python/event-test.py | 12 ++--
include/libvirt/libvirt.h | 20 ++++---
include/libvirt/libvirt.h.in | 20 ++++---
python/libvir.c | 8 +-
qemud/event.c | 27 ++++++---
qemud/event.h | 10 +--
qemud/mdns.c | 16 +++--
qemud/qemud.c | 59 +++++++++++----------
qemud/qemud.h | 2
src/domain_conf.h | 3 +
src/event.c | 8 +-
src/event.h | 8 +-
src/lxc_driver.c | 25 +++++---
src/qemu_driver.c | 43 ++++++++-------
src/remote_internal.c | 27 ++++++---
16 files changed, 176 insertions(+), 119 deletions(-)
Daniel
diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c
--- a/examples/domain-events/events-c/event-test.c
+++ b/examples/domain-events/events-c/event-test.c
@@ -40,8 +40,8 @@ int myDomainEventCallback2 (virConnectPt
int event, int detail, void *opaque);
int myEventAddHandleFunc (int fd, int event,
virEventHandleCallback cb, void *opaque);
-void myEventUpdateHandleFunc(int fd, int event);
-int myEventRemoveHandleFunc(int fd);
+void myEventUpdateHandleFunc(int watch, int event);
+int myEventRemoveHandleFunc(int watch);
int myEventAddTimeoutFunc(int timeout, virEventTimeoutCallback cb,
void *opaque);
@@ -308,7 +308,8 @@ int main(int argc, char **argv)
}
if(h_cb) {
- h_cb(h_fd,
+ h_cb(0,
+ h_fd,
myPollEventToEventHandleType(pfd.revents & h_event),
h_opaque);
}
diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py
--- a/examples/domain-events/events-python/event-test.py
+++ b/examples/domain-events/events-python/event-test.py
@@ -75,19 +75,19 @@ def myAddHandle(fd, events, cb, opaque):
mypoll.register(fd, myEventHandleTypeToPollEvent(events))
-def myUpdateHandle(fd, event):
+def myUpdateHandle(watch, event):
global h_fd, h_events
#print "Updating Handle %s %s" % (str(fd), str(events))
h_fd = fd
h_events = event
- mypoll.unregister(fd)
- mypoll.register(fd, myEventHandleTypeToPollEvent(event))
+ mypoll.unregister(watch)
+ mypoll.register(watch, myEventHandleTypeToPollEvent(event))
-def myRemoveHandle(fd):
+def myRemoveHandle(watch):
global h_fd
#print "Removing Handle %s" % str(fd)
h_fd = 0
- mypoll.unregister(fd)
+ mypoll.unregister(watch)
def myAddTimeout(timeout, cb, opaque):
global t_active, t_timeout, t_cb, t_opaque
@@ -175,7 +175,7 @@ def main():
if h_cb != None:
#print "Invoking Handle CB"
- h_cb(h_fd, myPollEventToEventHandleType(revents & h_events),
+ h_cb(0, h_fd, myPollEventToEventHandleType(revents & h_events),
h_opaque[0], h_opaque[1])
#print "DEBUG EXIT"
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -1121,13 +1121,15 @@ typedef enum {
/**
* virEventHandleCallback:
*
+ * @watch: watch on which the event occurred
* @fd: file handle on which the event occurred
* @events: bitset of events from virEventHandleType constants
* @opaque: user data registered with handle
*
- * callback for receiving file handle events
+ * Callback for receiving file handle events. The callback will
+ * be invoked once for each event which is pending.
*/
-typedef void (*virEventHandleCallback)(int fd, int events, void *opaque);
+typedef void (*virEventHandleCallback)(int watch, int fd, int events, void *opaque);
/**
* virEventAddHandleFunc:
@@ -1137,29 +1139,33 @@ typedef void (*virEventHandleCallback)(i
* @opaque: user data to pass to the callback
*
* Part of the EventImpl, this callback Adds a file handle callback to
- * listen for specific events
+ * listen for specific events. The same file handle can be registered
+ * multiple times provided the requested event sets are non-overlapping
+ *
+ * Returns a handle watch number to be used for updating
+ * and unregistering for events
*/
typedef int (*virEventAddHandleFunc)(int fd, int event,
virEventHandleCallback cb, void *opaque);
/**
* virEventUpdateHandleFunc:
- * @fd: file descriptor to modify
+ * @watch: file descriptor watch to modify
* @event: new events to listen on
*
* Part of the EventImpl, this user-provided callback is notified when
* events to listen on change
*/
-typedef void (*virEventUpdateHandleFunc)(int fd, int event);
+typedef void (*virEventUpdateHandleFunc)(int watch, int event);
/**
* virEventRemoveHandleFunc:
- * @fd: file descriptor to stop listening on
+ * @watch: file descriptor watch to stop listening on
*
* Part of the EventImpl, this user-provided callback is notified when
* an fd is no longer being listened on
*/
-typedef int (*virEventRemoveHandleFunc)(int fd);
+typedef int (*virEventRemoveHandleFunc)(int watch);
/**
* virEventTimeoutCallback:
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1121,13 +1121,15 @@ typedef enum {
/**
* virEventHandleCallback:
*
+ * @watch: watch on which the event occurred
* @fd: file handle on which the event occurred
* @events: bitset of events from virEventHandleType constants
* @opaque: user data registered with handle
*
- * callback for receiving file handle events
+ * Callback for receiving file handle events. The callback will
+ * be invoked once for each event which is pending.
*/
-typedef void (*virEventHandleCallback)(int fd, int events, void *opaque);
+typedef void (*virEventHandleCallback)(int watch, int fd, int events, void *opaque);
/**
* virEventAddHandleFunc:
@@ -1137,29 +1139,33 @@ typedef void (*virEventHandleCallback)(i
* @opaque: user data to pass to the callback
*
* Part of the EventImpl, this callback Adds a file handle callback to
- * listen for specific events
+ * listen for specific events. The same file handle can be registered
+ * multiple times provided the requested event sets are non-overlapping
+ *
+ * Returns a handle watch number to be used for updating
+ * and unregistering for events
*/
typedef int (*virEventAddHandleFunc)(int fd, int event,
virEventHandleCallback cb, void *opaque);
/**
* virEventUpdateHandleFunc:
- * @fd: file descriptor to modify
+ * @watch: file descriptor watch to modify
* @event: new events to listen on
*
* Part of the EventImpl, this user-provided callback is notified when
* events to listen on change
*/
-typedef void (*virEventUpdateHandleFunc)(int fd, int event);
+typedef void (*virEventUpdateHandleFunc)(int watch, int event);
/**
* virEventRemoveHandleFunc:
- * @fd: file descriptor to stop listening on
+ * @watch: file descriptor watch to stop listening on
*
* Part of the EventImpl, this user-provided callback is notified when
* an fd is no longer being listened on
*/
-typedef int (*virEventRemoveHandleFunc)(int fd);
+typedef int (*virEventRemoveHandleFunc)(int watch);
/**
* virEventTimeoutCallback:
diff --git a/python/libvir.c b/python/libvir.c
--- a/python/libvir.c
+++ b/python/libvir.c
@@ -1940,15 +1940,15 @@ libvirt_virEventInvokeHandleCallback(PyO
libvirt_virEventInvokeHandleCallback(PyObject *self ATTRIBUTE_UNUSED,
PyObject *args)
{
- int fd, event;
+ int watch, fd, event;
PyObject *py_f;
PyObject *py_opaque;
virEventHandleCallback cb;
void *opaque;
if (!PyArg_ParseTuple
- (args, (char *) "iiOO:virEventInvokeHandleCallback",
- &fd, &event, &py_f, &py_opaque
+ (args, (char *) "iiiOO:virEventInvokeHandleCallback",
+ &watch, &fd, &event, &py_f, &py_opaque
))
return VIR_PY_INT_FAIL;
@@ -1956,7 +1956,7 @@ libvirt_virEventInvokeHandleCallback(PyO
opaque = (void *) PyvirVoidPtr_Get(py_opaque);
if(cb)
- cb (fd, event, opaque);
+ cb (watch, fd, event, opaque);
return VIR_PY_INT_SUCCESS;
}
diff --git a/qemud/event.c b/qemud/event.c
--- a/qemud/event.c
+++ b/qemud/event.c
@@ -37,6 +37,7 @@
/* State for a single file handle being monitored */
struct virEventHandle {
+ int watch;
int fd;
int events;
virEventHandleCallback cb;
@@ -71,6 +72,9 @@ struct virEventLoop {
/* Only have one event loop */
static struct virEventLoop eventLoop;
+/* Unique ID for the next FD watch to be registered */
+static int nextWatch = 0;
+
/* Unique ID for the next timer to be registered */
static int nextTimer = 0;
@@ -91,6 +95,7 @@ int virEventAddHandleImpl(int fd, int ev
eventLoop.handlesAlloc += EVENT_ALLOC_EXTENT;
}
+ eventLoop.handles[eventLoop.handlesCount].watch = nextWatch++;
eventLoop.handles[eventLoop.handlesCount].fd = fd;
eventLoop.handles[eventLoop.handlesCount].events =
virEventHandleTypeToPollEvent(events);
@@ -100,13 +105,13 @@ int virEventAddHandleImpl(int fd, int ev
eventLoop.handlesCount++;
- return 0;
+ return nextWatch-1;
}
-void virEventUpdateHandleImpl(int fd, int events) {
+void virEventUpdateHandleImpl(int watch, int events) {
int i;
for (i = 0 ; i < eventLoop.handlesCount ; i++) {
- if (eventLoop.handles[i].fd == fd) {
+ if (eventLoop.handles[i].watch == watch) {
eventLoop.handles[i].events =
virEventHandleTypeToPollEvent(events);
break;
@@ -120,15 +125,15 @@ void virEventUpdateHandleImpl(int fd, in
* For this reason we only ever set a flag in the existing list.
* Actual deletion will be done out-of-band
*/
-int virEventRemoveHandleImpl(int fd) {
+int virEventRemoveHandleImpl(int watch) {
int i;
- EVENT_DEBUG("Remove handle %d", fd);
+ EVENT_DEBUG("Remove handle %d", watch);
for (i = 0 ; i < eventLoop.handlesCount ; i++) {
if (eventLoop.handles[i].deleted)
continue;
- if (eventLoop.handles[i].fd == fd) {
- EVENT_DEBUG("mark delete %d", i);
+ if (eventLoop.handles[i].watch == watch) {
+ EVENT_DEBUG("mark delete %d %d", i, eventLoop.handles[i].fd);
eventLoop.handles[i].deleted = 1;
return 0;
}
@@ -356,9 +361,13 @@ static int virEventDispatchHandles(struc
if (fds[i].revents) {
hEvents = virPollEventToEventHandleType(fds[i].revents);
- EVENT_DEBUG("Dispatch %d %d %p", fds[i].fd, fds[i].revents,
+ EVENT_DEBUG("Dispatch %d %d %d %p",
+ eventLoop.handles[i].watch,
+ fds[i].fd, fds[i].revents,
eventLoop.handles[i].opaque);
- (eventLoop.handles[i].cb)(fds[i].fd, hEvents,
+ (eventLoop.handles[i].cb)(eventLoop.handles[i].watch,
+ fds[i].fd,
+ hEvents,
eventLoop.handles[i].opaque);
}
}
diff --git a/qemud/event.h b/qemud/event.h
--- a/qemud/event.h
+++ b/qemud/event.h
@@ -24,7 +24,7 @@
#ifndef __VIRTD_EVENT_H__
#define __VIRTD_EVENT_H__
-#include "../src/event.h"
+#include "internal.h"
/**
* virEventAddHandleImpl: register a callback for monitoring file handle events
@@ -42,21 +42,21 @@ int virEventAddHandleImpl(int fd, int ev
/**
* virEventUpdateHandleImpl: change event set for a monitored file handle
*
- * @fd: file handle to monitor for events
+ * @watch: watch whose handle to update
* @events: bitset of events to watch from POLLnnn constants
*
* Will not fail if fd exists
*/
-void virEventUpdateHandleImpl(int fd, int events);
+void virEventUpdateHandleImpl(int watch, int events);
/**
* virEventRemoveHandleImpl: unregister a callback from a file handle
*
- * @fd: file handle to stop monitoring for events
+ * @watch: watch whose handle to remove
*
* returns -1 if the file handle was not registered, 0 upon success
*/
-int virEventRemoveHandleImpl(int fd);
+int virEventRemoveHandleImpl(int watch);
/**
* virEventAddTimeoutImpl: register a callback for a timer event
diff --git a/qemud/mdns.c b/qemud/mdns.c
--- a/qemud/mdns.c
+++ b/qemud/mdns.c
@@ -68,6 +68,7 @@ struct libvirtd_mdns {
/* Avahi API requires this struct names in the app :-( */
struct AvahiWatch {
+ int watch;
int fd;
int revents;
AvahiWatchCallback callback;
@@ -228,17 +229,18 @@ static void libvirtd_mdns_client_callbac
}
-static void libvirtd_mdns_watch_dispatch(int fd, int events, void *opaque)
+static void libvirtd_mdns_watch_dispatch(int watch, int fd, int events, void *opaque)
{
AvahiWatch *w = (AvahiWatch*)opaque;
int fd_events = virEventHandleTypeToPollEvent(events);
- AVAHI_DEBUG("Dispatch watch FD %d Event %d", fd, fd_events);
+ AVAHI_DEBUG("Dispatch watch %d FD %d Event %d", watch, fd, fd_events);
w->revents = fd_events;
w->callback(w, fd, fd_events, w->userdata);
}
static AvahiWatch *libvirtd_mdns_watch_new(const AvahiPoll *api ATTRIBUTE_UNUSED,
- int fd, AvahiWatchEvent event, AvahiWatchCallback cb, void *userdata) {
+ int fd, AvahiWatchEvent event,
+ AvahiWatchCallback cb, void *userdata) {
AvahiWatch *w;
virEventHandleType hEvents;
if (VIR_ALLOC(w) < 0)
@@ -251,8 +253,8 @@ static AvahiWatch *libvirtd_mdns_watch_n
AVAHI_DEBUG("New handle %p FD %d Event %d", w, w->fd, event);
hEvents = virPollEventToEventHandleType(event);
- if (virEventAddHandleImpl(fd, hEvents,
- libvirtd_mdns_watch_dispatch, w) < 0) {
+ if ((w->watch = virEventAddHandleImpl(fd, hEvents,
+ libvirtd_mdns_watch_dispatch, w)) < 0) {
VIR_FREE(w);
return NULL;
}
@@ -263,7 +265,7 @@ static void libvirtd_mdns_watch_update(A
static void libvirtd_mdns_watch_update(AvahiWatch *w, AvahiWatchEvent event)
{
AVAHI_DEBUG("Update handle %p FD %d Event %d", w, w->fd, event);
- virEventUpdateHandleImpl(w->fd, event);
+ virEventUpdateHandleImpl(w->watch, event);
}
static AvahiWatchEvent libvirtd_mdns_watch_get_events(AvahiWatch *w)
@@ -275,7 +277,7 @@ static void libvirtd_mdns_watch_free(Ava
static void libvirtd_mdns_watch_free(AvahiWatch *w)
{
AVAHI_DEBUG("Free handle %p %d", w, w->fd);
- virEventRemoveHandleImpl(w->fd);
+ virEventRemoveHandleImpl(w->watch);
VIR_FREE(w);
}
diff --git a/qemud/qemud.c b/qemud/qemud.c
--- a/qemud/qemud.c
+++ b/qemud/qemud.c
@@ -142,8 +142,8 @@ static void sig_handler(int sig, siginfo
errno = origerrno;
}
-static void qemudDispatchClientEvent(int fd, int events, void *opaque);
-static void qemudDispatchServerEvent(int fd, int events, void *opaque);
+static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque);
+static void qemudDispatchServerEvent(int watch, int fd, int events, void *opaque);
static int qemudRegisterClientEvent(struct qemud_server *server,
struct qemud_client *client,
int removeFirst);
@@ -245,7 +245,8 @@ remoteInitializeGnuTLS (void)
}
static void
-qemudDispatchSignalEvent(int fd ATTRIBUTE_UNUSED,
+qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED,
+ int fd ATTRIBUTE_UNUSED,
int events ATTRIBUTE_UNUSED,
void *opaque) {
struct qemud_server *server = (struct qemud_server *)opaque;
@@ -534,12 +535,12 @@ static int qemudListenUnix(struct qemud_
goto cleanup;
}
- if (virEventAddHandleImpl(sock->fd,
- VIR_EVENT_HANDLE_READABLE |
- VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- qemudDispatchServerEvent,
- server) < 0) {
+ if ((sock->watch = virEventAddHandleImpl(sock->fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchServerEvent,
+ server)) < 0) {
qemudLog(QEMUD_ERR, "%s",
_("Failed to add server event callback"));
goto cleanup;
@@ -666,12 +667,12 @@ remoteListenTCP (struct qemud_server *se
goto cleanup;
}
- if (virEventAddHandleImpl(sock->fd,
- VIR_EVENT_HANDLE_READABLE |
- VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- qemudDispatchServerEvent,
- server) < 0) {
+ if ((sock->watch = virEventAddHandleImpl(sock->fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchServerEvent,
+ server)) < 0) {
qemudLog(QEMUD_ERR, "%s", _("Failed to add server event callback"));
goto cleanup;
}
@@ -1232,7 +1233,7 @@ static void qemudDispatchClientFailure(s
tmp = tmp->next;
}
- virEventRemoveHandleImpl(client->fd);
+ virEventRemoveHandleImpl(client->watch);
/* Deregister event delivery callback */
if(client->conn) {
@@ -1596,18 +1597,21 @@ qemudDispatchClientWrite(struct qemud_se
static void
-qemudDispatchClientEvent(int fd, int events, void *opaque) {
+qemudDispatchClientEvent(int watch, int fd, int events, void *opaque) {
struct qemud_server *server = (struct qemud_server *)opaque;
struct qemud_client *client = server->clients;
while (client) {
- if (client->fd == fd)
+ if (client->watch == watch)
break;
client = client->next;
}
if (!client)
+ return;
+
+ if (client->fd != fd)
return;
if (events == VIR_EVENT_HANDLE_WRITABLE)
@@ -1644,32 +1648,35 @@ static int qemudRegisterClientEvent(stru
}
if (removeFirst)
- if (virEventRemoveHandleImpl(client->fd) < 0)
+ if (virEventRemoveHandleImpl(client->watch) < 0)
return -1;
- if (virEventAddHandleImpl(client->fd,
- mode | VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- qemudDispatchClientEvent,
- server) < 0)
+ if ((client->watch = virEventAddHandleImpl(client->fd,
+ mode | VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchClientEvent,
+ server)) < 0)
return -1;
return 0;
}
static void
-qemudDispatchServerEvent(int fd, int events, void *opaque) {
+qemudDispatchServerEvent(int watch, int fd, int events, void *opaque) {
struct qemud_server *server = (struct qemud_server *)opaque;
struct qemud_socket *sock = server->sockets;
while (sock) {
- if (sock->fd == fd)
+ if (sock->watch == watch)
break;
sock = sock->next;
}
if (!sock)
+ return;
+
+ if (sock->fd != fd)
return;
if (events)
diff --git a/qemud/qemud.h b/qemud/qemud.h
--- a/qemud/qemud.h
+++ b/qemud/qemud.h
@@ -96,6 +96,7 @@ struct qemud_client {
int magic;
int fd;
+ int watch;
int readonly;
enum qemud_mode mode;
@@ -141,6 +142,7 @@ struct qemud_client {
struct qemud_socket {
int fd;
+ int watch;
int readonly;
int type; /* qemud_sock_type */
int auth;
diff --git a/src/domain_conf.h b/src/domain_conf.h
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -455,8 +455,11 @@ struct _virDomainObj {
struct _virDomainObj {
int stdin_fd;
int stdout_fd;
+ int stdout_watch;
int stderr_fd;
+ int stderr_watch;
int monitor;
+ int monitorWatch;
int logfile;
int pid;
int state;
diff --git a/src/event.c b/src/event.c
--- a/src/event.c
+++ b/src/event.c
@@ -42,15 +42,15 @@ int virEventAddHandle(int fd, int events
return addHandleImpl(fd, events, cb, opaque);
}
-void virEventUpdateHandle(int fd, int events) {
- updateHandleImpl(fd, events);
+void virEventUpdateHandle(int watch, int events) {
+ updateHandleImpl(watch, events);
}
-int virEventRemoveHandle(int fd) {
+int virEventRemoveHandle(int watch) {
if (!removeHandleImpl)
return -1;
- return removeHandleImpl(fd);
+ return removeHandleImpl(watch);
}
int virEventAddTimeout(int timeout, virEventTimeoutCallback cb, void *opaque) {
diff --git a/src/event.h b/src/event.h
--- a/src/event.h
+++ b/src/event.h
@@ -40,21 +40,21 @@ int virEventAddHandle(int fd, int events
/**
* virEventUpdateHandle: change event set for a monitored file handle
*
- * @fd: file handle to monitor for events
+ * @watch: watch whose file handle to update
* @events: bitset of events to watch from virEventHandleType constants
*
* Will not fail if fd exists
*/
-void virEventUpdateHandle(int fd, int events);
+void virEventUpdateHandle(int watch, int events);
/**
* virEventRemoveHandle: unregister a callback from a file handle
*
- * @fd: file handle to stop monitoring for events
+ * @watch: watch whose file handle to remove
*
* returns -1 if the file handle was not registered, 0 upon success
*/
-int virEventRemoveHandle(int fd);
+int virEventRemoveHandle(int watch);
/**
* virEventAddTimeout: register a callback for a timer event
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -387,7 +387,7 @@ static int lxcVMCleanup(virConnectPtr co
DEBUG("container exited with rc: %d", rc);
}
- virEventRemoveHandle(vm->monitor);
+ virEventRemoveHandle(vm->monitorWatch);
close(vm->monitor);
virFileDeletePid(driver->stateDir, vm->def->name);
@@ -582,7 +582,8 @@ static int lxcVmTerminate(virConnectPtr
return lxcVMCleanup(conn, driver, vm);
}
-static void lxcMonitorEvent(int fd,
+static void lxcMonitorEvent(int watch,
+ int fd,
int events ATTRIBUTE_UNUSED,
void *data)
{
@@ -591,18 +592,23 @@ static void lxcMonitorEvent(int fd,
unsigned int i;
for (i = 0 ; i < driver->domains.count ; i++) {
- if (driver->domains.objs[i]->monitor == fd) {
+ if (driver->domains.objs[i]->monitorWatch == watch) {
vm = driver->domains.objs[i];
break;
}
}
if (!vm) {
- virEventRemoveHandle(fd);
+ virEventRemoveHandle(watch);
+ return;
+ }
+
+ if (vm->monitor != fd) {
+ virEventRemoveHandle(watch);
return;
}
if (lxcVmTerminate(NULL, driver, vm, SIGINT) < 0)
- virEventRemoveHandle(fd);
+ virEventRemoveHandle(watch);
}
@@ -810,10 +816,11 @@ static int lxcVmStart(virConnectPtr conn
vm->def->id = vm->pid;
vm->state = VIR_DOMAIN_RUNNING;
- if (virEventAddHandle(vm->monitor,
- VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
- lxcMonitorEvent,
- driver) < 0) {
+ if ((vm->monitorWatch = virEventAddHandle(
+ vm->monitor,
+ VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
+ lxcMonitorEvent,
+ driver)) < 0) {
lxcVmTerminate(conn, driver, vm, 0);
goto cleanup;
}
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -110,7 +110,8 @@ static void qemudDomainEventDispatch (st
int event,
int detail);
-static void qemudDispatchVMEvent(int fd,
+static void qemudDispatchVMEvent(int watch,
+ int fd,
int events,
void *opaque);
@@ -946,18 +947,18 @@ static int qemudStartVMDaemon(virConnect
}
if (ret == 0) {
- if ((virEventAddHandle(vm->stdout_fd,
- VIR_EVENT_HANDLE_READABLE |
- VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- qemudDispatchVMEvent,
- driver) < 0) ||
- (virEventAddHandle(vm->stderr_fd,
- VIR_EVENT_HANDLE_READABLE |
- VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- qemudDispatchVMEvent,
- driver) < 0) ||
+ if (((vm->stdout_watch = virEventAddHandle(vm->stdout_fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchVMEvent,
+ driver)) < 0) ||
+ ((vm->stderr_watch = virEventAddHandle(vm->stderr_fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchVMEvent,
+ driver)) < 0) ||
(qemudWaitForMonitor(conn, driver, vm) < 0) ||
(qemudDetectVcpuPIDs(conn, driver, vm) < 0) ||
(qemudInitCpus(conn, driver, vm, migrateFrom) < 0)) {
@@ -1008,8 +1009,8 @@ static void qemudShutdownVMDaemon(virCon
qemudVMData(driver, vm, vm->stdout_fd);
qemudVMData(driver, vm, vm->stderr_fd);
- virEventRemoveHandle(vm->stdout_fd);
- virEventRemoveHandle(vm->stderr_fd);
+ virEventRemoveHandle(vm->stdout_watch);
+ virEventRemoveHandle(vm->stderr_watch);
if (close(vm->logfile) < 0)
qemudLog(QEMUD_WARN, _("Unable to close logfile %d: %s\n"),
@@ -1072,15 +1073,15 @@ static int qemudDispatchVMFailure(struct
static void
-qemudDispatchVMEvent(int fd, int events, void *opaque) {
+qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) {
struct qemud_driver *driver = (struct qemud_driver *)opaque;
virDomainObjPtr vm = NULL;
unsigned int i;
for (i = 0 ; i < driver->domains.count ; i++) {
if (virDomainIsActive(driver->domains.objs[i]) &&
- (driver->domains.objs[i]->stdout_fd == fd ||
- driver->domains.objs[i]->stderr_fd == fd)) {
+ (driver->domains.objs[i]->stdout_watch == watch ||
+ driver->domains.objs[i]->stderr_watch == watch)) {
vm = driver->domains.objs[i];
break;
}
@@ -1088,6 +1089,12 @@ qemudDispatchVMEvent(int fd, int events,
if (!vm)
return;
+
+ if (vm->stdout_fd != fd &&
+ vm->stderr_fd != fd) {
+ qemudDispatchVMFailure(driver, vm, fd);
+ return;
+ }
if (events == VIR_EVENT_HANDLE_READABLE)
qemudDispatchVMLog(driver, vm, fd);
diff --git a/src/remote_internal.c b/src/remote_internal.c
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -95,6 +95,7 @@ struct private_data {
struct private_data {
int magic; /* Should be MAGIC or DEAD. */
int sock; /* Socket. */
+ int watch; /* File handle watch */
pid_t pid; /* PID of tunnel process */
int uses_tls; /* TLS enabled on socket? */
gnutls_session_t session; /* GnuTLS session (if uses_tls != 0). */
@@ -175,7 +176,7 @@ static void make_nonnull_network (remote
static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src);
static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src);
static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src);
-void remoteDomainEventFired(int fd, int event, void *data);
+void remoteDomainEventFired(int watch, int fd, int event, void *data);
static void remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr);
static void remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr);
void remoteDomainEventQueueFlush(int timer, void *opaque);
@@ -756,12 +757,12 @@ doRemoteOpen (virConnectPtr conn,
DEBUG0("Adding Handler for remote events");
/* Set up a callback to listen on the socket data */
- if (virEventAddHandle(priv->sock,
- VIR_EVENT_HANDLE_READABLE |
- VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- remoteDomainEventFired,
- conn) < 0) {
+ if ((priv->watch = virEventAddHandle(priv->sock,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ remoteDomainEventFired,
+ conn)) < 0) {
DEBUG0("virEventAddHandle failed: No addHandleImpl defined."
" continuing without events.");
} else {
@@ -5265,7 +5266,8 @@ remoteDomainQueueEvent(virConnectPtr con
* for event data
*/
void
-remoteDomainEventFired(int fd ATTRIBUTE_UNUSED,
+remoteDomainEventFired(int watch,
+ int fd,
int event,
void *opaque)
{
@@ -5278,13 +5280,18 @@ remoteDomainEventFired(int fd ATTRIBUTE_
virConnectPtr conn = opaque;
struct private_data *priv = conn->privateData;
- DEBUG("%s : Event fired %d %X", __FUNCTION__, event, event);
+ DEBUG("Event fired %d %d %d %X", watch, fd, event, event);
if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) {
DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or "
"VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__);
- virEventRemoveHandle(fd);
+ virEventRemoveHandle(watch);
return;
+ }
+
+ if (fd != priv->sock) {
+ virEventRemoveHandle(watch);
+ return;
}
/* Read and deserialise length word. */
--
|: 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 :|
3
3
[libvirt] PATCH: Pass a callback for freeing opaque data when registering handle/timer events
by Daniel P. Berrange 19 Nov '08
by Daniel P. Berrange 19 Nov '08
19 Nov '08
When registering for a file descriptor event, or timer events, the event
callback has an associated 'void *opaque' data blob. When removing a
registered event, the removal may be done asynchronously to allow safe
removal from within a callback. This means that it is not safe for the
application to assume they can free the 'void *opaque' data immediately
after calling virEventRemoveHandle/Timer. So, we extend the AddHandle/Timer
method to allow a 2nd callback to be provided. This callback is used to
free the 'void *opaque' data at the appropriate (safe) point in time.
examples/domain-events/events-c/event-test.c | 24 ++++++++++--
include/libvirt/libvirt.h | 52 ++++++++++++++++++++++++---
include/libvirt/libvirt.h.in | 52 ++++++++++++++++++++++++---
python/libvir.c | 25 +++++++++---
python/libvirt_wrap.h | 2 +
python/types.c | 32 ++++++++++++++++
qemud/event.c | 19 ++++++++-
qemud/event.h | 9 +++-
qemud/mdns.c | 22 +++++++++--
qemud/qemud.c | 9 ++++
src/event.c | 14 +++++--
src/event.h | 9 +++-
src/lxc_driver.c | 1
src/qemu_driver.c | 2 +
src/remote_internal.c | 2 +
15 files changed, 240 insertions(+), 34 deletions(-)
Daniel
diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c
--- a/examples/domain-events/events-c/event-test.c
+++ b/examples/domain-events/events-c/event-test.c
@@ -22,6 +22,7 @@ int h_fd = 0;
int h_fd = 0;
virEventHandleType h_event = 0;
virEventHandleCallback h_cb = NULL;
+virEventHandleFreeFunc h_ff = NULL;
void *h_opaque = NULL;
/* timeout globals */
@@ -29,6 +30,7 @@ int t_active = 0;
int t_active = 0;
int t_timeout = -1;
virEventTimeoutCallback t_cb = NULL;
+virEventTimeoutFreeFunc t_ff = NULL;
void *t_opaque = NULL;
@@ -39,11 +41,15 @@ int myDomainEventCallback2 (virConnectPt
int myDomainEventCallback2 (virConnectPtr conn, virDomainPtr dom,
int event, int detail, void *opaque);
int myEventAddHandleFunc (int fd, int event,
- virEventHandleCallback cb, void *opaque);
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
+ void *opaque);
void myEventUpdateHandleFunc(int watch, int event);
int myEventRemoveHandleFunc(int watch);
-int myEventAddTimeoutFunc(int timeout, virEventTimeoutCallback cb,
+int myEventAddTimeoutFunc(int timeout,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
void *opaque);
void myEventUpdateTimeoutFunc(int timer, int timout);
int myEventRemoveTimeoutFunc(int timer);
@@ -199,12 +205,15 @@ virEventHandleType myPollEventToEventHan
}
int myEventAddHandleFunc(int fd, int event,
- virEventHandleCallback cb, void *opaque)
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
+ void *opaque)
{
DEBUG("Add handle %d %d %p %p", fd, event, cb, opaque);
h_fd = fd;
h_event = myEventHandleTypeToPollEvent(event);
h_cb = cb;
+ h_ff = ff;
h_opaque = opaque;
return 0;
}
@@ -220,16 +229,21 @@ int myEventRemoveHandleFunc(int fd)
{
DEBUG("Removed Handle %d", fd);
h_fd = 0;
+ if (h_ff)
+ (h_ff)(h_opaque);
return 0;
}
-int myEventAddTimeoutFunc(int timeout, virEventTimeoutCallback cb,
+int myEventAddTimeoutFunc(int timeout,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
void *opaque)
{
DEBUG("Adding Timeout %d %p %p", timeout, cb, opaque);
t_active = 1;
t_timeout = timeout;
t_cb = cb;
+ t_ff = ff;
t_opaque = opaque;
return 0;
}
@@ -244,6 +258,8 @@ int myEventRemoveTimeoutFunc(int timer)
{
DEBUG("Timeout removed %d", timer);
t_active = 0;
+ if (t_ff)
+ (t_ff)(t_opaque);
return 0;
}
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -1132,21 +1132,39 @@ typedef void (*virEventHandleCallback)(i
typedef void (*virEventHandleCallback)(int watch, int fd, int events, void *opaque);
/**
+ * virEventHandleFreeFunc:
+ *
+ * @opaque: user data to be free'd
+ *
+ * Callback invoked when memory associated with a file handle
+ * watch is being freed. Will be passed a pointer to the application's
+ * opaque data blob.
+ */
+typedef void (*virEventHandleFreeFunc)(void *opaque);
+
+/**
* virEventAddHandleFunc:
* @fd: file descriptor to listen on
* @event: bitset of events on which to fire the callback
- * @cb: the callback to be called
+ * @cb: the callback to be called when an event occurrs
+ * @ff: the callback invoked to free opaque data blob
* @opaque: user data to pass to the callback
*
* Part of the EventImpl, this callback Adds a file handle callback to
* listen for specific events. The same file handle can be registered
* multiple times provided the requested event sets are non-overlapping
*
+ * If the opaque user data requires free'ing when the handle
+ * is unregistered, then a 2nd callback can be supplied for
+ * this purpose.
+ *
* Returns a handle watch number to be used for updating
* and unregistering for events
*/
typedef int (*virEventAddHandleFunc)(int fd, int event,
- virEventHandleCallback cb, void *opaque);
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
+ void *opaque);
/**
* virEventUpdateHandleFunc:
@@ -1163,7 +1181,11 @@ typedef void (*virEventUpdateHandleFunc)
* @watch: file descriptor watch to stop listening on
*
* Part of the EventImpl, this user-provided callback is notified when
- * an fd is no longer being listened on
+ * an fd is no longer being listened on.
+ *
+ * If a virEventHandleFreeFunc was supplied when the handle was
+ * registered, it will be invoked some time during, or after this
+ * function call, when it is safe to release the user data.
*/
typedef int (*virEventRemoveHandleFunc)(int watch);
@@ -1178,17 +1200,35 @@ typedef void (*virEventTimeoutCallback)(
typedef void (*virEventTimeoutCallback)(int timer, void *opaque);
/**
+ * virEventTimeoutFreeFunc:
+ *
+ * @opaque: user data to be free'd
+ *
+ * Callback invoked when memory associated with a timer
+ * is being freed. Will be passed a pointer to the application's
+ * opaque data blob.
+ */
+typedef void (*virEventTimeoutFreeFunc)(void *opaque);
+
+/**
* virEventAddTimeoutFunc:
* @timeout: The timeout to monitor
* @cb: the callback to call when timeout has expired
+ * @ff: the callback invoked to free opaque data blob
* @opaque: user data to pass to the callback
*
* Part of the EventImpl, this user-defined callback handles adding an
* event timeout.
*
+ * If the opaque user data requires free'ing when the handle
+ * is unregistered, then a 2nd callback can be supplied for
+ * this purpose.
+ *
* Returns a timer value
*/
-typedef int (*virEventAddTimeoutFunc)(int timeout, virEventTimeoutCallback cb,
+typedef int (*virEventAddTimeoutFunc)(int timeout,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
void *opaque);
/**
@@ -1207,6 +1247,10 @@ typedef void (*virEventUpdateTimeoutFunc
*
* Part of the EventImpl, this user-defined callback removes a timer
*
+ * If a virEventTimeoutFreeFunc was supplied when the handle was
+ * registered, it will be invoked some time during, or after this
+ * function call, when it is safe to release the user data.
+ *
* Returns 0 on success, -1 on failure
*/
typedef int (*virEventRemoveTimeoutFunc)(int timer);
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1132,21 +1132,39 @@ typedef void (*virEventHandleCallback)(i
typedef void (*virEventHandleCallback)(int watch, int fd, int events, void *opaque);
/**
+ * virEventHandleFreeFunc:
+ *
+ * @opaque: user data to be free'd
+ *
+ * Callback invoked when memory associated with a file handle
+ * watch is being freed. Will be passed a pointer to the application's
+ * opaque data blob.
+ */
+typedef void (*virEventHandleFreeFunc)(void *opaque);
+
+/**
* virEventAddHandleFunc:
* @fd: file descriptor to listen on
* @event: bitset of events on which to fire the callback
- * @cb: the callback to be called
+ * @cb: the callback to be called when an event occurrs
+ * @ff: the callback invoked to free opaque data blob
* @opaque: user data to pass to the callback
*
* Part of the EventImpl, this callback Adds a file handle callback to
* listen for specific events. The same file handle can be registered
* multiple times provided the requested event sets are non-overlapping
*
+ * If the opaque user data requires free'ing when the handle
+ * is unregistered, then a 2nd callback can be supplied for
+ * this purpose.
+ *
* Returns a handle watch number to be used for updating
* and unregistering for events
*/
typedef int (*virEventAddHandleFunc)(int fd, int event,
- virEventHandleCallback cb, void *opaque);
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
+ void *opaque);
/**
* virEventUpdateHandleFunc:
@@ -1163,7 +1181,11 @@ typedef void (*virEventUpdateHandleFunc)
* @watch: file descriptor watch to stop listening on
*
* Part of the EventImpl, this user-provided callback is notified when
- * an fd is no longer being listened on
+ * an fd is no longer being listened on.
+ *
+ * If a virEventHandleFreeFunc was supplied when the handle was
+ * registered, it will be invoked some time during, or after this
+ * function call, when it is safe to release the user data.
*/
typedef int (*virEventRemoveHandleFunc)(int watch);
@@ -1178,17 +1200,35 @@ typedef void (*virEventTimeoutCallback)(
typedef void (*virEventTimeoutCallback)(int timer, void *opaque);
/**
+ * virEventTimeoutFreeFunc:
+ *
+ * @opaque: user data to be free'd
+ *
+ * Callback invoked when memory associated with a timer
+ * is being freed. Will be passed a pointer to the application's
+ * opaque data blob.
+ */
+typedef void (*virEventTimeoutFreeFunc)(void *opaque);
+
+/**
* virEventAddTimeoutFunc:
* @timeout: The timeout to monitor
* @cb: the callback to call when timeout has expired
+ * @ff: the callback invoked to free opaque data blob
* @opaque: user data to pass to the callback
*
* Part of the EventImpl, this user-defined callback handles adding an
* event timeout.
*
+ * If the opaque user data requires free'ing when the handle
+ * is unregistered, then a 2nd callback can be supplied for
+ * this purpose.
+ *
* Returns a timer value
*/
-typedef int (*virEventAddTimeoutFunc)(int timeout, virEventTimeoutCallback cb,
+typedef int (*virEventAddTimeoutFunc)(int timeout,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
void *opaque);
/**
@@ -1207,6 +1247,10 @@ typedef void (*virEventUpdateTimeoutFunc
*
* Part of the EventImpl, this user-defined callback removes a timer
*
+ * If a virEventTimeoutFreeFunc was supplied when the handle was
+ * registered, it will be invoked some time during, or after this
+ * function call, when it is safe to release the user data.
+ *
* Returns 0 on success, -1 on failure
*/
typedef int (*virEventRemoveTimeoutFunc)(int timer);
diff --git a/python/libvir.c b/python/libvir.c
--- a/python/libvir.c
+++ b/python/libvir.c
@@ -1704,12 +1704,16 @@ static PyObject *removeTimeoutObj = NULL
static int
-libvirt_virEventAddHandleFunc (int fd ATTRIBUTE_UNUSED, int event ATTRIBUTE_UNUSED,
- virEventHandleCallback cb, void *opaque)
+libvirt_virEventAddHandleFunc (int fd,
+ int event,
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
+ void *opaque)
{
PyObject *result = NULL;
PyObject *python_cb;
PyObject *cb_obj;
+ PyObject *ff_obj;
PyObject *opaque_obj;
PyObject *cb_args;
PyObject *pyobj_args;
@@ -1730,11 +1734,13 @@ libvirt_virEventAddHandleFunc (int fd A
/* create tuple for cb */
cb_obj = libvirt_virEventHandleCallbackWrap(cb);
+ ff_obj = libvirt_virEventHandleFreeFuncWrap(ff);
opaque_obj = libvirt_virVoidPtrWrap(opaque);
- cb_args = PyTuple_New(2);
+ cb_args = PyTuple_New(3);
PyTuple_SetItem(cb_args, 0, cb_obj);
- PyTuple_SetItem(cb_args, 1, opaque_obj);
+ PyTuple_SetItem(cb_args, 1, ff_obj);
+ PyTuple_SetItem(cb_args, 2, opaque_obj);
pyobj_args = PyTuple_New(4);
PyTuple_SetItem(pyobj_args, 0, libvirt_intWrap(fd));
@@ -1799,7 +1805,9 @@ libvirt_virEventRemoveHandleFunc(int fd)
}
static int
-libvirt_virEventAddTimeoutFunc(int timeout, virEventTimeoutCallback cb,
+libvirt_virEventAddTimeoutFunc(int timeout,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
void *opaque)
{
PyObject *result = NULL;
@@ -1807,6 +1815,7 @@ libvirt_virEventAddTimeoutFunc(int timeo
PyObject *python_cb;
PyObject *cb_obj;
+ PyObject *ff_obj;
PyObject *opaque_obj;
PyObject *cb_args;
PyObject *pyobj_args;
@@ -1827,11 +1836,13 @@ libvirt_virEventAddTimeoutFunc(int timeo
/* create tuple for cb */
cb_obj = libvirt_virEventTimeoutCallbackWrap(cb);
+ ff_obj = libvirt_virEventTimeoutFreeFuncWrap(ff);
opaque_obj = libvirt_virVoidPtrWrap(opaque);
- cb_args = PyTuple_New(2);
+ cb_args = PyTuple_New(3);
PyTuple_SetItem(cb_args, 0, cb_obj);
- PyTuple_SetItem(cb_args, 1, opaque_obj);
+ PyTuple_SetItem(cb_args, 1, ff_obj);
+ PyTuple_SetItem(cb_args, 2, opaque_obj);
pyobj_args = PyTuple_New(3);
diff --git a/python/libvirt_wrap.h b/python/libvirt_wrap.h
--- a/python/libvirt_wrap.h
+++ b/python/libvirt_wrap.h
@@ -103,6 +103,8 @@ PyObject * libvirt_virStorageVolPtrWrap(
PyObject * libvirt_virStorageVolPtrWrap(virStorageVolPtr node);
PyObject * libvirt_virEventHandleCallbackWrap(virEventHandleCallback node);
PyObject * libvirt_virEventTimeoutCallbackWrap(virEventTimeoutCallback node);
+PyObject * libvirt_virEventHandleFreeFuncWrap(virEventHandleFreeFunc node);
+PyObject * libvirt_virEventTimeoutFreeFuncWrap(virEventTimeoutFreeFunc node);
PyObject * libvirt_virVoidPtrWrap(void* node);
/* Provide simple macro statement wrappers (adapted from GLib, in turn from Perl):
diff --git a/python/types.c b/python/types.c
--- a/python/types.c
+++ b/python/types.c
@@ -196,6 +196,38 @@ libvirt_virEventTimeoutCallbackWrap(virE
}
PyObject *
+libvirt_virEventHandleFreeFuncWrap(virEventHandleFreeFunc node)
+{
+ PyObject *ret;
+
+ if (node == NULL) {
+ Py_INCREF(Py_None);
+ printf("%s: WARNING - Wrapping None\n", __FUNCTION__);
+ return (Py_None);
+ }
+ ret =
+ PyCObject_FromVoidPtrAndDesc((void *) node, (char *) "virEventHandleFreeFunc",
+ NULL);
+ return (ret);
+}
+
+PyObject *
+libvirt_virEventTimeoutFreeFuncWrap(virEventTimeoutFreeFunc node)
+{
+ PyObject *ret;
+
+ if (node == NULL) {
+ printf("%s: WARNING - Wrapping None\n", __FUNCTION__);
+ Py_INCREF(Py_None);
+ return (Py_None);
+ }
+ ret =
+ PyCObject_FromVoidPtrAndDesc((void *) node, (char *) "virEventTimeoutFreeFunc",
+ NULL);
+ return (ret);
+}
+
+PyObject *
libvirt_virVoidPtrWrap(void* node)
{
PyObject *ret;
diff --git a/qemud/event.c b/qemud/event.c
--- a/qemud/event.c
+++ b/qemud/event.c
@@ -41,6 +41,7 @@ struct virEventHandle {
int fd;
int events;
virEventHandleCallback cb;
+ virEventHandleFreeFunc ff;
void *opaque;
int deleted;
};
@@ -51,6 +52,7 @@ struct virEventTimeout {
int frequency;
unsigned long long expiresAt;
virEventTimeoutCallback cb;
+ virEventTimeoutFreeFunc ff;
void *opaque;
int deleted;
};
@@ -83,7 +85,9 @@ static int nextTimer = 0;
* NB, it *must* be safe to call this from within a callback
* For this reason we only ever append to existing list.
*/
-int virEventAddHandleImpl(int fd, int events, virEventHandleCallback cb,
+int virEventAddHandleImpl(int fd, int events,
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
void *opaque) {
EVENT_DEBUG("Add handle %d %d %p %p", fd, events, cb, opaque);
if (eventLoop.handlesCount == eventLoop.handlesAlloc) {
@@ -100,6 +104,7 @@ int virEventAddHandleImpl(int fd, int ev
eventLoop.handles[eventLoop.handlesCount].events =
virEventHandleTypeToPollEvent(events);
eventLoop.handles[eventLoop.handlesCount].cb = cb;
+ eventLoop.handles[eventLoop.handlesCount].ff = ff;
eventLoop.handles[eventLoop.handlesCount].opaque = opaque;
eventLoop.handles[eventLoop.handlesCount].deleted = 0;
@@ -147,7 +152,10 @@ int virEventRemoveHandleImpl(int watch)
* NB, it *must* be safe to call this from within a callback
* For this reason we only ever append to existing list.
*/
-int virEventAddTimeoutImpl(int frequency, virEventTimeoutCallback cb, void *opaque) {
+int virEventAddTimeoutImpl(int frequency,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
+ void *opaque) {
struct timeval now;
EVENT_DEBUG("Adding timer %d with %d ms freq", nextTimer, frequency);
if (gettimeofday(&now, NULL) < 0) {
@@ -166,6 +174,7 @@ int virEventAddTimeoutImpl(int frequency
eventLoop.timeouts[eventLoop.timeoutsCount].timer = nextTimer++;
eventLoop.timeouts[eventLoop.timeoutsCount].frequency = frequency;
eventLoop.timeouts[eventLoop.timeoutsCount].cb = cb;
+ eventLoop.timeouts[eventLoop.timeoutsCount].ff = ff;
eventLoop.timeouts[eventLoop.timeoutsCount].opaque = opaque;
eventLoop.timeouts[eventLoop.timeoutsCount].deleted = 0;
eventLoop.timeouts[eventLoop.timeoutsCount].expiresAt =
@@ -393,6 +402,9 @@ static int virEventCleanupTimeouts(void)
}
EVENT_DEBUG("Purging timeout %d with id %d", i, eventLoop.timeouts[i].timer);
+ if (eventLoop.timeouts[i].ff)
+ (eventLoop.timeouts[i].ff)(eventLoop.timeouts[i].opaque);
+
if ((i+1) < eventLoop.timeoutsCount) {
memmove(eventLoop.timeouts+i,
eventLoop.timeouts+i+1,
@@ -428,6 +440,9 @@ static int virEventCleanupHandles(void)
i++;
continue;
}
+
+ if (eventLoop.handles[i].ff)
+ (eventLoop.handles[i].ff)(eventLoop.handles[i].opaque);
if ((i+1) < eventLoop.handlesCount) {
memmove(eventLoop.handles+i,
diff --git a/qemud/event.h b/qemud/event.h
--- a/qemud/event.h
+++ b/qemud/event.h
@@ -36,7 +36,9 @@
*
* returns -1 if the file handle cannot be registered, 0 upon success
*/
-int virEventAddHandleImpl(int fd, int events, virEventHandleCallback cb,
+int virEventAddHandleImpl(int fd, int events,
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
void *opaque);
/**
@@ -71,7 +73,10 @@ int virEventRemoveHandleImpl(int watch);
* returns -1 if the file handle cannot be registered, a positive
* integer timer id upon success
*/
-int virEventAddTimeoutImpl(int frequency, virEventTimeoutCallback cb, void *opaque);
+int virEventAddTimeoutImpl(int frequency,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
+ void *opaque);
/**
* virEventUpdateTimeoutImpl: change frequency for a timer
diff --git a/qemud/mdns.c b/qemud/mdns.c
--- a/qemud/mdns.c
+++ b/qemud/mdns.c
@@ -238,6 +238,12 @@ static void libvirtd_mdns_watch_dispatch
w->callback(w, fd, fd_events, w->userdata);
}
+static void libvirtd_mdns_watch_dofree(void *w)
+{
+ VIR_FREE(w);
+}
+
+
static AvahiWatch *libvirtd_mdns_watch_new(const AvahiPoll *api ATTRIBUTE_UNUSED,
int fd, AvahiWatchEvent event,
AvahiWatchCallback cb, void *userdata) {
@@ -254,7 +260,9 @@ static AvahiWatch *libvirtd_mdns_watch_n
AVAHI_DEBUG("New handle %p FD %d Event %d", w, w->fd, event);
hEvents = virPollEventToEventHandleType(event);
if ((w->watch = virEventAddHandleImpl(fd, hEvents,
- libvirtd_mdns_watch_dispatch, w)) < 0) {
+ libvirtd_mdns_watch_dispatch,
+ libvirtd_mdns_watch_dofree,
+ w)) < 0) {
VIR_FREE(w);
return NULL;
}
@@ -278,7 +286,6 @@ static void libvirtd_mdns_watch_free(Ava
{
AVAHI_DEBUG("Free handle %p %d", w, w->fd);
virEventRemoveHandleImpl(w->watch);
- VIR_FREE(w);
}
static void libvirtd_mdns_timeout_dispatch(int timer ATTRIBUTE_UNUSED, void *opaque)
@@ -287,6 +294,11 @@ static void libvirtd_mdns_timeout_dispat
AVAHI_DEBUG("Dispatch timeout %p %d", t, timer);
virEventUpdateTimeoutImpl(t->timer, -1);
t->callback(t, t->userdata);
+}
+
+static void libvirtd_mdns_timeout_dofree(void *t)
+{
+ VIR_FREE(t);
}
static AvahiTimeout *libvirtd_mdns_timeout_new(const AvahiPoll *api ATTRIBUTE_UNUSED,
@@ -319,7 +331,10 @@ static AvahiTimeout *libvirtd_mdns_timeo
timeout = -1;
}
- t->timer = virEventAddTimeoutImpl(timeout, libvirtd_mdns_timeout_dispatch, t);
+ t->timer = virEventAddTimeoutImpl(timeout,
+ libvirtd_mdns_timeout_dispatch,
+ libvirtd_mdns_timeout_dofree,
+ t);
t->callback = cb;
t->userdata = userdata;
@@ -358,7 +373,6 @@ static void libvirtd_mdns_timeout_free(A
{
AVAHI_DEBUG("Free timeout %p", t);
virEventRemoveTimeoutImpl(t->timer);
- VIR_FREE(t);
}
diff --git a/qemud/qemud.c b/qemud/qemud.c
--- a/qemud/qemud.c
+++ b/qemud/qemud.c
@@ -540,6 +540,7 @@ static int qemudListenUnix(struct qemud_
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
qemudDispatchServerEvent,
+ NULL,
server)) < 0) {
qemudLog(QEMUD_ERR, "%s",
_("Failed to add server event callback"));
@@ -672,6 +673,7 @@ remoteListenTCP (struct qemud_server *se
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
qemudDispatchServerEvent,
+ NULL,
server)) < 0) {
qemudLog(QEMUD_ERR, "%s", _("Failed to add server event callback"));
goto cleanup;
@@ -1655,6 +1657,7 @@ static int qemudRegisterClientEvent(stru
mode | VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
qemudDispatchClientEvent,
+ NULL,
server)) < 0)
return -1;
@@ -1721,7 +1724,10 @@ static int qemudRunLoop(struct qemud_ser
* shutdown after timeout seconds
*/
if (timeout > 0 && !virStateActive() && !server->clients) {
- timerid = virEventAddTimeoutImpl(timeout*1000, qemudInactiveTimer, server);
+ timerid = virEventAddTimeoutImpl(timeout*1000,
+ qemudInactiveTimer,
+ NULL,
+ server);
qemudDebug("Scheduling shutdown timer %d", timerid);
}
@@ -2270,6 +2276,7 @@ int main(int argc, char **argv) {
if (virEventAddHandleImpl(sigpipe[0],
VIR_EVENT_HANDLE_READABLE,
qemudDispatchSignalEvent,
+ NULL,
server) < 0) {
qemudLog(QEMUD_ERR,
"%s", _("Failed to register callback for signal pipe"));
diff --git a/src/event.c b/src/event.c
--- a/src/event.c
+++ b/src/event.c
@@ -34,12 +34,15 @@ static virEventUpdateTimeoutFunc updateT
static virEventUpdateTimeoutFunc updateTimeoutImpl = NULL;
static virEventRemoveTimeoutFunc removeTimeoutImpl = NULL;
-int virEventAddHandle(int fd, int events, virEventHandleCallback cb,
+int virEventAddHandle(int fd,
+ int events,
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
void *opaque) {
if (!addHandleImpl)
return -1;
- return addHandleImpl(fd, events, cb, opaque);
+ return addHandleImpl(fd, events, cb, ff, opaque);
}
void virEventUpdateHandle(int watch, int events) {
@@ -53,11 +56,14 @@ int virEventRemoveHandle(int watch) {
return removeHandleImpl(watch);
}
-int virEventAddTimeout(int timeout, virEventTimeoutCallback cb, void *opaque) {
+int virEventAddTimeout(int timeout,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
+ void *opaque) {
if (!addTimeoutImpl)
return -1;
- return addTimeoutImpl(timeout, cb, opaque);
+ return addTimeoutImpl(timeout, cb, ff, opaque);
}
void virEventUpdateTimeout(int timer, int timeout) {
diff --git a/src/event.h b/src/event.h
--- a/src/event.h
+++ b/src/event.h
@@ -34,7 +34,9 @@
*
* returns -1 if the file handle cannot be registered, 0 upon success
*/
-int virEventAddHandle(int fd, int events, virEventHandleCallback cb,
+int virEventAddHandle(int fd, int events,
+ virEventHandleCallback cb,
+ virEventHandleFreeFunc ff,
void *opaque);
/**
@@ -69,7 +71,10 @@ int virEventRemoveHandle(int watch);
* returns -1 if the file handle cannot be registered, a positive
* integer timer id upon success
*/
-int virEventAddTimeout(int frequency, virEventTimeoutCallback cb, void *opaque);
+int virEventAddTimeout(int frequency,
+ virEventTimeoutCallback cb,
+ virEventTimeoutFreeFunc ff,
+ void *opaque);
/**
* virEventUpdateTimeoutImpl: change frequency for a timer
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -820,6 +820,7 @@ static int lxcVmStart(virConnectPtr conn
vm->monitor,
VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
lxcMonitorEvent,
+ NULL,
driver)) < 0) {
lxcVmTerminate(conn, driver, vm, 0);
goto cleanup;
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -952,12 +952,14 @@ static int qemudStartVMDaemon(virConnect
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
qemudDispatchVMEvent,
+ NULL,
driver)) < 0) ||
((vm->stderr_watch = virEventAddHandle(vm->stderr_fd,
VIR_EVENT_HANDLE_READABLE |
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
qemudDispatchVMEvent,
+ NULL,
driver)) < 0) ||
(qemudWaitForMonitor(conn, driver, vm) < 0) ||
(qemudDetectVcpuPIDs(conn, driver, vm) < 0) ||
diff --git a/src/remote_internal.c b/src/remote_internal.c
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -762,6 +762,7 @@ doRemoteOpen (virConnectPtr conn,
VIR_EVENT_HANDLE_ERROR |
VIR_EVENT_HANDLE_HANGUP,
remoteDomainEventFired,
+ NULL,
conn)) < 0) {
DEBUG0("virEventAddHandle failed: No addHandleImpl defined."
" continuing without events.");
@@ -770,6 +771,7 @@ doRemoteOpen (virConnectPtr conn,
DEBUG0("Adding Timeout for remote event queue flushing");
if ( (priv->eventFlushTimer = virEventAddTimeout(-1,
remoteDomainEventQueueFlush,
+ NULL,
conn)) < 0) {
DEBUG0("virEventAddTimeout failed: No addTimeoutImpl defined. "
"continuing without events.");
--
|: 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 :|
3
2
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@example.com/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(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,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(a)redhat.com>
+ */
+
+#ifndef __UML_CONF_H
+#define __UML_CONF_H
+
+#include "internal.h"
+#include "bridge.h"
+#include "capabilities.h"
+#include "network_conf.h"
+#include "domain_conf.h"
+#include "virterror_internal.h"
+
+#define umlDebug(fmt, ...) do {} while(0)
+
+#define UML_CPUMASK_LEN CPU_SETSIZE
+
+/* 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(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"
+#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(a)redhat.com>
+ */
+
+
+#ifndef UML_DRIVER_H
+#define UML_DRIVER_H
+
+#include "internal.h"
+
+int umlRegister(void);
+
+#endif /* UML_DRIVER_H */
diff --git a/src/virterror.c b/src/virterror.c
--- 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 :|
2
3
Below is a simple PoC for setting a large MTU size on a tap device.
With this we are able to improve net i/o throughput substantially (~40%
improvement on TX and ~130% improvement on RX). This is just RFC because
it's hardcoded to an MTU of 9000 for any tap device. Thoughts on the
best way to add this kind of support?
diff -up libvirt-0.4.6/src/bridge.c~tap-mtu libvirt-0.4.6/src/bridge.c
--- libvirt-0.4.6/src/bridge.c~tap-mtu 2008-08-29 05:19:52.000000000 -0700
+++ libvirt-0.4.6/src/bridge.c 2008-11-17 18:37:56.000000000 -0800
@@ -276,6 +276,38 @@ brDeleteInterface(brControl *ctl ATTRIBU
#endif
/**
+ * ifSetMtu:
+ * @ctl: bridge control pointer
+ * @ifname: interface name to set MTU for
+ * @mtu: MTU value
+ *
+ * This function sets the @mtu for a given interface @ifname. Typically
+ * used on a tap device to set up for Jumbo Frames.
+ *
+ * Returns 0 in case of success or an errno code in case of failure.
+ */
+static int
+ifSetMtu(brControl *ctl, const char *ifname, int mtu)
+{
+ struct ifreq ifr;
+ int len;
+
+ if (!ctl || !ifname)
+ return EINVAL;
+
+ if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN)
+ return EINVAL;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+
+ strncpy(ifr.ifr_name, ifname, len);
+ ifr.ifr_name[len] = '\0';
+ ifr.ifr_mtu = mtu;
+
+ return ioctl(ctl->fd, SIOCSIFMTU, &ifr) == 0 ? 0 : errno;
+}
+
+/**
* brAddTap:
* @ctl: bridge control pointer
* @bridge: the bridge name
@@ -334,6 +366,8 @@ brAddTap(brControl *ctl,
}
if (ioctl(fd, TUNSETIFF, &try) == 0) {
+ if ((errno = ifSetMtu(ctl, try.ifr_name, 9000)))
+ goto error;
if ((errno = brAddInterface(ctl, bridge, try.ifr_name)))
goto error;
if ((errno = brSetInterfaceUp(ctl, try.ifr_name, 1)))
4
3
This patch allows the remote driver to work with an asynchronous
EventImpl (it's the only one using an externally-supplied one), assuming
libvirt is compiled with pthread support. (Without pthreads, this code
is harmless in a single-threaded environment.)
Basically it uses a mutex to protect reads from the RPC socket in such a
way that message reads (in their entirety) are done atomically
(otherwise the remoteDomainEventFired() can race the call() code that
reads replies & events).
In addition, I update the EventImpl handle to prevent
remoteDomainEventFired() from being called everytime a reply is sent.
(This helps us dispatch events in a timely manner, though it's not
strictly necessary. Without it, any events coming in during a call()
won't be dispatched until the call drops the socket lock (because
remoteDomainEventFired() will be stuck awaiting the lock).
Dave
2
2
18 Nov '08
I am pleased to announce the release of version 0.1.0 of the
ruby-libvirt bindings.
NEWS:
* Add binding for virConnectFindStoragePoolSources (clalance)
* Fix dom_migrate (clalance)
* Add the MIGRATE_LIVE (enum virDomainMigrateFlags) flag
* Slight improvements of the unit tests
The main site for the bindings is http://libvirt.org/ruby
API docs can be found at http://libvirt.org/ruby/api/index.html
Tarballs/src RPM's are at http://libvirt.org/ruby/download
Packages for Fedora and EPEL5 are in the works and should show up on a
friendly mirror near you soon.
David
1
0
17 Nov '08
This patch adds support for the DEFINED and UNDEFINED events in the
QEMU drive. This was more involved than I expected, because when the
daemon gets SIGHUP it re-loads config files and we need to broadcast
events for each new config file it finds. So I had to add a callback
to the internal virDomainLoadAllConfigs method.
The LoadConfig method also had a bogus line of code resetting the
domain state to SHUTOFF, which made all your active VMs suddenlly
appear inactive, after the daemon got SIGHUP !
Daniel
diff --git a/src/domain_conf.c b/src/domain_conf.c
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -3242,18 +3242,20 @@ virDomainObjPtr virDomainLoadConfig(virC
virDomainObjListPtr doms,
const char *configDir,
const char *autostartDir,
- const char *name)
+ const char *name,
+ virDomainLoadConfigNotify notify,
+ void *opaque)
{
char *configFile = NULL, *autostartLink = NULL;
virDomainDefPtr def = NULL;
virDomainObjPtr dom;
int autostart;
+ int newVM = 1;
if ((configFile = virDomainConfigFile(conn, configDir, name)) == NULL)
goto error;
if ((autostartLink = virDomainConfigFile(conn, autostartDir, name)) == NULL)
goto error;
-
if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
goto error;
@@ -3261,11 +3263,16 @@ virDomainObjPtr virDomainLoadConfig(virC
if (!(def = virDomainDefParseFile(conn, caps, configFile)))
goto error;
+ if (virDomainFindByName(doms, def->name))
+ newVM = 0;
+
if (!(dom = virDomainAssignDef(conn, doms, def)))
goto error;
- dom->state = VIR_DOMAIN_SHUTOFF;
dom->autostart = autostart;
+
+ if (notify)
+ (*notify)(dom, newVM, opaque);
return dom;
@@ -3280,7 +3287,9 @@ int virDomainLoadAllConfigs(virConnectPt
virCapsPtr caps,
virDomainObjListPtr doms,
const char *configDir,
- const char *autostartDir)
+ const char *autostartDir,
+ virDomainLoadConfigNotify notify,
+ void *opaque)
{
DIR *dir;
struct dirent *entry;
@@ -3310,7 +3319,9 @@ int virDomainLoadAllConfigs(virConnectPt
doms,
configDir,
autostartDir,
- entry->d_name);
+ entry->d_name,
+ notify,
+ opaque);
if (dom)
dom->persistent = 1;
}
diff --git a/src/domain_conf.h b/src/domain_conf.h
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -549,18 +549,26 @@ int virDomainSaveConfig(virConnectPtr co
const char *configDir,
virDomainDefPtr def);
+typedef void (*virDomainLoadConfigNotify)(virDomainObjPtr dom,
+ int newDomain,
+ void *opaque);
+
virDomainObjPtr virDomainLoadConfig(virConnectPtr conn,
virCapsPtr caps,
virDomainObjListPtr doms,
const char *configDir,
const char *autostartDir,
- const char *name);
+ const char *name,
+ virDomainLoadConfigNotify notify,
+ void *opaque);
int virDomainLoadAllConfigs(virConnectPtr conn,
virCapsPtr caps,
virDomainObjListPtr doms,
const char *configDir,
- const char *autostartDir);
+ const char *autostartDir,
+ virDomainLoadConfigNotify notify,
+ void *opaque);
int virDomainDeleteConfig(virConnectPtr conn,
const char *configDir,
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -1017,7 +1017,8 @@ static int lxcStartup(void)
lxc_driver->caps,
&lxc_driver->domains,
lxc_driver->configDir,
- lxc_driver->autostartDir) < 0) {
+ lxc_driver->autostartDir,
+ NULL, NULL) < 0) {
lxcShutdown();
return -1;
}
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -226,7 +226,8 @@ qemudStartup(void) {
qemu_driver->caps,
&qemu_driver->domains,
qemu_driver->configDir,
- qemu_driver->autostartDir) < 0) {
+ qemu_driver->autostartDir,
+ NULL, NULL) < 0) {
qemudShutdown();
return -1;
}
@@ -241,6 +242,16 @@ qemudStartup(void) {
VIR_FREE(base);
VIR_FREE(qemu_driver);
return -1;
+}
+
+static void qemudNotifyLoadDomain(virDomainObjPtr vm, int newVM, void *opaque)
+{
+ struct qemud_driver *driver = opaque;
+
+ if (newVM)
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_DEFINED,
+ VIR_DOMAIN_EVENT_DEFINED_ADDED);
}
/**
@@ -258,7 +269,8 @@ qemudReload(void) {
qemu_driver->caps,
&qemu_driver->domains,
qemu_driver->configDir,
- qemu_driver->autostartDir);
+ qemu_driver->autostartDir,
+ qemudNotifyLoadDomain, qemu_driver);
qemudAutostartConfigs(qemu_driver);
@@ -2356,9 +2368,14 @@ static virDomainPtr qemudDomainDefine(vi
virDomainDefPtr def;
virDomainObjPtr vm;
virDomainPtr dom;
+ int newVM = 1;
if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
return NULL;
+
+ vm = virDomainFindByName(&driver->domains, def->name);
+ if (vm)
+ newVM = 0;
if (!(vm = virDomainAssignDef(conn,
&driver->domains,
@@ -2375,6 +2392,12 @@ static virDomainPtr qemudDomainDefine(vi
vm);
return NULL;
}
+
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_DEFINED,
+ newVM ?
+ VIR_DOMAIN_EVENT_DEFINED_ADDED :
+ VIR_DOMAIN_EVENT_DEFINED_UPDATED);
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom) dom->id = vm->def->id;
@@ -2405,6 +2428,8 @@ static int qemudDomainUndefine(virDomain
if (virDomainDeleteConfig(dom->conn, driver->configDir, driver->autostartDir, vm) < 0)
return -1;
+
+ qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_UNDEFINED, 0);
virDomainRemoveInactive(&driver->domains,
vm);
--
|: 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 :|
2
2
As per our earlier discussion today, this patch expands the callback for
domain events so that it also gets a event type specific 'detail' field.
This is also kept as an int, and we define enumerations for the possible
values associated with each type. If a event type has no detail, 0 is
passed.
The RESTORED and SAVED event types disappear in this patch and just become
another piece of 'detail' to the STOPPED and STARTED events. I have also
renamed ADDED & REMOVED to DEFINED and UNDEFINED to match terminology we
have elsewhere & because the names were confusing me
Easiest to just look at the final set:
typedef enum {
VIR_DOMAIN_EVENT_DEFINED = 0,
VIR_DOMAIN_EVENT_UNDEFINED = 1,
VIR_DOMAIN_EVENT_STARTED = 2,
VIR_DOMAIN_EVENT_SUSPENDED = 3,
VIR_DOMAIN_EVENT_RESUMED = 4,
VIR_DOMAIN_EVENT_STOPPED = 5,
} virDomainEventType;
typedef enum {
VIR_DOMAIN_EVENT_STARTED_BOOTED = 0, /* Normal startup from boot */
VIR_DOMAIN_EVENT_STARTED_MIGRATED = 1, /* Incoming migration from another host */
VIR_DOMAIN_EVENT_STARTED_RESTORED = 2, /* Restored from a state file */
} virDomainEventStartedDetailType;
typedef enum {
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN = 0, /* Normal shutdown */
VIR_DOMAIN_EVENT_STOPPED_DESTROYED = 1, /* Forced poweroff from host */
VIR_DOMAIN_EVENT_STOPPED_CRASHED = 2, /* Guest crashed */
VIR_DOMAIN_EVENT_STOPPED_MIGRATED = 3, /* Migrated off to another host */
VIR_DOMAIN_EVENT_STOPPED_SAVED = 4, /* Saved to a state file */
VIR_DOMAIN_EVENT_STOPPED_FAILED = 5, /* Host emulator/mgmt failed */
} virDomainEventStoppedDetailType;
typedef enum {
VIR_DOMAIN_EVENT_DEFINED_ADDED = 0, /* Newly created config file */
VIR_DOMAIN_EVENT_DEFINED_UPDATED = 1, /* Changed config file */
} virDomainEventDefinedDetailType;
I don't make use of 'CRASHED' in QEMU driver yet. It might be useful in
Xen though - when a PV guest crashes, Xen stops the domain running, but
leaves it there in a shutoff state, but marked as crashed.
Now using the C event-test program you can see the effects:
myDomainEventCallback1 EVENT: Domain F9x86_64(2) Started Booted
myDomainEventCallback2 EVENT: Domain F9x86_64(2) Started Booted
myDomainEventCallback1 EVENT: Domain F9x86_64(-1) Stopped Destroyed
myDomainEventCallback2 EVENT: Domain F9x86_64(-1) Stopped Destroyed
myDomainEventCallback1 EVENT: Domain F9x86_64(3) Started Booted
myDomainEventCallback2 EVENT: Domain F9x86_64(3) Started Booted
myDomainEventCallback1 EVENT: Domain F9x86_64(3) Suspended
myDomainEventCallback2 EVENT: Domain F9x86_64(3) Suspended
myDomainEventCallback1 EVENT: Domain F9x86_64(3) Resumed
myDomainEventCallback2 EVENT: Domain F9x86_64(3) Resumed
myDomainEventCallback1 EVENT: Domain F9x86_64(-1) Stopped Shutdown
myDomainEventCallback2 EVENT: Domain F9x86_64(-1) Stopped Shutdown
Of the following sequence of actions
virsh start F9x86_64
virsh destroy F9x86_64
virsh start F9x86_64
virsh suspend F9x86_64
virsh resume F9x86_64
virsh shutdown F9x86_64
For the last 'shutdown' operation, you'll see the same if you just run
a graceful shutdown inside the guest itself.
NB, I've not tested saved/restored because my install of KVM is not new
enough to support that correctly, but I expect it to work without trouble.
Likewise for migration.
A word about migration...
- The destination host first gets a STARTED event, with detail MIGRATED
when it starts running
- The source host then gets a STOPPED event with detail MIGRATED when
it completes
- The destination host then gets a RESUMED event, on success, and
a STOPPED event with detail FAILED if migration aborts.
Daniel
examples/domain-events/events-c/event-test.c | 78 +++++++++++++++----
examples/domain-events/events-python/event-test.py | 8 +-
include/libvirt/libvirt.h | 29 ++++++-
include/libvirt/libvirt.h.in | 29 ++++++-
python/libvir.c | 6 +
qemud/qemud.h | 1
qemud/remote.c | 22 +++--
qemud/remote_protocol.c | 2
qemud/remote_protocol.h | 1
qemud/remote_protocol.x | 1
src/domain_event.c | 4 -
src/domain_event.h | 6 +
src/qemu_driver.c | 82 +++++++++++++++------
src/remote_internal.c | 29 ++++---
18 files changed, 366 insertions(+), 118 deletions(-)
diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c
--- a/examples/domain-events/events-c/event-test.c
+++ b/examples/domain-events/events-c/event-test.c
@@ -35,9 +35,9 @@ void *t_opaque = NULL;
/* Prototypes */
const char *eventToString(int event);
int myDomainEventCallback1 (virConnectPtr conn, virDomainPtr dom,
- int event, void *opaque);
+ int event, int detail, void *opaque);
int myDomainEventCallback2 (virConnectPtr conn, virDomainPtr dom,
- int event, void *opaque);
+ int event, int detail, void *opaque);
int myEventAddHandleFunc (int fd, int event,
virEventHandleCallback cb, void *opaque);
void myEventUpdateHandleFunc(int fd, int event);
@@ -58,11 +58,11 @@ const char *eventToString(int event) {
const char *eventToString(int event) {
const char *ret = NULL;
switch(event) {
- case VIR_DOMAIN_EVENT_ADDED:
- ret ="Added";
+ case VIR_DOMAIN_EVENT_DEFINED:
+ ret ="Defined";
break;
- case VIR_DOMAIN_EVENT_REMOVED:
- ret ="Removed";
+ case VIR_DOMAIN_EVENT_UNDEFINED:
+ ret ="Undefined";
break;
case VIR_DOMAIN_EVENT_STARTED:
ret ="Started";
@@ -76,14 +76,56 @@ const char *eventToString(int event) {
case VIR_DOMAIN_EVENT_STOPPED:
ret ="Stopped";
break;
- case VIR_DOMAIN_EVENT_SAVED:
- ret ="Saved";
- break;
- case VIR_DOMAIN_EVENT_RESTORED:
- ret ="Restored";
- break;
default:
ret ="Unknown Event";
+ }
+ return ret;
+}
+
+static const char *eventDetailToString(int event, int detail) {
+ const char *ret = "";
+ switch(event) {
+ case VIR_DOMAIN_EVENT_DEFINED:
+ if (detail == VIR_DOMAIN_EVENT_DEFINED_ADDED)
+ ret = "Added";
+ else if (detail == VIR_DOMAIN_EVENT_DEFINED_UPDATED)
+ ret = "Updated";
+ break;
+ case VIR_DOMAIN_EVENT_STARTED:
+ switch (detail) {
+ case VIR_DOMAIN_EVENT_STARTED_BOOTED:
+ ret = "Booted";
+ break;
+ case VIR_DOMAIN_EVENT_STARTED_MIGRATED:
+ ret = "Migrated";
+ break;
+ case VIR_DOMAIN_EVENT_STARTED_RESTORED:
+ ret = "Restored";
+ break;
+ }
+ break;
+ case VIR_DOMAIN_EVENT_STOPPED:
+ switch (detail) {
+ case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN:
+ ret = "Shutdown";
+ break;
+ case VIR_DOMAIN_EVENT_STOPPED_DESTROYED:
+ ret = "Destroyed";
+ break;
+ case VIR_DOMAIN_EVENT_STOPPED_CRASHED:
+ ret = "Crashed";
+ break;
+ case VIR_DOMAIN_EVENT_STOPPED_MIGRATED:
+ ret = "Migrated";
+ break;
+ case VIR_DOMAIN_EVENT_STOPPED_SAVED:
+ ret = "Failed";
+ break;
+ case VIR_DOMAIN_EVENT_STOPPED_FAILED:
+ ret = "Failed";
+ break;
+ }
+ break;
}
return ret;
}
@@ -91,20 +133,24 @@ int myDomainEventCallback1 (virConnectPt
int myDomainEventCallback1 (virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int event,
+ int detail,
void *opaque ATTRIBUTE_UNUSED)
{
- printf("%s EVENT: Domain %s(%d) %s\n", __FUNCTION__, virDomainGetName(dom),
- virDomainGetID(dom), eventToString(event));
+ printf("%s EVENT: Domain %s(%d) %s %s\n", __FUNCTION__, virDomainGetName(dom),
+ virDomainGetID(dom), eventToString(event),
+ eventDetailToString(event, detail));
return 0;
}
int myDomainEventCallback2 (virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int event,
+ int detail,
void *opaque ATTRIBUTE_UNUSED)
{
- printf("%s EVENT: Domain %s(%d) %s\n", __FUNCTION__, virDomainGetName(dom),
- virDomainGetID(dom), eventToString(event));
+ printf("%s EVENT: Domain %s(%d) %s %s\n", __FUNCTION__, virDomainGetName(dom),
+ virDomainGetID(dom), eventToString(event),
+ eventDetailToString(event, detail));
return 0;
}
diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py
--- a/examples/domain-events/events-python/event-test.py
+++ b/examples/domain-events/events-python/event-test.py
@@ -32,11 +32,11 @@ def eventToString(event):
"Restored" );
return eventStrings[event];
-def myDomainEventCallback1 (conn, dom, event, opaque):
- print "myDomainEventCallback1 EVENT: Domain %s(%s) %s" % (dom.name(), dom.ID(), eventToString(event))
+def myDomainEventCallback1 (conn, dom, event, detail, opaque):
+ print "myDomainEventCallback1 EVENT: Domain %s(%s) %s %d" % (dom.name(), dom.ID(), eventToString(event), detail)
-def myDomainEventCallback2 (conn, dom, event, opaque):
- print "myDomainEventCallback2 EVENT: Domain %s(%s) %s" % (dom.name(), dom.ID(), eventToString(event))
+def myDomainEventCallback2 (conn, dom, event, detail, opaque):
+ print "myDomainEventCallback2 EVENT: Domain %s(%s) %s %d" % (dom.name(), dom.ID(), eventToString(event), detail)
#####################################################
# EventImpl Functions
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -1004,21 +1004,41 @@ virDomainPtr virDomainCreateL
* a virDomainEventType is emitted during domain lifecycle events
*/
typedef enum {
- VIR_DOMAIN_EVENT_ADDED = 0,
- VIR_DOMAIN_EVENT_REMOVED = 1,
+ VIR_DOMAIN_EVENT_DEFINED = 0,
+ VIR_DOMAIN_EVENT_UNDEFINED = 1,
VIR_DOMAIN_EVENT_STARTED = 2,
VIR_DOMAIN_EVENT_SUSPENDED = 3,
VIR_DOMAIN_EVENT_RESUMED = 4,
VIR_DOMAIN_EVENT_STOPPED = 5,
- VIR_DOMAIN_EVENT_SAVED = 6,
- VIR_DOMAIN_EVENT_RESTORED = 7,
} virDomainEventType;
+
+typedef enum {
+ VIR_DOMAIN_EVENT_STARTED_BOOTED = 0, /* Normal startup from boot */
+ VIR_DOMAIN_EVENT_STARTED_MIGRATED = 1, /* Incoming migration from another host */
+ VIR_DOMAIN_EVENT_STARTED_RESTORED = 2, /* Restored from a state file */
+} virDomainEventStartedDetailType;
+
+typedef enum {
+ VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN = 0, /* Normal shutdown */
+ VIR_DOMAIN_EVENT_STOPPED_DESTROYED = 1, /* Forced poweroff from host */
+ VIR_DOMAIN_EVENT_STOPPED_CRASHED = 2, /* Guest crashed */
+ VIR_DOMAIN_EVENT_STOPPED_MIGRATED = 3, /* Migrated off to another host */
+ VIR_DOMAIN_EVENT_STOPPED_SAVED = 4, /* Saved to a state file */
+ VIR_DOMAIN_EVENT_STOPPED_FAILED = 5, /* Host emulator/mgmt failed */
+} virDomainEventStoppedDetailType;
+
+typedef enum {
+ VIR_DOMAIN_EVENT_DEFINED_ADDED = 0, /* Newly created config file */
+ VIR_DOMAIN_EVENT_DEFINED_UPDATED = 1, /* Changed config file */
+} virDomainEventDefinedDetailType;
+
/**
* virConnectDomainEventCallback:
* @conn: virConnect connection
* @dom: The domain on which the event occured
* @event: The specfic virDomainEventType which occured
+ * @detail: event specific detail information
* @opaque: opaque user data
*
* A callback function to be registered, and called when a domain event occurs
@@ -1026,6 +1046,7 @@ typedef int (*virConnectDomainEventCallb
typedef int (*virConnectDomainEventCallback)(virConnectPtr conn,
virDomainPtr dom,
int event,
+ int detail,
void *opaque);
int virConnectDomainEventRegister(virConnectPtr conn,
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1004,21 +1004,41 @@ virDomainPtr virDomainCreateL
* a virDomainEventType is emitted during domain lifecycle events
*/
typedef enum {
- VIR_DOMAIN_EVENT_ADDED = 0,
- VIR_DOMAIN_EVENT_REMOVED = 1,
+ VIR_DOMAIN_EVENT_DEFINED = 0,
+ VIR_DOMAIN_EVENT_UNDEFINED = 1,
VIR_DOMAIN_EVENT_STARTED = 2,
VIR_DOMAIN_EVENT_SUSPENDED = 3,
VIR_DOMAIN_EVENT_RESUMED = 4,
VIR_DOMAIN_EVENT_STOPPED = 5,
- VIR_DOMAIN_EVENT_SAVED = 6,
- VIR_DOMAIN_EVENT_RESTORED = 7,
} virDomainEventType;
+
+typedef enum {
+ VIR_DOMAIN_EVENT_STARTED_BOOTED = 0, /* Normal startup from boot */
+ VIR_DOMAIN_EVENT_STARTED_MIGRATED = 1, /* Incoming migration from another host */
+ VIR_DOMAIN_EVENT_STARTED_RESTORED = 2, /* Restored from a state file */
+} virDomainEventStartedDetailType;
+
+typedef enum {
+ VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN = 0, /* Normal shutdown */
+ VIR_DOMAIN_EVENT_STOPPED_DESTROYED = 1, /* Forced poweroff from host */
+ VIR_DOMAIN_EVENT_STOPPED_CRASHED = 2, /* Guest crashed */
+ VIR_DOMAIN_EVENT_STOPPED_MIGRATED = 3, /* Migrated off to another host */
+ VIR_DOMAIN_EVENT_STOPPED_SAVED = 4, /* Saved to a state file */
+ VIR_DOMAIN_EVENT_STOPPED_FAILED = 5, /* Host emulator/mgmt failed */
+} virDomainEventStoppedDetailType;
+
+typedef enum {
+ VIR_DOMAIN_EVENT_DEFINED_ADDED = 0, /* Newly created config file */
+ VIR_DOMAIN_EVENT_DEFINED_UPDATED = 1, /* Changed config file */
+} virDomainEventDefinedDetailType;
+
/**
* virConnectDomainEventCallback:
* @conn: virConnect connection
* @dom: The domain on which the event occured
* @event: The specfic virDomainEventType which occured
+ * @detail: event specific detail information
* @opaque: opaque user data
*
* A callback function to be registered, and called when a domain event occurs
@@ -1026,6 +1046,7 @@ typedef int (*virConnectDomainEventCallb
typedef int (*virConnectDomainEventCallback)(virConnectPtr conn,
virDomainPtr dom,
int event,
+ int detail,
void *opaque);
int virConnectDomainEventRegister(virConnectPtr conn,
diff --git a/python/libvir.c b/python/libvir.c
--- a/python/libvir.c
+++ b/python/libvir.c
@@ -1538,6 +1538,7 @@ libvirt_virConnectDomainEventCallback(vi
libvirt_virConnectDomainEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int event,
+ int detail,
void *opaque)
{
PyObject *pyobj_ret;
@@ -1595,9 +1596,10 @@ libvirt_virConnectDomainEventCallback(vi
/* Call the Callback Dispatcher */
pyobj_ret = PyObject_CallMethod(pyobj_conn_inst,
(char*)"dispatchDomainEventCallbacks",
- (char*)"Oi",
+ (char*)"Oii",
pyobj_dom_inst,
- event);
+ event,
+ detail);
Py_DECREF(pyobj_dom_inst);
diff --git a/qemud/qemud.h b/qemud/qemud.h
--- a/qemud/qemud.h
+++ b/qemud/qemud.h
@@ -190,6 +190,7 @@ int remoteRelayDomainEvent (virConnectPt
int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainPtr dom,
int event,
+ int detail,
void *opaque);
#endif
diff --git a/qemud/remote.c b/qemud/remote.c
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -80,7 +80,8 @@ static void
static void
remoteDispatchDomainEventSend (struct qemud_client *client,
virDomainPtr dom,
- virDomainEventType event);
+ int event,
+ int detail);
/* This function gets called from qemud when it detects an incoming
* remote protocol message. At this point, client->buffer contains
@@ -413,15 +414,16 @@ remoteDispatchError (struct qemud_client
}
int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED,
- virDomainPtr dom,
- int event,
- void *opaque)
+ virDomainPtr dom,
+ int event,
+ int detail,
+ void *opaque)
{
struct qemud_client *client = opaque;
- REMOTE_DEBUG("Relaying domain event %d", event);
+ REMOTE_DEBUG("Relaying domain event %d %d", event, detail);
if(client) {
- remoteDispatchDomainEventSend (client, dom, event);
+ remoteDispatchDomainEventSend (client, dom, event, detail);
qemudDispatchClientWrite(client->server,client);
}
return 0;
@@ -3762,8 +3764,9 @@ remoteDispatchDomainEventsDeregister (st
static void
remoteDispatchDomainEventSend (struct qemud_client *client,
- virDomainPtr dom,
- virDomainEventType event)
+ virDomainPtr dom,
+ int event,
+ int detail)
{
remote_message_header rep;
XDR xdr;
@@ -3799,7 +3802,8 @@ remoteDispatchDomainEventSend (struct qe
/* build return data */
make_nonnull_domain (&data.dom, dom);
- data.event = (int) event;
+ data.event = event;
+ data.detail = detail;
if (!xdr_remote_domain_event_ret(&xdr, &data)) {
remoteDispatchError (client, NULL, "%s", _("serialise return struct"));
diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c
--- a/qemud/remote_protocol.c
+++ b/qemud/remote_protocol.c
@@ -2024,6 +2024,8 @@ xdr_remote_domain_event_ret (XDR *xdrs,
return FALSE;
if (!xdr_int (xdrs, &objp->event))
return FALSE;
+ if (!xdr_int (xdrs, &objp->detail))
+ return FALSE;
return TRUE;
}
diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h
--- a/qemud/remote_protocol.h
+++ b/qemud/remote_protocol.h
@@ -1130,6 +1130,7 @@ struct remote_domain_event_ret {
struct remote_domain_event_ret {
remote_nonnull_domain dom;
int event;
+ int detail;
};
typedef struct remote_domain_event_ret remote_domain_event_ret;
#define REMOTE_PROGRAM 0x20008086
diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
--- a/qemud/remote_protocol.x
+++ b/qemud/remote_protocol.x
@@ -1007,6 +1007,7 @@ struct remote_domain_event_ret {
struct remote_domain_event_ret {
remote_nonnull_domain dom;
int event;
+ int detail;
};
/*----- Protocol. -----*/
diff --git a/src/domain_event.c b/src/domain_event.c
--- a/src/domain_event.c
+++ b/src/domain_event.c
@@ -198,7 +198,8 @@ int
int
virDomainEventCallbackQueuePush(virDomainEventQueuePtr evtQueue,
virDomainPtr dom,
- virDomainEventType event)
+ int event,
+ int detail)
{
virDomainEventPtr domEvent;
@@ -214,6 +215,7 @@ virDomainEventCallbackQueuePush(virDomai
}
domEvent->dom = dom;
domEvent->event = event;
+ domEvent->detail = detail;
/* Make space on queue */
if (VIR_REALLOC_N(evtQueue->events,
diff --git a/src/domain_event.h b/src/domain_event.h
--- a/src/domain_event.h
+++ b/src/domain_event.h
@@ -58,7 +58,8 @@ int virDomainEventCallbackListRemove(vir
*/
struct _virDomainEvent {
virDomainPtr dom;
- virDomainEventType event;
+ int event;
+ int detail;
};
typedef struct _virDomainEvent virDomainEvent;
typedef virDomainEvent *virDomainEventPtr;
@@ -72,7 +73,8 @@ typedef virDomainEventQueue *virDomainEv
int virDomainEventCallbackQueuePush(virDomainEventQueuePtr evtQueue,
virDomainPtr dom,
- virDomainEventType event);
+ int event,
+ int detail);
virDomainEventPtr
virDomainEventCallbackQueuePop(virDomainEventQueuePtr evtQueue);
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -107,7 +107,8 @@ static int qemudSetNonBlock(int fd) {
static void qemudDomainEventDispatch (struct qemud_driver *driver,
virDomainObjPtr vm,
- virDomainEventType evt);
+ int event,
+ int detail);
static void qemudDispatchVMEvent(int fd,
int events,
@@ -137,13 +138,19 @@ qemudAutostartConfigs(struct qemud_drive
unsigned int i;
for (i = 0 ; i < driver->domains.count ; i++) {
- if (driver->domains.objs[i]->autostart &&
- !virDomainIsActive(driver->domains.objs[i]) &&
- qemudStartVMDaemon(NULL, driver, driver->domains.objs[i], NULL) < 0) {
- virErrorPtr err = virGetLastError();
- qemudLog(QEMUD_ERR, _("Failed to autostart VM '%s': %s\n"),
- driver->domains.objs[i]->def->name,
- err ? err->message : NULL);
+ virDomainObjPtr vm = driver->domains.objs[i];
+ if (vm->autostart &&
+ !virDomainIsActive(vm)) {
+ int ret = qemudStartVMDaemon(NULL, driver, vm, NULL);
+ if (ret < 0) {
+ virErrorPtr err = virGetLastError();
+ qemudLog(QEMUD_ERR, _("Failed to autostart VM '%s': %s\n"),
+ vm->def->name,
+ err ? err->message : NULL);
+ } else {
+ qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_STARTED,
+ VIR_DOMAIN_EVENT_STARTED_BOOTED);
+ }
}
}
}
@@ -945,7 +952,6 @@ static int qemudStartVMDaemon(virConnect
qemudShutdownVMDaemon(conn, driver, vm);
return -1;
}
- qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_STARTED);
}
return ret;
@@ -1030,6 +1036,9 @@ static int qemudDispatchVMLog(struct qem
static int qemudDispatchVMLog(struct qemud_driver *driver, virDomainObjPtr vm, int fd) {
if (qemudVMData(driver, vm, fd) < 0) {
qemudShutdownVMDaemon(NULL, driver, vm);
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_FAILED);
if (!vm->persistent)
virDomainRemoveInactive(&driver->domains,
vm);
@@ -1040,7 +1049,9 @@ static int qemudDispatchVMFailure(struct
static int qemudDispatchVMFailure(struct qemud_driver *driver, virDomainObjPtr vm,
int fd ATTRIBUTE_UNUSED) {
qemudShutdownVMDaemon(NULL, driver, vm);
- qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_STOPPED);
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
if (!vm->persistent)
virDomainRemoveInactive(&driver->domains,
vm);
@@ -1513,6 +1524,9 @@ static virDomainPtr qemudDomainCreate(vi
vm);
return NULL;
}
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STARTED,
+ VIR_DOMAIN_EVENT_STARTED_BOOTED);
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom) dom->id = vm->def->id;
@@ -1543,7 +1557,7 @@ static int qemudDomainSuspend(virDomainP
}
vm->state = VIR_DOMAIN_PAUSED;
qemudDebug("Reply %s", info);
- qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_SUSPENDED);
+ qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_SUSPENDED, 0);
VIR_FREE(info);
return 0;
}
@@ -1572,7 +1586,7 @@ static int qemudDomainResume(virDomainPt
}
vm->state = VIR_DOMAIN_RUNNING;
qemudDebug("Reply %s", info);
- qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_RESUMED);
+ qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_RESUMED, 0);
VIR_FREE(info);
return 0;
}
@@ -1611,7 +1625,9 @@ static int qemudDomainDestroy(virDomainP
}
qemudShutdownVMDaemon(dom->conn, driver, vm);
- qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_STOPPED);
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
if (!vm->persistent)
virDomainRemoveInactive(&driver->domains,
vm);
@@ -1942,10 +1958,12 @@ static int qemudDomainSave(virDomainPtr
/* Shut it down */
qemudShutdownVMDaemon(dom->conn, driver, vm);
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_SAVED);
if (!vm->persistent)
virDomainRemoveInactive(&driver->domains,
vm);
- qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_SAVED);
return 0;
}
@@ -2240,6 +2258,10 @@ static int qemudDomainRestore(virConnect
return -1;
}
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STARTED,
+ VIR_DOMAIN_EVENT_STARTED_RESTORED);
+
/* If it was running before, resume it now. */
if (header.was_running) {
char *info;
@@ -2252,7 +2274,6 @@ static int qemudDomainRestore(virConnect
vm->state = VIR_DOMAIN_RUNNING;
}
- qemudDomainEventDispatch(driver, vm, VIR_DOMAIN_EVENT_RESTORED);
return 0;
}
@@ -2312,6 +2333,7 @@ static int qemudDomainStart(virDomainPtr
static int qemudDomainStart(virDomainPtr dom) {
struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ int ret;
if (!vm) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
@@ -2319,7 +2341,13 @@ static int qemudDomainStart(virDomainPtr
return -1;
}
- return qemudStartVMDaemon(dom->conn, driver, vm, NULL);
+ ret = qemudStartVMDaemon(dom->conn, driver, vm, NULL);
+ if (ret < 0)
+ return ret;
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STARTED,
+ VIR_DOMAIN_EVENT_STARTED_BOOTED);
+ return 0;
}
@@ -3340,7 +3368,8 @@ qemudDomainEventDeregister (virConnectPt
static void qemudDomainEventDispatch (struct qemud_driver *driver,
virDomainObjPtr vm,
- virDomainEventType evt)
+ int event,
+ int detail)
{
int i;
virDomainEventCallbackListPtr cbList;
@@ -3354,11 +3383,11 @@ static void qemudDomainEventDispatch (st
vm->def->uuid);
if (dom) {
dom->id = virDomainIsActive(vm) ? vm->def->id : -1;
- DEBUG("Dispatching callback %p %p event %d",
- cbList->callbacks[i],
- cbList->callbacks[i]->cb, evt);
+ DEBUG("Dispatching callback %p %p event %d detail %d",
+ cbList->callbacks[i],
+ cbList->callbacks[i]->cb, event, detail);
cbList->callbacks[i]->cb(cbList->callbacks[i]->conn,
- dom, evt,
+ dom, event, detail,
cbList->callbacks[i]->opaque);
virDomainFree(dom);
}
@@ -3507,6 +3536,9 @@ qemudDomainMigratePrepare2 (virConnectPt
return -1;
}
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STARTED,
+ VIR_DOMAIN_EVENT_STARTED_MIGRATED);
return 0;
}
@@ -3578,6 +3610,9 @@ qemudDomainMigratePerform (virDomainPtr
/* Clean up the source domain. */
qemudShutdownVMDaemon (dom->conn, driver, vm);
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_MIGRATED);
if (!vm->persistent)
virDomainRemoveInactive(&driver->domains, vm);
@@ -3612,9 +3647,14 @@ qemudDomainMigrateFinish2 (virConnectPtr
dom = virGetDomain (dconn, vm->def->name, vm->def->uuid);
VIR_FREE(info);
vm->state = VIR_DOMAIN_RUNNING;
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_RESUMED, 0);
return dom;
} else {
qemudShutdownVMDaemon (dconn, driver, vm);
+ qemudDomainEventDispatch(driver, vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_FAILED);
if (!vm->persistent)
virDomainRemoveInactive(&driver->domains, vm);
return NULL;
diff --git a/src/remote_internal.c b/src/remote_internal.c
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -5154,7 +5154,7 @@ remoteRegister (void)
*/
static int
remoteDomainReadEvent(virConnectPtr conn, XDR *xdr,
- virDomainPtr *dom, int *event)
+ virDomainPtr *dom, int *event, int *detail)
{
remote_domain_event_ret ret;
memset (&ret, 0, sizeof ret);
@@ -5168,6 +5168,7 @@ remoteDomainReadEvent(virConnectPtr conn
*dom = get_nonnull_domain(conn,ret.dom);
*event = ret.event;
+ *detail = ret.detail;
return 0;
}
@@ -5176,15 +5177,16 @@ remoteDomainProcessEvent(virConnectPtr c
remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr)
{
virDomainPtr dom;
- int event,i;
- struct private_data *priv = conn->privateData;
-
- if(!remoteDomainReadEvent(conn, xdr, &dom, &event)) {
+ int event, detail, i;
+ struct private_data *priv = conn->privateData;
+
+ if(!remoteDomainReadEvent(conn, xdr, &dom, &event, &detail)) {
DEBUG0("Calling domain event callbacks (no queue)");
for(i=0 ; i < priv->callbackList->count ; i++) {
- if( priv->callbackList->callbacks[i] )
- priv->callbackList->callbacks[i]->cb(conn, dom, event,
- priv->callbackList->callbacks[i]->opaque);
+ if (priv->callbackList->callbacks[i] )
+ priv->callbackList->callbacks[i]->cb(
+ conn, dom, event, detail,
+ priv->callbackList->callbacks[i]->opaque);
}
}
}
@@ -5193,13 +5195,13 @@ remoteDomainQueueEvent(virConnectPtr con
remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr)
{
virDomainPtr dom;
- int event;
- struct private_data *priv = conn->privateData;
-
- if(!remoteDomainReadEvent(conn, xdr, &dom, &event))
+ int event, detail;
+ struct private_data *priv = conn->privateData;
+
+ if(!remoteDomainReadEvent(conn, xdr, &dom, &event, &detail))
{
if( virDomainEventCallbackQueuePush(priv->domainEvents,
- dom, event) < 0 ) {
+ dom, event, detail) < 0 ) {
DEBUG("%s", "Error adding event to queue");
}
}
@@ -5292,6 +5294,7 @@ remoteDomainEventQueueFlush(int timer AT
priv->callbackList->callbacks[i]->cb(domEvent->dom->conn,
domEvent->dom,
domEvent->event,
+ domEvent->detail,
user_data);
}
}
--
|: 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 :|
3
6
This is an update of the patch
http://www.redhat.com/archives/libvir-list/2008-October/msg00326.html
To enable bridge support in the OpenVZ driver. As well as the fixes
suggested last time, it includes an initial bit of HTML doc for the
openvz driver, covering example XML, and the bridge configuration
requirements
Daniel
diff -r 34eb1c9bdd7c docs/drvopenvz.html
--- a/docs/drvopenvz.html Mon Nov 10 11:02:51 2008 +0000
+++ b/docs/drvopenvz.html Mon Nov 10 12:08:30 2008 +0000
@@ -122,6 +122,93 @@
</div>
<div id="content">
<h1>OpenVZ container driver</h1>
+ <p>
+ The OpenVZ driver for libvirt allows use and management of container
+ based virtualization on a Linux host OS. Prior to using the OpenVZ
+ driver, the OpenVZ enabled kernel must be installed & booted, and the
+ OpenVZ userspace tools installed. The libvirt driver has been tested
+ with OpenVZ 3.0.22, but other 3.0.x versions should also work without
+ undue trouble.
+ </p>
+ <h2>Connections to OpenVZ driver</h2>
+ <p>
+ The libvirt OpenVZ driver is a single-instance privileged driver,
+ with a driver name of 'openvz'. Some example conection URIs for
+ the libvirt driver are:
+ </p>
+ <pre>
+ openvz:///system (local access)
+ openvz://example.com/system (remote access, TLS/x509)
+ openvz+tcp://example.com/system (remote access, SASl/Kerberos)
+ openvz+ssh://root@example.com/system (remote access, SSH tunnelled)
+ </pre>
+ <h2>Notes on bridged networking</h2>
+ <p>
+ Bridged networking enables a guest domain (ie container) to have its
+ network interface connected directly to the host's physical LAN. Before
+ this can be used there are a couple of configuration pre-requisites for
+ the host OS.
+ </p>
+ <h3>Host network devices</h3>
+ <p>
+ One or more of the physical devices must be attached to a bridge. The
+ process for this varies according to the operating system in use, so
+ for up to date notes consult the <a href="http://wiki.libvirt.org">Wiki</a>
+ or your operating system's networking documentation. The basic idea is
+ that the host OS should end up with a bridge device "br0" containing a
+ physical device "eth0", or a bonding device "bond0".
+ </p>
+ <h3>OpenVZ tools configuration</h3>
+ <p>
+ OpenVZ releases later than 3.0.23 ship with a standard network device
+ setup script that is able to setup bridging, named
+ <code>/usr/sbin/vznetaddbr</code>. For releases prior to 3.0.23, this
+ script must be created manually by the host OS adminstrator. The
+ simplest way is to just download the latest version of this script
+ from a newer OpenVZ release, or upstream source repository. Then
+ a generic configuration file <code>/etc/vz/vznetctl.conf</code>
+ must be created containing
+ </p>
+ <pre>
+#!/bin/bash
+EXTERNAL_SCRIPT="/usr/sbin/vznetaddbr"
+ </pre>
+ <p>
+ The host OS is now ready to allow bridging of guest containers, which
+ will work whether the container is started with libvirt, or OpenVZ
+ tools.
+ </p>
+ <h2>Example guest domain XML configuration</h2>
+ <p>
+ The current libvirt OpenVZ driver has a restriction that the
+ domain names must match the OpenVZ container VEID, which by
+ convention start at 100, and are incremented from there. The
+ choice of OS template to use inside the container is determined
+ by the <code>filesystem</code> tag, and the template source name
+ matches the templates known to OpenVZ tools.
+ </p>
+ <pre>
+<domain type='openvz' id='104'>
+ <name>104</name>
+ <uuid>86c12009-e591-a159-6e9f-91d18b85ef78</uuid>
+ <vcpu>3</vcpu>
+ <os>
+ <type>exe</type>
+ <init>/sbin/init</init>
+ </os>
+ <devices>
+ <filesystem type='template'>
+ <source name='fedora-9-i386-minimal'/>
+ <target dir='/'/>
+ </filesystem>
+ <interface type='bridge'>
+ <mac address='00:18:51:5b:ea:bf'/>
+ <source bridge='br0'/>
+ <target dev='veth101.0'/>
+ </interface>
+ </devices>
+</domain>
+ </pre>
</div>
</div>
<div id="footer">
diff -r 34eb1c9bdd7c docs/drvopenvz.html.in
--- a/docs/drvopenvz.html.in Mon Nov 10 11:02:51 2008 +0000
+++ b/docs/drvopenvz.html.in Mon Nov 10 12:08:30 2008 +0000
@@ -1,5 +1,109 @@
-<html>
+<html> <!-- -*- html -*- -->
<body>
<h1>OpenVZ container driver</h1>
+
+ <p>
+ The OpenVZ driver for libvirt allows use and management of container
+ based virtualization on a Linux host OS. Prior to using the OpenVZ
+ driver, the OpenVZ enabled kernel must be installed & booted, and the
+ OpenVZ userspace tools installed. The libvirt driver has been tested
+ with OpenVZ 3.0.22, but other 3.0.x versions should also work without
+ undue trouble.
+ </p>
+
+ <h2>Connections to OpenVZ driver</h2>
+
+ <p>
+ The libvirt OpenVZ driver is a single-instance privileged driver,
+ with a driver name of 'openvz'. Some example conection URIs for
+ the libvirt driver are:
+ </p>
+
+ <pre>
+ openvz:///system (local access)
+ openvz://example.com/system (remote access, TLS/x509)
+ openvz+tcp://example.com/system (remote access, SASl/Kerberos)
+ openvz+ssh://root@example.com/system (remote access, SSH tunnelled)
+ </pre>
+
+ <h2>Notes on bridged networking</h2>
+
+ <p>
+ Bridged networking enables a guest domain (ie container) to have its
+ network interface connected directly to the host's physical LAN. Before
+ this can be used there are a couple of configuration pre-requisites for
+ the host OS.
+ </p>
+
+ <h3>Host network devices</h3>
+
+ <p>
+ One or more of the physical devices must be attached to a bridge. The
+ process for this varies according to the operating system in use, so
+ for up to date notes consult the <a href="http://wiki.libvirt.org">Wiki</a>
+ or your operating system's networking documentation. The basic idea is
+ that the host OS should end up with a bridge device "br0" containing a
+ physical device "eth0", or a bonding device "bond0".
+ </p>
+
+ <h3>OpenVZ tools configuration</h3>
+
+ <p>
+ OpenVZ releases later than 3.0.23 ship with a standard network device
+ setup script that is able to setup bridging, named
+ <code>/usr/sbin/vznetaddbr</code>. For releases prior to 3.0.23, this
+ script must be created manually by the host OS adminstrator. The
+ simplest way is to just download the latest version of this script
+ from a newer OpenVZ release, or upstream source repository. Then
+ a generic configuration file <code>/etc/vz/vznetctl.conf</code>
+ must be created containing
+ </p>
+
+ <pre>
+#!/bin/bash
+EXTERNAL_SCRIPT="/usr/sbin/vznetaddbr"
+ </pre>
+
+ <p>
+ The host OS is now ready to allow bridging of guest containers, which
+ will work whether the container is started with libvirt, or OpenVZ
+ tools.
+ </p>
+
+
+ <h2>Example guest domain XML configuration</h2>
+
+ <p>
+ The current libvirt OpenVZ driver has a restriction that the
+ domain names must match the OpenVZ container VEID, which by
+ convention start at 100, and are incremented from there. The
+ choice of OS template to use inside the container is determined
+ by the <code>filesystem</code> tag, and the template source name
+ matches the templates known to OpenVZ tools.
+ </p>
+
+ <pre>
+<domain type='openvz' id='104'>
+ <name>104</name>
+ <uuid>86c12009-e591-a159-6e9f-91d18b85ef78</uuid>
+ <vcpu>3</vcpu>
+ <os>
+ <type>exe</type>
+ <init>/sbin/init</init>
+ </os>
+ <devices>
+ <filesystem type='template'>
+ <source name='fedora-9-i386-minimal'/>
+ <target dir='/'/>
+ </filesystem>
+ <interface type='bridge'>
+ <mac address='00:18:51:5b:ea:bf'/>
+ <source bridge='br0'/>
+ <target dev='veth101.0'/>
+ </interface>
+ </devices>
+</domain>
+ </pre>
+
</body>
</html>
diff -r 34eb1c9bdd7c src/openvz_conf.c
--- a/src/openvz_conf.c Mon Nov 10 11:02:51 2008 +0000
+++ b/src/openvz_conf.c Mon Nov 10 12:08:30 2008 +0000
@@ -51,7 +51,7 @@
static char *openvzLocateConfDir(void);
static int openvzGetVPSUUID(int vpsid, char *uuidstr);
-static int openvzLocateConfFile(int vpsid, char *conffile, int maxlen);
+static int openvzLocateConfFile(int vpsid, char *conffile, int maxlen, const char *ext);
static int openvzAssignUUIDs(void);
int
@@ -144,6 +144,8 @@ virCapsPtr openvzCapsInit(void)
0, 0)) == NULL)
goto no_memory;
+ virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x52, 0x54, 0x00 });
+
if ((guest = virCapabilitiesAddGuest(caps,
"exe",
utsname.machine,
@@ -168,54 +170,6 @@ no_memory:
return NULL;
}
-
-/* function checks MAC address is empty
- return 0 - empty
- 1 - not
-*/
-int openvzCheckEmptyMac(const unsigned char *mac)
-{
- int i;
- for (i = 0; i < VIR_MAC_BUFLEN; i++)
- if (mac[i] != 0x00)
- return 1;
-
- return 0;
-}
-
-/* convert mac address to string
- return pointer to string or NULL
-*/
-char *openvzMacToString(const unsigned char *mac)
-{
- char str[20];
- if (snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
- mac[0], mac[1], mac[2],
- mac[3], mac[4], mac[5]) >= 18)
- return NULL;
-
- return strdup(str);
-}
-
-/*parse MAC from view: 00:18:51:8F:D9:F3
- return -1 - error
- 0 - OK
-*/
-static int openvzParseMac(const char *macaddr, unsigned char *mac)
-{
- int ret;
- ret = sscanf((const char *)macaddr, "%02X:%02X:%02X:%02X:%02X:%02X",
- (unsigned int*)&mac[0],
- (unsigned int*)&mac[1],
- (unsigned int*)&mac[2],
- (unsigned int*)&mac[3],
- (unsigned int*)&mac[4],
- (unsigned int*)&mac[5]) ;
- if (ret == 6)
- return 0;
-
- return -1;
-}
static int
openvzReadNetworkConf(virConnectPtr conn,
@@ -287,6 +241,9 @@ openvzReadNetworkConf(virConnectPtr conn
while (*next != '\0' && *next != ',') next++;
if (STRPREFIX(p, "ifname=")) {
p += 7;
+ /* skip in libvirt */
+ } else if (STRPREFIX(p, "host_ifname=")) {
+ p += 12;
len = next - p;
if (len > 16) {
openvzError(conn, VIR_ERR_INTERNAL_ERROR,
@@ -294,14 +251,25 @@ openvzReadNetworkConf(virConnectPtr conn
goto error;
}
+ if (VIR_ALLOC_N(net->ifname, len+1) < 0)
+ goto no_memory;
+
+ strncpy(net->ifname, p, len);
+ net->ifname[len] = '\0';
+ } else if (STRPREFIX(p, "bridge=")) {
+ p += 7;
+ len = next - p;
+ if (len > 16) {
+ openvzError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Too long bridge device name"));
+ goto error;
+ }
+
if (VIR_ALLOC_N(net->data.bridge.brname, len+1) < 0)
goto no_memory;
strncpy(net->data.bridge.brname, p, len);
net->data.bridge.brname[len] = '\0';
- } else if (STRPREFIX(p, "host_ifname=")) {
- p += 12;
- //skip in libvirt
} else if (STRPREFIX(p, "mac=")) {
p += 4;
len = next - p;
@@ -312,14 +280,11 @@ openvzReadNetworkConf(virConnectPtr conn
}
strncpy(cpy_temp, p, len);
cpy_temp[len] = '\0';
- if (openvzParseMac(cpy_temp, net->mac)<0) {
+ if (virParseMacAddr(cpy_temp, net->mac) < 0) {
openvzError(conn, VIR_ERR_INTERNAL_ERROR,
"%s", _("Wrong MAC address"));
goto error;
}
- } else if (STRPREFIX(p, "host_mac=")) {
- p += 9;
- //skip in libvirt
}
p = ++next;
} while (p < token + strlen(token));
@@ -491,6 +456,68 @@ int openvzLoadDomains(struct openvz_driv
return -1;
}
+
+int
+openvzWriteConfigParam(int vpsid, const char *param, const char *value)
+{
+ char conf_file[PATH_MAX];
+ char temp_file[PATH_MAX];
+ char line[PATH_MAX] ;
+ int fd, temp_fd;
+
+ if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX, "conf")<0)
+ return -1;
+ if (openvzLocateConfFile(vpsid, temp_file, PATH_MAX, "tmp")<0)
+ return -1;
+
+ fd = open(conf_file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+ temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (temp_fd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ while(1) {
+ if (openvz_readline(fd, line, sizeof(line)) <= 0)
+ break;
+
+ if (!STRPREFIX(line, param) &&
+ line[strlen(param)] == '=') {
+ if (safewrite(temp_fd, line, strlen(line)) !=
+ strlen(line))
+ goto error;
+ }
+ }
+
+ if (safewrite(temp_fd, param, strlen(param)) < 0 ||
+ safewrite(temp_fd, "=\"", 2) < 0 ||
+ safewrite(temp_fd, value, strlen(value)) < 0 ||
+ safewrite(temp_fd, "\"\n", 2) < 0)
+ goto error;
+
+ if (close(fd) < 0)
+ goto error;
+ fd = -1;
+ if (close(temp_fd) < 0)
+ goto error;
+ temp_fd = -1;
+
+ if (rename(temp_file, conf_file) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ if (fd != -1)
+ close(fd);
+ if (temp_fd != -1)
+ close(temp_fd);
+ unlink(temp_file);
+ return -1;
+}
+
/*
* Read parameter from container config
* sample: 133, "OSTEMPLATE", value, 1024
@@ -508,7 +535,7 @@ openvzReadConfigParam(int vpsid ,const c
char * sf, * token;
char *saveptr = NULL;
- if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX)<0)
+ if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX, "conf")<0)
return -1;
value[0] = 0;
@@ -548,7 +575,7 @@ openvzReadConfigParam(int vpsid ,const c
* 0 - OK
*/
static int
-openvzLocateConfFile(int vpsid, char *conffile, int maxlen)
+openvzLocateConfFile(int vpsid, char *conffile, int maxlen, const char *ext)
{
char * confdir;
int ret = 0;
@@ -557,7 +584,8 @@ openvzLocateConfFile(int vpsid, char *co
if (confdir == NULL)
return -1;
- if (snprintf(conffile, maxlen, "%s/%d.conf", confdir, vpsid) >= maxlen)
+ if (snprintf(conffile, maxlen, "%s/%d.%s",
+ confdir, vpsid, ext ? ext : "conf") >= maxlen)
ret = -1;
VIR_FREE(confdir);
@@ -614,7 +642,7 @@ openvzGetVPSUUID(int vpsid, char *uuidst
char iden[1024];
int fd, ret;
- if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX)<0)
+ if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX, "conf")<0)
return -1;
fd = open(conf_file, O_RDONLY);
@@ -654,7 +682,7 @@ openvzSetDefinedUUID(int vpsid, unsigned
if (uuid == NULL)
return -1;
- if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX)<0)
+ if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX, "conf")<0)
return -1;
if (openvzGetVPSUUID(vpsid, uuidstr))
@@ -722,4 +750,3 @@ static int openvzAssignUUIDs(void)
VIR_FREE(conf_dir);
return 0;
}
-
diff -r 34eb1c9bdd7c src/openvz_conf.h
--- a/src/openvz_conf.h Mon Nov 10 11:02:51 2008 +0000
+++ b/src/openvz_conf.h Mon Nov 10 12:08:30 2008 +0000
@@ -50,6 +50,8 @@ enum { OPENVZ_WARN, OPENVZ_ERR };
#define VZLIST "/usr/sbin/vzlist"
#define VZCTL "/usr/sbin/vzctl"
+#define VZCTL_BRIDGE_MIN_VERSION ((3 * 1000 * 1000) + (0 * 1000) + 22 + 1)
+
struct openvz_driver {
virCapsPtr caps;
virDomainObjList domains;
@@ -60,12 +62,11 @@ int openvzExtractVersion(virConnectPtr c
int openvzExtractVersion(virConnectPtr conn,
struct openvz_driver *driver);
int openvzReadConfigParam(int vpsid ,const char * param, char *value, int maxlen);
+int openvzWriteConfigParam(int vpsid, const char *param, const char *value);
virCapsPtr openvzCapsInit(void);
int openvzLoadDomains(struct openvz_driver *driver);
void openvzFreeDriver(struct openvz_driver *driver);
int strtoI(const char *str);
-int openvzCheckEmptyMac(const unsigned char *mac);
-char *openvzMacToString(const unsigned char *mac);
int openvzSetDefinedUUID(int vpsid, unsigned char *uuid);
#endif /* OPENVZ_CONF_H */
diff -r 34eb1c9bdd7c src/openvz_driver.c
--- a/src/openvz_driver.c Mon Nov 10 11:02:51 2008 +0000
+++ b/src/openvz_driver.c Mon Nov 10 12:08:30 2008 +0000
@@ -55,6 +55,7 @@
#include "openvz_conf.h"
#include "nodeinfo.h"
#include "memory.h"
+#include "bridge.h"
#define OPENVZ_MAX_ARG 28
#define CMDBUF_LEN 1488
@@ -329,13 +330,56 @@ static int openvzDomainReboot(virDomainP
return 0;
}
+static char *
+openvzGenerateVethName(int veid, char *dev_name_ve)
+{
+ char dev_name[32];
+ int ifNo = 0;
+
+ if (sscanf(dev_name_ve, "%*[^0-9]%d", &ifNo) != 1)
+ return NULL;
+ if (snprintf(dev_name, sizeof(dev_name), "veth%d.%d", veid, ifNo) < 7)
+ return NULL;
+ return strdup(dev_name);
+}
+
+static char *
+openvzGenerateContainerVethName(int veid)
+{
+ int ret;
+ char temp[1024];
+
+ /* try to get line "^NETIF=..." from config */
+ if ( (ret = openvzReadConfigParam(veid, "NETIF", temp, sizeof(temp))) <= 0) {
+ snprintf(temp, sizeof(temp), "eth0");
+ } else {
+ char *s;
+ int max = 0;
+
+ /* get maximum interface number (actually, it is the last one) */
+ for (s=strtok(temp, ";"); s; s=strtok(NULL, ";")) {
+ int x;
+
+ if (sscanf(s, "ifname=eth%d", &x) != 1) return NULL;
+ if (x > max) max = x;
+ }
+
+ /* set new name */
+ snprintf(temp, sizeof(temp), "eth%d", max+1);
+ }
+ return strdup(temp);
+}
+
static int
openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid,
- virDomainNetDefPtr net)
+ virDomainNetDefPtr net,
+ virBufferPtr configBuf)
{
int rc = 0, narg;
const char *prog[OPENVZ_MAX_ARG];
- char *mac = NULL;
+ char macaddr[VIR_MAC_STRING_BUFLEN];
+ struct openvz_driver *driver = (struct openvz_driver *) conn->privateData;
+ char *opt = NULL;
#define ADD_ARG_LIT(thisarg) \
do { \
@@ -367,21 +411,61 @@ openvzDomainSetNetwork(virConnectPtr con
ADD_ARG_LIT(vpsid);
}
- if (openvzCheckEmptyMac(net->mac) > 0)
- mac = openvzMacToString(net->mac);
-
- if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE &&
- net->data.bridge.brname != NULL) {
- char opt[1024];
+ virFormatMacAddr(net->mac, macaddr);
+
+ if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char *dev_name_ve;
+ int veid = strtoI(vpsid);
+
//--netif_add ifname[,mac,host_ifname,host_mac]
ADD_ARG_LIT("--netif_add") ;
- strncpy(opt, net->data.bridge.brname, 256);
- if (mac != NULL) {
- strcat(opt, ",");
- strcat(opt, mac);
- }
+
+ /* generate interface name in ve and copy it to options */
+ dev_name_ve = openvzGenerateContainerVethName(veid);
+ if (dev_name_ve == NULL) {
+ openvzError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Could not generate eth name for container"));
+ rc = -1;
+ goto exit;
+ }
+
+ /* if user doesn't specified host interface name,
+ * than we need to generate it */
+ if (net->ifname == NULL) {
+ net->ifname = openvzGenerateVethName(veid, dev_name_ve);
+ if (net->ifname == NULL) {
+ openvzError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Could not generate veth name"));
+ rc = -1;
+ VIR_FREE(dev_name_ve);
+ goto exit;
+ }
+ }
+
+ virBufferAdd(&buf, dev_name_ve, -1); /* Guest dev */
+ virBufferVSprintf(&buf, ",%s", macaddr); /* Guest dev mac */
+ virBufferVSprintf(&buf, ",%s", net->ifname); /* Host dev */
+ virBufferVSprintf(&buf, ",%s", macaddr); /* Host dev mac */
+
+ if (driver->version >= VZCTL_BRIDGE_MIN_VERSION) {
+ virBufferVSprintf(&buf, ",%s", net->data.bridge.brname); /* Host bridge */
+ } else {
+ virBufferVSprintf(configBuf, "ifname=%s", dev_name_ve);
+ virBufferVSprintf(configBuf, ",mac=%s", macaddr); /* Guest dev mac */
+ virBufferVSprintf(configBuf, ",host_ifname=%s", net->ifname); /* Host dev */
+ virBufferVSprintf(configBuf, ",host_mac=%s", macaddr); /* Host dev mac */
+ virBufferVSprintf(configBuf, ",bridge=%s", net->data.bridge.brname); /* Host bridge */
+ }
+
+ VIR_FREE(dev_name_ve);
+
+ if (!(opt = virBufferContentAndReset(&buf)))
+ goto no_memory;
+
ADD_ARG_LIT(opt) ;
- }else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
+ VIR_FREE(opt);
+ } else if (net->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
net->data.ethernet.ipaddr != NULL) {
//--ipadd ip
ADD_ARG_LIT("--ipadd") ;
@@ -402,18 +486,66 @@ openvzDomainSetNetwork(virConnectPtr con
exit:
cmdExecFree(prog);
- VIR_FREE(mac);
return rc;
no_memory:
+ VIR_FREE(opt);
openvzError(conn, VIR_ERR_INTERNAL_ERROR,
_("Could not put argument to %s"), VZCTL);
cmdExecFree(prog);
- VIR_FREE(mac);
return -1;
#undef ADD_ARG_LIT
}
+
+
+static int
+openvzDomainSetNetworkConfig(virConnectPtr conn,
+ virDomainDefPtr def)
+{
+ unsigned int i;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char *param;
+ int first = 1;
+ struct openvz_driver *driver = (struct openvz_driver *) conn->privateData;
+
+ for (i = 0 ; i < def->nnets ; i++) {
+ if (driver->version < VZCTL_BRIDGE_MIN_VERSION &&
+ def->nets[i]->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
+ if (first)
+ first = 0;
+ else
+ virBufferAddLit(&buf, ";");
+ }
+
+ if (openvzDomainSetNetwork(conn, def->name, def->nets[i], &buf) < 0) {
+ openvzError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Could not configure network"));
+ goto exit;
+ }
+ }
+
+ if (driver->version < VZCTL_BRIDGE_MIN_VERSION && def->nnets) {
+ param = virBufferContentAndReset(&buf);
+ if (param) {
+ if (openvzWriteConfigParam(strtoI(def->name), "NETIF", param) < 0) {
+ VIR_FREE(param);
+ openvzError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("cannot replace NETIF config"));
+ return -1;
+ }
+ VIR_FREE(param);
+ }
+ }
+
+ return 0;
+
+exit:
+ param = virBufferContentAndReset(&buf);
+ VIR_FREE(param);
+ return -1;
+}
+
static virDomainPtr
openvzDomainDefineXML(virConnectPtr conn, const char *xml)
@@ -422,7 +554,6 @@ openvzDomainDefineXML(virConnectPtr conn
virDomainDefPtr vmdef = NULL;
virDomainObjPtr vm = NULL;
virDomainPtr dom = NULL;
- int i;
const char *prog[OPENVZ_MAX_ARG];
prog[0] = NULL;
@@ -468,17 +599,12 @@ openvzDomainDefineXML(virConnectPtr conn
goto exit;
}
+ if (openvzDomainSetNetworkConfig(conn, vmdef) < 0)
+ goto exit;
+
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = -1;
-
- for (i = 0 ; i < vmdef->nnets ; i++) {
- if (openvzDomainSetNetwork(conn, vmdef->name, vmdef->nets[i]) < 0) {
- openvzError(conn, VIR_ERR_INTERNAL_ERROR,
- "%s", _("Could not configure network"));
- goto exit;
- }
- }
if (vmdef->vcpus > 0) {
if (openvzDomainSetVcpus(dom, vmdef->vcpus) < 0) {
@@ -500,7 +626,6 @@ openvzDomainCreateXML(virConnectPtr conn
virDomainDefPtr vmdef = NULL;
virDomainObjPtr vm = NULL;
virDomainPtr dom = NULL;
- int i;
struct openvz_driver *driver = (struct openvz_driver *) conn->privateData;
const char *progstart[] = {VZCTL, "--quiet", "start", NULL, NULL};
const char *progcreate[OPENVZ_MAX_ARG];
@@ -546,13 +671,8 @@ openvzDomainCreateXML(virConnectPtr conn
goto exit;
}
- for (i = 0 ; i < vmdef->nnets ; i++) {
- if (openvzDomainSetNetwork(conn, vmdef->name, vmdef->nets[i]) < 0) {
- openvzError(conn, VIR_ERR_INTERNAL_ERROR,
- "%s", _("Could not configure network"));
- goto exit;
- }
- }
+ if (openvzDomainSetNetworkConfig(conn, vmdef) < 0)
+ goto exit;
progstart[3] = vmdef->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 :|
4
5
The attached patch (against libvirt-java) contains Java bindings for the
new domain event code. It works (see EventTest.java), but there's a
certain amount of hokiness regarding the EventImpl stuff that I'd like
to discuss.
Unlike the C and Python interfaces, the Java interface does not
currently allow the client to supply an EventImpl. The problem is that
Java really has no way to interact with unix file descriptors so there's
no reasonable way to implement a fd-watching EventImpl in pure Java
(java.io.FileDescriptor doesn't do the trick since there's no way of
constructing one from raw (int) unix fd)[**].
So for now, I've had the Java bindings register an EventImpl when the
Connect class is loaded. This EventImpl is a Java class, with native
methods implementing it. In fact, I've simply stolen (verbatim) the
EventImpl from libvirt/qemud/events.c and made the native methods call
it. [If we stick with this solution, it would obviously be better to
somehow share this code with libvirtd rather than copy it.]
The other tricky subject is multi-threading. For now, I've avoided it
by exposing Connect.eventImpl.run_once() and forcing the client to call
it from their "event loop". But the normal Java way of doing things
would simply run the EventImpl in a separate thread. In fact, this
EventImpl does implement Runnable, which makes it trivial to run in a
separate thread -- but I don't declare that it implements Runnable yet
because it's not safe to run in a thread while another thread might be
making libvirt calls.
It shouldn't be hard to make this thread-safe using Java synchronized
methods and statements, but I haven't done that yet. Should I??
** java.nio.Channel and friends seem to be the "right" interface for
exposing abstract "selectable channels" in Java. It's just complicated
enough that I've avoided it for now. But I can look into going this way
for allowing Java to provide an EventImpl in the future ..
Cheers,
Dave
4
9
2008/11/15 Ivan Vovk <vovk.is(a)gmail.com>
> Hi Anton,
>
> do i understand correctly that patch for OpenVZ bridge support was
> committed? If so can it be obtained from CVS repository with a new snapshot?
>
Yes, it is commited in libvirt-0.4.6-alt2 that appear in Sisyphus on monday,
or so.
If you are interesting, you can see my git:
http://git.altlinux.org/people/aspsk/packages/?p=libvirt.git;a=summary
>
> Then can you help me a bit again? After I got a new checkout with
>
> cvs -d :pserver:anoncvs@libvirt.org:2401/data/cvs co libvirt
>
> and tried to do a build, i got the following:
>
> [root@alt-03 libvirt]# ./autogen.sh --prefix=$HOME/usr
>
> You must have autopoint installed to compile libvirt.
> Download the appropriate package for your distribution,
> But was is 'autopoint'? I was not able to find it neither in ALT's
> repository, not somewhere else ... =(
>
# apt-get install gettext-tools
# man autopoint
1
0
See https://bugzilla.redhat.com/show_bug.cgi?id=470693
Using ':' as a delimiter for the lvs command isn't reliable,
since it looks like encrypted volume groups have a colon in
the physical device name, which throws of the regex. The
attached patch changes the delimiter to ','.
Maintains existing behavior for me, just waiting on
confirmation from the reporter that this indeed does the
job.
Thanks,
Cole
2
2
[libvirt] [RFC] making (newly public) EventImpl interface more consistent
by David Lively 14 Nov '08
by David Lively 14 Nov '08
14 Nov '08
Hi Folks -
Since virEventRegisterImpl is now public (in libvirt.h), a nagging
concern of mine has become more urgent. Essentially this callback gives
clients a way of registering their own "handle (fd) watcher" and "timer"
functionality for use by libvirt.
What bugs me is the inconsistency between the handle-watcher and timer
interfaces: the timer "add" function returns a timer id, which is then
used to identify the timer to the "update" and "remove" functions. But
the handle-watcher add / update / remove functions identify the watcher
by the handle (fd). The semantics of registering the same handle twice
aren't specified (what happens when we pass that same fd in a subsequent
"update" or "remove"?). Even worse, this doesn't allow one to manage
multiple watches on the same handle reasonably.
So why not make the handle "add" function return a "watch
id" (analogous to the "timer id" returned by the timer "add" fn)? And
then use this watch id to specify the handle-watcher in the "update" and
"remove" functions. This conveniently handles multiple watches on the
same handle, and also makes the "handle-watching" interface more
consistent with the "timer" interface (which is registered at the same
time). We'd pass both the watch id and the handle (fd) into the
watch-handler callback.
I'd like to implement and submit this (along with fixups to the event
test code) if there are no objections.
Thanks,
Dave
P.S. I'm currently working on Java bindings to the new event code ...
4
15
>From f06595fb110e1e5a4c4f6ea8c729da3341f89ac4 Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering(a)redhat.com>
Date: Fri, 14 Nov 2008 13:19:29 +0100
Subject: [PATCH] avoid format string warnings
* src/openvz_driver.c (ADD_ARG_LIT): Add "%s" arg before _(...).
* src/qemu_driver.c (PCI_ATTACH_OK_MSG): Likewise.
* src/util.c (virExec, virRun): Likewise.
---
src/openvz_driver.c | 4 ++--
src/qemu_driver.c | 2 +-
src/util.c | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/openvz_driver.c b/src/openvz_driver.c
index 48ffa13..95631ee 100644
--- a/src/openvz_driver.c
+++ b/src/openvz_driver.c
@@ -426,7 +426,7 @@ openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid,
dev_name_ve = openvzGenerateContainerVethName(veid);
if (dev_name_ve == NULL) {
openvzError(conn, VIR_ERR_INTERNAL_ERROR,
- _("Could not generate eth name for container"));
+ "%s", _("Could not generate eth name for container"));
rc = -1;
goto exit;
}
@@ -437,7 +437,7 @@ openvzDomainSetNetwork(virConnectPtr conn, const char *vpsid,
net->ifname = openvzGenerateVethName(veid, dev_name_ve);
if (net->ifname == NULL) {
openvzError(conn, VIR_ERR_INTERNAL_ERROR,
- _("Could not generate veth name"));
+ "%s", _("Could not generate veth name"));
rc = -1;
VIR_FREE(dev_name_ve);
goto exit;
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 8291bfe..47167b7 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -2611,7 +2611,7 @@ static int qemudDomainAttachPciDiskDevice(virDomainPtr dom, virDomainDeviceDefPt
s += strlen(PCI_ATTACH_OK_MSG);
if (virStrToLong_i ((const char*)s, &dummy, 10, &dev->data.disk->slotnum) == -1)
- qemudLog(QEMUD_WARN, _("Unable to parse slot number\n"));
+ qemudLog(QEMUD_WARN, "%s", _("Unable to parse slot number\n"));
} else {
qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
_("adding %s disk failed"), type);
diff --git a/src/util.c b/src/util.c
index 6141847..9b1c5f4 100644
--- a/src/util.c
+++ b/src/util.c
@@ -406,7 +406,7 @@ virExec(virConnectPtr conn,
char *argv_str;
if ((argv_str = virArgvToString(argv)) == NULL) {
- ReportError(conn, VIR_ERR_NO_MEMORY, _("command debug string"));
+ ReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("command debug string"));
return -1;
}
DEBUG0(argv_str);
@@ -523,7 +523,7 @@ virRun(virConnectPtr conn,
char *argv_str = NULL;
if ((argv_str = virArgvToString(argv)) == NULL) {
- ReportError(conn, VIR_ERR_NO_MEMORY, _("command debug string"));
+ ReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("command debug string"));
goto error;
}
DEBUG0(argv_str);
--
1.6.0.4.911.gc990
2
2
14 Nov '08
We currently have a set of domain lifecycle events
VIR_DOMAIN_EVENT_ADDED = 0,
VIR_DOMAIN_EVENT_REMOVED = 1,
VIR_DOMAIN_EVENT_STARTED = 2,
VIR_DOMAIN_EVENT_SUSPENDED = 3,
VIR_DOMAIN_EVENT_RESUMED = 4,
VIR_DOMAIN_EVENT_STOPPED = 5,
VIR_DOMAIN_EVENT_SAVED = 6,
VIR_DOMAIN_EVENT_RESTORED = 7,
And a couple more I proposed last week to allow distinguishing of new
domains which are transient vs persistent
VIR_DOMAIN_EVENT_DEFINED = 8,
VIR_DOMAIN_EVENT_UNDEFINED = 9,
Previously Stefan has suggested we should consider having an event for
migration, and though I rejected that at the time, I'm now inclined to
agree that this info would be useful here. I'm also thinking I'd like
to have more information about STOPPED & STARTED events in general.
eg, there are a number of reasons why an domain may have started
- explicitly booted on the host
- restored from a saved image
- incoming migration operation
and there are a number of reasons why a domain might have stopped
- forcably destroyed by host admin
- shutdown by host admin
- shutdown by guest admin
- host emulator process crashed
- killed by mgmt after host emulation hung
- migrated to another host
- saved to a memory image
We have explicit events for the SAVED/RESTORED reasons, but what should
we do about the other reasons ?
One option is to add alot more events
VIR_DOMAIN_MIGRATED_IN (migrated to another node)
VIR_DOMAIN_MIGRATED_OUT (migrated from another node)
VIR_DOMAIN_SHUTDOWN (graceful shutdown by host admin)
VIR_DOMAIN_DESTROYED (force destroyed by host admin)
VIR_DOMAIN_CRASHED (guest kernel crashed)
VIR_DOMAIN_HUNG (host emulator hung)
leaving STOPPED to just be a generic stop event, with no particular
reason.
The downside with this, is if an application just wants to know about
whether a domain shutdown, not why, then they have to track lots and
lots of events.
Also, not every driver would be able to provide all of these events,
so would often have to fallback on a generic STOPPED event
So one alternative is to provide a generic 'char * reason' with each
event with provides scope on the cause of the lifecycle operation.
So you'd get
VIR_DOMAIN_STOPPED ("crashed", "shutdown", "destroyed",
"quit", "hung", "migrated", "saved")
VIR_DOMAIN_STARTED ("booted", "migrated", "restored")
nb, we could remove the explicit SAVED and RESTORED events in this style.
With such a 'reason' arg, we could possibly avoid adding the DEFINED
and UNDEFINED events I suggested. Instead, adding a reason for ADDED
and REMOVED events,
VIR_DOMAIN_ADDED ("started", "defined")
VIR_DOMAIN_REMOVED ("shutdown", "undefined")
which lets you distinguish transient from persistent domains. The downside
of this though, is that we can't explicitly track when a configuration
file is 're-defined', eg adding a config file for an existing running
guest.
Daniel
--
|: 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 :|
3
3
All,
Attached is a patch to implement Qemu/KVM live migration in libvirt. Now
that upstream Qemu has settled on an interface that is friendly to libvirt (i.e.
one that doesn't block the monitor on -incoming), we can implement it here.
Note that the bulk of this patch was written by Rich Jones quite a while ago.
My hand in it has mostly been to forward port it to current libvirt CVS, tweak
it for the new Qemu style, and test it out with a recent KVM (kvm-78, in
particular).
Note that there is a FIXME in the code. When testing this out with kvm-78,
I found that if I let the source side kill the source VM immediately after the
"migrate" monitor command completed, the migration to the destination would
never complete. This seems to be a bug in Qemu; the migrate monitor command
shouldn't return until the migrate is really, truly complete. I'm posting this
now so I can get reviews, and start to address review comments while I try to
figure out what's going on with Qemu. With my temporary hack of the sleep()
call in there, though, I was able to successfully live migrate an F-9 guest
between two machines.
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
3
4
[libvirt] [PATCH] add the check whether the domain has already used a disk which attach
by S.Sakamoto 14 Nov '08
by S.Sakamoto 14 Nov '08
14 Nov '08
Hi,
"attach-disk" does not give an error,
when the domain which is attached has already used the source which attach.
This has danger of the data corruption.
I make the patch to add the check of this.
This patch outputs an error,
when the domain which is attached has already used the source which attach.
Thanks,
Shigeki Sakamoto.
Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.170
diff -u -p -r1.170 virsh.c
--- src/virsh.c 13 Oct 2008 16:46:29 -0000 1.170
+++ src/virsh.c 4 Nov 2008 07:28:20 -0000
@@ -4993,6 +4993,11 @@ cmdAttachDisk(vshControl *ctl, const vsh
char *source, *target, *driver, *subdriver, *type, *mode;
int isFile = 0, ret = FALSE;
char *buf = NULL, *tmp = NULL;
+ xmlDocPtr xml = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ char *doc;
+ char xpath[PATH_MAX];
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
goto cleanup;
@@ -5034,6 +5039,35 @@ cmdAttachDisk(vshControl *ctl, const vsh
}
}
+ if (source) {
+ doc = virDomainGetXMLDesc(dom, 0);
+ if (!doc)
+ goto cleanup;
+
+ xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOWARNING);
+ free(doc);
+ if (!xml)
+ goto cleanup;
+ ctxt = xmlXPathNewContext(xml);
+ if (!ctxt)
+ goto cleanup;
+
+ sprintf(xpath, "string(/domain/devices/disk/source[@dev='%s']/@dev)", source);
+ obj = xmlXPathEval(BAD_CAST xpath, ctxt);
+ if (!strcmp(obj->stringval, source)) {
+ vshError(ctl, FALSE, _("Disk %s is already in use by own guest"), source);
+ goto cleanup;
+ }
+ sprintf(xpath, "string(/domain/devices/disk/source[@file='%s']/@file)", source);
+ obj = xmlXPathEval(BAD_CAST xpath, ctxt);
+ if (!strcmp(obj->stringval, source)) {
+ vshError(ctl, FALSE, _("Disk %s is already in use by own guest"), source);
+ goto cleanup;
+ }
+ }
+
/* Make XML of disk */
tmp = vshMalloc(ctl, 1);
if (!tmp) goto cleanup;
@@ -5123,6 +5157,10 @@ cmdAttachDisk(vshControl *ctl, const vsh
ret = TRUE;
cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ if (xml)
+ xmlFreeDoc(xml);
if (dom)
virDomainFree(dom);
free(buf);
3
6
The refactored storage backend registering broke the test driver. The
attached patch makes everything work again by explicitly registering the
backends on driver startup.
Thanks,
Cole
diff --git a/src/test.c b/src/test.c
index 30dc52a..a785f04 100644
--- a/src/test.c
+++ b/src/test.c
@@ -43,6 +43,11 @@
#include "network_conf.h"
#include "domain_conf.h"
#include "storage_conf.h"
+#include "storage_backend.h"
+#include "storage_backend_logical.h"
+#include "storage_backend_iscsi.h"
+#include "storage_backend_disk.h"
+#include "storage_backend_fs.h"
#include "xml.h"
#define MAX_CPUS 128
@@ -663,6 +668,17 @@ static int testOpen(virConnectPtr conn,
return VIR_DRV_OPEN_ERROR;
}
+ if (virStorageBackendRegister(&virStorageBackendDirectory) < 0 ||
+ virStorageBackendRegister(&virStorageBackendFileSystem) < 0 ||
+ virStorageBackendRegister(&virStorageBackendNetFileSystem) < 0 ||
+ virStorageBackendRegister(&virStorageBackendLogical) < 0 ||
+ virStorageBackendRegister(&virStorageBackendISCSI) < 0 ||
+ virStorageBackendRegister(&virStorageBackendDisk) < 0) {
+ testError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to register storage backends."));
+ return VIR_DRV_OPEN_DECLINED;
+ }
+
if (STREQ(uri->path, "/default"))
ret = testOpenDefault(conn);
else
2
1
[libvirt] [PATCH] 0/7 host ("node") device enumeration - completed submission
by David Lively 12 Nov '08
by David Lively 12 Nov '08
12 Nov '08
Since the version of this I posted last week still had some outstaning
TODO items that I have since finished, I'm re-submitting the whole
thing. I consider this a "complete submission" since I don't think it's
necessary to implement any additional functionality to make this
acceptable. (The two remaining unimplemented items are
virNodeDeviceCreate / Destroy and a real Devkit impl, neither of which
needs to be done immediately, for reasons discussed earlier.)
These patches implement the node device enumeration functionality, as
discussed here:
https://www.redhat.com/archives/libvir-list/2008-September/msg00398.html
I've broken it into the following pieces:
1-public-api additions to the public API
2-internal-api additions to the internal API
3-local-node-drivers the HAL & DeviceKit implementations
4-remote-node-driver the remote driver
5-virsh-support virsh support
6-python-bindings python bindings
7-java-bindings Java bindings (for libvirt-java)
Dave
3
12
Hi, Anton.
> You can specify an IP address and a netmask like this:
>> http://libvirt.org/formatnetwork.html#elementsAddress
>> Such bridge will be automatically set up.
>>
>
- thanks for the hint. I assigned 0.0.0.0 as I don't need IP on bridges at
the moment.
> 2) there is a default network "Default" with enabled 'Autostart'. Is
>>> there any way to remove this network completely?
>>>
>> Yes,
>> # virsh net-destroy defaul
>>
> correct answer is
> # virsh net-undefine default
>
- you were correct in both cases: you can't undefine active network so it
should be destroyed and after that undefine. So it worked!
> Short answer: it doesn't work now. Wait for nex libvirt release
> (libvirt-4.6-alt2). I will try to make it as soon as possible.
> For details see
> http://www.redhat.com/archives/libvir-list/2008-October/msg00323.html
>
- yeah, i've already read that thread and known it was a bug. Just needed a
confirmation that it was going to be fixed in next release. So may be my
question will be a bit indelicate, but is there a hope to see this release
by the end of november?
> 5) Is there any way to destroy domain completely like it can be done via
>> 'vzctl destroy <ID>'?
>>
> Yes,
> # virsh destroy <domain-name>
>
>> And here too, if domain was previously defined, command must be:
> # virsh undefine <domain-name>
>
- got the idea. And noticed the following:
virsh # define ovz.xml
Domain 3005 defined from ovz.xml
virsh # start 3005
Domain 3005 started
virsh # shutdown 3005
Domain 3005 is being shutdown
virsh # undefine 3005
libvir: OpenVZ error : Domain not found
Domain 3005 has been undefined
It really removes domain from file system, but why does it throw that error
message?
6) Where does libvirt store info about already created domains? I can't
>> create a new domain with ID 3005 after I completely destroy previous one via
>> 'vzctl destroy 3005'.
>>
> And what does it saying?
>
- if i simply destroy domain via vzctl (without undefining it via virsh):
[root@alt-03 ~]# vzctl destroy 3005
Destroying container private area: /var/lib/vz/private/3005
Container private area was destroyed
- and then try to create another one (this time via virsh)
virsh # create ovz.xml
libvir: OpenVZ error : operation failed: Already an OPENVZ VM defined with
the id '3005'
error: Failed to create domain from ovz.xml
It seems domain is still defined from LibVirt's point of view but actually
it doesn't exist any more. It was removed via native OpenVZ tool. In case I
try to undefine it:
virsh # undefine 3005
libvir: OpenVZ error : Domain not found
libvir: error : vzctl exited with non-zero status 14 and signal 0
libvir: OpenVZ error : internal error Could not exec vzctl
error: Failed to undefine domain 3005
And from this moment it is imposible to create domain with ID 3005. So how
can I obtain a list of already defined domains? And how can it be purged?
7) Is there any way to publish domain's console on a host system so that I
> can connect via telnet to HW node on some TCP/4567 and get into domain's
> console?
>
vzctl enter <domain ID>
>
- yeah, that for sure is true wheh you are on HW node itself. But I need to
organize access into domain's consoles from remote mashines. In my case
there is no network connectivity between real lan and domains. I believed,
this XML definition could help me:
http://libvirt.org/formatdomain.html#elementsCharTCP - "*The character
device acts as a TCP client connecting to a remote server, or as a server
waiting for a client connection*", so when you connect to HW node on some
TCP port you get into domain's console. But it seems I got it in a wrong way
because is didn't work in case of OpenVZ.
P.S. You can ask ALT-specific questions to me directly.
>
- thanks, will keep that in mind.
___________
Best regards,
Ivan V.
3
2
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I have a startup script that runs a second script to do some work on the
system. This second script calls:
virsh -c qemu:///system --readonly nodeinfo
and then scrapes from that the amount of free memory.
When I run this from my init script, I get:
libvir: Remote error : unable to connect to
'/var/run/libvirt/libvirt-sock-ro': Permission denied
But if I run the second script itself directly (outside of the init
script) it works correctly.
Any ideas?
- --
Darryl L. Pierce <dpierce(a)redhat.com> : GPG KEYID: 6C4E7F1B
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org
iEYEARECAAYFAkkYsEEACgkQjaT4DmxOfxuwzACgzYNQ7ku5P0YGr4jdHaDzf6Pq
MicAoJ5nB7e+TSwE2Vfd9YrCgiahKXwo
=01FA
-----END PGP SIGNATURE-----
2
1
Hi!
>
> I faced with several difficulties / misunderstandings and I hope someone
> could clarify me (at the moment I use 'virsh' for testing).
>
> 1) after creating network via 'virsh # net-create network.xml' i get vzbr0
> bridge in down state and have to make ip up manually. Is there any way to
> automatically make it up?
>
You can specify an IP address and a netmask like this:
http://libvirt.org/formatnetwork.html#elementsAddress
Such bridge will be automatically set up.
> 2) there is a default network "Default" with enabled 'Autostart'. Is there
> any way to remove this network completely?
>
Yes,
# virsh net-destroy defaul
> 3) after creating domain via 'virsh # create ovz.xml' i get a new
> container, but it doesn't work as it should do:
> - at first, it has 'vzbr0' veth interface inside and its pair on a HW node
> is added into default bridge vmbr0. I believed tag <source bridge='vzbr0'/>
> should point to a bridge name on a HW node, but instead it created veth
> interface with such name. In openvz config file for created domain i see
> this:
> [root@alt-03 ~]# cat /etc/vz/conf/3005.conf | grep NETIF
>
> NETIF="ifname=vzbr0,mac=52:54:00:35:10:6D,host_ifname=veth3005.0,host_mac=52:54:00:35:10:6D"
>
Short answer: it doesn't work now. Wait for nex libvirt release
(libvirt-4.6-alt2). I will try to make it as soon as possible.
For details see
http://www.redhat.com/archives/libvir-list/2008-October/msg00323.html
> 4) Is there any way to specify config file for created domain like it can
> be done via 'vzctl create <ID> --oostemplate <templname> --config
> <confname>'?
>
No
> 5) Is there any way to destroy domain completely like it can be done via
> 'vzctl destroy <ID>'?
>
Yes,
# virsh destroy <domain-name>
> 6) Where does libvirt store info about already created domains? I can't
> create a new domain with ID 3005 after I completely destroy previous one via
> 'vzctl destroy 3005'.
>
And what does it saying?
7) Is there any way to publish domain's console on a host system so that I
> can connect via telnet to HW node on some TCP/4567 and get into domain's
> console?
>
vzctl enter <domain ID>
> 8) Does libvirt use some OpenVZ bindings or simply executes vzctl and other
> stuff?
>
simply executes vzctl and reading/writing <veid>.conf
P.S. You can ask ALT-specific questions to me directly.
2
2