diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2015-06-08 16:02:51 +0200 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2015-06-08 16:02:51 +0200 |
commit | f62d03779bc8869ef80ab4e7deb7f0c813608cae (patch) | |
tree | 3ac12b201e2a79982c98121dc46e5164f3322f17 | |
parent | 32d07c00381c6c9ea74ac7cfa325bd52a5328af6 (diff) | |
download | vconsole-f62d03779bc8869ef80ab4e7deb7f0c813608cae.tar.gz |
[wip] mdns announce for guest vnc servers
-rw-r--r-- | GNUmakefile | 14 | ||||
-rw-r--r-- | list.h | 169 | ||||
-rw-r--r-- | mdns-publish.c | 409 | ||||
-rw-r--r-- | mdns-publish.h | 28 | ||||
-rw-r--r-- | vpublish.c | 268 | ||||
-rw-r--r-- | vpublish.h | 0 |
6 files changed, 883 insertions, 5 deletions
diff --git a/GNUmakefile b/GNUmakefile index 2996ee9..e89806c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -4,12 +4,13 @@ include mk/Variables.mk # add our flags + libs CFLAGS += -DVERSION='"$(VERSION)"' -DLIB='"$(LIB)"' +CFLAGS += -Wno-pointer-sign # valgrind options VFLAGS := --leak-check=full --show-possibly-lost=no # build -TARGETS := vconsole +TARGETS := vconsole vpublish # default target all: build @@ -33,18 +34,20 @@ endef ifeq ($(HAVE_GTK3)-$(HAVE_VTE3),yes-yes) CFLAGS += -Wno-deprecated-declarations wanted := $(HAVE_GLIB)-$(HAVE_GTHREAD)-$(HAVE_GTK3)-$(HAVE_VTE3)-$(HAVE_LIBVIRT) -pkglst := glib-2.0 gthread-2.0 gtk+-3.0 vte-2.90 libvirt +vconsole : pkglst := glib-2.0 gthread-2.0 gtk+-3.0 vte-2.90 libvirt +vpublish : pkglst := glib-2.0 gthread-2.0 libvirt libxml-2.0 avahi-client avahi-glib else CFLAGS += -DGTK_DISABLE_SINGLE_INCLUDES CFLAGS += -DGTK_DISABLE_DEPRECATED CFLAGS += -DGSEAL_ENABLE wanted := $(HAVE_GLIB)-$(HAVE_GTHREAD)-$(HAVE_GTK2)-$(HAVE_VTE2)-$(HAVE_LIBVIRT) -pkglst := glib-2.0 gthread-2.0 gtk+-2.0 vte libvirt +vconsole : pkglst := glib-2.0 gthread-2.0 gtk+-2.0 vte libvirt +vpublish : pkglst := glib-2.0 gthread-2.0 libvirt libxml-2.0 avahi-client avahi-glib endif CFLAGS += -Wno-strict-prototypes -CFLAGS += $(shell test "$(pkglst)" != "" && pkg-config --cflags $(pkglst)) -LDLIBS += $(shell test "$(pkglst)" != "" && pkg-config --libs $(pkglst)) +CFLAGS += $(shell pkg-config --cflags $(pkglst)) +LDLIBS += $(shell pkg-config --libs $(pkglst)) # desktop files DESKTOP := $(wildcard $(patsubst %,%.desktop,$(TARGETS))) @@ -81,6 +84,7 @@ realclean distclean: clean ############################################# vconsole: vconsole.o connect.o domain.o libvirt-glib-event.o +vpublish: vpublish.o mdns-publish.o libvirt-glib-event.o include mk/Compile.mk include mk/Maintainer.mk @@ -0,0 +1,169 @@ +#ifndef _LIST_H +#define _LIST_H 1 + +/* + * Simple doubly linked list implementation. + * -- shameless stolen from the linux kernel sources + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a item entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * item, + struct list_head * prev, + struct list_head * next) +{ + next->prev = item; + item->next = next; + item->prev = prev; + prev->next = item; +} + +/** + * list_add - add a item entry + * @item: item entry to be added + * @head: list head to add it after + * + * Insert a item entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *item, struct list_head *head) +{ + __list_add(item, head, head->next); +} + +/** + * list_add_tail - add a item entry + * @item: item entry to be added + * @head: list head to add it before + * + * Insert a item entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *item, struct list_head *head) +{ + __list_add(item, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the item list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev - iterate over a list in reverse order + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +#endif /* _LIST_H */ diff --git a/mdns-publish.c b/mdns-publish.c new file mode 100644 index 0000000..868a7b9 --- /dev/null +++ b/mdns-publish.c @@ -0,0 +1,409 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/signal.h> + +#include <avahi-client/client.h> +#include <avahi-client/publish.h> + +#include <avahi-common/alternative.h> +#include <avahi-common/thread-watch.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> + +#include "list.h" +#include "mdns-publish.h" + +/* --------------------------------------------------------------------- */ + +struct mdns_pub { + int have_tty; + int have_syslog; + int debug; + + AvahiThreadedPoll *thread_poll; + AvahiClient *client; + + struct list_head entries; +}; + +struct mdns_pub_entry { + struct list_head next; + + char *name; + const char *service; + int port; + char *txt[4]; + + struct mdns_pub *mdns; + AvahiEntryGroup *group; +}; + +char *mdns_pub_appname; +int mdns_pub_termsig; +int mdns_pub_appquit; + +/* ------------------------------------------------------------------ */ + +static char *group_state_name[] = { + [ AVAHI_ENTRY_GROUP_UNCOMMITED ] = "uncommited", + [ AVAHI_ENTRY_GROUP_REGISTERING ] = "registering", + [ AVAHI_ENTRY_GROUP_ESTABLISHED ] = "established", + [ AVAHI_ENTRY_GROUP_COLLISION ] = "collision", + [ AVAHI_ENTRY_GROUP_FAILURE ] = "failure", +}; + +static char *client_state_name[] = { + [ AVAHI_CLIENT_S_REGISTERING ] = "server registering", + [ AVAHI_CLIENT_S_RUNNING ] = "server running", + [ AVAHI_CLIENT_S_COLLISION ] = "server collision", + [ AVAHI_CLIENT_FAILURE ] = "failure", + [ AVAHI_CLIENT_CONNECTING ] = "connecting", +}; + +static void update_services(AvahiClient *c, struct mdns_pub *mdns); + +static void entry_group_callback(AvahiEntryGroup *g, + AvahiEntryGroupState state, + void *userdata) +{ + struct mdns_pub_entry *entry = userdata; + char *n; + + mdns_log_printf(entry->mdns, LOG_DEBUG, "%s: %s: state %d [%s]\n", __FUNCTION__, + entry->name, state, group_state_name[state]); + + switch (state) { + case AVAHI_ENTRY_GROUP_COLLISION: + n = avahi_alternative_service_name(entry->name); + mdns_log_printf(entry->mdns, LOG_NOTICE, + "service name collision, renaming '%s' to '%s'\n", + entry->name, n); + avahi_free(entry->name); + entry->name = n; + avahi_entry_group_reset(entry->group); + update_services(avahi_entry_group_get_client(g), entry->mdns); + break; + case AVAHI_ENTRY_GROUP_FAILURE: + mdns_pub_appquit++; + break; + default: + break; + } +} + +static void update_services(AvahiClient *c, struct mdns_pub *mdns) +{ + struct list_head *item; + struct mdns_pub_entry *entry; + AvahiEntryGroupState state; + int ret; + + if (AVAHI_CLIENT_S_RUNNING != avahi_client_get_state(c)) + return; + + list_for_each(item, &mdns->entries) { + entry = list_entry(item, struct mdns_pub_entry, next); + + /* If this is the first time we're called, let's create a new entry group */ + if (!entry->group) { + entry->group = avahi_entry_group_new(c, entry_group_callback, entry); + if (!entry->group) { + mdns_log_printf(mdns, LOG_ERR, "avahi_entry_group_new() failed: %s\n", + avahi_strerror(avahi_client_errno(c))); + goto fail; + } + } + + /* something to do ? */ + state = avahi_entry_group_get_state(entry->group); + mdns_log_printf(mdns, LOG_DEBUG, "%s: %s: %d [%s]\n", __FUNCTION__, + entry->name, state, group_state_name[state]); + if (AVAHI_ENTRY_GROUP_UNCOMMITED != state) + continue; + + /* Add the service */ + ret = avahi_entry_group_add_service(entry->group, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, + entry->name, entry->service, + NULL, NULL, + entry->port, + entry->txt[0], entry->txt[1], + entry->txt[2], entry->txt[3], + NULL); + if (ret < 0) { + mdns_log_printf(mdns, LOG_ERR, "failed to add '%s' service: %s\n", + entry->name, avahi_strerror(ret)); + goto fail; + } + + /* Tell the server to register the service */ + ret = avahi_entry_group_commit(entry->group); + if (ret < 0) { + mdns_log_printf(mdns, LOG_ERR, "failed to commit entry_group: %s\n", + avahi_strerror(ret)); + goto fail; + } + } + return; + +fail: + mdns_pub_appquit++; +} + +static void reset_services(struct mdns_pub *mdns, int free_groups) +{ + struct list_head *item; + struct mdns_pub_entry *entry; + + list_for_each(item, &mdns->entries) { + entry = list_entry(item, struct mdns_pub_entry, next); + avahi_entry_group_reset(entry->group); + if (!free_groups) + continue; + avahi_entry_group_free(entry->group); + entry->group = NULL; + } +} + +static void client_callback(AvahiClient *c, + AvahiClientState state, + void * userdata) +{ + struct mdns_pub *mdns = userdata; + int error; + + mdns_log_printf(mdns, LOG_DEBUG, "%s: state %d [%s]\n", __FUNCTION__, + state, client_state_name[state]); + + switch (state) { + case AVAHI_CLIENT_CONNECTING: + mdns_log_printf(mdns, LOG_NOTICE, + "avahi daemon not running (yet), I'll keep trying ...\n"); + break; + case AVAHI_CLIENT_S_RUNNING: + update_services(c, mdns); + break; + case AVAHI_CLIENT_S_COLLISION: + reset_services(mdns, 0); + break; + case AVAHI_CLIENT_FAILURE: + switch (avahi_client_errno(c)) { + case AVAHI_ERR_DISCONNECTED: + reset_services(mdns, 1); + avahi_client_free(c); + + mdns_log_printf(mdns, LOG_NOTICE, "disconnected from avahi daemon, reconnecting ...\n"); + mdns->client = avahi_client_new(avahi_threaded_poll_get(mdns->thread_poll), + AVAHI_CLIENT_NO_FAIL, + client_callback, mdns, &error); + if (!mdns->client) { + mdns_log_printf(mdns, LOG_ERR, "failed to create client: %s\n", + avahi_strerror(error)); + goto fail; + } + break; + default: + mdns_log_printf(mdns, LOG_ERR, "client failure: %s\n", + avahi_strerror(avahi_client_errno(c))); + goto fail; + break; + } + break; + default: + break; + } + return; + + fail: + mdns_pub_appquit++; +} + +/* ------------------------------------------------------------------ */ + +struct mdns_pub *mdns_pub_init(int debug) +{ + struct mdns_pub *mdns; + int error; + + mdns = avahi_malloc(sizeof(*mdns)); + if (NULL == mdns) { + fprintf(stderr, "%s: out of memory\n", mdns_pub_appname); + goto fail; + } + memset(mdns, 0, sizeof(*mdns)); + INIT_LIST_HEAD(&mdns->entries); + mdns->debug = debug; + mdns->have_tty = isatty(2); + + openlog(mdns_pub_appname, 0, LOG_LOCAL0); + mdns->have_syslog = 1; + + mdns->thread_poll = avahi_threaded_poll_new(); + if (!mdns->thread_poll) { + mdns_log_printf(mdns, LOG_ERR, "failed to create simple poll object\n"); + goto fail; + } + + mdns->client = avahi_client_new(avahi_threaded_poll_get(mdns->thread_poll), + AVAHI_CLIENT_NO_FAIL, + client_callback, mdns, &error); + if (!mdns->client) { + mdns_log_printf(mdns, LOG_ERR, "failed to create client: %s\n", avahi_strerror(error)); + goto fail; + } + return mdns; + + fail: + mdns_pub_fini(mdns); + return NULL; +} + +int mdns_pub_start(struct mdns_pub *mdns) +{ + return avahi_threaded_poll_stop(mdns->thread_poll); +} + +int mdns_pub_stop(struct mdns_pub *mdns) +{ + return avahi_threaded_poll_stop(mdns->thread_poll); +} + +void mdns_pub_fini(struct mdns_pub *mdns) +{ + if (!mdns) + return; + mdns_pub_del_all(mdns); + if (mdns->client) + avahi_client_free(mdns->client); + if (mdns->thread_poll) + avahi_threaded_poll_free(mdns->thread_poll); + avahi_free(mdns); +} + +/* --------------------------------------------------------------------- */ + +struct mdns_pub_entry *mdns_pub_add(struct mdns_pub *mdns, + const char *name, + const char *service, + int port, + ...) +{ + struct mdns_pub_entry *entry; + va_list args; + char *txt; + int i; + + entry = avahi_malloc(sizeof(*entry)); + if (NULL == entry) + return NULL; + memset(entry, 0, sizeof(*entry)); + + entry->name = avahi_strdup(name); + entry->service = service; + entry->port = port; + entry->mdns = mdns; + + va_start(args, port); + for (i = 0; i < sizeof(entry->txt)/sizeof(entry->txt[0]); i++) { + txt = va_arg(args,char*); + if (NULL == txt) + break; + entry->txt[i] = txt; + } + va_end(args); + + list_add_tail(&entry->next, &mdns->entries); + if (mdns->client) + update_services(mdns->client, mdns); + + mdns_log_printf(entry->mdns, LOG_DEBUG, "%s: %s\n", __FUNCTION__, entry->name); + return entry; +} + +void mdns_pub_del(struct mdns_pub_entry *entry) +{ + mdns_log_printf(entry->mdns, LOG_DEBUG, "%s: %s\n", __FUNCTION__, entry->name); + if (entry->group) { + avahi_entry_group_reset(entry->group); + avahi_entry_group_free(entry->group); + entry->group = NULL; + } + avahi_free(entry->name); + list_del(&entry->next); + avahi_free(entry); +} + +void mdns_pub_del_all(struct mdns_pub *mdns) +{ + struct mdns_pub_entry *entry; + + while (!list_empty(&mdns->entries)) { + entry = list_entry(mdns->entries.next, struct mdns_pub_entry, next); + mdns_pub_del(entry); + } +} + +/* --------------------------------------------------------------------- */ + +int mdns_log_printf(struct mdns_pub *mdns, int priority, + char *fmt, ...) +{ + va_list args; + char msgbuf[1024]; + int rc; + + va_start(args, fmt); + rc = vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + va_end(args); + + if (!mdns || mdns->have_tty) + fprintf(stderr, "%s: %s", mdns_pub_appname, msgbuf); + if (mdns && mdns->have_syslog) + syslog(priority, "%s", msgbuf); + return rc; +} + +int mdns_daemonize(void) +{ + switch (fork()) { + case -1: + mdns_log_printf(NULL, LOG_ERR, "fork: %s", strerror(errno)); + return -1; + case 0: + /* child */ + close(0); close(1); close(2); + setsid(); + open("/dev/null", O_RDWR); dup(0); dup(0); + return 0; + default: + /* parent */ + exit(0); + } +} + +static void catchsig(int signal) +{ + mdns_pub_termsig = signal; + mdns_pub_appquit = 1; +} + +void mdns_sigsetup(struct mdns_pub *mdns) +{ + struct sigaction act,old; + + memset(&act,0,sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE,&act,&old); + act.sa_handler = catchsig; + sigaction(SIGHUP,&act,&old); + sigaction(SIGUSR1,&act,&old); + sigaction(SIGTERM,&act,&old); + if (mdns->debug) + sigaction(SIGINT,&act,&old); +} diff --git a/mdns-publish.h b/mdns-publish.h new file mode 100644 index 0000000..82fbe41 --- /dev/null +++ b/mdns-publish.h @@ -0,0 +1,28 @@ +struct mdns_pub; +struct mdns_pub_entry; + +extern char *mdns_pub_appname; +extern int mdns_pub_termsig; +extern int mdns_pub_appquit; + +/* initialization and cleanup */ +struct mdns_pub *mdns_pub_init(int debug); +int mdns_pub_start(struct mdns_pub *mdns); +int mdns_pub_stop(struct mdns_pub *mdns); +void mdns_pub_fini(struct mdns_pub *mdns); + +/* add and remove services */ +struct mdns_pub_entry *mdns_pub_add(struct mdns_pub *mdns, + const char *name, + const char *service, + int port, + ...); +void mdns_pub_del(struct mdns_pub_entry *entry); +void mdns_pub_del_all(struct mdns_pub *mdns); + +/* misc helper functions */ +int __attribute__ ((format (printf, 3, 0))) +mdns_log_printf(struct mdns_pub *mdns, int priority, + char *fmt, ...); +int mdns_daemonize(void); +void mdns_sigsetup(struct mdns_pub *mdns); diff --git a/vpublish.c b/vpublish.c new file mode 100644 index 0000000..5a4b547 --- /dev/null +++ b/vpublish.c @@ -0,0 +1,268 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <assert.h> +#include <inttypes.h> + +#include <sys/stat.h> +#include <sys/time.h> + +#include <glib.h> + +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + +#include "mdns-publish.h" +#include "libvirt-glib-event.h" + +#define APPNAME "vpublish" + +/* ------------------------------------------------------------------ */ + +int debug = 1; + +/* ------------------------------------------------------------------ */ + +typedef struct display display; +struct display { + struct mdns_pub_entry *entry; + display *next; +}; + +static display *domains; +static struct mdns_pub *mdns; + +static void display_add_vnc(virDomainPtr d, xmlChar *port) +{ + const char *name = virDomainGetName(d); + display *dpy = g_new0(display, 1); + + dpy->entry = mdns_pub_add(mdns, name, "_rfb._tcp", atoi(port), NULL); + + dpy->next = domains; + domains = dpy; +} + +/* ------------------------------------------------------------------ */ + +static void domain_check(virConnectPtr c, virDomainPtr d) +{ + static const unsigned char *xpath_spice = + "//domain//graphics[@type='spice']"; + static const unsigned char *xpath_vnc = + "//domain//graphics[@type='vnc']"; + const char *name = virDomainGetName(d); + xmlXPathContextPtr ctx; + xmlXPathObjectPtr obj; + xmlNodePtr cur; + xmlDocPtr xml; + char *domain; + int i; + + if (debug) + fprintf(stderr, "%s: %s [enter]\n", __func__, name); + + domain = virDomainGetXMLDesc(d, 0); + if (!domain) { + if (debug) + fprintf(stderr, "%s: %s virDomainGetXMLDesc failure\n", __func__, name); + return; + } + + xml = xmlReadMemory(domain, strlen(domain), NULL, NULL, 0); + if (!xml) { + if (debug) + fprintf(stderr, "%s: %s xmlReadMemory failure\n", __func__, name); + goto err_domain; + } + + ctx = xmlXPathNewContext(xml); + + obj = xmlXPathEvalExpression(xpath_spice, ctx); + if (obj && obj->nodesetval && obj->nodesetval->nodeNr) { + fprintf(stderr, "%s: %s, %d spice nodes\n", __func__, name, + obj->nodesetval->nodeNr); + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + cur = obj->nodesetval->nodeTab[i]; + /* TODO */ + } + } + xmlXPathFreeObject(obj); + + obj = xmlXPathEvalExpression(xpath_vnc, ctx); + if (obj && obj->nodesetval && obj->nodesetval->nodeNr) { + fprintf(stderr, "%s: %s, %d vnc nodes\n", __func__, name, + obj->nodesetval->nodeNr); + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + cur = obj->nodesetval->nodeTab[i]; + fprintf(stderr, " %d: %s:%s\n", i + 1, + xmlGetProp(cur, "listen"), + xmlGetProp(cur, "port")); + display_add_vnc(d, xmlGetProp(cur, "port")); + } + } + xmlXPathFreeObject(obj); + + xmlXPathFreeContext(ctx); + xmlFreeDoc(xml); + + if (debug) + fprintf(stderr, "%s: %s [done]\n", __func__, name); + return; + +err_domain: + free(domain); + return; +} + +static void domain_update(virConnectPtr c, virDomainPtr d, virDomainEventType event) +{ + const char *name = virDomainGetName(d); + + /* handle events */ + switch (event) { + case VIR_DOMAIN_EVENT_UNDEFINED: + if (debug) + fprintf(stderr, "%s: %s: undefined\n", __func__, name); + break; + case VIR_DOMAIN_EVENT_STARTED: + if (debug) + fprintf(stderr, "%s: %s: started\n", __func__, name); + domain_check(c, d); + break; + case VIR_DOMAIN_EVENT_STOPPED: + if (debug) + fprintf(stderr, "%s: %s: stopped\n", __func__, name); + break; + default: + if (debug) + fprintf(stderr, "%s: %s: Oops, unknown (default catch)\n", + __func__, name); + break; + } +} + +/* ------------------------------------------------------------------ */ + +static int connect_domain_event(virConnectPtr c, virDomainPtr d, + int event, int detail, void *opaque) +{ + if (debug) + fprintf(stderr, "%s: %s, event %d\n", __func__, + virDomainGetName(d), event); + domain_update(c, d, event); + return 0; +} + +static void connect_list(virConnectPtr c) +{ + virDomainPtr d; + int i, n; + int *active; + + n = virConnectNumOfDomains(c); + active = malloc(sizeof(int) * n); + n = virConnectListDomains(c, active, n); + for (i = 0; i < n; i++) { + d = virDomainLookupByID(c, active[i]); + domain_check(c, d); + virDomainFree(d); + } + free(active); +} + +static void connect_init(const char *uri) +{ + virConnectPtr c; + + c = virConnectOpen(uri); + if (c == NULL) { + fprintf(stderr, "Failed to open connection to %s\n", uri); + exit(1); + } + if (debug) + fprintf(stderr, "%s: connected to %s\n", __func__, uri); + + virConnectDomainEventRegister(c, connect_domain_event, + NULL, NULL); + connect_list(c); +} + +/* ------------------------------------------------------------------ */ + +static void usage(FILE *fp) +{ + fprintf(fp, + "This is a virtual machine display publisher.\n" + "It'll announce vnc screens via mdns (aka zeroconf/bonjour).\n" + "\n" + "usage: %s [ options ]\n" + "options:\n" + " -h Print this text.\n" + " -d Enable debugging.\n" + " -c <uri> Connect to libvirt.\n" + "\n" + "-- \n" + "(c) 2015 Gerd Hoffmann <kraxel@redhat.com>\n", + APPNAME); +} + +int +main(int argc, char *argv[]) +{ + GMainLoop *mainloop; + char *uri = NULL; + int c; + + for (;;) { + if (-1 == (c = getopt(argc, argv, "hdc:"))) + break; + switch (c) { + case 'd': + debug++; + break; + case 'c': + uri = optarg; + break; + case 'h': + usage(stdout); + exit(0); + default: + usage(stderr); + exit(1); + } + } + + if (uri == NULL) + uri = getenv("LIBVIRT_DEFAULT_URI"); + if (uri == NULL) + uri = getenv("VIRSH_DEFAULT_CONNECT_URI"); + + if (uri == NULL) { + fprintf(stderr, "No libvirt uri\n"); + exit(1); + } + + /* init */ + mainloop = g_main_loop_new(NULL, false); + g_thread_init(NULL); + gvir_event_register(); + mdns = mdns_pub_init(debug); + mdns_pub_start(mdns); + + connect_init(uri); + + /* main loop */ + g_main_loop_run(mainloop); + + /* cleanup */ + exit(0); +} diff --git a/vpublish.h b/vpublish.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vpublish.h |