X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fafpd%2Fafp_avahi.c;h=89ff95fdca66bf01ee8bc2dfa360bd9fb643623c;hb=df7560dfdb12b06090dc4b2c6e88d0858930b591;hp=31d0120a634483070c93d09a4be52d7e5ac4a4f3;hpb=da0e5334692c08b9de9f42fcd077d573fa27fa3b;p=netatalk.git diff --git a/etc/afpd/afp_avahi.c b/etc/afpd/afp_avahi.c index 31d0120a..89ff95fd 100644 --- a/etc/afpd/afp_avahi.c +++ b/etc/afpd/afp_avahi.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */ /* * Author: Daniel S. Haischt * Purpose: Avahi based Zeroconf support @@ -13,7 +12,27 @@ #ifdef HAVE_AVAHI #include +#include + +#include + +#include +#include +#include +#include + #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, @@ -23,55 +42,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", - 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'", 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", - avahi_strerror(avahi_client_errno(ctx->client))); - goto fail; - } + if (avahi_entry_group_is_empty(ctx->group)) { + /* Register our service */ - 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; - } - } - - return; + /* 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.dsi; + 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; fail: - avahi_client_free (ctx->client); - avahi_threaded_poll_quit(ctx->threaded_poll); + time(NULL); +// avahi_threaded_poll_quit(ctx->threaded_poll); } /* Called when publishing of service data completes */ @@ -79,234 +186,166 @@ static void publish_reply(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { - struct context *ctx = userdata; - char *n; + assert(ctx->group == NULL || g == ctx->group); - assert(g == ctx->group); + switch (state) { - 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_ESTABLISHED : - /* The entry group has been established successfully */ - break; - - case AVAHI_ENTRY_GROUP_COLLISION: - /* 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_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_client_free(avahi_entry_group_get_client(g)); - avahi_threaded_poll_quit(ctx->threaded_poll); - break; - - case AVAHI_ENTRY_GROUP_UNCOMMITED: - break; - case AVAHI_ENTRY_GROUP_REGISTERING: - 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) { - 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 */ - 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\n", - avahi_strerror(error)); + 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; + } - avahi_client_free (ctx->client); - avahi_threaded_poll_quit(ctx->threaded_poll); - } - - } else { - LOG(log_error, logtype_afpd, "Client failure: %s\n", - avahi_strerror(avahi_client_errno(client))); - avahi_client_free (ctx->client); - avahi_threaded_poll_quit(ctx->threaded_poll); - } - break; - } - - case AVAHI_CLIENT_S_REGISTERING: - break; - case AVAHI_CLIENT_CONNECTING: - 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_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; - ctx->threaded_poll = NULL; - ctx->thread_running = 0; - - LOG(log_debug, logtype_afpd, "Setting port for Zeroconf service to: %i.", port); - ctx->port = port; - - /* Prepare service name */ - if (!name) { - LOG(log_debug, logtype_afpd, "Assigning default service name."); - 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 */ - if (!(ctx->threaded_poll = avahi_threaded_poll_new())) { - goto fail; - } + 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, - ctx, - &error))) { - LOG(log_error, logtype_afpd, "Failed to create client object: %s", - avahi_strerror(avahi_client_errno(ctx->client))); - goto fail; - } - - return ctx; - -fail: - if (ctx) - av_zeroconf_unregister(ctx); + 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; + } - return NULL; -} + 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."); + } -/* - * This function finally runs the loop impl. - */ -int av_zeroconf_run(void *u) { - struct context *ctx = u; - int ret; - - /* 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", - avahi_strerror(avahi_client_errno(ctx->client))); - goto fail; - } else { - LOG(log_info, logtype_afpd, "Successfully started avahi loop."); - } - - ctx->thread_running = 1; - return 0; + ctx->thread_running = 1; + return; fail: - if (ctx) - av_zeroconf_unregister(ctx); - - return -1; -} + av_zeroconf_unregister(); -/* - * 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 */ - avahi_threaded_poll_stop(ctx->threaded_poll); - avahi_free(ctx->name); - avahi_client_free(ctx->client); - avahi_threaded_poll_free(ctx->threaded_poll); + 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) { - /* 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); - ctx->thread_running = 0; - } - - avahi_free(ctx->name); - - if (ctx->client) - avahi_client_free(ctx->client); - - if (ctx->threaded_poll) - avahi_threaded_poll_free(ctx->threaded_poll); - - 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 */ +