diff options
-rw-r--r-- | .cvsignore | 2 | ||||
-rw-r--r-- | GNUmakefile | 41 | ||||
-rw-r--r-- | mdns-browser.c | 81 | ||||
-rw-r--r-- | mdns-publish-xendom.c | 180 | ||||
-rw-r--r-- | mdns.c | 655 | ||||
-rw-r--r-- | mdns.h | 11 | ||||
-rw-r--r-- | xd_view.c | 35 | ||||
-rw-r--r-- | xenwatch.c | 7 |
8 files changed, 909 insertions, 103 deletions
@@ -1,3 +1,5 @@ Make.config xenlog xenwatch +mdns-browser +mdns-publish-xendom diff --git a/GNUmakefile b/GNUmakefile index 5289274..57fa683 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -7,7 +7,12 @@ CFLAGS += -DVERSION='"$(VERSION)"' # build TARGETS := xenlog -GTK_TARGETS := xenwatch +BUILD_GTK := xenwatch mdns-browser +BUILD_MDNS := mdns-publish-xendom + +NEEDS_XENSTORE := xenlog xenwatch mdns-publish-xendom +NEEDS_GTK := xenwatch mdns-browser +NEEDS_MDNS := xenwatch mdns-browser mdns-publish-xendom # default target all: build @@ -26,22 +31,29 @@ endef # gtk stuff ifeq ($(HAVE_GTK),yes) - pkgs := gtk+-x11-2.0 - ifeq ($(HAVE_AVAHI),yes) - CFLAGS += -DHAVE_AVAHI=1 - pkgs += avahi-glib avahi-client - endif - $(GTK_TARGETS) : CFLAGS += $(shell pkg-config --cflags $(pkgs)) -Wno-strict-prototypes - $(GTK_TARGETS) : LDLIBS += $(shell pkg-config --libs $(pkgs)) - TARGETS += $(GTK_TARGETS) + $(NEEDS_GTK) : CFLAGS += -Wno-strict-prototypes + $(NEEDS_GTK) : pkglst += gtk+-x11-2.0 + TARGETS += $(BUILD_GTK) +endif + +# avahi stuff +ifeq ($(HAVE_AVAHI),yes) + $(NEEDS_MDNS) : CFLAGS += -DHAVE_AVAHI=1 + $(NEEDS_MDNS) : pkglst += avahi-client + $(NEEDS_GTK) : pkglst += avahi-glib + TARGETS += $(BUILD_MDNS) endif +# pkg-config flags +CFLAGS += $(shell test "$(pkglst)" != "" && pkg-config --cflags $(pkglst)) +LDLIBS += $(shell test "$(pkglst)" != "" && pkg-config --libs $(pkglst)) + # xenstore ifneq ($(XENSRC),) - CFLAGS += -I $(XENSRC)/dist/install/usr/include - LDLIBS += -I $(XENSRC)/dist/install/usr/$(LIB) + $(NEEDS_XENSTORE) : CFLAGS += -I $(XENSRC)/dist/install/usr/include + $(NEEDS_XENSTORE) : LDLIBS += -I $(XENSRC)/dist/install/usr/$(LIB) endif -LDLIBS += -lxenstore +$(NEEDS_XENSTORE) : LDLIBS += -lxenstore ######################################################################## @@ -62,9 +74,10 @@ realclean distclean: clean ############################################# -xenwatch: xenwatch.o xs_view.o xs_store.o xd_view.o xd_store.o tcp.o mdns.o - xenlog: xenlog.o +xenwatch: xenwatch.o xs_view.o xs_store.o xd_view.o xd_store.o tcp.o mdns.o +mdns-browser: mdns-browser.o mdns.o +mdns-publish-xendom: mdns-publish-xendom.o include mk/Compile.mk include mk/Maintainer.mk diff --git a/mdns-browser.c b/mdns-browser.c new file mode 100644 index 0000000..9f004b7 --- /dev/null +++ b/mdns-browser.c @@ -0,0 +1,81 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <locale.h> +#include <signal.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include "mdns.h" + +/* ------------------------------------------------------------------ */ + +static Display *dpy; +static struct mdns_window *mdns; + +static char *service = "_ssh._tcp"; +static char *domain = NULL; + +/* ------------------------------------------------------------------ */ + +static void usage(FILE *fp) +{ + fprintf(fp, + "This is a mDNS browser\n" + "\n" + "usage: mdns-browser [options]\n" + "options:\n" + " -h print this text\n" + " -s <service> specify service [%s]\n" + " -d <domain> specify domain\n" + "\n" + "-- \n" + "(c) 2006 Gerd Hoffmann <kraxel@suse.de>\n", + service); +} + +int +main(int argc, char *argv[]) +{ + int c; + + gtk_init(&argc, &argv); + for (;;) { + if (-1 == (c = getopt(argc, argv, "hs:d:"))) + break; + switch (c) { + case 's': + service = optarg; + break; + case 'd': + domain = optarg; + break; + case 'h': + usage(stdout); + exit(0); + default: + usage(stderr); + exit(1); + } + } + + dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC); + + mdns = mdns_create_window(1); + if (NULL == mdns) { + fprintf(stderr,"Oops: mDNS did't initialize ok\n"); + exit(1); + } + mdns_show_window(mdns); + mdns_browse(mdns, service, domain); + + gtk_main(); + fprintf(stderr,"bye...\n"); + exit(0); +} diff --git a/mdns-publish-xendom.c b/mdns-publish-xendom.c new file mode 100644 index 0000000..3d69ab6 --- /dev/null +++ b/mdns-publish-xendom.c @@ -0,0 +1,180 @@ +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/utsname.h> + +#include <xs.h> + +#include <avahi-client/client.h> +#include <avahi-client/publish.h> + +#include <avahi-common/alternative.h> +#include <avahi-common/simple-watch.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> + +static AvahiEntryGroup *group = NULL; +static AvahiSimplePoll *simple_poll = NULL; + +static char *name; +static char *service = "_xendom._tcp"; +static int port = 9; + +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: + fprintf(stderr, "Service '%s' successfully established.\n", name); + break; + case AVAHI_ENTRY_GROUP_COLLISION: + n = avahi_alternative_service_name(name); + avahi_free(name); + name = n; + fprintf(stderr, "Service name collision, renaming service to '%s'\n", name); + 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) +{ + char r[128]; + 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))) { + fprintf(stderr, "avahi_entry_group_new() failed: %s\n", + avahi_strerror(avahi_client_errno(c))); + goto fail; + } + } + + fprintf(stderr, "Adding service '%s'\n", name); + + /* Create some random TXT data */ + snprintf(r, sizeof(r), "random=%i", rand()); + + /* Add the service for IPP */ + ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, + name, service, NULL, NULL, port, + "test=blah", r, NULL); + if (ret < 0) { + fprintf(stderr, "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) { + fprintf(stderr, "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) +{ + switch (state) { + 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: + fprintf(stderr, "Client failure: %s\n", avahi_strerror(avahi_client_errno(c))); + avahi_simple_poll_quit(simple_poll); + break; + default: + break; + } +} + +static void xendom_info(void) +{ + struct xs_handle *xenstore; + struct xs_transaction_handle *xst; + char *xs_value; + + xenstore = xs_domain_open(); + if (NULL == xenstore) { + fprintf(stderr,"%s: can't connect to %s\n", __FUNCTION__, + xs_domain_dev()); + return; + } + + if (NULL == (xst = xs_transaction_start(xenstore))) + goto out; + xs_value = xs_read(xenstore, xst, "vm", NULL); + xs_transaction_end(xenstore, xst, 0); + if (!xs_value) + goto out; + + fprintf(stderr, "%s", xs_value); + + out: + return; +} + +int main(int argc, char*argv[]) +{ + AvahiClient *client = NULL; + char buf[128]; + struct utsname uts; + int error; + int ret = 1; + + /* figure name */ + uname(&uts); + snprintf(buf, sizeof(buf), "Xen domain %s", uts.nodename); + name = avahi_strdup(buf); + + /* figure domain info */ + xendom_info(); + + /* go announce info */ + simple_poll = avahi_simple_poll_new(); + if (!simple_poll) { + fprintf(stderr, "Failed to create simple poll object.\n"); + goto fail; + } + + client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, + client_callback, NULL, &error); + if (!client) { + fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error)); + goto fail; + } + + avahi_simple_poll_loop(simple_poll); + ret = 0; + +fail: + /* Cleanup things */ + if (client) + avahi_client_free(client); + + if (simple_poll) + avahi_simple_poll_free(simple_poll); + + avahi_free(name); + return ret; +} @@ -3,8 +3,15 @@ #include <unistd.h> #include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/if.h> /* FIXME: find more portable one */ + +#include <glib.h> +#include <gtk/gtk.h> + #ifdef HAVE_AVAHI -# include <glib.h> # include <avahi-client/client.h> # include <avahi-client/lookup.h> # include <avahi-common/error.h> @@ -18,6 +25,41 @@ #ifdef HAVE_AVAHI +static int debug = 0; + +/* ---------------------------------------------------------------------- */ + +enum { + /* browse */ + ST_COL_NAME = 0, + ST_COL_TYPE, + ST_COL_DOMAIN, + ST_COL_INTERFACE, + ST_COL_PROTOCOL, + + /* resolve */ + ST_COL_HOSTNAME, + ST_COL_ADDR, + ST_COL_PORT, + ST_COL_PATH, + + /* other */ + ST_COL_URL, + + ST_NUM_COLS +}; + +struct mdns_window { + GtkListStore *store; + GtkWidget *toplevel, *view, *status; + int standalone; + + const AvahiPoll *poll_api; + AvahiGLibPoll *glib_poll; + AvahiClient *client; + AvahiServiceBrowser *sb; +}; + /* ---------------------------------------------------------------------- */ static const char *revents[] = { @@ -32,27 +74,108 @@ static const char *bevents[] = { [ AVAHI_BROWSER_FAILURE ] = "FAILURE", }; -static const AvahiPoll *poll_api; -static AvahiGLibPoll *glib_poll; -static AvahiClient *client; - -struct mdns_entry { - const char *name; - const char *type; - const char *domain; - AvahiIfIndex interface; - AvahiProtocol protocol; - - const char *address; - const char *hostname; - uint16_t port; - - struct list_head next; -}; -static LIST_HEAD(mdns_entries); +/* ---------------------------------------------------------------------- */ + +static int find_entry(struct mdns_window *mdns, GtkTreeIter *iter, + const char *sname, const char *stype, const char *sdomain, + const char *sproto, const char *snif) +{ + GtkTreeModel *model = GTK_TREE_MODEL(mdns->store); + gboolean valid; + char *name, *type, *domain, *proto, *nif; + + for (valid = gtk_tree_model_get_iter_first(model, iter); + valid; + valid = gtk_tree_model_iter_next(model, iter)) + { + gtk_tree_model_get(model, iter, + ST_COL_NAME, &name, + ST_COL_TYPE, &type, + ST_COL_DOMAIN, &domain, + ST_COL_PROTOCOL, &proto, + ST_COL_INTERFACE, &nif, + -1); + if (0 == strcmp(name, sname) && + 0 == strcmp(type, stype) && + 0 == strcmp(domain, sdomain) && + 0 == strcmp(proto, sproto) && + 0 == strcmp(nif, snif)) + return 0; + } + return -1; +} + +static void get_entry(struct mdns_window *mdns, GtkTreeIter *iter, + const char *sname, const char *stype, const char *sdomain, + const char *sproto, const char *snif) +{ + if (0 == find_entry(mdns, iter, sname, stype, sdomain, sproto, snif)) + return; + gtk_list_store_append(mdns->store, iter); + gtk_list_store_set(mdns->store, iter, + ST_COL_NAME, sname, + ST_COL_TYPE, stype, + ST_COL_DOMAIN, sdomain, + ST_COL_PROTOCOL, sproto, + ST_COL_INTERFACE, snif, + -1); + if (debug) + fprintf(stderr, "add: %s: %s\n",stype, sname); +} + +static void del_entry(struct mdns_window *mdns, + const char *sname, const char *stype, const char *sdomain, + const char *sproto, const char *snif) +{ + GtkTreeIter iter; + + if (0 == find_entry(mdns, &iter, sname, stype, sdomain, sproto, snif)) + return; + gtk_list_store_remove(mdns->store, &iter); + if (debug) + fprintf(stderr, "del: %s: %s\n", stype, sname); +} + +static void del_entries(struct mdns_window *mdns) +{ + GtkTreeModel *model = GTK_TREE_MODEL(mdns->store); + GtkTreeIter iter; + + while (gtk_tree_model_get_iter_first(model, &iter)) + gtk_list_store_remove(mdns->store, &iter); +} /* ---------------------------------------------------------------------- */ +static void ifname(char *dst, int len, int nif) +{ +#ifdef SIOCGIFNAME + struct ifreq ifr; + int fd; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) + goto fallback; + + ifr.ifr_ifindex = nif; + if (ioctl (fd, SIOCGIFNAME, &ifr) < 0) { + close (fd); + goto fallback; + } + snprintf(dst, len, "%s", ifr.ifr_name); + close (fd); + return; + + fallback: +#endif + + if (-1 == nif) { + snprintf(dst, len, "wide"); + return; + } + snprintf(dst, len, "if-%d", nif); +} + static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, @@ -67,19 +190,66 @@ static void resolve_callback(AvahiServiceResolver *r, AvahiLookupResultFlags flags, void* userdata) { - struct mdns_entry *e = userdata; - char a[AVAHI_ADDRESS_STR_MAX]; + static const struct { + char *type; + char *proto; + int defport; + } protos[] = { + { "_http._tcp", "http", 80 }, + { "_ftp._tcp", "ftp", 21 }, + }; + struct mdns_window *mdns = userdata; + char a[AVAHI_ADDRESS_STR_MAX], p[32], url[256]; + unsigned char *path = NULL; + char *proto = NULL; + AvahiStringList *pathlist; + GtkTreeIter iter; + char nif[32]; + int defport, i; switch (event) { case AVAHI_RESOLVER_FOUND: + ifname(nif, sizeof(nif), interface); + if (0 != find_entry(mdns, &iter, name, type, domain, + avahi_proto_to_string(protocol), nif)) + break; avahi_address_snprint(a, sizeof(a), address); - e->address = strdup(a); - e->hostname = strdup(host_name); - e->port = port; - fprintf(stderr, "%s @ %s: %s | %s if #%d | %s:%d (%s)\n", - e->type, e->domain, e->name, - avahi_proto_to_string(e->protocol), e->interface, - e->address, e->port, e->hostname); + snprintf(p, sizeof(p), "%d", port); + gtk_list_store_set(mdns->store, &iter, + ST_COL_HOSTNAME, host_name, + ST_COL_ADDR, a, + ST_COL_PORT, p, + -1); + + /* path */ + pathlist = avahi_string_list_find(txt, "path"); + if (pathlist) + path = avahi_string_list_get_text(pathlist); + if (!path) + break; + gtk_list_store_set(mdns->store, &iter, + ST_COL_PATH, path+5, + -1); + + /* url */ + for (i = 0; i < sizeof(protos)/sizeof(protos[0]); i++) { + if (0 != strcmp(protos[i].type, type)) + continue; + proto = protos[i].proto; + defport = protos[i].defport; + break; + } + if (!proto) + break; + if (defport != port) + snprintf(url, sizeof(url), "%s://%s:%d%s", + proto, host_name, port, path+5); + else + snprintf(url, sizeof(url), "%s://%s%s", + proto, host_name, path+5); + gtk_list_store_set(mdns->store, &iter, + ST_COL_URL, url, + -1); break; default: fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, @@ -99,29 +269,29 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiLookupResultFlags flags, void* userdata) { - AvahiClient *c = userdata; - struct mdns_entry *e; + struct mdns_window *mdns = userdata; + GtkTreeIter iter; + char nif[32]; + + ifname(nif, sizeof(nif), interface); switch (event) { case AVAHI_BROWSER_NEW: - e = malloc(sizeof(*e)); - memset(e,0,sizeof(*e)); - e->name = strdup(name); - e->type = strdup(type); - e->domain = strdup(domain); - e->interface = interface; - e->protocol = protocol; - list_add_tail(&e->next, &mdns_entries); - avahi_service_resolver_new(c, interface, protocol, name, type, domain, - AVAHI_PROTO_UNSPEC, 0, resolve_callback, e); + get_entry(mdns, &iter, name, type, domain, + avahi_proto_to_string(protocol), nif); + avahi_service_resolver_new(mdns->client, + interface, protocol, name, type, domain, + AVAHI_PROTO_UNSPEC, 0, + resolve_callback, mdns); break; case AVAHI_BROWSER_REMOVE: - fprintf(stderr, "%s: DEL: service '%s' of type '%s' in domain '%s'\n", - __FUNCTION__, name, type, domain); + del_entry(mdns, name, type, domain, + avahi_proto_to_string(protocol), nif); break; default: - fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, - bevents[event], event); + if (debug) + fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, + bevents[event], event); break; } } @@ -129,80 +299,419 @@ static void browse_callback(AvahiServiceBrowser *b, static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) { +// struct mdns_window *mdns = userdata; + switch (state) { case AVAHI_CLIENT_FAILURE: fprintf(stderr, "%s: error: connection lost\n", __FUNCTION__); break; default: - fprintf(stderr, "%s: state %d\n", __FUNCTION__, state); + if (debug) + fprintf(stderr, "%s: state %d\n", __FUNCTION__, state); + break; + } +} + +/* ---------------------------------------------------------------------- */ + +static void service_type_browser_callback(AvahiServiceTypeBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata) +{ + char nif[32]; + + switch (event) { + case AVAHI_BROWSER_NEW: + ifname(nif, sizeof(nif), interface); + fprintf(stderr, " service: %s, %s, %s: %s\n", + nif, avahi_proto_to_string(protocol), domain, type); + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_FAILURE: + avahi_service_type_browser_free(b); + break; + case AVAHI_BROWSER_CACHE_EXHAUSTED: + /* don't log */ + break; + default: + fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, + bevents[event], event); + break; + } +} + +static void domain_browser_callback(AvahiDomainBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *domain, + AvahiLookupResultFlags flags, + void *userdata) +{ + struct mdns_window *mdns = userdata; + char nif[32]; + + switch (event) { + case AVAHI_BROWSER_NEW: + ifname(nif, sizeof(nif), interface); + fprintf(stderr, " domain : %s, %s, %s\n", + nif, avahi_proto_to_string(protocol), domain); + avahi_service_type_browser_new(mdns->client, + interface, protocol, domain, + 0, service_type_browser_callback, mdns); + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_FAILURE: + avahi_domain_browser_free(b); + break; + case AVAHI_BROWSER_CACHE_EXHAUSTED: + /* don't log */ + break; + default: + fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, + bevents[event], event); break; } } +static void dump_stuff(struct mdns_window *mdns) +{ + const char *domain; + + /* playground ... */ + domain = avahi_client_get_domain_name(mdns->client); + fprintf(stderr, "default domain is \"%s\"\n", domain); + avahi_service_type_browser_new(mdns->client, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, domain, + 0, service_type_browser_callback, mdns); + avahi_domain_browser_new(mdns->client, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, + AVAHI_DOMAIN_BROWSER_BROWSE, + 0, domain_browser_callback, mdns); +} + /* ---------------------------------------------------------------------- */ -int mdns_init(void) +static void mdns_fini(struct mdns_window *mdns) +{ + if (mdns->client) { + if (debug) + fprintf(stderr, "%s\n", __FUNCTION__); + avahi_client_free(mdns->client); + mdns->client = NULL; + } + if (mdns->glib_poll) { + avahi_glib_poll_free(mdns->glib_poll); + mdns->glib_poll = NULL; + } +} + +static int mdns_init(struct mdns_window *mdns) { int error; + if (mdns->client) + return 0; + if (debug) + fprintf(stderr, "%s\n", __FUNCTION__); + /* Create the GLIB Adaptor */ avahi_set_allocator(avahi_glib_allocator()); - glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT); - poll_api = avahi_glib_poll_get(glib_poll); - client = avahi_client_new(poll_api, AVAHI_CLIENT_NO_FAIL, - client_callback, NULL /* user data */, - &error); - if (client == NULL) + mdns->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT); + mdns->poll_api = avahi_glib_poll_get(mdns->glib_poll); + mdns->client = avahi_client_new(mdns->poll_api, AVAHI_CLIENT_NO_FAIL, + client_callback, mdns, + &error); + if (mdns->client == NULL) goto fail; + + if (0) + dump_stuff(mdns); return 0; fail: - mdns_fini(); + mdns_fini(mdns); return -1; } -int mdns_browse(const char *service, const char *domain) +int mdns_browse(struct mdns_window *mdns, + const char *service, const char *domain) { - AvahiServiceBrowser *sb = NULL; + char label[256]; + + if (mdns->sb) { + avahi_service_browser_free(mdns->sb); + mdns->sb = NULL; + del_entries(mdns); + gtk_label_set_text(GTK_LABEL(mdns->status), "idle"); + } if (NULL == domain) - domain = avahi_client_get_domain_name(client); - sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, - service, domain, 0, browse_callback, client); - if (NULL == sb) { + domain = avahi_client_get_domain_name(mdns->client); + mdns->sb = avahi_service_browser_new(mdns->client, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + service, domain, 0, + browse_callback, mdns); + if (NULL == mdns->sb) { fprintf(stderr, "%s: failed to create service browser: %s\n", - __FUNCTION__, avahi_strerror(avahi_client_errno(client))); + __FUNCTION__, avahi_strerror(avahi_client_errno(mdns->client))); return -1; } + snprintf(label, sizeof(label), + "service \"%s\" in domain \"%s\"", + service, domain); + gtk_label_set_text(GTK_LABEL(mdns->status), label); return 0; } -void mdns_fini(void) +/* ---------------------------------------------------------------------- */ + +static void menu_cb_close(GtkWidget *whatever, gpointer userdata) +{ + struct mdns_window *mdns = userdata; + + gtk_widget_destroy(mdns->toplevel); +} + +static void destroy(GtkWidget *widget, + gpointer data) +{ + struct mdns_window *mdns = data; + + mdns_fini(mdns); + g_object_unref(mdns->store); + if (mdns->standalone) + gtk_main_quit(); + free(mdns); +} + +/* ---------------------------------------------------------------------- */ + +static const GtkActionEntry entries[] = { + { + .name = "FileMenu", + .label = "_File", + },{ + .name = "Close", + .stock_id = GTK_STOCK_CLOSE, + .label = "_Close", + .accelerator = "<control>Q", + .callback = G_CALLBACK(menu_cb_close), + }, +}; + +static char ui_xml[] = +"<ui>" +" <menubar name='MainMenu'>" +" <menu action='FileMenu'>" +" <menuitem action='Close'/>" +" </menu>" +" </menubar>" +" <toolbar action='ToolBar'>" +" <toolitem action='Close'/>" +" </toolbar>" +"</ui>"; + +/* ------------------------------------------------------------------ */ + +static GtkWidget *mdns_create_view(struct mdns_window *mdns) +{ + GtkCellRenderer *renderer; + GtkWidget *view; + + view = gtk_tree_view_new(); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), + GTK_TREE_MODEL(mdns->store)); + gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), + GTK_SELECTION_SINGLE); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "name", renderer, + "text", ST_COL_NAME, + NULL); + +#if 0 + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "type", renderer, + "text", ST_COL_TYPE, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "domain", renderer, + "text", ST_COL_DOMAIN, + NULL); +#endif + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "if", renderer, + "text", ST_COL_INTERFACE, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "proto", renderer, + "text", ST_COL_PROTOCOL, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "hostname", renderer, + "text", ST_COL_HOSTNAME, + NULL); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, + "xalign", 1.0, + NULL); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "address", renderer, + "text", ST_COL_ADDR, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "port", renderer, + "text", ST_COL_PORT, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "path", renderer, + "text", ST_COL_PATH, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "url", renderer, + "text", ST_COL_URL, + NULL); + + /* fill remaining space */ + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW(view), -1, "", renderer, + NULL); + + return view; +} + +struct mdns_window *mdns_create_window(int standalone) { - if (client) { - avahi_client_free(client); - client = NULL; + struct mdns_window *mdns; + GtkWidget *vbox, *menubar, *toolbar, *scroll; + GtkAccelGroup *accel; + GtkActionGroup *ag; + GtkUIManager *ui; + GError *err; + + mdns = malloc(sizeof(*mdns)); + memset(mdns,0,sizeof(*mdns)); + if (-1 == mdns_init(mdns)) { + free(mdns); + return NULL; } - if (glib_poll) { - avahi_glib_poll_free(glib_poll); - glib_poll = NULL; + mdns->standalone = standalone; + + mdns->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(mdns->toplevel), "mdns"); + gtk_widget_set_size_request(GTK_WIDGET(mdns->toplevel), 480, 320); + g_signal_connect(G_OBJECT(mdns->toplevel), "destroy", + G_CALLBACK(destroy), mdns); + + /* menu + toolbar */ + ui = gtk_ui_manager_new(); + ag = gtk_action_group_new("MenuActions"); + gtk_action_group_add_actions(ag, entries, G_N_ELEMENTS(entries), mdns); + gtk_ui_manager_insert_action_group(ui, ag, 0); + accel = gtk_ui_manager_get_accel_group(ui); + gtk_window_add_accel_group(GTK_WINDOW(mdns->toplevel), accel); + + err = NULL; + if (!gtk_ui_manager_add_ui_from_string(ui, ui_xml, -1, &err)) { + g_message("building menus failed: %s", err->message); + g_error_free(err); + exit(1); } + + /* list */ + mdns->store = gtk_list_store_new(ST_NUM_COLS, + G_TYPE_STRING, // ST_COL_NAME + G_TYPE_STRING, // ST_COL_TYPE + G_TYPE_STRING, // ST_COL_DOMAIN + G_TYPE_STRING, // ST_COL_INTERFACE + G_TYPE_STRING, // ST_COL_PROTOCOL + + G_TYPE_STRING, // ST_COL_HOSTNAME + G_TYPE_STRING, // ST_COL_ADDR + G_TYPE_STRING, // ST_COL_PORT + G_TYPE_STRING, // ST_COL_PATH + + G_TYPE_STRING); // ST_COL_URL + mdns->view = mdns_create_view(mdns); +#if 0 + g_signal_connect(mdns->view, "row-activated", G_CALLBACK(activate), mdns); +#endif + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + + /* other widgets */ + mdns->status = gtk_widget_new(GTK_TYPE_LABEL, + "label", "status line", + "xalign", 0.0, + NULL); + + /* Make a vbox and put stuff in */ + vbox = gtk_vbox_new(FALSE, 1); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 1); + gtk_container_add(GTK_CONTAINER(mdns->toplevel), vbox); + menubar = gtk_ui_manager_get_widget(ui, "/MainMenu"); + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); + toolbar = gtk_ui_manager_get_widget(ui, "/ToolBar"); + gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(scroll), mdns->view); + gtk_box_pack_end(GTK_BOX(vbox), mdns->status, FALSE, TRUE, 0); + + return mdns; } -#else +void mdns_show_window(struct mdns_window *mdns) +{ + gtk_widget_show_all(mdns->toplevel); +} -int mdns_init(void) +void mdns_destroy_window(struct mdns_window *mdns) { - return -1; + gtk_widget_destroy(mdns->toplevel); } -int mdns_browse(const char *service, const char *domain) +/* ---------------------------------------------------------------------- */ + +#else /* ! HAVE_AVAHI */ + +struct mdns_window *mdns_create_window(void) { - return -1; + return NULL; } -void mdns_fini(void) +void mdns_show_window(struct mdns_window *mdns) {} +void mdns_destroy_window(struct mdns_window *mdns) {} + +int mdns_browse(struct mdns_window *mdns, + const char *service, const char *domain) { + return -1; } #endif @@ -1,3 +1,8 @@ -int mdns_init(void); -int mdns_browse(const char *service, const char *domain); -void mdns_fini(void); +struct mdns_window; + +struct mdns_window *mdns_create_window(int standalone); +void mdns_show_window(struct mdns_window *mdns); +void mdns_destroy_window(struct mdns_window *mdns); + +int mdns_browse(struct mdns_window *mdns, + const char *service, const char *domain); @@ -16,6 +16,7 @@ #include "xd_store.h" #include "xenviews.h" #include "tcp.h" +#include "mdns.h" #define XENCONSOLE "/usr/lib/xen/bin/xenconsole" @@ -420,6 +421,23 @@ static void menu_cb_xenstore(void) gtk_widget_show_all(xs_toplevel); } +static void menu_cb_mdns(void) +{ +#ifdef HAVE_AVAHI + struct mdns_window *mdns; + + mdns = mdns_create_window(0); + if (NULL == mdns) { + gtk_message(GTK_MESSAGE_ERROR, "Can't setup mDNS browser, sorry.\n"); + return; + } + mdns_browse(mdns, "_workstation._tcp", NULL); + mdns_show_window(mdns); +#else + gtk_message(GTK_MESSAGE_ERROR, "Compiled without mDNS support, sorry.\n"); +#endif +} + static void menu_cb_open_vnc(void) { char *name, *tty, *ostype; @@ -541,12 +559,6 @@ static const GtkActionEntry entries[] = { .callback = menu_cb_about, },{ - .name = "Xenstore", - .label = "_Xenstore browser", - .accelerator = "<control>X", - .callback = menu_cb_xenstore, - },{ - .name = "OpenVNC", .label = "_VNC", .accelerator = "<control>V", @@ -601,6 +613,16 @@ static const GtkActionEntry entries[] = { .label = "_Destroy", .tooltip = "Radically kill off domain", .callback = menu_cb_domain_destroy, + },{ + + .name = "Xenstore", + .label = "_Xenstore browser", + .accelerator = "<control>X", + .callback = menu_cb_xenstore, + },{ + .name = "mDNS", + .label = "mDNS browser", + .callback = menu_cb_mdns, }, }; @@ -624,6 +646,7 @@ static char ui_xml[] = " </menu>" " <menu action='WindowMenu'>" " <menuitem action='Xenstore'/>" +" <menuitem action='mDNS'/>" " </menu>" " <menu action='HelpMenu'>" " <menuitem action='About'/>" @@ -12,7 +12,6 @@ #include <gtk/gtk.h> #include "xenviews.h" -#include "mdns.h" /* ------------------------------------------------------------------ */ @@ -24,7 +23,6 @@ int main(int argc, char *argv[]) { setlocale(LC_ALL,""); - gtk_init(&argc, &argv); dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC); @@ -32,12 +30,7 @@ main(int argc, char *argv[]) xen_doms_create_window(); gtk_widget_show_all(xd_toplevel); - mdns_init(); - mdns_browse("_ssh._tcp", NULL); - gtk_main(); - - mdns_fini(); fprintf(stderr,"bye...\n"); exit(0); } |