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