From 7e2e7661c325718982387c3355d4f929e4025ec7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Sep 2007 23:46:14 +0200 Subject: add avahi browsing support --- lassi-avahi.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 lassi-avahi.c (limited to 'lassi-avahi.c') diff --git a/lassi-avahi.c b/lassi-avahi.c new file mode 100644 index 0000000..9fb81c9 --- /dev/null +++ b/lassi-avahi.c @@ -0,0 +1,229 @@ +#include + +#include +#include +#include + +#include "lassi-avahi.h" + +#define SERVICE_TYPE "_mango-lassi._tcp" + +/* FIXME: Error and collision handling is suboptimal */ + +static void resolve_cb( + AvahiServiceResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void* userdata) { + + LassiAvahiInfo *i = userdata; + + g_assert(r); + g_assert(i); + + /* Called whenever a service has been resolved successfully or timed out */ + + switch (event) { + case AVAHI_RESOLVER_FOUND: { + char a[AVAHI_ADDRESS_STR_MAX], *t; + + avahi_address_snprint(a, sizeof(a), address); + t = g_strdup_printf("tcp:port=%u,host=%s", port, a); + lassi_server_connect(i->server, t); + g_free(t); + break; + } + + case AVAHI_RESOLVER_FAILURE: + g_message("Failed to resolve service '%s' of type '%s' in domain '%s': %s", name, type, domain, avahi_strerror(avahi_client_errno(i->client))); + break; + + } + + avahi_service_resolver_free(r); + } + +static void browse_cb( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void* userdata) { + + LassiAvahiInfo *i = userdata; + + g_assert(b); + g_assert(i); + + switch (event) { + case AVAHI_BROWSER_NEW: + + if (!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN)) + if (!(avahi_service_resolver_new(i->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_cb, i))) + g_message("Failed to resolve service '%s': %s", name, avahi_strerror(avahi_client_errno(i->client))); + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + + case AVAHI_BROWSER_FAILURE: + g_message("Browsing failed: %s", avahi_strerror(avahi_client_errno(i->client))); + break; + } +} + + +static void create_service(LassiAvahiInfo *i); + +static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { + LassiAvahiInfo *i = userdata; + + g_assert(g); + g_assert(i); + + i->group = g; + + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED : + g_message("Service '%s' successfully established.", i->service_name); + break; + + case AVAHI_ENTRY_GROUP_COLLISION : { + char *n; + + n = avahi_alternative_service_name(i->service_name); + avahi_free(i->service_name); + i->service_name = n; + + g_message("Service name collision, renaming service to '%s'", n); + + /* And recreate the services */ + create_service(i); + break; + } + + case AVAHI_ENTRY_GROUP_FAILURE : + g_message("Entry group failure: %s", avahi_strerror(avahi_client_errno(i->client))); + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + } +} + +static void create_service(LassiAvahiInfo *i) { + g_assert(i); + + if (!i->group) + if (!(i->group = avahi_entry_group_new(i->client, entry_group_callback, i))) { + g_message("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(i->client))); + return; + } + + if (avahi_entry_group_is_empty(i->group)) { + int ret; + + if (!i->service_name) + i->service_name = g_strdup(i->server->id); + + if ((ret = avahi_entry_group_add_service(i->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, i->service_name, SERVICE_TYPE, NULL, NULL, i->server->port, NULL)) < 0) { + g_message("Failed to add service: %s", avahi_strerror(ret)); + return; + } + + if ((ret = avahi_entry_group_commit(i->group)) < 0) { + g_message("Failed to commit entry group: %s", avahi_strerror(ret)); + return; + } + } +} + +static void client_cb(AvahiClient *client, AvahiClientState state, void *userdata) { + LassiAvahiInfo *i = userdata; + + i->client = client; + + switch (state) { + case AVAHI_CLIENT_S_RUNNING: + if (!i->group) + create_service(i); + break; + + case AVAHI_CLIENT_FAILURE: + g_message("Client failure: %s", avahi_strerror(avahi_client_errno(client))); + break; + + case AVAHI_CLIENT_S_COLLISION: + case AVAHI_CLIENT_S_REGISTERING: + if (i->group) + avahi_entry_group_reset(i->group); + + break; + + case AVAHI_CLIENT_CONNECTING: + ; + } +} + +int lassi_avahi_init(LassiAvahiInfo *i, LassiServer *server) { + int error; + + g_assert(i); + g_assert(server); + + memset(i, 0, sizeof(*i)); + i->server = server; + + avahi_set_allocator(avahi_glib_allocator()); + + if (!(i->poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) { + g_message("avahi_glib_poll_new() failed."); + goto fail; + } + + if (!(i->client = avahi_client_new(avahi_glib_poll_get(i->poll), 0, client_cb, i, &error))) { + g_message("avahi_client_new() failed: %s", avahi_strerror(error)); + goto fail; + } + + if (!(i->browser = avahi_service_browser_new(i->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, NULL, 0, browse_cb, i))) { + g_message("avahi_service_browser_new(): %s", avahi_strerror(avahi_client_errno(i->client))); + goto fail; + } + + return 0; + +fail: + lassi_avahi_done(i); + return -1; +} + +void lassi_avahi_done(LassiAvahiInfo *i) { + g_assert(i); + + if (i->client) + avahi_client_free(i->client); + + if (i->poll) + avahi_glib_poll_free(i->poll); + + g_free(i->service_name); + + memset(i, 0, sizeof(*i)); +} -- cgit