diff --git a/ChangeLog b/ChangeLog index d61715aa83..d0d5abd460 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Tue Sep 18 21:34:00 EST 2007 Daniel P. Berrange + + * 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 * src/event.h, src/event.c: Added new APIs definitions for updating diff --git a/configure.in b/configure.in index c3c93bd975..c5afb83d4c 100644 --- a/configure.in +++ b/configure.in @@ -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 ]) + +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([]) diff --git a/docs/libvir.html b/docs/libvir.html index 04e2d39d13..8b899a486b 100644 --- a/docs/libvir.html +++ b/docs/libvir.html @@ -2191,6 +2191,25 @@ Blank lines and comments beginning with # are ignored. + + mdns_adv [0|1] + 1 (advertise with mDNS) + + If set to 1 then the virtualization service will be advertised over + mDNS to hosts on the local LAN segment. + + + + + mdns_name "name" + "Virtualization Host HOSTNAME" + + 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. + + + tls_no_verify_certificate [0|1] 0 (certificates are verified) diff --git a/docs/remote.html b/docs/remote.html index d7810f06df..42366021b7 100644 --- a/docs/remote.html +++ b/docs/remote.html @@ -471,6 +471,19 @@ Blank lines and comments beginning with # are ignored. The port number or service name to listen on for unencrypted TCP connections. + mdns_adv [0|1] + 1 (advertise with mDNS) + + If set to 1 then the virtualization service will be advertised over + mDNS to hosts on the local LAN segment. + + mdns_name "name" + "Virtualization Host HOSTNAME" + + 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. + tls_no_verify_certificate [0|1] 0 (certificates are verified) diff --git a/libvirt.spec.in b/libvirt.spec.in index f6e9dffdb6..4ecf0b646c 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -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 diff --git a/qemud/Makefile.am b/qemud/Makefile.am index 898d719afe..5afa99e756 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -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 $@ diff --git a/qemud/internal.h b/qemud/internal.h index 744e176381..9b1b72125e 100644 --- a/qemud/internal.h +++ b/qemud/internal.h @@ -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 index 0000000000..bd1f548062 --- /dev/null +++ b/qemud/mdns.c @@ -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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#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 index 0000000000..71bf8ad7c4 --- /dev/null +++ b/qemud/mdns.h @@ -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 + */ + +#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__ */ diff --git a/qemud/qemud.c b/qemud/qemud.c index 4f9eef93b3..a2554b8050 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -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;