thread: nuke the concept of 'joinable'
[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 [-s] [hostname | IP | service/protocol/domain ] ...\n");
44         fprintf (stderr, "       resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
45         fprintf (stderr, "       Use -s to do synchronous lookups.\n");
46         fprintf (stderr, "       Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
47         fprintf (stderr, "       The given NUMBER determines how many times the connectable will be enumerated.\n");
48         exit (1);
49 }
50
51 G_LOCK_DEFINE_STATIC (response);
52
53 static void
54 done_lookup (void)
55 {
56   nlookups--;
57   if (nlookups == 0)
58     {
59       /* In the sync case we need to make sure we don't call
60        * g_main_loop_quit before the loop is actually running...
61        */
62       g_idle_add ((GSourceFunc)g_main_loop_quit, loop);
63     }
64 }
65
66 static void
67 print_resolved_name (const char *phys,
68                      char       *name,
69                      GError     *error)
70 {
71   G_LOCK (response);
72   printf ("Address: %s\n", phys);
73   if (error)
74     {
75       printf ("Error:   %s\n", error->message);
76       g_error_free (error);
77     }
78   else
79     {
80       printf ("Name:    %s\n", name);
81       g_free (name);
82     }
83   printf ("\n");
84
85   done_lookup ();
86   G_UNLOCK (response);
87 }
88
89 static void
90 print_resolved_addresses (const char *name,
91                           GList      *addresses,
92                           GError     *error)
93 {
94   char *phys;
95   GList *a;
96
97   G_LOCK (response);
98   printf ("Name:    %s\n", name);
99   if (error)
100     {
101       printf ("Error:   %s\n", error->message);
102       g_error_free (error);
103     }
104   else
105     {
106       for (a = addresses; a; a = a->next)
107         {
108           phys = g_inet_address_to_string (a->data);
109           printf ("Address: %s\n", phys);
110           g_free (phys);
111           g_object_unref (a->data);
112         }
113       g_list_free (addresses);
114     }
115   printf ("\n");
116
117   done_lookup ();
118   G_UNLOCK (response);
119 }
120
121 static void
122 print_resolved_service (const char *service,
123                         GList      *targets,
124                         GError     *error)
125 {
126   GList *t;  
127
128   G_LOCK (response);
129   printf ("Service: %s\n", service);
130   if (error)
131     {
132       printf ("Error: %s\n", error->message);
133       g_error_free (error);
134     }
135   else
136     {
137       for (t = targets; t; t = t->next)
138         {
139           printf ("%s:%u (pri %u, weight %u)\n",
140                   g_srv_target_get_hostname (t->data),
141                   g_srv_target_get_port (t->data),
142                   g_srv_target_get_priority (t->data),
143                   g_srv_target_get_weight (t->data));
144           g_srv_target_free (t->data);
145         }
146       g_list_free (targets);
147     }
148   printf ("\n");
149
150   done_lookup ();
151   G_UNLOCK (response);
152 }
153
154 static void
155 lookup_one_sync (const char *arg)
156 {
157   GError *error = NULL;
158
159   if (strchr (arg, '/'))
160     {
161       GList *targets;
162       /* service/protocol/domain */
163       char **parts = g_strsplit (arg, "/", 3);
164
165       if (!parts || !parts[2])
166         usage ();
167
168       targets = g_resolver_lookup_service (resolver,
169                                            parts[0], parts[1], parts[2],
170                                            cancellable, &error);
171       print_resolved_service (arg, targets, error);
172     }
173   else if (g_hostname_is_ip_address (arg))
174     {
175       GInetAddress *addr = g_inet_address_new_from_string (arg);
176       char *name;
177
178       name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error);
179       print_resolved_name (arg, name, error);
180       g_object_unref (addr);
181     }
182   else
183     {
184       GList *addresses;
185
186       addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error);
187       print_resolved_addresses (arg, addresses, error);
188     }
189 }
190
191 static gpointer
192 lookup_thread (gpointer arg)
193 {
194   lookup_one_sync (arg);
195   return NULL;
196 }
197
198 static void
199 start_sync_lookups (char **argv, int argc)
200 {
201   int i;
202
203   for (i = 0; i < argc; i++)
204     {
205       GThread *thread;
206
207       thread = g_thread_new ("lookup", lookup_thread, argv[i], NULL);
208       g_assert (thread != NULL);
209       g_thread_unref (thread);
210     }
211 }
212
213 static void
214 lookup_by_addr_callback (GObject *source, GAsyncResult *result,
215                          gpointer user_data)
216 {
217   const char *phys = user_data;
218   GError *error = NULL;
219   char *name;
220
221   name = g_resolver_lookup_by_address_finish (resolver, result, &error);
222   print_resolved_name (phys, name, error);
223 }
224
225 static void
226 lookup_by_name_callback (GObject *source, GAsyncResult *result,
227                          gpointer user_data)
228 {
229   const char *name = user_data;
230   GError *error = NULL;
231   GList *addresses;
232
233   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
234   print_resolved_addresses (name, addresses, error);
235 }
236
237 static void
238 lookup_service_callback (GObject *source, GAsyncResult *result,
239                          gpointer user_data)
240 {
241   const char *service = user_data;
242   GError *error = NULL;
243   GList *targets;
244
245   targets = g_resolver_lookup_service_finish (resolver, result, &error);
246   print_resolved_service (service, targets, error);
247 }
248
249 static void
250 start_async_lookups (char **argv, int argc)
251 {
252   int i;
253
254   for (i = 0; i < argc; i++)
255     {
256       if (strchr (argv[i], '/'))
257         {
258           /* service/protocol/domain */
259           char **parts = g_strsplit (argv[i], "/", 3);
260
261           if (!parts || !parts[2])
262             usage ();
263
264           g_resolver_lookup_service_async (resolver,
265                                            parts[0], parts[1], parts[2],
266                                            cancellable,
267                                            lookup_service_callback, argv[i]);
268         }
269       else if (g_hostname_is_ip_address (argv[i]))
270         {
271           GInetAddress *addr = g_inet_address_new_from_string (argv[i]);
272
273           g_resolver_lookup_by_address_async (resolver, addr, cancellable,
274                                               lookup_by_addr_callback, argv[i]);
275           g_object_unref (addr);
276         }
277       else
278         {
279           g_resolver_lookup_by_name_async (resolver, argv[i], cancellable,
280                                            lookup_by_name_callback,
281                                            argv[i]);
282         }
283
284       /* Stress-test the reloading code */
285       g_signal_emit_by_name (resolver, "reload");
286     }
287 }
288
289 static void
290 print_connectable_sockaddr (GSocketAddress *sockaddr,
291                             GError         *error)
292 {
293   char *phys;
294
295   if (error)
296     {
297       printf ("Error:   %s\n", error->message);
298       g_error_free (error);
299     }
300   else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
301     {
302       printf ("Error:   Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr));
303       g_object_unref (sockaddr);
304     }
305   else
306     {
307       GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr);
308       phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa));
309       printf ("Address: %s%s%s:%d\n",
310               strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "",
311               g_inet_socket_address_get_port (isa));
312       g_free (phys);
313       g_object_unref (sockaddr);
314     }
315 }
316
317 static void
318 do_sync_connectable (GSocketAddressEnumerator *enumerator)
319 {
320   GSocketAddress *sockaddr;
321   GError *error = NULL;
322
323   while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error)))
324     print_connectable_sockaddr (sockaddr, error);
325
326   g_object_unref (enumerator);
327   done_lookup ();
328 }
329
330 static void do_async_connectable (GSocketAddressEnumerator *enumerator);
331
332 static void
333 got_next_async (GObject *source, GAsyncResult *result, gpointer user_data)
334 {
335   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source);
336   GSocketAddress *sockaddr;
337   GError *error = NULL;
338
339   sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
340   if (sockaddr || error)
341     print_connectable_sockaddr (sockaddr, error);
342   if (sockaddr)
343     do_async_connectable (enumerator);
344   else
345     {
346       g_object_unref (enumerator);
347       done_lookup ();
348     }
349 }
350
351 static void
352 do_async_connectable (GSocketAddressEnumerator *enumerator)
353 {
354   g_socket_address_enumerator_next_async (enumerator, cancellable,
355                                           got_next_async, NULL);
356 }
357
358 static void
359 do_connectable (const char *arg, gboolean synchronous, guint count)
360 {
361   char **parts;
362   GSocketConnectable *connectable;
363   GSocketAddressEnumerator *enumerator;
364
365   if (strchr (arg, '/'))
366     {
367       /* service/protocol/domain */
368       parts = g_strsplit (arg, "/", 3);
369       if (!parts || !parts[2])
370         usage ();
371
372       connectable = g_network_service_new (parts[0], parts[1], parts[2]);
373     }
374   else
375     {
376       guint16 port;
377
378       parts = g_strsplit (arg, ":", 2);
379       if (parts && parts[1])
380         {
381           arg = parts[0];
382           port = strtoul (parts[1], NULL, 10);
383         }
384       else
385         port = 0;
386
387       if (g_hostname_is_ip_address (arg))
388         {
389           GInetAddress *addr = g_inet_address_new_from_string (arg);
390           GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port);
391
392           g_object_unref (addr);
393           connectable = G_SOCKET_CONNECTABLE (sockaddr);
394         }
395       else
396         connectable = g_network_address_new (arg, port);
397     }
398
399   while (count--)
400     {
401       enumerator = g_socket_connectable_enumerate (connectable);
402
403       if (synchronous)
404         do_sync_connectable (enumerator);
405       else
406         do_async_connectable (enumerator);
407     }
408   
409   g_object_unref (connectable);
410 }
411
412 #ifdef G_OS_UNIX
413 static int cancel_fds[2];
414
415 static void
416 interrupted (int sig)
417 {
418   gssize c;
419
420   signal (SIGINT, SIG_DFL);
421   c = write (cancel_fds[1], "x", 1);
422   g_assert_cmpint(c, ==, 1);
423 }
424
425 static gboolean
426 async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
427 {
428   g_cancellable_cancel (cancel);
429   return FALSE;
430 }
431 #endif
432
433 int
434 main (int argc, char **argv)
435 {
436   gboolean synchronous = FALSE;
437   guint connectable_count = 0;
438 #ifdef G_OS_UNIX
439   GIOChannel *chan;
440   guint watch;
441 #endif
442
443   g_type_init ();
444
445   /* FIXME: use GOptionContext */
446   while (argc >= 2 && argv[1][0] == '-')
447     {
448       if (!strcmp (argv[1], "-s"))
449         synchronous = TRUE;
450       else if (!strcmp (argv[1], "-c"))
451         {
452           connectable_count = atoi (argv[2]);
453           argv++;
454           argc--;
455         }
456       else
457         usage ();
458
459       argv++;
460       argc--;
461     }
462
463   if (argc < 2 || (argc > 2 && connectable_count))
464     usage ();
465
466   resolver = g_resolver_get_default ();
467
468   cancellable = g_cancellable_new ();
469
470 #ifdef G_OS_UNIX
471   /* Set up cancellation; we want to cancel if the user ^C's the
472    * program, but we can't cancel directly from an interrupt.
473    */
474   signal (SIGINT, interrupted);
475
476   if (pipe (cancel_fds) == -1)
477     {
478       perror ("pipe");
479       exit (1);
480     }
481   chan = g_io_channel_unix_new (cancel_fds[0]);
482   watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
483   g_io_channel_unref (chan);
484 #endif
485
486   nlookups = argc - 1;
487   loop = g_main_loop_new (NULL, TRUE);
488
489   if (connectable_count)
490     {
491       nlookups = connectable_count;
492       do_connectable (argv[1], synchronous, connectable_count);
493     }
494   else
495     {
496       if (synchronous)
497         start_sync_lookups (argv + 1, argc - 1);
498       else
499         start_async_lookups (argv + 1, argc - 1);
500     }
501
502   g_main_loop_run (loop);
503   g_main_loop_unref (loop);
504
505 #ifdef G_OS_UNIX
506   g_source_remove (watch);
507 #endif
508   g_object_unref (cancellable);
509
510   return 0;
511 }