]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Added support for Avahi mDNS advertisement
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 19 Sep 2007 01:56:55 +0000 (01:56 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 19 Sep 2007 01:56:55 +0000 (01:56 +0000)
ChangeLog
configure.in
docs/libvir.html
docs/remote.html
libvirt.spec.in
qemud/Makefile.am
qemud/internal.h
qemud/mdns.c [new file with mode: 0644]
qemud/mdns.h [new file with mode: 0644]
qemud/qemud.c

index d61715aa833c5bb755cad5bb8542e4a7bc990fda..d0d5abd4601af2c3abeaf879cb1e67b2a7b2019e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Tue Sep 18 21:34:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
+
+       * configure.in: Added checks for locating Avahi.
+       * qemud/mdns.c, qemud/mdns.h, qemud/Makefile.am: Convenience API to 
+       bridge between state machine provided by Avahi APIs, and the libvirt
+       daemon.
+       * qemud/qemud.c, qemud/internal.h: Register the daemon as an mDNS
+       service under _libvirt._tcp.
+       * docs/libvir.html: Added notes on mdns config params.
+       * libvirt.spec.in: Added avahi-devel as a BuildRequires
+
 Tue Sep 18 20:42:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
 
        * src/event.h, src/event.c: Added new APIs definitions for updating
index c3c93bd975d03e27a4e05953e4926cfc91d137ac..c5afb83d4c2c9e1d89f3ffb35110c74c376ddc3f 100644 (file)
@@ -21,6 +21,8 @@ AC_SUBST(LIBVIRT_VERSION)
 AC_SUBST(LIBVIRT_VERSION_INFO)
 AC_SUBST(LIBVIRT_VERSION_NUMBER)
 
+AVAHI_REQUIRED="0.6.0"
+
 dnl Checks for programs.
 AC_PROG_CC
 AC_PROG_INSTALL
@@ -232,6 +234,9 @@ if test "$with_qemu" = "yes" ; then
                    AC_MSG_ERROR([You must install kernel-headers in order to compile libvirt]))
 fi
 
+dnl Need to test if pkg-config exists
+PKG_PROG_PKG_CONFIG
+
 dnl ==========================================================================
 dnl find libxml2 library, borrowed from xmlsec
 dnl ==========================================================================
@@ -300,6 +305,27 @@ AC_CHECK_TYPE(gnutls_session,
                [enable GnuTLS 1.0 compatibility macros]),,
        [#include <gnutls/gnutls.h>])
 
+
+dnl Avahi library
+AC_ARG_WITH(avahi,
+  [  --with-avahi         use avahi to advertise remote daemon],
+  [],
+  [with_avahi=check])
+
+if test "$with_avahi" = "check" -a "x$PKG_CONFIG" != "x" ; then
+  PKG_CHECK_EXISTS(avahi-client >= $AVAHI_REQUIRED, [with_avahi=yes], [with_avahi=no])
+fi
+
+AVAHI_CFLAGS=
+AVAHI_LIBS=
+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 presense])
+fi
+AM_CONDITIONAL(HAVE_AVAHI, [test "$with_avahi" = "yes"])
+AC_SUBST(AVAHI_CFLAGS)
+AC_SUBST(AVAHI_LIBS)
+
 dnl virsh libraries
 AC_CHECK_LIB(curses, initscr, 
        [VIRSH_LIBS="$VIRSH_LIBS -lcurses"],
@@ -485,6 +511,11 @@ AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Libraries])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([  libxml: $LIBXML_CFLAGS $LIBXML_LIBS])
+if test "$with_avahi" = "yes" ; then
+AC_MSG_NOTICE([   avahi: $AVAHI_CFLAGS $AVAHI_LIBS])
+else
+AC_MSG_NOTICE([   avahi: no])
+fi
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Miscellaneous])
 AC_MSG_NOTICE([])
index 04e2d39d135688043a71c6763a620c20b6c6aabd..8b899a486b8b713a1f0cf0d3b6de29f05cc02e9d 100644 (file)
@@ -2191,6 +2191,25 @@ Blank lines and comments beginning with <code>#</code> are ignored.
 </td>
 </tr>
 
+<tr>
+<td> mdns_adv <i>[0|1]</i> </td>
+<td> 1 (advertise with mDNS) </td>
+<td>
+  If set to 1 then the virtualization service will be advertised over
+  mDNS to hosts on the local LAN segment.
+</td>
+</tr>
+
+<tr>
+<td> mdns_name <i>"name"</i> </td>
+<td> "Virtualization Host HOSTNAME" </td>
+<td>
+  The name to advertise for this host with Avahi mDNS. The default
+  includes the machine's short hostname. This must be unique to the
+  local LAN segment.
+</td>
+</tr>
+
 <tr>
 <td> tls_no_verify_certificate <i>[0|1]</i> </td>
 <td> 0 (certificates are verified) </td>
index d7810f06df112bcafb96562d2a9ef81c4516e0a3..42366021b7aa55eb0e3bd00a174c17d15c872efd 100644 (file)
@@ -471,6 +471,19 @@ Blank lines and comments beginning with <code>#</code> are ignored.
 <td>
   The port number or service name to listen on for unencrypted TCP connections.
 </td>
+</tr><tr><td> mdns_adv <i>[0|1]</i> </td>
+<td> 1 (advertise with mDNS) </td>
+<td>
+  If set to 1 then the virtualization service will be advertised over
+  mDNS to hosts on the local LAN segment.
+</td>
+</tr><tr><td> mdns_name <i>"name"</i> </td>
+<td> "Virtualization Host HOSTNAME" </td>
+<td>
+  The name to advertise for this host with Avahi mDNS. The default
+  includes the machine's short hostname. This must be unique to the
+  local LAN segment.
+</td>
 </tr><tr><td> tls_no_verify_certificate <i>[0|1]</i> </td>
 <td> 0 (certificates are verified) </td>
 <td>
index f6e9dffdb6953ccb34241d65620c3345be2aafad..4ecf0b646c92020119021910c0ab104c10dace4d 100644 (file)
@@ -22,6 +22,7 @@ BuildRequires: readline-devel
 BuildRequires: ncurses-devel
 BuildRequires: gettext
 BuildRequires: gnutls-devel
+BuildRequires: avahi-devel
 Obsoletes: libvir
 ExclusiveArch: i386 x86_64 ia64
 
index 898d719afeb4832c8558e7d05c2475cf3f5a613f..5afa99e75682fb4089ab67681dc5d347b65437c2 100644 (file)
@@ -5,12 +5,25 @@ UUID=$(shell uuidgen)
 
 sbin_PROGRAMS = libvirtd
 
+# 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 \
+       mdns.c mdns.h
+
 libvirtd_SOURCES = \
                qemud.c internal.h \
                protocol.h protocol.c \
                remote_protocol.h remote_protocol.c \
                remote.c \
-                event.c event.h
+               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 +37,12 @@ libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS)
 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)
+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,16 +61,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 $@
index 744e1763814e9ff9491b5951c69cf803487cd0d0..9b1b72125e685cdd10fc86c519684b39ac9bbbf0 100644 (file)
@@ -111,7 +111,7 @@ struct qemud_socket {
     int readonly;
     /* If set, TLS is required on this socket. */
     int tls;
-
+    int port;
     struct qemud_socket *next;
 };
 
@@ -124,6 +124,9 @@ struct qemud_server {
     int sigread;
     char logDir[PATH_MAX];
     unsigned int shutdown : 1;
+#ifdef HAVE_AVAHI
+    struct libvirtd_mdns *mdns;
+#endif
 };
 
 void qemudLog(int priority, const char *fmt, ...)
diff --git a/qemud/mdns.c b/qemud/mdns.c
new file mode 100644 (file)
index 0000000..bd1f548
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * 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@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"
+#include "../src/internal.h"
+
+#define AVAHI_DEBUG(fmt, ...) qemudDebug("AVAHI: " fmt, __VA_ARGS__)
+
+struct libvirtd_mdns_entry {
+    char *type;
+    int port;
+    struct libvirtd_mdns_entry *next;
+};
+
+struct libvirtd_mdns_group {
+    struct libvirtd_mdns *mdns;
+    AvahiEntryGroup *handle;
+    char *name;
+    struct libvirtd_mdns_entry *entry;
+    struct libvirtd_mdns_group *next;
+};
+
+struct libvirtd_mdns {
+    AvahiClient *client;
+    AvahiPoll *poller;
+    struct libvirtd_mdns_group *group;
+};
+
+/* Avahi API requires this struct names in the app :-( */
+struct AvahiWatch {
+    int fd;
+    int revents;
+    AvahiWatchCallback callback;
+    void *userdata;
+};
+
+/* Avahi API requires this struct names in the app :-( */
+struct AvahiTimeout {
+    int timer;
+    AvahiTimeoutCallback callback;
+    void  *userdata;
+};
+
+
+static void libvirtd_mdns_create_services(struct libvirtd_mdns_group *group);
+
+/* Called whenever the entry group state changes */
+static void libvirtd_mdns_group_callback(AvahiEntryGroup *g ATTRIBUTE_UNUSED, AvahiEntryGroupState state, void *userdata) {
+    struct libvirtd_mdns_group *group = (struct libvirtd_mdns_group *)userdata;
+
+    switch (state) {
+    case AVAHI_ENTRY_GROUP_ESTABLISHED:
+        /* The entry group has been established successfully */
+        AVAHI_DEBUG("Group '%s' established", group->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(group->name);
+            free(group->name);
+            group->name = n;
+
+            AVAHI_DEBUG("Group name collision, renaming service to '%s'", group->name);
+
+            /* And recreate the services */
+            libvirtd_mdns_create_services(group);
+        }
+        break;
+
+    case AVAHI_ENTRY_GROUP_FAILURE :
+        AVAHI_DEBUG("Group failure: %s", avahi_strerror(avahi_client_errno(group->mdns->client)));
+
+        /* 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 libvirtd_mdns_create_services(struct libvirtd_mdns_group *group) {
+    struct libvirtd_mdns *mdns = group->mdns;
+    struct libvirtd_mdns_entry *entry;
+    int ret;
+    AVAHI_DEBUG("Adding services to '%s'", group->name);
+
+    /* If we've no services to advertise, just reset the group to make
+     * sure it is emptied of any previously advertised services */
+    if (!group->entry) {
+        if (group->handle)
+            avahi_entry_group_reset(group->handle);
+        return;
+    }
+
+    /* If this is the first time we're called, let's create a new entry group */
+    if (!group->handle) {
+        AVAHI_DEBUG("Creating initial group %s", group->name);
+        if (!(group->handle = avahi_entry_group_new(mdns->client, libvirtd_mdns_group_callback, group))) {
+            AVAHI_DEBUG("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(mdns->client)));
+            return;
+        }
+    }
+
+    entry = group->entry;
+    while (entry) {
+        if ((ret = avahi_entry_group_add_service(group->handle,
+                                                 AVAHI_IF_UNSPEC,
+                                                 AVAHI_PROTO_UNSPEC,
+                                                 0,
+                                                 group->name,
+                                                 entry->type,
+                                                 NULL,
+                                                 NULL, 
+                                                 entry->port,
+                                                 NULL)) < 0) {
+            AVAHI_DEBUG("Failed to add %s service on port %d: %s", 
+                        entry->type, entry->port, avahi_strerror(ret));
+            avahi_entry_group_reset(group->handle);
+            return;
+        }
+        entry = entry->next;
+    }
+
+    /* Tell the server to register the service */
+    if ((ret = avahi_entry_group_commit(group->handle)) < 0) {
+        avahi_entry_group_reset(group->handle);
+        AVAHI_DEBUG("Failed to commit entry_group: %s", avahi_strerror(ret));
+        return;
+    }
+}
+
+
+static void libvirtd_mdns_client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+    struct libvirtd_mdns *mdns = (struct libvirtd_mdns *)userdata;
+    struct libvirtd_mdns_group *group = mdns->group;
+    if (!mdns->client)
+        mdns->client = 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 */
+            AVAHI_DEBUG("Client running %p", mdns->client);
+            group = mdns->group;
+            while (group) {
+                libvirtd_mdns_create_services(group);
+                group = group->next;
+            }
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            AVAHI_DEBUG("Client failure: %s", avahi_strerror(avahi_client_errno(c)));
+            libvirtd_mdns_stop(mdns);
+            libvirtd_mdns_start(mdns);
+            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. */
+            AVAHI_DEBUG("Client collision/connecting %p", mdns->client);
+            group = mdns->group;
+            while (group) {
+                if (group->handle)
+                    avahi_entry_group_reset(group->handle);
+                group = group->next;
+            }
+            break;
+
+        case AVAHI_CLIENT_CONNECTING:
+            AVAHI_DEBUG("Client connecting.... %p", mdns->client);
+            ;
+    }
+}
+
+
+static void libvirtd_mdns_watch_dispatch(int fd, int events, void *opaque)
+{
+    AvahiWatch *w = (AvahiWatch*)opaque;
+    AVAHI_DEBUG("Dispatch watch FD %d Event %d", fd, events);
+    w->revents = events;
+    w->callback(w, fd, events, w->userdata);
+}
+
+static AvahiWatch *libvirtd_mdns_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;
+
+    AVAHI_DEBUG("New handle %p FD %d Event %d", w, w->fd, event);
+    if (virEventAddHandleImpl(fd, event, libvirtd_mdns_watch_dispatch, w) < 0) {
+        free(w);
+        return NULL;
+    }
+
+    return w;
+}
+
+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);
+}
+
+static AvahiWatchEvent libvirtd_mdns_watch_get_events(AvahiWatch *w)
+{
+    AVAHI_DEBUG("Get handle events %p %d", w, w->fd);
+    return w->revents;
+}
+
+static void libvirtd_mdns_watch_free(AvahiWatch *w)
+{
+    AVAHI_DEBUG("Free handle %p %d", w, w->fd);
+    virEventRemoveHandleImpl(w->fd);
+    free(w);
+}
+
+static void libvirtd_mdns_timeout_dispatch(int timer ATTRIBUTE_UNUSED, void *opaque)
+{
+    AvahiTimeout *t = (AvahiTimeout*)opaque;
+    AVAHI_DEBUG("Dispatch timeout %p %d", t, timer);
+    virEventUpdateTimeoutImpl(t->timer, -1);
+    t->callback(t, t->userdata);
+}
+
+static AvahiTimeout *libvirtd_mdns_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;
+    AVAHI_DEBUG("Add timeout %p TV %p", t, tv);
+    if (!t)
+        return NULL;
+
+    if (gettimeofday(&now, NULL) < 0) {
+        free(t);
+        return NULL;
+    }
+
+    AVAHI_DEBUG("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);
+    if (tv) {
+        thenms = (tv->tv_sec * 1000ll) + (tv->tv_usec/1000ll);
+        timeout = thenms > nowms ? nowms - thenms : 0;
+        if (timeout < 0)
+            timeout = 0;
+    } else {
+        timeout = -1;
+    }
+
+    t->timer = virEventAddTimeoutImpl(timeout, libvirtd_mdns_timeout_dispatch, t);
+    t->callback = cb;
+    t->userdata = userdata;
+
+    if (t->timer < 0) {
+        free(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+static void libvirtd_mdns_timeout_update(AvahiTimeout *t, const struct timeval *tv)
+{
+    struct timeval now;
+    long long nowms, thenms, timeout;
+    AVAHI_DEBUG("Update timeout %p TV %p", t, tv);
+    if (gettimeofday(&now, NULL) < 0) {
+        free(t);
+        return;
+    }
+
+    nowms = (now.tv_sec * 1000ll) + (now.tv_usec / 1000ll);
+    if (tv) {
+        thenms = ((tv->tv_sec * 1000ll) + (tv->tv_usec/1000ll));
+        timeout = thenms > nowms ? nowms - thenms : 0;
+        if (timeout < 0)
+            timeout = 0;
+    } else {
+        timeout = -1;
+    }
+
+    virEventUpdateTimeoutImpl(t->timer, timeout);
+}
+
+static void libvirtd_mdns_timeout_free(AvahiTimeout *t)
+{
+    AVAHI_DEBUG("Free timeout %p", t);
+    virEventRemoveTimeoutImpl(t->timer);
+    free(t);
+}
+
+
+static AvahiPoll *libvirtd_create_poll(void)
+{
+    AvahiPoll *p = malloc(sizeof(AvahiPoll));
+    if (!p)
+        return NULL;
+
+    p->userdata = NULL;
+
+    p->watch_new = libvirtd_mdns_watch_new;
+    p->watch_update = libvirtd_mdns_watch_update;
+    p->watch_get_events = libvirtd_mdns_watch_get_events;
+    p->watch_free = libvirtd_mdns_watch_free;
+
+    p->timeout_new = libvirtd_mdns_timeout_new;
+    p->timeout_update = libvirtd_mdns_timeout_update;
+    p->timeout_free = libvirtd_mdns_timeout_free;
+
+    return p;
+}
+
+struct libvirtd_mdns *libvirtd_mdns_new(void)
+{
+    struct libvirtd_mdns *mdns = malloc(sizeof(struct libvirtd_mdns));
+    if (!mdns)
+        return NULL;
+    memset(mdns, 0, sizeof(*mdns));
+
+    /* Allocate main loop object */
+    if (!(mdns->poller = libvirtd_create_poll())) {
+        free(mdns);
+        return NULL;
+    }
+
+    return mdns;
+}
+
+int libvirtd_mdns_start(struct libvirtd_mdns *mdns)
+{
+    int error;
+    AVAHI_DEBUG("Starting client %p", mdns);
+    mdns->client = avahi_client_new(mdns->poller, AVAHI_CLIENT_NO_FAIL, libvirtd_mdns_client_callback, mdns, &error);
+
+    if (!mdns->client) {
+        AVAHI_DEBUG("Failed to create mDNS client: %s", avahi_strerror(error));
+        return -1;
+    }
+
+    return 0;
+}
+
+struct libvirtd_mdns_group *libvirtd_mdns_add_group(struct libvirtd_mdns *mdns, const char *name) {
+    struct libvirtd_mdns_group *group = malloc(sizeof(struct libvirtd_mdns_group));
+
+    AVAHI_DEBUG("Adding group '%s'", name);
+    if (!group)
+        return NULL;
+
+    memset(group, 0, sizeof(*group));
+    if (!(group->name = strdup(name))) {
+        free(group);
+        return NULL;
+    }
+    group->mdns = mdns;
+    group->next = mdns->group;
+    mdns->group = group;
+    return group;
+}
+
+void libvirtd_mdns_remove_group(struct libvirtd_mdns *mdns, struct libvirtd_mdns_group *group) {
+    struct libvirtd_mdns_group *tmp = mdns->group, *prev = NULL;
+
+    while (tmp) {
+        if (tmp == group) {
+            free(group->name);
+            if (prev)
+                prev->next = group->next;
+            else
+                group->mdns->group = group->next;
+            free(group);
+            return;
+        }
+        prev = tmp;
+        tmp = tmp->next;
+    }
+}
+
+struct libvirtd_mdns_entry *libvirtd_mdns_add_entry(struct libvirtd_mdns_group *group, const char *type, int port) {
+    struct libvirtd_mdns_entry *entry = malloc(sizeof(struct libvirtd_mdns_entry));
+
+    AVAHI_DEBUG("Adding entry %s %d to group %s", type, port, group->name);
+    if (!entry)
+        return NULL;
+
+    entry->port = port;
+    if (!(entry->type = strdup(type))) {
+        free(entry);
+        return NULL;
+    }
+    entry->next = group->entry;
+    group->entry = entry;
+    return entry;
+}
+
+void libvirtd_mdns_remove_entry(struct libvirtd_mdns_group *group, struct libvirtd_mdns_entry *entry) {
+    struct libvirtd_mdns_entry *tmp = group->entry, *prev = NULL;
+
+    while (tmp) {
+        if (tmp == entry) {
+            free(entry->type);
+            if (prev)
+                prev->next = entry->next;
+            else
+                group->entry = entry->next;
+            return;
+        }
+        prev = tmp;
+        tmp = tmp->next;
+    }
+}
+
+void libvirtd_mdns_stop(struct libvirtd_mdns *mdns)
+{
+    struct libvirtd_mdns_group *group = mdns->group;
+    while (group) {
+        if (group->handle) {
+            avahi_entry_group_free(group->handle);
+            group->handle = NULL;
+        }
+        group = group->next;
+    }
+    if (mdns->client)
+        avahi_client_free(mdns->client);
+    mdns->client = NULL;
+}
+
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/mdns.h b/qemud/mdns.h
new file mode 100644 (file)
index 0000000..71bf8ad
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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@redhat.com>
+ */
+
+#include "internal.h"
+
+#ifndef __VIRTD_MDNS_H__
+#define __VIRTD_MDNS_H__
+
+struct libvirtd_mdns;
+struct libvirtd_mdns_group;
+struct libvirtd_mdns_entry;
+
+/**
+ * Prepares a new mdns manager object for use
+ */
+struct libvirtd_mdns *libvirtd_mdns_new(void);
+
+/**
+ * Starts the mdns client, advertizing any groups/entries currently registered
+ * 
+ * @mdns: manager to start advertizing
+ *
+ * Starts the mdns client. Services may not be immediately visible, since
+ * it may asynchronously wait for the mdns service to startup
+ *
+ * returns -1 upon failure, 0 upon success.
+ */
+int libvirtd_mdns_start(struct libvirtd_mdns *mdns);
+
+/**
+ * Stops the mdns client, removing any advertizements
+ *
+ * @mdns: manager to start advertizing
+ *
+ */
+void libvirtd_mdns_stop(struct libvirtd_mdns *mdns);
+
+/**
+ * Adds a group container for advertizement
+ *
+ * @mdns manager to attach the group to
+ * @name unique human readable service name
+ *
+ * returns the group record, or NULL upon failure
+ */
+struct libvirtd_mdns_group *libvirtd_mdns_add_group(struct libvirtd_mdns *mdns, const char *name);
+
+/**
+ * Removes a group container from advertizement
+ *
+ * @mdns amanger to detatch group from
+ * @group group to remove
+ */
+void libvirtd_mdns_remove_group(struct libvirtd_mdns *mdns, struct libvirtd_mdns_group *group);
+
+/**
+ * Adds a service entry in a group
+ *
+ * @group group to attach the entry to
+ * @type service type string
+ * @port tcp port number
+ *
+ * returns the service record, or NULL upon failure
+ */
+struct libvirtd_mdns_entry *libvirtd_mdns_add_entry(struct libvirtd_mdns_group *group, const char *type, int port);
+
+/**
+ * Removes a service entry from a group
+ *
+ * @group group to deteach service entry from
+ * @entry service entry to remove
+ */
+void libvirtd_mdns_remove_entry(struct libvirtd_mdns_group *group, struct libvirtd_mdns_entry *entry);
+
+#endif /* __VIRTD_MDNS_H__ */
index 4f9eef93b37710cb2881d02bc8cd6f79bb6d4c09..a2554b8050f803841c330033a59d45ade183b710 100644 (file)
@@ -56,6 +56,9 @@
 #include "../src/remote_internal.h"
 #include "../src/conf.h"
 #include "event.h"
+#ifdef HAVE_AVAHI
+#include "mdns.h"
+#endif
 
 static int godaemon = 0;        /* -d: Be a daemon */
 static int verbose = 0;         /* -v: Verbose mode */
@@ -69,6 +72,11 @@ static int listen_tcp = 0;
 static const char *tls_port = LIBVIRTD_TLS_PORT;
 static const char *tcp_port = LIBVIRTD_TCP_PORT;
 
+#ifdef HAVE_AVAHI
+static int mdns_adv = 1;
+static const char *mdns_name = NULL;
+#endif
+
 static int tls_no_verify_certificate = 0;
 static int tls_no_verify_address = 0;
 static const char **tls_allowed_ip_list = 0;
@@ -448,6 +456,7 @@ static int qemudListenUnix(struct qemud_server *server,
     }
 
     sock->readonly = readonly;
+    sock->port = -1;
 
     if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
         qemudLog(QEMUD_ERR, "Failed to create socket: %s",
@@ -570,6 +579,9 @@ remoteListenTCP (struct qemud_server *server,
         return -1;
 
     for (i = 0; i < nfds; ++i) {
+        struct sockaddr_storage sa;
+        socklen_t salen = sizeof(sa);
+
         sock = calloc (1, sizeof *sock);
 
         if (!sock) {
@@ -586,6 +598,16 @@ remoteListenTCP (struct qemud_server *server,
         sock->fd = fds[i];
         sock->tls = tls;
 
+        if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0)
+            return -1;
+
+        if (sa.ss_family == AF_INET)
+            sock->port = htons(((struct sockaddr_in*)&sa)->sin_port);
+        else if (sa.ss_family == AF_INET6)
+            sock->port = htons(((struct sockaddr_in6*)&sa)->sin6_port);
+        else
+            sock->port = -1;
+
         if (qemudSetCloseExec(sock->fd) < 0 ||
             qemudSetNonBlock(sock->fd) < 0)
             return -1;
@@ -665,6 +687,7 @@ static int qemudInitPaths(struct qemud_server *server,
 
 static struct qemud_server *qemudInitialize(int sigread) {
     struct qemud_server *server;
+    struct qemud_socket *sock;
     char sockname[PATH_MAX];
     char roSockname[PATH_MAX];
 
@@ -709,11 +732,55 @@ static struct qemud_server *qemudInitialize(int sigread) {
         }
     }
 
+#ifdef HAVE_AVAHI
+    if (getuid() == 0 && mdns_adv) {
+        struct libvirtd_mdns_group *group;
+        int port = 0;
+
+        server->mdns = libvirtd_mdns_new();
+
+        if (!mdns_name) {
+            char groupname[64], localhost[HOST_NAME_MAX+1], *tmp;
+            /* Extract the host part of the potentially FQDN */
+            gethostname(localhost, HOST_NAME_MAX);
+            localhost[HOST_NAME_MAX] = '\0';
+            if ((tmp = strchr(localhost, '.')))
+                *tmp = '\0';
+            snprintf(groupname, sizeof(groupname)-1, "Virtualization Host %s", localhost);
+            groupname[sizeof(groupname)-1] = '\0';
+            group = libvirtd_mdns_add_group(server->mdns, groupname);
+        } else {
+            group = libvirtd_mdns_add_group(server->mdns, mdns_name);
+        }
+
+        /* 
+         * See if there's a TLS enabled port we can advertise. Cowardly
+         * don't bother to advertise TCP since we don't want people using
+         * them for real world apps
+         */
+        sock = server->sockets;
+        while (sock) {
+            if (sock->port != -1 && sock->tls) {
+                port = sock->port;
+                break;
+            }
+            sock = sock->next;
+        }
+    
+        /*
+         * Add the primary entry - we choose SSH because its most likely to always
+         * be available
+         */
+        libvirtd_mdns_add_entry(group, "_libvirt._tcp", port);
+        libvirtd_mdns_start(server->mdns);
+    }
+#endif
+
     return server;
 
  cleanup:
     if (server) {
-        struct qemud_socket *sock = server->sockets;
+        sock = server->sockets;
         while (sock) {
             close(sock->fd);
             sock = sock->next;
@@ -1489,6 +1556,16 @@ remoteReadConfigFile (const char *filename)
     CHECK_TYPE ("tcp_port", VIR_CONF_STRING);
     tcp_port = p ? strdup (p->str) : tcp_port;
 
+#ifdef HAVE_AVAHI
+    p = virConfGetValue (conf, "mdns_adv");
+    CHECK_TYPE ("mdns_adv", VIR_CONF_LONG);
+    mdns_adv = p ? p->l : mdns_adv;
+
+    p = virConfGetValue (conf, "mdns_name");
+    CHECK_TYPE ("mdns_name", VIR_CONF_STRING);
+    mdns_name = p ? strdup (p->str) : NULL;
+#endif
+
     p = virConfGetValue (conf, "tls_no_verify_certificate");
     CHECK_TYPE ("tls_no_verify_certificate", VIR_CONF_LONG);
     tls_no_verify_certificate = p ? p->l : tls_no_verify_certificate;