]> git.ipfire.org Git - thirdparty/shairport-sync.git/blame - mdns_avahi.c
Update RELEASENOTES-DEVELOPMENT.md
[thirdparty/shairport-sync.git] / mdns_avahi.c
CommitLineData
f6eddb84
JL
1/*
2 * Embedded Avahi client. This file is part of Shairport.
3 * Copyright (c) James Laird 2013
4d6f5b48 4 * Additions for metadata and for detecting IPv6 Copyright (c) Mike Brady 2015--2019
f6eddb84
JL
5 * All rights reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person
8 * obtaining a copy of this software and associated documentation
9 * files (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use,
11 * copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
139c18bf 28#include <pthread.h>
d343a851 29#include <stdlib.h>
f0195c5d 30
fe3b70b4
MB
31#include "config.h"
32
ca8ae1fa
MB
33#include "common.h"
34#include "mdns.h"
3cb86f40 35#include "rtsp.h"
69642bb7 36#ifdef CONFIG_DACP_CLIENT
fe3b70b4 37#include "dacp.h"
e0ecdac9 38#endif
064bd293 39#include <string.h>
ca8ae1fa 40
f6eddb84
JL
41#include <avahi-client/client.h>
42#include <avahi-client/publish.h>
f6eddb84 43#include <avahi-common/error.h>
064bd293
MB
44#include <avahi-common/malloc.h>
45#include <avahi-common/thread-watch.h>
f6eddb84 46
f864adf3 47#include <avahi-client/lookup.h>
dace5b05 48#include <avahi-common/alternative.h>
f6eddb84 49
23d86d0a
MB
50void threaded_poll_unlock(void *arg) { avahi_threaded_poll_unlock((AvahiThreadedPoll *)arg); }
51
fd880056
MB
52#define pthread_avahi_threaded_poll_lock_and_push(mu) \
53 avahi_threaded_poll_lock(mu); \
23d86d0a
MB
54 pthread_cleanup_push(threaded_poll_unlock, (void *)mu)
55
de180967
MB
56#define check_avahi_response(debugLevelArg, veryUnLikelyArgumentName) \
57 { \
58 int rc = veryUnLikelyArgumentName; \
59 if (rc) \
60 debug(debugLevelArg, "avahi call response %d at __FILE__, __LINE__)", rc); \
61 }
62
139c18bf 63typedef struct {
d343a851 64 AvahiServiceBrowser *service_browser;
32c7a535 65 char *dacp_id;
139c18bf
MB
66} dacp_browser_struct;
67
72079ada
MB
68dacp_browser_struct private_dbs;
69
3a02b79a 70AvahiStringList *text_record_string_list = NULL;
e1034e11 71AvahiStringList *ap2_text_record_string_list = NULL;
3a02b79a 72
d343a851 73// static AvahiServiceBrowser *sb = NULL;
f6eddb84 74static AvahiClient *client = NULL;
139c18bf 75// static AvahiClient *service_client = NULL;
f6eddb84
JL
76static AvahiEntryGroup *group = NULL;
77static AvahiThreadedPoll *tpoll = NULL;
139c18bf 78// static AvahiThreadedPoll *service_poll = NULL;
f6eddb84 79
139c18bf 80static char *service_name = NULL;
e1034e11
MB
81char *ap2_service_name = NULL;
82
38969090 83static int port = 0;
f6eddb84 84
d343a851
MB
85static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface,
86 AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event,
87 const char *name, const char *type, const char *domain,
8991f342
MB
88 __attribute__((unused)) const char *host_name,
89 __attribute__((unused)) const AvahiAddress *address, uint16_t port,
90 __attribute__((unused)) AvahiStringList *txt,
91 __attribute__((unused)) AvahiLookupResultFlags flags, void *userdata) {
4d6f5b48 92 // debug(1,"resolve_callback, event %d.", event);
d343a851 93 assert(r);
1637a79d 94
32c7a535 95 dacp_browser_struct *dbs = (dacp_browser_struct *)userdata;
d343a851
MB
96
97 /* Called whenever a service has been resolved successfully or timed out */
98 switch (event) {
c2e3fa5a
MB
99 case AVAHI_RESOLVER_FAILURE:
100 debug(2, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s.", name,
101 type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
102 break;
103 case AVAHI_RESOLVER_FOUND: {
104 // char a[AVAHI_ADDRESS_STR_MAX], *t;
4d6f5b48 105 debug(3, "resolve_callback: Service '%s' of type '%s' in domain '%s':", name, type, domain);
72079ada
MB
106 if (dbs->dacp_id) {
107 char *dacpid = strstr(name, "iTunes_Ctrl_");
108 if (dacpid) {
109 dacpid += strlen("iTunes_Ctrl_");
eef6bbf9 110 while (*dacpid == '0')
df7d6df0 111 dacpid++; // skip any leading zeroes
72079ada 112 if (strcmp(dacpid, dbs->dacp_id) == 0) {
4090b2d8 113 debug(3, "resolve_callback: client dacp_id \"%s\" dacp port: %u.", dbs->dacp_id, port);
69642bb7 114#ifdef CONFIG_DACP_CLIENT
72079ada 115 dacp_monitor_port_update_callback(dacpid, port);
c2e3fa5a
MB
116#endif
117#ifdef CONFIG_METADATA
72079ada
MB
118 char portstring[20];
119 memset(portstring, 0, sizeof(portstring));
120 snprintf(portstring, sizeof(portstring), "%u", port);
7258a45b 121 send_ssnc_metadata('dapo', portstring, strlen(portstring), 0);
c2e3fa5a 122#endif
72079ada
MB
123 }
124 } else {
125 debug(1, "Resolve callback: Can't see a DACP string in a DACP Record!");
d343a851 126 }
f864adf3 127 }
d343a851 128 }
c2e3fa5a 129 }
7e0f6acb 130 // debug(1,"service resolver freed by resolve_callback");
4d6f5b48 131 avahi_service_resolver_free(r);
f864adf3 132}
4d6f5b48 133
d343a851
MB
134static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol,
135 AvahiBrowserEvent event, const char *name, const char *type,
136 const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
137 void *userdata) {
4d6f5b48 138 // debug(1,"browse_callback, event %d.", event);
d343a851
MB
139 assert(b);
140 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
141 switch (event) {
142 case AVAHI_BROWSER_FAILURE:
143 warn("avahi: browser failure.",
144 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
145 avahi_threaded_poll_quit(tpoll);
146 break;
147 case AVAHI_BROWSER_NEW:
4d6f5b48
MB
148 // debug(1, "browse_callback: avahi_service_resolver_new for service '%s' of type '%s' in domain
149 // '%s'.", name, type, domain);
d343a851
MB
150 /* We ignore the returned resolver object. In the callback
151 function we free it. If the server is terminated before
152 the callback function is called the server will free
153 the resolver for us. */
4d6f5b48 154 if (!(avahi_service_resolver_new(client, interface, protocol, name, type, domain,
d343a851
MB
155 AVAHI_PROTO_UNSPEC, 0, resolve_callback, userdata)))
156 debug(1, "Failed to resolve service '%s': %s.", name,
4d6f5b48 157 avahi_strerror(avahi_client_errno(client)));
d343a851
MB
158 break;
159 case AVAHI_BROWSER_REMOVE:
4d6f5b48 160 debug(2, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'.", name, type, domain);
69642bb7 161#ifdef CONFIG_DACP_CLIENT
cf70c123 162 dacp_browser_struct *dbs = (dacp_browser_struct *)userdata;
d343a851
MB
163 char *dacpid = strstr(name, "iTunes_Ctrl_");
164 if (dacpid) {
165 dacpid += strlen("iTunes_Ctrl_");
282871ea 166 while (*dacpid == '0')
df7d6df0 167 dacpid++; // skip any leading zeroes
72079ada 168 if ((dbs->dacp_id) && (strcmp(dacpid, dbs->dacp_id) == 0))
32c7a535 169 dacp_monitor_port_update_callback(dbs->dacp_id, 0); // say the port is withdrawn
d343a851
MB
170 } else {
171 debug(1, "Browse callback: Can't see a DACP string in a DACP Record!");
139c18bf 172 }
32c7a535 173#endif
d343a851
MB
174 break;
175 case AVAHI_BROWSER_ALL_FOR_NOW:
176 case AVAHI_BROWSER_CACHE_EXHAUSTED:
177 // debug(1, "(Browser) %s.", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" :
178 // "ALL_FOR_NOW");
179 break;
180 }
181}
139c18bf 182
064bd293 183static void register_service(AvahiClient *c);
49093398 184
87a0475c 185static void egroup_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
f6eddb84 186 AVAHI_GCC_UNUSED void *userdata) {
4d6f5b48 187 // debug(1,"egroup_callback, state %d.", state);
064bd293 188 switch (state) {
c2e3fa5a
MB
189 case AVAHI_ENTRY_GROUP_ESTABLISHED:
190 /* The entry group has been established successfully */
4d6f5b48 191 debug(2, "avahi: service '%s' successfully added.", service_name);
c2e3fa5a 192 break;
064bd293 193
c2e3fa5a
MB
194 case AVAHI_ENTRY_GROUP_COLLISION: {
195 char *n;
064bd293 196
c2e3fa5a
MB
197 /* A service name collision with a remote service
198 * happened. Let's pick a new name */
e1034e11 199 debug(1, "avahi name collision -- look for another");
c2e3fa5a
MB
200 n = avahi_alternative_service_name(service_name);
201 if (service_name)
202 avahi_free(service_name);
203 else
204 debug(1, "avahi attempt to free a NULL service name");
205 service_name = n;
064bd293 206
c2e3fa5a 207 debug(2, "avahi: service name collision, renaming service to '%s'", service_name);
064bd293 208
04e3f891 209 /* And try to recreate the services */
c2e3fa5a
MB
210 register_service(avahi_entry_group_get_client(g));
211 break;
212 }
064bd293 213
c2e3fa5a
MB
214 case AVAHI_ENTRY_GROUP_FAILURE:
215 debug(1, "avahi: entry group failure: %s",
216 avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
217 break;
064bd293 218
c2e3fa5a
MB
219 case AVAHI_ENTRY_GROUP_UNCOMMITED:
220 debug(2, "avahi: service '%s' group is not yet committed.", service_name);
221 break;
222
223 case AVAHI_ENTRY_GROUP_REGISTERING:
224 debug(2, "avahi: service '%s' group is registering.", service_name);
225 break;
226
227 default:
228 debug(1, "avahi: unhandled egroup state: %d", state);
229 break;
064bd293 230 }
f6eddb84
JL
231}
232
04e3f891
MB
233static int deregister_service(AVAHI_GCC_UNUSED AvahiClient *c) {
234 int response = 0;
235 if (group != NULL) {
236 response = avahi_entry_group_free(group);
237 group = NULL;
238 }
239 return response;
240}
241
f6eddb84 242static void register_service(AvahiClient *c) {
87a0475c
MB
243 if (!group)
244 group = avahi_entry_group_new(c, egroup_callback, NULL);
245 if (!group)
4d6f5b48 246 debug(1, "avahi: avahi_entry_group_new failed");
7b3694d9 247 else {
4d6f5b48 248 // debug(2, "register_service -- go ahead and register.");
7b3694d9
MB
249 if (!avahi_entry_group_is_empty(group))
250 return;
251
3a02b79a
MB
252 int ret = 0;
253
2cab35cd 254 AvahiIfIndex selected_interface;
cf29625d 255 if (config.interface != NULL)
2cab35cd
MB
256 selected_interface = config.interface_index;
257 else
258 selected_interface = AVAHI_IF_UNSPEC;
e1034e11 259 if (ap2_text_record_string_list) {
a382ac66 260 ret = avahi_entry_group_add_service_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC, 0,
a109b587
MB
261 ap2_service_name, config.regtype2, NULL, NULL,
262 port, ap2_text_record_string_list);
a382ac66
MB
263 if (ret == AVAHI_ERR_COLLISION) {
264 die("Error: AirPlay 2 name \"%s\" is already in use.", ap2_service_name);
265 }
e1034e11
MB
266 }
267 if ((ret == 0) && (text_record_string_list)) {
3a02b79a
MB
268 ret = avahi_entry_group_add_service_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC, 0,
269 service_name, config.regtype, NULL, NULL, port,
270 text_record_string_list);
a382ac66
MB
271 if (ret == AVAHI_ERR_COLLISION) {
272 die("Error: AirPlay 1 name \"%s\" is already in use.", service_name);
273 }
e1034e11 274 }
a109b587
MB
275 if (ret == 0) {
276 ret = avahi_entry_group_commit(group);
277 debug(2, "avahi: avahi_entry_group_commit %d", ret);
278 if (ret < 0)
279 debug(1, "avahi: avahi_entry_group_commit failed");
280 } else if (ret < 0) {
281 debug(1, "avahi: avahi_entry_group_add_service failed");
282 } else {
283 debug(1, "avahi: unexpected positive return");
284 }
87a0475c 285 }
f6eddb84
JL
286}
287
87a0475c
MB
288static void client_callback(AvahiClient *c, AvahiClientState state,
289 AVAHI_GCC_UNUSED void *userdata) {
4d6f5b48 290 // debug(1,"client_callback, state %d.", state);
9e61da91
JK
291 int err;
292
87a0475c 293 switch (state) {
064bd293
MB
294 case AVAHI_CLIENT_S_REGISTERING:
295 if (group)
de180967 296 check_avahi_response(1, avahi_entry_group_reset(group));
064bd293
MB
297 break;
298
299 case AVAHI_CLIENT_S_RUNNING:
300 register_service(c);
301 break;
302
303 case AVAHI_CLIENT_FAILURE:
9e61da91 304 err = avahi_client_errno(c);
9e61da91 305 if (err == AVAHI_ERR_DISCONNECTED) {
04e3f891
MB
306 debug(1, "avahi client disconnected -- reconnection attempted.");
307 if (c) {
308 // it seems that the avahi_threaded_poll thread is still running and locked here
309 deregister_service(c); // delete the group
310 dacp_browser_struct *dbs = &private_dbs;
311 if (dbs->service_browser) {
312 int rc = avahi_service_browser_free(dbs->service_browser); // delete the service browser
313 if (rc != 0)
314 debug(1,
315 "Error %d freeing the Avahi service browser after the Avahi client has been "
316 "disconnected.",
317 rc);
318 dbs->service_browser = NULL;
319 }
320 avahi_client_free(c); // delete the client
321 }
cf29625d
MB
322 if (!(client = avahi_client_new(avahi_threaded_poll_get(tpoll), AVAHI_CLIENT_NO_FAIL,
323 client_callback, userdata, &err))) {
04e3f891 324 warn("avahi: failed to create a replacement client object: %s", avahi_strerror(err));
9e61da91
JK
325 avahi_threaded_poll_quit(tpoll);
326 }
327 } else {
328 warn("avahi: client failure: %s", avahi_strerror(err));
329 avahi_threaded_poll_quit(tpoll);
330 }
064bd293
MB
331 break;
332
333 case AVAHI_CLIENT_S_COLLISION:
139c18bf 334 debug(2, "avahi: state is AVAHI_CLIENT_S_COLLISION...needs a rename: %s", service_name);
064bd293
MB
335 break;
336
337 case AVAHI_CLIENT_CONNECTING:
338 debug(2, "avahi: received AVAHI_CLIENT_CONNECTING");
339 break;
340
341 default:
342 debug(1, "avahi: unexpected and unhandled avahi client state: %d", state);
343 break;
87a0475c 344 }
f6eddb84
JL
345}
346
4090b2d8 347static int avahi_update(char **txt_records, char **secondary_txt_records) {
2e4156d3
MB
348 // debug(1, "avahi_update.");
349
4090b2d8
MB
350 /*
351 service_name = strdup(ap1name);
352 if (ap2name != NULL)
353 ap2_service_name = strdup(ap2name);
354 port = srvport;
355 */
2e4156d3
MB
356 int err = 0;
357 AvahiIfIndex selected_interface;
4090b2d8
MB
358 if (config.interface != NULL)
359 selected_interface = config.interface_index;
360 else
361 selected_interface = AVAHI_IF_UNSPEC;
23d86d0a 362 pthread_avahi_threaded_poll_lock_and_push(tpoll);
fd880056
MB
363 // avahi_threaded_poll_lock(tpoll);
364 // pthread_cleanup_push(threaded_poll_unlock, (void *)tpoll)
2e4156d3
MB
365
366 if (txt_records != NULL) {
367 if (text_record_string_list)
368 avahi_string_list_free(text_record_string_list);
4090b2d8
MB
369 text_record_string_list = avahi_string_list_new_from_array((const char **)txt_records, -1);
370 err = avahi_entry_group_update_service_txt_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC,
371 0, service_name, config.regtype, NULL,
372 text_record_string_list);
2e4156d3 373 if (err != 0)
4090b2d8 374 debug(1, "avahi_update error updating primary txt records.");
2e4156d3
MB
375 }
376
377 if (secondary_txt_records != NULL) {
378 if (ap2_text_record_string_list)
379 avahi_string_list_free(ap2_text_record_string_list);
380 ap2_text_record_string_list =
4090b2d8
MB
381 avahi_string_list_new_from_array((const char **)secondary_txt_records, -1);
382 err = avahi_entry_group_update_service_txt_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC,
383 0, ap2_service_name, config.regtype2, NULL,
384 ap2_text_record_string_list);
2e4156d3 385 if (err != 0)
4090b2d8 386 debug(1, "avahi_update error updating secondary txt records.");
2e4156d3 387 }
7fde77c4 388
23d86d0a 389 pthread_cleanup_pop(1); // unlock the avahi_threaded_poll_lock
2e4156d3
MB
390 return 0;
391}
392
a109b587
MB
393static int avahi_register(char *ap1name, char *ap2name, int srvport, char **txt_records,
394 char **secondary_txt_records) {
4d6f5b48 395 // debug(1, "avahi_register.");
e1034e11
MB
396 service_name = strdup(ap1name);
397 if (ap2name != NULL)
398 ap2_service_name = strdup(ap2name);
3a02b79a
MB
399
400 text_record_string_list = avahi_string_list_new_from_array((const char **)txt_records, -1);
401
e1034e11 402 if (secondary_txt_records != NULL)
a109b587
MB
403 ap2_text_record_string_list =
404 avahi_string_list_new_from_array((const char **)secondary_txt_records, -1);
e1034e11 405
87a0475c
MB
406 port = srvport;
407
408 int err;
409 if (!(tpoll = avahi_threaded_poll_new())) {
410 warn("couldn't create avahi threaded tpoll!");
411 return -1;
412 }
cf29625d
MB
413 if (!(client = avahi_client_new(avahi_threaded_poll_get(tpoll), AVAHI_CLIENT_NO_FAIL,
414 client_callback, NULL, &err))) {
87a0475c
MB
415 warn("couldn't create avahi client: %s!", avahi_strerror(err));
416 return -1;
417 }
f864adf3 418
87a0475c
MB
419 if (avahi_threaded_poll_start(tpoll) < 0) {
420 warn("couldn't start avahi tpoll thread");
421 return -1;
422 }
423
424 return 0;
f6eddb84
JL
425}
426
38969090 427static void avahi_unregister(void) {
3a02b79a 428 debug(2, "avahi_unregister.");
d805ce91 429 if (tpoll) {
4e1f74db 430 debug(2, "avahi: stop the threaded poll.");
5f750ba9 431 avahi_threaded_poll_stop(tpoll);
de180967 432
d805ce91 433 if (client) {
4e1f74db 434 debug(2, "avahi: free the client.");
d805ce91
MB
435 avahi_client_free(client);
436 client = NULL;
de180967
MB
437 } else {
438 debug(1, "avahi attempting to unregister a NULL client");
439 }
4e1f74db 440 debug(2, "avahi: free the threaded poll.");
d805ce91
MB
441 avahi_threaded_poll_free(tpoll);
442 tpoll = NULL;
f1d45034
MB
443 } else {
444 debug(1, "No avahi threaded poll.");
d805ce91 445 }
87a0475c 446
f1d45034 447 if (service_name) {
4e1f74db 448 debug(2, "avahi: free the service name.");
139c18bf 449 free(service_name);
e1034e11 450 service_name = NULL;
f1d45034 451 } else
de180967 452 debug(1, "avahi attempt to free NULL service name");
3a02b79a 453
e1034e11
MB
454 if (ap2_service_name) {
455 debug(2, "avahi: free the ap2 service_name.");
456 free(ap2_service_name);
457 ap2_service_name = NULL;
458 }
459
3a02b79a
MB
460 if (text_record_string_list) {
461 debug(2, "avahi free text_record_string_list");
462 avahi_string_list_free(text_record_string_list);
e1034e11
MB
463 text_record_string_list = NULL;
464 } else
465 debug(1, "avahi attempt to free NULL text_record_string_list");
466
e1034e11
MB
467 if (ap2_text_record_string_list) {
468 debug(2, "avahi free ap_2text_record_string_list");
469 avahi_string_list_free(ap2_text_record_string_list);
470 ap2_text_record_string_list = NULL;
471 }
139c18bf
MB
472}
473
72079ada 474void avahi_dacp_monitor_start(void) {
4d6f5b48 475 // debug(1, "avahi_dacp_monitor_start.");
72079ada 476 memset((void *)&private_dbs, 0, sizeof(dacp_browser_struct));
4090b2d8 477 debug(2, "avahi_dacp_monitor_start Avahi DACP monitor successfully started");
72079ada 478 return;
139c18bf
MB
479}
480
72079ada 481void avahi_dacp_monitor_set_id(const char *dacp_id) {
4d6f5b48 482 // debug(1, "avahi_dacp_monitor_set_id: Search for DACP ID \"%s\".", t);
72079ada
MB
483 dacp_browser_struct *dbs = &private_dbs;
484
4090b2d8
MB
485 if (((dbs->dacp_id) && (dacp_id) && (strcmp(dbs->dacp_id, dacp_id) == 0)) ||
486 ((dbs->dacp_id == NULL) && (dacp_id == NULL))) {
487 debug(3, "no change...");
488 } else {
489 if (dbs->dacp_id)
490 free(dbs->dacp_id);
491 if (dacp_id == NULL)
492 dbs->dacp_id = NULL;
493 else {
494 char *t = strdup(dacp_id);
495 if (t) {
496 dbs->dacp_id = t;
23d86d0a
MB
497 pthread_avahi_threaded_poll_lock_and_push(tpoll);
498 // avahi_threaded_poll_lock(tpoll);
4090b2d8
MB
499 if (dbs->service_browser)
500 avahi_service_browser_free(dbs->service_browser);
501
502 if (!(dbs->service_browser =
503 avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
504 "_dacp._tcp", NULL, 0, browse_callback, (void *)dbs))) {
505 warn("failed to create avahi service browser: %s\n",
506 avahi_strerror(avahi_client_errno(client)));
507 }
23d86d0a
MB
508 pthread_cleanup_pop(1); // unlock the avahi_threaded_poll_lock
509 // avahi_threaded_poll_unlock(tpoll);
4090b2d8
MB
510 debug(2, "dacp_monitor for \"%s\"", dacp_id);
511 } else {
512 warn("avahi_dacp_set_id: can not allocate a dacp_id string in dacp_browser_struct.");
72079ada 513 }
7e0f6acb 514 }
72079ada
MB
515 }
516}
517
518void avahi_dacp_monitor_stop() {
4d6f5b48 519 // debug(1, "avahi_dacp_monitor_stop");
72079ada
MB
520 dacp_browser_struct *dbs = &private_dbs;
521 // stop and dispose of everything
23d86d0a
MB
522 pthread_avahi_threaded_poll_lock_and_push(tpoll);
523 // avahi_threaded_poll_lock(tpoll);
4d6f5b48
MB
524 if (dbs->service_browser) {
525 avahi_service_browser_free(dbs->service_browser);
526 dbs->service_browser = NULL;
3cb86f40 527 }
23d86d0a
MB
528 pthread_cleanup_pop(1); // unlock the avahi_threaded_poll_lock
529 // avahi_threaded_poll_unlock(tpoll);
72079ada 530 free(dbs->dacp_id);
4e1f74db 531 debug(2, "avahi_dacp_monitor_stop Avahi DACP monitor successfully stopped");
f6eddb84
JL
532}
533
d343a851
MB
534mdns_backend mdns_avahi = {.name = "avahi",
535 .mdns_register = avahi_register,
2e4156d3 536 .mdns_update = avahi_update,
d343a851 537 .mdns_unregister = avahi_unregister,
72079ada
MB
538 .mdns_dacp_monitor_start = avahi_dacp_monitor_start,
539 .mdns_dacp_monitor_set_id = avahi_dacp_monitor_set_id,
540 .mdns_dacp_monitor_stop = avahi_dacp_monitor_stop};