RFC: Broadcast our presence with avahi

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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  -=| 
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@xxxxxxxxxx>
+ */
+
+#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@xxxxxxxxxx>
+ */
+
+#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"
--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]