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>
23 #include <atalk/netatalk_conf.h>
25 #include "afp_zeroconf.h"
26 #include "afp_avahi.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 struct vol *volume;
48 char name[MAXINSTANCENAMELEN+1];
49 AvahiStringList *strlist = NULL;
50 AvahiStringList *strlist2 = NULL;
56 if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
57 LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
58 avahi_strerror(avahi_client_errno(ctx->client)));
63 if (avahi_entry_group_is_empty(ctx->group)) {
64 /* Register our service */
66 /* Build AFP volumes list */
68 strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
70 for (volume = getvolumes(); volume; volume = volume->v_next) {
72 if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
73 LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
77 if (volume->v_flags & AFPVOL_TM) {
79 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
80 volume->v_localname, volume->v_uuid);
81 strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
82 i++, tmpname, volume->v_uuid);
84 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
86 strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
92 port = atoi(ctx->obj->options.port);
94 LOG(log_info, logtype_afpd, "hostname: %s", ctx->obj->options.hostname);
96 if (convert_string(ctx->obj->options.unixcharset,
98 ctx->obj->options.hostname,
101 MAXINSTANCENAMELEN) <= 0) {
102 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name: %s", ctx->obj->options.hostname);
106 LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour", name);
108 if (avahi_entry_group_add_service(ctx->group,
113 AFP_DNS_SERVICE_TYPE,
118 LOG(log_error, logtype_afpd, "Failed to add service: %s",
119 avahi_strerror(avahi_client_errno(ctx->client)));
123 if (i && avahi_entry_group_add_service_strlst(ctx->group,
133 LOG(log_error, logtype_afpd, "Failed to add service: %s",
134 avahi_strerror(avahi_client_errno(ctx->client)));
138 if (ctx->obj->options.mimicmodel) {
139 strlist2 = avahi_string_list_add_printf(strlist2, "model=%s", ctx->obj->options.mimicmodel);
140 if (avahi_entry_group_add_service_strlst(ctx->group,
145 DEV_INFO_SERVICE_TYPE,
150 LOG(log_error, logtype_afpd, "Failed to add service: %s",
151 avahi_strerror(avahi_client_errno(ctx->client)));
154 } /* if (config->obj.options.mimicmodel) */
156 if (avahi_entry_group_commit(ctx->group) < 0) {
157 LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
158 avahi_strerror(avahi_client_errno(ctx->client)));
162 } /* if avahi_entry_group_is_empty*/
168 // avahi_threaded_poll_quit(ctx->threaded_poll);
171 /* Called when publishing of service data completes */
172 static void publish_reply(AvahiEntryGroup *g,
173 AvahiEntryGroupState state,
174 AVAHI_GCC_UNUSED void *userdata)
176 assert(ctx->group == NULL || g == ctx->group);
180 case AVAHI_ENTRY_GROUP_ESTABLISHED :
181 /* The entry group has been established successfully */
182 LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
185 case AVAHI_ENTRY_GROUP_COLLISION:
186 /* With multiple names there's no way to know which one collided */
187 LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
188 avahi_strerror(avahi_client_errno(ctx->client)));
189 avahi_threaded_poll_quit(ctx->threaded_poll);
192 case AVAHI_ENTRY_GROUP_FAILURE:
193 LOG(log_error, logtype_afpd, "Failed to register service: %s",
194 avahi_strerror(avahi_client_errno(ctx->client)));
195 avahi_threaded_poll_quit(ctx->threaded_poll);
198 case AVAHI_ENTRY_GROUP_UNCOMMITED:
200 case AVAHI_ENTRY_GROUP_REGISTERING:
205 static void client_callback(AvahiClient *client,
206 AvahiClientState state,
209 ctx->client = client;
212 case AVAHI_CLIENT_S_RUNNING:
213 /* The server has startup successfully and registered its host
214 * name on the network, so it's time to create our services */
219 case AVAHI_CLIENT_S_COLLISION:
221 avahi_entry_group_reset(ctx->group);
224 case AVAHI_CLIENT_FAILURE: {
225 if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
228 avahi_client_free(ctx->client);
232 /* Reconnect to the server */
233 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
234 AVAHI_CLIENT_NO_FAIL,
239 LOG(log_error, logtype_afpd, "Failed to contact server: %s",
240 avahi_strerror(error));
242 avahi_threaded_poll_quit(ctx->threaded_poll);
246 LOG(log_error, logtype_afpd, "Client failure: %s",
247 avahi_strerror(avahi_client_errno(client)));
248 avahi_threaded_poll_quit(ctx->threaded_poll);
253 case AVAHI_CLIENT_S_REGISTERING:
255 case AVAHI_CLIENT_CONNECTING:
260 /************************************************************************
262 ************************************************************************/
265 * Tries to setup the Zeroconf thread and any
266 * neccessary config setting.
268 void av_zeroconf_register(const AFPObj *obj) {
271 /* initialize the struct that holds our config settings. */
273 LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
274 avahi_entry_group_reset(ctx->group);
276 ctx = calloc(1, sizeof(struct context));
281 /* first of all we need to initialize our threading env */
282 if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
286 /* now we need to acquire a client */
287 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
288 AVAHI_CLIENT_NO_FAIL,
292 LOG(log_error, logtype_afpd, "Failed to create client object: %s",
293 avahi_strerror(error));
297 if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
298 LOG(log_error, logtype_afpd, "Failed to create thread: %s",
299 avahi_strerror(avahi_client_errno(ctx->client)));
302 LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
305 ctx->thread_running = 1;
309 av_zeroconf_unregister();
315 * Tries to shutdown this loop impl.
316 * Call this function from inside this thread.
318 int av_zeroconf_unregister() {
319 LOG(log_debug, logtype_afpd, "av_zeroconf_unregister");
322 if (ctx->threaded_poll)
323 avahi_threaded_poll_stop(ctx->threaded_poll);
325 avahi_client_free(ctx->client);
326 if (ctx->threaded_poll)
327 avahi_threaded_poll_free(ctx->threaded_poll);
334 #endif /* USE_AVAHI */