4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <avahi-common/strlst.h>
31 #include <avahi-common/malloc.h>
32 #include <avahi-common/domain.h>
33 #include <avahi-common/simple-watch.h>
34 #include <avahi-common/error.h>
35 #include <avahi-client/client.h>
45 COMMAND_POLL_DONE = 'P'
56 typedef struct oid_data {
59 sw_discovery discovery;
61 sw_result (*reply)(void);
64 struct _sw_discovery {
66 AvahiSimplePoll *simple_poll;
69 oid_data oid_table[OID_MAX];
70 sw_discovery_oid oid_index;
72 int thread_fd, main_fd;
77 pthread_mutex_t mutex;
81 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
83 static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
90 if (s[strlen(s)-1] == '.')
93 snprintf(buf, buf_len, "%s.", s);
97 static sw_result map_error(int error) {
102 case AVAHI_ERR_NO_MEMORY:
109 static int read_command(int fd) {
115 if ((r = read(fd, &command, 1)) != 1) {
116 fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
123 static int write_command(int fd, char reply) {
126 if (write(fd, &reply, 1) != 1) {
127 fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
134 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
135 sw_discovery self = userdata;
140 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
141 ret = poll(ufds, nfds, timeout);
142 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
147 static void * thread_func(void *data) {
148 sw_discovery self = data;
152 pthread_sigmask(SIG_BLOCK, &mask, NULL);
154 self->thread = pthread_self();
155 self->thread_running = 1;
160 if ((command = read_command(self->thread_fd)) < 0)
163 /* fprintf(stderr, "Command: %c\n", command); */
169 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
171 if (avahi_simple_poll_run(self->simple_poll) < 0) {
172 fprintf(stderr, __FILE__": avahi_simple_poll_run() failed.\n");
173 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
177 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
179 if (write_command(self->thread_fd, COMMAND_POLL_DONE) < 0)
193 static int oid_alloc(sw_discovery self, oid_type type) {
197 for (i = 0; i < OID_MAX; i++) {
199 while (self->oid_index >= OID_MAX)
200 self->oid_index -= OID_MAX;
202 if (self->oid_table[self->oid_index].type == OID_UNUSED) {
203 self->oid_table[self->oid_index].type = type;
204 self->oid_table[self->oid_index].discovery = self;
205 return self->oid_index ++;
211 /* No free entry found */
213 return (sw_discovery_oid) -1;
216 static void oid_release(sw_discovery self, sw_discovery_oid oid) {
218 assert(oid < OID_MAX);
220 assert(self->oid_table[oid].type != OID_UNUSED);
222 self->oid_table[oid].type = OID_UNUSED;
223 self->oid_table[oid].discovery = NULL;
224 self->oid_table[oid].reply = NULL;
225 self->oid_table[oid].object = NULL;
226 self->oid_table[oid].extra = NULL;
229 static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) {
235 if (self->oid_table[oid].type == OID_UNUSED)
238 return &self->oid_table[oid];
241 sw_result sw_discovery_init(sw_discovery * self) {
242 int fd[2] = { -1, -1};
243 sw_result result = SW_E_UNKNOWN;
244 pthread_mutexattr_t mutex_attr;
253 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
256 if (!(*self = avahi_new(struct _sw_discovery, 1))) {
262 (*self)->thread_fd = fd[0];
263 (*self)->main_fd = fd[1];
265 (*self)->client = NULL;
266 (*self)->simple_poll = NULL;
268 memset((*self)->oid_table, 0, sizeof((*self)->oid_table));
269 (*self)->oid_index = 0;
271 (*self)->thread_running = 0;
273 ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
274 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
275 ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, NULL));
277 if (!((*self)->simple_poll = avahi_simple_poll_new()))
280 avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self);
282 if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), NULL, *self, &error))) {
283 result = map_error(error);
287 /* Start simple poll */
288 if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0)
291 /* Queue an initial POLL command for the thread */
292 if (write_command((*self)->main_fd, COMMAND_POLL) < 0)
295 if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0)
298 (*self)->thread_running = 1;
305 sw_discovery_fina(*self);
310 static int stop_thread(sw_discovery self) {
313 if (!self->thread_running)
316 if (write_command(self->main_fd, COMMAND_QUIT) < 0)
319 avahi_simple_poll_wakeup(self->simple_poll);
321 ASSERT_SUCCESS(pthread_join(self->thread, NULL));
322 self->thread_running = 0;
326 static sw_discovery discover_ref(sw_discovery self) {
328 assert(self->n_ref >= 1);
335 static void discover_unref(sw_discovery self) {
337 assert(self->n_ref >= 1);
339 if (--self->n_ref > 0)
345 avahi_client_free(self->client);
347 if (self->simple_poll)
348 avahi_simple_poll_free(self->simple_poll);
350 if (self->thread_fd >= 0)
351 close(self->thread_fd);
353 if (self->main_fd >= 0)
354 close(self->main_fd);
356 ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex));
361 sw_result sw_discovery_fina(sw_discovery self) {
367 discover_unref(self);
372 sw_result sw_discovery_run(sw_discovery self) {
377 return sw_salt_run((sw_salt) self);
380 sw_result sw_discovery_stop_run(sw_discovery self) {
385 return sw_salt_stop_run((sw_salt) self);
388 int sw_discovery_socket(sw_discovery self) {
393 return self->main_fd;
396 sw_result sw_discovery_read_socket(sw_discovery self) {
397 sw_result result = SW_E_UNKNOWN;
403 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
405 /* Cleanup notification socket */
406 if (read_command(self->main_fd) != COMMAND_POLL_DONE)
409 if (avahi_simple_poll_dispatch(self->simple_poll) < 0)
412 if (self->n_ref > 1) /* Perhaps we should die */
414 /* Dispatch events */
415 if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0)
420 /* Request the poll */
421 if (write_command(self->main_fd, COMMAND_POLL) < 0)
428 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
430 discover_unref(self);
435 sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) {
441 *salt = (sw_salt) self;
446 sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) {
453 if (!((sw_discovery) self)->thread_running)
456 memset(&p, 0, sizeof(p));
457 p.fd = ((sw_discovery) self)->main_fd;
460 if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) {
462 /* Don't treat EINTR as error */
476 if (p.revents != POLLIN)
479 if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY)
486 sw_result sw_salt_run(sw_salt self) {
492 if ((ret = sw_salt_step(self, NULL)) != SW_OKAY)
496 sw_result sw_salt_stop_run(sw_salt self) {
499 if (stop_thread((sw_discovery) self) < 0)
505 sw_result sw_discovery_publish(
507 sw_uint32 interface_index,
508 sw_const_string name,
509 sw_const_string type,
510 sw_const_string domain,
511 sw_const_string host,
513 sw_octets text_record,
514 sw_uint32 text_record_len,
515 sw_discovery_publish_reply reply,
517 sw_discovery_oid * oid) {
518 AVAHI_WARN_UNSUPPORTED;
522 sw_result sw_discovery_browse_domains(
524 sw_uint32 interface_index,
525 sw_discovery_browse_reply reply,
527 sw_discovery_oid * oid) {
528 AVAHI_WARN_UNSUPPORTED;
532 sw_result sw_discovery_resolve(
534 sw_uint32 interface_index,
535 sw_const_string name,
536 sw_const_string type,
537 sw_const_string domain,
538 sw_discovery_resolve_reply reply,
540 sw_discovery_oid * oid) {
541 AVAHI_WARN_UNSUPPORTED;
545 #define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table))/sizeof(oid_data)))
547 static void service_browser_callback(
548 AvahiServiceBrowser *b,
549 AvahiIfIndex interface,
550 AvahiProtocol protocol,
551 AvahiBrowserEvent event,
555 AvahiLookupResultFlags flags,
558 oid_data* data = userdata;
559 char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
560 sw_discovery_browse_reply reply;
565 reply = (sw_discovery_browse_reply) data->reply;
567 type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
568 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
571 case AVAHI_BROWSER_NEW:
572 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra);
575 case AVAHI_BROWSER_REMOVE:
576 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra);
579 case AVAHI_BROWSER_FAILURE:
580 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra);
583 case AVAHI_BROWSER_CACHE_EXHAUSTED:
584 case AVAHI_BROWSER_ALL_FOR_NOW:
590 sw_result sw_discovery_browse(
592 sw_uint32 interface_index,
593 sw_const_string type,
594 sw_const_string domain,
595 sw_discovery_browse_reply reply,
597 sw_discovery_oid * oid) {
600 AvahiIfIndex ifindex;
601 sw_result result = SW_E_UNKNOWN;
610 if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1)
613 data = oid_get(self, *oid);
615 data->reply = (sw_result (*)(void)) reply;
618 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
620 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
622 if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_UNSPEC, type, domain, 0, service_browser_callback, data))) {
623 result = map_error(avahi_client_errno(self->client));
631 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
633 if (result != SW_OKAY)
634 if (*oid != (sw_discovery_oid) -1)
635 oid_release(self, *oid);
640 sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) {
646 if (!(data = oid_get(self, oid)))
649 switch (data->type) {
650 case OID_SERVICE_BROWSER:
651 avahi_service_browser_free(data->object);
658 oid_release(self, oid);