aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkraxel <kraxel>2007-02-06 16:07:24 +0000
committerkraxel <kraxel>2007-02-06 16:07:24 +0000
commit32f212ab9d1b4dcf4e2be5abc3776d8192bb93f4 (patch)
tree6b7fcc6e5b3a4af7220c4a3070f76e7ebbd6867f
parentcbd9777e41ce186c9ef2e060aea5dab9647f1b4e (diff)
downloadxenwatch-32f212ab9d1b4dcf4e2be5abc3776d8192bb93f4.tar.gz
mdns vnc stuff
-rw-r--r--.cvsignore1
-rw-r--r--GNUmakefile9
-rw-r--r--mdns-publish-vnc.c238
-rw-r--r--mdns-publish-xendom.c287
-rw-r--r--mdns-publish.c391
-rw-r--r--mdns-publish.h25
6 files changed, 703 insertions, 248 deletions
diff --git a/.cvsignore b/.cvsignore
index a5f0085..7488888 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -5,5 +5,6 @@ xenscreenrc
xenwatch
mdns-browser
mdns-publish-xendom
+mdns-publish-vnc
vnc-client
xenapi
diff --git a/GNUmakefile b/GNUmakefile
index 8b5aff6..643d5a6 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -8,14 +8,14 @@ CFLAGS += -DVERSION='"$(VERSION)"' -DLIB='"$(LIB)"'
# build
TARGETS := xenlog xenscreen
BUILD_GTK := xenwatch mdns-browser
-BUILD_MDNS := mdns-publish-xendom
+BUILD_MDNS := mdns-publish-xendom mdns-publish-vnc
BUILD_VNC := vnc-client
BUILD_XENAPI := xenapi
-NEEDS_XENSTORE := xenlog xenscreen xenwatch mdns-publish-xendom
+NEEDS_XENSTORE := xenlog xenscreen xenwatch mdns-publish-xendom mdns-publish-vnc
NEEDS_XENAPI := xenapi
NEEDS_GTK := xenwatch mdns-browser vnc-client
-NEEDS_MDNS := xenwatch mdns-browser mdns-publish-xendom
+NEEDS_MDNS := xenwatch mdns-browser mdns-publish-xendom mdns-publish-vnc
NEEDS_VNC := xenwatch vnc-client
# default target
@@ -98,7 +98,8 @@ xenscreen: xenscreen.o xenstore.o apps.o
xenwatch: xenwatch.o xs_view.o xs_store.o xd_view.o xd_store.o \
apps.o apps-x11.o tcp.o mdns.o vnc.o x11.o
mdns-browser: mdns-browser.o mdns.o apps.o apps-x11.o
-mdns-publish-xendom: mdns-publish-xendom.o
+mdns-publish-xendom: mdns-publish-xendom.o mdns-publish.o
+mdns-publish-vnc: mdns-publish-vnc.o mdns-publish.o xenstore.o
vnc-client: vnc-client.o vnc.o x11.o
xenapi: xenapi.o tcp.o
diff --git a/mdns-publish-vnc.c b/mdns-publish-vnc.c
new file mode 100644
index 0000000..1a88fa0
--- /dev/null
+++ b/mdns-publish-vnc.c
@@ -0,0 +1,238 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/syslog.h>
+#include <sys/utsname.h>
+
+#include <xs.h>
+
+#include "list.h"
+#include "xenstore.h"
+#include "mdns-publish.h"
+
+/* ------------------------------------------------------------- */
+
+struct dom {
+ int domid;
+ char name[128];
+ int destroyed;
+
+ char label[256];
+ int vnc_port;
+ int published;
+ struct mdns_pub_entry *entry;
+
+ struct list_head next;
+};
+
+static LIST_HEAD(doms);
+static struct utsname uts;
+
+/* ------------------------------------------------------------- */
+
+static struct dom *find_dom(int domid)
+{
+ struct dom *dom;
+ struct list_head *item;
+
+ list_for_each(item, &doms) {
+ dom = list_entry(item, struct dom, next);
+ if (dom->domid == domid)
+ return dom;
+ }
+ return NULL;
+}
+
+static struct dom *get_dom(int domid)
+{
+ struct dom *dom;
+
+ dom = find_dom(domid);
+ if (!dom) {
+ dom = malloc(sizeof(*dom));
+ memset(dom,0,sizeof(*dom));
+ dom->domid = domid;
+ list_add_tail(&dom->next, &doms);
+ }
+ return dom;
+}
+
+static void try_attach_domain(struct mdns_pub *mdns, struct dom *dom, int boot)
+{
+ if (dom->published)
+ return;
+ if (!strlen(dom->name))
+ return;
+ if (!dom->vnc_port)
+ return;
+
+ snprintf(dom->label, sizeof(dom->label), "host %s, guest %s",
+ uts.nodename, dom->name);
+ mdns_log_printf(mdns, LOG_INFO, "pub: %s (%d)\n", dom->name, dom->domid);
+ dom->entry = mdns_pub_add(mdns, dom->label, "_rfb._tcp", dom->vnc_port);
+
+ dom->published = 1;
+}
+
+static void try_release_domain(struct mdns_pub *mdns, struct dom *dom)
+{
+ if (!dom->destroyed)
+ return;
+ if (dom->published) {
+ mdns_log_printf(mdns, LOG_INFO, "del: %s (%d)\n", dom->name, dom->domid);
+ mdns_pub_del(dom->entry);
+ }
+ list_del(&dom->next);
+ free(dom);
+}
+
+/* ------------------------------------------------------------- */
+
+static void usage(FILE *fp)
+{
+ fprintf(fp, "usage: [fixme]\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct mdns_pub *mdns = NULL;
+ struct xs_handle *xenstore = NULL;
+ xs_transaction_t xst;
+ char **vec = NULL;
+ int domid, c;
+ int debug = 0;
+ char path[128], value[128];
+ unsigned int count, i, rc;
+ struct dom *dom;
+ fd_set set;
+
+ for (;;) {
+ if (-1 == (c = getopt(argc, argv, "hd")))
+ break;
+ switch (c) {
+
+ case 'd':
+ debug++;
+ break;
+
+ case 'h':
+ usage(stdout);
+ exit(0);
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ mdns_pub_appname = "mdns-publish-vnc";
+ uname(&uts);
+ if (!debug)
+ mdns_daemonize();
+
+ /* connect to xenstore */
+ xenstore = xenstore_open(1,1,1,1);
+ if (NULL == xenstore) {
+ mdns_log_printf(mdns, LOG_ERR, "can't access xenstore, exiting\n");
+ exit(1);
+ }
+ xs_watch(xenstore, "/local/domain", "token");
+
+ mdns = mdns_pub_init(debug);
+ if (NULL == mdns)
+ exit(1);
+ mdns_sigsetup(mdns);
+ mdns_pub_start(mdns);
+
+ /* look for running domains */
+ if (!(xst = xs_transaction_start(xenstore))) {
+ mdns_log_printf(mdns, LOG_ERR, "Oops, can't start xenstore transaction\n");
+ exit(1);
+ }
+ vec = xs_directory(xenstore, xst, "/local/domain", &count);
+ xs_transaction_end(xenstore, xst, 0);
+
+ fprintf(stderr,"looking for existing domains\n");
+ for (i = 0; i < count; i++) {
+ domid = atoi(vec[i]);
+ dom = get_dom(domid);
+ snprintf(path, sizeof(path), "/local/domain/%d/name", domid);
+ xenstore_read(xenstore, path, dom->name, sizeof(dom->name));
+ snprintf(path, sizeof(path), "/local/domain/%d/console/vnc-port", domid);
+ if (0 == xenstore_read(xenstore, path, value, sizeof(value)))
+ dom->vnc_port = atoi(value);
+ try_attach_domain(mdns, dom, 1);
+ }
+
+ /* main loop */
+ fprintf(stderr,"ok, watching out for changes now\n");
+ for (;;) {
+ if (mdns_pub_appquit)
+ break;
+
+ FD_ZERO(&set);
+ FD_SET(xs_fileno(xenstore), &set);
+ switch (select(xs_fileno(xenstore)+1, &set, NULL, NULL, NULL)) {
+ case -1:
+ if (EINTR == errno)
+ continue; /* termsig check */
+ perror("select");
+ break;
+ case 0:
+ fprintf(stderr,"Huh, select() timeout?\n");
+ mdns_pub_appquit++;
+ break;
+ default:
+ break;
+ }
+
+ if (vec)
+ free(vec);
+ vec = xs_read_watch(xenstore, &count);
+ if (NULL == vec) {
+ fprintf(stderr,"xs_read_watch() failed\n");
+ exit(1);
+ }
+ if (2 != sscanf(vec[XS_WATCH_PATH], "/local/domain/%d/%64s", &domid, path)) {
+ if (1 != sscanf(vec[XS_WATCH_PATH], "/local/domain/%d", &domid))
+ continue;
+ strcpy(path, "");
+ }
+ dom = get_dom(domid);
+
+ if (0 == strcmp(path,"")) {
+ rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value));
+ if (0 != rc)
+ dom->destroyed = 1;
+
+ } else if (0 == strcmp(path, "name")) {
+ rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value));
+ if (0 != rc)
+ continue;
+ strcpy(dom->name, value);
+ mdns_log_printf(mdns, LOG_INFO, "new: %s (%d)\n", dom->name, dom->domid);
+
+ } else if (0 == strcmp(path, "console/vnc-port")) {
+ rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value));
+ if (0 != rc)
+ continue;
+ dom->vnc_port = atoi(value);
+
+ } else {
+ continue;
+
+ }
+
+ try_attach_domain(mdns, dom, 0);
+ try_release_domain(mdns, dom);
+ }
+
+ mdns_pub_del_all(mdns);
+ mdns_pub_stop(mdns);
+ mdns_pub_fini(mdns);
+ return 0;
+}
diff --git a/mdns-publish-xendom.c b/mdns-publish-xendom.c
index 4401794..339858f 100644
--- a/mdns-publish-xendom.c
+++ b/mdns-publish-xendom.c
@@ -15,29 +15,15 @@
#include <xs.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 "mdns-publish.h"
/* --------------------------------------------------------------------- */
-static AvahiEntryGroup *group = NULL;
-static AvahiThreadedPoll *thread_poll = NULL;
-
static struct xs_handle *xenstore;
static int debug = 0;
-static int have_tty = 1;
-static int have_syslog = 0;
static char *appname = "mdns-publish-xendom";
-static int app_quit = 0;
-static int termsig = 0;
-static char *name;
static char *service = "_xendom._tcp";
static int port = 9;
static char vm_uuid[256];
@@ -45,183 +31,51 @@ static char dom_id[256];
/* --------------------------------------------------------------------- */
-static int __attribute__ ((format (printf, 2, 0)))
-log_printf(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 (have_tty)
- fprintf(stderr, "%s: %s", appname, msgbuf);
- if (have_syslog)
- syslog(priority, "%s", msgbuf);
- return rc;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void create_services(AvahiClient *c);
-
-static void entry_group_callback(AvahiEntryGroup *g,
- AvahiEntryGroupState state,
- void *userdata)
-{
- char *n;
-
- switch (state) {
- case AVAHI_ENTRY_GROUP_ESTABLISHED:
- log_printf(LOG_INFO, "service '%s' successfully established\n", name);
- break;
- case AVAHI_ENTRY_GROUP_COLLISION:
- n = avahi_alternative_service_name(name);
- log_printf(LOG_NOTICE, "service name collision, renaming '%s' to '%s'\n",
- name, n);
- avahi_free(name);
- name = n;
- create_services(avahi_entry_group_get_client(g));
- break;
- case AVAHI_ENTRY_GROUP_FAILURE:
- app_quit = 1;
- break;
- default:
- break;
- }
-}
-
-static void create_services(AvahiClient *c)
-{
- int ret;
-
- /* If this is the first time we're called, let's create a new entry group */
- if (!group) {
- if (!(group = avahi_entry_group_new(c, entry_group_callback, NULL))) {
- log_printf(LOG_ERR, "avahi_entry_group_new() failed: %s\n",
- avahi_strerror(avahi_client_errno(c)));
- goto fail;
- }
- }
-
- /* Add the service */
- ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
- name, service, NULL, NULL, port,
- vm_uuid, dom_id, NULL);
- if (ret < 0) {
- log_printf(LOG_ERR, "failed to add '%s' service: %s\n", service, avahi_strerror(ret));
- goto fail;
- }
-
- /* Tell the server to register the service */
- if ((ret = avahi_entry_group_commit(group)) < 0) {
- log_printf(LOG_ERR, "failed to commit entry_group: %s\n", avahi_strerror(ret));
- goto fail;
- }
- return;
-
-fail:
- app_quit = 1;
-}
-
-static void client_callback(AvahiClient *c,
- AvahiClientState state,
- void * userdata)
-{
- int error;
-
- switch (state) {
- case AVAHI_CLIENT_CONNECTING:
- log_printf(LOG_NOTICE, "avahi daemon not running (yet), I'll keep trying ...\n");
- break;
- case AVAHI_CLIENT_S_RUNNING:
- if (!group)
- create_services(c);
- break;
- case AVAHI_CLIENT_S_COLLISION:
- if (group)
- avahi_entry_group_reset(group);
- break;
- case AVAHI_CLIENT_FAILURE:
- switch (avahi_client_errno(c)) {
- case AVAHI_ERR_DISCONNECTED:
- avahi_entry_group_free(group);
- avahi_client_free(c);
- group = NULL;
-
- log_printf(LOG_NOTICE, "disconnected from avahi daemon, reconnecting ...\n");
- c = avahi_client_new(avahi_threaded_poll_get(thread_poll),
- AVAHI_CLIENT_NO_FAIL,
- client_callback, NULL, &error);
- if (!c) {
- log_printf(LOG_ERR, "failed to create client: %s\n",
- avahi_strerror(error));
- goto fail;
- }
- break;
- default:
- log_printf(LOG_ERR, "client failure: %s\n",
- avahi_strerror(avahi_client_errno(c)));
- goto fail;
- break;
- }
- break;
- default:
- break;
- }
- return;
-
- fail:
- app_quit = 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int xen_init(void)
+static int xen_init(struct mdns_pub *mdns)
{
struct stat st;
if (-1 == stat("/proc/xen", &st)) {
- log_printf(LOG_ERR, "/proc/xen not found, running on bare metal?\n");
+ mdns_log_printf(mdns, LOG_ERR,
+ "/proc/xen not found, running on bare metal?\n");
return -1;
}
xenstore = xs_domain_open();
if (NULL == xenstore) {
- log_printf(LOG_ERR, "can't connect to xenstore (%s)\n", xs_domain_dev());
+ mdns_log_printf(mdns, LOG_ERR,
+ "can't connect to xenstore (%s)\n", xs_domain_dev());
return -1;
}
return 0;
}
-static int xen_get_info(void)
+static int xen_get_info(struct mdns_pub *mdns)
{
xs_transaction_t xst;
char *xs_value;
if (!(xst = xs_transaction_start(xenstore))) {
- log_printf(LOG_ERR, "can't start xenstore transaction\n");
+ mdns_log_printf(mdns, LOG_ERR, "can't start xenstore transaction\n");
goto out;
}
xs_value = xs_read(xenstore, xst, "vm", NULL);
xs_transaction_end(xenstore, xst, 0);
if (!xs_value) {
- log_printf(LOG_ERR, "can't read 'vm' value from xenstore\n");
+ mdns_log_printf(mdns, LOG_ERR, "can't read 'vm' value from xenstore\n");
goto out;
}
snprintf(vm_uuid, sizeof(vm_uuid), "vm-uuid=%s", xs_value+4);
if (!(xst = xs_transaction_start(xenstore))) {
- log_printf(LOG_ERR, "can't start xenstore transaction\n");
+ mdns_log_printf(mdns, LOG_ERR, "can't start xenstore transaction\n");
goto out;
}
xs_value = xs_read(xenstore, xst, "domid", NULL);
xs_transaction_end(xenstore, xst, 0);
if (!xs_value) {
- log_printf(LOG_ERR, "can't read 'domid' value from xenstore\n");
+ mdns_log_printf(mdns, LOG_ERR, "can't read 'domid' value from xenstore\n");
goto out;
}
snprintf(dom_id, sizeof(dom_id), "dom-id=%s", xs_value);
@@ -232,26 +86,26 @@ static int xen_get_info(void)
return -1;
}
-static int xen_watch_add(char *path)
+static int xen_watch_add(struct mdns_pub *mdns, char *path)
{
int ret = 0;
/* Hmm, not working ... */
if (!xs_watch(xenstore, path, "token")) {
- log_printf(LOG_ERR, "%s: xs_watch for \"%s\" failed\n",
- __FUNCTION__, path);
+ mdns_log_printf(mdns, LOG_ERR, "%s: xs_watch for \"%s\" failed\n",
+ __FUNCTION__, path);
ret = -1;
}
return ret;
}
-static int xen_watch_data(void)
+static int xen_watch_data(struct mdns_pub *mdns)
{
char **vec = NULL;
unsigned int count;
vec = xs_read_watch(xenstore, &count);
- log_printf(LOG_DEBUG, "%s: \"%s\"\n", __FUNCTION__, vec[XS_WATCH_PATH]);
+ mdns_log_printf(mdns, LOG_DEBUG, "%s: \"%s\"\n", __FUNCTION__, vec[XS_WATCH_PATH]);
if (vec)
free(vec);
@@ -294,19 +148,12 @@ static int wait_fd(int fd, int secs)
return rc;
}
-static void catchsig(int signal)
-{
- termsig = signal;
- app_quit = 1;
-}
-
int main(int argc, char*argv[])
{
- struct sigaction act,old;
- AvahiClient *client = NULL;
+ struct mdns_pub *mdns = NULL;
char buf[128];
struct utsname uts;
- int error,c;
+ int c;
int ret = 1;
/* parse options */
@@ -326,108 +173,60 @@ int main(int argc, char*argv[])
}
}
- /* open syslog */
- openlog(appname, 0, LOG_LOCAL0);
- have_syslog = 1;
-
/* figure name */
uname(&uts);
snprintf(buf, sizeof(buf), "Xen domain %s", uts.nodename);
- name = avahi_strdup(buf);
+ mdns_pub_appname = appname;
+ if (!debug)
+ mdns_daemonize();
/* figure domain info */
- if (0 != xen_init())
+ if (0 != xen_init(mdns))
goto fail;
- xen_get_info();
-
- /* prepare nDNS bits */
- thread_poll = avahi_threaded_poll_new();
- if (!thread_poll) {
- log_printf(LOG_ERR, "failed to create simple poll object\n");
- goto fail;
- }
-
- client = avahi_client_new(avahi_threaded_poll_get(thread_poll),
- AVAHI_CLIENT_NO_FAIL,
- client_callback, NULL, &error);
- if (!client) {
- log_printf(LOG_ERR, "failed to create client: %s\n", avahi_strerror(error));
- goto fail;
- }
+ xen_get_info(mdns);
- /* setup signal handler */
- 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 (debug)
- sigaction(SIGINT,&act,&old);
-
- /* damonize */
- if (!debug) {
- switch (fork()) {
- case -1:
- log_printf(LOG_ERR, "fork: %s", strerror(errno));
- goto fail;
- case 0:
- /* child */
- close(0); close(1); close(2);
- setsid();
- open("/dev/null", O_RDWR); dup(0); dup(0);
- have_tty = 0;
- break;
- default:
- /* parent */
- exit(0);
- }
- }
+ mdns = mdns_pub_init(debug);
+ if (NULL == mdns)
+ goto fail;
+ mdns_pub_add(mdns, buf, service, port);
+ mdns_sigsetup(mdns);
/* enter main loop */
ret = 0;
- avahi_threaded_poll_start(thread_poll);
+ mdns_pub_start(mdns);
if (0) {
/* hmm, not working */
- if (0 != xen_watch_add("memory/target"))
+ if (0 != xen_watch_add(mdns, "memory/target"))
goto fail;
- xen_watch_add("vm");
- xen_watch_add("domid");
+ xen_watch_add(mdns, "vm");
+ xen_watch_add(mdns, "domid");
}
for (;;) {
- if (app_quit)
+ if (mdns_pub_appquit)
break;
switch (wait_fd(xs_fileno(xenstore),1)) {
case -1:
- log_printf(LOG_ERR, "select: %s\n", strerror(errno));
- app_quit = 1;
+ mdns_log_printf(mdns, LOG_ERR, "select: %s\n", strerror(errno));
+ mdns_pub_appquit = 1;
case 0:
/* timeout */
break;
default:
/* data to read */
- xen_watch_data();
+ xen_watch_data(mdns);
}
}
- avahi_threaded_poll_stop(thread_poll);
-
-fail:
- log_printf(ret ? LOG_ERR : LOG_INFO, "exiting (%d)%s%s\n", ret,
- termsig ? ", on signal " : "",
- termsig ? strsignal(termsig) : "");
-
- /* Cleanup things */
- if (client)
- avahi_client_free(client);
+ mdns_pub_del_all(mdns);
+ mdns_pub_stop(mdns);
- if (thread_poll)
- avahi_threaded_poll_free(thread_poll);
+fail:
+ mdns_log_printf(mdns, ret ? LOG_ERR : LOG_INFO, "exiting (%d)%s%s\n", ret,
+ mdns_pub_termsig ? ", on signal " : "",
+ mdns_pub_termsig ? strsignal(mdns_pub_termsig) : "");
- avahi_free(name);
+ mdns_pub_fini(mdns);
return ret;
}
diff --git a/mdns-publish.c b/mdns-publish.c
new file mode 100644
index 0000000..a0e3cc4
--- /dev/null
+++ b/mdns-publish.c
@@ -0,0 +1,391 @@
+#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;
+ 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 entry_group_callback(AvahiEntryGroup *g,
+ AvahiEntryGroupState state,
+ void *userdata)
+{
+ struct mdns_pub_entry *entry = userdata;
+
+ 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:
+#if 0
+ n = avahi_alternative_service_name(name);
+ mdns_log_printf(mdns, LOG_NOTICE,
+ "service name collision, renaming '%s' to '%s'\n",
+ name, n);
+ avahi_free(name);
+ name = n;
+ create_services(avahi_entry_group_get_client(g));
+ break;
+#endif
+ 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 = 1;
+
+ 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,
+ char *name, char *service, int port)
+{
+ struct mdns_pub_entry *entry;
+
+ entry = avahi_malloc(sizeof(*entry));
+ if (NULL == entry)
+ return NULL;
+ memset(entry, 0, sizeof(*entry));
+
+ entry->name = name;
+ entry->service = service;
+ entry->port = port;
+ entry->mdns = mdns;
+
+ 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;
+ }
+ 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..46a87fb
--- /dev/null
+++ b/mdns-publish.h
@@ -0,0 +1,25 @@
+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,
+ char *name, 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);