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