]> arthur.barton.de Git - netatalk.git/commitdiff
afpd: move mDNS service registration to netatalk process
authorRalph Boehme <rb@sernet.de>
Fri, 8 Aug 2014 13:25:37 +0000 (15:25 +0200)
committerRalph Boehme <rb@sernet.de>
Sat, 9 Aug 2014 16:48:49 +0000 (18:48 +0200)
Signed-off-by: Ralph Boehme <rb@sernet.de>
17 files changed:
etc/afpd/Makefile.am
etc/afpd/afp_avahi.c [deleted file]
etc/afpd/afp_avahi.h [deleted file]
etc/afpd/afp_config.c
etc/afpd/afp_mdns.c [deleted file]
etc/afpd/afp_mdns.h [deleted file]
etc/afpd/afp_zeroconf.c [deleted file]
etc/afpd/afp_zeroconf.h [deleted file]
etc/afpd/main.c
etc/netatalk/Makefile.am
etc/netatalk/afp_avahi.c [new file with mode: 0644]
etc/netatalk/afp_avahi.h [new file with mode: 0644]
etc/netatalk/afp_mdns.c [new file with mode: 0644]
etc/netatalk/afp_mdns.h [new file with mode: 0644]
etc/netatalk/afp_zeroconf.c [new file with mode: 0644]
etc/netatalk/afp_zeroconf.h [new file with mode: 0644]
etc/netatalk/netatalk.c

index 724cb52fa5f8fee61a19f04b076794af7fa3e596..7d7405c354fe6cb49129ef02598c77051b6bf684 100644 (file)
@@ -9,13 +9,10 @@ DISTCLEANFILES =
 sbin_PROGRAMS = afpd
 
 afpd_SOURCES = \
-       afp_avahi.c \
        afp_config.c \
        afp_dsi.c \
-       afp_mdns.c \
        afp_options.c \
        afp_util.c \
-       afp_zeroconf.c \
        afprun.c \
        afs.c \
        appl.c \
@@ -49,12 +46,12 @@ afpd_SOURCES = \
 
 afpd_LDADD =  \
        $(top_builddir)/libatalk/libatalk.la \
-       @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@ @MYSQL_LIBS@
+       @LIBGCRYPT_LIBS@ @QUOTA_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @PTHREAD_LIBS@ @GSSAPI_LIBS@ @KRB5_LIBS@ @MYSQL_LIBS@
 
 afpd_LDFLAGS = -export-dynamic
 
 afpd_CFLAGS = \
-       @ZEROCONF_CFLAGS@ @GSSAPI_CFLAGS@ @KRB5_CFLAGS@ @PTHREAD_CFLAGS@\
+       @GSSAPI_CFLAGS@ @KRB5_CFLAGS@ @PTHREAD_CFLAGS@\
        -DAPPLCNAME \
        -DSERVERTEXT=\"$(SERVERTEXT)/\" \
        -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
@@ -101,4 +98,4 @@ endif
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h file.h \
         filedir.h fork.h icon.h mangle.h misc.h status.h switch.h \
         uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
-        dircache.h afp_zeroconf.h afp_avahi.h afp_mdns.h afpstats.h afpstats_obj.h
+        dircache.h afpstats_obj.h
diff --git a/etc/afpd/afp_avahi.c b/etc/afpd/afp_avahi.c
deleted file mode 100644 (file)
index 5218d51..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: Avahi based Zeroconf support
- * Docs:    http://avahi.org/download/doxygen/
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef HAVE_AVAHI
-
-#include <unistd.h>
-#include <time.h>
-
-#include <avahi-common/strlst.h>
-
-#include <atalk/logger.h>
-#include <atalk/util.h>
-#include <atalk/dsi.h>
-#include <atalk/unicode.h>
-#include <atalk/netatalk_conf.h>
-
-#include "afp_zeroconf.h"
-#include "afp_avahi.h"
-
-/*****************************************************************
- * Global variables
- *****************************************************************/
-struct context *ctx = NULL;
-
-/*****************************************************************
- * Private functions
- *****************************************************************/
-
-static void publish_reply(AvahiEntryGroup *g,
-                          AvahiEntryGroupState state,
-                          void *userdata);
-
-/*
- * This function tries to register the AFP DNS
- * SRV service type.
- */
-static void register_stuff(void) {
-    uint port;
-    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;
-        }
-    }
-
-    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_u8mname, -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 (dsi = ctx->obj->dsi; dsi; dsi = dsi->next) {
-            port = getip_port((struct sockaddr *)&dsi->server);
-
-            LOG(log_info, logtype_afpd, "hostname: %s", ctx->obj->options.hostname);
-
-            if (convert_string(ctx->obj->options.unixcharset,
-                               CH_UTF8,
-                               ctx->obj->options.hostname,
-                               -1,
-                               name,
-                               MAXINSTANCENAMELEN) <= 0) {
-                LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name: %s", ctx->obj->options.hostname);
-                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 (ctx->obj->options.mimicmodel) {
-                strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", ctx->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;
-
-fail:
-    time(NULL);
-//    avahi_threaded_poll_quit(ctx->threaded_poll);
-}
-
-/* Called when publishing of service data completes */
-static void publish_reply(AvahiEntryGroup *g,
-                          AvahiEntryGroupState state,
-                          AVAHI_GCC_UNUSED void *userdata)
-{
-    assert(ctx->group == NULL || g == ctx->group);
-
-    switch (state) {
-
-    case AVAHI_ENTRY_GROUP_ESTABLISHED :
-        /* The entry group has been established successfully */
-        LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
-        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:
-        break;
-    case AVAHI_ENTRY_GROUP_REGISTERING:
-        break;
-    }
-}
-
-static void client_callback(AvahiClient *client,
-                            AvahiClientState state,
-                            void *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();
-        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;
-    }
-
-    case AVAHI_CLIENT_S_REGISTERING:
-        break;
-    case AVAHI_CLIENT_CONNECTING:
-        break;
-    }
-}
-
-/************************************************************************
- * Public funcions
- ************************************************************************/
-
-/*
- * Tries to setup the Zeroconf thread and any
- * neccessary config setting.
- */
-void av_zeroconf_register(const AFPObj *obj) {
-    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->obj = obj;
-        assert(ctx);
-    }
-
-/* first of all we need to initialize our threading env */
-    if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
-        goto fail;
-    }
-
-/* now we need to acquire a client */
-    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;
-    }
-
-    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.");
-    }
-
-    ctx->thread_running = 1;
-    return;
-
-fail:
-    av_zeroconf_unregister();
-
-    return;
-}
-
-/*
- * Tries to shutdown this loop impl.
- * Call this function from inside this thread.
- */
-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 */
-
diff --git a/etc/afpd/afp_avahi.h b/etc/afpd/afp_avahi.h
deleted file mode 100644 (file)
index ae3366d..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- 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
- * Docs:    http://avahi.org/download/doxygen/
- *
- */
-
-#ifndef AFPD_AVAHI_H
-#define AFPD_AVAHI_H
-
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include <avahi-client/client.h>
-#include <avahi-client/publish.h>
-#include <avahi-common/alternative.h>
-#include <avahi-common/thread-watch.h>
-#include <avahi-common/malloc.h>
-#include <avahi-common/error.h>
-
-#include <atalk/logger.h>
-
-#include "afp_config.h"
-
-struct context {
-       /* Avahi stuff */
-  int               thread_running;
-  AvahiThreadedPoll *threaded_poll;
-  AvahiClient       *client;
-  AvahiEntryGroup   *group;
-       /* Netatalk stuff */
-       const AFPObj      *obj;
-};
-
-/* prototype definitions */
-void av_zeroconf_register(const AFPObj *obj);
-int av_zeroconf_unregister(void);
-
-#endif   /* AFPD_AVAHI_H */
index ba5b232b43c0df41ccdd4d569438c8d18f073d83..2082a318c9060fe2cb8ff7b3f6e04a894ad3d4b9 100644 (file)
@@ -40,7 +40,6 @@
 #include "uam_auth.h"
 #include "status.h"
 #include "volume.h"
-#include "afp_zeroconf.h"
 
 /*!
  * Free and cleanup config and DSI
@@ -55,9 +54,6 @@ void configfree(AFPObj *obj, DSI *dsi)
     if (!dsi) {
         /* Master afpd reloading config */
         auth_unload();
-        if (! (obj->options.flags & OPTION_NOZEROCONF)) {
-            zeroconf_deregister();
-        }
     }
 
     unload_volumes(obj);
@@ -207,12 +203,6 @@ int configinit(AFPObj *obj)
     acl_ldap_readconfig(obj->iniconfig);
 #endif /* HAVE_LDAP */
 
-    /* Now register with zeroconf, we also need the volumes for that */
-    if (! (obj->options.flags & OPTION_NOZEROCONF)) {
-        load_volumes(obj, lv_all);
-        zeroconf_register(obj);
-    }
-
     if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce listener", NULL))) {
                LOG(log_note, logtype_afpd, "Adding FCE listener: %s", r);
                fce_add_udp_socket(r);
diff --git a/etc/afpd/afp_mdns.c b/etc/afpd/afp_mdns.c
deleted file mode 100644 (file)
index d3e6cd8..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Author:   Lee Essen <lee.essen@nowonline.co.uk>
- * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose:  mdns based Zeroconf support
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef HAVE_MDNS
-
-#include <unistd.h>
-#include <time.h>
-#include <pthread.h>
-#include <poll.h>
-
-#include <atalk/logger.h>
-#include <atalk/util.h>
-#include <atalk/dsi.h>
-#include <atalk/unicode.h>
-#include <atalk/netatalk_conf.h>
-
-#include "afp_zeroconf.h"
-#include "afp_mdns.h"
-
-/*
- * We'll store all the DNSServiceRef's here so that we can
- * deallocate them later
- */
-static DNSServiceRef   *svc_refs = NULL;
-static int             svc_ref_count = 0;
-static pthread_t       poller;
-
-/*
- * Its easier to use asprintf to set the TXT record values
- */
-
-int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... ) 
-{
-    int ret = 0;
-    char *str;
-    va_list ap;
-    va_start( ap, fmt );
-
-    if( 0 > vasprintf(&str, fmt, ap ) ) {
-        va_end(ap);
-        return -1;    
-    }
-    va_end(ap);
-
-    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
-        ret = -1;
-    }
-
-    free(str);
-    return ret;
-}
-
-int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...) 
-{
-    int ret = 0;
-    char *key = NULL, *str = NULL;
-    va_list ap;
-
-    if( 0 > asprintf(&key, key_fmt, key_var))
-        return -1;
-
-    va_start( ap, fmt );
-    if( 0 > vasprintf(&str, fmt, ap )) {
-        va_end(ap);
-        ret = -1;
-        goto exit;
-    }
-    va_end(ap);
-
-    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
-        ret = -1;
-        goto exit;
-    }
-
-exit:
-    if (str)
-        free(str);
-    if (key)
-        free(key);
-    return ret;
-}
-
-static struct pollfd *fds;
-
-/*
- * This is the thread that polls the filehandles
- */
-static void *polling_thread(void *arg) {
-    // First we loop through getting the filehandles and adding them to our poll, we
-    // need to allocate our pollfd's
-    DNSServiceErrorType error;
-    fds = calloc(svc_ref_count, sizeof(struct pollfd));
-    assert(fds);
-
-    for(int i=0; i < svc_ref_count; i++) {
-        int fd = DNSServiceRefSockFD(svc_refs[i]);
-        fds[i].fd = fd;
-        fds[i].events = POLLIN;
-    }
-
-    // Now we can poll and process the results...
-    while(poll(fds, svc_ref_count, -1) > 0) {
-        for(int i=0; i < svc_ref_count; i++) {
-            if(fds[i].revents & POLLIN) {
-                error = DNSServiceProcessResult(svc_refs[i]);
-            }
-        }
-    }
-    return(NULL);
-}
-
-/*
- * This is the callback for the service register function ... actually there isn't a lot
- * we can do if we get problems, so we don't really need to do anything other than report
- * the issue.
- */
-static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
-                          const char *name, const char *regtype, const char *domain, void *context)
-{
-    if (errorCode != kDNSServiceErr_NoError) {
-        LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
-            name, regtype, domain, errorCode);
-    }
-}
-
-/*
- * This function unregisters anything we have already
- * registered and frees associated memory
- */
-static void unregister_stuff() {
-    pthread_cancel(poller);    
-
-    for (int i = 0; i < svc_ref_count; i++)
-        close(fds[i].fd);
-    free(fds);
-    fds = NULL;
-
-    if(svc_refs) {
-        for(int i=0; i < svc_ref_count; i++) {
-            DNSServiceRefDeallocate(svc_refs[i]);
-        }
-        free(svc_refs);
-        svc_refs = NULL;
-        svc_ref_count = 0;
-    }
-}
-
-/*
- * This function tries to register the AFP DNS
- * SRV service type.
- */
-static void register_stuff(const AFPObj *obj) {
-    uint                                        port;
-    const struct vol                *volume;
-    DSI                                         *dsi;
-    char                                        name[MAXINSTANCENAMELEN+1];
-    DNSServiceErrorType         error;
-    TXTRecordRef                        txt_adisk;
-    TXTRecordRef                        txt_devinfo;
-    char                                        tmpname[256];
-
-    // If we had already registered, then we will unregister and re-register
-    if(svc_refs) unregister_stuff();
-
-    /* Register our service, prepare the TXT record */
-    TXTRecordCreate(&txt_adisk, 0, NULL);
-    if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100") ) {
-        LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for sys");
-        goto fail;
-    }
-
-    /* Build AFP volumes list */
-    int i = 0;
-
-    for (volume = getvolumes(); volume; volume = volume->v_next) {
-
-        if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -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);
-                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
-                                   tmpname, volume->v_uuid) ) {
-                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
-                    goto fail;
-                }
-            } else {
-                LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
-                    volume->v_localname);
-                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname) ) {
-                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
-                    goto fail;
-                }
-            }
-        }
-    }
-
-    // Now we can count the configs so we know how many service
-    // records to allocate
-    for (dsi = obj->dsi; dsi; dsi = dsi->next) {
-        svc_ref_count++;                    // AFP_DNS_SERVICE_TYPE
-        if (i) svc_ref_count++;      // ADISK_SERVICE_TYPE
-        if (obj->options.mimicmodel) svc_ref_count++;        // DEV_INFO_SERVICE_TYPE
-    }
-
-    // Allocate the memory to store our service refs
-    svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
-    assert(svc_refs);
-    svc_ref_count = 0;
-
-    /* AFP server */
-    for (dsi = obj->dsi; dsi; dsi = dsi->next) {
-
-        port = getip_port((struct sockaddr *)&dsi->server);
-
-        if (convert_string(obj->options.unixcharset,
-                           CH_UTF8,
-                           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);
-
-        error = DNSServiceRegister(&svc_refs[svc_ref_count++],
-                                   0,               // no flags
-                                   0,               // all network interfaces
-                                   dsi->bonjourname,
-                                   AFP_DNS_SERVICE_TYPE,
-                                   "",            // default domains
-                                   NULL,            // default host name
-                                   htons(port),
-                                   0,               // length of TXT
-                                   NULL,            // no TXT
-                                   RegisterReply,           // callback
-                                   NULL);       // no context
-        if(error != kDNSServiceErr_NoError) {
-            LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
-                AFP_DNS_SERVICE_TYPE, error);
-            goto fail;
-        }
-
-        if(i) {
-            error = DNSServiceRegister(&svc_refs[svc_ref_count++],
-                                       0,               // no flags
-                                       0,               // all network interfaces
-                                       dsi->bonjourname,
-                                       ADISK_SERVICE_TYPE,
-                                       "",            // default domains
-                                       NULL,            // default host name
-                                       htons(port),
-                                       TXTRecordGetLength(&txt_adisk),
-                                       TXTRecordGetBytesPtr(&txt_adisk),
-                                       RegisterReply,           // callback
-                                       NULL);       // no context
-            if(error != kDNSServiceErr_NoError) {
-                LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
-                    ADISK_SERVICE_TYPE, error);
-                goto fail;
-            }
-        }
-
-        if (obj->options.mimicmodel) {
-            LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
-                dsi->bonjourname, obj->options.mimicmodel);
-            TXTRecordCreate(&txt_devinfo, 0, NULL);
-            if( 0 > TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel) ) {
-              LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for model");
-              goto fail;
-            }
-
-            error = DNSServiceRegister(&svc_refs[svc_ref_count++],
-                                       0,               // no flags
-                                       0,               // all network interfaces
-                                       dsi->bonjourname,
-                                       DEV_INFO_SERVICE_TYPE,
-                                       "",            // default domains
-                                       NULL,            // default host name
-                                       /*
-                                        * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
-                                        *   "A value of 0 for a port is passed to register placeholder services.
-                                        *    Place holder services are not found  when browsing, but other
-                                        *    clients cannot register with the same name as the placeholder service."
-                                        * We therefor use port 9 which is used by the adisk service type.
-                                        */
-                                       htons(9),
-                                       TXTRecordGetLength(&txt_devinfo),
-                                       TXTRecordGetBytesPtr(&txt_devinfo),
-                                       RegisterReply,           // callback
-                                       NULL);       // no context
-            TXTRecordDeallocate(&txt_devinfo);
-            if(error != kDNSServiceErr_NoError) {
-                LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
-                    DEV_INFO_SERVICE_TYPE, error);
-                goto fail;
-            }
-        } /* if (config->obj.options.mimicmodel) */
-    }   /* for config*/
-
-        /*
-         * Now we can create the thread that will poll for the results
-         * and handle the calling of the callbacks
-         */
-    if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
-        LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
-        goto fail;
-    }
-
-fail:
-    TXTRecordDeallocate(&txt_adisk);
-    return;
-}
-
-/************************************************************************
- * Public funcions
- ************************************************************************/
-
-/*
- * Tries to setup the Zeroconf thread and any
- * neccessary config setting.
- */
-void md_zeroconf_register(const AFPObj *obj) {
-    int error;
-
-    register_stuff(obj);
-    return;
-}
-
-/*
- * Tries to shutdown this loop impl.
- * Call this function from inside this thread.
- */
-int md_zeroconf_unregister() {
-    unregister_stuff();
-    return 0;
-}
-
-#endif /* USE_MDNS */
-
diff --git a/etc/afpd/afp_mdns.h b/etc/afpd/afp_mdns.h
deleted file mode 100644 (file)
index 5008ae4..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Author:   Lee Essen <lee.essen@nowonline.co.uk>
- * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose:  mdns based Zeroconf support
- */
-
-#ifndef AFPD_MDNS_H
-#define AFPD_MDNS_H
-
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include <dns_sd.h>
-
-#include <atalk/logger.h>
-
-#include "afp_config.h"
-
-/* prototype definitions */
-void md_zeroconf_register(const AFPObj *obj);
-int md_zeroconf_unregister(void);
-
-#endif   /* AFPD_MDNS_H */
diff --git a/etc/afpd/afp_zeroconf.c b/etc/afpd/afp_zeroconf.c
deleted file mode 100644 (file)
index cf370fc..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
-/*
- * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: Zeroconf facade, that abstracts access to a
- *          particular Zeroconf implementation
- * Doc:     http://www.dns-sd.org/
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "afp_zeroconf.h"
-#include "afp_config.h"
-
-#ifdef HAVE_MDNS
-#include "afp_mdns.h"
-#elif defined (HAVE_AVAHI)
-#include "afp_avahi.h"
-#endif
-
-
-/*
- * Functions (actually they are just facades)
- */
-void zeroconf_register(const AFPObj *configs)
-{
-#if defined (HAVE_MDNS)
-  LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using mDNSResponder");
-
-       md_zeroconf_register(configs);
-#elif defined (HAVE_AVAHI)
-  LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using Avahi");
-
-       av_zeroconf_register(configs);
-#endif
-}
-
-void zeroconf_deregister(void)
-{
-#if defined (HAVE_MDNS)
-  LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using mDNSResponder");
-       md_zeroconf_unregister();
-#elif defined (HAVE_AVAHI)
-  LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using Avahi");
-       av_zeroconf_unregister();
-#endif
-}
diff --git a/etc/afpd/afp_zeroconf.h b/etc/afpd/afp_zeroconf.h
deleted file mode 100644 (file)
index 89fc3ca..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
-/*
- * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
- * Purpose: Zeroconf facade, that abstracts access to a
- *          particular Zeroconf implementation
- * Doc:     http://www.dns-sd.org/
- *
- */
-
-#ifndef AFPD_ZEROCONF_H
-#define AFPD_ZEROCONF_H
-
-#include "afp_config.h"
-
-#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
-#define ADISK_SERVICE_TYPE "_adisk._tcp"
-#define DEV_INFO_SERVICE_TYPE "_device-info._tcp"
-
-#define MAXINSTANCENAMELEN 63
-
-/*
- * Prototype Definitions
- */
-
-/*
- * registers service with a particular Zerconf implemenation.
- */
-void zeroconf_register(const AFPObj *obj);
-
-/*
- * de-registers the ntpd service with a particular Zerconf implemenation.
- */
-void zeroconf_deregister(void);
-
-#endif /* AFPD_ZEROCONF_H */
index e612a01cae9615f3264378d4df11ff37a4832fa6..32ed6834a877598d280cf1ad46d2db790261288e 100644 (file)
@@ -37,7 +37,6 @@
 #include "status.h"
 #include "fork.h"
 #include "uam_auth.h"
-#include "afp_zeroconf.h"
 #include "afpstats.h"
 
 #define ASEV_THRESHHOLD 10
index ca3e163f68ce3d2c390cf8492325731aa7e94bcf..5392b9ddc0e1bd795c9b090663e13ad2cc238c59 100644 (file)
@@ -4,8 +4,9 @@ pkgconfdir = @PKGCONFDIR@
 
 sbin_PROGRAMS = netatalk
 
-netatalk_SOURCES = netatalk.c
+netatalk_SOURCES = netatalk.c afp_avahi.c afp_mdns.c afp_zeroconf.c
 netatalk_CFLAGS = \
+       @ZEROCONF_CFLAGS@ \
        -D_PATH_CONFDIR=\"$(pkgconfdir)/\" \
        -D_PATH_STATEDIR='"$(localstatedir)/netatalk/"' \
        -D_PATH_AFPD=\"$(sbindir)/afpd\" \
@@ -13,6 +14,7 @@ netatalk_CFLAGS = \
 
 netatalk_LDADD = \
        @MYSQL_LIBS@ \
+       @ZEROCONF_LIBS@ \
        $(top_builddir)/libatalk/libatalk.la
 
 netatalk_LDFLAGS =
@@ -28,3 +30,5 @@ else
 netatalk_CFLAGS += @LIBEVENT_CFLAGS@
 netatalk_LDFLAGS += @LIBEVENT_LDFLAGS@ -levent
 endif
+
+noinst_HEADERS = afp_zeroconf.h afp_avahi.h afp_mdns.h
diff --git a/etc/netatalk/afp_avahi.c b/etc/netatalk/afp_avahi.c
new file mode 100644 (file)
index 0000000..63bc950
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs:    http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_AVAHI
+
+#include <unistd.h>
+#include <time.h>
+
+#include <avahi-common/strlst.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+#include <atalk/netatalk_conf.h>
+
+#include "afp_zeroconf.h"
+#include "afp_avahi.h"
+
+/*****************************************************************
+ * Global variables
+ *****************************************************************/
+struct context *ctx = NULL;
+
+/*****************************************************************
+ * Private functions
+ *****************************************************************/
+
+static void publish_reply(AvahiEntryGroup *g,
+                          AvahiEntryGroupState state,
+                          void *userdata);
+
+/*
+ * This function tries to register the AFP DNS
+ * SRV service type.
+ */
+static void register_stuff(void) {
+    uint port;
+    const struct vol *volume;
+    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;
+        }
+    }
+
+    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_u8mname, -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);
+                }      
+            }
+        }
+
+        port = atoi(ctx->obj->options.port);
+
+        LOG(log_info, logtype_afpd, "hostname: %s", ctx->obj->options.hostname);
+
+        if (convert_string(ctx->obj->options.unixcharset,
+                           CH_UTF8,
+                           ctx->obj->options.hostname,
+                           -1,
+                           name,
+                           MAXINSTANCENAMELEN) <= 0) {
+            LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name: %s", ctx->obj->options.hostname);
+            goto fail;
+        }
+
+        LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour", name);            
+
+        if (avahi_entry_group_add_service(ctx->group,
+                                          AVAHI_IF_UNSPEC,
+                                          AVAHI_PROTO_UNSPEC,
+                                          0,
+                                          name,
+                                          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,
+                                                      name,
+                                                      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 (ctx->obj->options.mimicmodel) {
+            strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", ctx->obj->options.mimicmodel);
+            if (avahi_entry_group_add_service_strlst(ctx->group,
+                                                     AVAHI_IF_UNSPEC,
+                                                     AVAHI_PROTO_UNSPEC,
+                                                     0,
+                                                     name,
+                                                     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) */
+
+        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;
+
+fail:
+    time(NULL);
+//    avahi_threaded_poll_quit(ctx->threaded_poll);
+}
+
+/* Called when publishing of service data completes */
+static void publish_reply(AvahiEntryGroup *g,
+                          AvahiEntryGroupState state,
+                          AVAHI_GCC_UNUSED void *userdata)
+{
+    assert(ctx->group == NULL || g == ctx->group);
+
+    switch (state) {
+
+    case AVAHI_ENTRY_GROUP_ESTABLISHED :
+        /* The entry group has been established successfully */
+        LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
+        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:
+        break;
+    case AVAHI_ENTRY_GROUP_REGISTERING:
+        break;
+    }
+}
+
+static void client_callback(AvahiClient *client,
+                            AvahiClientState state,
+                            void *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();
+        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;
+    }
+
+    case AVAHI_CLIENT_S_REGISTERING:
+        break;
+    case AVAHI_CLIENT_CONNECTING:
+        break;
+    }
+}
+
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
+/*
+ * Tries to setup the Zeroconf thread and any
+ * neccessary config setting.
+ */
+void av_zeroconf_register(const AFPObj *obj) {
+    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->obj = obj;
+        assert(ctx);
+    }
+
+/* first of all we need to initialize our threading env */
+    if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
+        goto fail;
+    }
+
+/* now we need to acquire a client */
+    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;
+    }
+
+    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.");
+    }
+
+    ctx->thread_running = 1;
+    return;
+
+fail:
+    av_zeroconf_unregister();
+
+    return;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from inside this thread.
+ */
+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 */
+
diff --git a/etc/netatalk/afp_avahi.h b/etc/netatalk/afp_avahi.h
new file mode 100644 (file)
index 0000000..588a688
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*- 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
+ * Docs:    http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifndef AFPD_AVAHI_H
+#define AFPD_AVAHI_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include <atalk/logger.h>
+
+struct context {
+       /* Avahi stuff */
+  int               thread_running;
+  AvahiThreadedPoll *threaded_poll;
+  AvahiClient       *client;
+  AvahiEntryGroup   *group;
+       /* Netatalk stuff */
+       const AFPObj      *obj;
+};
+
+/* prototype definitions */
+void av_zeroconf_register(const AFPObj *obj);
+int av_zeroconf_unregister(void);
+
+#endif   /* AFPD_AVAHI_H */
diff --git a/etc/netatalk/afp_mdns.c b/etc/netatalk/afp_mdns.c
new file mode 100644 (file)
index 0000000..65ea55a
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Author:   Lee Essen <lee.essen@nowonline.co.uk>
+ * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose:  mdns based Zeroconf support
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_MDNS
+
+#include <unistd.h>
+#include <time.h>
+#include <pthread.h>
+#include <poll.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+#include <atalk/netatalk_conf.h>
+
+#include "afp_zeroconf.h"
+#include "afp_mdns.h"
+
+/*
+ * We'll store all the DNSServiceRef's here so that we can
+ * deallocate them later
+ */
+static DNSServiceRef   *svc_refs = NULL;
+static int             svc_ref_count = 0;
+static pthread_t       poller;
+
+/*
+ * Its easier to use asprintf to set the TXT record values
+ */
+
+int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... ) 
+{
+    int ret = 0;
+    char *str;
+    va_list ap;
+    va_start( ap, fmt );
+
+    if( 0 > vasprintf(&str, fmt, ap ) ) {
+        va_end(ap);
+        return -1;    
+    }
+    va_end(ap);
+
+    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+        ret = -1;
+    }
+
+    free(str);
+    return ret;
+}
+
+int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...) 
+{
+    int ret = 0;
+    char *key = NULL, *str = NULL;
+    va_list ap;
+
+    if( 0 > asprintf(&key, key_fmt, key_var))
+        return -1;
+
+    va_start( ap, fmt );
+    if( 0 > vasprintf(&str, fmt, ap )) {
+        va_end(ap);
+        ret = -1;
+        goto exit;
+    }
+    va_end(ap);
+
+    if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
+        ret = -1;
+        goto exit;
+    }
+
+exit:
+    if (str)
+        free(str);
+    if (key)
+        free(key);
+    return ret;
+}
+
+static struct pollfd *fds;
+
+/*
+ * This is the thread that polls the filehandles
+ */
+static void *polling_thread(void *arg) {
+    // First we loop through getting the filehandles and adding them to our poll, we
+    // need to allocate our pollfd's
+    DNSServiceErrorType error;
+    fds = calloc(svc_ref_count, sizeof(struct pollfd));
+    assert(fds);
+
+    for(int i=0; i < svc_ref_count; i++) {
+        int fd = DNSServiceRefSockFD(svc_refs[i]);
+        fds[i].fd = fd;
+        fds[i].events = POLLIN;
+    }
+
+    // Now we can poll and process the results...
+    while(poll(fds, svc_ref_count, -1) > 0) {
+        for(int i=0; i < svc_ref_count; i++) {
+            if(fds[i].revents & POLLIN) {
+                error = DNSServiceProcessResult(svc_refs[i]);
+            }
+        }
+    }
+    return(NULL);
+}
+
+/*
+ * This is the callback for the service register function ... actually there isn't a lot
+ * we can do if we get problems, so we don't really need to do anything other than report
+ * the issue.
+ */
+static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
+                          const char *name, const char *regtype, const char *domain, void *context)
+{
+    if (errorCode != kDNSServiceErr_NoError) {
+        LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
+            name, regtype, domain, errorCode);
+    }
+}
+
+/*
+ * This function unregisters anything we have already
+ * registered and frees associated memory
+ */
+static void unregister_stuff() {
+    pthread_cancel(poller);    
+
+    for (int i = 0; i < svc_ref_count; i++)
+        close(fds[i].fd);
+    free(fds);
+    fds = NULL;
+
+    if(svc_refs) {
+        for(int i=0; i < svc_ref_count; i++) {
+            DNSServiceRefDeallocate(svc_refs[i]);
+        }
+        free(svc_refs);
+        svc_refs = NULL;
+        svc_ref_count = 0;
+    }
+}
+
+/*
+ * This function tries to register the AFP DNS
+ * SRV service type.
+ */
+static void register_stuff(const AFPObj *obj) {
+    uint                                        port;
+    const struct vol                *volume;
+    DSI                                         *dsi;
+    char                                        name[MAXINSTANCENAMELEN+1];
+    DNSServiceErrorType         error;
+    TXTRecordRef                        txt_adisk;
+    TXTRecordRef                        txt_devinfo;
+    char                                        tmpname[256];
+
+    // If we had already registered, then we will unregister and re-register
+    if(svc_refs) unregister_stuff();
+
+    /* Register our service, prepare the TXT record */
+    TXTRecordCreate(&txt_adisk, 0, NULL);
+    if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100") ) {
+        LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for sys");
+        goto fail;
+    }
+
+    /* Build AFP volumes list */
+    int i = 0;
+
+    for (volume = getvolumes(); volume; volume = volume->v_next) {
+
+        if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -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);
+                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
+                                   tmpname, volume->v_uuid) ) {
+                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+                    goto fail;
+                }
+            } else {
+                LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
+                    volume->v_localname);
+                if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname) ) {
+                    LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
+                    goto fail;
+                }
+            }
+        }
+    }
+
+    /* AFP_DNS_SERVICE_TYPE */
+    svc_ref_count = 1;
+    if (i) {
+        /* ADISK_SERVICE_TYPE */
+        svc_ref_count++;
+    }
+    if (obj->options.mimicmodel) {
+        /* DEV_INFO_SERVICE_TYPE */
+        svc_ref_count++;
+    }
+
+    // Allocate the memory to store our service refs
+    svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
+    assert(svc_refs);
+    svc_ref_count = 0;
+
+    port = atoi(obj->options.port);
+
+    if (convert_string(obj->options.unixcharset,
+                       CH_UTF8,
+                       obj->options.hostname,
+                       -1,
+                       name,
+                       MAXINSTANCENAMELEN) <= 0) {
+        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);
+
+    error = DNSServiceRegister(&svc_refs[svc_ref_count++],
+                               0,               // no flags
+                               0,               // all network interfaces
+                               name,
+                               AFP_DNS_SERVICE_TYPE,
+                               "",            // default domains
+                               NULL,            // default host name
+                               htons(port),
+                               0,               // length of TXT
+                               NULL,            // no TXT
+                               RegisterReply,           // callback
+                               NULL);       // no context
+    if (error != kDNSServiceErr_NoError) {
+        LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
+            AFP_DNS_SERVICE_TYPE, error);
+        goto fail;
+    }
+
+    if (i) {
+        error = DNSServiceRegister(&svc_refs[svc_ref_count++],
+                                   0,               // no flags
+                                   0,               // all network interfaces
+                                   name,
+                                   ADISK_SERVICE_TYPE,
+                                   "",            // default domains
+                                   NULL,            // default host name
+                                   htons(port),
+                                   TXTRecordGetLength(&txt_adisk),
+                                   TXTRecordGetBytesPtr(&txt_adisk),
+                                   RegisterReply,           // callback
+                                   NULL);       // no context
+        if (error != kDNSServiceErr_NoError) {
+            LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
+                ADISK_SERVICE_TYPE, error);
+            goto fail;
+        }
+    }
+
+    if (obj->options.mimicmodel) {
+        LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
+            dsi->bonjourname, obj->options.mimicmodel);
+        TXTRecordCreate(&txt_devinfo, 0, NULL);
+        if ( 0 > TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel) ) {
+            LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for model");
+            goto fail;
+        }
+
+        error = DNSServiceRegister(&svc_refs[svc_ref_count++],
+                                   0,               // no flags
+                                   0,               // all network interfaces
+                                   name,
+                                   DEV_INFO_SERVICE_TYPE,
+                                   "",            // default domains
+                                   NULL,            // default host name
+                                   /*
+                                    * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
+                                    *   "A value of 0 for a port is passed to register placeholder services.
+                                    *    Place holder services are not found  when browsing, but other
+                                    *    clients cannot register with the same name as the placeholder service."
+                                    * We therefor use port 9 which is used by the adisk service type.
+                                    */
+                                   htons(9),
+                                   TXTRecordGetLength(&txt_devinfo),
+                                   TXTRecordGetBytesPtr(&txt_devinfo),
+                                   RegisterReply,           // callback
+                                   NULL);       // no context
+        TXTRecordDeallocate(&txt_devinfo);
+        if (error != kDNSServiceErr_NoError) {
+            LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
+                DEV_INFO_SERVICE_TYPE, error);
+            goto fail;
+        }
+    } /* if (config->obj.options.mimicmodel) */
+
+    /*
+     * Now we can create the thread that will poll for the results
+     * and handle the calling of the callbacks
+     */
+    if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
+        LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
+        goto fail;
+    }
+
+fail:
+    TXTRecordDeallocate(&txt_adisk);
+    return;
+}
+
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
+/*
+ * Tries to setup the Zeroconf thread and any
+ * neccessary config setting.
+ */
+void md_zeroconf_register(const AFPObj *obj) {
+    int error;
+
+    register_stuff(obj);
+    return;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from inside this thread.
+ */
+int md_zeroconf_unregister() {
+    unregister_stuff();
+    return 0;
+}
+
+#endif /* USE_MDNS */
+
diff --git a/etc/netatalk/afp_mdns.h b/etc/netatalk/afp_mdns.h
new file mode 100644 (file)
index 0000000..7e876c2
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Author:   Lee Essen <lee.essen@nowonline.co.uk>
+ * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose:  mdns based Zeroconf support
+ */
+
+#ifndef AFPD_MDNS_H
+#define AFPD_MDNS_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <dns_sd.h>
+
+#include <atalk/logger.h>
+
+/* prototype definitions */
+void md_zeroconf_register(const AFPObj *obj);
+int md_zeroconf_unregister(void);
+
+#endif   /* AFPD_MDNS_H */
diff --git a/etc/netatalk/afp_zeroconf.c b/etc/netatalk/afp_zeroconf.c
new file mode 100644 (file)
index 0000000..c5359ca
--- /dev/null
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ *          particular Zeroconf implementation
+ * Doc:     http://www.dns-sd.org/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "afp_zeroconf.h"
+
+#ifdef HAVE_MDNS
+#include "afp_mdns.h"
+#elif defined (HAVE_AVAHI)
+#include "afp_avahi.h"
+#endif
+
+
+/*
+ * Functions (actually they are just facades)
+ */
+void zeroconf_register(const AFPObj *configs)
+{
+#if defined (HAVE_MDNS)
+  LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using mDNSResponder");
+
+       md_zeroconf_register(configs);
+#elif defined (HAVE_AVAHI)
+  LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using Avahi");
+
+       av_zeroconf_register(configs);
+#endif
+}
+
+void zeroconf_deregister(void)
+{
+#if defined (HAVE_MDNS)
+  LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using mDNSResponder");
+       md_zeroconf_unregister();
+#elif defined (HAVE_AVAHI)
+  LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using Avahi");
+       av_zeroconf_unregister();
+#endif
+}
diff --git a/etc/netatalk/afp_zeroconf.h b/etc/netatalk/afp_zeroconf.h
new file mode 100644 (file)
index 0000000..88bb6e7
--- /dev/null
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ *          particular Zeroconf implementation
+ * Doc:     http://www.dns-sd.org/
+ *
+ */
+
+#ifndef AFPD_ZEROCONF_H
+#define AFPD_ZEROCONF_H
+
+#include <atalk/globals.h>
+
+#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
+#define ADISK_SERVICE_TYPE "_adisk._tcp"
+#define DEV_INFO_SERVICE_TYPE "_device-info._tcp"
+
+#define MAXINSTANCENAMELEN 63
+
+/*
+ * Prototype Definitions
+ */
+
+/*
+ * registers service with a particular Zerconf implemenation.
+ */
+void zeroconf_register(const AFPObj *obj);
+
+/*
+ * de-registers the ntpd service with a particular Zerconf implemenation.
+ */
+void zeroconf_deregister(void);
+
+#endif /* AFPD_ZEROCONF_H */
index b8987b7e74297d6016dd1258e97d2c94d4109780..bf7e15891df26833e89315222159ce5b7b17d6a9 100644 (file)
@@ -42,6 +42,7 @@
 #include <atalk/netatalk_conf.h>
 #include <atalk/bstrlib.h>
 #include <atalk/bstradd.h>
+#include "afp_zeroconf.h"
 
 #include <event2/event.h>
 
@@ -439,6 +440,12 @@ int main(int argc, char **argv)
     }
 #endif
 
+    /* Now register with zeroconf, we also need the volumes for that */
+    if (! (obj.options.flags & OPTION_NOZEROCONF)) {
+        LOG(log_note, logtype_default, "Registering with Zeroconf");
+        zeroconf_register(&obj);
+        LOG(log_note, logtype_default, "done");
+    }
 
     /* run the event loop */
     ret = event_base_dispatch(base);