1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
3 * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
4 * Purpose: Avahi based Zeroconf support
5 * Docs: http://avahi.org/download/doxygen/
17 #include <avahi-common/strlst.h>
19 #include <atalk/logger.h>
20 #include <atalk/util.h>
21 #include <atalk/dsi.h>
22 #include <atalk/unicode.h>
24 #include "afp_avahi.h"
25 #include "afp_config.h"
28 /*****************************************************************
30 *****************************************************************/
31 struct context *ctx = NULL;
33 /*****************************************************************
35 *****************************************************************/
37 static void publish_reply(AvahiEntryGroup *g,
38 AvahiEntryGroupState state,
42 * This function tries to register the AFP DNS
45 static void register_stuff(void) {
47 const AFPConfig *config;
48 const struct vol *volume;
51 AvahiStringList *strlist = NULL;
57 if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
58 LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
59 avahi_strerror(avahi_client_errno(ctx->client)));
64 if (avahi_entry_group_is_empty(ctx->group)) {
65 /* Register our service */
67 /* Build AFP volumes list */
69 strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
71 for (volume = getvolumes(); volume; volume = volume->v_next) {
73 if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0)
76 if (volume->v_flags & AFPVOL_TM) {
78 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
79 volume->v_localname, volume->v_uuid);
80 strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
81 i++, tmpname, volume->v_uuid);
83 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
85 strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
92 for (config = ctx->configs; config; config = config->next) {
94 dsi = (DSI *)config->obj.handle;
95 name = config->obj.options.server ?
96 config->obj.options.server : config->obj.options.hostname;
97 port = getip_port((struct sockaddr *)&dsi->server);
99 if (avahi_entry_group_add_service(ctx->group,
104 AFP_DNS_SERVICE_TYPE,
109 LOG(log_error, logtype_afpd, "Failed to add service: %s",
110 avahi_strerror(avahi_client_errno(ctx->client)));
114 if (i && avahi_entry_group_add_service_strlst(ctx->group,
124 LOG(log_error, logtype_afpd, "Failed to add service: %s",
125 avahi_strerror(avahi_client_errno(ctx->client)));
130 if (avahi_entry_group_commit(ctx->group) < 0) {
131 LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
132 avahi_strerror(avahi_client_errno(ctx->client)));
136 } /* if avahi_entry_group_is_empty*/
141 avahi_client_free (ctx->client);
142 avahi_threaded_poll_quit(ctx->threaded_poll);
145 /* Called when publishing of service data completes */
146 static void publish_reply(AvahiEntryGroup *g,
147 AvahiEntryGroupState state,
148 AVAHI_GCC_UNUSED void *userdata)
150 assert(ctx->group == NULL || g == ctx->group);
154 case AVAHI_ENTRY_GROUP_ESTABLISHED :
155 /* The entry group has been established successfully */
156 LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
159 case AVAHI_ENTRY_GROUP_COLLISION:
160 /* With multiple names there's no way to know which one collided */
161 LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
162 avahi_strerror(avahi_client_errno(ctx->client)));
163 avahi_client_free(avahi_entry_group_get_client(g));
164 avahi_threaded_poll_quit(ctx->threaded_poll);
167 case AVAHI_ENTRY_GROUP_FAILURE:
168 LOG(log_error, logtype_afpd, "Failed to register service: %s",
169 avahi_strerror(avahi_client_errno(ctx->client)));
170 avahi_client_free(avahi_entry_group_get_client(g));
171 avahi_threaded_poll_quit(ctx->threaded_poll);
174 case AVAHI_ENTRY_GROUP_UNCOMMITED:
176 case AVAHI_ENTRY_GROUP_REGISTERING:
181 static void client_callback(AvahiClient *client,
182 AvahiClientState state,
185 ctx->client = client;
188 case AVAHI_CLIENT_S_RUNNING:
189 /* The server has startup successfully and registered its host
190 * name on the network, so it's time to create our services */
195 case AVAHI_CLIENT_S_COLLISION:
197 avahi_entry_group_reset(ctx->group);
200 case AVAHI_CLIENT_FAILURE: {
201 if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
204 avahi_client_free(ctx->client);
208 /* Reconnect to the server */
209 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
210 AVAHI_CLIENT_NO_FAIL,
215 LOG(log_error, logtype_afpd, "Failed to contact server: %s",
216 avahi_strerror(error));
218 avahi_client_free (ctx->client);
219 avahi_threaded_poll_quit(ctx->threaded_poll);
223 LOG(log_error, logtype_afpd, "Client failure: %s",
224 avahi_strerror(avahi_client_errno(client)));
225 avahi_client_free (ctx->client);
226 avahi_threaded_poll_quit(ctx->threaded_poll);
231 case AVAHI_CLIENT_S_REGISTERING:
233 case AVAHI_CLIENT_CONNECTING:
238 /************************************************************************
240 ************************************************************************/
243 * Tries to setup the Zeroconf thread and any
244 * neccessary config setting.
246 void av_zeroconf_setup(const AFPConfig *configs) {
249 /* initialize the struct that holds our config settings. */
251 LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
252 avahi_entry_group_reset(ctx->group);
254 ctx = calloc(1, sizeof(struct context));
255 ctx->configs = configs;
259 /* first of all we need to initialize our threading env */
260 if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
264 /* now we need to acquire a client */
265 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
266 AVAHI_CLIENT_NO_FAIL,
270 LOG(log_error, logtype_afpd, "Failed to create client object: %s",
271 avahi_strerror(avahi_client_errno(ctx->client)));
279 av_zeroconf_unregister();
285 * This function finally runs the loop impl.
287 int av_zeroconf_run(void) {
290 /* Finally, start the event loop thread */
291 if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
292 LOG(log_error, logtype_afpd, "Failed to create thread: %s",
293 avahi_strerror(avahi_client_errno(ctx->client)));
296 LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
299 ctx->thread_running = 1;
304 av_zeroconf_unregister();
310 * Tries to shutdown this loop impl.
311 * Call this function from outside this thread.
313 void av_zeroconf_shutdown() {
314 /* Call this when the app shuts down */
315 avahi_threaded_poll_stop(ctx->threaded_poll);
316 avahi_client_free(ctx->client);
317 avahi_threaded_poll_free(ctx->threaded_poll);
323 * Tries to shutdown this loop impl.
324 * Call this function from inside this thread.
326 int av_zeroconf_unregister() {
327 if (ctx->thread_running) {
328 /* First, block the event loop */
329 avahi_threaded_poll_lock(ctx->threaded_poll);
331 /* Than, do your stuff */
332 avahi_threaded_poll_quit(ctx->threaded_poll);
334 /* Finally, unblock the event loop */
335 avahi_threaded_poll_unlock(ctx->threaded_poll);
336 ctx->thread_running = 0;
340 avahi_client_free(ctx->client);
342 if (ctx->threaded_poll)
343 avahi_threaded_poll_free(ctx->threaded_poll);
351 #endif /* USE_AVAHI */