]> git.meshlink.io Git - catta/blob - avahi-compat-howl/compat.c
* fix compat-howl to work with nautilus
[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;
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
374     if (!((*self)->simple_poll = avahi_simple_poll_new()))
375         goto fail;
376
377     avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self);
378
379     if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), client_callback, *self, &error))) {
380         result = map_error(error);
381         goto fail;
382     }
383     
384     /* Start simple poll */
385     if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0)
386         goto fail;
387
388     /* Queue an initial POLL command for the thread */
389     if (write_command((*self)->main_fd, COMMAND_POLL) < 0)
390         goto fail;
391     
392     if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0)
393         goto fail;
394
395     (*self)->thread_running = 1;
396     
397     return SW_OKAY;
398
399 fail:
400
401     if (*self)
402         sw_discovery_fina(*self);
403
404     return result;
405 }
406
407 static int stop_thread(sw_discovery self) {
408     assert(self);
409
410     if (!self->thread_running)
411         return 0;
412
413     if (write_command(self->main_fd, COMMAND_QUIT) < 0)
414         return -1;
415     
416     avahi_simple_poll_wakeup(self->simple_poll);
417     
418     ASSERT_SUCCESS(pthread_join(self->thread, NULL));
419     self->thread_running = 0;
420     return 0;
421 }
422
423 static sw_discovery discovery_ref(sw_discovery self) {
424     assert(self);
425     assert(self->n_ref >= 1);
426
427     self->n_ref++;
428
429     return self;
430 }
431
432 static void discovery_unref(sw_discovery self) {
433     assert(self);
434     assert(self->n_ref >= 1);
435
436     if (--self->n_ref > 0)
437         return;
438
439     stop_thread(self);
440
441     if (self->client)
442         avahi_client_free(self->client);
443
444     if (self->simple_poll)
445         avahi_simple_poll_free(self->simple_poll);
446
447     if (self->thread_fd >= 0)
448         close(self->thread_fd);
449
450     if (self->main_fd >= 0)
451         close(self->main_fd);
452
453     ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex));
454
455     while (self->services)
456         service_data_free(self, self->services);
457     
458     avahi_free(self);
459 }
460
461 sw_result sw_discovery_fina(sw_discovery self) {
462     assert(self);
463     
464     AVAHI_WARN_LINKAGE;
465
466     stop_thread(self);
467     discovery_unref(self);
468     
469     return SW_OKAY;
470 }
471
472 sw_result sw_discovery_run(sw_discovery self) {
473     assert(self);
474     
475     AVAHI_WARN_LINKAGE;
476
477     return sw_salt_run((sw_salt) self);
478 }
479
480 sw_result sw_discovery_stop_run(sw_discovery self) {
481     assert(self);
482     
483     AVAHI_WARN_LINKAGE;
484
485     return sw_salt_stop_run((sw_salt) self);
486 }
487
488 int sw_discovery_socket(sw_discovery self) {
489     assert(self);
490     
491     AVAHI_WARN_LINKAGE;
492
493     return self->main_fd;
494 }
495
496 sw_result sw_discovery_read_socket(sw_discovery self) {
497     sw_result result = SW_E_UNKNOWN;
498     
499     assert(self);
500
501     discovery_ref(self);
502
503     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
504     
505     /* Cleanup notification socket */
506     if (read_command(self->main_fd) != COMMAND_POLL_DONE)
507         goto finish;
508     
509     if (avahi_simple_poll_dispatch(self->simple_poll) < 0)
510         goto finish;
511
512     if (self->n_ref > 1) /* Perhaps we should die */
513
514         /* Dispatch events */
515         if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0)
516             goto finish;
517
518     if (self->n_ref > 1)
519
520         /* Request the poll */
521         if (write_command(self->main_fd, COMMAND_POLL) < 0)
522             goto finish;
523     
524     result = SW_OKAY;
525     
526 finish:
527
528     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
529
530     discovery_unref(self);
531     
532     return result;
533 }
534
535 sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) {
536     assert(self);
537     assert(salt);
538     
539     AVAHI_WARN_LINKAGE;
540
541     *salt = (sw_salt) self;
542     
543     return SW_OKAY;
544 }
545
546 sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) {
547     struct pollfd p;
548     int r;
549     sw_result result;
550
551     AVAHI_WARN_LINKAGE;
552
553     if (!((sw_discovery) self)->thread_running)
554         return SW_E_UNKNOWN;
555     
556     memset(&p, 0, sizeof(p));
557     p.fd = ((sw_discovery) self)->main_fd;
558     p.events = POLLIN;
559
560     if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) {
561         
562         /* Don't treat EINTR as error */
563         if (errno == EINTR)
564             return SW_OKAY;
565         
566         return SW_E_UNKNOWN;
567         
568     } else if (r == 0) {
569         
570         /* Timeoout */
571         return SW_OKAY;
572
573     } else {
574         /* Success */
575     
576         if (p.revents != POLLIN)
577             return SW_E_UNKNOWN;
578
579         if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY)
580             return result;
581     }
582     
583     return SW_OKAY;
584 }
585
586 sw_result sw_salt_run(sw_salt self) {
587     sw_result ret;
588
589     AVAHI_WARN_LINKAGE;
590
591     assert(self);
592     
593     for (;;)
594         if ((ret = sw_salt_step(self, NULL)) != SW_OKAY)
595             return ret;
596 }
597
598 sw_result sw_salt_stop_run(sw_salt self) {
599     AVAHI_WARN_LINKAGE;
600
601     assert(self);
602
603     if (stop_thread((sw_discovery) self) < 0)
604         return SW_E_UNKNOWN;
605
606     return SW_OKAY;
607 }
608
609 sw_result sw_salt_lock(sw_salt self) {
610     AVAHI_WARN_LINKAGE;
611
612     assert(self);
613     ASSERT_SUCCESS(pthread_mutex_lock(&((sw_discovery) self)->mutex));
614
615     return SW_OKAY;
616 }
617
618 sw_result sw_salt_unlock(sw_salt self) {
619     assert(self);
620     
621     AVAHI_WARN_LINKAGE;
622
623     ASSERT_SUCCESS(pthread_mutex_unlock(&((sw_discovery) self)->mutex));
624
625     return SW_OKAY;
626 }
627
628 static void reg_report_status(oid_data *data, sw_discovery_publish_status status) {
629     sw_discovery_publish_reply reply;
630
631     assert(data);
632     
633     reply = (sw_discovery_publish_reply) data->reply;
634     
635     reply(data->discovery,
636           OID_GET_INDEX(data),
637           status,
638           data->extra);
639 }
640
641 static int reg_create_service(oid_data *data) {
642     int ret;
643     const char *real_type;
644     
645     assert(data);
646
647     real_type = avahi_get_type_from_subtype(data->service_data->regtype);
648     
649     if ((ret = avahi_entry_group_add_service_strlst(
650              data->object,
651              data->service_data->interface,
652              AVAHI_PROTO_INET,
653              0,
654              data->service_data->name,
655              real_type ? real_type : data->service_data->regtype,
656              data->service_data->domain,
657              data->service_data->host,
658              data->service_data->port,
659              data->service_data->txt)) < 0)
660         return ret;
661     
662     if (real_type) {
663         /* Create a subtype entry */
664
665         if (avahi_entry_group_add_service_subtype(
666                 data->object,
667                 data->service_data->interface,
668                 AVAHI_PROTO_INET,
669                 0,
670                 data->service_data->name,
671                 real_type,
672                 data->service_data->domain,
673                 data->service_data->regtype) < 0)
674             return ret;
675
676     }
677
678     if ((ret = avahi_entry_group_commit(data->object)) < 0)
679         return ret;
680
681     return 0;
682 }
683
684 static void reg_client_callback(oid_data *data, AvahiClientState state) {
685     assert(data);
686
687     /* We've not been setup completely */
688     if (!data->object)
689         return;
690     
691     switch (state) {
692         case AVAHI_CLIENT_DISCONNECTED:
693             reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
694             break;
695         
696         case AVAHI_CLIENT_S_RUNNING: {
697             int ret;
698
699             /* Register the service */
700             if ((ret = reg_create_service(data)) < 0) {
701                 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
702                 return;
703             }
704             
705             break;
706         }
707             
708         case AVAHI_CLIENT_S_COLLISION:
709
710             /* Remove our entry */
711             avahi_entry_group_reset(data->object);
712             break;
713
714         case AVAHI_CLIENT_S_INVALID:
715         case AVAHI_CLIENT_S_REGISTERING:
716             /* Ignore */
717             break;
718     }
719
720 }
721
722 static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
723     oid_data *data = userdata;
724
725     assert(g);
726     assert(data);
727
728     switch (state) {
729         case AVAHI_ENTRY_GROUP_ESTABLISHED:
730
731             reg_report_status(data, SW_DISCOVERY_PUBLISH_STARTED);
732             break;
733
734         case AVAHI_ENTRY_GROUP_COLLISION:
735
736             reg_report_status(data, SW_DISCOVERY_PUBLISH_NAME_COLLISION);
737             break;
738
739         case AVAHI_ENTRY_GROUP_REGISTERING:
740         case AVAHI_ENTRY_GROUP_UNCOMMITED:
741             /* Ignore */
742             break;
743     }
744 }
745
746 sw_result sw_discovery_publish(
747     sw_discovery self,
748     sw_uint32 interface_index,
749     sw_const_string name,
750     sw_const_string type,
751     sw_const_string domain,
752     sw_const_string host,
753     sw_port port,
754     sw_octets text_record,
755     sw_uint32 text_record_len,
756     sw_discovery_publish_reply reply,
757     sw_opaque extra,
758     sw_discovery_oid * oid) {
759
760     oid_data *data;
761     sw_result result = SW_E_UNKNOWN;
762     service_data *sdata;
763     
764     assert(self);
765     assert(name);
766     assert(type);
767     assert(reply);
768     assert(oid);
769     
770     AVAHI_WARN_LINKAGE;
771
772     if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1)
773         return SW_E_UNKNOWN;
774
775     if (!(sdata = service_data_new(self))) {
776         oid_release(self, *oid);
777         return SW_E_MEM;
778     }
779
780     data = oid_get(self, *oid);
781     assert(data);
782     data->reply = (sw_result (*)(void)) reply;
783     data->extra = extra;
784     data->service_data = sdata;
785
786     sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
787     sdata->name = avahi_strdup(name);
788     sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL;
789     sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
790     sdata->host = host ? avahi_normalize_name_strdup(host) : NULL;
791     sdata->port = port;
792     sdata->txt = text_record && text_record_len > 0 ? avahi_string_list_parse(text_record, text_record_len) : NULL;
793
794     /* Some OOM checking would be cool here */
795
796     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
797
798     if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) {
799         result = map_error(avahi_client_errno(self->client));
800         goto finish;
801     }
802
803     if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) {
804         int error;
805         
806         if ((error = reg_create_service(data)) < 0) {
807             result = map_error(error);
808             goto finish;
809         }
810     }
811
812     result = SW_OKAY;
813
814 finish:
815
816     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
817     
818     if (result != SW_OKAY) 
819         if (*oid != (sw_discovery_oid) -1)
820             sw_discovery_cancel(self, *oid);
821
822     return result;
823 }
824
825 static void domain_browser_callback(
826     AvahiDomainBrowser *b,
827     AvahiIfIndex interface,
828     AvahiProtocol protocol,
829     AvahiBrowserEvent event,
830     const char *domain,
831     AvahiLookupResultFlags flags,
832     void *userdata) {
833
834     oid_data* data = userdata;
835     sw_discovery_browse_reply reply;
836     static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
837
838     assert(b);
839     assert(data);
840
841     reply = (sw_discovery_browse_reply) data->reply;
842
843     domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
844
845     switch (event) {
846         case AVAHI_BROWSER_NEW:
847             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra);
848             break;
849
850         case AVAHI_BROWSER_REMOVE:
851             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra);
852             break;
853
854         case AVAHI_BROWSER_FAILURE:
855             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra);
856             break;
857             
858         case AVAHI_BROWSER_CACHE_EXHAUSTED:
859         case AVAHI_BROWSER_ALL_FOR_NOW:
860             break;
861     }
862 }
863
864 sw_result sw_discovery_browse_domains(
865     sw_discovery self,
866     sw_uint32 interface_index,
867     sw_discovery_browse_reply reply,
868     sw_opaque extra,
869     sw_discovery_oid * oid) {
870     
871     oid_data *data;
872     AvahiIfIndex ifindex;
873     sw_result result = SW_E_UNKNOWN;
874     
875     assert(self);
876     assert(reply);
877     assert(oid);
878     
879     AVAHI_WARN_LINKAGE;
880
881     if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1)
882         return SW_E_UNKNOWN;
883
884     data = oid_get(self, *oid);
885     assert(data);
886     data->reply = (sw_result (*)(void)) reply;
887     data->extra = extra;
888     
889     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
890
891     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
892     
893     if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) {
894         result = map_error(avahi_client_errno(self->client));
895         goto finish;
896     }
897
898     result = SW_OKAY;
899
900 finish:
901
902     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
903     
904     if (result != SW_OKAY)
905         if (*oid != (sw_discovery_oid) -1)
906             sw_discovery_cancel(self, *oid);
907
908     return result;
909 }
910
911 static void service_resolver_callback(
912     AvahiServiceResolver *r,
913     AvahiIfIndex interface,
914     AvahiProtocol protocol,
915     AvahiResolverEvent event,
916     const char *name,
917     const char *type,
918     const char *domain,
919     const char *host_name,
920     const AvahiAddress *a,
921     uint16_t port,
922     AvahiStringList *txt,
923     AvahiLookupResultFlags flags,
924     void *userdata) {
925
926     oid_data* data = userdata;
927     sw_discovery_resolve_reply reply;
928     
929     assert(r);
930     assert(data);
931
932     reply = (sw_discovery_resolve_reply) data->reply;
933
934     switch (event) {
935         case AVAHI_RESOLVER_FOUND: {
936
937             char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
938             uint8_t *p = NULL;
939             size_t l = 0;
940             sw_ipv4_address addr;
941
942             sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address);
943
944             host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
945             
946             if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
947                 avahi_string_list_serialize(txt, p, l);
948
949             reply(data->discovery, OID_GET_INDEX(data), interface, name, type, domain, addr, port, p, l, data->extra);
950
951             avahi_free(p);
952             break;
953         }
954
955         case AVAHI_RESOLVER_FAILURE:
956
957             /* Apparently there is no way in HOWL to inform about failed resolvings ... */
958             
959             avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. "
960                        "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. "
961                        "Please fix your application to use the native API of Avahi!",
962                        avahi_exe_name());
963
964             break;
965     }
966 }
967
968 sw_result sw_discovery_resolve(
969     sw_discovery self,
970     sw_uint32 interface_index,
971     sw_const_string name,
972     sw_const_string type,
973     sw_const_string domain,
974     sw_discovery_resolve_reply reply,
975     sw_opaque extra,
976     sw_discovery_oid * oid) {
977
978     oid_data *data;
979     AvahiIfIndex ifindex;
980     sw_result result = SW_E_UNKNOWN;
981     
982     assert(self);
983     assert(name);
984     assert(type);
985     assert(reply);
986     assert(oid);
987     
988     AVAHI_WARN_LINKAGE;
989
990     if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1)
991         return SW_E_UNKNOWN;
992
993     data = oid_get(self, *oid);
994     assert(data);
995     data->reply = (sw_result (*)(void)) reply;
996     data->extra = extra;
997     
998     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
999
1000     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
1001     
1002     if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) {
1003         result = map_error(avahi_client_errno(self->client));
1004         goto finish;
1005     }
1006
1007     result = SW_OKAY;
1008     
1009 finish:
1010
1011     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
1012     
1013     if (result != SW_OKAY)
1014         if (*oid != (sw_discovery_oid) -1)
1015             sw_discovery_cancel(self, *oid);
1016
1017     return result;
1018 }
1019
1020 static void service_browser_callback(
1021     AvahiServiceBrowser *b,
1022     AvahiIfIndex interface,
1023     AvahiProtocol protocol,
1024     AvahiBrowserEvent event,
1025     const char *name,
1026     const char *type,
1027     const char *domain,
1028     AvahiLookupResultFlags flags,
1029     void *userdata) {
1030
1031     oid_data* data = userdata;
1032     char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
1033     sw_discovery_browse_reply reply;
1034     
1035     assert(b);
1036     assert(data);
1037
1038     reply = (sw_discovery_browse_reply) data->reply;
1039
1040     type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
1041     domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
1042
1043     switch (event) {
1044         case AVAHI_BROWSER_NEW:
1045             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra);
1046             break;
1047
1048         case AVAHI_BROWSER_REMOVE:
1049             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra);
1050             break;
1051
1052         case AVAHI_BROWSER_FAILURE:
1053             reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra);
1054             break;
1055             
1056         case AVAHI_BROWSER_CACHE_EXHAUSTED:
1057         case AVAHI_BROWSER_ALL_FOR_NOW:
1058             break;
1059     }
1060 }
1061
1062 sw_result sw_discovery_browse(
1063     sw_discovery self,
1064     sw_uint32 interface_index,
1065     sw_const_string type,
1066     sw_const_string domain,
1067     sw_discovery_browse_reply reply,
1068     sw_opaque extra,
1069     sw_discovery_oid * oid) {
1070
1071     oid_data *data;
1072     AvahiIfIndex ifindex;
1073     sw_result result = SW_E_UNKNOWN;
1074     
1075     assert(self);
1076     assert(type);
1077     assert(reply);
1078     assert(oid);
1079     
1080     AVAHI_WARN_LINKAGE;
1081
1082     if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1)
1083         return SW_E_UNKNOWN;
1084
1085     data = oid_get(self, *oid);
1086     assert(data);
1087     data->reply = (sw_result (*)(void)) reply;
1088     data->extra = extra;
1089     
1090     ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
1091
1092     ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
1093     
1094     if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) {
1095         result = map_error(avahi_client_errno(self->client));
1096         goto finish;
1097     }
1098
1099     result = SW_OKAY;
1100     
1101 finish:
1102
1103     ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
1104     
1105     if (result != SW_OKAY)
1106         if (*oid != (sw_discovery_oid) -1)
1107             sw_discovery_cancel(self, *oid);
1108
1109     return result;
1110 }
1111
1112 sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) {
1113     oid_data *data;
1114     assert(self);                             
1115
1116     AVAHI_WARN_LINKAGE;
1117
1118     if (!(data = oid_get(self, oid)))
1119         return SW_E_UNKNOWN;
1120
1121     if (data->object) {
1122         switch (data->type) {
1123             case OID_SERVICE_BROWSER:
1124                 avahi_service_browser_free(data->object);
1125                 break;
1126                 
1127             case OID_SERVICE_RESOLVER:
1128                 avahi_service_resolver_free(data->object);
1129                 break;
1130                 
1131             case OID_DOMAIN_BROWSER:
1132                 avahi_domain_browser_free(data->object);
1133                 break;
1134                 
1135             case OID_ENTRY_GROUP:
1136                 avahi_entry_group_free(data->object);
1137                 break;
1138                 
1139             case OID_UNUSED:
1140             ;
1141         }
1142     }
1143
1144     if (data->service_data) {
1145         assert(data->type == OID_ENTRY_GROUP);
1146         service_data_free(self, data->service_data);
1147     }
1148
1149     oid_release(self, oid);
1150     
1151     return SW_OKAY;
1152 }
1153
1154 sw_result sw_discovery_init_with_flags(
1155     sw_discovery * self,
1156     sw_discovery_init_flags flags) {
1157
1158     assert(self);
1159
1160     AVAHI_WARN_LINKAGE;
1161
1162     if (flags != SW_DISCOVERY_USE_SHARED_SERVICE)
1163         return SW_E_NO_IMPL;
1164
1165     return sw_discovery_init(self);
1166 }