#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* --------------------------------------------------------------------- */ 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]; 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) { struct stat st; if (-1 == stat("/proc/xen", &st)) { log_printf(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()); return -1; } return 0; } static int xen_get_info(void) { xs_transaction_t xst; char *xs_value; if (!(xst = xs_transaction_start(xenstore))) { log_printf(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"); 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"); 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"); goto out; } snprintf(dom_id, sizeof(dom_id), "dom-id=%s", xs_value); return 0; out: return -1; } static int xen_watch_add(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); ret = -1; } return ret; } static int xen_watch_data(void) { char **vec = NULL; unsigned int count; vec = xs_read_watch(xenstore, &count); log_printf(LOG_DEBUG, "%s: \"%s\"\n", __FUNCTION__, vec[XS_WATCH_PATH]); if (vec) free(vec); return 0; } /* --------------------------------------------------------------------- */ static void usage(FILE *fp) { fprintf(fp, "This little daemon publishes xen domain info,\n" "via mDNS (using avahi), as service '_xendom._tcp'.\n" "\n" "usage: %s [options]\n" "options:\n" " -h print this text\n" " -d enable debug mode\n" "\n" "-- \n" "(c) 2006 Gerd Hoffmann \n", appname); } static int wait_fd(int fd, int secs) { struct timeval tv; fd_set rd; int rc; FD_ZERO(&rd); FD_SET(fd,&rd); tv.tv_sec = secs; tv.tv_usec = 0; rc = select(fd+1, &rd, NULL, NULL, &tv); if (-1 == rc) { if (EINTR == errno) return 0; } 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; char buf[128]; struct utsname uts; int error,c; int ret = 1; /* parse options */ for (;;) { if (-1 == (c = getopt(argc, argv, "hd"))) break; switch (c) { case 'd': debug = 1; break; case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } /* 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); /* figure domain info */ if (0 != xen_init()) 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; } /* 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); } } /* enter main loop */ ret = 0; avahi_threaded_poll_start(thread_poll); if (0) { /* hmm, not working */ if (0 != xen_watch_add("memory/target")) goto fail; xen_watch_add("vm"); xen_watch_add("domid"); } for (;;) { if (app_quit) break; switch (wait_fd(xs_fileno(xenstore),1)) { case -1: log_printf(LOG_ERR, "select: %s\n", strerror(errno)); app_quit = 1; case 0: /* timeout */ break; default: /* data to read */ xen_watch_data(); } } 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); if (thread_poll) avahi_threaded_poll_free(thread_poll); avahi_free(name); return ret; }