summaryrefslogtreecommitdiffstats
path: root/lassi-avahi.c
diff options
context:
space:
mode:
Diffstat (limited to 'lassi-avahi.c')
-rw-r--r--lassi-avahi.c229
1 files changed, 229 insertions, 0 deletions
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 <string.h>
+
+#include <avahi-common/error.h>
+#include <avahi-common/alternative.h>
+#include <avahi-glib/glib-malloc.h>
+
+#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));
+}