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