]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/afp_avahi.c
Merge master
[netatalk.git] / etc / afpd / afp_avahi.c
index 5f6dfb2da7281df5fdddaec0fd0913922dd4d41b..df685e4a0b2df3bb8246a80ddcde156591708306 100644 (file)
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
 /*
  * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
  * Purpose: Avahi based Zeroconf support
 
 #ifdef HAVE_AVAHI
 
+#include <unistd.h>
+
+#include <avahi-common/strlst.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+
 #include "afp_avahi.h"
+#include "afp_config.h"
+#include "volume.h"
+
+/*****************************************************************
+ * Global variables
+ *****************************************************************/
+struct context *ctx = NULL;
+
+/*****************************************************************
+ * Private functions
+ *****************************************************************/
 
 static void publish_reply(AvahiEntryGroup *g,
                           AvahiEntryGroupState state,
@@ -22,66 +41,143 @@ static void publish_reply(AvahiEntryGroup *g,
  * This function tries to register the AFP DNS
  * SRV service type.
  */
-static void register_stuff(struct context *ctx) {
-  char r[128];
-  int ret;
-
-  assert(ctx->client);
-
-  if (!ctx->group) {
-
-    if (!(ctx->group = avahi_entry_group_new(ctx->client,
-                                             publish_reply,
-                                             ctx))) {
-      LOG(log_error,
-          logtype_afpd,
-          "Failed to create entry group: %s\n",
-          avahi_strerror(avahi_client_errno(ctx->client)));
-      goto fail;
+static void register_stuff(void) {
+    uint port;
+    const AFPConfig *config;
+    const struct vol *volume;
+    DSI *dsi;
+    char name[MAXINSTANCENAMELEN+1];
+    AvahiStringList *strlist = NULL;
+    AvahiStringList *strlist2 = NULL;
+    char tmpname[256];
+
+    assert(ctx->client);
+
+    if (!ctx->group) {
+        if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
+            LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
+                avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
     }
 
-  }
-
-  LOG(log_info, logtype_afpd, "Adding service '%s'\n", ctx->name);
-
-  if (avahi_entry_group_is_empty(ctx->group)) {
-    /* Register our service */
-
-    if (avahi_entry_group_add_service(ctx->group,
-                                      AVAHI_IF_UNSPEC,
-                                      AVAHI_PROTO_UNSPEC,
-                                      0,
-                                      ctx->name,
-                                      AFP_DNS_SERVICE_TYPE,
-                                      NULL,
-                                      NULL,
-                                      ctx->port,
-                                      NULL) < 0) {
-      LOG(log_error,
-          logtype_afpd,
-          "Failed to add service: %s\n",
-          avahi_strerror(avahi_client_errno(ctx->client)));
-      goto fail;
-    }
+    if (avahi_entry_group_is_empty(ctx->group)) {
+        /* Register our service */
+
+        /* Build AFP volumes list */
+        int i = 0;
+        strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
+               
+        for (volume = getvolumes(); volume; volume = volume->v_next) {
+
+            if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0) {
+                LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
+                goto fail;
+            }
+
+            if (volume->v_flags & AFPVOL_TM) {
+                if (volume->v_uuid) {
+                    LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
+                        volume->v_localname, volume->v_uuid);
+                    strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
+                                                           i++, tmpname, volume->v_uuid);
+                } else {
+                    LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
+                        volume->v_localname);
+                    strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
+                                                           i++, tmpname);
+                }      
+            }
+        }
+
+        /* AFP server */
+        for (config = ctx->configs; config; config = config->next) {
+
+            dsi = (DSI *)config->obj.handle;
+            port = getip_port((struct sockaddr *)&dsi->server);
+
+            if (convert_string(config->obj.options.unixcharset,
+                               CH_UTF8,
+                               config->obj.options.server ?
+                               config->obj.options.server :
+                               config->obj.options.hostname,
+                               -1,
+                               name,
+                               MAXINSTANCENAMELEN) <= 0) {
+                LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
+                goto fail;
+            }
+            if ((dsi->bonjourname = strdup(name)) == NULL) {
+                LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
+                goto fail;
+
+            }
+            LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
+                dsi->bonjourname);
+
+            if (avahi_entry_group_add_service(ctx->group,
+                                              AVAHI_IF_UNSPEC,
+                                              AVAHI_PROTO_UNSPEC,
+                                              0,
+                                              dsi->bonjourname,
+                                              AFP_DNS_SERVICE_TYPE,
+                                              NULL,
+                                              NULL,
+                                              port,
+                                              NULL) < 0) {
+                LOG(log_error, logtype_afpd, "Failed to add service: %s",
+                    avahi_strerror(avahi_client_errno(ctx->client)));
+                goto fail;
+            }
+
+            if (i && avahi_entry_group_add_service_strlst(ctx->group,
+                                                          AVAHI_IF_UNSPEC,
+                                                          AVAHI_PROTO_UNSPEC,
+                                                          0,
+                                                          dsi->bonjourname,
+                                                          ADISK_SERVICE_TYPE,
+                                                          NULL,
+                                                          NULL,
+                                                          9, /* discard */
+                                                          strlist) < 0) {
+                LOG(log_error, logtype_afpd, "Failed to add service: %s",
+                    avahi_strerror(avahi_client_errno(ctx->client)));
+                goto fail;
+            }  /* if */
+
+            if (config->obj.options.mimicmodel) {
+                strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", config->obj.options.mimicmodel);
+                if (avahi_entry_group_add_service_strlst(ctx->group,
+                                                         AVAHI_IF_UNSPEC,
+                                                         AVAHI_PROTO_UNSPEC,
+                                                         0,
+                                                         dsi->bonjourname,
+                                                         DEV_INFO_SERVICE_TYPE,
+                                                         NULL,
+                                                         NULL,
+                                                         0,
+                                                         strlist2) < 0) {
+                    LOG(log_error, logtype_afpd, "Failed to add service: %s",
+                        avahi_strerror(avahi_client_errno(ctx->client)));
+                    goto fail;
+                }
+            } /* if (config->obj.options.mimicmodel) */
+
+        }      /* for config*/
+
+        if (avahi_entry_group_commit(ctx->group) < 0) {
+            LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
+                avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+
+    }  /* if avahi_entry_group_is_empty*/
+
+    return;
 
-    if (avahi_entry_group_commit(ctx->group) < 0) {
-      LOG(log_error,
-          logtype_afpd,
-          "Failed to commit entry group: %s\n",
-          avahi_strerror(avahi_client_errno(ctx->client)));
-      goto fail;
-    }
-  }
-
-  return;
-
-  fail:
-    avahi_client_free (ctx->client);
-#ifndef HAVE_AVAHI_THREADED_POLL
-    avahi_simple_poll_quit(ctx->simple_poll);
-#else
-    avahi_threaded_poll_quit(ctx->threaded_poll);
-#endif
+fail:
+    time(NULL);
+//    avahi_threaded_poll_quit(ctx->threaded_poll);
 }
 
 /* Called when publishing of service data completes */
@@ -89,412 +185,166 @@ static void publish_reply(AvahiEntryGroup *g,
                           AvahiEntryGroupState state,
                           AVAHI_GCC_UNUSED void *userdata)
 {
-  struct context *ctx = userdata;
-
-  assert(g == ctx->group);
-
-  switch (state) {
+    assert(ctx->group == NULL || g == ctx->group);
 
-  case AVAHI_ENTRY_GROUP_ESTABLISHED :
-    /* The entry group has been established successfully */
-    break;
+    switch (state) {
 
-  case AVAHI_ENTRY_GROUP_COLLISION: {
-    char *n;
+    case AVAHI_ENTRY_GROUP_ESTABLISHED :
+        /* The entry group has been established successfully */
+        LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
+        break;
 
-    /* Pick a new name for our service */
-
-    n = avahi_alternative_service_name(ctx->name);
-    assert(n);
-
-    avahi_free(ctx->name);
-    ctx->name = n;
-
-    register_stuff(ctx);
-    break;
-  }
-
-  case AVAHI_ENTRY_GROUP_FAILURE: {
-    LOG(log_error,
-        logtype_afpd,
-        "Failed to register service: %s\n",
-        avahi_strerror(avahi_client_errno(ctx->client)));
-    avahi_client_free (avahi_entry_group_get_client(g));
-#ifndef HAVE_AVAHI_THREADED_POLL
-    avahi_simple_poll_quit(ctx->simple_poll);
-#else
-    avahi_threaded_poll_quit(ctx->threaded_poll);
-#endif
-    break;
-  }
+    case AVAHI_ENTRY_GROUP_COLLISION:
+        /* With multiple names there's no way to know which one collided */
+        LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+        break;
+               
+    case AVAHI_ENTRY_GROUP_FAILURE:
+        LOG(log_error, logtype_afpd, "Failed to register service: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+        break;
 
-  case AVAHI_ENTRY_GROUP_UNCOMMITED:
-  case AVAHI_ENTRY_GROUP_REGISTERING:
-    ;
-  }
+    case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        break;
+    case AVAHI_ENTRY_GROUP_REGISTERING:
+        break;
+    }
 }
 
 static void client_callback(AvahiClient *client,
                             AvahiClientState state,
                             void *userdata)
 {
-  struct context *ctx = userdata;
-
-  ctx->client = client;
-
-  switch (state) {
-
-  case AVAHI_CLIENT_S_RUNNING:
-
-    /* The server has startup successfully and registered its host
-     * name on the network, so it's time to create our services */
-    if (!ctx->group)
-      register_stuff(ctx);
-    break;
-
-  case AVAHI_CLIENT_S_COLLISION:
-
-    if (ctx->group)
-      avahi_entry_group_reset(ctx->group);
-    break;
-
-  case AVAHI_CLIENT_FAILURE: {
-
-    if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
-      int error;
-
-      avahi_client_free(ctx->client);
-      ctx->client = NULL;
-      ctx->group = NULL;
-
-      /* Reconnect to the server */
-
-#ifndef HAVE_AVAHI_THREADED_POLL
-      if (!(ctx->client = avahi_client_new(avahi_simple_poll_get(ctx->simple_poll),
-#else
-      if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
-#endif
-                                           AVAHI_CLIENT_NO_FAIL,
-                                           client_callback,
-                                           ctx,
-                                           &error))) {
-
-        LOG(log_error,
-            logtype_afpd,
-            "Failed to contact server: %s\n",
-            avahi_strerror(error));
-
-        avahi_client_free (ctx->client);
-#ifndef HAVE_AVAHI_THREADED_POLL
-        avahi_simple_poll_quit(ctx->simple_poll);
-#else
-        avahi_threaded_poll_quit(ctx->threaded_poll);
-#endif
-      }
-
-      } else {
-        LOG(log_error,
-            logtype_afpd,
-            "Client failure: %s\n",
-            avahi_strerror(avahi_client_errno(client)));
-
-        avahi_client_free (ctx->client);
-#ifndef HAVE_AVAHI_THREADED_POLL
-        avahi_simple_poll_quit(ctx->simple_poll);
-#else
-        avahi_threaded_poll_quit(ctx->threaded_poll);
-#endif
-      }
-
-    break;
-  }
-
-  case AVAHI_CLIENT_S_REGISTERING:
-  case AVAHI_CLIENT_CONNECTING:
-    ;
-  }
-}
+    ctx->client = client;
+
+    switch (state) {
+    case AVAHI_CLIENT_S_RUNNING:
+        /* The server has startup successfully and registered its host
+         * name on the network, so it's time to create our services */
+        if (!ctx->group)
+            register_stuff();
+        break;
+
+    case AVAHI_CLIENT_S_COLLISION:
+        if (ctx->group)
+            avahi_entry_group_reset(ctx->group);
+        break;
+
+    case AVAHI_CLIENT_FAILURE: {
+        if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
+            int error;
+
+            avahi_client_free(ctx->client);
+            ctx->client = NULL;
+            ctx->group = NULL;
+
+            /* Reconnect to the server */
+            if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+                                                 AVAHI_CLIENT_NO_FAIL,
+                                                 client_callback,
+                                                 ctx,
+                                                 &error))) {
+
+                LOG(log_error, logtype_afpd, "Failed to contact server: %s",
+                    avahi_strerror(error));
+
+                avahi_threaded_poll_quit(ctx->threaded_poll);
+            }
+
+        } else {
+            LOG(log_error, logtype_afpd, "Client failure: %s",
+                avahi_strerror(avahi_client_errno(client)));
+            avahi_threaded_poll_quit(ctx->threaded_poll);
+        }
+        break;
+    }
 
-static void* thread(void *userdata) {
-#ifndef HAVE_AVAHI_THREADED_POLL
-  struct context *ctx = userdata;
-  sigset_t mask;
-  int r;
-
-  /* Make sure that signals are delivered to the main thread */
-  sigfillset(&mask);
-  pthread_sigmask(SIG_BLOCK, &mask, NULL);
-    
-  pthread_mutex_lock(&ctx->mutex);
-
-  /* Run the main loop */
-  LOG(log_info, logtype_afpd, "Starting avahi loop...");
-  r = avahi_simple_poll_loop(ctx->simple_poll);
-
-  /* Cleanup some stuff */
-  if (ctx->client)
-    avahi_client_free(ctx->client);
-  ctx->client = NULL;
-  ctx->group = NULL;
-    
-  pthread_mutex_unlock(&ctx->mutex);
-#endif    
-  return NULL;
+    case AVAHI_CLIENT_S_REGISTERING:
+        break;
+    case AVAHI_CLIENT_CONNECTING:
+        break;
+    }
 }
 
-static int poll_func(struct pollfd *ufds,
-                     unsigned int nfds,
-                     int timeout,
-                     void *userdata) {
-#ifndef HAVE_AVAHI_THREADED_POLL
-  pthread_mutex_t *mutex = userdata;
-  int r;
-
-  /* Before entering poll() we unlock the mutex, so that
-   * avahi_simple_poll_quit() can succeed from another thread. */
-    
-  pthread_mutex_unlock(mutex);
-  r = poll(ufds, nfds, timeout);
-  pthread_mutex_lock(mutex);
-
-  return r;
-#else
-  return 0;
-#endif
-}
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
 
 /*
  * Tries to setup the Zeroconf thread and any
  * neccessary config setting.
  */
-void* av_zeroconf_setup(unsigned long port, const char *name) {
-  struct context *ctx = NULL;
-
-  /* default service name, if there's none in
-   * the config file.
-   */
-  char service[256] = "AFP Server on ";
-  int error, ret;
-
-  /* initialize the struct that holds our
-   * config settings.
-   */
-  ctx = malloc(sizeof(struct context));
-  assert(ctx);
-  ctx->client = NULL;
-  ctx->group = NULL;
-#ifndef HAVE_AVAHI_THREADED_POLL
-  ctx->simple_poll = NULL;
-  pthread_mutex_init(&ctx->mutex, NULL);
-#else
-  ctx->threaded_poll = NULL;
-#endif
-  ctx->thread_running = 0;
-
-  LOG(log_info,
-      logtype_afpd,
-      "Setting port for Zeroconf service to: %i.\n",
-      port);  
-  ctx->port = port;
-
-  /* Prepare service name */
-  if (!name) {
-    LOG(log_info,
-        logtype_afpd,
-        "Assigning default service name.\n");
-    gethostname(service+14, sizeof(service)-15);
-    service[sizeof(service)-1] = 0;
-
-    ctx->name = strdup(service);
-  }
-  else {
-    ctx->name = strdup(name);
-  }
-
-  assert(ctx->name);
+void av_zeroconf_register(const AFPConfig *configs) {
+    int error;
+
+    /* initialize the struct that holds our config settings. */
+    if (ctx) {
+        LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
+        avahi_entry_group_reset(ctx->group);
+    } else {
+        ctx = calloc(1, sizeof(struct context));
+        ctx->configs = configs;
+        assert(ctx);
+    }
 
 /* first of all we need to initialize our threading env */
-#ifdef HAVE_AVAHI_THREADED_POLL
-  if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
-     goto fail;
-  }
-#else
-  if (!(ctx->simple_poll = avahi_simple_poll_new())) {
-      LOG(log_error,
-          logtype_afpd,
-          "Failed to create event loop object.\n");
-      goto fail;
-  }
-
-  avahi_simple_poll_set_func(ctx->simple_poll, poll_func, &ctx->mutex);
-#endif
+    if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
+        goto fail;
+    }
 
 /* now we need to acquire a client */
-#ifdef HAVE_AVAHI_THREADED_POLL
-  if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
-                                       AVAHI_CLIENT_NO_FAIL,
-                                       client_callback,
-                                       ctx,
-                                       &error))) {
-    LOG(log_error,
-        logtype_afpd,
-        "Failed to create client object: %s\n",
-        avahi_strerror(avahi_client_errno(ctx->client)));
-    goto fail;
-  }
-#else
-  if (!(ctx->client = avahi_client_new(avahi_simple_poll_get(ctx->simple_poll),
-                                       AVAHI_CLIENT_NO_FAIL,
-                                       client_callback,
-                                       ctx,
-                                       &error))) {
-    LOG(log_error,
-        logtype_afpd,
-        "Failed to create client object: %s\n",
-        avahi_strerror(avahi_client_errno(ctx->client)));
-    goto fail;
-  }
-#endif
-
-  return ctx;
-
-fail:
-
-  if (ctx)
-    av_zeroconf_unregister(ctx);
-
-  return NULL;
-}
-
-/*
- * This function finally runs the loop impl.
- */
-int av_zeroconf_run(void *u) {
-  struct context *ctx = u;
-  int ret;
-
-#ifdef HAVE_AVAHI_THREADED_POLL
-  /* Finally, start the event loop thread */
-  if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
-    LOG(log_error,
-        logtype_afpd,
-        "Failed to create thread: %s\n",
-        avahi_strerror(avahi_client_errno(ctx->client)));
-    goto fail;
-  } else {
-    LOG(log_info, logtype_afpd, "Successfully started avahi loop.\n");
-  }
-#else
-  /* Create the mDNS event handler */
-  if ((ret = pthread_create(&ctx->thread_id, NULL, thread, ctx)) < 0) {
-    LOG(log_error,
-        logtype_afpd,
-        "Failed to create thread: %s\n", strerror(ret));
-    goto fail;
-  } else {
-    LOG(log_info, logtype_afpd, "Successfully started avahi loop.\n");
-  }
-#endif
+    if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+                                         AVAHI_CLIENT_NO_FAIL,
+                                         client_callback,
+                                         NULL,
+                                         &error))) {
+        LOG(log_error, logtype_afpd, "Failed to create client object: %s",
+            avahi_strerror(error));
+        goto fail;
+    }
 
-  ctx->thread_running = 1;
+    if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
+        LOG(log_error, logtype_afpd, "Failed to create thread: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        goto fail;
+    } else {
+        LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
+    }
 
-  return 0;
+    ctx->thread_running = 1;
+    return;
 
 fail:
+    av_zeroconf_unregister();
 
-  if (ctx)
-    av_zeroconf_unregister(ctx);
-
-  return -1;
-}
-
-/*
- * Used to lock access to the loop.
- * Currently unused.
- */
-void av_zeroconf_lock(void *u) {
-#ifdef HAVE_AVAHI_THREADED_POLL
-  struct context *ctx = u;
-
-  avahi_threaded_poll_lock(ctx->threaded_poll);
-#endif
-}
-
-/*
- * Used to unlock access to the loop.
- * Currently unused.
- */
-void av_zeroconf_unlock(void *u) {
-#ifdef HAVE_AVAHI_THREADED_POLL
-  struct context *ctx = u;
-
-  avahi_threaded_poll_unlock(ctx->threaded_poll);
-#endif
-}
-
-/*
- * Tries to shutdown this loop impl.
- * Call this function from outside this thread.
- */
-void av_zeroconf_shutdown(void *u) {
-  struct context *ctx = u;
-
-  /* Call this when the app shuts down */
-#ifdef HAVE_AVAHI_THREADED_POLL
-  avahi_threaded_poll_stop(ctx->threaded_poll);
-  avahi_free(ctx->name);
-  avahi_client_free(ctx->client);
-  avahi_threaded_poll_free(ctx->threaded_poll);
-#else
-  av_zeroconf_unregister(ctx);
-#endif
+    return;
 }
 
 /*
  * Tries to shutdown this loop impl.
  * Call this function from inside this thread.
  */
-int av_zeroconf_unregister(void *u) {
-  struct context *ctx = u;
-
-  if (ctx->thread_running) {
-#ifndef HAVE_AVAHI_THREADED_POLL
-    pthread_mutex_lock(&ctx->mutex);
-    avahi_simple_poll_quit(ctx->simple_poll);
-    pthread_mutex_unlock(&ctx->mutex);
-
-    pthread_join(ctx->thread_id, NULL);
-#else
-    /* First, block the event loop */
-    avahi_threaded_poll_lock(ctx->threaded_poll);
-
-    /* Than, do your stuff */
-    avahi_threaded_poll_quit(ctx->threaded_poll);
-
-    /* Finally, unblock the event loop */
-    avahi_threaded_poll_unlock(ctx->threaded_poll);
-#endif
-    ctx->thread_running = 0;
-  }
-
-  avahi_free(ctx->name);
-
-  if (ctx->client)
-    avahi_client_free(ctx->client);
-
-#ifndef HAVE_AVAHI_THREADED_POLL
-  if (ctx->simple_poll)
-    avahi_simple_poll_free(ctx->simple_poll);
-
-  pthread_mutex_destroy(&ctx->mutex);
-#else
-  if (ctx->threaded_poll)
-    avahi_threaded_poll_free(ctx->threaded_poll);
-#endif
-
-  free(ctx);
-
-  return 0;
+int av_zeroconf_unregister() {
+    LOG(log_error, logtype_afpd, "av_zeroconf_unregister");
+
+    if (ctx) {
+        LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_stop");
+        if (ctx->threaded_poll)
+            avahi_threaded_poll_stop(ctx->threaded_poll);
+        LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_client_free");
+        if (ctx->client)
+            avahi_client_free(ctx->client);
+        LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_free");
+        if (ctx->threaded_poll)
+            avahi_threaded_poll_free(ctx->threaded_poll);
+        free(ctx);
+        ctx = NULL;
+    }
+    return 0;
 }
 
 #endif /* USE_AVAHI */
+