]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_avahi.c
Advertise volumes too
[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, i++) {
72                         if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0)
73                                 goto fail;
74                         LOG(log_debug, logtype_afpd, "Adding volume '%s'", volume->v_localname);
75                         strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0x01", i, tmpname);
76                 }
77
78                 /* AFP server */
79                 for (config = ctx->configs; config; config = config->next) {
80
81                         dsi = (DSI *)config->obj.handle;
82                         name = config->obj.options.server ?
83                                 config->obj.options.server : config->obj.options.hostname;
84                         port = getip_port((struct sockaddr *)&dsi->server);
85
86                         if (avahi_entry_group_add_service(ctx->group,
87                                                                                                                                                                 AVAHI_IF_UNSPEC,
88                                                                                                                                                                 AVAHI_PROTO_UNSPEC,
89                                                                                                                                                                 0,
90                                                                                                                                                                 name,
91                                                                                                                                                                 AFP_DNS_SERVICE_TYPE,
92                                                                                                                                                                 NULL,
93                                                                                                                                                                 NULL,
94                                                                                                                                                                 port,
95                                                                                                                                                                 NULL) < 0) {
96                                 LOG(log_error, logtype_afpd, "Failed to add service: %s",
97                                                 avahi_strerror(avahi_client_errno(ctx->client)));
98                                 goto fail;
99                         }
100
101                         if (avahi_entry_group_add_service_strlst(ctx->group,
102                                                                                                                                                                                          AVAHI_IF_UNSPEC,
103                                                                                                                                                                                          AVAHI_PROTO_UNSPEC,
104                                                                                                                                                                                          0,
105                                                                                                                                                                                          name,
106                                                                                                                                                                                          "_adisk._tcp",
107                                                                                                                                                                                          NULL,
108                                                                                                                                                                                          NULL,
109                                                                                                                                                                                          port,
110                                                                                                                                                                                          strlist) < 0) {
111                                 LOG(log_error, logtype_afpd, "Failed to add service: %s",
112                                                 avahi_strerror(avahi_client_errno(ctx->client)));
113                                 goto fail;
114                         }       /* if */
115                 }       /* for config*/
116
117                 if (avahi_entry_group_commit(ctx->group) < 0) {
118                         LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
119                                         avahi_strerror(avahi_client_errno(ctx->client)));
120                         goto fail;
121                 }
122
123         }       /* if avahi_entry_group_is_empty*/
124
125   return;
126
127 fail:
128         avahi_client_free (ctx->client);
129         avahi_threaded_poll_quit(ctx->threaded_poll);
130 }
131
132 /* Called when publishing of service data completes */
133 static void publish_reply(AvahiEntryGroup *g,
134                           AvahiEntryGroupState state,
135                           AVAHI_GCC_UNUSED void *userdata)
136 {
137   assert(ctx->group == NULL || g == ctx->group);
138
139   switch (state) {
140
141   case AVAHI_ENTRY_GROUP_ESTABLISHED :
142     /* The entry group has been established successfully */
143                 LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
144     break;
145
146   case AVAHI_ENTRY_GROUP_COLLISION:
147                 /* With multiple names there's no way to know which one collided */
148     LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
149                                 avahi_strerror(avahi_client_errno(ctx->client)));
150                 avahi_client_free(avahi_entry_group_get_client(g));
151                 avahi_threaded_poll_quit(ctx->threaded_poll);
152     break;
153                 
154   case AVAHI_ENTRY_GROUP_FAILURE:
155     LOG(log_error, logtype_afpd, "Failed to register service: %s",
156                                 avahi_strerror(avahi_client_errno(ctx->client)));
157                 avahi_client_free(avahi_entry_group_get_client(g));
158                 avahi_threaded_poll_quit(ctx->threaded_poll);
159                 break;
160
161         case AVAHI_ENTRY_GROUP_UNCOMMITED:
162                 break;
163   case AVAHI_ENTRY_GROUP_REGISTERING:
164                 break;
165   }
166 }
167
168 static void client_callback(AvahiClient *client,
169                             AvahiClientState state,
170                             void *userdata)
171 {
172   ctx->client = client;
173
174   switch (state) {
175   case AVAHI_CLIENT_S_RUNNING:
176     /* The server has startup successfully and registered its host
177      * name on the network, so it's time to create our services */
178     if (!ctx->group)
179       register_stuff();
180     break;
181
182   case AVAHI_CLIENT_S_COLLISION:
183     if (ctx->group)
184       avahi_entry_group_reset(ctx->group);
185     break;
186
187   case AVAHI_CLIENT_FAILURE: {
188     if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
189       int error;
190
191       avahi_client_free(ctx->client);
192       ctx->client = NULL;
193       ctx->group = NULL;
194
195       /* Reconnect to the server */
196       if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
197                                            AVAHI_CLIENT_NO_FAIL,
198                                            client_callback,
199                                            ctx,
200                                            &error))) {
201
202         LOG(log_error, logtype_afpd, "Failed to contact server: %s",
203             avahi_strerror(error));
204
205         avahi_client_free (ctx->client);
206         avahi_threaded_poll_quit(ctx->threaded_poll);
207       }
208
209                 } else {
210                         LOG(log_error, logtype_afpd, "Client failure: %s",
211                                         avahi_strerror(avahi_client_errno(client)));
212                         avahi_client_free (ctx->client);
213                         avahi_threaded_poll_quit(ctx->threaded_poll);
214                 }
215     break;
216   }
217
218   case AVAHI_CLIENT_S_REGISTERING:
219                 break;
220   case AVAHI_CLIENT_CONNECTING:
221     break;
222   }
223 }
224
225 /************************************************************************
226  * Public funcions
227  ************************************************************************/
228
229 /*
230  * Tries to setup the Zeroconf thread and any
231  * neccessary config setting.
232  */
233 void av_zeroconf_setup(const AFPConfig *configs) {
234   int error, ret;
235
236   /* initialize the struct that holds our config settings. */
237   if (!ctx) {
238                 ctx = calloc(1, sizeof(struct context));
239                 ctx->configs = configs;
240         }
241   assert(ctx);
242
243 /* first of all we need to initialize our threading env */
244   if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
245      goto fail;
246   }
247
248 /* now we need to acquire a client */
249   if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
250                                        AVAHI_CLIENT_NO_FAIL,
251                                        client_callback,
252                                        NULL,
253                                        &error))) {
254     LOG(log_error, logtype_afpd, "Failed to create client object: %s",
255         avahi_strerror(avahi_client_errno(ctx->client)));
256     goto fail;
257   }
258
259   return;
260
261 fail:
262   if (ctx)
263     av_zeroconf_unregister();
264
265   return;
266 }
267
268 /*
269  * This function finally runs the loop impl.
270  */
271 int av_zeroconf_run(void) {
272   int ret;
273
274   /* Finally, start the event loop thread */
275   if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
276     LOG(log_error, logtype_afpd, "Failed to create thread: %s",
277         avahi_strerror(avahi_client_errno(ctx->client)));
278     goto fail;
279   } else {
280     LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
281   }
282
283   ctx->thread_running = 1;
284   return 0;
285
286 fail:
287         if (ctx)
288     av_zeroconf_unregister();
289
290   return -1;
291 }
292
293 /*
294  * Tries to shutdown this loop impl.
295  * Call this function from outside this thread.
296  */
297 void av_zeroconf_shutdown() {
298   /* Call this when the app shuts down */
299   avahi_threaded_poll_stop(ctx->threaded_poll);
300   avahi_client_free(ctx->client);
301   avahi_threaded_poll_free(ctx->threaded_poll);
302 }
303
304 /*
305  * Tries to shutdown this loop impl.
306  * Call this function from inside this thread.
307  */
308 int av_zeroconf_unregister() {
309   if (ctx->thread_running) {
310     /* First, block the event loop */
311     avahi_threaded_poll_lock(ctx->threaded_poll);
312
313     /* Than, do your stuff */
314     avahi_threaded_poll_quit(ctx->threaded_poll);
315
316     /* Finally, unblock the event loop */
317     avahi_threaded_poll_unlock(ctx->threaded_poll);
318     ctx->thread_running = 0;
319   }
320
321   if (ctx->client)
322     avahi_client_free(ctx->client);
323
324   if (ctx->threaded_poll)
325     avahi_threaded_poll_free(ctx->threaded_poll);
326
327   free(ctx);
328
329   return 0;
330 }
331
332 #endif /* USE_AVAHI */