]> git.meshlink.io Git - catta/blob - avahi-utils/avahi-browse.c
dcf3e473e90ecd9d8c76ba7819ad58f917afc5c2
[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_RUN
54 } Command;
55
56 typedef struct Config {
57     int verbose;
58     int terminate_on_all_for_now;
59     int terminate_on_cache_exhausted;
60     char *domain;
61     char *stype;
62     int ignore_local;
63     int show_all;
64     Command command;
65     int resolve;
66 #ifdef HAVE_GDBM
67     int no_db_lookup;
68 #endif
69 } Config;
70
71 typedef struct ServiceInfo ServiceInfo;
72
73 struct ServiceInfo {
74     AvahiIfIndex interface;
75     AvahiProtocol protocol;
76     char *name, *type, *domain;
77
78     AvahiServiceResolver *resolver;
79     Config *config;
80
81     AVAHI_LLIST_FIELDS(ServiceInfo, info);
82 };
83
84 static AvahiSimplePoll *simple_poll = NULL;
85 static AvahiClient *client = NULL;
86 static int n_all_for_now = 0, n_cache_exhausted = 0, n_resolving = 0;
87 static AvahiStringList *browsed_types = NULL;
88 static ServiceInfo *services = NULL;
89 static int n_columns = 80;
90
91 static void check_terminate(Config *c) {
92
93     assert(n_all_for_now >= 0);
94     assert(n_cache_exhausted >= 0);
95     assert(n_resolving >= 0);
96     
97     if (n_all_for_now <= 0 && n_resolving <= 0) {
98
99         if (c->verbose) {
100             printf(": All for now\n");
101             n_all_for_now++; /* Make sure that this event is not repeated */
102         }
103         
104         if (c->terminate_on_all_for_now)
105             avahi_simple_poll_quit(simple_poll);
106     }
107     
108     if (n_cache_exhausted <= 0 && n_resolving <= 0) {
109
110         if (c->verbose) {
111             printf(": Cache exhausted\n");
112             n_cache_exhausted++; /* Make sure that this event is not repeated */
113         }
114         
115         if (c->terminate_on_cache_exhausted)
116             avahi_simple_poll_quit(simple_poll);
117     }
118 }
119
120 static ServiceInfo *find_service(AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
121     ServiceInfo *i;
122
123     for (i = services; i; i = i->info_next)
124         if (i->interface == interface &&
125             i->protocol == protocol &&
126             strcasecmp(i->name, name) == 0 &&
127             avahi_domain_equal(i->type, type) == 0 &&
128             avahi_domain_equal(i->domain, domain) == 0)
129
130             return i;
131
132     return NULL;
133 }
134
135 static void print_service_line(Config *config, char c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
136     char ifname[IF_NAMESIZE];
137
138 #ifdef HAVE_GDBM
139     if (!config->no_db_lookup)
140         type = stdb_lookup(type);
141 #endif
142     
143     printf("%c %4s %4s %-*s %-20s %s\n",
144            c,
145            if_indextoname(interface, ifname), avahi_proto_to_string(protocol), 
146            n_columns-35, name, type, domain);
147 }
148
149 static void service_resolver_callback(
150     AvahiServiceResolver *r,
151     AvahiIfIndex interface,
152     AvahiProtocol protocol,
153     AvahiResolverEvent event,
154     const char *name,
155     const char *type,
156     const char *domain,
157     const char *host_name,
158     const AvahiAddress *a,
159     uint16_t port,
160     AvahiStringList *txt,
161     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
162     void *userdata) {
163     
164     ServiceInfo *i = userdata;
165     
166     assert(r);
167     assert(i);
168
169     switch (event) {
170         case AVAHI_RESOLVER_FOUND: {
171             char address[AVAHI_ADDRESS_STR_MAX], *t;
172
173             avahi_address_snprint(address, sizeof(address), a);
174
175             t = avahi_string_list_to_string(txt);
176
177             print_service_line(i->config, '=', interface, protocol, name, type, domain);
178             
179             printf("   hostname = [%s]\n"
180                    "   address = [%s]\n"
181                    "   port = [%i]\n"
182                    "   txt = [%s]\n",
183                    host_name,
184                    address,
185                    port,
186                    t);
187             avahi_free(t);
188
189             break;
190         }
191
192         case AVAHI_RESOLVER_FAILURE:
193             
194             fprintf(stderr, "Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(client)));
195             break;
196     }
197
198     
199     avahi_service_resolver_free(i->resolver);
200     i->resolver = NULL;
201
202     assert(n_resolving > 0);
203     n_resolving--;
204     check_terminate(i->config);
205 }
206
207 static ServiceInfo *add_service(Config *c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
208     ServiceInfo *i;
209
210     i = avahi_new(ServiceInfo, 1);
211
212     if (c->resolve) {
213         if (!(i->resolver = avahi_service_resolver_new(client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, service_resolver_callback, i))) {
214             avahi_free(i);
215             fprintf(stderr, "Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(client)));
216             return NULL;
217         }
218
219         n_resolving++;
220     } else
221         i->resolver = NULL;
222
223     i->interface = interface;
224     i->protocol = protocol;
225     i->name = avahi_strdup(name);
226     i->type = avahi_strdup(type);
227     i->domain = avahi_strdup(domain);
228     i->config = c;
229
230     AVAHI_LLIST_PREPEND(ServiceInfo, info, services, i);
231
232     return i;
233 }
234
235 static void remove_service(Config *c, ServiceInfo *i) {
236     assert(c);
237     assert(i);
238
239     AVAHI_LLIST_REMOVE(ServiceInfo, info, services, i);
240
241     if (i->resolver)
242         avahi_service_resolver_free(i->resolver);
243     
244     avahi_free(i->name);
245     avahi_free(i->type);
246     avahi_free(i->domain);
247     avahi_free(i);
248 }
249
250 static void service_browser_callback(
251     AvahiServiceBrowser *b,
252     AvahiIfIndex interface,
253     AvahiProtocol protocol,
254     AvahiBrowserEvent event,
255     const char *name,
256     const char *type,
257     const char *domain,
258     AvahiLookupResultFlags flags,
259     void *userdata) {
260
261     Config *c = userdata;
262     
263     assert(b);
264     assert(c);
265
266     switch (event) {
267         case AVAHI_BROWSER_NEW: {
268             if (c->ignore_local && (flags & AVAHI_LOOKUP_RESULT_LOCAL))
269                 break;
270
271             if (find_service(interface, protocol, name, type, domain))
272                 return;
273
274             add_service(c, interface, protocol, name, type, domain);
275
276             print_service_line(c, '+', interface, protocol, name, type, domain);
277             break;
278
279         }
280
281         case AVAHI_BROWSER_REMOVE: {
282             ServiceInfo *info;
283             
284             if (!(info = find_service(interface, protocol, name, type, domain)))
285                 return;
286
287             remove_service(c, info);
288             
289             print_service_line(c, '-', interface, protocol, name, type, domain);
290             break;
291         }
292             
293         case AVAHI_BROWSER_FAILURE:
294             fprintf(stderr, "service_browser failed: %s\n", avahi_strerror(avahi_client_errno(client)));
295             avahi_simple_poll_quit(simple_poll);
296             break;
297
298         case AVAHI_BROWSER_CACHE_EXHAUSTED:
299             n_cache_exhausted --;
300             check_terminate(c);
301             break;
302             
303         case AVAHI_BROWSER_ALL_FOR_NOW:
304             n_all_for_now --;
305             check_terminate(c);
306             break;
307     }
308 }
309
310 static void browse_service_type(Config *c, const char *stype, const char *domain) {
311     AvahiServiceBrowser *b;
312     AvahiStringList *i;
313     
314     assert(c);
315     assert(client);
316     assert(stype);
317
318     for (i = browsed_types; i; i = i->next)
319         if (avahi_domain_equal(stype, (char*) i->text))
320             return;
321
322     if (!(b = avahi_service_browser_new(
323               client,
324               AVAHI_IF_UNSPEC,
325               AVAHI_PROTO_UNSPEC,
326               stype,
327               domain,
328               0,
329               service_browser_callback,
330               c))) {
331
332         fprintf(stderr, "avahi_service_browser_new() failed: %s\n", avahi_strerror(avahi_client_errno(client)));
333         avahi_simple_poll_quit(simple_poll);
334     }
335
336     browsed_types = avahi_string_list_add(browsed_types, stype);
337
338     n_all_for_now++;
339     n_cache_exhausted++;
340 }
341
342 static void service_type_browser_callback(
343     AvahiServiceTypeBrowser *b,
344     AVAHI_GCC_UNUSED AvahiIfIndex interface,
345     AVAHI_GCC_UNUSED AvahiProtocol protocol,
346     AvahiBrowserEvent event,
347     const char *type,
348     const char *domain,
349     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
350     void *userdata) {
351
352     Config *c = userdata;
353
354     assert(b);
355     assert(c);
356     
357     switch (event) {
358         
359         case AVAHI_BROWSER_NEW:
360             browse_service_type(c, type, domain);
361             break;
362
363         case AVAHI_BROWSER_REMOVE:
364             /* We're dirty and never remove the browser again */
365             break;
366
367         case AVAHI_BROWSER_FAILURE:
368             fprintf(stderr, "service_type_browser failed: %s\n", avahi_strerror(avahi_client_errno(client)));
369             avahi_simple_poll_quit(simple_poll);
370             break;
371             
372         case AVAHI_BROWSER_CACHE_EXHAUSTED:
373             n_cache_exhausted --;
374             check_terminate(c);
375             break;
376             
377         case AVAHI_BROWSER_ALL_FOR_NOW:
378             n_all_for_now --;
379             check_terminate(c);
380             break;
381     }
382 }
383
384 static void browse_all(Config *c) {
385     AvahiServiceTypeBrowser *b;
386     
387     assert(c);
388
389     if (!(b = avahi_service_type_browser_new(
390               client,
391               AVAHI_IF_UNSPEC,
392               AVAHI_PROTO_UNSPEC,
393               c->domain,
394               0,
395               service_type_browser_callback,
396               c))) {
397         
398         fprintf(stderr, "avahi_service_type_browser_new() failed: %s\n", avahi_strerror(avahi_client_errno(client)));
399         avahi_simple_poll_quit(simple_poll);
400     }
401
402     n_cache_exhausted++;
403     n_all_for_now++;
404 }
405
406 static void client_callback(AVAHI_GCC_UNUSED AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
407     switch (state) {
408         case AVAHI_CLIENT_DISCONNECTED:
409             fprintf(stderr, "Client disconnected, exiting.\n");
410             avahi_simple_poll_quit(simple_poll);
411             break;
412
413         case AVAHI_CLIENT_S_REGISTERING:
414         case AVAHI_CLIENT_S_RUNNING:
415         case AVAHI_CLIENT_S_COLLISION:
416             ;
417     }
418 }
419
420 static void help(FILE *f, const char *argv0) {
421     fprintf(f,
422             "%s [options] <type>\n"
423             "%s [options] -a\n\n"
424             "    -h --help          Show this help\n"
425             "    -V --version       Show version\n"
426             "    -d --domain=DOMAIN The domain to browse\n"
427             "    -a --all           Show all services, regardless of the type\n"
428             "    -v --verbose       Enable verbose mode\n"
429             "    -t --terminate     Terminate after getting or more or less complete list\n"
430             "    -c --cache         Terminate after dumping all entries from the cache\n"
431             "    -l --ignore-local  Ignore local services\n"
432             "    -r --resolve       Resolve services found\n"
433 #ifdef HAVE_GDBM
434             "    -k --no-db-lookup  Don't lookup service types\n"
435 #endif
436
437             , argv0, argv0);
438 }
439
440 static int parse_command_line(Config *c, int argc, char *argv[]) {
441     int o;
442
443     static const struct option long_options[] = {
444         { "help",         no_argument,       NULL, 'h' },
445         { "version",      no_argument,       NULL, 'V' },
446         { "domain",       required_argument, NULL, 'd' },
447         { "all",          no_argument,       NULL, 'a' },
448         { "verbose",      no_argument,       NULL, 'v' },
449         { "terminate",    no_argument,       NULL, 't' },
450         { "cache",        no_argument,       NULL, 'c' },
451         { "ignore-local", no_argument,       NULL, 'l' },
452         { "resolve",      no_argument,       NULL, 'r' },
453 #ifdef HAVE_GDBM
454         { "no-db-lookup", no_argument,       NULL, 'k' },
455 #endif
456         { NULL, 0, NULL, 0 }
457     };
458
459     assert(c);
460
461     c->command = COMMAND_RUN;
462     c->verbose =
463         c->terminate_on_cache_exhausted =
464         c->terminate_on_all_for_now =
465         c->show_all =
466         c->ignore_local =
467         c->resolve = 0;
468     c->domain = c->stype = NULL;
469
470 #ifdef HAVE_GDBM
471     c->no_db_lookup = 0;
472 #endif
473     
474     opterr = 0;
475     while ((o = getopt_long(argc, argv, "hVd:avtclr"
476 #ifdef HAVE_GDBM
477                             "k"
478 #endif
479                             , long_options, NULL)) >= 0) {
480
481         switch(o) {
482             case 'h':
483                 c->command = COMMAND_HELP;
484                 break;
485             case 'V':
486                 c->command = COMMAND_VERSION;
487                 break;
488             case 'd':
489                 c->domain = avahi_strdup(optarg);
490                 break;
491             case 'a':
492                 c->show_all = 1;
493                 break;
494             case 'v':
495                 c->verbose = 1;
496                 break;
497             case 't':
498                 c->terminate_on_all_for_now = 1;
499                 break;
500             case 'c':
501                 c->terminate_on_cache_exhausted = 1;
502                 break;
503             case 'l':
504                 c->ignore_local = 1;
505                 break;
506             case 'r':
507                 c->resolve = 1;
508                 break;
509 #ifdef HAVE_GDBM
510             case 'k':
511                 c->no_db_lookup = 1;
512                 break;
513 #endif
514             default:
515                 fprintf(stderr, "Invalid command line argument: %c\n", o);
516                 return -1;
517         }
518     }
519
520     if (c->command == COMMAND_RUN && !c->show_all) {
521         if (optind >= argc) {
522             fprintf(stderr, "Too few arguments\n");
523             return -1;
524         }
525
526         c->stype = avahi_strdup(argv[optind]);
527         optind++;
528     }
529     
530     if (optind < argc) {
531         fprintf(stderr, "Too many arguments\n");
532         return -1;
533     }
534         
535     return 0;
536 }
537
538 int main(int argc, char *argv[]) {
539     int ret = 1, error;
540     Config config;
541     const char *argv0;
542     char *ec;
543
544     setlocale(LC_ALL, "");
545
546     if ((argv0 = strrchr(argv[0], '/')))
547         argv0++;
548     else
549         argv0 = argv[0];
550
551     if ((ec = getenv("COLUMNS")))
552         n_columns = atoi(ec);
553
554     if (n_columns < 40)
555         n_columns = 40;
556     
557     if (parse_command_line(&config, argc, argv) < 0)
558         goto fail;
559
560     switch (config.command) {
561         case COMMAND_HELP:
562             help(stdout, argv0);
563             ret = 0;
564             break;
565             
566         case COMMAND_VERSION:
567             printf("%s "PACKAGE_VERSION"\n", argv0);
568             ret = 0;
569             break;
570
571         case COMMAND_RUN:
572             
573             if (!(simple_poll = avahi_simple_poll_new())) {
574                 fprintf(stderr, "Failed to create simple poll object.\n");
575                 goto fail;
576             }
577             
578             if (sigint_install(simple_poll) < 0)
579                 goto fail;
580             
581             if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), client_callback, NULL, &error))) {
582                 fprintf(stderr, "Failed to create client object: %s\n", avahi_strerror(error));
583                 goto fail;
584             }
585
586             if (config.verbose) {
587                 const char *version, *hn;
588
589                 if (!(version = avahi_client_get_version_string(client))) {
590                     fprintf(stderr, "Failed to query version string: %s\n", avahi_strerror(avahi_client_errno(client)));
591                     goto fail;
592                 }
593
594                 if (!(hn = avahi_client_get_host_name_fqdn(client))) {
595                     fprintf(stderr, "Failed to query host name: %s\n", avahi_strerror(avahi_client_errno(client)));
596                     goto fail;
597                 }
598                 
599                 fprintf(stderr, "Server version: %s; Host name: %s\n\n", version, hn);
600                 fprintf(stderr, "E Ifce Prot %-*s %-20s Domain\n", n_columns-35, "Name", "Type");
601             }
602             
603             if (config.show_all)
604                 browse_all(&config);
605             else
606                 browse_service_type(&config, config.stype, config.domain);
607             
608             avahi_simple_poll_loop(simple_poll);
609             ret = 0;
610             break;
611     }
612     
613     
614 fail:
615
616     while (services)
617         remove_service(&config, services);
618
619     if (client)
620         avahi_client_free(client);
621
622     sigint_uninstall();
623     
624     if (simple_poll)
625         avahi_simple_poll_free(simple_poll);
626
627     avahi_free(config.domain);
628     avahi_free(config.stype);
629
630     avahi_string_list_free(browsed_types);
631
632 #ifdef HAVE_GDBM
633     stdb_shutdown();
634 #endif    
635
636     return ret;
637 }