gio: Add a count to the resolver test for number of enumerators
[platform/upstream/glib.git] / gio / tests / resolver.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4  * 
5  * Copyright (C) 2008 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "config.h"
24 #include <glib.h>
25 #include "glibintl.h"
26
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include <gio/gio.h>
34
35 static GResolver *resolver;
36 static GCancellable *cancellable;
37 static GMainLoop *loop;
38 static int nlookups = 0;
39
40 static void G_GNUC_NORETURN
41 usage (void)
42 {
43         fprintf (stderr, "Usage: resolver [-t] [-s] [hostname | IP | service/protocol/domain ] ...\n");
44         fprintf (stderr, "       resolver [-t] [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
45         fprintf (stderr, "       Use -t to enable threading.\n");
46         fprintf (stderr, "       Use -s to do synchronous lookups.\n");
47         fprintf (stderr, "       Both together will result in simultaneous lookups in multiple threads\n");
48         fprintf (stderr, "       Use -c NUMBER(and only a single resolvable argument) to test GSocketConnectable.\n");
49         fprintf (stderr, "       The given NUMBER determines how often the connectable will be enumerated.\n");
50         exit (1);
51 }
52
53 G_LOCK_DEFINE_STATIC (response);
54
55 static void
56 done_lookup (void)
57 {
58   nlookups--;
59   if (nlookups == 0)
60     {
61       /* In the sync case we need to make sure we don't call
62        * g_main_loop_quit before the loop is actually running...
63        */
64       g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
65     }
66 }
67
68 static void
69 print_resolved_name (const char *phys,
70                      char       *name,
71                      GError     *error)
72 {
73   G_LOCK (response);
74   printf ("Address: %s\n", phys);
75   if (error)
76     {
77       printf ("Error:   %s\n", error->message);
78       g_error_free (error);
79     }
80   else
81     {
82       printf ("Name:    %s\n", name);
83       g_free (name);
84     }
85   printf ("\n");
86
87   done_lookup ();
88   G_UNLOCK (response);
89 }
90
91 static void
92 print_resolved_addresses (const char *name,
93                           GList      *addresses,
94                           GError     *error)
95 {
96   char *phys;
97   GList *a;
98
99   G_LOCK (response);
100   printf ("Name:    %s\n", name);
101   if (error)
102     {
103       printf ("Error:   %s\n", error->message);
104       g_error_free (error);
105     }
106   else
107     {
108       for (a = addresses; a; a = a->next)
109         {
110           phys = g_inet_address_to_string (a->data);
111           printf ("Address: %s\n", phys);
112           g_free (phys);
113           g_object_unref (a->data);
114         }
115       g_list_free (addresses);
116     }
117   printf ("\n");
118
119   done_lookup ();
120   G_UNLOCK (response);
121 }
122
123 static void
124 print_resolved_service (const char *service,
125                         GList      *targets,
126                         GError     *error)
127 {
128   GList *t;  
129
130   G_LOCK (response);
131   printf ("Service: %s\n", service);
132   if (error)
133     {
134       printf ("Error: %s\n", error->message);
135       g_error_free (error);
136     }
137   else
138     {
139       for (t = targets; t; t = t->next)
140         {
141           printf ("%s:%u (pri %u, weight %u)\n",
142                   g_srv_target_get_hostname (t->data),
143                   g_srv_target_get_port (t->data),
144                   g_srv_target_get_priority (t->data),
145                   g_srv_target_get_weight (t->data));
146           g_srv_target_free (t->data);
147         }
148       g_list_free (targets);
149     }
150   printf ("\n");
151
152   done_lookup ();
153   G_UNLOCK (response);
154 }
155
156 static void
157 lookup_one_sync (const char *arg)
158 {
159   GError *error = NULL;
160
161   if (strchr (arg, '/'))
162     {
163       GList *targets;
164       /* service/protocol/domain */
165       char **parts = g_strsplit (arg, "/", 3);
166
167       if (!parts || !parts[2])
168         usage ();
169
170       targets = g_resolver_lookup_service (resolver,
171                                            parts[0], parts[1], parts[2],
172                                            cancellable, &error);
173       print_resolved_service (arg, targets, error);
174     }
175   else if (g_hostname_is_ip_address (arg))
176     {
177       GInetAddress *addr = g_inet_address_new_from_string (arg);
178       char *name;
179
180       name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
181       print_resolved_name (arg, name, error);
182       g_object_unref (addr);
183     }
184   else
185     {
186       GList *addresses;
187
188       addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
189       print_resolved_addresses (arg, addresses, error);
190     }
191 }
192
193 static gpointer
194 lookup_thread (gpointer arg)
195 {
196   lookup_one_sync (arg);
197   return NULL;
198 }
199
200 static void
201 start_threaded_lookups (char **argv, int argc)
202 {
203   int i;
204
205   for (i = 0; i < argc; i++)
206     g_thread_create (lookup_thread, argv[i], FALSE, NULL);
207 }
208
209 static void
210 start_sync_lookups (char **argv, int argc)
211 {
212   int i;
213
214   for (i = 0; i < argc; i++)
215     lookup_one_sync (argv[i]);
216 }
217
218 static void
219 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
220                          gpointer user_data)
221 {
222   const char *phys = user_data;
223   GError *error = NULL;
224   char *name;
225
226   name = g_resolver_lookup_by_address_finish (resolver, result, &error);
227   print_resolved_name (phys, name, error);
228 }
229
230 static void
231 lookup_by_name_callback (GObject *source, GAsyncResult *result,
232                          gpointer user_data)
233 {
234   const char *name = user_data;
235   GError *error = NULL;
236   GList *addresses;
237
238   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
239   print_resolved_addresses (name, addresses, error);
240 }
241
242 static void
243 lookup_service_callback (GObject *source, GAsyncResult *result,
244                          gpointer user_data)
245 {
246   const char *service = user_data;
247   GError *error = NULL;
248   GList *targets;
249
250   targets = g_resolver_lookup_service_finish (resolver, result, &error);
251   print_resolved_service (service, targets, error);
252 }
253
254 static void
255 start_async_lookups (char **argv, int argc)
256 {
257   int i;
258
259   for (i = 0; i < argc; i++)
260     {
261       if (strchr (argv[i], '/'))
262         {
263           /* service/protocol/domain */
264           char **parts = g_strsplit (argv[i], "/", 3);
265
266           if (!parts || !parts[2])
267             usage ();
268
269           g_resolver_lookup_service_async (resolver,
270                                            parts[0], parts[1], parts[2],
271                                            cancellable,
272                                            lookup_service_callback, argv[i]);
273         }
274       else if (g_hostname_is_ip_address (argv[i]))
275         {
276           GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
277
278           g_resolver_lookup_by_address_async (resolver, addr, cancellable,
279                                               lookup_by_addr_callback, argv[i]);
280           g_object_unref (addr);
281         }
282       else
283         {
284           g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
285                                            lookup_by_name_callback,
286                                            argv[i]);
287         }
288
289       /* Stress-test the reloading code */
290       g_signal_emit_by_name (resolver, "reload");
291     }
292 }
293
294 static void
295 print_connectable_sockaddr (GSocketAddress *sockaddr,
296                             GError         *error)
297 {
298   char *phys;
299
300   if (error)
301     {
302       printf ("Error:   %s\n", error->message);
303       g_error_free (error);
304     }
305   else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
306     {
307       printf ("Error:   Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
308       g_object_unref (sockaddr);
309     }
310   else
311     {
312       GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
313       phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
314       printf ("Address: %s%s%s:%d\n",
315               strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
316               g_inet_socket_address_get_port (isa));
317       g_free (phys);
318       g_object_unref (sockaddr);
319     }
320 }
321
322 static void
323 do_sync_connectable (GSocketAddressEnumerator *enumerator)
324 {
325   GSocketAddress *sockaddr;
326   GError *error = NULL;
327
328   while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
329     print_connectable_sockaddr (sockaddr, error);
330
331   g_object_unref (enumerator);
332   done_lookup ();
333 }
334
335 static void do_async_connectable (GSocketAddressEnumerator *enumerator);
336
337 static void
338 got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
339 {
340   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
341   GSocketAddress *sockaddr;
342   GError *error = NULL;
343
344   sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
345   if (sockaddr || error)
346     print_connectable_sockaddr (sockaddr, error);
347   if (sockaddr)
348     do_async_connectable (enumerator);
349   else
350     {
351       g_object_unref (enumerator);
352       done_lookup ();
353     }
354 }
355
356 static void
357 do_async_connectable (GSocketAddressEnumerator *enumerator)
358 {
359   g_socket_address_enumerator_next_async (enumerator, cancellable,
360                                           got_next_async, NULL);
361 }
362
363 static void
364 do_connectable (const char *arg, gboolean synchronous, guint count)
365 {
366   char **parts;
367   GSocketConnectable *connectable;
368   GSocketAddressEnumerator *enumerator;
369
370   if (strchr (arg, '/'))
371     {
372       /* service/protocol/domain */
373       parts = g_strsplit (arg, "/", 3);
374       if (!parts || !parts[2])
375         usage ();
376
377       connectable = g_network_service_new (parts[0], parts[1], parts[2]);
378     }
379   else
380     {
381       guint16 port;
382
383       parts = g_strsplit (arg, ":", 2);
384       if (parts && parts[1])
385         {
386           arg = parts[0];
387           port = strtoul (parts[1], NULL, 10);
388         }
389       else
390         port = 0;
391
392       if (g_hostname_is_ip_address (arg))
393         {
394           GInetAddress *addr = g_inet_address_new_from_string (arg);
395           GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
396
397           g_object_unref (addr);
398           connectable = G_SOCKET_CONNECTABLE (sockaddr);
399         }
400       else
401         connectable = g_network_address_new (arg, port);
402     }
403
404   while (count--)
405     {
406       enumerator = g_socket_connectable_enumerate (connectable);
407
408       if (synchronous)
409         do_sync_connectable (enumerator);
410       else
411         do_async_connectable (enumerator);
412     }
413   
414   g_object_unref (connectable);
415 }
416
417 #ifdef G_OS_UNIX
418 static int cancel_fds[2];
419
420 static void
421 interrupted (int sig)
422 {
423   gssize c;
424
425   signal (SIGINT, SIG_DFL);
426   c = write (cancel_fds[1], "x", 1);
427   g_assert_cmpint(c, ==, 1);
428 }
429
430 static gboolean
431 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
432 {
433   g_cancellable_cancel (cancel);
434   return FALSE;
435 }
436 #endif
437
438 int
439 main (int argc, char **argv)
440 {
441   gboolean threaded = FALSE, synchronous = FALSE;
442   guint connectable_count = 0;
443 #ifdef G_OS_UNIX
444   GIOChannel *chan;
445   guint watch;
446 #endif
447
448   /* We can't use GOptionContext because we use the arguments to
449    * decide whether or not to call g_thread_init().
450    */
451   while (argc >= 2 && argv[1][0] == '-')
452     {
453       if (!strcmp (argv[1], "-t"))
454         {
455           g_thread_init (NULL);
456           threaded = TRUE;
457         }
458       else if (!strcmp (argv[1], "-s"))
459         synchronous = TRUE;
460       else if (!strcmp (argv[1], "-c"))
461         {
462           connectable_count = atoi (argv[2]);
463           argv++;
464           argc--;
465         }
466       else
467         usage ();
468
469       argv++;
470       argc--;
471     }
472   g_type_init ();
473
474   if (argc < 2 || (argc > 2 && connectable_count))
475     usage ();
476
477   resolver = g_resolver_get_default ();
478
479   cancellable = g_cancellable_new ();
480
481 #ifdef G_OS_UNIX
482   /* Set up cancellation; we want to cancel if the user ^C's the
483    * program, but we can't cancel directly from an interrupt.
484    */
485   signal (SIGINT, interrupted);
486
487   if (pipe (cancel_fds) == -1)
488     {
489       perror ("pipe");
490       exit (1);
491     }
492   chan = g_io_channel_unix_new (cancel_fds[0]);
493   watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
494   g_io_channel_unref (chan);
495 #endif
496
497   nlookups = argc - 1;
498   loop = g_main_loop_new (NULL, TRUE);
499
500   if (connectable_count)
501     {
502       nlookups = connectable_count;
503       do_connectable (argv[1], synchronous, connectable_count);
504     }
505   else
506     {
507       if (threaded && synchronous)
508         start_threaded_lookups (argv + 1, argc - 1);
509       else if (synchronous)
510         start_sync_lookups (argv + 1, argc - 1);
511       else
512         start_async_lookups (argv + 1, argc - 1);
513     }
514
515   g_main_loop_run (loop);
516   g_main_loop_unref (loop);
517
518 #ifdef G_OS_UNIX
519   g_source_remove (watch);
520 #endif
521   g_object_unref (cancellable);
522
523   return 0;
524 }