[Libvir] RFC: Broadcast our presence with avahi

Daniel P. Berrange berrange at redhat.com
Fri Sep 14 02:26:40 UTC 2007


All the really trendy network services these days broadcast their presence
on the LAN using mDNS. In Linux world this means becoming an Avahi client
and registering our services. virt-manager is also able to become an Avahi
client and browser for services. So you'll be able to let the user just
pick a host straight off a list instead of typing in hostname.

The attached patch does two things:

 - Extends our event loop implementation so it can modify the event mask
   associated with an FD, and the timeout associated with a timer. While
   you could simulate this with an add & remove, this has the possibility
   of failure (from malloc). Merely updating an existing event can be done
   without failure. Avahi needs this ability for its event loop integration

 - Added qemu/mdns.c file to actually provide the service information. This
   has two parts. The first section of code is taken straight from one of
   the Avahi example programs. The second section is a implementaiton of the
   Avahi event loop contract in terms of our event API - it works very nicely
   which says good things about design of our event loop

Some things...

 - I arbitrarily picked a service type of '_libvirtd._tcp'. The docs on
   picking service types seem non-existant on Avahi website, but it seems
   to be common to use service name from /etc/services and protocol both
   prefixed with _.

 - I advertise two subtypes, of '_xen.libvirtd._tcp' and '_qemu.libvirtd._tcp'
   What I actually want todo is to be able to probe the libvirt local drivers
   to auto-discover what virtualization platforms are available. A sort of
   lightweight  virConnectOpen which merely returns TRUE/FALSE and doesn't
   actually allocate a virConnectPtr object. Need to extend the internal
   driver API for this.

 - I want to advertise whether the server is configured with TLS certs or
   not, so remote clients can automatically choose to use SSH urls with
   the remote driver if neccessary.

 - Let the admin turn advertisement on/off in the config file because some
   people may not like it on their LAN.

If you fancy trying it,  avahi-browse --all  from another host on the LAN
should show the service being advertised. The advertisements do not cross
LAN routers by default.

Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
-------------- next part --------------
Index: configure.in
===================================================================
RCS file: /data/cvs/libvirt/configure.in,v
retrieving revision 1.81
diff -u -p -r1.81 configure.in
--- configure.in	21 Aug 2007 14:59:47 -0000	1.81
+++ configure.in	14 Sep 2007 02:11:10 -0000
@@ -33,6 +33,10 @@ VERSION=${LIBVIRT_VERSION}
 
 AM_INIT_AUTOMAKE(libvirt, $VERSION)
 
+AVAHI_REQUIRED=0.6.0
+
+PKG_PROG_PKG_CONFIG()
+
 dnl Checks for programs.
 AC_PROG_CC
 AC_PROG_INSTALL
@@ -265,7 +269,7 @@ AC_ARG_WITH(libxml, [  --with-libxml=[PF
 if test "z$with_libxml" = "zno" ; then 
     AC_MSG_CHECKING(for libxml2 libraries >= $LIBXML_MIN_VERSION) 
     AC_MSG_ERROR(libxml2 >= $LIBXML_MIN_VERSION is required for $XMLSEC_PACKAGE)
-elif test "z$with_libxml" = "z" -a "z$PKG_CONFIG_ENABLED" = "zyes" ; then
+elif test "z$with_libxml" = "z" ; then
     PKG_CHECK_MODULES(LIBXML, libxml-2.0 >= $LIBXML_MIN_VERSION,
 	[LIBXML_FOUND=yes],
 	[LIBXML_FOUND=no])
@@ -424,6 +428,30 @@ AC_SUBST(PYTHON_VERSION)
 AC_SUBST(PYTHON_INCLUDES)
 AC_SUBST(PYTHON_SITE_PACKAGES)
 
+
+AC_ARG_WITH(avahi,
+  [  --with-avahi         use Avahi to broadcast server presence],
+  [],
+  [with_avahi=check])
+
+if test "$with_avahi" = "check"; then
+  AC_MSG_CHECKING([if Avahi >= $AVAHI_REQUIRED is available])
+  PKG_CHECK_EXISTS(avahi-client >= $AVAHI_REQUIRED, [have_avahi=yes], [have_avahi=no])
+  AC_MSG_RESULT([$have_avahi])
+  with_avahi="$have_avahi"
+fi
+
+if test "$with_avahi" = "yes" ; then
+  PKG_CHECK_MODULES(AVAHI, avahi-client >= $AVAHI_REQUIRED)
+  AC_DEFINE_UNQUOTED(HAVE_AVAHI, 1, [whether Avahi is used to broadcast server prescence])
+else
+  AVAHI_CFLAGS=
+  AVAHI_LIBS=
+fi
+AM_CONDITIONAL(HAVE_AVAHI, [test "$with_avahi" = "yes"])
+AC_SUBST(AVAHI_CFLAGS)
+AC_SUBST(AVAHI_LIBS)
+
 AC_MSG_CHECKING([whether this host is running a Xen kernel])
 RUNNING_XEN=
 if test -d /proc/sys/xen
Index: qemud/Makefile.am
===================================================================
RCS file: /data/cvs/libvirt/qemud/Makefile.am,v
retrieving revision 1.29
diff -u -p -r1.29 Makefile.am
--- qemud/Makefile.am	27 Jun 2007 00:12:29 -0000	1.29
+++ qemud/Makefile.am	14 Sep 2007 02:11:17 -0000
@@ -3,6 +3,17 @@
 INCLUDES = @LIBXML_CFLAGS@
 UUID=$(shell uuidgen)
 
+# Distribute the generated files so that rpcgen isn't required on the
+# target machine (although almost any Unix machine will have it).
+EXTRA_DIST = libvirtd.init.in libvirtd.sysconf default-network.xml \
+	protocol.x remote_protocol.x \
+	protocol.c protocol.h \
+	remote_protocol.c remote_protocol.h \
+	remote_generate_stubs.pl rpcgen_fix.pl \
+	remote_dispatch_prototypes.h \
+	remote_dispatch_localvars.h \
+	remote_dispatch_proc_switch.h
+
 sbin_PROGRAMS = libvirtd
 
 libvirtd_SOURCES = \
@@ -11,6 +22,7 @@ libvirtd_SOURCES = \
 		remote_protocol.h remote_protocol.c \
 		remote.c \
                 event.c event.h
+
 #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L
 libvirtd_CFLAGS = \
         -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \
@@ -24,6 +36,16 @@ libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBX
 libvirtd_DEPENDENCIES = ../src/libvirt.la
 libvirtd_LDADD = ../src/libvirt.la
 
+
+if HAVE_AVAHI
+libvirtd_SOURCES += mdns.c mdns.h
+libvirtd_CFLAGS += $(AVAHI_CFLAGS)
+libvirtd_LDADD += $(AVAHI_LIBS)
+else
+EXTRA_DIST += mdns.c mdns.h
+endif
+
+
 install-data-local: install-init
 	mkdir -p $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart
 	$(INSTALL_DATA) $(srcdir)/default-network.xml $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/default.xml
@@ -42,17 +64,6 @@ uninstall-local: uninstall-init
 	rmdir $(DESTDIR)$(localstatedir)/run/libvirt || :
 	rmdir $(DESTDIR)$(localstatedir)/lib/libvirt || :
 
-# Distribute the generated files so that rpcgen isn't required on the
-# target machine (although almost any Unix machine will have it).
-EXTRA_DIST = libvirtd.init.in libvirtd.sysconf default-network.xml \
-	protocol.x remote_protocol.x \
-	protocol.c protocol.h \
-	remote_protocol.c remote_protocol.h \
-	remote_generate_stubs.pl rpcgen_fix.pl \
-	remote_dispatch_prototypes.h \
-	remote_dispatch_localvars.h \
-	remote_dispatch_proc_switch.h
-
 .x.c:
 	rm -f $@
 	rpcgen -c -o $@ $<
Index: qemud/event.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/event.c,v
retrieving revision 1.2
diff -u -p -r1.2 event.c
--- qemud/event.c	26 Jun 2007 22:51:01 -0000	1.2
+++ qemud/event.c	14 Sep 2007 02:11:18 -0000
@@ -77,10 +77,10 @@ static int nextTimer = 0;
  * For this reason we only ever append to existing list.
  */
 int virEventAddHandleImpl(int fd, int events, virEventHandleCallback cb, void *opaque) {
-    qemudDebug("Add handle %d %d %p %p\n", fd, events, cb, opaque);
+    qemudDebug("Add handle %d %d %p %p", fd, events, cb, opaque);
     if (eventLoop.handlesCount == eventLoop.handlesAlloc) {
         struct virEventHandle *tmp;
-        qemudDebug("Used %d handle slots, adding %d more\n",
+        qemudDebug("Used %d handle slots, adding %d more",
                    eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT);
         tmp = realloc(eventLoop.handles,
                       sizeof(struct virEventHandle) *
@@ -103,6 +103,15 @@ int virEventAddHandleImpl(int fd, int ev
     return 0;
 }
 
+void virEventUpdateHandleImpl(int fd, int events) {
+    int i;
+    for (i = 0 ; i < eventLoop.handlesCount ; i++) {
+        if (eventLoop.handles[i].fd == fd) {
+            eventLoop.handles[i].events = events;
+        }
+    }
+}
+
 /*
  * Unregister a callback from a file handle
  * NB, it *must* be safe to call this from within a callback
@@ -111,13 +120,13 @@ int virEventAddHandleImpl(int fd, int ev
  */
 int virEventRemoveHandleImpl(int fd) {
     int i;
-    qemudDebug("Remove handle %d\n", fd);
+    qemudDebug("Remove handle %d", fd);
     for (i = 0 ; i < eventLoop.handlesCount ; i++) {
         if (eventLoop.handles[i].deleted)
             continue;
 
         if (eventLoop.handles[i].fd == fd) {
-            qemudDebug("mark delete %d\n", i);
+            qemudDebug("mark delete %d", i);
             eventLoop.handles[i].deleted = 1;
             return 0;
         }
@@ -140,7 +149,7 @@ int virEventAddTimeoutImpl(int timeout, 
 
     if (eventLoop.timeoutsCount == eventLoop.timeoutsAlloc) {
         struct virEventTimeout *tmp;
-        qemudDebug("Used %d timeout slots, adding %d more\n",
+        qemudDebug("Used %d timeout slots, adding %d more",
                    eventLoop.timeoutsAlloc, EVENT_ALLOC_EXTENT);
         tmp = realloc(eventLoop.timeouts,
                       sizeof(struct virEventTimeout) *
@@ -158,15 +167,33 @@ int virEventAddTimeoutImpl(int timeout, 
     eventLoop.timeouts[eventLoop.timeoutsCount].opaque = opaque;
     eventLoop.timeouts[eventLoop.timeoutsCount].deleted = 0;
     eventLoop.timeouts[eventLoop.timeoutsCount].expiresAt =
-        timeout +
+        timeout ? timeout +
         (((unsigned long long)tv.tv_sec)*1000) +
-        (((unsigned long long)tv.tv_usec)/1000);
+        (((unsigned long long)tv.tv_usec)/1000) : 0;
 
     eventLoop.timeoutsCount++;
 
     return nextTimer-1;
 }
 
+void virEventUpdateTimeoutImpl(int timer, int timeout) {
+    struct timeval tv;
+    int i;
+    qemudDebug("Updating timer %d timeout with %d ms period", timer, timeout);
+    if (gettimeofday(&tv, NULL) < 0) {
+        return;
+    }
+
+    for (i = 0 ; i < eventLoop.timeoutsCount ; i++) {
+        if (eventLoop.timeouts[i].timer == timer) {
+            eventLoop.timeouts[i].expiresAt =
+                timeout ? timeout +
+                (((unsigned long long)tv.tv_sec)*1000) +
+                (((unsigned long long)tv.tv_usec)/1000) : 0;
+        }
+    }
+}
+
 /*
  * Unregister a callback for a timer
  * NB, it *must* be safe to call this from within a callback
@@ -199,7 +226,7 @@ static int virEventCalculateTimeout(int 
 
     /* Figure out if we need a timeout */
     for (i = 0 ; i < eventLoop.timeoutsCount ; i++) {
-        if (eventLoop.timeouts[i].deleted)
+        if (eventLoop.timeouts[i].deleted || !eventLoop.timeouts[i].timeout)
             continue;
 
         qemudDebug("Got a timeout scheduled for %llu", eventLoop.timeouts[i].expiresAt);
@@ -256,7 +283,7 @@ static int virEventMakePollFDs(struct po
         fds[nfds].fd = eventLoop.handles[i].fd;
         fds[nfds].events = eventLoop.handles[i].events;
         fds[nfds].revents = 0;
-        qemudDebug("Wait for %d %d\n", eventLoop.handles[i].fd, eventLoop.handles[i].events);
+        qemudDebug("Wait for %d %d", eventLoop.handles[i].fd, eventLoop.handles[i].events);
         nfds++;
     }
 
@@ -291,7 +318,7 @@ static int virEventDispatchTimeouts(void
         (((unsigned long long)tv.tv_usec)/1000);
 
     for (i = 0 ; i < ntimeouts ; i++) {
-        if (eventLoop.timeouts[i].deleted)
+        if (eventLoop.timeouts[i].deleted || !eventLoop.timeouts[i].timeout)
             continue;
 
         if (eventLoop.timeouts[i].expiresAt <= now) {
@@ -322,12 +349,12 @@ static int virEventDispatchHandles(struc
 
     for (i = 0 ; i < nhandles ; i++) {
         if (eventLoop.handles[i].deleted) {
-            qemudDebug("Skip deleted %d\n", eventLoop.handles[i].fd);
+            qemudDebug("Skip deleted %d", eventLoop.handles[i].fd);
             continue;
         }
 
         if (fds[i].revents) {
-            qemudDebug("Dispatch %d %d %p\n", fds[i].fd, fds[i].revents, eventLoop.handles[i].opaque);
+            qemudDebug("Dispatch %d %d %p", fds[i].fd, fds[i].revents, eventLoop.handles[i].opaque);
             (eventLoop.handles[i].cb)(fds[i].fd, fds[i].revents,
                                       eventLoop.handles[i].opaque);
         }
@@ -365,7 +392,7 @@ static int virEventCleanupTimeouts(void)
     /* Release some memory if we've got a big chunk free */
     if ((eventLoop.timeoutsAlloc - EVENT_ALLOC_EXTENT) > eventLoop.timeoutsCount) {
         struct virEventTimeout *tmp;
-        qemudDebug("Releasing %d out of %d timeout slots used, releasing %d\n",
+        qemudDebug("Releasing %d out of %d timeout slots used, releasing %d",
                    eventLoop.timeoutsCount, eventLoop.timeoutsAlloc, EVENT_ALLOC_EXTENT);
         tmp = realloc(eventLoop.timeouts,
                       sizeof(struct virEventTimeout) *
@@ -406,7 +433,7 @@ static int virEventCleanupHandles(void) 
     /* Release some memory if we've got a big chunk free */
     if ((eventLoop.handlesAlloc - EVENT_ALLOC_EXTENT) > eventLoop.handlesCount) {
         struct virEventHandle *tmp;
-        qemudDebug("Releasing %d out of %d handles slots used, releasing %d\n",
+        qemudDebug("Releasing %d out of %d handles slots used, releasing %d",
                    eventLoop.handlesCount, eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT);
         tmp = realloc(eventLoop.handles,
                       sizeof(struct virEventHandle) *
@@ -437,9 +464,9 @@ int virEventRunOnce(void) {
     }
 
  retry:
-    qemudDebug("Poll on %d handles %p timeout %d\n", nfds, fds, timeout);
+    qemudDebug("Poll on %d handles %p timeout %d", nfds, fds, timeout);
     ret = poll(fds, nfds, timeout);
-    qemudDebug("Poll got %d event\n", ret);
+    qemudDebug("Poll got %d event", ret);
     if (ret < 0) {
         if (errno == EINTR) {
             goto retry;
Index: qemud/event.h
===================================================================
RCS file: /data/cvs/libvirt/qemud/event.h,v
retrieving revision 1.2
diff -u -p -r1.2 event.h
--- qemud/event.h	26 Jun 2007 22:51:01 -0000	1.2
+++ qemud/event.h	14 Sep 2007 02:11:18 -0000
@@ -37,7 +37,7 @@
  * returns -1 if the file handle cannot be registered, 0 upon success
  */
 int virEventAddHandleImpl(int fd, int events, virEventHandleCallback cb, void *opaque);
-
+void virEventUpdateHandleImpl(int fd, int events);
 /**
  * virEventRemoveHandleImpl: unregister a callback from a file handle
  *
@@ -58,6 +58,7 @@ int virEventRemoveHandleImpl(int fd);
  * integer timer id upon success
  */
 int virEventAddTimeoutImpl(int timeout, virEventTimeoutCallback cb, void *opaque);
+void virEventUpdateTimeoutImpl(int timer, int timeout);
 
 /**
  * virEventRemoveTimeoutImpl: unregister a callback for a timer
Index: qemud/internal.h
===================================================================
RCS file: /data/cvs/libvirt/qemud/internal.h,v
retrieving revision 1.34
diff -u -p -r1.34 internal.h
--- qemud/internal.h	9 Aug 2007 20:19:12 -0000	1.34
+++ qemud/internal.h	14 Sep 2007 02:11:18 -0000
@@ -29,6 +29,10 @@
 #include <gnutls/x509.h>
 #include "../src/gnutls_1_0_compat.h"
 
+#ifdef HAVE_AVAHI
+#include <avahi-client/client.h>
+#endif
+
 #include "protocol.h"
 #include "remote_protocol.h"
 #include "../config.h"
@@ -115,6 +119,10 @@ struct qemud_socket {
     struct qemud_socket *next;
 };
 
+struct qemud_mdns {
+//
+};
+
 /* Main server state */
 struct qemud_server {
     int nsockets;
@@ -124,6 +132,9 @@ struct qemud_server {
     int sigread;
     char logDir[PATH_MAX];
     unsigned int shutdown : 1;
+#ifdef HAVE_AVAHI
+    AvahiClient *mdns_client;
+#endif
 };
 
 void qemudLog(int priority, const char *fmt, ...)
Index: qemud/mdns.c
===================================================================
RCS file: qemud/mdns.c
diff -N qemud/mdns.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ qemud/mdns.c	14 Sep 2007 02:11:19 -0000
@@ -0,0 +1,367 @@
+/*
+ * mdns.c: advertise libvirt hypervisor connections
+ *
+ * Copyright (C) 2007 Daniel P. Berrange
+ *
+ * Derived from Avahi example service provider code.
+ *
+ * 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 at redhat.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+
+#include <avahi-common/alternative.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/timeval.h>
+
+#include "mdns.h"
+#include "event.h"
+#include "../src/remote_internal.h"
+
+#if 1
+static AvahiEntryGroup *group = NULL;
+char *name;
+static void create_services(AvahiClient *c);
+
+static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
+    assert(g == group || group == NULL);
+
+    /* Called whenever the entry group state changes */
+
+    switch (state) {
+    case AVAHI_ENTRY_GROUP_ESTABLISHED :
+        /* The entry group has been established successfully */
+        fprintf(stderr, "Service '%s' successfully established.\n", name);
+        break;
+
+    case AVAHI_ENTRY_GROUP_COLLISION : {
+        char *n;
+
+        /* A service name collision happened. Let's pick a new name */
+        n = avahi_alternative_service_name(name);
+        avahi_free(name);
+        name = n;
+
+        fprintf(stderr, "Service name collision, renaming service to '%s'\n", name);
+
+        /* And recreate the services */
+        create_services(avahi_entry_group_get_client(g));
+        break;
+    }
+
+    case AVAHI_ENTRY_GROUP_FAILURE :
+        fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
+
+        /* Some kind of failure happened while we were registering our services */
+        //avahi_simple_poll_quit(simple_poll);
+        break;
+
+    case AVAHI_ENTRY_GROUP_UNCOMMITED:
+    case AVAHI_ENTRY_GROUP_REGISTERING:
+        ;
+    }
+}
+
+static void create_services(AvahiClient *c) {
+    int ret;
+    assert(c);
+
+    /* If this is the first time we're called, let's create a new entry group */
+    if (!group) {
+        if (!(group = avahi_entry_group_new(c, entry_group_callback, NULL))) {
+            fprintf(stderr, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_client_errno(c)));
+            goto fail;
+        }
+    }
+
+    fprintf(stderr, "Adding service '%s'\n", name);
+
+    /* Add the service for IPP */
+    if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, "_libvirtd._tcp", NULL, NULL, LIBVIRTD_TLS_PORT_NUM, NULL)) < 0) {
+        fprintf(stderr, "Failed to add _libvirtd._tcp service: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+    if ((ret = avahi_entry_group_add_service_subtype(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, "_libvirtd._tcp", NULL, "_qemu._sub._libvirtd._tcp")) < 0) {
+        fprintf(stderr, "Failed to add _qemu._libvirtd._tcp service: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+    if ((ret = avahi_entry_group_add_service_subtype(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, "_libvirtd._tcp", NULL, "_xen._sub._libvirtd._tcp")) < 0) {
+        fprintf(stderr, "Failed to add _xen._libvirtd._tcp service: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+
+    /* Tell the server to register the service */
+    if ((ret = avahi_entry_group_commit(group)) < 0) {
+        fprintf(stderr, "Failed to commit entry_group: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+
+ fail:
+    return;
+}
+
+
+static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+    assert(c);
+
+    /* Called whenever the client or server state changes */
+
+    switch (state) {
+        case AVAHI_CLIENT_S_RUNNING:
+            /* The server has startup successfully and registered its host
+             * name on the network, so it's time to create our services */
+            if (!group)
+                create_services(c);
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            fprintf(stderr, "Client failure: %s\n", avahi_strerror(avahi_client_errno(c)));
+            //avahi_simple_poll_quit(simple_poll);
+            break;
+
+        case AVAHI_CLIENT_S_COLLISION:
+            /* Let's drop our registered services. When the server is back
+             * in AVAHI_SERVER_RUNNING state we will register them
+             * again with the new host name. */
+
+            /* Fallthrough */
+
+        case AVAHI_CLIENT_S_REGISTERING:
+
+            /* The server records are now being established. This
+             * might be caused by a host name change. We need to wait
+             * for our own records to register until the host name is
+             * properly esatblished. */
+            if (group)
+                avahi_entry_group_reset(group);
+            break;
+
+        case AVAHI_CLIENT_CONNECTING:
+            ;
+    }
+}
+
+#endif
+
+struct AvahiWatch {
+    int fd;
+    int revents;
+    AvahiWatchCallback callback;
+    void *userdata;
+};
+
+struct AvahiTimeout {
+    int timer;
+    AvahiTimeoutCallback callback;
+    void  *userdata;
+};
+
+static void libvirtd_avahi_watch_dispatch(int fd, int events, void *opaque)
+{
+    AvahiWatch *w = (AvahiWatch*)opaque;
+    qemudDebug("Dispatch watch %d", fd);
+    w->revents = events;
+    w->callback(w, fd, events, w->userdata);
+}
+
+static AvahiWatch *libvirtd_avahi_watch_new(const AvahiPoll *api ATTRIBUTE_UNUSED,
+                                            int fd, AvahiWatchEvent event, AvahiWatchCallback cb, void *userdata) {
+    AvahiWatch *w = malloc(sizeof(AvahiWatch));
+    if (!w)
+        return NULL;
+
+    w->fd = fd;
+    w->revents = 0;
+    w->callback = cb;
+    w->userdata = userdata;
+
+    qemudDebug("New handle %p %d", w, w->fd);
+    if (virEventAddHandleImpl(fd, event, libvirtd_avahi_watch_dispatch, w) < 0) {
+        free(w);
+        return NULL;
+    }
+
+    return w;
+}
+
+static void libvirtd_avahi_watch_update(AvahiWatch *w, AvahiWatchEvent event) {
+    qemudDebug("Update handle %p %d", w, w->fd);
+    virEventUpdateHandleImpl(w->fd, event);
+}
+
+static AvahiWatchEvent libvirtd_avahi_watch_get_events(AvahiWatch *w) {
+    qemudDebug("Get handle events %p %d", w, w->fd);
+    return w->revents;
+}
+
+static void libvirtd_avahi_watch_free(AvahiWatch *w) {
+    qemudDebug("Free handle %p %d", w, w->fd);
+    virEventRemoveHandleImpl(w->fd);
+    free(w);
+}
+
+static void libvirtd_avahi_timeout_dispatch(int timer, void *opaque)
+{
+    AvahiTimeout *t = (AvahiTimeout*)opaque;
+    qemudDebug("Dispatch timeout %p %d", t, timer);
+    virEventRemoveTimeoutImpl(t->timer);
+    t->callback(t, t->userdata);
+}
+
+static AvahiTimeout *libvirtd_avahi_timeout_new(const AvahiPoll *api ATTRIBUTE_UNUSED,
+                                                const struct timeval *tv, AvahiTimeoutCallback cb, void *userdata) {
+    AvahiTimeout *t = malloc(sizeof(AvahiTimeout));
+    struct timeval now;
+    long long nowms, thenms, timeout;
+    qemudDebug("Add timeout %p", t);
+    if (!t)
+        return NULL;
+
+    if (gettimeofday(&now, NULL) < 0) {
+        free(t);
+        return NULL;
+    }
+
+    qemudDebug("Trigger timed for %d %d      %d %d",
+               (int)now.tv_sec, (int)now.tv_usec,
+               (int)(tv ? tv->tv_sec : 0), (int)(tv ? tv->tv_usec : 0));
+    nowms = (now.tv_sec * 1000ll) + (now.tv_usec / 1000ll);
+    thenms = tv ? ((tv->tv_sec * 1000ll) + (tv->tv_usec/1000ll)) : 0;
+    timeout = thenms ? thenms - nowms : 0;
+    if (thenms && timeout <= 0)
+        timeout = 1;
+
+    t->timer = virEventAddTimeoutImpl(timeout, libvirtd_avahi_timeout_dispatch, t);
+    t->callback = cb;
+    t->userdata = userdata;
+
+    if (t->timer < 0) {
+        free(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+static void libvirtd_avahi_timeout_update(AvahiTimeout *t, const struct timeval *tv) {
+    struct timeval now;
+    long long nowms, thenms, timeout;
+    qemudDebug("Update timeout %p", t);
+    if (gettimeofday(&now, NULL) < 0) {
+        free(t);
+        return;
+    }
+
+    nowms = (now.tv_sec * 1000ll) + (now.tv_usec / 1000ll);
+    thenms = tv ? ((tv->tv_sec * 1000ll) + (tv->tv_usec/1000ll)) : 0;
+    timeout = thenms ? nowms-thenms : 0;
+    if (thenms && timeout <= 0)
+        timeout = 1;
+
+    virEventUpdateTimeoutImpl(t->timer, timeout);
+}
+
+static void libvirtd_avahi_timeout_free(AvahiTimeout *t) {
+    qemudDebug("Free timeout %p", t);
+    virEventRemoveTimeoutImpl(t->timer);
+    free(t);
+}
+
+
+static AvahiPoll *libvirtd_create_poll(struct qemud_server *server) {
+    AvahiPoll *p = malloc(sizeof(AvahiPoll));
+    if (!p)
+        return NULL;
+
+    p->userdata = server;
+
+    p->watch_new = libvirtd_avahi_watch_new;
+    p->watch_update = libvirtd_avahi_watch_update;
+    p->watch_get_events = libvirtd_avahi_watch_get_events;
+    p->watch_free = libvirtd_avahi_watch_free;
+
+    p->timeout_new = libvirtd_avahi_timeout_new;
+    p->timeout_update = libvirtd_avahi_timeout_update;
+    p->timeout_free = libvirtd_avahi_timeout_free;
+
+    return p;
+}
+
+int libvirtd_init_mdns(struct qemud_server *server) {
+    AvahiClient *client = NULL;
+    AvahiPoll *poller = NULL;
+    int error;
+
+    /* Allocate main loop object */
+    if (!(poller = libvirtd_create_poll(server))) {
+        fprintf(stderr, "Failed to create poll object.\n");
+        goto fail;
+    }
+
+    if (!(name = strdup("Virtualization Provider"))) {
+        fprintf(stderr, "Failed to alloc name");
+        goto fail;
+    }
+
+    /* Allocate a new client */
+    client = avahi_client_new(poller, 0, client_callback, NULL, &error);
+
+    /* Check wether creating the client object succeeded */
+    if (!client) {
+        fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error));
+        goto fail;
+    }
+
+    server->mdns_client = client;
+
+    return 0;
+
+fail:
+    /* Cleanup things */
+
+    if (client)
+        avahi_client_free(client);
+
+    if (poller)
+        free(poller);
+
+    if (name)
+        free(name);
+
+    return -1;
+}
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
Index: qemud/mdns.h
===================================================================
RCS file: qemud/mdns.h
diff -N qemud/mdns.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ qemud/mdns.h	14 Sep 2007 02:11:19 -0000
@@ -0,0 +1,27 @@
+/*
+ * mdns.c: advertise libvirt hypervisor connections
+ *
+ * Copyright (C) 2007 Daniel P. Berrange
+ *
+ * Derived from Avahi example service provider code.
+ *
+ * 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 at redhat.com>
+ */
+
+#include "internal.h"
+
+int libvirtd_init_mdns(struct qemud_server *server);
Index: qemud/qemud.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/qemud.c,v
retrieving revision 1.56
diff -u -p -r1.56 qemud.c
--- qemud/qemud.c	7 Aug 2007 13:24:22 -0000	1.56
+++ qemud/qemud.c	14 Sep 2007 02:11:19 -0000
@@ -56,6 +56,9 @@
 #include "../src/remote_internal.h"
 #include "../src/conf.h"
 #include "event.h"
+#if HAVE_AVAHI
+#include "mdns.h"
+#endif
 
 static int godaemon = 0;        /* -d: Be a daemon */
 static int verbose = 0;         /* -v: Verbose mode */
@@ -688,8 +691,10 @@ static struct qemud_server *qemudInitial
         goto cleanup;
 
     __virEventRegisterImpl(virEventAddHandleImpl,
+                           virEventUpdateHandleImpl,
                            virEventRemoveHandleImpl,
                            virEventAddTimeoutImpl,
+                           virEventUpdateTimeoutImpl,
                            virEventRemoveTimeoutImpl);
 
     virStateInitialize();
@@ -707,6 +712,10 @@ static struct qemud_server *qemudInitial
         }
     }
 
+#if HAVE_AVAHI
+    libvirtd_init_mdns(server);
+#endif
+
     return server;
 
  cleanup:
Index: src/event.c
===================================================================
RCS file: /data/cvs/libvirt/src/event.c,v
retrieving revision 1.1
diff -u -p -r1.1 event.c
--- src/event.c	26 Jun 2007 22:51:01 -0000	1.1
+++ src/event.c	14 Sep 2007 02:11:21 -0000
@@ -27,8 +27,10 @@
 #include <stdlib.h>
 
 static virEventAddHandleFunc addHandleImpl = NULL;
+static virEventUpdateHandleFunc updateHandleImpl = NULL;
 static virEventRemoveHandleFunc removeHandleImpl = NULL;
 static virEventAddTimeoutFunc addTimeoutImpl = NULL;
+static virEventUpdateTimeoutFunc updateTimeoutImpl = NULL;
 static virEventRemoveTimeoutFunc removeTimeoutImpl = NULL;
 
 int virEventAddHandle(int fd, int events, virEventHandleCallback cb, void *opaque) {
@@ -38,6 +40,10 @@ int virEventAddHandle(int fd, int events
     return addHandleImpl(fd, events, cb, opaque);
 }
 
+void virEventUpdateHandle(int fd, int events) {
+    updateHandleImpl(fd, events);
+}
+
 int virEventRemoveHandle(int fd) {
     if (!removeHandleImpl)
         return -1;
@@ -52,6 +58,10 @@ int virEventAddTimeout(int timeout, virE
     return addTimeoutImpl(timeout, cb, opaque);
 }
 
+void virEventUpdateTimeout(int timer, int timeout) {
+    updateTimeoutImpl(timer, timeout);
+}
+
 int virEventRemoveTimeout(int timer) {
     if (!removeTimeoutImpl)
         return -1;
@@ -60,12 +70,16 @@ int virEventRemoveTimeout(int timer) {
 }
 
 void __virEventRegisterImpl(virEventAddHandleFunc addHandle,
-                           virEventRemoveHandleFunc removeHandle,
-                           virEventAddTimeoutFunc addTimeout,
-                           virEventRemoveTimeoutFunc removeTimeout) {
+                            virEventUpdateHandleFunc updateHandle,
+                            virEventRemoveHandleFunc removeHandle,
+                            virEventAddTimeoutFunc addTimeout,
+                            virEventUpdateTimeoutFunc updateTimeout,
+                            virEventRemoveTimeoutFunc removeTimeout) {
     addHandleImpl = addHandle;
+    updateHandleImpl = updateHandle;
     removeHandleImpl = removeHandle;
     addTimeoutImpl = addTimeout;
+    updateTimeoutImpl = updateTimeout;
     removeTimeoutImpl = removeTimeout;
 }
 
Index: src/event.h
===================================================================
RCS file: /data/cvs/libvirt/src/event.h,v
retrieving revision 1.1
diff -u -p -r1.1 event.h
--- src/event.h	26 Jun 2007 22:51:01 -0000	1.1
+++ src/event.h	14 Sep 2007 02:11:21 -0000
@@ -47,6 +47,16 @@ typedef void (*virEventHandleCallback)(i
 int virEventAddHandle(int fd, int events, virEventHandleCallback cb, void *opaque);
 
 /**
+ * virEventUpdateHandle: change an event mask for monitoring file handle events
+ *
+ * @fd: file handle whose events to change
+ * @events: bitset of events to wach from POLLnnn constants
+ *
+ * This cannot fail if
+ */
+void virEventUpdateHandle(int fd, int events);
+
+/**
  * virEventRemoveHandle: unregister a callback from a file handle
  *
  * @fd: file handle to stop monitoring for events
@@ -75,6 +85,19 @@ typedef void (*virEventTimeoutCallback)(
  */
 int virEventAddTimeout(int timeout, virEventTimeoutCallback cb, void *opaque);
 
+
+/**
+ * virEventAddTimeout: update the timeout for a timer event
+ *
+ * @timer: timer whose timeout to update
+ * @timeout: timeout between events in milliseconds
+ *
+ * This cannot fail
+ */
+void virEventUpdateTimeout(int timer, int timeout);
+
+
+
 /**
  * virEventRemoveTimeout: unregister a callback for a timer
  *
@@ -85,14 +108,18 @@ int virEventAddTimeout(int timeout, virE
 int virEventRemoveTimeout(int timer);
 
 typedef int (*virEventAddHandleFunc)(int, int, virEventHandleCallback, void *);
+typedef void (*virEventUpdateHandleFunc)(int, int);
 typedef int (*virEventRemoveHandleFunc)(int);
 
 typedef int (*virEventAddTimeoutFunc)(int, virEventTimeoutCallback, void *);
+typedef void (*virEventUpdateTimeoutFunc)(int, int);
 typedef int (*virEventRemoveTimeoutFunc)(int);
 
 void __virEventRegisterImpl(virEventAddHandleFunc addHandle,
+			    virEventUpdateHandleFunc updateHandle,
 			    virEventRemoveHandleFunc removeHandle,
 			    virEventAddTimeoutFunc addTimeout,
+			    virEventUpdateTimeoutFunc updateTimeout,
 			    virEventRemoveTimeoutFunc removeTimeout);
 
 #define virEventRegisterImpl(ah,rh,at,rt) __virEventRegisterImpl(ah,rh,at,rt)
Index: src/remote_internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/remote_internal.h,v
retrieving revision 1.2
diff -u -p -r1.2 remote_internal.h
--- src/remote_internal.h	26 Jun 2007 23:48:47 -0000	1.2
+++ src/remote_internal.h	14 Sep 2007 02:11:23 -0000
@@ -33,7 +33,9 @@ extern "C" {
 int remoteRegister (void);
 
 #define LIBVIRTD_TLS_PORT "16514"
+#define LIBVIRTD_TLS_PORT_NUM 16514
 #define LIBVIRTD_TCP_PORT "16509"
+#define LIBVIRTD_TCP_PORT_NUM 16509
 #define LIBVIRTD_PRIV_UNIX_SOCKET LOCAL_STATE_DIR "/run/libvirt/libvirt-sock"
 #define LIBVIRTD_PRIV_UNIX_SOCKET_RO LOCAL_STATE_DIR "/run/libvirt/libvirt-sock-ro"
 #define LIBVIRTD_USER_UNIX_SOCKET "/.libvirt/libvirt-sock"


More information about the libvir-list mailing list