1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
3 * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
4 * Purpose: Avahi based Zeroconf support
5 * Docs: http://avahi.org/download/doxygen/
16 #include "afp_avahi.h"
18 static void publish_reply(AvahiEntryGroup *g,
19 AvahiEntryGroupState state,
23 * This function tries to register the AFP DNS
26 static void register_stuff(struct context *ctx) {
33 if (!(ctx->group = avahi_entry_group_new(ctx->client,
36 LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
37 avahi_strerror(avahi_client_errno(ctx->client)));
43 LOG(log_info, logtype_afpd, "Adding service '%s'", ctx->name);
45 if (avahi_entry_group_is_empty(ctx->group)) {
46 /* Register our service */
48 if (avahi_entry_group_add_service(ctx->group,
58 LOG(log_error, logtype_afpd, "Failed to add service: %s",
59 avahi_strerror(avahi_client_errno(ctx->client)));
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)));
73 avahi_client_free (ctx->client);
74 avahi_threaded_poll_quit(ctx->threaded_poll);
77 /* Called when publishing of service data completes */
78 static void publish_reply(AvahiEntryGroup *g,
79 AvahiEntryGroupState state,
80 AVAHI_GCC_UNUSED void *userdata)
82 struct context *ctx = userdata;
85 assert(g == ctx->group);
89 case AVAHI_ENTRY_GROUP_ESTABLISHED :
90 /* The entry group has been established successfully */
93 case AVAHI_ENTRY_GROUP_COLLISION:
94 /* Pick a new name for our service */
95 n = avahi_alternative_service_name(ctx->name);
97 avahi_free(ctx->name);
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);
109 case AVAHI_ENTRY_GROUP_UNCOMMITED:
111 case AVAHI_ENTRY_GROUP_REGISTERING:
116 static void client_callback(AvahiClient *client,
117 AvahiClientState state,
120 struct context *ctx = userdata;
122 ctx->client = client;
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 */
132 case AVAHI_CLIENT_S_COLLISION:
134 avahi_entry_group_reset(ctx->group);
137 case AVAHI_CLIENT_FAILURE: {
138 if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
141 avahi_client_free(ctx->client);
145 /* Reconnect to the server */
146 if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
147 AVAHI_CLIENT_NO_FAIL,
152 LOG(log_error, logtype_afpd, "Failed to contact server: %s\n",
153 avahi_strerror(error));
155 avahi_client_free (ctx->client);
156 avahi_threaded_poll_quit(ctx->threaded_poll);
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);
168 case AVAHI_CLIENT_S_REGISTERING:
170 case AVAHI_CLIENT_CONNECTING:
176 * Tries to setup the Zeroconf thread and any
177 * neccessary config setting.
179 void* av_zeroconf_setup(unsigned long port, const char *name) {
180 struct context *ctx = NULL;
182 /* default service name, if there's none in
185 char service[256] = "AFP Server on ";
188 /* initialize the struct that holds our
191 ctx = malloc(sizeof(struct context));
195 ctx->threaded_poll = NULL;
196 ctx->thread_running = 0;
198 LOG(log_debug, logtype_afpd, "Setting port for Zeroconf service to: %i.", port);
201 /* Prepare service 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);
208 ctx->name = strdup(name);
213 /* first of all we need to initialize our threading env */
214 if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
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,
224 LOG(log_error, logtype_afpd, "Failed to create client object: %s",
225 avahi_strerror(avahi_client_errno(ctx->client)));
233 av_zeroconf_unregister(ctx);
239 * This function finally runs the loop impl.
241 int av_zeroconf_run(void *u) {
242 struct context *ctx = u;
245 /* Finally, start the event loop thread */
246 if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
249 "Failed to create thread: %s",
250 avahi_strerror(avahi_client_errno(ctx->client)));
253 LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
256 ctx->thread_running = 1;
261 av_zeroconf_unregister(ctx);
267 * Tries to shutdown this loop impl.
268 * Call this function from outside this thread.
270 void av_zeroconf_shutdown(void *u) {
271 struct context *ctx = u;
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);
281 * Tries to shutdown this loop impl.
282 * Call this function from inside this thread.
284 int av_zeroconf_unregister(void *u) {
285 struct context *ctx = u;
287 if (ctx->thread_running) {
288 /* First, block the event loop */
289 avahi_threaded_poll_lock(ctx->threaded_poll);
291 /* Than, do your stuff */
292 avahi_threaded_poll_quit(ctx->threaded_poll);
294 /* Finally, unblock the event loop */
295 avahi_threaded_poll_unlock(ctx->threaded_poll);
296 ctx->thread_running = 0;
299 avahi_free(ctx->name);
302 avahi_client_free(ctx->client);
304 if (ctx->threaded_poll)
305 avahi_threaded_poll_free(ctx->threaded_poll);
312 #endif /* USE_AVAHI */