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