Allow RPC servers to advertize themselves using MDNS,
via Avahi

* src/rpc/virnetserver.c, src/rpc/virnetserver.h: Allow
  registration of MDNS services via avahi
* src/rpc/virnetserverservice.c, src/rpc/virnetserverservice.h: Add
  API to fetch the listen port number
* src/rpc/virnetsocket.c, src/rpc/virnetsocket.h: Add API to
  fetch the local port number
* src/rpc/virnetservermdns.c, src/rpc/virnetservermdns.h: Represent
  an MDNS advertisement
---
 cfg.mk                        |    3 +
 src/Makefile.am               |    9 +
 src/rpc/virnetserver.c        |   47 +++-
 src/rpc/virnetserver.h        |    4 +-
 src/rpc/virnetservermdns.c    |  617 +++++++++++++++++++++++++++++++++++++++++
 src/rpc/virnetservermdns.h    |  109 ++++++++
 src/rpc/virnetserverservice.c |    8 +
 src/rpc/virnetserverservice.h |    2 +
 src/rpc/virnetsocket.c        |    6 +
 src/rpc/virnetsocket.h        |    2 +
 10 files changed, 805 insertions(+), 2 deletions(-)
 create mode 100644 src/rpc/virnetservermdns.c
 create mode 100644 src/rpc/virnetservermdns.h

diff --git a/cfg.mk b/cfg.mk
index 436970c..8c71154 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -117,6 +117,9 @@ useless_free_options =                              \
   --name=virNetMessageFree                      \
   --name=virNetServerFree                       \
   --name=virNetServerClientFree                 \
+  --name=virNetServerMDNSFree                   \
+  --name=virNetServerMDNSEntryFree              \
+  --name=virNetServerMDNSGroupFree              \
   --name=virNetServerProgramFree                \
   --name=virNetServerServiceFree                \
   --name=virNetSocketFree                       \
diff --git a/src/Makefile.am b/src/Makefile.am
index 0ab4ffd..38b815b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1255,10 +1255,19 @@ libvirt_net_rpc_server_la_SOURCES = \
        rpc/virnetserverservice.h rpc/virnetserverservice.c \
        rpc/virnetserverclient.h rpc/virnetserverclient.c \
        rpc/virnetserver.h rpc/virnetserver.c
+if HAVE_AVAHI
+libvirt_net_rpc_server_la_SOURCES += \
+       rpc/virnetservermdns.h rpc/virnetservermdns.c
+else
+EXTRA_DIST += \
+       rpc/virnetservermdns.h rpc/virnetservermdns.c
+endif
 libvirt_net_rpc_server_la_CFLAGS = \
+                       $(AVAHI_CFLAGS) \
                        $(AM_CFLAGS)
 libvirt_net_rpc_server_la_LDFLAGS = \
                        $(AM_LDFLAGS) \
+                       $(AVAHI_LIBS) \
                        $(CYGWIN_EXTRA_LDFLAGS) \
                        $(MINGW_EXTRA_LDFLAGS)l
 libvirt_net_rpc_server_la_LIBADD = \
diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c
index 547bab3..f78f8d6 100644
--- a/src/rpc/virnetserver.c
+++ b/src/rpc/virnetserver.c
@@ -35,6 +35,9 @@
 #include "util.h"
 #include "files.h"
 #include "event.h"
+#if HAVE_AVAHI
+#include "virnetservermdns.h"
+#endif
 
 #define VIR_FROM_THIS VIR_FROM_RPC
 
@@ -75,6 +78,12 @@ struct _virNetServer {
     int sigwrite;
     int sigwatch;
 
+    char *mdnsGroupName;
+#if HAVE_AVAHI
+    virNetServerMDNSPtr mdns;
+    virNetServerMDNSGroupPtr mdnsGroup;
+#endif
+
     size_t nservices;
     virNetServerServicePtr *services;
 
@@ -260,6 +269,7 @@ static void virNetServerFatalSignal(int sig, siginfo_t * 
siginfo ATTRIBUTE_UNUSE
 virNetServerPtr virNetServerNew(size_t min_workers,
                                 size_t max_workers,
                                 size_t max_clients,
+                                const char *mdnsGroupName,
                                 virNetServerClientInitHook clientInitHook)
 {
     virNetServerPtr srv;
@@ -282,6 +292,19 @@ virNetServerPtr virNetServerNew(size_t min_workers,
     srv->clientInitHook = clientInitHook;
     srv->privileged = geteuid() == 0 ? true : false;
 
+    if (!(srv->mdnsGroupName = strdup(mdnsGroupName))) {
+        virReportOOMError();
+        goto error;
+    }
+#if HAVE_AVAHI
+    if (srv->mdnsGroupName) {
+        if (!(srv->mdns = virNetServerMDNSNew()))
+            goto error;
+        if (!(srv->mdnsGroup = virNetServerMDNSAddGroup(srv->mdns, 
mdnsGroupName)))
+            goto error;
+    }
+#endif
+
     if (virMutexInit(&srv->lock) < 0) {
         virNetError(VIR_ERR_INTERNAL_ERROR, "%s",
                     _("cannot initialize mutex"));
@@ -503,13 +526,26 @@ error:
 
 
 int virNetServerAddService(virNetServerPtr srv,
-                           virNetServerServicePtr svc)
+                           virNetServerServicePtr svc,
+                           const char *mdnsEntryName ATTRIBUTE_UNUSED)
 {
     virNetServerLock(srv);
 
     if (VIR_EXPAND_N(srv->services, srv->nservices, 1) < 0)
         goto no_memory;
 
+#if HAVE_AVAHI
+    if (mdnsEntryName) {
+        int port = virNetServerServiceGetPort(svc);
+        virNetServerMDNSEntryPtr entry;
+
+        if (!(entry = virNetServerMDNSAddEntry(srv->mdnsGroup,
+                                               mdnsEntryName,
+                                               port)))
+            goto error;
+    }
+#endif
+
     srv->services[srv->nservices-1] = svc;
     virNetServerServiceRef(svc);
 
@@ -522,6 +558,9 @@ int virNetServerAddService(virNetServerPtr srv,
 
 no_memory:
     virReportOOMError();
+#if HAVE_AVAHI
+error:
+#endif
     virNetServerUnlock(srv);
     return -1;
 }
@@ -591,6 +630,12 @@ void virNetServerRun(virNetServerPtr srv)
 
     virNetServerLock(srv);
 
+#if HAVE_AVAHI
+    if (srv->mdns &&
+        virNetServerMDNSStart(srv->mdns) < 0)
+        goto cleanup;
+#endif
+
     if (srv->autoShutdownTimeout &&
         (timerid = virEventAddTimeout(-1,
                                       virNetServerAutoShutdownTimer,
diff --git a/src/rpc/virnetserver.h b/src/rpc/virnetserver.h
index d8d7c8e..ed09ecf 100644
--- a/src/rpc/virnetserver.h
+++ b/src/rpc/virnetserver.h
@@ -38,6 +38,7 @@ typedef int (*virNetServerClientInitHook)(virNetServerPtr srv,
 virNetServerPtr virNetServerNew(size_t min_workers,
                                 size_t max_workers,
                                 size_t max_clients,
+                                const char *mdnsGroupName,
                                 virNetServerClientInitHook clientInitHook);
 
 typedef int (*virNetServerAutoShutdownFunc)(virNetServerPtr srv, void *opaque);
@@ -59,7 +60,8 @@ int virNetServerAddSignalHandler(virNetServerPtr srv,
                                  void *opaque);
 
 int virNetServerAddService(virNetServerPtr srv,
-                           virNetServerServicePtr svc);
+                           virNetServerServicePtr svc,
+                           const char *mdnsEntryName);
 
 int virNetServerAddProgram(virNetServerPtr srv,
                            virNetServerProgramPtr prog);
diff --git a/src/rpc/virnetservermdns.c b/src/rpc/virnetservermdns.c
new file mode 100644
index 0000000..c6faa3e
--- /dev/null
+++ b/src/rpc/virnetservermdns.c
@@ -0,0 +1,617 @@
+/*
+ * virnetservermdns.c: advertise server sockets
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * 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 <berra...@redhat.com>
+ */
+
+#include <config.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.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 "virnetservermdns.h"
+#include "event.h"
+#include "event_poll.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+
+#define virNetError(code, ...)                                    \
+    virReportErrorHelper(NULL, VIR_FROM_RPC, code, __FILE__,      \
+                         __FUNCTION__, __LINE__, __VA_ARGS__)
+
+struct _virNetServerMDNSEntry {
+    char *type;
+    int port;
+    virNetServerMDNSEntryPtr next;
+};
+
+struct _virNetServerMDNSGroup {
+    virNetServerMDNSPtr mdns;
+    AvahiEntryGroup *handle;
+    char *name;
+    virNetServerMDNSEntryPtr entry;
+    virNetServerMDNSGroupPtr next;
+};
+
+struct _virNetServerMDNS {
+    AvahiClient *client;
+    AvahiPoll *poller;
+    virNetServerMDNSGroupPtr group;
+};
+
+/* Avahi API requires this struct name in the app :-( */
+struct AvahiWatch {
+    int watch;
+    int fd;
+    int revents;
+    AvahiWatchCallback callback;
+    void *userdata;
+};
+
+/* Avahi API requires this struct name in the app :-( */
+struct AvahiTimeout {
+    int timer;
+    AvahiTimeoutCallback callback;
+    void  *userdata;
+};
+
+static void virNetServerMDNSCreateServices(virNetServerMDNSGroupPtr group);
+
+/* Called whenever the entry group state changes */
+static void virNetServerMDNSGroupCallback(AvahiEntryGroup *g ATTRIBUTE_UNUSED,
+                                          AvahiEntryGroupState state,
+                                          void *data)
+{
+    virNetServerMDNSGroupPtr group = data;
+
+    switch (state) {
+    case AVAHI_ENTRY_GROUP_ESTABLISHED:
+        /* The entry group has been established successfully */
+        VIR_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);
+            VIR_FREE(group->name);
+            group->name = n;
+
+            VIR_DEBUG("Group name collision, renaming service to '%s'", 
group->name);
+
+            /* And recreate the services */
+            virNetServerMDNSCreateServices(group);
+        }
+        break;
+
+    case AVAHI_ENTRY_GROUP_FAILURE :
+        VIR_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 virNetServerMDNSCreateServices(virNetServerMDNSGroupPtr group)
+{
+    virNetServerMDNSPtr mdns = group->mdns;
+    virNetServerMDNSEntryPtr entry;
+    int ret;
+    VIR_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) {
+        VIR_DEBUG("Creating initial group %s", group->name);
+        if (!(group->handle =
+              avahi_entry_group_new(mdns->client,
+                                    virNetServerMDNSGroupCallback,
+                                    group))) {
+            VIR_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) {
+            VIR_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);
+        VIR_DEBUG("Failed to commit entry_group: %s",
+                  avahi_strerror(ret));
+        return;
+    }
+}
+
+
+static void virNetServerMDNSClientCallback(AvahiClient *c,
+                                           AvahiClientState state,
+                                           void *data)
+{
+    virNetServerMDNSPtr mdns = data;
+    virNetServerMDNSGroupPtr group;
+    if (!mdns->client)
+        mdns->client = c;
+
+    VIR_DEBUG("Callback state=%d", state);
+
+    /* 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 */
+            VIR_DEBUG("Client running %p", mdns->client);
+            group = mdns->group;
+            while (group) {
+                virNetServerMDNSCreateServices(group);
+                group = group->next;
+            }
+            break;
+
+        case AVAHI_CLIENT_FAILURE:
+            VIR_DEBUG("Client failure: %s",
+                      avahi_strerror(avahi_client_errno(c)));
+            virNetServerMDNSStop(mdns);
+            virNetServerMDNSStart(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 established. */
+            VIR_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:
+            VIR_DEBUG("Client connecting.... %p", mdns->client);
+            ;
+    }
+}
+
+
+static void virNetServerMDNSWatchDispatch(int watch, int fd, int events, void 
*opaque)
+{
+    AvahiWatch *w = opaque;
+    int fd_events = virEventPollToNativeEvents(events);
+    VIR_DEBUG("Dispatch watch %d FD %d Event %d", watch, fd, fd_events);
+    w->revents = fd_events;
+    w->callback(w, fd, fd_events, w->userdata);
+}
+
+static void virNetServerMDNSWatchDofree(void *w)
+{
+    VIR_FREE(w);
+}
+
+
+static AvahiWatch *virNetServerMDNSWatchNew(const AvahiPoll *api 
ATTRIBUTE_UNUSED,
+                                            int fd, AvahiWatchEvent event,
+                                            AvahiWatchCallback cb, void 
*userdata)
+{
+    AvahiWatch *w;
+    virEventHandleType hEvents;
+    if (VIR_ALLOC(w) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    w->fd = fd;
+    w->revents = 0;
+    w->callback = cb;
+    w->userdata = userdata;
+
+    VIR_DEBUG("New handle %p FD %d Event %d", w, w->fd, event);
+    hEvents = virEventPollFromNativeEvents(event);
+    if ((w->watch = virEventAddHandle(fd, hEvents,
+                                      virNetServerMDNSWatchDispatch,
+                                      w,
+                                      virNetServerMDNSWatchDofree)) < 0) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("Failed to add watch for fd %d events %d"), fd, hEvents);
+        VIR_FREE(w);
+        return NULL;
+    }
+
+    return w;
+}
+
+static void virNetServerMDNSWatchUpdate(AvahiWatch *w, AvahiWatchEvent event)
+{
+    VIR_DEBUG("Update handle %p FD %d Event %d", w, w->fd, event);
+    virEventUpdateHandle(w->watch, event);
+}
+
+static AvahiWatchEvent virNetServerMDNSWatchGetEvents(AvahiWatch *w)
+{
+    VIR_DEBUG("Get handle events %p %d", w, w->fd);
+    return w->revents;
+}
+
+static void virNetServerMDNSWatchFree(AvahiWatch *w)
+{
+    VIR_DEBUG("Free handle %p %d", w, w->fd);
+    virEventRemoveHandle(w->watch);
+}
+
+static void virNetServerMDNSTimeoutDispatch(int timer ATTRIBUTE_UNUSED, void 
*opaque)
+{
+    AvahiTimeout *t = (AvahiTimeout*)opaque;
+    VIR_DEBUG("Dispatch timeout %p %d", t, timer);
+    virEventUpdateTimeout(t->timer, -1);
+    t->callback(t, t->userdata);
+}
+
+static void virNetServerMDNSTimeoutDofree(void *t)
+{
+    VIR_FREE(t);
+}
+
+static AvahiTimeout *virNetServerMDNSTimeoutNew(const AvahiPoll *api 
ATTRIBUTE_UNUSED,
+                                                const struct timeval *tv,
+                                                AvahiTimeoutCallback cb,
+                                                void *userdata)
+{
+    AvahiTimeout *t;
+    struct timeval now;
+    long long nowms, thenms, timeout;
+    VIR_DEBUG("Add timeout TV %p", tv);
+    if (VIR_ALLOC(t) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    if (gettimeofday(&now, NULL) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to get current time"));
+        VIR_FREE(t);
+        return NULL;
+    }
+
+    VIR_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 = virEventAddTimeout(timeout,
+                                  virNetServerMDNSTimeoutDispatch,
+                                  t,
+                                  virNetServerMDNSTimeoutDofree);
+    t->callback = cb;
+    t->userdata = userdata;
+
+    if (t->timer < 0) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("Failed to add timer with timeout %d"), (int)timeout);
+        VIR_FREE(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+static void virNetServerMDNSTimeoutUpdate(AvahiTimeout *t, const struct 
timeval *tv)
+{
+    struct timeval now;
+    long long nowms, thenms, timeout;
+    VIR_DEBUG("Update timeout %p TV %p", t, tv);
+    if (gettimeofday(&now, NULL) < 0) {
+        VIR_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;
+    }
+
+    virEventUpdateTimeout(t->timer, timeout);
+}
+
+static void virNetServerMDNSTimeoutFree(AvahiTimeout *t)
+{
+    VIR_DEBUG("Free timeout %p", t);
+    virEventRemoveTimeout(t->timer);
+}
+
+
+static AvahiPoll *virNetServerMDNSCreatePoll(void)
+{
+    AvahiPoll *p;
+    if (VIR_ALLOC(p) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    p->userdata = NULL;
+
+    p->watch_new = virNetServerMDNSWatchNew;
+    p->watch_update = virNetServerMDNSWatchUpdate;
+    p->watch_get_events = virNetServerMDNSWatchGetEvents;
+    p->watch_free = virNetServerMDNSWatchFree;
+
+    p->timeout_new = virNetServerMDNSTimeoutNew;
+    p->timeout_update = virNetServerMDNSTimeoutUpdate;
+    p->timeout_free = virNetServerMDNSTimeoutFree;
+
+    return p;
+}
+
+
+virNetServerMDNS *virNetServerMDNSNew(void)
+{
+    virNetServerMDNS *mdns;
+    if (VIR_ALLOC(mdns) < 0)
+        return NULL;
+
+    /* Allocate main loop object */
+    if (!(mdns->poller = virNetServerMDNSCreatePoll())) {
+        VIR_FREE(mdns);
+        return NULL;
+    }
+
+    return mdns;
+}
+
+
+int virNetServerMDNSStart(virNetServerMDNS *mdns)
+{
+    int error;
+    VIR_DEBUG("Starting client %p", mdns);
+    mdns->client = avahi_client_new(mdns->poller,
+                                    AVAHI_CLIENT_NO_FAIL,
+                                    virNetServerMDNSClientCallback,
+                                    mdns, &error);
+
+    if (!mdns->client) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("Failed to create mDNS client: %s"),
+                    avahi_strerror(error));
+        return -1;
+    }
+
+    return 0;
+}
+
+
+virNetServerMDNSGroupPtr virNetServerMDNSAddGroup(virNetServerMDNS *mdns,
+                                                  const char *name)
+{
+    virNetServerMDNSGroupPtr group;
+
+    VIR_DEBUG("Adding group '%s'", name);
+    if (VIR_ALLOC(group) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    if (!(group->name = strdup(name))) {
+        VIR_FREE(group);
+        virReportOOMError();
+        return NULL;
+    }
+    group->mdns = mdns;
+    group->next = mdns->group;
+    mdns->group = group;
+    return group;
+}
+
+
+void virNetServerMDNSRemoveGroup(virNetServerMDNSPtr mdns,
+                                 virNetServerMDNSGroupPtr group)
+{
+    virNetServerMDNSGroupPtr tmp = mdns->group, prev = NULL;
+
+    while (tmp) {
+        if (tmp == group) {
+            VIR_FREE(group->name);
+            if (prev)
+                prev->next = group->next;
+            else
+                group->mdns->group = group->next;
+            VIR_FREE(group);
+            return;
+        }
+        prev = tmp;
+        tmp = tmp->next;
+    }
+}
+
+
+virNetServerMDNSEntryPtr virNetServerMDNSAddEntry(virNetServerMDNSGroupPtr 
group,
+                                                  const char *type,
+                                                  int port)
+{
+    virNetServerMDNSEntryPtr entry;
+
+    VIR_DEBUG("Adding entry %s %d to group %s", type, port, group->name);
+    if (VIR_ALLOC(entry) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    entry->port = port;
+    if (!(entry->type = strdup(type))) {
+        VIR_FREE(entry);
+        virReportOOMError();
+        return NULL;
+    }
+    entry->next = group->entry;
+    group->entry = entry;
+    return entry;
+}
+
+
+void virNetServerMDNSRemoveEntry(virNetServerMDNSGroupPtr group,
+                                 virNetServerMDNSEntryPtr entry)
+{
+    virNetServerMDNSEntryPtr tmp = group->entry, prev = NULL;
+
+    while (tmp) {
+        if (tmp == entry) {
+            VIR_FREE(entry->type);
+            if (prev)
+                prev->next = entry->next;
+            else
+                group->entry = entry->next;
+            return;
+        }
+        prev = tmp;
+        tmp = tmp->next;
+    }
+}
+
+
+void virNetServerMDNSStop(virNetServerMDNSPtr mdns)
+{
+    virNetServerMDNSGroupPtr 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;
+}
+
+
+void virNetServerMDNSFree(virNetServerMDNSPtr mdns)
+{
+    virNetServerMDNSGroupPtr group, tmp;
+
+    if (!mdns)
+        return;
+
+    group = mdns->group;
+    while (group) {
+        tmp = group->next;
+        virNetServerMDNSGroupFree(group);
+        group = tmp;
+    }
+
+    VIR_FREE(mdns);
+}
+
+
+void virNetServerMDNSGroupFree(virNetServerMDNSGroupPtr grp)
+{
+    virNetServerMDNSEntryPtr entry, tmp;
+
+    if (!grp)
+        return;
+
+    entry = grp->entry;
+    while (entry) {
+        tmp = entry->next;
+        virNetServerMDNSEntryFree(entry);
+        entry = tmp;
+    }
+
+    VIR_FREE(grp);
+}
+
+
+void virNetServerMDNSEntryFree(virNetServerMDNSEntryPtr entry)
+{
+    if (!entry)
+        return;
+
+    VIR_FREE(entry);
+}
+
+
diff --git a/src/rpc/virnetservermdns.h b/src/rpc/virnetservermdns.h
new file mode 100644
index 0000000..6261aef
--- /dev/null
+++ b/src/rpc/virnetservermdns.h
@@ -0,0 +1,109 @@
+/*
+ * virnetservermdns.c: advertise server sockets
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * 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 <berra...@redhat.com>
+ */
+
+#ifndef __VIR_NET_SERVER_MDNS_H__
+# define __VIR_NET_SERVER_MDNS_H__
+
+#include "internal.h"
+
+typedef struct _virNetServerMDNS virNetServerMDNS;
+typedef virNetServerMDNS *virNetServerMDNSPtr;
+typedef struct _virNetServerMDNSGroup virNetServerMDNSGroup;
+typedef virNetServerMDNSGroup *virNetServerMDNSGroupPtr;
+typedef struct _virNetServerMDNSEntry virNetServerMDNSEntry;
+typedef virNetServerMDNSEntry *virNetServerMDNSEntryPtr;
+
+
+/**
+ * Prepares a new mdns manager object for use
+ */
+virNetServerMDNSPtr virNetServerMDNSNew(void);
+
+/**
+ * Starts the mdns client, advertising any groups/entries currently registered
+ *
+ * @mdns: manager to start advertising
+ *
+ * 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 virNetServerMDNSStart(virNetServerMDNSPtr mdns);
+
+/**
+ * Stops the mdns client, removing any advertisements
+ *
+ * @mdns: manager to start advertising
+ *
+ */
+void virNetServerMDNSStop(virNetServerMDNSPtr mdns);
+
+/**
+ * Adds a group container for advertisement
+ *
+ * @mdns manager to attach the group to
+ * @name unique human readable service name
+ *
+ * returns the group record, or NULL upon failure
+ */
+virNetServerMDNSGroupPtr virNetServerMDNSAddGroup(virNetServerMDNSPtr mdns,
+                                                  const char *name);
+
+/**
+ * Removes a group container from advertisement
+ *
+ * @mdns amanger to detach group from
+ * @group group to remove
+ */
+void virNetServerMDNSRemoveGroup(virNetServerMDNSPtr mdns,
+                                 virNetServerMDNSGroupPtr 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
+ */
+virNetServerMDNSEntryPtr virNetServerMDNSAddEntry(virNetServerMDNSGroupPtr 
group,
+                                                  const char *type, int port);
+
+/**
+ * Removes a service entry from a group
+ *
+ * @group group to detach service entry from
+ * @entry service entry to remove
+ */
+void virNetServerMDNSRemoveEntry(virNetServerMDNSGroupPtr group,
+                                 virNetServerMDNSEntryPtr entry);
+
+void virNetServerMDNSFree(virNetServerMDNSPtr ptr);
+void virNetServerMDNSGroupFree(virNetServerMDNSGroupPtr ptr);
+void virNetServerMDNSEntryFree(virNetServerMDNSEntryPtr ptr);
+
+#endif /* __VIR_NET_SERVER_MDNS_H__ */
diff --git a/src/rpc/virnetserverservice.c b/src/rpc/virnetserverservice.c
index 0cc65c3..e5a47b0 100644
--- a/src/rpc/virnetserverservice.c
+++ b/src/rpc/virnetserverservice.c
@@ -187,6 +187,14 @@ error:
 }
 
 
+int virNetServerServiceGetPort(virNetServerServicePtr svc)
+{
+    /* We're assuming if there are multiple sockets
+     * for IPv4 & 6, then they are all on same port */
+    return virNetSocketGetPort(svc->socks[0]);
+}
+
+
 int virNetServerServiceGetAuth(virNetServerServicePtr svc)
 {
     return svc->auth;
diff --git a/src/rpc/virnetserverservice.h b/src/rpc/virnetserverservice.h
index b8ccd55..378fa0b 100644
--- a/src/rpc/virnetserverservice.h
+++ b/src/rpc/virnetserverservice.h
@@ -48,6 +48,8 @@ virNetServerServicePtr virNetServerServiceNewUNIX(const char 
*path,
                                                   bool readonly,
                                                   virNetTLSContextPtr tls);
 
+int virNetServerServiceGetPort(virNetServerServicePtr svc);
+
 int virNetServerServiceGetAuth(virNetServerServicePtr svc);
 bool virNetServerServiceIsReadonly(virNetServerServicePtr svc);
 
diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
index daa40f4..3c0867f 100644
--- a/src/rpc/virnetsocket.c
+++ b/src/rpc/virnetsocket.c
@@ -693,6 +693,12 @@ bool virNetSocketIsLocal(virNetSocketPtr sock)
 }
 
 
+int virNetSocketGetPort(virNetSocketPtr sock)
+{
+    return virSocketGetPort(&sock->localAddr);
+}
+
+
 #ifdef SO_PEERCRED
 int virNetSocketGetLocalIdentity(virNetSocketPtr sock,
                                  uid_t *uid,
diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h
index 59ff288..356d6c6 100644
--- a/src/rpc/virnetsocket.h
+++ b/src/rpc/virnetsocket.h
@@ -77,6 +77,8 @@ int virNetSocketNewConnectExternal(const char **cmdargv,
 int virNetSocketGetFD(virNetSocketPtr sock);
 bool virNetSocketIsLocal(virNetSocketPtr sock);
 
+int virNetSocketGetPort(virNetSocketPtr sock);
+
 int virNetSocketGetLocalIdentity(virNetSocketPtr sock,
                                  uid_t *uid,
                                  pid_t *pid);
-- 
1.7.4

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to