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