2 * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
3 * Purpose: Avahi based Zeroconf support
4 * 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_zeroconf.h"
25 #include "afp_avahi.h"
26 #include "afp_config.h"
29 /*****************************************************************
31 *****************************************************************/
32 struct context *ctx = NULL;
34 /*****************************************************************
36 *****************************************************************/
38 static void publish_reply(AvahiEntryGroup *g,
39 AvahiEntryGroupState state,
43 * This function tries to register the AFP DNS
46 static void register_stuff(void) {
48 const AFPConfig *config;
49 const struct vol *volume;
51 char name[MAXINSTANCENAMELEN+1];
52 AvahiStringList *strlist = NULL;
53 AvahiStringList *strlist2 = NULL;
59 if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
60 LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
61 avahi_strerror(avahi_client_errno(ctx->client)));
66 if (avahi_entry_group_is_empty(ctx->group)) {
67 /* Register our service */
69 /* Build AFP volumes list */
71 strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
73 for (volume = getvolumes(); volume; volume = volume->v_next) {
75 if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0) {
76 LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
80 if (volume->v_flags & AFPVOL_TM) {
82 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
83 volume->v_localname, volume->v_uuid);
84 strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
85 i++, tmpname, volume->v_uuid);
87 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
89 strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
96 for (config = ctx->configs; config; config = config->next) {
98 dsi = (DSI *)config->obj.handle;
99 port = getip_port((struct sockaddr *)&dsi->server);
101 if (convert_string(config->obj.options.unixcharset,
103 config->obj.options.server ?
104 config->obj.options.server :
105 config->obj.options.hostname,
108 MAXINSTANCENAMELEN) <= 0) {
109 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
112 if ((dsi->bonjourname = strdup(name)) == NULL) {
113 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
117 LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
120 if (avahi_entry_group_add_service(ctx->group,
125 AFP_DNS_SERVICE_TYPE,
130 LOG(log_error, logtype_afpd, "Failed to add service: %s",
131 avahi_strerror(avahi_client_errno(ctx->client)));
135 if (i && avahi_entry_group_add_service_strlst(ctx->group,
145 LOG(log_error, logtype_afpd, "Failed to add service: %s",
146 avahi_strerror(avahi_client_errno(ctx->client)));
150 if (config->obj.options.mimicmodel) {
151 strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", config->obj.options.mimicmodel);
152 if (avahi_entry_group_add_service_strlst(ctx->group,
157 DEV_INFO_SERVICE_TYPE,
162 LOG(log_error, logtype_afpd, "Failed to add service: %s",
163 avahi_strerror(avahi_client_errno(ctx->client)));
166 } /* if (config->obj.options.mimicmodel) */
170 if (avahi_entry_group_commit(ctx->group) < 0) {
171 LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
172 avahi_strerror(avahi_client_errno(ctx->client)));
176 } /* if avahi_entry_group_is_empty*/
182 // avahi_threaded_poll_quit(ctx->threaded_poll);
185 /* Called when publishing of service data completes */
186 static void publish_reply(AvahiEntryGroup *g,
187 AvahiEntryGroupState state,
188 AVAHI_GCC_UNUSED void *userdata)
190 assert(ctx->group == NULL || g == ctx->group);
194 case AVAHI_ENTRY_GROUP_ESTABLISHED :
195 /* The entry group has been established successfully */
196 LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
199 case AVAHI_ENTRY_GROUP_COLLISION:
200 /* With multiple names there's no way to know which one collided */
201 LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
202 avahi_strerror(avahi_client_errno(ctx->client)));
203 avahi_threaded_poll_quit(ctx->threaded_poll);
206 case AVAHI_ENTRY_GROUP_FAILURE:
207 LOG(log_error, logtype_afpd, "Failed to register service: %s",
208 avahi_strerror(avahi_client_errno(ctx->client)));
209 avahi_threaded_poll_quit(ctx->threaded_poll);
212 case AVAHI_ENTRY_GROUP_UNCOMMITED:
214 case AVAHI_ENTRY_GROUP_REGISTERING:
219 static void client_callback(AvahiClient *client,
220 AvahiClientState state,
223 ctx->client = client;
226 case AVAHI_CLIENT_S_RUNNING:
227 /* The server has startup successfully and registered its host
228 * name on the network, so it's time to create our services */
233 case AVAHI_CLIENT_S_COLLISION:
235 avahi_entry_group_reset(ctx->group);
238 case AVAHI_CLIENT_FAILURE: {
239 if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
242 avahi_client_free(ctx->client);
246 /* Reconnect to the server */
247 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
248 AVAHI_CLIENT_NO_FAIL,
253 LOG(log_error, logtype_afpd, "Failed to contact server: %s",
254 avahi_strerror(error));
256 avahi_threaded_poll_quit(ctx->threaded_poll);
260 LOG(log_error, logtype_afpd, "Client failure: %s",
261 avahi_strerror(avahi_client_errno(client)));
262 avahi_threaded_poll_quit(ctx->threaded_poll);
267 case AVAHI_CLIENT_S_REGISTERING:
269 case AVAHI_CLIENT_CONNECTING:
274 /************************************************************************
276 ************************************************************************/
279 * Tries to setup the Zeroconf thread and any
280 * neccessary config setting.
282 void av_zeroconf_register(const AFPConfig *configs) {
285 /* initialize the struct that holds our config settings. */
287 LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
288 avahi_entry_group_reset(ctx->group);
290 ctx = calloc(1, sizeof(struct context));
291 ctx->configs = configs;
295 /* first of all we need to initialize our threading env */
296 if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
300 /* now we need to acquire a client */
301 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
302 AVAHI_CLIENT_NO_FAIL,
306 LOG(log_error, logtype_afpd, "Failed to create client object: %s",
307 avahi_strerror(error));
311 if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
312 LOG(log_error, logtype_afpd, "Failed to create thread: %s",
313 avahi_strerror(avahi_client_errno(ctx->client)));
316 LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
319 ctx->thread_running = 1;
323 av_zeroconf_unregister();
329 * Tries to shutdown this loop impl.
330 * Call this function from inside this thread.
332 int av_zeroconf_unregister() {
333 LOG(log_error, logtype_afpd, "av_zeroconf_unregister");
336 LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_stop");
337 if (ctx->threaded_poll)
338 avahi_threaded_poll_stop(ctx->threaded_poll);
339 LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_client_free");
341 avahi_client_free(ctx->client);
342 LOG(log_error, logtype_afpd, "av_zeroconf_unregister: avahi_threaded_poll_free");
343 if (ctx->threaded_poll)
344 avahi_threaded_poll_free(ctx->threaded_poll);
351 #endif /* USE_AVAHI */