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