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.1 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, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "config.h" |
22 | #include <glib.h> |
23 | #include "glibintl.h" |
24 | |
25 | #include <signal.h> |
26 | #include <stdio.h> |
27 | #include <stdlib.h> |
28 | #include <string.h> |
29 | #ifdef G_OS_UNIX |
30 | #include <unistd.h> |
31 | #endif |
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 | static gboolean synchronous = FALSE; |
40 | static guint connectable_count = 0; |
41 | static GResolverRecordType record_type = 0; |
42 | |
43 | static G_NORETURN void |
44 | usage (void) |
45 | { |
46 | fprintf (stderr, format: "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n" ); |
47 | fprintf (stderr, format: "Usage: resolver [-s] [-t MX|TXT|NS|SOA] rrname ...\n" ); |
48 | fprintf (stderr, format: " resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n" ); |
49 | fprintf (stderr, format: " Use -s to do synchronous lookups.\n" ); |
50 | fprintf (stderr, format: " Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n" ); |
51 | fprintf (stderr, format: " The given NUMBER determines how many times the connectable will be enumerated.\n" ); |
52 | fprintf (stderr, format: " Use -t with MX, TXT, NS or SOA to look up DNS records of those types.\n" ); |
53 | exit (status: 1); |
54 | } |
55 | |
56 | G_LOCK_DEFINE_STATIC (response); |
57 | |
58 | static void |
59 | done_lookup (void) |
60 | { |
61 | nlookups--; |
62 | if (nlookups == 0) |
63 | { |
64 | /* In the sync case we need to make sure we don't call |
65 | * g_main_loop_quit before the loop is actually running... |
66 | */ |
67 | g_idle_add (function: (GSourceFunc)g_main_loop_quit, data: loop); |
68 | } |
69 | } |
70 | |
71 | static void |
72 | print_resolved_name (const char *phys, |
73 | char *name, |
74 | GError *error) |
75 | { |
76 | G_LOCK (response); |
77 | printf (format: "Address: %s\n" , phys); |
78 | if (error) |
79 | { |
80 | printf (format: "Error: %s\n" , error->message); |
81 | g_error_free (error); |
82 | } |
83 | else |
84 | { |
85 | printf (format: "Name: %s\n" , name); |
86 | g_free (mem: name); |
87 | } |
88 | printf (format: "\n" ); |
89 | |
90 | done_lookup (); |
91 | G_UNLOCK (response); |
92 | } |
93 | |
94 | static void |
95 | print_resolved_addresses (const char *name, |
96 | GList *addresses, |
97 | GError *error) |
98 | { |
99 | char *phys; |
100 | GList *a; |
101 | |
102 | G_LOCK (response); |
103 | printf (format: "Name: %s\n" , name); |
104 | if (error) |
105 | { |
106 | printf (format: "Error: %s\n" , error->message); |
107 | g_error_free (error); |
108 | } |
109 | else |
110 | { |
111 | for (a = addresses; a; a = a->next) |
112 | { |
113 | phys = g_inet_address_to_string (address: a->data); |
114 | printf (format: "Address: %s\n" , phys); |
115 | g_free (mem: phys); |
116 | g_object_unref (object: a->data); |
117 | } |
118 | g_list_free (list: addresses); |
119 | } |
120 | printf (format: "\n" ); |
121 | |
122 | done_lookup (); |
123 | G_UNLOCK (response); |
124 | } |
125 | |
126 | static void |
127 | print_resolved_service (const char *service, |
128 | GList *targets, |
129 | GError *error) |
130 | { |
131 | GList *t; |
132 | |
133 | G_LOCK (response); |
134 | printf (format: "Service: %s\n" , service); |
135 | if (error) |
136 | { |
137 | printf (format: "Error: %s\n" , error->message); |
138 | g_error_free (error); |
139 | } |
140 | else |
141 | { |
142 | for (t = targets; t; t = t->next) |
143 | { |
144 | printf (format: "%s:%u (pri %u, weight %u)\n" , |
145 | g_srv_target_get_hostname (target: t->data), |
146 | (guint)g_srv_target_get_port (target: t->data), |
147 | (guint)g_srv_target_get_priority (target: t->data), |
148 | (guint)g_srv_target_get_weight (target: t->data)); |
149 | g_srv_target_free (target: t->data); |
150 | } |
151 | g_list_free (list: targets); |
152 | } |
153 | printf (format: "\n" ); |
154 | |
155 | done_lookup (); |
156 | G_UNLOCK (response); |
157 | } |
158 | |
159 | static void |
160 | print_resolved_mx (const char *rrname, |
161 | GList *records, |
162 | GError *error) |
163 | { |
164 | const gchar *hostname; |
165 | guint16 priority; |
166 | GList *t; |
167 | |
168 | G_LOCK (response); |
169 | printf (format: "Domain: %s\n" , rrname); |
170 | if (error) |
171 | { |
172 | printf (format: "Error: %s\n" , error->message); |
173 | g_error_free (error); |
174 | } |
175 | else if (!records) |
176 | { |
177 | printf (format: "no MX records\n" ); |
178 | } |
179 | else |
180 | { |
181 | for (t = records; t; t = t->next) |
182 | { |
183 | g_variant_get (value: t->data, format_string: "(q&s)" , &priority, &hostname); |
184 | printf (format: "%s (pri %u)\n" , hostname, (guint)priority); |
185 | g_variant_unref (value: t->data); |
186 | } |
187 | g_list_free (list: records); |
188 | } |
189 | printf (format: "\n" ); |
190 | |
191 | done_lookup (); |
192 | G_UNLOCK (response); |
193 | } |
194 | |
195 | static void |
196 | print_resolved_txt (const char *rrname, |
197 | GList *records, |
198 | GError *error) |
199 | { |
200 | const gchar **contents; |
201 | GList *t; |
202 | gint i; |
203 | |
204 | G_LOCK (response); |
205 | printf (format: "Domain: %s\n" , rrname); |
206 | if (error) |
207 | { |
208 | printf (format: "Error: %s\n" , error->message); |
209 | g_error_free (error); |
210 | } |
211 | else if (!records) |
212 | { |
213 | printf (format: "no TXT records\n" ); |
214 | } |
215 | else |
216 | { |
217 | for (t = records; t; t = t->next) |
218 | { |
219 | if (t != records) |
220 | printf (format: "\n" ); |
221 | g_variant_get (value: t->data, format_string: "(^a&s)" , &contents); |
222 | for (i = 0; contents[i] != NULL; i++) |
223 | printf (format: "%s\n" , contents[i]); |
224 | g_variant_unref (value: t->data); |
225 | g_free (mem: contents); |
226 | } |
227 | g_list_free (list: records); |
228 | } |
229 | printf (format: "\n" ); |
230 | |
231 | done_lookup (); |
232 | G_UNLOCK (response); |
233 | } |
234 | |
235 | static void |
236 | print_resolved_soa (const char *rrname, |
237 | GList *records, |
238 | GError *error) |
239 | { |
240 | GList *t; |
241 | const gchar *primary_ns; |
242 | const gchar *administrator; |
243 | guint32 serial, refresh, retry, expire, ttl; |
244 | |
245 | G_LOCK (response); |
246 | printf (format: "Zone: %s\n" , rrname); |
247 | if (error) |
248 | { |
249 | printf (format: "Error: %s\n" , error->message); |
250 | g_error_free (error); |
251 | } |
252 | else if (!records) |
253 | { |
254 | printf (format: "no SOA records\n" ); |
255 | } |
256 | else |
257 | { |
258 | for (t = records; t; t = t->next) |
259 | { |
260 | g_variant_get (value: t->data, format_string: "(&s&suuuuu)" , &primary_ns, &administrator, |
261 | &serial, &refresh, &retry, &expire, &ttl); |
262 | printf (format: "%s %s (serial %u, refresh %u, retry %u, expire %u, ttl %u)\n" , |
263 | primary_ns, administrator, (guint)serial, (guint)refresh, |
264 | (guint)retry, (guint)expire, (guint)ttl); |
265 | g_variant_unref (value: t->data); |
266 | } |
267 | g_list_free (list: records); |
268 | } |
269 | printf (format: "\n" ); |
270 | |
271 | done_lookup (); |
272 | G_UNLOCK (response); |
273 | } |
274 | |
275 | static void |
276 | print_resolved_ns (const char *rrname, |
277 | GList *records, |
278 | GError *error) |
279 | { |
280 | GList *t; |
281 | const gchar *hostname; |
282 | |
283 | G_LOCK (response); |
284 | printf (format: "Zone: %s\n" , rrname); |
285 | if (error) |
286 | { |
287 | printf (format: "Error: %s\n" , error->message); |
288 | g_error_free (error); |
289 | } |
290 | else if (!records) |
291 | { |
292 | printf (format: "no NS records\n" ); |
293 | } |
294 | else |
295 | { |
296 | for (t = records; t; t = t->next) |
297 | { |
298 | g_variant_get (value: t->data, format_string: "(&s)" , &hostname); |
299 | printf (format: "%s\n" , hostname); |
300 | g_variant_unref (value: t->data); |
301 | } |
302 | g_list_free (list: records); |
303 | } |
304 | printf (format: "\n" ); |
305 | |
306 | done_lookup (); |
307 | G_UNLOCK (response); |
308 | } |
309 | |
310 | static void |
311 | lookup_one_sync (const char *arg) |
312 | { |
313 | GError *error = NULL; |
314 | |
315 | if (record_type != 0) |
316 | { |
317 | GList *records; |
318 | |
319 | records = g_resolver_lookup_records (resolver, rrname: arg, record_type, cancellable, error: &error); |
320 | switch (record_type) |
321 | { |
322 | case G_RESOLVER_RECORD_MX: |
323 | print_resolved_mx (rrname: arg, records, error); |
324 | break; |
325 | case G_RESOLVER_RECORD_SOA: |
326 | print_resolved_soa (rrname: arg, records, error); |
327 | break; |
328 | case G_RESOLVER_RECORD_NS: |
329 | print_resolved_ns (rrname: arg, records, error); |
330 | break; |
331 | case G_RESOLVER_RECORD_TXT: |
332 | print_resolved_txt (rrname: arg, records, error); |
333 | break; |
334 | default: |
335 | g_warn_if_reached (); |
336 | break; |
337 | } |
338 | } |
339 | else if (strchr (s: arg, c: '/')) |
340 | { |
341 | GList *targets; |
342 | /* service/protocol/domain */ |
343 | char **parts = g_strsplit (string: arg, delimiter: "/" , max_tokens: 3); |
344 | |
345 | if (!parts || !parts[2]) |
346 | usage (); |
347 | |
348 | targets = g_resolver_lookup_service (resolver, |
349 | service: parts[0], protocol: parts[1], domain: parts[2], |
350 | cancellable, error: &error); |
351 | print_resolved_service (service: arg, targets, error); |
352 | } |
353 | else if (g_hostname_is_ip_address (hostname: arg)) |
354 | { |
355 | GInetAddress *addr = g_inet_address_new_from_string (string: arg); |
356 | char *name; |
357 | |
358 | name = g_resolver_lookup_by_address (resolver, address: addr, cancellable, error: &error); |
359 | print_resolved_name (phys: arg, name, error); |
360 | g_object_unref (object: addr); |
361 | } |
362 | else |
363 | { |
364 | GList *addresses; |
365 | |
366 | addresses = g_resolver_lookup_by_name (resolver, hostname: arg, cancellable, error: &error); |
367 | print_resolved_addresses (name: arg, addresses, error); |
368 | } |
369 | } |
370 | |
371 | static gpointer |
372 | lookup_thread (gpointer arg) |
373 | { |
374 | lookup_one_sync (arg); |
375 | return NULL; |
376 | } |
377 | |
378 | static void |
379 | start_sync_lookups (char **argv, int argc) |
380 | { |
381 | int i; |
382 | |
383 | for (i = 0; i < argc; i++) |
384 | { |
385 | GThread *thread; |
386 | thread = g_thread_new (name: "lookup" , func: lookup_thread, data: argv[i]); |
387 | g_thread_unref (thread); |
388 | } |
389 | } |
390 | |
391 | static void |
392 | lookup_by_addr_callback (GObject *source, GAsyncResult *result, |
393 | gpointer user_data) |
394 | { |
395 | const char *phys = user_data; |
396 | GError *error = NULL; |
397 | char *name; |
398 | |
399 | name = g_resolver_lookup_by_address_finish (resolver, result, error: &error); |
400 | print_resolved_name (phys, name, error); |
401 | } |
402 | |
403 | static void |
404 | lookup_by_name_callback (GObject *source, GAsyncResult *result, |
405 | gpointer user_data) |
406 | { |
407 | const char *name = user_data; |
408 | GError *error = NULL; |
409 | GList *addresses; |
410 | |
411 | addresses = g_resolver_lookup_by_name_finish (resolver, result, error: &error); |
412 | print_resolved_addresses (name, addresses, error); |
413 | } |
414 | |
415 | static void |
416 | lookup_service_callback (GObject *source, GAsyncResult *result, |
417 | gpointer user_data) |
418 | { |
419 | const char *service = user_data; |
420 | GError *error = NULL; |
421 | GList *targets; |
422 | |
423 | targets = g_resolver_lookup_service_finish (resolver, result, error: &error); |
424 | print_resolved_service (service, targets, error); |
425 | } |
426 | |
427 | static void |
428 | lookup_records_callback (GObject *source, |
429 | GAsyncResult *result, |
430 | gpointer user_data) |
431 | { |
432 | const char *arg = user_data; |
433 | GError *error = NULL; |
434 | GList *records; |
435 | |
436 | records = g_resolver_lookup_records_finish (resolver, result, error: &error); |
437 | |
438 | switch (record_type) |
439 | { |
440 | case G_RESOLVER_RECORD_MX: |
441 | print_resolved_mx (rrname: arg, records, error); |
442 | break; |
443 | case G_RESOLVER_RECORD_SOA: |
444 | print_resolved_soa (rrname: arg, records, error); |
445 | break; |
446 | case G_RESOLVER_RECORD_NS: |
447 | print_resolved_ns (rrname: arg, records, error); |
448 | break; |
449 | case G_RESOLVER_RECORD_TXT: |
450 | print_resolved_txt (rrname: arg, records, error); |
451 | break; |
452 | default: |
453 | g_warn_if_reached (); |
454 | break; |
455 | } |
456 | } |
457 | |
458 | static void |
459 | start_async_lookups (char **argv, int argc) |
460 | { |
461 | int i; |
462 | |
463 | for (i = 0; i < argc; i++) |
464 | { |
465 | if (record_type != 0) |
466 | { |
467 | g_resolver_lookup_records_async (resolver, rrname: argv[i], record_type, |
468 | cancellable, callback: lookup_records_callback, user_data: argv[i]); |
469 | } |
470 | else if (strchr (s: argv[i], c: '/')) |
471 | { |
472 | /* service/protocol/domain */ |
473 | char **parts = g_strsplit (string: argv[i], delimiter: "/" , max_tokens: 3); |
474 | |
475 | if (!parts || !parts[2]) |
476 | usage (); |
477 | |
478 | g_resolver_lookup_service_async (resolver, |
479 | service: parts[0], protocol: parts[1], domain: parts[2], |
480 | cancellable, |
481 | callback: lookup_service_callback, user_data: argv[i]); |
482 | } |
483 | else if (g_hostname_is_ip_address (hostname: argv[i])) |
484 | { |
485 | GInetAddress *addr = g_inet_address_new_from_string (string: argv[i]); |
486 | |
487 | g_resolver_lookup_by_address_async (resolver, address: addr, cancellable, |
488 | callback: lookup_by_addr_callback, user_data: argv[i]); |
489 | g_object_unref (object: addr); |
490 | } |
491 | else |
492 | { |
493 | g_resolver_lookup_by_name_async (resolver, hostname: argv[i], cancellable, |
494 | callback: lookup_by_name_callback, |
495 | user_data: argv[i]); |
496 | } |
497 | |
498 | /* Stress-test the reloading code */ |
499 | g_signal_emit_by_name (instance: resolver, detailed_signal: "reload" ); |
500 | } |
501 | } |
502 | |
503 | static void |
504 | print_connectable_sockaddr (GSocketAddress *sockaddr, |
505 | GError *error) |
506 | { |
507 | char *phys; |
508 | |
509 | if (error) |
510 | { |
511 | printf (format: "Error: %s\n" , error->message); |
512 | g_error_free (error); |
513 | } |
514 | else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr)) |
515 | { |
516 | printf (format: "Error: Unexpected sockaddr type '%s'\n" , g_type_name_from_instance (instance: (GTypeInstance *)sockaddr)); |
517 | g_object_unref (object: sockaddr); |
518 | } |
519 | else |
520 | { |
521 | GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr); |
522 | phys = g_inet_address_to_string (address: g_inet_socket_address_get_address (address: isa)); |
523 | printf (format: "Address: %s%s%s:%d\n" , |
524 | strchr (s: phys, c: ':') ? "[" : "" , phys, strchr (s: phys, c: ':') ? "]" : "" , |
525 | g_inet_socket_address_get_port (address: isa)); |
526 | g_free (mem: phys); |
527 | g_object_unref (object: sockaddr); |
528 | } |
529 | } |
530 | |
531 | static void |
532 | do_sync_connectable (GSocketAddressEnumerator *enumerator) |
533 | { |
534 | GSocketAddress *sockaddr; |
535 | GError *error = NULL; |
536 | |
537 | while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, error: &error))) |
538 | print_connectable_sockaddr (sockaddr, error); |
539 | |
540 | g_object_unref (object: enumerator); |
541 | done_lookup (); |
542 | } |
543 | |
544 | static void do_async_connectable (GSocketAddressEnumerator *enumerator); |
545 | |
546 | static void |
547 | got_next_async (GObject *source, GAsyncResult *result, gpointer user_data) |
548 | { |
549 | GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source); |
550 | GSocketAddress *sockaddr; |
551 | GError *error = NULL; |
552 | |
553 | sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, error: &error); |
554 | if (sockaddr || error) |
555 | print_connectable_sockaddr (sockaddr, error); |
556 | if (sockaddr) |
557 | do_async_connectable (enumerator); |
558 | else |
559 | { |
560 | g_object_unref (object: enumerator); |
561 | done_lookup (); |
562 | } |
563 | } |
564 | |
565 | static void |
566 | do_async_connectable (GSocketAddressEnumerator *enumerator) |
567 | { |
568 | g_socket_address_enumerator_next_async (enumerator, cancellable, |
569 | callback: got_next_async, NULL); |
570 | } |
571 | |
572 | static void |
573 | do_connectable (const char *arg, gboolean synchronous, guint count) |
574 | { |
575 | char **parts; |
576 | GSocketConnectable *connectable; |
577 | GSocketAddressEnumerator *enumerator; |
578 | |
579 | if (strchr (s: arg, c: '/')) |
580 | { |
581 | /* service/protocol/domain */ |
582 | parts = g_strsplit (string: arg, delimiter: "/" , max_tokens: 3); |
583 | if (!parts || !parts[2]) |
584 | usage (); |
585 | |
586 | connectable = g_network_service_new (service: parts[0], protocol: parts[1], domain: parts[2]); |
587 | } |
588 | else |
589 | { |
590 | guint16 port; |
591 | |
592 | parts = g_strsplit (string: arg, delimiter: ":" , max_tokens: 2); |
593 | if (parts && parts[1]) |
594 | { |
595 | arg = parts[0]; |
596 | port = strtoul (nptr: parts[1], NULL, base: 10); |
597 | } |
598 | else |
599 | port = 0; |
600 | |
601 | if (g_hostname_is_ip_address (hostname: arg)) |
602 | { |
603 | GInetAddress *addr = g_inet_address_new_from_string (string: arg); |
604 | GSocketAddress *sockaddr = g_inet_socket_address_new (address: addr, port); |
605 | |
606 | g_object_unref (object: addr); |
607 | connectable = G_SOCKET_CONNECTABLE (sockaddr); |
608 | } |
609 | else |
610 | connectable = g_network_address_new (hostname: arg, port); |
611 | } |
612 | |
613 | while (count--) |
614 | { |
615 | enumerator = g_socket_connectable_enumerate (connectable); |
616 | |
617 | if (synchronous) |
618 | do_sync_connectable (enumerator); |
619 | else |
620 | do_async_connectable (enumerator); |
621 | } |
622 | |
623 | g_object_unref (object: connectable); |
624 | } |
625 | |
626 | #ifdef G_OS_UNIX |
627 | static int cancel_fds[2]; |
628 | |
629 | static void |
630 | interrupted (int sig) |
631 | { |
632 | gssize c; |
633 | |
634 | signal (SIGINT, SIG_DFL); |
635 | c = write (fd: cancel_fds[1], buf: "x" , n: 1); |
636 | g_assert_cmpint(c, ==, 1); |
637 | } |
638 | |
639 | static gboolean |
640 | async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel) |
641 | { |
642 | g_cancellable_cancel (cancellable: cancel); |
643 | return FALSE; |
644 | } |
645 | #endif |
646 | |
647 | |
648 | static gboolean |
649 | record_type_arg (const gchar *option_name, |
650 | const gchar *value, |
651 | gpointer data, |
652 | GError **error) |
653 | { |
654 | if (g_ascii_strcasecmp (s1: value, s2: "MX" ) == 0) { |
655 | record_type = G_RESOLVER_RECORD_MX; |
656 | } else if (g_ascii_strcasecmp (s1: value, s2: "TXT" ) == 0) { |
657 | record_type = G_RESOLVER_RECORD_TXT; |
658 | } else if (g_ascii_strcasecmp (s1: value, s2: "SOA" ) == 0) { |
659 | record_type = G_RESOLVER_RECORD_SOA; |
660 | } else if (g_ascii_strcasecmp (s1: value, s2: "NS" ) == 0) { |
661 | record_type = G_RESOLVER_RECORD_NS; |
662 | } else { |
663 | g_set_error (err: error, G_OPTION_ERROR, code: G_OPTION_ERROR_BAD_VALUE, |
664 | format: "Specify MX, TXT, NS or SOA for the special record lookup types" ); |
665 | return FALSE; |
666 | } |
667 | |
668 | return TRUE; |
669 | } |
670 | |
671 | static const GOptionEntry option_entries[] = { |
672 | { "synchronous" , 's', 0, G_OPTION_ARG_NONE, &synchronous, "Synchronous connections" , NULL }, |
673 | { "connectable" , 'c', 0, G_OPTION_ARG_INT, &connectable_count, "Connectable count" , "C" }, |
674 | { "special-type" , 't', 0, G_OPTION_ARG_CALLBACK, record_type_arg, "Record type like MX, TXT, NS or SOA" , "RR" }, |
675 | { NULL }, |
676 | }; |
677 | |
678 | int |
679 | main (int argc, char **argv) |
680 | { |
681 | GOptionContext *context; |
682 | GError *error = NULL; |
683 | #ifdef G_OS_UNIX |
684 | GIOChannel *chan; |
685 | guint watch; |
686 | #endif |
687 | |
688 | context = g_option_context_new (parameter_string: "lookups ..." ); |
689 | g_option_context_add_main_entries (context, entries: option_entries, NULL); |
690 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
691 | { |
692 | g_printerr (format: "%s\n" , error->message); |
693 | g_error_free (error); |
694 | usage(); |
695 | } |
696 | |
697 | if (argc < 2 || (argc > 2 && connectable_count)) |
698 | usage (); |
699 | |
700 | resolver = g_resolver_get_default (); |
701 | |
702 | cancellable = g_cancellable_new (); |
703 | |
704 | #ifdef G_OS_UNIX |
705 | /* Set up cancellation; we want to cancel if the user ^C's the |
706 | * program, but we can't cancel directly from an interrupt. |
707 | */ |
708 | signal (SIGINT, handler: interrupted); |
709 | |
710 | if (pipe (pipedes: cancel_fds) == -1) |
711 | { |
712 | perror (s: "pipe" ); |
713 | exit (status: 1); |
714 | } |
715 | chan = g_io_channel_unix_new (fd: cancel_fds[0]); |
716 | watch = g_io_add_watch (channel: chan, condition: G_IO_IN, func: async_cancel, user_data: cancellable); |
717 | g_io_channel_unref (channel: chan); |
718 | #endif |
719 | |
720 | nlookups = argc - 1; |
721 | loop = g_main_loop_new (NULL, TRUE); |
722 | |
723 | if (connectable_count) |
724 | { |
725 | nlookups = connectable_count; |
726 | do_connectable (arg: argv[1], synchronous, count: connectable_count); |
727 | } |
728 | else |
729 | { |
730 | if (synchronous) |
731 | start_sync_lookups (argv: argv + 1, argc: argc - 1); |
732 | else |
733 | start_async_lookups (argv: argv + 1, argc: argc - 1); |
734 | } |
735 | |
736 | g_main_loop_run (loop); |
737 | g_main_loop_unref (loop); |
738 | |
739 | #ifdef G_OS_UNIX |
740 | g_source_remove (tag: watch); |
741 | #endif |
742 | g_object_unref (object: cancellable); |
743 | g_option_context_free (context); |
744 | |
745 | return 0; |
746 | } |
747 | |