]> git.meshlink.io Git - catta/blob - avahi-compat-howl/compat.c
compat-howl:
[catta] / avahi-compat-howl / compat.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
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.
10  
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.
15  
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
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
27
28 #include <pthread.h>
29
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-common/llist.h>
36 #include <avahi-client/client.h>
37
38 #include "howl.h"
39 #include "warn.h"
40
41 #define OID_MAX 50
42
43 enum {
44     COMMAND_POLL = 'p',
45     COMMAND_QUIT = 'q',
46     COMMAND_POLL_DONE = 'P'
47 };
48
49 typedef enum {
50     OID_UNUSED = 0,
51     OID_SERVICE_BROWSER,
52     OID_SERVICE_RESOLVER,
53     OID_DOMAIN_BROWSER,
54     OID_ENTRY_GROUP
55 } oid_type;
56
57 typedef struct service_data service_data;
58
59 typedef struct oid_data {
60     oid_type type;
61     sw_opaque extra;
62     sw_discovery discovery;
63     void *object;
64     sw_result (*reply)(void);
65     service_data *service_data;
66 } oid_data;
67
68
69 struct service_data {
70     char *name, *regtype, *domain, *host;
71     uint16_t port;
72     AvahiIfIndex interface;
73     AvahiStringList *txt;
74     AVAHI_LLIST_FIELDS(service_data, services);
75 };
76
77 struct _sw_discovery {
78     int n_ref;
79     AvahiSimplePoll *simple_poll;
80     AvahiClient *client;
81
82     oid_data oid_table[OID_MAX];
83     sw_discovery_oid oid_index;
84
85     int thread_fd, main_fd;
86     
87     pthread_t thread;
88     int thread_running;
89
90     pthread_mutex_t mutex;
91
92     AVAHI_LLIST_HEAD(service_data, services);
93 };
94
95 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
96
97 #define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table))))
98
99 static sw_discovery discovery_ref(sw_discovery self);
100 static void discovery_unref(sw_discovery self);
101
102 static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
103     if (!s)
104         return NULL;
105
106     if (*s == 0)
107         return s;
108
109     if (s[strlen(s)-1] == '.')
110         return s;
111
112     snprintf(buf, buf_len, "%s.", s);
113     return buf;
114 }
115
116 static sw_result map_error(int error) {
117     switch (error) {
118         case AVAHI_OK:
119             return SW_OKAY;
120             
121         case AVAHI_ERR_NO_MEMORY:
122             return SW_E_MEM;
123     }
124
125     return SW_E_UNKNOWN;
126 }
127
128 static int read_command(int fd) {
129     ssize_t r;
130     char command;
131
132     assert(fd >= 0);
133     
134     if ((r = read(fd, &command, 1)) != 1) {
135         fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
136         return -1;
137     }
138
139     return command;
140 }
141
142 static int write_command(int fd, char reply) {
143     assert(fd >= 0);
144
145     if (write(fd, &reply, 1) != 1) {
146         fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
147         return -1;
148     }
149
150     return 0;
151 }
152
153 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
154     sw_discovery self = userdata;
155     int ret;
156     
157     assert(self);
158     
159     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
160     ret = poll(ufds, nfds, timeout);
161     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
162
163     return ret;
164 }
165
166 static void * thread_func(void *data) {
167     sw_discovery self = data;
168     sigset_t mask;
169
170     sigfillset(&mask);
171     pthread_sigmask(SIG_BLOCK, &mask, NULL);
172     
173     self->thread = pthread_self();
174     self->thread_running = 1;
175
176     for (;;) {
177         char command;
178
179         if ((command = read_command(self->thread_fd)) < 0)
180             break;
181
182 /*         fprintf(stderr, "Command: %c\n", command); */
183         
184         switch (command) {
185
186             case COMMAND_POLL:
187
188                 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
189                 
190                 if (avahi_simple_poll_run(self->simple_poll) < 0) {
191                     fprintf(stderr, __FILE__": avahi_simple_poll_run() failed.\n");
192                     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
193                     break;
194                 }
195
196                 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
197                 
198                 if (write_command(self->thread_fd, COMMAND_POLL_DONE) < 0)
199                     break;
200                 
201                 break;
202
203             case COMMAND_QUIT:
204                 return NULL;
205         }
206         
207     }
208
209     return NULL;
210 }
211
212 static int oid_alloc(sw_discovery self, oid_type type) {
213     sw_discovery_oid i;
214     assert(self);
215
216     for (i = 0; i < OID_MAX; i++) {
217
218         while (self->oid_index >= OID_MAX)
219             self->oid_index -= OID_MAX;
220         
221         if (self->oid_table[self->oid_index].type == OID_UNUSED) {
222             self->oid_table[self->oid_index].type = type;
223             self->oid_table[self->oid_index].discovery = self;
224
225             assert(OID_GET_INDEX(&self->oid_table[self->oid_index]) == self->oid_index);
226             
227             return self->oid_index ++;
228         }
229
230         self->oid_index ++;
231     }
232
233     /* No free entry found */
234     
235     return (sw_discovery_oid) -1;
236 }
237
238 static void oid_release(sw_discovery self, sw_discovery_oid oid) {
239     assert(self);
240     assert(oid < OID_MAX);
241
242     assert(self->oid_table[oid].type != OID_UNUSED);
243
244     self->oid_table[oid].type = OID_UNUSED;
245     self->oid_table[oid].discovery = NULL;
246     self->oid_table[oid].reply = NULL;
247     self->oid_table[oid].object = NULL;
248     self->oid_table[oid].extra = NULL;
249     self->oid_table[oid].service_data = NULL;
250 }
251
252 static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) {
253     assert(self);
254
255     if (oid >= OID_MAX)
256         return NULL;
257
258     if (self->oid_table[oid].type == OID_UNUSED)
259         return NULL;
260     
261     return &self->oid_table[oid];
262 }
263
264 static service_data* service_data_new(sw_discovery self) {
265     service_data *sdata;
266     
267     assert(self);
268
269     if (!(sdata = avahi_new0(service_data, 1)))
270         return NULL;
271
272     AVAHI_LLIST_PREPEND(service_data, services, self->services, sdata);
273     
274     return sdata;
275     
276 }
277
278 static void service_data_free(sw_discovery self, service_data* sdata) {
279     assert(self);
280     assert(sdata);
281
282     AVAHI_LLIST_REMOVE(service_data, services, self->services, sdata);
283     
284     avahi_free(sdata->name);
285     avahi_free(sdata->regtype);
286     avahi_free(sdata->domain);
287     avahi_free(sdata->host);
288     avahi_string_list_free(sdata->txt);
289     avahi_free(sdata);
290 }
291
292 static void reg_client_callback(oid_data *data, AvahiClientState state);
293
294 static void client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
295     sw_discovery self = userdata;
296     sw_discovery_oid oid;
297     
298     assert(s);
299     assert(self);
300
301     discovery_ref(self);
302     
303     for (oid = 0; oid < OID_MAX; oid++) {
304
305         switch (self->oid_table[oid].type) {
306
307             case OID_ENTRY_GROUP:
308                 reg_client_callback(&self->oid_table[oid], state);
309                 break;
310
311             case OID_DOMAIN_BROWSER:
312             case OID_SERVICE_BROWSER:
313                 ((sw_discovery_browse_reply) self->oid_table[oid].reply)(self, oid, SW_DISCOVERY_BROWSE_INVALID, 0, NULL, NULL, NULL, self->oid_table[oid].extra);
314                 break;
315
316             case OID_SERVICE_RESOLVER:
317             case OID_UNUSED:
318                 ;
319         }
320     }
321
322     discovery_unref(self);
323 }
324
325 sw_result sw_discovery_init(sw_discovery * self) {
326     int fd[2] = { -1, -1};
327     sw_result result = SW_E_UNKNOWN;
328     pthread_mutexattr_t mutex_attr;
329     int error;
330     
331     assert(self);
332     
333     AVAHI_WARN_LINKAGE;
334
335     *self = NULL;
336
337     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
338         goto fail;
339     
340     if (!(*self = avahi_new(struct _sw_discovery, 1))) {
341         result = SW_E_MEM;
342         goto fail;
343     }
344
345     (*self)->n_ref = 1;
346     (*self)->thread_fd = fd[0];
347     (*self)->main_fd = fd[1];
348
349     (*self)->client = NULL;
350     (*self)->simple_poll = NULL;
351
352     memset((*self)->oid_table, 0, sizeof((*self)->oid_table));
353     (*self)->oid_index = 0;
354     
355     (*self)->thread_running = 0;
356
357     AVAHI_LLIST_HEAD_INIT(service_info, (*self)->services);
358
359     ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
360     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
361     ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, &mutex_attr));
362
363     if (!((*self)->simple_poll = avahi_simple_poll_new()))
364         goto fail;
365
366     avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self);
367
368     if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), client_callback, *self, &error))) {
369         result = map_error(error);
370         goto fail;
371     }
372     
373     /* Start simple poll */
374     if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0)
375         goto fail;
376
377     /* Queue an initial POLL command for the thread */
378     if (write_command((*self)->main_fd, COMMAND_POLL) < 0)
379         goto fail;
380     
381     if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0)
382         goto fail;
383
384     (*self)->thread_running = 1;
385     
386     return SW_OKAY;
387
388 fail:
389
390     if (*self)
391         sw_discovery_fina(*self);
392
393     return result;
394 }
395
396 static int stop_thread(sw_discovery self) {
397     assert(self);
398
399     if (!self->thread_running)
400         return 0;
401
402     if (write_command(self->main_fd, COMMAND_QUIT) < 0)
403         return -1;
404     
405     avahi_simple_poll_wakeup(self->simple_poll);
406     
407     ASSERT_SUCCESS(pthread_join(self->thread, NULL));
408     self->thread_running = 0;
409     return 0;
410 }
411
412 static sw_discovery discovery_ref(sw_discovery self) {
413     assert(self);
414     assert(self->n_ref >= 1);
415
416     self->n_ref++;
417
418     return self;
419 }
420
421 static void discovery_unref(sw_discovery self) {
422     assert(self);
423     assert(self->n_ref >= 1);
424
425     if (--self->n_ref > 0)
426         return;
427
428     stop_thread(self);
429
430     if (self->client)
431         avahi_client_free(self->client);
432
433     if (self->simple_poll)
434         avahi_simple_poll_free(self->simple_poll);
435
436     if (self->thread_fd >= 0)
437         close(self->thread_fd);
438
439     if (self->main_fd >= 0)
440         close(self->main_fd);
441
442     ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex));
443
444     while (self->services)
445         service_data_free(self, self->services);
446     
447     avahi_free(self);
448 }
449
450 sw_result sw_discovery_fina(sw_discovery self) {
451     assert(self);
452     
453     AVAHI_WARN_LINKAGE;
454
455     stop_thread(self);
456     discovery_unref(self);
457     
458     return SW_OKAY;
459 }
460
461 sw_result sw_discovery_run(sw_discovery self) {
462     assert(self);
463     
464     AVAHI_WARN_LINKAGE;
465
466     return sw_salt_run((sw_salt) self);
467 }
468
469 sw_result sw_discovery_stop_run(sw_discovery self) {
470     assert(self);
471     
472     AVAHI_WARN_LINKAGE;
473
474     return sw_salt_stop_run((sw_salt) self);
475 }
476
477 int sw_discovery_socket(sw_discovery self) {
478     assert(self);
479     
480     AVAHI_WARN_LINKAGE;
481
482     return self->main_fd;
483 }
484
485 sw_result sw_discovery_read_socket(sw_discovery self) {
486     sw_result result = SW_E_UNKNOWN;
487     
488     assert(self);
489
490     discovery_ref(self);
491
492     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
493     
494     /* Cleanup notification socket */
495     if (read_command(self->main_fd) != COMMAND_POLL_DONE)
496         goto finish;
497     
498     if (avahi_simple_poll_dispatch(self->simple_poll) < 0)
499         goto finish;
500
501     if (self->n_ref > 1) /* Perhaps we should die */
502
503         /* Dispatch events */
504         if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0)
505             goto finish;
506
507     if (self->n_ref > 1)
508
509         /* Request the poll */
510         if (write_command(self->main_fd, COMMAND_POLL) < 0)
511             goto finish;
512     
513     result = SW_OKAY;
514     
515 finish:
516
517     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
518
519     discovery_unref(self);
520     
521     return result;
522 }
523
524 sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) {
525     assert(self);
526     assert(salt);
527     
528     AVAHI_WARN_LINKAGE;
529
530     *salt = (sw_salt) self;
531     
532     return SW_OKAY;
533 }
534
535 sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) {
536     struct pollfd p;
537     int r;
538     sw_result result;
539
540     AVAHI_WARN_LINKAGE;
541
542     if (!((sw_discovery) self)->thread_running)
543         return SW_E_UNKNOWN;
544     
545     memset(&p, 0, sizeof(p));
546     p.fd = ((sw_discovery) self)->main_fd;
547     p.events = POLLIN;
548
549     if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) {
550         
551         /* Don't treat EINTR as error */
552         if (errno == EINTR)
553             return SW_OKAY;
554         
555         return SW_E_UNKNOWN;
556         
557     } else if (r == 0) {
558         
559         /* Timeoout */
560         return SW_OKAY;
561
562     } else {
563         /* Success */
564     
565         if (p.revents != POLLIN)
566             return SW_E_UNKNOWN;
567
568         if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY)
569             return result;
570     }
571     
572     return SW_OKAY;
573 }
574
575 sw_result sw_salt_run(sw_salt self) {
576     sw_result ret;
577     
578     AVAHI_WARN_LINKAGE;
579     
580     for (;;)
581         if ((ret = sw_salt_step(self, NULL)) != SW_OKAY)
582             return ret;
583 }
584
585 sw_result sw_salt_stop_run(sw_salt self) {
586     AVAHI_WARN_LINKAGE;
587
588     if (stop_thread((sw_discovery) self) < 0)
589         return SW_E_UNKNOWN;
590
591     return SW_OKAY;
592 }
593
594 static void reg_report_status(oid_data *data, sw_discovery_publish_status status) {
595     sw_discovery_publish_reply reply;
596
597     assert(data);
598     
599     reply = (sw_discovery_publish_reply) data->reply;
600     
601     reply(data->discovery,
602           OID_GET_INDEX(data),
603           status,
604           data->extra);
605 }
606
607 static int reg_create_service(oid_data *data) {
608     int ret;
609     const char *real_type;
610     
611     assert(data);
612
613     real_type = avahi_get_type_from_subtype(data->service_data->regtype);
614     
615     if ((ret = avahi_entry_group_add_service_strlst(
616              data->object,
617              data->service_data->interface,
618              AVAHI_PROTO_INET,
619              0,
620              data->service_data->name,
621              real_type ? real_type : data->service_data->regtype,
622              data->service_data->domain,
623              data->service_data->host,
624              data->service_data->port,
625              data->service_data->txt)) < 0)
626         return ret;
627     
628     if (real_type) {
629         /* Create a subtype entry */
630
631         if (avahi_entry_group_add_service_subtype(
632                 data->object,
633                 data->service_data->interface,
634                 AVAHI_PROTO_INET,
635                 0,
636                 data->service_data->name,
637                 real_type,
638                 data->service_data->domain,
639                 data->service_data->regtype) < 0)
640             return ret;
641
642     }
643
644     if ((ret = avahi_entry_group_commit(data->object)) < 0)
645         return ret;
646
647     return 0;
648 }
649
650 static void reg_client_callback(oid_data *data, AvahiClientState state) {
651     assert(data);
652
653     /* We've not been setup completely */
654     if (!data->object)
655         return;
656     
657     switch (state) {
658         case AVAHI_CLIENT_DISCONNECTED:
659             reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
660             break;
661         
662         case AVAHI_CLIENT_S_RUNNING: {
663             int ret;
664
665             /* Register the service */
666             if ((ret = reg_create_service(data)) < 0) {
667                 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
668                 return;
669             }
670             
671             break;
672         }
673             
674         case AVAHI_CLIENT_S_COLLISION:
675
676             /* Remove our entry */
677             avahi_entry_group_reset(data->object);
678             break;
679
680         case AVAHI_CLIENT_S_INVALID:
681         case AVAHI_CLIENT_S_REGISTERING:
682             /* Ignore */
683             break;
684     }
685
686 }
687
688 static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
689     oid_data *data = userdata;
690
691     assert(g);
692     assert(data);
693
694     switch (state) {
695         case AVAHI_ENTRY_GROUP_ESTABLISHED:
696
697             reg_report_status(data, SW_DISCOVERY_PUBLISH_STARTED);
698             break;
699
700         case AVAHI_ENTRY_GROUP_COLLISION:
701
702             reg_report_status(data, SW_DISCOVERY_PUBLISH_NAME_COLLISION);
703             break;
704
705         case AVAHI_ENTRY_GROUP_REGISTERING:
706         case AVAHI_ENTRY_GROUP_UNCOMMITED:
707             /* Ignore */
708             break;
709     }
710 }
711
712 sw_result sw_discovery_publish(
713     sw_discovery self,
714     sw_uint32 interface_index,
715     sw_const_string name,
716     sw_const_string type,
717     sw_const_string domain,
718     sw_const_string host,
719     sw_port port,
720     sw_octets text_record,
721     sw_uint32 text_record_len,
722     sw_discovery_publish_reply reply,
723     sw_opaque extra,
724     sw_discovery_oid * oid) {
725
726     oid_data *data;
727     sw_result result = SW_E_UNKNOWN;
728     service_data *sdata;
729     
730     assert(self);
731     assert(name);
732     assert(type);
733     assert(reply);
734     assert(oid);
735     
736     AVAHI_WARN_LINKAGE;
737
738     if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1)
739         return SW_E_UNKNOWN;
740
741     if (!(sdata = service_data_new(self))) {
742         oid_release(self, *oid);
743         return SW_E_MEM;
744     }
745
746     data = oid_get(self, *oid);
747     assert(data);
748     data->reply = (sw_result (*)(void)) reply;
749     data->extra = extra;
750     data->service_data = sdata;
751
752     sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
753     sdata->name = avahi_strdup(name);
754     sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL;
755     sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
756     sdata->host = host ? avahi_normalize_name_strdup(host) : NULL;
757     sdata->port = port;
758     sdata->txt = text_record && text_record_len > 0 ? avahi_string_list_parse(text_record, text_record_len) : NULL;
759
760     /* Some OOM checking would be cool here */
761
762     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
763
764     if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) {
765         result = map_error(avahi_client_errno(self->client));
766         goto finish;
767     }
768
769     if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) {
770         int error;
771         
772         if ((error = reg_create_service(data)) < 0) {
773             result = map_error(error);
774             goto finish;
775         }
776     }
777
778     result = SW_OKAY;
779
780 finish:
781
782     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
783     
784     if (result != SW_OKAY) 
785         if (*oid != (sw_discovery_oid) -1)
786             sw_discovery_cancel(self, *oid);
787
788     return result;
789 }
790
791 static void domain_browser_callback(
792     AvahiDomainBrowser *b,
793     AvahiIfIndex interface,
794     AvahiProtocol protocol,
795     AvahiBrowserEvent event,
796     const char *domain,
797     AvahiLookupResultFlags flags,
798     void *userdata) {
799
800     oid_data* data = userdata;
801     sw_discovery_browse_reply reply;
802     static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
803
804     assert(b);
805     assert(data);
806
807     reply = (sw_discovery_browse_reply) data->reply;
808
809     domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
810
811     switch (event) {
812         case AVAHI_BROWSER_NEW:
813             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra);
814             break;
815
816         case AVAHI_BROWSER_REMOVE:
817             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra);
818             break;
819
820         case AVAHI_BROWSER_FAILURE:
821             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra);
822             break;
823             
824         case AVAHI_BROWSER_CACHE_EXHAUSTED:
825         case AVAHI_BROWSER_ALL_FOR_NOW:
826             break;
827     }
828 }
829
830 sw_result sw_discovery_browse_domains(
831     sw_discovery self,
832     sw_uint32 interface_index,
833     sw_discovery_browse_reply reply,
834     sw_opaque extra,
835     sw_discovery_oid * oid) {
836     
837     oid_data *data;
838     AvahiIfIndex ifindex;
839     sw_result result = SW_E_UNKNOWN;
840     
841     assert(self);
842     assert(reply);
843     assert(oid);
844     
845     AVAHI_WARN_LINKAGE;
846
847     if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1)
848         return SW_E_UNKNOWN;
849
850     data = oid_get(self, *oid);
851     assert(data);
852     data->reply = (sw_result (*)(void)) reply;
853     data->extra = extra;
854     
855     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
856
857     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
858     
859     if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) {
860         result = map_error(avahi_client_errno(self->client));
861         goto finish;
862     }
863
864     result = SW_OKAY;
865
866 finish:
867
868     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
869     
870     if (result != SW_OKAY)
871         if (*oid != (sw_discovery_oid) -1)
872             sw_discovery_cancel(self, *oid);
873
874     return result;
875 }
876
877 static void service_resolver_callback(
878     AvahiServiceResolver *r,
879     AvahiIfIndex interface,
880     AvahiProtocol protocol,
881     AvahiResolverEvent event,
882     const char *name,
883     const char *type,
884     const char *domain,
885     const char *host_name,
886     const AvahiAddress *a,
887     uint16_t port,
888     AvahiStringList *txt,
889     AvahiLookupResultFlags flags,
890     void *userdata) {
891
892     oid_data* data = userdata;
893     sw_discovery_resolve_reply reply;
894     
895     assert(r);
896     assert(data);
897
898     reply = (sw_discovery_resolve_reply) data->reply;
899
900     switch (event) {
901         case AVAHI_RESOLVER_FOUND: {
902
903             char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
904             uint8_t *p = NULL;
905             size_t l = 0;
906             sw_ipv4_address addr;
907
908             sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address);
909
910             host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
911             
912             if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
913                 avahi_string_list_serialize(txt, p, l);
914
915             reply(data->discovery, OID_GET_INDEX(data), interface, name, type, domain, addr, port, p, l, data->extra);
916
917             avahi_free(p);
918             break;
919         }
920
921         case AVAHI_RESOLVER_FAILURE:
922
923             /* Apparently there is no way in HOWL to inform about failed resolvings ... */
924             
925             avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. "
926                        "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. "
927                        "Please fix your application to use the native API of Avahi!",
928                        avahi_exe_name());
929
930             break;
931     }
932 }
933
934 sw_result sw_discovery_resolve(
935     sw_discovery self,
936     sw_uint32 interface_index,
937     sw_const_string name,
938     sw_const_string type,
939     sw_const_string domain,
940     sw_discovery_resolve_reply reply,
941     sw_opaque extra,
942     sw_discovery_oid * oid) {
943
944     oid_data *data;
945     AvahiIfIndex ifindex;
946     sw_result result = SW_E_UNKNOWN;
947     
948     assert(self);
949     assert(name);
950     assert(type);
951     assert(reply);
952     assert(oid);
953     
954     AVAHI_WARN_LINKAGE;
955
956     if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1)
957         return SW_E_UNKNOWN;
958
959     data = oid_get(self, *oid);
960     assert(data);
961     data->reply = (sw_result (*)(void)) reply;
962     data->extra = extra;
963     
964     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
965
966     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
967     
968     if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) {
969         result = map_error(avahi_client_errno(self->client));
970         goto finish;
971     }
972
973     result = SW_OKAY;
974     
975 finish:
976
977     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
978     
979     if (result != SW_OKAY)
980         if (*oid != (sw_discovery_oid) -1)
981             sw_discovery_cancel(self, *oid);
982
983     return result;
984 }
985
986 static void service_browser_callback(
987     AvahiServiceBrowser *b,
988     AvahiIfIndex interface,
989     AvahiProtocol protocol,
990     AvahiBrowserEvent event,
991     const char *name,
992     const char *type,
993     const char *domain,
994     AvahiLookupResultFlags flags,
995     void *userdata) {
996
997     oid_data* data = userdata;
998     char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
999     sw_discovery_browse_reply reply;
1000     
1001     assert(b);
1002     assert(data);
1003
1004     reply = (sw_discovery_browse_reply) data->reply;
1005
1006     type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
1007     domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
1008
1009     switch (event) {
1010         case AVAHI_BROWSER_NEW:
1011             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra);
1012             break;
1013
1014         case AVAHI_BROWSER_REMOVE:
1015             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra);
1016             break;
1017
1018         case AVAHI_BROWSER_FAILURE:
1019             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra);
1020             break;
1021             
1022         case AVAHI_BROWSER_CACHE_EXHAUSTED:
1023         case AVAHI_BROWSER_ALL_FOR_NOW:
1024             break;
1025     }
1026 }
1027
1028 sw_result sw_discovery_browse(
1029     sw_discovery self,
1030     sw_uint32 interface_index,
1031     sw_const_string type,
1032     sw_const_string domain,
1033     sw_discovery_browse_reply reply,
1034     sw_opaque extra,
1035     sw_discovery_oid * oid) {
1036
1037     oid_data *data;
1038     AvahiIfIndex ifindex;
1039     sw_result result = SW_E_UNKNOWN;
1040     
1041     assert(self);
1042     assert(type);
1043     assert(reply);
1044     assert(oid);
1045     
1046     AVAHI_WARN_LINKAGE;
1047
1048     if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1)
1049         return SW_E_UNKNOWN;
1050
1051     data = oid_get(self, *oid);
1052     assert(data);
1053     data->reply = (sw_result (*)(void)) reply;
1054     data->extra = extra;
1055     
1056     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
1057
1058     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
1059     
1060     if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) {
1061         result = map_error(avahi_client_errno(self->client));
1062         goto finish;
1063     }
1064
1065     result = SW_OKAY;
1066     
1067 finish:
1068
1069     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
1070     
1071     if (result != SW_OKAY)
1072         if (*oid != (sw_discovery_oid) -1)
1073             sw_discovery_cancel(self, *oid);
1074
1075     return result;
1076 }
1077
1078 sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) {
1079     oid_data *data;
1080     assert(self);                             
1081
1082     AVAHI_WARN_LINKAGE;
1083
1084     if (!(data = oid_get(self, oid)))
1085         return SW_E_UNKNOWN;
1086
1087     if (data->object) {
1088         switch (data->type) {
1089             case OID_SERVICE_BROWSER:
1090                 avahi_service_browser_free(data->object);
1091                 break;
1092                 
1093             case OID_SERVICE_RESOLVER:
1094                 avahi_service_resolver_free(data->object);
1095                 break;
1096                 
1097             case OID_DOMAIN_BROWSER:
1098                 avahi_domain_browser_free(data->object);
1099                 break;
1100                 
1101             case OID_ENTRY_GROUP:
1102                 avahi_entry_group_free(data->object);
1103                 break;
1104                 
1105             case OID_UNUSED:
1106             ;
1107         }
1108     }
1109
1110     if (data->service_data) {
1111         assert(data->type == OID_ENTRY_GROUP);
1112         service_data_free(self, data->service_data);
1113     }
1114
1115     oid_release(self, oid);
1116     
1117     return SW_OKAY;
1118 }
1119
1120 sw_result sw_discovery_init_with_flags(
1121     sw_discovery * self,
1122     sw_discovery_init_flags flags) {
1123
1124     assert(self);
1125
1126     AVAHI_WARN_LINKAGE;
1127
1128     if (flags != SW_DISCOVERY_USE_SHARED_SERVICE)
1129         return SW_E_NO_IMPL;
1130
1131     return sw_discovery_init(self);
1132 }