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