Home | History | Annotate | Download | only in avahi-discover-standalone
      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 <sys/ioctl.h>
     25 #include <string.h>
     26 #include <sys/types.h>
     27 #include <sys/socket.h>
     28 #include <net/if.h>
     29 #include <unistd.h>
     30 
     31 #include <gtk/gtk.h>
     32 
     33 #include <avahi-core/core.h>
     34 #include <avahi-core/lookup.h>
     35 
     36 #include <avahi-common/strlst.h>
     37 #include <avahi-common/domain.h>
     38 #include <avahi-common/error.h>
     39 
     40 #include <avahi-glib/glib-watch.h>
     41 #include <avahi-glib/glib-malloc.h>
     42 
     43 struct ServiceType;
     44 
     45 struct Service {
     46     struct ServiceType *service_type;
     47     gchar *service_name;
     48     gchar *domain_name;
     49 
     50     AvahiIfIndex interface;
     51     AvahiProtocol protocol;
     52 
     53     GtkTreeRowReference *tree_ref;
     54 };
     55 
     56 struct ServiceType {
     57     gchar *service_type;
     58     AvahiSServiceBrowser *browser;
     59 
     60     GList *services;
     61     GtkTreeRowReference *tree_ref;
     62 };
     63 
     64 static GtkWidget *main_window = NULL;
     65 static GtkTreeView *tree_view = NULL;
     66 static GtkTreeStore *tree_store = NULL;
     67 static GtkLabel *info_label = NULL;
     68 static AvahiServer *server = NULL;
     69 static AvahiSServiceTypeBrowser *service_type_browser = NULL;
     70 static GHashTable *service_type_hash_table = NULL;
     71 static AvahiSServiceResolver *service_resolver = NULL;
     72 static struct Service *current_service = NULL;
     73 
     74 static struct Service *get_service(const gchar *service_type, const gchar *service_name, const gchar*domain_name, AvahiIfIndex interface, AvahiProtocol protocol) {
     75     struct ServiceType *st;
     76     GList *l;
     77 
     78     if (!(st = g_hash_table_lookup(service_type_hash_table, service_type)))
     79         return NULL;
     80 
     81     for (l = st->services; l; l = l->next) {
     82         struct Service *s = l->data;
     83 
     84         if (s->interface == interface &&
     85             s->protocol == protocol &&
     86             avahi_domain_equal(s->service_name, service_name) &&
     87             avahi_domain_equal(s->domain_name, domain_name))
     88             return s;
     89     }
     90 
     91     return NULL;
     92 }
     93 
     94 static void free_service(struct Service *s) {
     95     GtkTreeIter iter;
     96     GtkTreePath *path;
     97 
     98     if (current_service == s) {
     99         current_service = NULL;
    100 
    101         if (service_resolver) {
    102             avahi_s_service_resolver_free(service_resolver);
    103             service_resolver = NULL;
    104         }
    105 
    106         gtk_label_set_text(info_label, "<i>Service removed</i>");
    107     }
    108 
    109     s->service_type->services = g_list_remove(s->service_type->services, s);
    110 
    111     if ((path = gtk_tree_row_reference_get_path(s->tree_ref))) {
    112         gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &iter, path);
    113         gtk_tree_path_free(path);
    114     }
    115 
    116     gtk_tree_store_remove(tree_store, &iter);
    117 
    118     gtk_tree_row_reference_free(s->tree_ref);
    119 
    120     g_free(s->service_name);
    121     g_free(s->domain_name);
    122     g_free(s);
    123 }
    124 
    125 static void service_browser_callback(
    126     AVAHI_GCC_UNUSED AvahiSServiceBrowser *b,
    127     AvahiIfIndex interface,
    128     AvahiProtocol protocol,
    129     AvahiBrowserEvent event,
    130     const char *service_name,
    131     const char *service_type,
    132     const char *domain_name,
    133     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
    134     AVAHI_GCC_UNUSED void* userdata) {
    135 
    136     if (event == AVAHI_BROWSER_NEW) {
    137         struct Service *s;
    138         GtkTreeIter iter, piter;
    139         GtkTreePath *path, *ppath;
    140         gchar iface[256];
    141 	char name[IF_NAMESIZE];
    142 
    143         s = g_new(struct Service, 1);
    144         s->service_name = g_strdup(service_name);
    145         s->domain_name = g_strdup(domain_name);
    146         s->interface = interface;
    147         s->protocol = protocol;
    148         s->service_type = g_hash_table_lookup(service_type_hash_table, service_type);
    149         g_assert(s->service_type);
    150 
    151         s->service_type->services = g_list_prepend(s->service_type->services, s);
    152 
    153         ppath = gtk_tree_row_reference_get_path(s->service_type->tree_ref);
    154         gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &piter, ppath);
    155 
    156         snprintf(iface, sizeof(iface), "%s %s", if_indextoname(interface, name), avahi_proto_to_string(protocol));
    157 
    158         gtk_tree_store_append(tree_store, &iter, &piter);
    159         gtk_tree_store_set(tree_store, &iter, 0, s->service_name, 1, iface, 2, s, -1);
    160 
    161         path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &iter);
    162         s->tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(tree_store), path);
    163         gtk_tree_path_free(path);
    164 
    165         gtk_tree_view_expand_row(tree_view, ppath, FALSE);
    166         gtk_tree_path_free(ppath);
    167 
    168 
    169     } else if (event == AVAHI_BROWSER_REMOVE) {
    170         struct Service* s;
    171 
    172         if ((s = get_service(service_type, service_name, domain_name, interface, protocol)))
    173             free_service(s);
    174     }
    175 }
    176 
    177 static void service_type_browser_callback(
    178     AVAHI_GCC_UNUSED AvahiSServiceTypeBrowser *b,
    179     AVAHI_GCC_UNUSED AvahiIfIndex interface,
    180     AVAHI_GCC_UNUSED AvahiProtocol protocol,
    181     AvahiBrowserEvent event,
    182     const char *service_type,
    183     const char *domain,
    184     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
    185     AVAHI_GCC_UNUSED void * userdata) {
    186 
    187     struct ServiceType *st;
    188     GtkTreePath *path;
    189     GtkTreeIter iter;
    190 
    191     if (event != AVAHI_BROWSER_NEW)
    192         return;
    193 
    194     if (g_hash_table_lookup(service_type_hash_table, service_type))
    195         return;
    196 
    197     st = g_new(struct ServiceType, 1);
    198     st->service_type = g_strdup(service_type);
    199     st->services = NULL;
    200 
    201     gtk_tree_store_append(tree_store, &iter, NULL);
    202     gtk_tree_store_set(tree_store, &iter, 0, st->service_type, 1, "", 2, NULL, -1);
    203 
    204     path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &iter);
    205     st->tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(tree_store), path);
    206     gtk_tree_path_free(path);
    207 
    208     g_hash_table_insert(service_type_hash_table, st->service_type, st);
    209 
    210     st->browser = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, st->service_type, domain, 0, service_browser_callback, NULL);
    211 }
    212 
    213 static void update_label(struct Service *s, const gchar *hostname, const AvahiAddress *a, guint16 port, AvahiStringList *txt) {
    214     gchar t[512], address[64], *txt_s;
    215     char name[IF_NAMESIZE];
    216 
    217     if (a && hostname) {
    218         char na[AVAHI_ADDRESS_STR_MAX];
    219         avahi_address_snprint(na, sizeof(na), a);
    220         snprintf(address, sizeof(address), "%s/%s:%u", hostname, na, port);
    221 
    222         if (txt)
    223             txt_s = avahi_string_list_to_string(txt);
    224         else
    225             txt_s = g_strdup("<i>empty</i>");
    226     } else {
    227         snprintf(address, sizeof(address), "<i>n/a</i>");
    228         txt_s = g_strdup("<i>n/a</i>");
    229     }
    230 
    231     snprintf(t, sizeof(t),
    232              "<b>Service Type:</b> %s\n"
    233              "<b>Service Name:</b> %s\n"
    234              "<b>Domain Name:</b> %s\n"
    235              "<b>Interface:</b> %s %s\n"
    236              "<b>Address:</b> %s\n"
    237              "<b>TXT Data:</b> %s",
    238              s->service_type->service_type,
    239              s->service_name,
    240              s->domain_name,
    241              if_indextoname(s->interface,name), avahi_proto_to_string(s->protocol),
    242              address,
    243              txt_s);
    244 
    245     gtk_label_set_markup(info_label, t);
    246 
    247     g_free(txt_s);
    248 }
    249 
    250 static struct Service *get_service_on_cursor(void) {
    251     GtkTreePath *path;
    252     struct Service *s;
    253     GtkTreeIter iter;
    254 
    255     gtk_tree_view_get_cursor(tree_view, &path, NULL);
    256 
    257     if (!path)
    258         return NULL;
    259 
    260     gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &iter, path);
    261     gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter, 2, &s, -1);
    262     gtk_tree_path_free(path);
    263 
    264     return s;
    265 }
    266 
    267 static void service_resolver_callback(
    268     AvahiSServiceResolver *r,
    269     AVAHI_GCC_UNUSED AvahiIfIndex interface,
    270     AVAHI_GCC_UNUSED AvahiProtocol protocol,
    271     AvahiResolverEvent event,
    272     AVAHI_GCC_UNUSED const char *name,
    273     AVAHI_GCC_UNUSED const char *type,
    274     AVAHI_GCC_UNUSED const char *domain,
    275     const char *host_name,
    276     const AvahiAddress *a,
    277     uint16_t port,
    278     AvahiStringList *txt,
    279     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
    280     void* userdata) {
    281 
    282     struct Service *s;
    283     g_assert(r);
    284 
    285     if (!(s = get_service_on_cursor()) || userdata != s) {
    286         g_assert(r == service_resolver);
    287         avahi_s_service_resolver_free(service_resolver);
    288         service_resolver = NULL;
    289         return;
    290     }
    291 
    292     if (event == AVAHI_RESOLVER_FAILURE) {
    293         char t[256];
    294         snprintf(t, sizeof(t), "<i>Failed to resolve: %s.</i>", avahi_strerror(avahi_server_errno(server)));
    295         gtk_label_set_markup(info_label, t);
    296     } else if (event == AVAHI_RESOLVER_FOUND)
    297         update_label(s, host_name, a, port, txt);
    298 }
    299 
    300 static void tree_view_on_cursor_changed(AVAHI_GCC_UNUSED GtkTreeView *tv, AVAHI_GCC_UNUSED gpointer userdata) {
    301     struct Service *s;
    302 
    303     if (!(s = get_service_on_cursor()))
    304         return;
    305 
    306     if (service_resolver)
    307         avahi_s_service_resolver_free(service_resolver);
    308 
    309     update_label(s, NULL, NULL, 0, NULL);
    310 
    311     service_resolver = avahi_s_service_resolver_new(server, s->interface, s->protocol, s->service_name, s->service_type->service_type, s->domain_name, AVAHI_PROTO_UNSPEC, 0, service_resolver_callback, s);
    312 }
    313 
    314 static gboolean main_window_on_delete_event(AVAHI_GCC_UNUSED GtkWidget *widget, AVAHI_GCC_UNUSED GdkEvent *event, AVAHI_GCC_UNUSED gpointer user_data) {
    315     gtk_main_quit();
    316     return FALSE;
    317 }
    318 
    319 int main(int argc, char *argv[]) {
    320     GtkBuilder *ui;
    321     AvahiServerConfig config;
    322     GtkTreeViewColumn *c;
    323     gint error;
    324     AvahiGLibPoll *poll_api;
    325 
    326     gtk_init(&argc, &argv);
    327 
    328     avahi_set_allocator(avahi_glib_allocator());
    329 
    330     poll_api = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT);
    331 
    332     ui = gtk_builder_new();
    333     gtk_builder_add_from_file(ui, AVAHI_INTERFACES_DIR"avahi-discover.ui", NULL);
    334     main_window = GTK_WIDGET(gtk_builder_get_object(ui, "main_window"));
    335     g_signal_connect(main_window, "delete-event", (GCallback) main_window_on_delete_event, NULL);
    336 
    337     tree_view = GTK_TREE_VIEW(gtk_builder_get_object(ui, "tree_view"));
    338     g_signal_connect(GTK_WIDGET(tree_view), "cursor-changed", (GCallback) tree_view_on_cursor_changed, NULL);
    339 
    340     info_label = GTK_LABEL(gtk_builder_get_object(ui, "info_label"));
    341 
    342     tree_store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
    343     gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(tree_store));
    344     gtk_tree_view_insert_column_with_attributes(tree_view, -1, "Name", gtk_cell_renderer_text_new(), "text", 0, NULL);
    345     gtk_tree_view_insert_column_with_attributes(tree_view, -1, "Interface", gtk_cell_renderer_text_new(), "text", 1, NULL);
    346 
    347     gtk_tree_view_column_set_resizable(c = gtk_tree_view_get_column(tree_view, 0), TRUE);
    348     gtk_tree_view_column_set_sizing(c, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
    349     gtk_tree_view_column_set_expand(c, TRUE);
    350 
    351     service_type_hash_table = g_hash_table_new((GHashFunc) avahi_domain_hash, (GEqualFunc) avahi_domain_equal);
    352 
    353     avahi_server_config_init(&config);
    354     config.publish_hinfo = config.publish_addresses = config.publish_domain = config.publish_workstation = FALSE;
    355     server = avahi_server_new(avahi_glib_poll_get(poll_api), &config, NULL, NULL, &error);
    356     avahi_server_config_free(&config);
    357 
    358     g_assert(server);
    359 
    360     service_type_browser = avahi_s_service_type_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, argc >= 2 ? argv[1] : NULL, 0, service_type_browser_callback, NULL);
    361 
    362     gtk_main();
    363 
    364     avahi_server_free(server);
    365     avahi_glib_poll_free(poll_api);
    366 
    367     return 0;
    368 }
    369