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