]> git.meshlink.io Git - catta/blobdiff - avahi-utils/avahi-browse.c
Fix compilation with -pthread
[catta] / avahi-utils / avahi-browse.c
index 09792a47ec4714dc53f84c18226b66630ffd7472..a5d9d25f06d2407cf8da5f06de24ae1c512334d8 100644 (file)
 #include <getopt.h>
 #include <assert.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <net/if.h>
+#include <locale.h>
 
 #include <avahi-common/simple-watch.h>
 #include <avahi-common/error.h>
 
 #include "sigint.h"
 
+#ifdef HAVE_GDBM
+#include "stdb.h"
+#endif
+
 typedef enum {
     COMMAND_HELP,
     COMMAND_VERSION,
-    COMMAND_RUN
+    COMMAND_BROWSE_SERVICES,
+    COMMAND_BROWSE_ALL_SERVICES,
+    COMMAND_BROWSE_DOMAINS
 } Command;
 
 typedef struct Config {
@@ -53,12 +62,14 @@ typedef struct Config {
     char *domain;
     char *stype;
     int ignore_local;
-    int show_all;
     Command command;
     int resolve;
+    int no_fail;
+#ifdef HAVE_GDBM
+    int no_db_lookup;
+#endif
 } Config;
 
-
 typedef struct ServiceInfo ServiceInfo;
 
 struct ServiceInfo {
@@ -78,6 +89,7 @@ static int n_all_for_now = 0, n_cache_exhausted = 0, n_resolving = 0;
 static AvahiStringList *browsed_types = NULL;
 static ServiceInfo *services = NULL;
 static int n_columns = 80;
+static int browsing = 0;
 
 static void check_terminate(Config *c) {
 
@@ -123,6 +135,21 @@ static ServiceInfo *find_service(AvahiIfIndex interface, AvahiProtocol protocol,
     return NULL;
 }
 
+static void print_service_line(Config *config, char c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
+    char ifname[IF_NAMESIZE];
+
+#ifdef HAVE_GDBM
+    if (!config->no_db_lookup)
+        type = stdb_lookup(type);
+#endif
+    
+    printf("%c %4s %4s %-*s %-20s %s\n",
+           c,
+           interface != AVAHI_IF_UNSPEC ? if_indextoname(interface, ifname) : "n/a",
+           protocol != AVAHI_PROTO_UNSPEC ? avahi_proto_to_string(protocol) : "n/a", 
+           n_columns-35, name, type, domain);
+}
+
 static void service_resolver_callback(
     AvahiServiceResolver *r,
     AvahiIfIndex interface,
@@ -146,19 +173,17 @@ static void service_resolver_callback(
     switch (event) {
         case AVAHI_RESOLVER_FOUND: {
             char address[AVAHI_ADDRESS_STR_MAX], *t;
-            char ifname[IF_NAMESIZE];
 
             avahi_address_snprint(address, sizeof(address), a);
 
             t = avahi_string_list_to_string(txt);
 
-            printf("= %4s %4s %-*s %-20s %s\n"
-                   "   hostname = [%s]\n"
+            print_service_line(i->config, '=', interface, protocol, name, type, domain);
+            
+            printf("   hostname = [%s]\n"
                    "   address = [%s]\n"
                    "   port = [%i]\n"
                    "   txt = [%s]\n",
-                   if_indextoname(interface, ifname), avahi_proto_to_string(protocol), 
-                   n_columns-35, name, type, domain,
                    host_name,
                    address,
                    port,
@@ -244,7 +269,6 @@ static void service_browser_callback(
 
     switch (event) {
         case AVAHI_BROWSER_NEW: {
-            char ifname[IF_NAMESIZE];
             if (c->ignore_local && (flags & AVAHI_LOOKUP_RESULT_LOCAL))
                 break;
 
@@ -253,13 +277,12 @@ static void service_browser_callback(
 
             add_service(c, interface, protocol, name, type, domain);
 
-            printf("+ %4s %4s %-*s %-20s %s\n", if_indextoname(interface, ifname), avahi_proto_to_string(protocol), n_columns-35, name, type, domain);
+            print_service_line(c, '+', interface, protocol, name, type, domain);
             break;
 
         }
 
         case AVAHI_BROWSER_REMOVE: {
-            char ifname[IF_NAMESIZE];
             ServiceInfo *info;
             
             if (!(info = find_service(interface, protocol, name, type, domain)))
@@ -267,7 +290,7 @@ static void service_browser_callback(
 
             remove_service(c, info);
             
-            printf("- %4s %4s %-*s %-20s %s\n", if_indextoname(interface, ifname), avahi_proto_to_string(protocol), n_columns-35, name, type, domain);
+            print_service_line(c, '-', interface, protocol, name, type, domain);
             break;
         }
             
@@ -384,66 +407,241 @@ static void browse_all(Config *c) {
     n_all_for_now++;
 }
 
-static void client_callback(AVAHI_GCC_UNUSED AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
-    switch (state) {
-        case AVAHI_CLIENT_DISCONNECTED:
-            fprintf(stderr, "Client disconnected, exiting.\n");
+static void 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) {
+
+    Config *c = userdata;
+
+    assert(b);
+    assert(c);
+    
+    switch (event) {
+        
+        case AVAHI_BROWSER_NEW:
+        case AVAHI_BROWSER_REMOVE: {
+            char ifname[IF_NAMESIZE];
+            
+            printf("%c %4s %4s %s\n",
+                   event == AVAHI_BROWSER_NEW ? '+' : '-',
+                   interface != AVAHI_IF_UNSPEC ? if_indextoname(interface, ifname) : "n/a",
+                   protocol != AVAHI_PROTO_UNSPEC ? avahi_proto_to_string(protocol) : "n/a", 
+                   domain);
+            break;
+        }
+
+        case AVAHI_BROWSER_FAILURE:
+            fprintf(stderr, "domain_browser failed: %s\n", avahi_strerror(avahi_client_errno(client)));
             avahi_simple_poll_quit(simple_poll);
             break;
+            
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+            n_cache_exhausted --;
+            check_terminate(c);
+            break;
+            
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+            n_all_for_now --;
+            check_terminate(c);
+            break;
+    }
+}
+
+static void browse_domains(Config *c) {
+    AvahiDomainBrowser *b;
+
+    assert(c);
+
+    if (!(b = avahi_domain_browser_new(
+              client,
+              AVAHI_IF_UNSPEC,
+              AVAHI_PROTO_UNSPEC,
+              c->domain,
+              AVAHI_DOMAIN_BROWSER_BROWSE,
+              0,
+              domain_browser_callback,
+              c))) {
+
+        fprintf(stderr, "avahi_domain_browser_new() failed: %s\n", avahi_strerror(avahi_client_errno(client)));
+        avahi_simple_poll_quit(simple_poll);
+    }
+
+    n_cache_exhausted++;
+    n_all_for_now++;
+}
+
+static int start(Config *config) {
 
+    assert(!browsing);
+    
+    if (config->verbose) {
+        const char *version, *hn;
+        
+        if (!(version = avahi_client_get_version_string(client))) {
+            fprintf(stderr, "Failed to query version string: %s\n", avahi_strerror(avahi_client_errno(client)));
+            return -1;
+        }
+        
+        if (!(hn = avahi_client_get_host_name_fqdn(client))) {
+            fprintf(stderr, "Failed to query host name: %s\n", avahi_strerror(avahi_client_errno(client)));
+            return -1;
+        }
+        
+        fprintf(stderr, "Server version: %s; Host name: %s\n", version, hn);
+        
+        if (config->command == COMMAND_BROWSE_DOMAINS)
+            fprintf(stderr, "E Ifce Prot Domain\n");
+        else
+            fprintf(stderr, "E Ifce Prot %-*s %-20s Domain\n", n_columns-35, "Name", "Type");
+    }
+    
+    if (config->command == COMMAND_BROWSE_SERVICES)
+        browse_service_type(config, config->stype, config->domain);
+    else if (config->command == COMMAND_BROWSE_ALL_SERVICES)
+        browse_all(config);
+    else {
+        assert(config->command == COMMAND_BROWSE_DOMAINS);
+        browse_domains(config);
+    }
+
+    browsing = 1;
+    return 0;
+}
+    
+static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+    Config *config = userdata;
+
+    /* This function might be called when avahi_client_new() has not
+     * returned yet.*/
+    client = c;
+    
+    switch (state) {
+        case AVAHI_CLIENT_FAILURE:
+            
+            if (config->no_fail && avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+                int error;
+
+                /* We have been disconnected, so let reconnect */
+
+                fprintf(stderr, "Disconnected, reconnecting ...\n");
+
+                avahi_client_free(client);
+                client = NULL;
+
+                avahi_string_list_free(browsed_types);
+                browsed_types = NULL;
+                
+                while (services)
+                    remove_service(config, services);
+
+                browsing = 0;
+
+                if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL, client_callback, config, &error))) {
+                    fprintf(stderr, "Failed to create client object: %s\n", avahi_strerror(error));
+                    avahi_simple_poll_quit(simple_poll);
+                }
+
+            } else {
+                fprintf(stderr, "Client failure, exiting: %s\n", avahi_strerror(avahi_client_errno(c)));
+                avahi_simple_poll_quit(simple_poll);
+            }
+            
+            break;
+            
         case AVAHI_CLIENT_S_REGISTERING:
         case AVAHI_CLIENT_S_RUNNING:
         case AVAHI_CLIENT_S_COLLISION:
-            ;
+
+            if (!browsing)
+                if (start(config) < 0)
+                    avahi_simple_poll_quit(simple_poll);
+
+            break;
+            
+        case AVAHI_CLIENT_CONNECTING:
+            
+            if (config->verbose)
+                fprintf(stderr, "Waiting for daemon ...\n");
+
+            break;
     }
 }
 
 static void help(FILE *f, const char *argv0) {
-    fprintf(f,
-            "%s [options] <type>\n"
-            "%s [options] -a\n\n"
-            "    -h --help          Show this help\n"
-            "    -V --version       Show version\n"
-            "    -d --domain=DOMAIN The domain to browse\n"
-            "    -a --all           Show all services, regardless of the type\n"
-            "    -v --verbose       Enable verbose mode\n"
-            "    -t --terminate     Terminate after getting or more or less complete list\n"
-            "    -c --cache         Terminate after dumping all entries from the cache\n"
-            "    -l --ignore-local  Ignore local services\n"
-            "    -r --resolve       Resolve services found\n",
-            argv0, argv0);
+    if (strstr(argv0, "domain"))
+        fprintf(f, "%s [options] \n\n", argv0);
+    else
+        fprintf(f,
+                "%s [options] <service type>\n"
+                "%s [options] -a\n"
+                "%s [options] -D\n\n",
+                argv0, argv0, argv0);
+
+            fprintf(f, 
+            "    -h --help            Show this help\n"
+            "    -V --version         Show version\n"
+            "    -D --browse-domains  Browse for browsing domains instead of services\n"
+            "    -a --all             Show all services, regardless of the type\n"
+            "    -d --domain=DOMAIN   The domain to browse in\n"
+            "    -v --verbose         Enable verbose mode\n"
+            "    -t --terminate       Terminate after dumping a more or less complete list\n"
+            "    -c --cache           Terminate after dumping all entries from the cache\n"
+            "    -l --ignore-local    Ignore local services\n"
+            "    -r --resolve         Resolve services found\n"
+            "    -f --no-fail         Don't fail if the daemon is not available\n"
+#ifdef HAVE_GDBM
+            "    -k --no-db-lookup    Don't lookup service types\n"
+#endif
+            );
 }
 
-
-static int parse_command_line(Config *c, int argc, char *argv[]) {
+static int parse_command_line(Config *c, const char *argv0, int argc, char *argv[]) {
     int o;
 
     static const struct option long_options[] = {
-        { "help",         no_argument,       NULL, 'h' },
-        { "version",      no_argument,       NULL, 'V' },
-        { "domain",       required_argument, NULL, 'd' },
-        { "all",          no_argument,       NULL, 'a' },
-        { "verbose",      no_argument,       NULL, 'v' },
-        { "terminate",    no_argument,       NULL, 't' },
-        { "cache",        no_argument,       NULL, 'c' },
-        { "ignore-local", no_argument,       NULL, 'l' },
-        { "resolve",      no_argument,       NULL, 'r' },
+        { "help",           no_argument,       NULL, 'h' },
+        { "version",        no_argument,       NULL, 'V' },
+        { "browse-domains", no_argument,       NULL, 'D' },
+        { "domain",         required_argument, NULL, 'd' },
+        { "all",            no_argument,       NULL, 'a' },
+        { "verbose",        no_argument,       NULL, 'v' },
+        { "terminate",      no_argument,       NULL, 't' },
+        { "cache",          no_argument,       NULL, 'c' },
+        { "ignore-local",   no_argument,       NULL, 'l' },
+        { "resolve",        no_argument,       NULL, 'r' },
+        { "no-fail",        no_argument,       NULL, 'f' },
+#ifdef HAVE_GDBM
+        { "no-db-lookup",   no_argument,       NULL, 'k' },
+#endif
         { NULL, 0, NULL, 0 }
     };
 
     assert(c);
 
-    c->command = COMMAND_RUN;
+    c->command = strstr(argv0, "domain") ? COMMAND_BROWSE_DOMAINS : COMMAND_BROWSE_SERVICES;
     c->verbose =
         c->terminate_on_cache_exhausted =
         c->terminate_on_all_for_now =
-        c->show_all =
         c->ignore_local =
-        c->resolve = 0;
+        c->resolve =
+        c->no_fail = 0;
     c->domain = c->stype = NULL;
+
+#ifdef HAVE_GDBM
+    c->no_db_lookup = 0;
+#endif
     
     opterr = 0;
-    while ((o = getopt_long(argc, argv, "hVd:avtclr", long_options, NULL)) >= 0) {
+    while ((o = getopt_long(argc, argv, "hVd:avtclrDf"
+#ifdef HAVE_GDBM
+                            "k"
+#endif
+                            , long_options, NULL)) >= 0) {
 
         switch(o) {
             case 'h':
@@ -452,12 +650,16 @@ static int parse_command_line(Config *c, int argc, char *argv[]) {
             case 'V':
                 c->command = COMMAND_VERSION;
                 break;
+            case 'a':
+                c->command = COMMAND_BROWSE_ALL_SERVICES;
+                break;
+            case 'D':
+                c->command = COMMAND_BROWSE_DOMAINS;
+                break;
             case 'd':
+                avahi_free(c->domain);
                 c->domain = avahi_strdup(optarg);
                 break;
-            case 'a':
-                c->show_all = 1;
-                break;
             case 'v':
                 c->verbose = 1;
                 break;
@@ -473,13 +675,21 @@ static int parse_command_line(Config *c, int argc, char *argv[]) {
             case 'r':
                 c->resolve = 1;
                 break;
+            case 'f':
+                c->no_fail = 1;
+                break;
+#ifdef HAVE_GDBM
+            case 'k':
+                c->no_db_lookup = 1;
+                break;
+#endif
             default:
                 fprintf(stderr, "Invalid command line argument: %c\n", o);
                 return -1;
         }
     }
 
-    if (c->command == COMMAND_RUN && !c->show_all) {
+    if (c->command == COMMAND_BROWSE_SERVICES) {
         if (optind >= argc) {
             fprintf(stderr, "Too few arguments\n");
             return -1;
@@ -503,6 +713,8 @@ int main(int argc, char *argv[]) {
     const char *argv0;
     char *ec;
 
+    setlocale(LC_ALL, "");
+
     if ((argv0 = strrchr(argv[0], '/')))
         argv0++;
     else
@@ -514,7 +726,7 @@ int main(int argc, char *argv[]) {
     if (n_columns < 40)
         n_columns = 40;
     
-    if (parse_command_line(&config, argc, argv) < 0)
+    if (parse_command_line(&config, argv0, argc, argv) < 0)
         goto fail;
 
     switch (config.command) {
@@ -528,7 +740,9 @@ int main(int argc, char *argv[]) {
             ret = 0;
             break;
 
-        case COMMAND_RUN:
+        case COMMAND_BROWSE_SERVICES:
+        case COMMAND_BROWSE_ALL_SERVICES:
+        case COMMAND_BROWSE_DOMAINS:
             
             if (!(simple_poll = avahi_simple_poll_new())) {
                 fprintf(stderr, "Failed to create simple poll object.\n");
@@ -538,32 +752,10 @@ int main(int argc, char *argv[]) {
             if (sigint_install(simple_poll) < 0)
                 goto fail;
             
-            if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), client_callback, NULL, &error))) {
+            if (!(client = avahi_client_new(avahi_simple_poll_get(simple_poll), config.no_fail ? AVAHI_CLIENT_NO_FAIL : 0, client_callback, &config, &error))) {
                 fprintf(stderr, "Failed to create client object: %s\n", avahi_strerror(error));
                 goto fail;
             }
-
-            if (config.verbose) {
-                const char *version, *hn;
-
-                if (!(version = avahi_client_get_version_string(client))) {
-                    fprintf(stderr, "Failed to query version string: %s\n", avahi_strerror(avahi_client_errno(client)));
-                    goto fail;
-                }
-
-                if (!(hn = avahi_client_get_host_name_fqdn(client))) {
-                    fprintf(stderr, "Failed to query host name: %s\n", avahi_strerror(avahi_client_errno(client)));
-                    goto fail;
-                }
-                
-                fprintf(stderr, "Server version: %s; Host name: %s\n\n", version, hn);
-                fprintf(stderr, "E Ifce Prot %-*s %-20s Domain\n", n_columns-35, "Name", "Type");
-            }
-            
-            if (config.show_all)
-                browse_all(&config);
-            else
-                browse_service_type(&config, config.stype, config.domain);
             
             avahi_simple_poll_loop(simple_poll);
             ret = 0;
@@ -589,5 +781,9 @@ fail:
 
     avahi_string_list_free(browsed_types);
 
+#ifdef HAVE_GDBM
+    stdb_shutdown();
+#endif    
+
     return ret;
 }