#include <avahi-common/domain.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/error.h>
+#include <avahi-common/llist.h>
#include <avahi-client/client.h>
#include "howl.h"
OID_ENTRY_GROUP
} oid_type;
+typedef struct service_data service_data;
+
typedef struct oid_data {
oid_type type;
sw_opaque extra;
sw_discovery discovery;
void *object;
sw_result (*reply)(void);
+ service_data *service_data;
} oid_data;
+
+struct service_data {
+ char *name, *regtype, *domain, *host;
+ uint16_t port;
+ AvahiIfIndex interface;
+ AvahiStringList *txt;
+ AVAHI_LLIST_FIELDS(service_data, services);
+};
+
struct _sw_discovery {
int n_ref;
AvahiSimplePoll *simple_poll;
pthread_mutex_t mutex;
+ AVAHI_LLIST_HEAD(service_data, services);
};
#define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
#define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table))))
+static sw_discovery discovery_ref(sw_discovery self);
+static void discovery_unref(sw_discovery self);
+
static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
if (!s)
return NULL;
self->oid_table[oid].reply = NULL;
self->oid_table[oid].object = NULL;
self->oid_table[oid].extra = NULL;
+ self->oid_table[oid].service_data = NULL;
}
static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) {
return &self->oid_table[oid];
}
+static service_data* service_data_new(sw_discovery self) {
+ service_data *sdata;
+
+ assert(self);
+
+ if (!(sdata = avahi_new0(service_data, 1)))
+ return NULL;
+
+ AVAHI_LLIST_PREPEND(service_data, services, self->services, sdata);
+
+ return sdata;
+
+}
+
+static void service_data_free(sw_discovery self, service_data* sdata) {
+ assert(self);
+ assert(sdata);
+
+ AVAHI_LLIST_REMOVE(service_data, services, self->services, sdata);
+
+ avahi_free(sdata->name);
+ avahi_free(sdata->regtype);
+ avahi_free(sdata->domain);
+ avahi_free(sdata->host);
+ avahi_string_list_free(sdata->txt);
+ avahi_free(sdata);
+}
+
+static void reg_client_callback(oid_data *data, AvahiClientState state);
+
+static void client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
+ sw_discovery self = userdata;
+ sw_discovery_oid oid;
+
+ assert(s);
+ assert(self);
+
+ discovery_ref(self);
+
+ for (oid = 0; oid < OID_MAX; oid++) {
+
+ switch (self->oid_table[oid].type) {
+
+ case OID_ENTRY_GROUP:
+ reg_client_callback(&self->oid_table[oid], state);
+ break;
+
+ case OID_DOMAIN_BROWSER:
+ case OID_SERVICE_BROWSER:
+ ((sw_discovery_browse_reply) self->oid_table[oid].reply)(self, oid, SW_DISCOVERY_BROWSE_INVALID, 0, NULL, NULL, NULL, self->oid_table[oid].extra);
+ break;
+
+ case OID_SERVICE_RESOLVER:
+ case OID_UNUSED:
+ ;
+ }
+ }
+
+ discovery_unref(self);
+}
+
sw_result sw_discovery_init(sw_discovery * self) {
int fd[2] = { -1, -1};
sw_result result = SW_E_UNKNOWN;
(*self)->thread_running = 0;
+ AVAHI_LLIST_HEAD_INIT(service_info, (*self)->services);
+
ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, &mutex_attr));
avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self);
- if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), NULL, *self, &error))) {
+ if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), client_callback, *self, &error))) {
result = map_error(error);
goto fail;
}
return 0;
}
-static sw_discovery discover_ref(sw_discovery self) {
+static sw_discovery discovery_ref(sw_discovery self) {
assert(self);
assert(self->n_ref >= 1);
return self;
}
-static void discover_unref(sw_discovery self) {
+static void discovery_unref(sw_discovery self) {
assert(self);
assert(self->n_ref >= 1);
close(self->main_fd);
ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex));
+
+ while (self->services)
+ service_data_free(self, self->services);
avahi_free(self);
}
AVAHI_WARN_LINKAGE;
stop_thread(self);
- discover_unref(self);
+ discovery_unref(self);
return SW_OKAY;
}
assert(self);
- discover_ref(self);
+ discovery_ref(self);
ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
- discover_unref(self);
+ discovery_unref(self);
return result;
}
return SW_OKAY;
}
+static void reg_report_status(oid_data *data, sw_discovery_publish_status status) {
+ sw_discovery_publish_reply reply;
+
+ assert(data);
+
+ reply = (sw_discovery_publish_reply) data->reply;
+
+ reply(data->discovery,
+ OID_GET_INDEX(data),
+ status,
+ data->extra);
+}
+
+static int reg_create_service(oid_data *data) {
+ int ret;
+ const char *real_type;
+
+ assert(data);
+
+ real_type = avahi_get_type_from_subtype(data->service_data->regtype);
+
+ if ((ret = avahi_entry_group_add_service_strlst(
+ data->object,
+ data->service_data->interface,
+ AVAHI_PROTO_INET,
+ 0,
+ data->service_data->name,
+ real_type ? real_type : data->service_data->regtype,
+ data->service_data->domain,
+ data->service_data->host,
+ data->service_data->port,
+ data->service_data->txt)) < 0)
+ return ret;
+
+ if (real_type) {
+ /* Create a subtype entry */
+
+ if (avahi_entry_group_add_service_subtype(
+ data->object,
+ data->service_data->interface,
+ AVAHI_PROTO_INET,
+ 0,
+ data->service_data->name,
+ real_type,
+ data->service_data->domain,
+ data->service_data->regtype) < 0)
+ return ret;
+
+ }
+
+ if ((ret = avahi_entry_group_commit(data->object)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static void reg_client_callback(oid_data *data, AvahiClientState state) {
+ assert(data);
+
+ /* We've not been setup completely */
+ if (!data->object)
+ return;
+
+ switch (state) {
+ case AVAHI_CLIENT_DISCONNECTED:
+ reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
+ break;
+
+ case AVAHI_CLIENT_S_RUNNING: {
+ int ret;
+
+ /* Register the service */
+ if ((ret = reg_create_service(data)) < 0) {
+ reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
+ return;
+ }
+
+ break;
+ }
+
+ case AVAHI_CLIENT_S_COLLISION:
+
+ /* Remove our entry */
+ avahi_entry_group_reset(data->object);
+ break;
+
+ case AVAHI_CLIENT_S_INVALID:
+ case AVAHI_CLIENT_S_REGISTERING:
+ /* Ignore */
+ break;
+ }
+
+}
+
+static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
+ oid_data *data = userdata;
+
+ assert(g);
+ assert(data);
+
+ switch (state) {
+ case AVAHI_ENTRY_GROUP_ESTABLISHED:
+
+ reg_report_status(data, SW_DISCOVERY_PUBLISH_STARTED);
+ break;
+
+ case AVAHI_ENTRY_GROUP_COLLISION:
+
+ reg_report_status(data, SW_DISCOVERY_PUBLISH_NAME_COLLISION);
+ break;
+
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ /* Ignore */
+ break;
+ }
+}
+
sw_result sw_discovery_publish(
sw_discovery self,
sw_uint32 interface_index,
sw_discovery_publish_reply reply,
sw_opaque extra,
sw_discovery_oid * oid) {
- AVAHI_WARN_UNSUPPORTED;
- return SW_E_NO_IMPL;
+
+ oid_data *data;
+ sw_result result = SW_E_UNKNOWN;
+ service_data *sdata;
+
+ assert(self);
+ assert(name);
+ assert(type);
+ assert(reply);
+ assert(oid);
+
+ AVAHI_WARN_LINKAGE;
+
+ if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1)
+ return SW_E_UNKNOWN;
+
+ if (!(sdata = service_data_new(self))) {
+ oid_release(self, *oid);
+ return SW_E_MEM;
+ }
+
+ data = oid_get(self, *oid);
+ assert(data);
+ data->reply = (sw_result (*)(void)) reply;
+ data->extra = extra;
+ data->service_data = sdata;
+
+ sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
+ sdata->name = avahi_strdup(name);
+ sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL;
+ sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
+ sdata->host = host ? avahi_normalize_name_strdup(host) : NULL;
+ sdata->port = port;
+ sdata->txt = text_record && text_record_len > 0 ? avahi_string_list_parse(text_record, text_record_len) : NULL;
+
+ /* Some OOM checking would be cool here */
+
+ ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
+
+ if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) {
+ result = map_error(avahi_client_errno(self->client));
+ goto finish;
+ }
+
+ if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) {
+ int error;
+
+ if ((error = reg_create_service(data)) < 0) {
+ result = map_error(error);
+ goto finish;
+ }
+ }
+
+ result = SW_OKAY;
+
+finish:
+
+ ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
+
+ if (result != SW_OKAY)
+ if (*oid != (sw_discovery_oid) -1)
+ sw_discovery_cancel(self, *oid);
+
+ return result;
}
static void domain_browser_callback(
if (result != SW_OKAY)
if (*oid != (sw_discovery_oid) -1)
- oid_release(self, *oid);
+ sw_discovery_cancel(self, *oid);
return result;
}
if (result != SW_OKAY)
if (*oid != (sw_discovery_oid) -1)
- oid_release(self, *oid);
+ sw_discovery_cancel(self, *oid);
return result;
}
if (result != SW_OKAY)
if (*oid != (sw_discovery_oid) -1)
- oid_release(self, *oid);
+ sw_discovery_cancel(self, *oid);
return result;
}
if (!(data = oid_get(self, oid)))
return SW_E_UNKNOWN;
- switch (data->type) {
- case OID_SERVICE_BROWSER:
- avahi_service_browser_free(data->object);
- break;
-
- case OID_SERVICE_RESOLVER:
- avahi_service_resolver_free(data->object);
- break;
-
- case OID_DOMAIN_BROWSER:
- avahi_domain_browser_free(data->object);
- break;
-
- case OID_ENTRY_GROUP:
- avahi_entry_group_free(data->object);
- break;
-
- case OID_UNUSED:
+ if (data->object) {
+ switch (data->type) {
+ case OID_SERVICE_BROWSER:
+ avahi_service_browser_free(data->object);
+ break;
+
+ case OID_SERVICE_RESOLVER:
+ avahi_service_resolver_free(data->object);
+ break;
+
+ case OID_DOMAIN_BROWSER:
+ avahi_domain_browser_free(data->object);
+ break;
+
+ case OID_ENTRY_GROUP:
+ avahi_entry_group_free(data->object);
+ break;
+
+ case OID_UNUSED:
;
+ }
+ }
+
+ if (data->service_data) {
+ assert(data->type == OID_ENTRY_GROUP);
+ service_data_free(self, data->service_data);
}
oid_release(self, oid);
return SW_OKAY;
}
+
+sw_result sw_discovery_init_with_flags(
+ sw_discovery * self,
+ sw_discovery_init_flags flags) {
+
+ assert(self);
+
+ AVAHI_WARN_LINKAGE;
+
+ if (flags != SW_DISCOVERY_USE_SHARED_SERVICE)
+ return SW_E_NO_IMPL;
+
+ return sw_discovery_init(self);
+}
-Functions marked with "x" are already implemented.
+-- Supported --
-Supported:
-
-x sw_discovery_init
-x sw_discovery_fina
+sw_discovery_init
+sw_discovery_init_with_flags
+sw_discovery_fina
sw_discovery_publish
-x sw_discovery_browse_domains
-x sw_discovery_browse
-x sw_discovery_resolve
-x sw_discovery_cancel
-x sw_discovery_run
-x sw_discovery_stop_run
-x sw_discovery_socket
-x sw_discovery_read_socket
-x sw_discovery_salt
+sw_discovery_browse_domains
+sw_discovery_browse
+sw_discovery_resolve
+sw_discovery_cancel
+sw_discovery_run
+sw_discovery_stop_run
+sw_discovery_socket
+sw_discovery_read_socket
+sw_discovery_salt
+
+sw_text_record_init
+sw_text_record_fina
+sw_text_record_add_string
+sw_text_record_add_key_and_string_value
+sw_text_record_add_key_and_binary_value
+sw_text_record_bytes
+sw_text_record_len
+sw_text_record_iterator_init
+sw_text_record_iterator_fina
+sw_text_record_iterator_next
-x sw_text_record_init
-x sw_text_record_fina
-x sw_text_record_add_string
-x sw_text_record_add_key_and_string_value
-x sw_text_record_add_key_and_binary_value
-x sw_text_record_bytes
-x sw_text_record_len
-x sw_text_record_iterator_init
-x sw_text_record_iterator_fina
-x sw_text_record_iterator_next
+sw_ipv4_address_any
+sw_ipv4_address_loopback
+sw_ipv4_address_init
+sw_ipv4_address_init_from_saddr
+sw_ipv4_address_init_from_name
+sw_ipv4_address_init_from_address
+sw_ipv4_address_init_from_this_host
+sw_ipv4_address_fina
+sw_ipv4_address_is_any
+sw_ipv4_address_saddr
+sw_ipv4_address_name
+sw_ipv4_address_decompose
+sw_ipv4_address_equals
-x sw_ipv4_address_any
-x sw_ipv4_address_loopback
-x sw_ipv4_address_init
-x sw_ipv4_address_init_from_saddr
-x sw_ipv4_address_init_from_name
-x sw_ipv4_address_init_from_address
-x sw_ipv4_address_init_from_this_host
-x sw_ipv4_address_fina
-x sw_ipv4_address_is_any
-x sw_ipv4_address_saddr
-x sw_ipv4_address_name
-x sw_ipv4_address_decompose
-x sw_ipv4_address_equals
+sw_salt_step
-x sw_salt_step
+-- Unsupported but Relevant --
+
+sw_discovery_publish_update
+sw_discovery_publish_host
+sw_discovery_query_record
-Unsupported:
+-- Unsupported and Irrelevant --
sw_strdup
sw_strerror
sw_corby_object_recv
sw_corby_object_channel
sw_corby_object_set_channel
-sw_discovery_init_with_flags
-sw_discovery_publish_update
-sw_discovery_publish_host
-sw_discovery_query_record
sw_text_record_string_iterator_init
sw_text_record_string_iterator_fina
sw_text_record_string_iterator_next
+
+$Id$