Intermediate update... consider this version completely broken
[handlervirt.git] / handler_avahi.c
blobe50e4cc83ae98322510a81027215bc195d918da1
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* Cherokee
5 * Authors:
6 * Alvaro Lopez Ortega <alvaro@alobbs.com>
7 * Stefan de Konink <stefan@konink.de>
9 * Copyright (C) 2001-2008 Alvaro Lopez Ortega
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
26 #include "handler_avahi.h"
27 #include <cherokee/cherokee.h>
29 #include <avahi-client/client.h>
30 #include <avahi-client/lookup.h>
32 #include <avahi-common/malloc.h>
33 #include <avahi-common/error.h>
35 /* Plug-in initialization
37 * In this function you can use any of these:
38 * http_delete | http_get | http_post | http_put
40 * For a full list: cherokee_http_method_t
42 * It is what your handler to be implements.
45 PLUGIN_INFO_HANDLER_EASIEST_INIT (avahi, http_get);
48 /* Methods implementation
51 ret_t
52 cherokee_handler_avahi_props_free (cherokee_handler_avahi_props_t *props)
54 if (props->threaded_poll)
55 avahi_threaded_poll_stop(props->threaded_poll);
57 if (props->sb)
58 avahi_service_browser_free(props->sb);
60 if (props->client)
61 avahi_client_free(props->client);
63 if (props->threaded_poll)
64 avahi_threaded_poll_free(props->threaded_poll);
66 cherokee_buffer_mrproper(&props->service_type);
68 cherokee_avl_mrproper (&props->entries, (cherokee_func_free_t) cherokee_buffer_mrproper);
70 return cherokee_module_props_free_base (MODULE_PROPS(props));
74 ret_t
75 cherokee_handler_avahi_configure (cherokee_config_node_t *conf, cherokee_server_t *srv, cherokee_module_props_t **_props)
77 cherokee_list_t *i;
78 cherokee_handler_avahi_props_t *props;
79 int error;
81 if (*_props == NULL) {
82 CHEROKEE_NEW_STRUCT (n, handler_avahi_props);
84 cherokee_module_props_init_base (MODULE_PROPS(n),
85 MODULE_PROPS_FREE(cherokee_handler_avahi_props_free));
87 /* Look at handler_avahi.h
88 * This is an avahi of configuration.
90 cherokee_buffer_init (&n->service_type);
92 *_props = MODULE_PROPS(n);
95 props = PROP_AVAHI(*_props);
97 cherokee_config_node_foreach (i, conf) {
98 cherokee_config_node_t *subconf = CONFIG_NODE(i);
100 if (equal_buf_str (&subconf->key, "service_type")) {
101 cherokee_buffer_add_buffer (&props->service_type, &subconf->val);
103 /* else {
104 PRINT_MSG ("ERROR: Handler file: Unknown key: '%s'\n", subconf->key.buf);
105 return ret_error;
109 /* If the configuration for avahi was not set, we should not continue */
110 if (props->service_type.len == 0) {
111 return ret_error;
114 props->client = NULL;
115 props->sb = NULL;
116 props->threaded_poll = NULL;
118 cherokee_avl_init(&props->entries);
120 if (!(props->threaded_poll = avahi_threaded_poll_new())) {
121 return ret_error;
124 if (!(props->client = avahi_client_new(avahi_threaded_poll_get(props->threaded_poll), 0, client_callback, props, &error))) {
125 return ret_error;
128 /* create some browsers on the client object here, if you wish */
129 if (!(props->sb = avahi_service_browser_new(props->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, props->service_type.buf, NULL, 0, browse_callback, props))) {
130 TRACE ("avahi", "Failed to create service browser: %s (%s)\n", avahi_strerror(avahi_client_errno(props->client)), props->service_type.buf);
131 return ret_error;
134 /* Finally, start the event loop thread */
135 if (avahi_threaded_poll_start(props->threaded_poll) < 0) {
136 return ret_error;
140 return ret_ok;
143 ret_t
144 cherokee_handler_avahi_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
146 ret_t ret;
148 CHEROKEE_NEW_STRUCT (n, handler_avahi);
150 /* Init the base class object
152 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(avahi));
154 MODULE(n)->init = (handler_func_init_t) cherokee_handler_avahi_init;
155 MODULE(n)->free = (module_func_free_t) cherokee_handler_avahi_free;
156 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_avahi_step;
157 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_avahi_add_headers;
159 HANDLER(n)->support = hsupport_length | hsupport_range;
161 /* Init
163 ret = cherokee_buffer_init (&n->buffer);
164 if (unlikely(ret != ret_ok))
165 return ret;
167 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
168 if (unlikely(ret != ret_ok))
169 return ret;
171 *hdl = HANDLER(n);
173 return ret_ok;
177 ret_t
178 cherokee_handler_avahi_free (cherokee_handler_avahi_t *hdl)
180 cherokee_buffer_mrproper (&hdl->buffer);
182 return ret_ok;
185 static ret_t
186 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
187 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
188 cherokee_buffer_t *buf2 = (cherokee_buffer_t *)value;
190 cherokee_buffer_add_buffer (buf, key);
191 cherokee_buffer_add_str (buf, " - ");
192 cherokee_buffer_add_buffer (buf, buf2);
193 // cherokee_buffer_add (buf, value, strlen(value));
194 cherokee_buffer_add_str (buf, "\n");
196 return ret_ok;
199 static void
200 avahi_build_page (cherokee_handler_avahi_t *hdl)
202 ret_t ret;
203 cherokee_buffer_t *buf;
205 /* Init
207 buf = &hdl->buffer;
209 /* Useful output
212 /* First, block the event loop */
213 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
215 /* Than, do your stuff */
216 cherokee_avl_while (&HDL_AVAHI_PROPS(hdl)->entries, (cherokee_avl_while_func_t) while_func_entries, (void *) buf, NULL, NULL);
218 /* Finally, unblock the event loop */
219 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
222 ret_t
223 cherokee_handler_avahi_init (cherokee_handler_avahi_t *hdl)
225 ret_t ret;
226 void *param;
227 cint_t web_interface = 1;
229 /* Build the page
231 if (web_interface) {
232 avahi_build_page (hdl);
235 hdl->action = send_page;
237 return ret_ok;
241 ret_t
242 cherokee_handler_avahi_step (cherokee_handler_avahi_t *hdl, cherokee_buffer_t *buffer)
244 cherokee_buffer_add_buffer (buffer, &hdl->buffer);
245 return ret_eof_have_data;
249 ret_t
250 cherokee_handler_avahi_add_headers (cherokee_handler_avahi_t *hdl, cherokee_buffer_t *buffer)
252 if (hdl->buffer.len == 0) {
253 cherokee_connection_t *conn = HANDLER_CONN(hdl);
254 conn->error_code = http_not_found;
257 cherokee_buffer_add_va (buffer, "Content-Length: %d"CRLF, hdl->buffer.len);
259 switch (hdl->action) {
260 case send_page:
261 default:
262 cherokee_buffer_add_str (buffer, "Content-Type: text/plain"CRLF);
263 break;
266 return ret_ok;
270 /* Avahi example stuff */
272 static void resolve_callback(
273 AvahiServiceResolver *r,
274 AVAHI_GCC_UNUSED AvahiIfIndex interface,
275 AVAHI_GCC_UNUSED AvahiProtocol protocol,
276 AvahiResolverEvent event,
277 const char *name,
278 const char *type,
279 const char *domain,
280 const char *host_name,
281 const AvahiAddress *address,
282 uint16_t port,
283 AvahiStringList *txt,
284 AvahiLookupResultFlags flags,
285 void* userdata) {
286 assert(r);
288 /* Called whenever a service has been resolved successfully or timed out */
290 switch (event) {
291 case AVAHI_RESOLVER_FAILURE:
292 TRACE("avahi", "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
293 break;
295 case AVAHI_RESOLVER_FOUND: {
296 char a[AVAHI_ADDRESS_STR_MAX], *t;
298 avahi_address_snprint(a, sizeof(a), address);
300 cherokee_buffer_t buf;
301 cherokee_buffer_t buf2;
302 cherokee_buffer_t *buf2a;
303 cherokee_buffer_init(&buf);
304 cherokee_buffer_init(&buf2);
305 cherokee_buffer_add(&buf, name, strlen(name));
306 // cherokee_buffer_add(&buf2, a, strlen(a));
307 cherokee_buffer_add (&buf2, host_name, strlen(host_name));
308 cherokee_buffer_dup(&buf2, &buf2a);
309 cherokee_avl_add(&PROP_AVAHI(userdata)->entries, &buf, buf2a);
310 cherokee_buffer_mrproper(&buf);
311 cherokee_buffer_mrproper(&buf2);
313 // cherokee_avl_add_ptr (&PROP_AVAHI(userdata)->entries, name, (void *) host_name);
315 TRACE("avahi", "Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
317 t = avahi_string_list_to_string(txt);
318 TRACE("avahi",
319 "\t%s:%u (%s)\n"
320 "\tTXT=%s\n"
321 "\tcookie is %u\n"
322 "\tis_local: %i\n"
323 "\tour_own: %i\n"
324 "\twide_area: %i\n"
325 "\tmulticast: %i\n"
326 "\tcached: %i\n",
327 host_name, port, a,
329 avahi_string_list_get_service_cookie(txt),
330 !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
331 !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN),
332 !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
333 !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
334 !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
336 avahi_free(t);
340 avahi_service_resolver_free(r);
343 static void browse_callback(
344 AvahiServiceBrowser *b,
345 AvahiIfIndex interface,
346 AvahiProtocol protocol,
347 AvahiBrowserEvent event,
348 const char *name,
349 const char *type,
350 const char *domain,
351 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
352 void* userdata) {
354 AvahiClient *c = PROP_AVAHI(userdata)->client;
355 assert(b);
357 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
359 switch (event) {
360 case AVAHI_BROWSER_FAILURE:
361 TRACE("avahi", "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
362 // avahi_threaded_poll_stop(PROP_AVAHI(userdata)->threaded_poll);
363 return;
365 case AVAHI_BROWSER_NEW:
366 TRACE("avahi", "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
368 /* We ignore the returned resolver object. In the callback
369 function we free it. If the server is terminated before
370 the callback function is called the server will free
371 the resolver for us. */
373 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, userdata)))
374 TRACE("avahi", "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c)));
376 break;
378 case AVAHI_BROWSER_REMOVE: {
379 void **val;
380 cherokee_buffer_t buf;
381 cherokee_buffer_init(&buf);
382 cherokee_buffer_add(&buf, name, strlen(name));
383 if (cherokee_avl_del(&PROP_AVAHI(userdata)->entries, &buf, val) == ret_ok) {
384 cherokee_buffer_mrproper((cherokee_buffer_t *) *val);
386 cherokee_buffer_mrproper(&buf);
388 // cherokee_avl_del_ptr(&PROP_AVAHI(userdata)->entries, name, val);
390 TRACE("avahi", "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
391 break;
394 case AVAHI_BROWSER_ALL_FOR_NOW:
395 case AVAHI_BROWSER_CACHE_EXHAUSTED:
396 TRACE("avahi", "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
397 break;
401 static void client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
402 assert(c);
404 /* Called whenever the client or server state changes */
406 if (state == AVAHI_CLIENT_FAILURE) {
407 TRACE("avahi", "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c)));
408 // avahi_threaded_poll_stop(PROP_AVAHI(userdata)->threaded_poll);
412 ret_t
413 cherokee_handler_avahi_props_init_base (cherokee_handler_avahi_props_t *props, module_func_props_free_t free_func)
415 cherokee_buffer_init (&props->service_type);
417 return cherokee_handler_props_init_base (HANDLER_PROPS(props), free_func);