]> git.meshlink.io Git - catta/blob - avahi-utils/avahi-browse.c
flush stdout after every line, this makes avahi-browse more useful in shell scripts...
[catta] / avahi-utils / avahi-browse.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <getopt.h>
29 #include <assert.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <net/if.h>
34 #include <locale.h>
35
36 #include <avahi-common/simple-watch.h>
37 #include <avahi-common/error.h>
38 #include <avahi-common/malloc.h>
39 #include <avahi-common/domain.h>
40 #include <avahi-common/llist.h>
41 #include <avahi-client/client.h>
42 #include <avahi-client/lookup.h>
43
44 #include "sigint.h"
45
46 #ifdef HAVE_GDBM
47 #include "stdb.h"
48 #endif
49
50 typedef enum {
51     COMMAND_HELP,
52     COMMAND_VERSION,
53     COMMAND_BROWSE_SERVICES,
54     COMMAND_BROWSE_ALL_SERVICES,
55     COMMAND_BROWSE_DOMAINS
56 } Command;
57
58 typedef struct Config {
59     int verbose;
60     int terminate_on_all_for_now;
61     int terminate_on_cache_exhausted;
62     char *domain;
63     char *stype;
64     int ignore_local;
65     Command command;
66     int resolve;
67     int no_fail;
68 #ifdef HAVE_GDBM
69     int no_db_lookup;
70 #endif
71 } Config;
72
73 typedef struct ServiceInfo ServiceInfo;
74
75 struct ServiceInfo {
76     AvahiIfIndex interface;
77     AvahiProtocol protocol;
78     char *name, *type, *domain;
79
80     AvahiServiceResolver *resolver;
81     Config *config;
82
83     AVAHI_LLIST_FIELDS(ServiceInfo, info);
84 };
85
86 static AvahiSimplePoll *simple_poll = NULL;
87 static AvahiClient *client = NULL;
88 static int n_all_for_now = 0, n_cache_exhausted = 0, n_resolving = 0;
89 static AvahiStringList *browsed_types = NULL;
90 static ServiceInfo *services = NULL;
91 static int n_columns = 80;
92 static int browsing = 0;
93
94 static void check_terminate(Config *c) {
95
96     assert(n_all_for_now >= 0);
97     assert(n_cache_exhausted >= 0);
98     assert(n_resolving >= 0);
99     
100     if (n_all_for_now <= 0 && n_resolving <= 0) {
101
102         if (c->verbose) {
103             printf(": All for now\n");
104             n_all_for_now++; /* Make sure that this event is not repeated */
105         }
106         
107         if (c->terminate_on_all_for_now)
108             avahi_simple_poll_quit(simple_poll);
109     }
110     
111     if (n_cache_exhausted <= 0 && n_resolving <= 0) {
112
113         if (c->verbose) {
114             printf(": Cache exhausted\n");
115             n_cache_exhausted++; /* Make sure that this event is not repeated */
116         }
117         
118         if (c->terminate_on_cache_exhausted)
119             avahi_simple_poll_quit(simple_poll);
120     }
121 }
122
123 static ServiceInfo *find_service(AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
124     ServiceInfo *i;
125
126     for (i = services; i; i = i->info_next)
127         if (i->interface == interface &&
128             i->protocol == protocol &&
129             strcasecmp(i->name, name) == 0 &&
130             avahi_domain_equal(i->type, type) &&
131             avahi_domain_equal(i->domain, domain))
132
133             return i;
134
135     return NULL;
136 }
137
138 static void print_service_line(Config *config, char c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
139     char ifname[IF_NAMESIZE];
140
141 #ifdef HAVE_GDBM
142     if (!config->no_db_lookup)
143         type = stdb_lookup(type);
144 #endif
145     
146     printf("%c %4s %4s %-*s %-20s %s\n",
147            c,
148            interface != AVAHI_IF_UNSPEC ? if_indextoname(interface, ifname) : "n/a",
149            protocol != AVAHI_PROTO_UNSPEC ? avahi_proto_to_string(protocol) : "n/a", 
150            n_columns-35, name, type, domain);
151     fflush(stdout);
152 }
153
154 static void service_resolver_callback(
155     AvahiServiceResolver *r,
156     AvahiIfIndex interface,
157     AvahiProtocol protocol,
158     AvahiResolverEvent event,
159     const char *name,
160     const char *type,
161     const char *domain,
162     const char *host_name,
163     const AvahiAddress *a,
164     uint16_t port,
165     AvahiStringList *txt,
166     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
167     void *userdata) {
168     
169     ServiceInfo *i = userdata;
170     
171     assert(r);
172     assert(i);
173
174     switch (event) {
175         case AVAHI_RESOLVER_FOUND: {
176             char address[AVAHI_ADDRESS_STR_MAX], *t;
177
178             avahi_address_snprint(address, sizeof(address), a);
179
180             t = avahi_string_list_to_string(txt);
181
182             print_service_line(i->config, '=', interface, protocol, name, type, domain);
183             
184             printf("   hostname = [%s]\n"
185                    "   address = [%s]\n"
186                    "   port = [%i]\n"
187                    "   txt = [%s]\n",
188                    host_name,
189                    address,
190                    port,
191                    t);
192             avahi_free(t);
193
194             break;
195         }
196
197         case AVAHI_RESOLVER_FAILURE:
198             
199             fprintf(stderr, "Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(client)));
200             break;
201     }
202
203     
204     avahi_service_resolver_free(i->resolver);
205     i->resolver = NULL;
206
207     assert(n_resolving > 0);
208     n_resolving--;
209     check_terminate(i->config);
210     fflush(stdout);
211 }
212
213 static ServiceInfo *add_service(Config *c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
214     ServiceInfo *i;
215
216     i = avahi_new(ServiceInfo, 1);
217
218     if (c->resolve) {
219         if (!(i->resolver = avahi_service_resolver_new(client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, service_resolver_callback, i))) {
220             avahi_free(i);
221             fprintf(stderr, "Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(client)));
222             return NULL;
223         }
224
225         n_resolving++;
226     } else
227         i->resolver = NULL;
228
229     i->interface = interface;
230     i->protocol = protocol;
231     i->name = avahi_strdup(name);
232     i->type = avahi_strdup(type);
233     i->domain = avahi_strdup(domain);
234     i->config = c;
235
236     AVAHI_LLIST_PREPEND(ServiceInfo, info, services, i);
237
238     return i;
239 }
240
241 static void remove_service(Config *c, ServiceInfo *i) {
242     assert(c);
243     assert(i);
244
245     AVAHI_LLIST_REMOVE(ServiceInfo, info, services, i);
246
247     if (i->resolver)
248         avahi_service_resolver_free(i->resolver);
249     
250     avahi_free(i->name);
251     avahi_free(i->type);
252     avahi_free(i->domain);
253     avahi_free(i);
254 }
255
256 static void service_browser_callback(
257     AvahiServiceBrowser *b,
258     AvahiIfIndex interface,
259     AvahiProtocol protocol,
260     AvahiBrowserEvent event,
261     const char *name,
262     const char *type,
263     const char *domain,
264     AvahiLookupResultFlags flags,
265     void *userdata) {
266
267     Config *c = userdata;
268     
269     assert(b);
270     assert(c);
271
272     switch (event) {
273         case AVAHI_BROWSER_NEW: {
274             if (c->ignore_local && (flags & AVAHI_LOOKUP_RESULT_LOCAL))
275                 break;
276
277             if (find_service(interface, protocol, name, type, domain))
278                 return;
279
280             add_service(c, interface, protocol, name, type, domain);
281
282             print_service_line(c, '+', interface, protocol, name, type, domain);
283             break;
284
285         }
286
287         case AVAHI_BROWSER_REMOVE: {
288             ServiceInfo *info;
289             
290             if (!(info = find_service(interface, protocol, name, type, domain)))
291                 return;
292
293             remove_service(c, info);
294             
295             print_service_line(c, '-', interface, protocol, name, type, domain);
296             break;
297         }
298             
299         case AVAHI_BROWSER_FAILURE:
300             fprintf(stderr, "service_browser failed: %s\n", avahi_strerror(avahi_client_errno(client)));
301             avahi_simple_poll_quit(simple_poll);
302             break;
303
304         case AVAHI_BROWSER_CACHE_EXHAUSTED:
305             n_cache_exhausted --;
306             check_terminate(c);
307             break;
308             
309         case AVAHI_BROWSER_ALL_FOR_NOW:
310             n_all_for_now --;
311             check_terminate(c);
312             break;
313     }
314 }
315
316 static void browse_service_type(Config *c, const char *stype, const char *domain) {
317     AvahiServiceBrowser *b;
318     AvahiStringList *i;
319     
320     assert(c);
321     assert(client);
322     assert(stype);
323
324     for (i = browsed_types; i; i = i->next)
325         if (avahi_domain_equal(stype, (char*) i->text))
326             return;
327
328     if (!(b = avahi_service_browser_new(
329               client,
330               AVAHI_IF_UNSPEC,
331               AVAHI_PROTO_UNSPEC,
332               stype,
333               domain,
334               0,
335               service_browser_callback,
336               c))) {
337
338         fprintf(stderr, "avahi_service_browser_new() failed: %s\n", avahi_strerror(avahi_client_errno(client)));
339         avahi_simple_poll_quit(simple_poll);
340     }
341
342     browsed_types = avahi_string_list_add(browsed_types, stype);
343
344     n_all_for_now++;
345     n_cache_exhausted++;
346 }
347
348 static void service_type_browser_callback(
349     AvahiServiceTypeBrowser *b,
350     AVAHI_GCC_UNUSED AvahiIfIndex interface,
351     AVAHI_GCC_UNUSED AvahiProtocol protocol,
352     AvahiBrowserEvent event,
353     const char *type,
354     const char *domain,
355     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
356     void *userdata) {
357
358     Config *c = userdata;
359
360     assert(b);
361     assert(c);
362     
363     switch (event) {
364         
365         case AVAHI_BROWSER_NEW:
366             browse_service_type(c, type, domain);
367             break;
368
369         case AVAHI_BROWSER_REMOVE:
370             /* We're dirty and never remove the browser again */
371             break;
372
373         case AVAHI_BROWSER_FAILURE:
374             fprintf(stderr, "service_type_browser failed: %s\n", avahi_strerror(avahi_client_errno(client)));
375             avahi_simple_poll_quit(simple_poll);
376             break;
377             
378         case AVAHI_BROWSER_CACHE_EXHAUSTED:
379             n_cache_exhausted --;
380             check_terminate(c);
381             break;
382             
383         case AVAHI_BROWSER_ALL_FOR_NOW:
384             n_all_for_now --;
385             check_terminate(c);
386             break;
387     }
388 }
389
390 static void browse_all(Config *c) {
391     AvahiServiceTypeBrowser *b;
392     
393     assert(c);
394
395     if (!(b = avahi_service_type_browser_new(
396               client,
397               AVAHI_IF_UNSPEC,
398               AVAHI_PROTO_UNSPEC,
399               c->domain,
400               0,
401               service_type_browser_callback,
402               c))) {
403         
404         fprintf(stderr, "avahi_service_type_browser_new() failed: %s\n", avahi_strerror(avahi_client_errno(client)));
405         avahi_simple_poll_quit(simple_poll);
406     }
407
408     n_cache_exhausted++;
409     n_all_for_now++;
410 }
411
412 static void domain_browser_callback(
413     AvahiDomainBrowser *b,
414     AVAHI_GCC_UNUSED AvahiIfIndex interface,
415     AVAHI_GCC_UNUSED AvahiProtocol protocol,
416     AvahiBrowserEvent event,
417     const char *domain,
418     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
419     void *userdata) {
420
421     Config *c = userdata;
422
423     assert(b);
424     assert(c);
425     
426     switch (event) {
427         
428         case AVAHI_BROWSER_NEW:
429         case AVAHI_BROWSER_REMOVE: {
430             char ifname[IF_NAMESIZE];
431             
432             printf("%c %4s %4s %s\n",
433                    event == AVAHI_BROWSER_NEW ? '+' : '-',
434                    interface != AVAHI_IF_UNSPEC ? if_indextoname(interface, ifname) : "n/a",
435                    protocol != AVAHI_PROTO_UNSPEC ? avahi_proto_to_string(protocol) : "n/a", 
436                    domain);
437             break;
438         }
439
440         case AVAHI_BROWSER_FAILURE:
441             fprintf(stderr, "domain_browser failed: %s\n", avahi_strerror(avahi_client_errno(client)));
442             avahi_simple_poll_quit(simple_poll);
443             break;
444             
445         case AVAHI_BROWSER_CACHE_EXHAUSTED:
446             n_cache_exhausted --;
447             check_terminate(c);
448             break;
449             
450         case AVAHI_BROWSER_ALL_FOR_NOW:
451             n_all_for_now --;
452             check_terminate(c);
453             break;
454     }
455 }
456
457 static void browse_domains(Config *c) {
458     AvahiDomainBrowser *b;
459
460     assert(c);
461
462     if (!(b = avahi_domain_browser_new(
463               client,
464               AVAHI_IF_UNSPEC,
465               AVAHI_PROTO_UNSPEC,
466               c->domain,
467               AVAHI_DOMAIN_BROWSER_BROWSE,
468               0,
469               domain_browser_callback,
470               c))) {
471
472         fprintf(stderr, "avahi_domain_browser_new() failed: %s\n", avahi_strerror(avahi_client_errno(client)));
473         avahi_simple_poll_quit(simple_poll);
474     }
475
476     n_cache_exhausted++;
477     n_all_for_now++;
478 }
479
480 static int start(Config *config) {
481
482     assert(!browsing);
483     
484     if (config->verbose) {
485         const char *version, *hn;
486         
487         if (!(version = avahi_client_get_version_string(client))) {
488             fprintf(stderr, "Failed to query version string: %s\n", avahi_strerror(avahi_client_errno(client)));
489             return -1;
490         }
491         
492         if (!(hn = avahi_client_get_host_name_fqdn(client))) {
493             fprintf(stderr, "Failed to query host name: %s\n", avahi_strerror(avahi_client_errno(client)));
494             return -1;
495         }
496         
497         fprintf(stderr, "Server version: %s; Host name: %s\n", version, hn);
498         
499         if (config->command == COMMAND_BROWSE_DOMAINS)
500             fprintf(stderr, "E Ifce Prot Domain\n");
501         else
502             fprintf(stderr, "E Ifce Prot %-*s %-20s Domain\n", n_columns-35, "Name", "Type");
503     }
504     
505     if (config->command == COMMAND_BROWSE_SERVICES)
506         browse_service_type(config, config->stype, config->domain);
507     else if (config->command == COMMAND_BROWSE_ALL_SERVICES)
508         browse_all(config);
509     else {
510         assert(config->command == COMMAND_BROWSE_DOMAINS);
511         browse_domains(config);
512     }
513
514     browsing = 1;
515     return 0;
516 }
517     
518 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
519     Config *config = userdata;
520
521     /* This function might be called when avahi_client_new() has not
522      * returned yet.*/
523     client = c;
524     
525     switch (state) {
526         case AVAHI_CLIENT_FAILURE:
527             
528             if (config->no_fail && avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
529                 int error;
530
531                 /* We have been disconnected, so let reconnect */
532
533                 fprintf(stderr, "Disconnected, reconnecting ...\n");
534
535                 avahi_client_free(client);
536                 client = NULL;
537
538                 avahi_string_list_free(browsed_types);
539                 browsed_types = NULL;
540                 
541                 while (services)
542                     remove_service(config, services);
543
544                 browsing = 0;
545
546                 if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL, client_callback, config, &error))) {
547                     fprintf(stderr, "Failed to create client object: %s\n", avahi_strerror(error));
548                     avahi_simple_poll_quit(simple_poll);
549                 }
550
551             } else {
552                 fprintf(stderr, "Client failure, exiting: %s\n", avahi_strerror(avahi_client_errno(c)));
553                 avahi_simple_poll_quit(simple_poll);
554             }
555             
556             break;
557             
558         case AVAHI_CLIENT_S_REGISTERING:
559         case AVAHI_CLIENT_S_RUNNING:
560         case AVAHI_CLIENT_S_COLLISION:
561
562             if (!browsing)
563                 if (start(config) < 0)
564                     avahi_simple_poll_quit(simple_poll);
565
566             break;
567             
568         case AVAHI_CLIENT_CONNECTING:
569             
570             if (config->verbose)
571                 fprintf(stderr, "Waiting for daemon ...\n");
572
573             break;
574     }
575 }
576
577 static void help(FILE *f, const char *argv0) {
578     if (strstr(argv0, "domain"))
579         fprintf(f, "%s [options] \n\n", argv0);
580     else
581         fprintf(f,
582                 "%s [options] <service type>\n"
583                 "%s [options] -a\n"
584                 "%s [options] -D\n\n",
585                 argv0, argv0, argv0);
586
587             fprintf(f, 
588             "    -h --help            Show this help\n"
589             "    -V --version         Show version\n"
590             "    -D --browse-domains  Browse for browsing domains instead of services\n"
591             "    -a --all             Show all services, regardless of the type\n"
592             "    -d --domain=DOMAIN   The domain to browse in\n"
593             "    -v --verbose         Enable verbose mode\n"
594             "    -t --terminate       Terminate after dumping a more or less complete list\n"
595             "    -c --cache           Terminate after dumping all entries from the cache\n"
596             "    -l --ignore-local    Ignore local services\n"
597             "    -r --resolve         Resolve services found\n"
598             "    -f --no-fail         Don't fail if the daemon is not available\n"
599 #ifdef HAVE_GDBM
600             "    -k --no-db-lookup    Don't lookup service types\n"
601 #endif
602             );
603 }
604
605 static int parse_command_line(Config *c, const char *argv0, int argc, char *argv[]) {
606     int o;
607
608     static const struct option long_options[] = {
609         { "help",           no_argument,       NULL, 'h' },
610         { "version",        no_argument,       NULL, 'V' },
611         { "browse-domains", no_argument,       NULL, 'D' },
612         { "domain",         required_argument, NULL, 'd' },
613         { "all",            no_argument,       NULL, 'a' },
614         { "verbose",        no_argument,       NULL, 'v' },
615         { "terminate",      no_argument,       NULL, 't' },
616         { "cache",          no_argument,       NULL, 'c' },
617         { "ignore-local",   no_argument,       NULL, 'l' },
618         { "resolve",        no_argument,       NULL, 'r' },
619         { "no-fail",        no_argument,       NULL, 'f' },
620 #ifdef HAVE_GDBM
621         { "no-db-lookup",   no_argument,       NULL, 'k' },
622 #endif
623         { NULL, 0, NULL, 0 }
624     };
625
626     assert(c);
627
628     c->command = strstr(argv0, "domain") ? COMMAND_BROWSE_DOMAINS : COMMAND_BROWSE_SERVICES;
629     c->verbose =
630         c->terminate_on_cache_exhausted =
631         c->terminate_on_all_for_now =
632         c->ignore_local =
633         c->resolve =
634         c->no_fail = 0;
635     c->domain = c->stype = NULL;
636
637 #ifdef HAVE_GDBM
638     c->no_db_lookup = 0;
639 #endif
640     
641     opterr = 0;
642     while ((o = getopt_long(argc, argv, "hVd:avtclrDf"
643 #ifdef HAVE_GDBM
644                             "k"
645 #endif
646                             , long_options, NULL)) >= 0) {
647
648         switch(o) {
649             case 'h':
650                 c->command = COMMAND_HELP;
651                 break;
652             case 'V':
653                 c->command = COMMAND_VERSION;
654                 break;
655             case 'a':
656                 c->command = COMMAND_BROWSE_ALL_SERVICES;
657                 break;
658             case 'D':
659                 c->command = COMMAND_BROWSE_DOMAINS;
660                 break;
661             case 'd':
662                 avahi_free(c->domain);
663                 c->domain = avahi_strdup(optarg);
664                 break;
665             case 'v':
666                 c->verbose = 1;
667                 break;
668             case 't':
669                 c->terminate_on_all_for_now = 1;
670                 break;
671             case 'c':
672                 c->terminate_on_cache_exhausted = 1;
673                 break;
674             case 'l':
675                 c->ignore_local = 1;
676                 break;
677             case 'r':
678                 c->resolve = 1;
679                 break;
680             case 'f':
681                 c->no_fail = 1;
682                 break;
683 #ifdef HAVE_GDBM
684             case 'k':
685                 c->no_db_lookup = 1;
686                 break;
687 #endif
688             default:
689                 fprintf(stderr, "Invalid command line argument: %c\n", o);
690                 return -1;
691         }
692     }
693
694     if (c->command == COMMAND_BROWSE_SERVICES) {
695         if (optind >= argc) {
696             fprintf(stderr, "Too few arguments\n");
697             return -1;
698         }
699
700         c->stype = avahi_strdup(argv[optind]);
701         optind++;
702     }
703     
704     if (optind < argc) {
705         fprintf(stderr, "Too many arguments\n");
706         return -1;
707     }
708         
709     return 0;
710 }
711
712 int main(int argc, char *argv[]) {
713     int ret = 1, error;
714     Config config;
715     const char *argv0;
716     char *ec;
717
718     setlocale(LC_ALL, "");
719
720     if ((argv0 = strrchr(argv[0], '/')))
721         argv0++;
722     else
723         argv0 = argv[0];
724
725     if ((ec = getenv("COLUMNS")))
726         n_columns = atoi(ec);
727
728     if (n_columns < 40)
729         n_columns = 40;
730     
731     if (parse_command_line(&config, argv0, argc, argv) < 0)
732         goto fail;
733
734     switch (config.command) {
735         case COMMAND_HELP:
736             help(stdout, argv0);
737             ret = 0;
738             break;
739             
740         case COMMAND_VERSION:
741             printf("%s "PACKAGE_VERSION"\n", argv0);
742             ret = 0;
743             break;
744
745         case COMMAND_BROWSE_SERVICES:
746         case COMMAND_BROWSE_ALL_SERVICES:
747         case COMMAND_BROWSE_DOMAINS:
748             
749             if (!(simple_poll = avahi_simple_poll_new())) {
750                 fprintf(stderr, "Failed to create simple poll object.\n");
751                 goto fail;
752             }
753             
754             if (sigint_install(simple_poll) < 0)
755                 goto fail;
756             
757             if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), config.no_fail ? AVAHI_CLIENT_NO_FAIL : 0, client_callback, &config, &error))) {
758                 fprintf(stderr, "Failed to create client object: %s\n", avahi_strerror(error));
759                 goto fail;
760             }
761             
762             avahi_simple_poll_loop(simple_poll);
763             ret = 0;
764             break;
765     }
766     
767     
768 fail:
769
770     while (services)
771         remove_service(&config, services);
772
773     if (client)
774         avahi_client_free(client);
775
776     sigint_uninstall();
777     
778     if (simple_poll)
779         avahi_simple_poll_free(simple_poll);
780
781     avahi_free(config.domain);
782     avahi_free(config.stype);
783
784     avahi_string_list_free(browsed_types);
785
786 #ifdef HAVE_GDBM
787     stdb_shutdown();
788 #endif    
789
790     return ret;
791 }