[libvirt] [PATCH] fix WIN64 compatibility bug in external EventImpl API
by David Lively
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(-)
15 years, 11 months
[libvirt] PATCH: Change signature of virEventAddHandle to allow multiple calls per FD
by Daniel P. Berrange
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 :|
15 years, 11 months
[libvirt] PATCH: Pass a callback for freeing opaque data when registering handle/timer events
by Daniel P. Berrange
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 :|
15 years, 11 months
[libvirt] PATCH: User mode linux driver
by Daniel P. Berrange
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 :|
15 years, 11 months
[libvirt] [RFC PATCH] set tap mtu
by Chris Wright
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)))
15 years, 11 months
[libvirt] [PATCH 2/2] Java bindings for domain events
by David Lively
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
15 years, 11 months
[libvirt] PATCH: Support events for define/undefine in QEMU
by Daniel P. Berrange
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 :|
15 years, 11 months
[libvirt] PATCH: Add domain event "detail" information
by Daniel P. Berrange
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 :|
15 years, 11 months
[libvirt] PATCH: OpenVZ bridge support
by Daniel P. Berrange
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 :|
15 years, 11 months