#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* --------------------------------------------------------------------- */ static AvahiEntryGroup *group = NULL; static AvahiSimplePoll *simple_poll = NULL; static int debug = 0; static int have_tty = 1; static int have_syslog = 0; static char *appname = "mdns-publish-xendom"; 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: avahi_simple_poll_quit(simple_poll); 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: avahi_simple_poll_quit(simple_poll); } 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_simple_poll_get(simple_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: avahi_simple_poll_quit(simple_poll); } static int update_xendom_info(void) { struct xs_handle *xenstore; xs_transaction_t xst; char *xs_value; 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; } 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 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); } int main(int argc, char*argv[]) { 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 != update_xendom_info()) goto fail; /* prepare nDNS bits */ simple_poll = avahi_simple_poll_new(); if (!simple_poll) { log_printf(LOG_ERR, "failed to create simple poll object\n"); goto fail; } client = avahi_client_new(avahi_simple_poll_get(simple_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; } /* 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); } } /* go ahead */ avahi_simple_poll_loop(simple_poll); ret = 0; fail: log_printf(ret ? LOG_ERR : LOG_INFO, "exiting (%d)\n", ret); /* Cleanup things */ if (client) avahi_client_free(client); if (simple_poll) avahi_simple_poll_free(simple_poll); avahi_free(name); return ret; }