]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_avahi.c
Use only one avahi thread to register all services
[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 <atalk/logger.h>
18 #include <atalk/util.h>
19 #include <atalk/dsi.h>
20
21 #include "afp_avahi.h"
22 #include "afp_config.h"
23
24 /*****************************************************************
25  * Global variables
26  *****************************************************************/
27 struct context *ctx = NULL;
28
29 /*****************************************************************
30  * Private functions
31  *****************************************************************/
32
33 static void publish_reply(AvahiEntryGroup *g,
34                           AvahiEntryGroupState state,
35                           void *userdata);
36
37 /*
38  * This function tries to register the AFP DNS
39  * SRV service type.
40  */
41 static void register_stuff(void) {
42         const AFPConfig *config;
43         DSI *dsi;
44   assert(ctx->client);
45
46   if (!ctx->group) {
47     if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
48       LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
49           avahi_strerror(avahi_client_errno(ctx->client)));
50       goto fail;
51     }
52   }
53
54   if (avahi_entry_group_is_empty(ctx->group)) {
55     /* Register our service */
56
57                 /* AFP server */
58                 for (config = ctx->configs; config; config = config->next) {
59                         dsi = (DSI *)config->obj.handle;
60                         if (avahi_entry_group_add_service(ctx->group,
61                                                                                                                                                                 AVAHI_IF_UNSPEC,
62                                                                                                                                                                 AVAHI_PROTO_UNSPEC,
63                                                                                                                                                                 0,
64                                                                                                                                                                 config->obj.options.server ?
65                                                                                                                                                                     config->obj.options.server
66                                                                                                                                                                     : config->obj.options.hostname,
67                                                                                                                                                                 AFP_DNS_SERVICE_TYPE,
68                                                                                                                                                                 NULL,
69                                                                                                                                                                 NULL,
70                                                                                                                                                                 getip_port((struct sockaddr *)&dsi->server),
71                                                                                                                                                                 NULL) < 0) {
72                                 LOG(log_error, logtype_afpd, "Failed to add service: %s",
73                                                 avahi_strerror(avahi_client_errno(ctx->client)));
74                                 goto fail;
75                         }
76
77                 }       /* for config*/
78
79                 /* AFP volumes */
80
81                 if (avahi_entry_group_commit(ctx->group) < 0) {
82                         LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
83                                         avahi_strerror(avahi_client_errno(ctx->client)));
84                         goto fail;
85                 }
86
87         }       /* if avahi_entry_group_is_empty*/
88
89   return;
90
91 fail:
92         avahi_client_free (ctx->client);
93         avahi_threaded_poll_quit(ctx->threaded_poll);
94 }
95
96 /* Called when publishing of service data completes */
97 static void publish_reply(AvahiEntryGroup *g,
98                           AvahiEntryGroupState state,
99                           AVAHI_GCC_UNUSED void *userdata)
100 {
101   assert(ctx->group == NULL || g == ctx->group);
102
103   switch (state) {
104
105   case AVAHI_ENTRY_GROUP_ESTABLISHED :
106     /* The entry group has been established successfully */
107                 LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
108     break;
109
110   case AVAHI_ENTRY_GROUP_COLLISION:
111                 /* With multiple names there's no way to know which one collided */
112     LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
113                                 avahi_strerror(avahi_client_errno(ctx->client)));
114                 avahi_client_free(avahi_entry_group_get_client(g));
115                 avahi_threaded_poll_quit(ctx->threaded_poll);
116     break;
117                 
118   case AVAHI_ENTRY_GROUP_FAILURE:
119     LOG(log_error, logtype_afpd, "Failed to register service: %s",
120                                 avahi_strerror(avahi_client_errno(ctx->client)));
121                 avahi_client_free(avahi_entry_group_get_client(g));
122                 avahi_threaded_poll_quit(ctx->threaded_poll);
123                 break;
124
125         case AVAHI_ENTRY_GROUP_UNCOMMITED:
126                 break;
127   case AVAHI_ENTRY_GROUP_REGISTERING:
128                 break;
129   }
130 }
131
132 static void client_callback(AvahiClient *client,
133                             AvahiClientState state,
134                             void *userdata)
135 {
136   ctx->client = client;
137
138   switch (state) {
139   case AVAHI_CLIENT_S_RUNNING:
140     /* The server has startup successfully and registered its host
141      * name on the network, so it's time to create our services */
142     if (!ctx->group)
143       register_stuff();
144     break;
145
146   case AVAHI_CLIENT_S_COLLISION:
147     if (ctx->group)
148       avahi_entry_group_reset(ctx->group);
149     break;
150
151   case AVAHI_CLIENT_FAILURE: {
152     if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
153       int error;
154
155       avahi_client_free(ctx->client);
156       ctx->client = NULL;
157       ctx->group = NULL;
158
159       /* Reconnect to the server */
160       if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
161                                            AVAHI_CLIENT_NO_FAIL,
162                                            client_callback,
163                                            ctx,
164                                            &error))) {
165
166         LOG(log_error, logtype_afpd, "Failed to contact server: %s",
167             avahi_strerror(error));
168
169         avahi_client_free (ctx->client);
170         avahi_threaded_poll_quit(ctx->threaded_poll);
171       }
172
173                 } else {
174                         LOG(log_error, logtype_afpd, "Client failure: %s",
175                                         avahi_strerror(avahi_client_errno(client)));
176                         avahi_client_free (ctx->client);
177                         avahi_threaded_poll_quit(ctx->threaded_poll);
178                 }
179     break;
180   }
181
182   case AVAHI_CLIENT_S_REGISTERING:
183                 break;
184   case AVAHI_CLIENT_CONNECTING:
185     break;
186   }
187 }
188
189 /************************************************************************
190  * Public funcions
191  ************************************************************************/
192
193 /*
194  * Tries to setup the Zeroconf thread and any
195  * neccessary config setting.
196  */
197 void av_zeroconf_setup(const AFPConfig *configs) {
198   int error, ret;
199
200   /* initialize the struct that holds our config settings. */
201   if (!ctx) {
202                 ctx = calloc(1, sizeof(struct context));
203                 ctx->configs = configs;
204         }
205   assert(ctx);
206
207 /* first of all we need to initialize our threading env */
208   if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
209      goto fail;
210   }
211
212 /* now we need to acquire a client */
213   if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
214                                        AVAHI_CLIENT_NO_FAIL,
215                                        client_callback,
216                                        NULL,
217                                        &error))) {
218     LOG(log_error, logtype_afpd, "Failed to create client object: %s",
219         avahi_strerror(avahi_client_errno(ctx->client)));
220     goto fail;
221   }
222
223   return;
224
225 fail:
226   if (ctx)
227     av_zeroconf_unregister();
228
229   return;
230 }
231
232 /*
233  * This function finally runs the loop impl.
234  */
235 int av_zeroconf_run(void) {
236   int ret;
237
238   /* Finally, start the event loop thread */
239   if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
240     LOG(log_error, logtype_afpd, "Failed to create thread: %s",
241         avahi_strerror(avahi_client_errno(ctx->client)));
242     goto fail;
243   } else {
244     LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
245   }
246
247   ctx->thread_running = 1;
248   return 0;
249
250 fail:
251         if (ctx)
252     av_zeroconf_unregister();
253
254   return -1;
255 }
256
257 /*
258  * Tries to shutdown this loop impl.
259  * Call this function from outside this thread.
260  */
261 void av_zeroconf_shutdown() {
262   /* Call this when the app shuts down */
263   avahi_threaded_poll_stop(ctx->threaded_poll);
264   avahi_client_free(ctx->client);
265   avahi_threaded_poll_free(ctx->threaded_poll);
266 }
267
268 /*
269  * Tries to shutdown this loop impl.
270  * Call this function from inside this thread.
271  */
272 int av_zeroconf_unregister() {
273   if (ctx->thread_running) {
274     /* First, block the event loop */
275     avahi_threaded_poll_lock(ctx->threaded_poll);
276
277     /* Than, do your stuff */
278     avahi_threaded_poll_quit(ctx->threaded_poll);
279
280     /* Finally, unblock the event loop */
281     avahi_threaded_poll_unlock(ctx->threaded_poll);
282     ctx->thread_running = 0;
283   }
284
285   if (ctx->client)
286     avahi_client_free(ctx->client);
287
288   if (ctx->threaded_poll)
289     avahi_threaded_poll_free(ctx->threaded_poll);
290
291   free(ctx);
292
293   return 0;
294 }
295
296 #endif /* USE_AVAHI */