]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_avahi.c
Advertise adisk with port 9.
[netatalk.git] / etc / afpd / afp_avahi.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /*
3  * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
4  * Purpose: Avahi based Zeroconf support
5  * Docs:    http://avahi.org/download/doxygen/
6  *
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #ifdef HAVE_AVAHI
14
15 #include <unistd.h>
16
17 #include <avahi-common/strlst.h>
18
19 #include <atalk/logger.h>
20 #include <atalk/util.h>
21 #include <atalk/dsi.h>
22 #include <atalk/unicode.h>
23
24 #include "afp_avahi.h"
25 #include "afp_config.h"
26 #include "volume.h"
27
28 /*****************************************************************
29  * Global variables
30  *****************************************************************/
31 struct context *ctx = NULL;
32
33 /*****************************************************************
34  * Private functions
35  *****************************************************************/
36
37 static void publish_reply(AvahiEntryGroup *g,
38                           AvahiEntryGroupState state,
39                           void *userdata);
40
41 /*
42  * This function tries to register the AFP DNS
43  * SRV service type.
44  */
45 static void register_stuff(void) {
46         uint port;
47         const AFPConfig *config;
48         const struct vol *volume;
49         DSI *dsi;
50         const char *name;
51         AvahiStringList *strlist = NULL;
52         char        tmpname[256];
53
54   assert(ctx->client);
55
56   if (!ctx->group) {
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)));
60       goto fail;
61     }
62   }
63
64   if (avahi_entry_group_is_empty(ctx->group)) {
65     /* Register our service */
66
67                 /* Build AFP volumes list */
68                 int i = 0;
69                 strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
70                 
71                 for (volume = getvolumes(); volume; volume = volume->v_next) {
72
73                         if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0)
74                                 goto fail;
75
76                         if ((volume->v_flags & AFPVOL_TM) && volume->v_uuid) {
77                                 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
78                                                 volume->v_localname, volume->v_uuid);
79                                 strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
80                                                                                                                                                                                          i++, tmpname, volume->v_uuid);
81                         }
82                 }
83
84                 /* AFP server */
85                 for (config = ctx->configs; config; config = config->next) {
86
87                         dsi = (DSI *)config->obj.handle;
88                         name = config->obj.options.server ?
89                                 config->obj.options.server : config->obj.options.hostname;
90                         port = getip_port((struct sockaddr *)&dsi->server);
91
92                         if (avahi_entry_group_add_service(ctx->group,
93                                                                                                                                                                 AVAHI_IF_UNSPEC,
94                                                                                                                                                                 AVAHI_PROTO_UNSPEC,
95                                                                                                                                                                 0,
96                                                                                                                                                                 name,
97                                                                                                                                                                 AFP_DNS_SERVICE_TYPE,
98                                                                                                                                                                 NULL,
99                                                                                                                                                                 NULL,
100                                                                                                                                                                 port,
101                                                                                                                                                                 NULL) < 0) {
102                                 LOG(log_error, logtype_afpd, "Failed to add service: %s",
103                                                 avahi_strerror(avahi_client_errno(ctx->client)));
104                                 goto fail;
105                         }
106
107                         if (i && avahi_entry_group_add_service_strlst(ctx->group,
108                                                                                                                                                                                                                 AVAHI_IF_UNSPEC,
109                                                                                                                                                                                                                 AVAHI_PROTO_UNSPEC,
110                                                                                                                                                                                                                 0,
111                                                                                                                                                                                                                 name,
112                                                                                                                                                                                                                 "_adisk._tcp",
113                                                                                                                                                                                                                 NULL,
114                                                                                                                                                                                                                 NULL,
115                                                                                                                                                                                                                 9, /* discard */
116                                                                                                                                                                                                                 strlist) < 0) {
117                                 LOG(log_error, logtype_afpd, "Failed to add service: %s",
118                                                 avahi_strerror(avahi_client_errno(ctx->client)));
119                                 goto fail;
120                         }       /* if */
121                 }       /* for config*/
122
123                 if (avahi_entry_group_commit(ctx->group) < 0) {
124                         LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
125                                         avahi_strerror(avahi_client_errno(ctx->client)));
126                         goto fail;
127                 }
128
129         }       /* if avahi_entry_group_is_empty*/
130
131   return;
132
133 fail:
134         avahi_client_free (ctx->client);
135         avahi_threaded_poll_quit(ctx->threaded_poll);
136 }
137
138 /* Called when publishing of service data completes */
139 static void publish_reply(AvahiEntryGroup *g,
140                           AvahiEntryGroupState state,
141                           AVAHI_GCC_UNUSED void *userdata)
142 {
143   assert(ctx->group == NULL || g == ctx->group);
144
145   switch (state) {
146
147   case AVAHI_ENTRY_GROUP_ESTABLISHED :
148     /* The entry group has been established successfully */
149                 LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
150     break;
151
152   case AVAHI_ENTRY_GROUP_COLLISION:
153                 /* With multiple names there's no way to know which one collided */
154     LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
155                                 avahi_strerror(avahi_client_errno(ctx->client)));
156                 avahi_client_free(avahi_entry_group_get_client(g));
157                 avahi_threaded_poll_quit(ctx->threaded_poll);
158     break;
159                 
160   case AVAHI_ENTRY_GROUP_FAILURE:
161     LOG(log_error, logtype_afpd, "Failed to register service: %s",
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);
165                 break;
166
167         case AVAHI_ENTRY_GROUP_UNCOMMITED:
168                 break;
169   case AVAHI_ENTRY_GROUP_REGISTERING:
170                 break;
171   }
172 }
173
174 static void client_callback(AvahiClient *client,
175                             AvahiClientState state,
176                             void *userdata)
177 {
178   ctx->client = client;
179
180   switch (state) {
181   case AVAHI_CLIENT_S_RUNNING:
182     /* The server has startup successfully and registered its host
183      * name on the network, so it's time to create our services */
184     if (!ctx->group)
185       register_stuff();
186     break;
187
188   case AVAHI_CLIENT_S_COLLISION:
189     if (ctx->group)
190       avahi_entry_group_reset(ctx->group);
191     break;
192
193   case AVAHI_CLIENT_FAILURE: {
194     if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
195       int error;
196
197       avahi_client_free(ctx->client);
198       ctx->client = NULL;
199       ctx->group = NULL;
200
201       /* Reconnect to the server */
202       if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
203                                            AVAHI_CLIENT_NO_FAIL,
204                                            client_callback,
205                                            ctx,
206                                            &error))) {
207
208         LOG(log_error, logtype_afpd, "Failed to contact server: %s",
209             avahi_strerror(error));
210
211         avahi_client_free (ctx->client);
212         avahi_threaded_poll_quit(ctx->threaded_poll);
213       }
214
215                 } else {
216                         LOG(log_error, logtype_afpd, "Client failure: %s",
217                                         avahi_strerror(avahi_client_errno(client)));
218                         avahi_client_free (ctx->client);
219                         avahi_threaded_poll_quit(ctx->threaded_poll);
220                 }
221     break;
222   }
223
224   case AVAHI_CLIENT_S_REGISTERING:
225                 break;
226   case AVAHI_CLIENT_CONNECTING:
227     break;
228   }
229 }
230
231 /************************************************************************
232  * Public funcions
233  ************************************************************************/
234
235 /*
236  * Tries to setup the Zeroconf thread and any
237  * neccessary config setting.
238  */
239 void av_zeroconf_setup(const AFPConfig *configs) {
240   int error, ret;
241
242   /* initialize the struct that holds our config settings. */
243   if (!ctx) {
244                 ctx = calloc(1, sizeof(struct context));
245                 ctx->configs = configs;
246         }
247   assert(ctx);
248
249 /* first of all we need to initialize our threading env */
250   if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
251      goto fail;
252   }
253
254 /* now we need to acquire a client */
255   if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
256                                        AVAHI_CLIENT_NO_FAIL,
257                                        client_callback,
258                                        NULL,
259                                        &error))) {
260     LOG(log_error, logtype_afpd, "Failed to create client object: %s",
261         avahi_strerror(avahi_client_errno(ctx->client)));
262     goto fail;
263   }
264
265   return;
266
267 fail:
268   if (ctx)
269     av_zeroconf_unregister();
270
271   return;
272 }
273
274 /*
275  * This function finally runs the loop impl.
276  */
277 int av_zeroconf_run(void) {
278   int ret;
279
280   /* Finally, start the event loop thread */
281   if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
282     LOG(log_error, logtype_afpd, "Failed to create thread: %s",
283         avahi_strerror(avahi_client_errno(ctx->client)));
284     goto fail;
285   } else {
286     LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
287   }
288
289   ctx->thread_running = 1;
290   return 0;
291
292 fail:
293         if (ctx)
294     av_zeroconf_unregister();
295
296   return -1;
297 }
298
299 /*
300  * Tries to shutdown this loop impl.
301  * Call this function from outside this thread.
302  */
303 void av_zeroconf_shutdown() {
304   /* Call this when the app shuts down */
305   avahi_threaded_poll_stop(ctx->threaded_poll);
306   avahi_client_free(ctx->client);
307   avahi_threaded_poll_free(ctx->threaded_poll);
308 }
309
310 /*
311  * Tries to shutdown this loop impl.
312  * Call this function from inside this thread.
313  */
314 int av_zeroconf_unregister() {
315   if (ctx->thread_running) {
316     /* First, block the event loop */
317     avahi_threaded_poll_lock(ctx->threaded_poll);
318
319     /* Than, do your stuff */
320     avahi_threaded_poll_quit(ctx->threaded_poll);
321
322     /* Finally, unblock the event loop */
323     avahi_threaded_poll_unlock(ctx->threaded_poll);
324     ctx->thread_running = 0;
325   }
326
327   if (ctx->client)
328     avahi_client_free(ctx->client);
329
330   if (ctx->threaded_poll)
331     avahi_threaded_poll_free(ctx->threaded_poll);
332
333   free(ctx);
334
335   return 0;
336 }
337
338 #endif /* USE_AVAHI */