]> git.meshlink.io Git - catta/blob - compat-bonjour/compat.c
* add pthreads autoconf scripts
[catta] / compat-bonjour / 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 <pthread.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <signal.h>
33 #include <netinet/in.h>
34
35 #include <avahi-common/simple-watch.h>
36 #include <avahi-common/malloc.h>
37 #include <avahi-common/error.h>
38 #include <avahi-client/client.h>
39
40
41 #include "dns_sd.h"
42
43 enum {
44     COMMAND_POLL = 'P',
45     COMMAND_QUIT = 'Q',
46     COMMAND_POLLED = 'D'
47 };
48
49 struct _DNSServiceRef_t {
50     AvahiSimplePoll *simple_poll;
51
52     int thread_fd, main_fd;
53
54     pthread_t thread;
55     int thread_running;
56
57     pthread_mutex_t mutex;
58
59     void *context;
60     DNSServiceBrowseReply service_browser_callback;
61     DNSServiceResolveReply service_resolver_callback;
62
63     AvahiClient *client;
64     AvahiServiceBrowser *service_browser;
65     AvahiServiceResolver *service_resolver;
66 };
67
68 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
69
70 static int read_command(int fd) {
71     ssize_t r;
72     char command;
73
74     assert(fd >= 0);
75     
76     if ((r = read(fd, &command, 1)) != 1) {
77         fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
78         return -1;
79     }
80
81     return command;
82 }
83
84 static int write_command(int fd, char reply) {
85     assert(fd >= 0);
86
87     if (write(fd, &reply, 1) != 1) {
88         fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
89         return -1;
90     }
91
92     return 0;
93 }
94
95 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
96     DNSServiceRef sdref = userdata;
97     int ret;
98     
99     assert(sdref);
100     
101     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
102
103 /*     fprintf(stderr, "pre-syscall\n"); */
104     ret = poll(ufds, nfds, timeout);
105 /*     fprintf(stderr, "post-syscall\n"); */
106     
107     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
108
109     return ret;
110 }
111
112 static void * thread_func(void *data) {
113     DNSServiceRef sdref = data;
114     sigset_t mask;
115
116     sigfillset(&mask);
117     pthread_sigmask(SIG_BLOCK, &mask, NULL);
118     
119     sdref->thread = pthread_self();
120     sdref->thread_running = 1;
121
122     for (;;) {
123         char command;
124
125         if ((command = read_command(sdref->thread_fd)) < 0)
126             break;
127
128 /*         fprintf(stderr, "Command: %c\n", command); */
129         
130         switch (command) {
131
132             case COMMAND_POLL:
133
134                 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
135                     
136                 
137                 if (avahi_simple_poll_run(sdref->simple_poll) < 0) {
138                     fprintf(stderr, __FILE__": avahi_simple_poll_run() failed.\n");
139                     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
140                     break;
141                 }
142
143                 if (write_command(sdref->thread_fd, COMMAND_POLLED) < 0) {
144                     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
145                     break;
146                 }
147
148                 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
149                 
150                 break;
151
152             case COMMAND_QUIT:
153                 return NULL;
154         }
155         
156     }
157
158     return NULL;
159 }
160
161 static DNSServiceRef sdref_new(void) {
162     int fd[2] = { -1, -1 };
163     DNSServiceRef sdref = NULL;
164
165     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
166         goto fail;
167
168     if (!(sdref = avahi_new(struct _DNSServiceRef_t, 1)))
169         goto fail;
170
171     sdref->thread_fd = fd[0];
172     sdref->main_fd = fd[1];
173
174     sdref->client = NULL;
175     sdref->service_browser = NULL;
176     sdref->service_resolver = NULL;
177
178     pthread_mutex_init(&sdref->mutex, NULL);
179
180     sdref->thread_running = 0;
181
182     if (!(sdref->simple_poll = avahi_simple_poll_new()))
183         goto fail;
184
185     avahi_simple_poll_set_func(sdref->simple_poll, poll_func, sdref);
186
187     /* Start simple poll */
188     if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
189         goto fail;
190
191     /* Queue a initiall POLL command for the thread */
192     if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
193         goto fail;
194     
195     if (pthread_create(&sdref->thread, NULL, thread_func, sdref) != 0)
196         goto fail;
197
198     sdref->thread_running = 1;
199     
200     return sdref;
201
202 fail:
203
204     if (sdref)
205         DNSServiceRefDeallocate(sdref);
206
207     return NULL;
208 }
209
210 int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) {
211     assert(sdRef);
212
213     return sdRef->main_fd;
214 }
215
216 DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdref) {
217     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
218
219     if (pthread_mutex_lock(&sdref->mutex) != 0)
220         return kDNSServiceErr_Unknown;
221     
222     /* Cleanup notification socket */
223     if (read_command(sdref->main_fd) != COMMAND_POLLED)
224         goto finish;
225     
226     if (avahi_simple_poll_dispatch(sdref->simple_poll) < 0)
227         goto finish;
228     
229     if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
230         goto finish;
231
232     /* Request the poll */
233     if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
234         goto finish;
235     
236     ret = kDNSServiceErr_NoError;
237     
238 finish:
239
240     pthread_mutex_unlock(&sdref->mutex);
241     
242     return ret;
243 }
244
245 void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdref) {
246     assert(sdref);
247
248     fprintf(stderr, "deallocating()\n");
249     
250     if (sdref->thread_running) {
251         write_command(sdref->main_fd, COMMAND_QUIT);
252         avahi_simple_poll_wakeup(sdref->simple_poll);
253         pthread_join(sdref->thread, NULL);
254     }
255
256     if (sdref->client)
257         avahi_client_free(sdref->client);
258
259     if (sdref->thread_fd >= 0)
260         close(sdref->thread_fd);
261
262     if (sdref->main_fd >= 0)
263         close(sdref->main_fd);
264
265     if (sdref->simple_poll)
266         avahi_simple_poll_free(sdref->simple_poll);
267
268     pthread_mutex_destroy(&sdref->mutex);
269     
270     avahi_free(sdref);
271 }
272
273 static DNSServiceErrorType map_error(int error) {
274     switch (error) {
275         case AVAHI_OK :
276             return kDNSServiceErr_NoError;
277             
278         case AVAHI_ERR_BAD_STATE :
279             return kDNSServiceErr_BadState;
280             
281         case AVAHI_ERR_INVALID_HOST_NAME:
282         case AVAHI_ERR_INVALID_DOMAIN_NAME:
283         case AVAHI_ERR_INVALID_TTL:
284         case AVAHI_ERR_IS_PATTERN:
285         case AVAHI_ERR_INVALID_RECORD:
286         case AVAHI_ERR_INVALID_SERVICE_NAME:
287         case AVAHI_ERR_INVALID_SERVICE_TYPE:
288         case AVAHI_ERR_INVALID_PORT:
289         case AVAHI_ERR_INVALID_KEY:
290         case AVAHI_ERR_INVALID_ADDRESS:
291             return kDNSServiceErr_BadParam;
292
293
294         case AVAHI_ERR_LOCAL_COLLISION:
295             return kDNSServiceErr_NameConflict;
296
297         case AVAHI_ERR_TOO_MANY_CLIENTS:
298         case AVAHI_ERR_TOO_MANY_OBJECTS:
299         case AVAHI_ERR_TOO_MANY_ENTRIES:
300         case AVAHI_ERR_ACCESS_DENIED:
301             return kDNSServiceErr_Refused;
302
303         case AVAHI_ERR_INVALID_OPERATION:
304         case AVAHI_ERR_INVALID_OBJECT:
305             return kDNSServiceErr_Invalid;
306
307         case AVAHI_ERR_NO_MEMORY:
308             return kDNSServiceErr_NoMemory;
309
310         case AVAHI_ERR_INVALID_INTERFACE:
311         case AVAHI_ERR_INVALID_PROTOCOL:
312             return kDNSServiceErr_BadInterfaceIndex;
313         
314         case AVAHI_ERR_INVALID_FLAGS:
315             return kDNSServiceErr_BadFlags;
316             
317         case AVAHI_ERR_NOT_FOUND:
318             return kDNSServiceErr_NoSuchName;
319             
320         case AVAHI_ERR_VERSION_MISMATCH:
321             return kDNSServiceErr_Incompatible;
322
323         case AVAHI_ERR_NO_NETWORK:
324         case AVAHI_ERR_OS:
325         case AVAHI_ERR_INVALID_CONFIG:
326         case AVAHI_ERR_TIMEOUT:
327         case AVAHI_ERR_DBUS_ERROR:
328         case AVAHI_ERR_NOT_CONNECTED:
329         case AVAHI_ERR_NO_DAEMON:
330             break;
331
332     }
333
334     return kDNSServiceErr_Unknown;
335 }
336
337 static void service_browser_callback(
338     AvahiServiceBrowser *b,
339     AvahiIfIndex interface,
340     AvahiProtocol protocol,
341     AvahiBrowserEvent event,
342     const char *name,
343     const char *type,
344     const char *domain,
345     AvahiLookupResultFlags flags,
346     void *userdata) {
347
348     DNSServiceRef sdref = userdata;
349
350     assert(b);
351     assert(sdref);
352
353     switch (event) {
354         case AVAHI_BROWSER_NEW:
355             sdref->service_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
356             break;
357
358         case AVAHI_BROWSER_REMOVE:
359             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
360             break;
361
362         case AVAHI_BROWSER_FAILURE:
363             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_Unknown, name, type, domain, sdref->context);
364             break;
365             
366         case AVAHI_BROWSER_NOT_FOUND:
367             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoSuchName, name, type, domain, sdref->context);
368             break;
369             
370         case AVAHI_BROWSER_CACHE_EXHAUSTED:
371         case AVAHI_BROWSER_ALL_FOR_NOW:
372             break;
373     }
374 }
375
376 DNSServiceErrorType DNSSD_API DNSServiceBrowse(
377     DNSServiceRef *ret_sdref,
378     DNSServiceFlags flags,
379     uint32_t interface,
380     const char *regtype,
381     const char *domain,
382     DNSServiceBrowseReply callback,
383     void *context) {
384
385     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
386     int error;
387     DNSServiceRef sdref = NULL;
388     AvahiIfIndex ifindex;
389     
390     assert(ret_sdref);
391     assert(regtype);
392     assert(domain);
393     assert(callback);
394
395     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0)
396         return kDNSServiceErr_Unsupported;
397
398     if (!(sdref = sdref_new()))
399         return kDNSServiceErr_Unknown;
400
401     sdref->context = context;
402     sdref->service_browser_callback = callback;
403
404     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
405     
406     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), NULL, NULL, &error))) {
407         ret =  map_error(error);
408         goto finish;
409     }
410
411     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
412     
413     if (!(sdref->service_browser = avahi_service_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, regtype, domain, 0, service_browser_callback, sdref))) {
414         ret = map_error(avahi_client_errno(sdref->client));
415         goto finish;
416     }
417     
418
419     ret = kDNSServiceErr_NoError;
420     *ret_sdref = sdref;
421                                                               
422 finish:
423
424     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
425     
426     if (ret != kDNSServiceErr_NoError)
427         DNSServiceRefDeallocate(sdref);
428
429     return ret;
430 }
431
432 static void service_resolver_callback(
433     AvahiServiceResolver *r,
434     AvahiIfIndex interface,
435     AvahiProtocol protocol,
436     AvahiResolverEvent event,
437     const char *name,
438     const char *type,
439     const char *domain,
440     const char *host_name,
441     const AvahiAddress *a,
442     uint16_t port,
443     AvahiStringList *txt,
444     AvahiLookupResultFlags flags,
445     void *userdata) {
446
447     DNSServiceRef sdref = userdata;
448
449     assert(r);
450     assert(sdref);
451
452     switch (event) {
453         case AVAHI_RESOLVER_FOUND: {
454
455             char *p = NULL;
456             size_t l = 0;
457
458             if ((p = avahi_new0(char, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
459                 avahi_string_list_serialize(txt, p, l);
460             
461             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, "blaa", host_name, htons(port), l, p, sdref->context);
462
463             avahi_free(p);
464             break;
465         }
466
467         case AVAHI_RESOLVER_TIMEOUT:
468         case AVAHI_RESOLVER_NOT_FOUND:
469             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoSuchName, NULL, NULL, 0, 0, NULL, sdref->context);
470             break;
471             
472         case AVAHI_RESOLVER_FAILURE:
473             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_Unknown, NULL, NULL, 0, 0, NULL, sdref->context);
474             
475     }
476 }
477
478
479 DNSServiceErrorType DNSSD_API DNSServiceResolve(
480     DNSServiceRef *ret_sdref,
481     DNSServiceFlags flags,
482     uint32_t interface,
483     const char *name,
484     const char *regtype,
485     const char *domain,
486     DNSServiceResolveReply callback,
487     void *context) {
488
489     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
490     int error;
491     DNSServiceRef sdref = NULL;
492     AvahiIfIndex ifindex;
493
494     assert(ret_sdref);
495     assert(name);
496     assert(regtype);
497     assert(domain);
498     assert(callback);
499
500     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0)
501         return kDNSServiceErr_Unsupported;
502
503     if (!(sdref = sdref_new()))
504         return kDNSServiceErr_Unknown;
505
506     sdref->context = context;
507     sdref->service_resolver_callback = callback;
508
509     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
510     
511     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), NULL, NULL, &error))) {
512         ret =  map_error(error);
513         goto finish;
514     }
515
516     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
517     
518     if (!(sdref->service_resolver = avahi_service_resolver_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, name, regtype, domain, AVAHI_PROTO_UNSPEC, 0, service_resolver_callback, sdref))) {
519         ret = map_error(avahi_client_errno(sdref->client));
520         goto finish;
521     }
522     
523
524     ret = kDNSServiceErr_NoError;
525     *ret_sdref = sdref;
526                                                               
527 finish:
528
529     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
530     
531     if (ret != kDNSServiceErr_NoError)
532         DNSServiceRefDeallocate(sdref);
533
534     return ret;
535 }
536