1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
3 * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
4 * Purpose: Avahi based Zeroconf support
5 * Docs: http://avahi.org/download/doxygen/
15 #include "afp_avahi.h"
17 static void publish_reply(AvahiEntryGroup *g,
18 AvahiEntryGroupState state,
22 * This function tries to register the AFP DNS
25 static void register_stuff(struct context *ctx) {
33 if (!(ctx->group = avahi_entry_group_new(ctx->client,
38 "Failed to create entry group: %s\n",
39 avahi_strerror(avahi_client_errno(ctx->client)));
45 LOG(log_info, logtype_afpd, "Adding service '%s'\n", ctx->name);
47 if (avahi_entry_group_is_empty(ctx->group)) {
48 /* Register our service */
50 if (avahi_entry_group_add_service(ctx->group,
62 "Failed to add service: %s\n",
63 avahi_strerror(avahi_client_errno(ctx->client)));
67 if (avahi_entry_group_commit(ctx->group) < 0) {
70 "Failed to commit entry group: %s\n",
71 avahi_strerror(avahi_client_errno(ctx->client)));
79 avahi_client_free (ctx->client);
80 #ifndef HAVE_AVAHI_THREADED_POLL
81 avahi_simple_poll_quit(ctx->simple_poll);
83 avahi_threaded_poll_quit(ctx->threaded_poll);
87 /* Called when publishing of service data completes */
88 static void publish_reply(AvahiEntryGroup *g,
89 AvahiEntryGroupState state,
90 AVAHI_GCC_UNUSED void *userdata)
92 struct context *ctx = userdata;
94 assert(g == ctx->group);
98 case AVAHI_ENTRY_GROUP_ESTABLISHED :
99 /* The entry group has been established successfully */
102 case AVAHI_ENTRY_GROUP_COLLISION: {
105 /* Pick a new name for our service */
107 n = avahi_alternative_service_name(ctx->name);
110 avahi_free(ctx->name);
117 case AVAHI_ENTRY_GROUP_FAILURE: {
120 "Failed to register service: %s\n",
121 avahi_strerror(avahi_client_errno(ctx->client)));
122 avahi_client_free (avahi_entry_group_get_client(g));
123 #ifndef HAVE_AVAHI_THREADED_POLL
124 avahi_simple_poll_quit(ctx->simple_poll);
126 avahi_threaded_poll_quit(ctx->threaded_poll);
131 case AVAHI_ENTRY_GROUP_UNCOMMITED:
132 case AVAHI_ENTRY_GROUP_REGISTERING:
137 static void client_callback(AvahiClient *client,
138 AvahiClientState state,
141 struct context *ctx = userdata;
143 ctx->client = client;
147 case AVAHI_CLIENT_S_RUNNING:
149 /* The server has startup successfully and registered its host
150 * name on the network, so it's time to create our services */
155 case AVAHI_CLIENT_S_COLLISION:
158 avahi_entry_group_reset(ctx->group);
161 case AVAHI_CLIENT_FAILURE: {
163 if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
166 avahi_client_free(ctx->client);
170 /* Reconnect to the server */
172 #ifndef HAVE_AVAHI_THREADED_POLL
173 if (!(ctx->client = avahi_client_new(avahi_simple_poll_get(ctx->simple_poll),
175 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
177 AVAHI_CLIENT_NO_FAIL,
184 "Failed to contact server: %s\n",
185 avahi_strerror(error));
187 avahi_client_free (ctx->client);
188 #ifndef HAVE_AVAHI_THREADED_POLL
189 avahi_simple_poll_quit(ctx->simple_poll);
191 avahi_threaded_poll_quit(ctx->threaded_poll);
198 "Client failure: %s\n",
199 avahi_strerror(avahi_client_errno(client)));
201 avahi_client_free (ctx->client);
202 #ifndef HAVE_AVAHI_THREADED_POLL
203 avahi_simple_poll_quit(ctx->simple_poll);
205 avahi_threaded_poll_quit(ctx->threaded_poll);
212 case AVAHI_CLIENT_S_REGISTERING:
213 case AVAHI_CLIENT_CONNECTING:
218 static void* thread(void *userdata) {
219 #ifndef HAVE_AVAHI_THREADED_POLL
220 struct context *ctx = userdata;
224 /* Make sure that signals are delivered to the main thread */
226 pthread_sigmask(SIG_BLOCK, &mask, NULL);
228 pthread_mutex_lock(&ctx->mutex);
230 /* Run the main loop */
231 LOG(log_info, logtype_afpd, "Starting avahi loop...");
232 r = avahi_simple_poll_loop(ctx->simple_poll);
234 /* Cleanup some stuff */
236 avahi_client_free(ctx->client);
240 pthread_mutex_unlock(&ctx->mutex);
245 static int poll_func(struct pollfd *ufds,
249 #ifndef HAVE_AVAHI_THREADED_POLL
250 pthread_mutex_t *mutex = userdata;
253 /* Before entering poll() we unlock the mutex, so that
254 * avahi_simple_poll_quit() can succeed from another thread. */
256 pthread_mutex_unlock(mutex);
257 r = poll(ufds, nfds, timeout);
258 pthread_mutex_lock(mutex);
267 * Tries to setup the Zeroconf thread and any
268 * neccessary config setting.
270 void* av_zeroconf_setup(unsigned long port, const char *name) {
271 struct context *ctx = NULL;
273 /* default service name, if there's none in
276 char service[256] = "AFP Server on ";
279 /* initialize the struct that holds our
282 ctx = malloc(sizeof(struct context));
286 #ifndef HAVE_AVAHI_THREADED_POLL
287 ctx->simple_poll = NULL;
288 pthread_mutex_init(&ctx->mutex, NULL);
290 ctx->threaded_poll = NULL;
292 ctx->thread_running = 0;
296 "Setting port for Zeroconf service to: %i.\n",
300 /* Prepare service name */
304 "Assigning default service name.\n");
305 gethostname(service+14, sizeof(service)-15);
306 service[sizeof(service)-1] = 0;
308 ctx->name = strdup(service);
311 ctx->name = strdup(name);
316 /* first of all we need to initialize our threading env */
317 #ifdef HAVE_AVAHI_THREADED_POLL
318 if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
322 if (!(ctx->simple_poll = avahi_simple_poll_new())) {
325 "Failed to create event loop object.\n");
329 avahi_simple_poll_set_func(ctx->simple_poll, poll_func, &ctx->mutex);
332 /* now we need to acquire a client */
333 #ifdef HAVE_AVAHI_THREADED_POLL
334 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
335 AVAHI_CLIENT_NO_FAIL,
341 "Failed to create client object: %s\n",
342 avahi_strerror(avahi_client_errno(ctx->client)));
346 if (!(ctx->client = avahi_client_new(avahi_simple_poll_get(ctx->simple_poll),
347 AVAHI_CLIENT_NO_FAIL,
353 "Failed to create client object: %s\n",
354 avahi_strerror(avahi_client_errno(ctx->client)));
364 av_zeroconf_unregister(ctx);
370 * This function finally runs the loop impl.
372 int av_zeroconf_run(void *u) {
373 struct context *ctx = u;
376 #ifdef HAVE_AVAHI_THREADED_POLL
377 /* Finally, start the event loop thread */
378 if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
381 "Failed to create thread: %s\n",
382 avahi_strerror(avahi_client_errno(ctx->client)));
385 LOG(log_info, logtype_afpd, "Successfully started avahi loop.\n");
388 /* Create the mDNS event handler */
389 if ((ret = pthread_create(&ctx->thread_id, NULL, thread, ctx)) < 0) {
392 "Failed to create thread: %s\n", strerror(ret));
395 LOG(log_info, logtype_afpd, "Successfully started avahi loop.\n");
399 ctx->thread_running = 1;
406 av_zeroconf_unregister(ctx);
412 * Used to lock access to the loop.
415 void av_zeroconf_lock(void *u) {
416 #ifdef HAVE_AVAHI_THREADED_POLL
417 struct context *ctx = u;
419 avahi_threaded_poll_lock(ctx->threaded_poll);
424 * Used to unlock access to the loop.
427 void av_zeroconf_unlock(void *u) {
428 #ifdef HAVE_AVAHI_THREADED_POLL
429 struct context *ctx = u;
431 avahi_threaded_poll_unlock(ctx->threaded_poll);
436 * Tries to shutdown this loop impl.
437 * Call this function from outside this thread.
439 void av_zeroconf_shutdown(void *u) {
440 struct context *ctx = u;
442 /* Call this when the app shuts down */
443 #ifdef HAVE_AVAHI_THREADED_POLL
444 avahi_threaded_poll_stop(ctx->threaded_poll);
445 avahi_free(ctx->name);
446 avahi_client_free(ctx->client);
447 avahi_threaded_poll_free(ctx->threaded_poll);
449 av_zeroconf_unregister(ctx);
454 * Tries to shutdown this loop impl.
455 * Call this function from inside this thread.
457 int av_zeroconf_unregister(void *u) {
458 struct context *ctx = u;
460 if (ctx->thread_running) {
461 #ifndef HAVE_AVAHI_THREADED_POLL
462 pthread_mutex_lock(&ctx->mutex);
463 avahi_simple_poll_quit(ctx->simple_poll);
464 pthread_mutex_unlock(&ctx->mutex);
466 pthread_join(ctx->thread_id, NULL);
468 /* First, block the event loop */
469 avahi_threaded_poll_lock(ctx->threaded_poll);
471 /* Than, do your stuff */
472 avahi_threaded_poll_quit(ctx->threaded_poll);
474 /* Finally, unblock the event loop */
475 avahi_threaded_poll_unlock(ctx->threaded_poll);
477 ctx->thread_running = 0;
480 avahi_free(ctx->name);
483 avahi_client_free(ctx->client);
485 #ifndef HAVE_AVAHI_THREADED_POLL
486 if (ctx->simple_poll)
487 avahi_simple_poll_free(ctx->simple_poll);
489 pthread_mutex_destroy(&ctx->mutex);
491 if (ctx->threaded_poll)
492 avahi_threaded_poll_free(ctx->threaded_poll);
500 #endif /* USE_AVAHI */