diff options
author | kraxel <kraxel> | 2007-02-06 16:07:24 +0000 |
---|---|---|
committer | kraxel <kraxel> | 2007-02-06 16:07:24 +0000 |
commit | 32f212ab9d1b4dcf4e2be5abc3776d8192bb93f4 (patch) | |
tree | 6b7fcc6e5b3a4af7220c4a3070f76e7ebbd6867f | |
parent | cbd9777e41ce186c9ef2e060aea5dab9647f1b4e (diff) | |
download | xenwatch-32f212ab9d1b4dcf4e2be5abc3776d8192bb93f4.tar.gz |
mdns vnc stuff
-rw-r--r-- | .cvsignore | 1 | ||||
-rw-r--r-- | GNUmakefile | 9 | ||||
-rw-r--r-- | mdns-publish-vnc.c | 238 | ||||
-rw-r--r-- | mdns-publish-xendom.c | 287 | ||||
-rw-r--r-- | mdns-publish.c | 391 | ||||
-rw-r--r-- | mdns-publish.h | 25 |
6 files changed, 703 insertions, 248 deletions
@@ -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); |