d933da692121ca5560c8e0f1c973e920c370fb9f
[dhcpcd-ui] / main.c
1 /*
2  * dhcpcd-gtk
3  * Copyright 2009 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 /* TODO: Animate the icon from carrier -> address
28  * maybe use network-idle -> network-transmit ->
29  * network-receive -> network-transmit-receive */
30
31 #include <locale.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <libnotify/notify.h>
36
37 #include "dhcpcd-gtk.h"
38 #include "menu.h"
39
40 DBusGProxy *dbus = NULL;
41 GList *interfaces = NULL;
42
43 static GtkStatusIcon *status_icon;
44 static gint ani_timer;
45 static int ani_counter;
46 static gboolean online;
47 static gboolean carrier;
48 static char **interface_order;
49 static NotifyNotification *nn;
50
51 const char *const up_reasons[] = {
52         "BOUND",
53         "RENEW",
54         "REBIND",
55         "REBOOT",
56         "IPV4LL",
57         "INFORM",
58         "TIMEOUT",
59         NULL
60 };
61
62 const char *const down_reasons[] = {
63         "EXPIRE",
64         "FAIL",
65         "NAK",
66         "NOCARRIER",
67         "STOP",
68         NULL
69 };
70
71 static gboolean
72 ignore_if_msg(const struct if_msg *ifm)
73 {
74         if (g_strcmp0(ifm->reason, "STOP") == 0 ||
75             g_strcmp0(ifm->reason, "RELEASE") == 0)
76                 return TRUE;
77         return FALSE;
78 }
79
80 static struct if_msg *
81 find_if_msg(const char *iface)
82 {
83         GList *gl;
84         struct if_msg *ifm;
85
86         for (gl = interfaces; gl; gl = gl->next) {
87                 ifm = (struct if_msg *)gl->data;
88                 if (g_strcmp0(ifm->name, iface) == 0)
89                         return ifm;
90         }
91         return NULL;
92 }
93
94 static void
95 free_if_msg(struct if_msg *ifm)
96 {
97         GSList *gl;
98
99         g_free(ifm->name);
100         g_free(ifm->reason);
101         g_free(ifm->ssid);
102         for (gl = ifm->scan_results; gl; gl = gl->next)
103                 g_free(gl->data);
104         g_slist_free(ifm->scan_results);
105         g_free(ifm);
106 }
107
108 static void
109 free_if_ap(struct if_ap *ifa)
110 {
111         g_free(ifa->bssid);
112         g_free(ifa->flags);
113         g_free(ifa->ssid);
114         g_free(ifa);
115 }
116
117 static void
118 error_exit(const char *msg, GError *error)
119 {
120         GtkWidget *dialog;
121
122         if (error) {
123                 g_critical("%s: %s", msg, error->message);
124                 dialog = gtk_message_dialog_new(NULL,
125                                                 0,
126                                                 GTK_MESSAGE_ERROR,
127                                                 GTK_BUTTONS_CLOSE,
128                                                 "%s: %s",
129                                                 msg,
130                                                 error->message);
131         } else {
132                 g_critical("%s", msg);
133                 dialog = gtk_message_dialog_new(NULL,
134                                                 0,
135                                                 GTK_MESSAGE_ERROR,
136                                                 GTK_BUTTONS_CLOSE,
137                                                 "%s",
138                                                 msg);
139         }
140         gtk_dialog_run(GTK_DIALOG(dialog));
141         gtk_widget_destroy(dialog);
142         if (gtk_main_level())
143                 gtk_main_quit();
144         else
145                 exit(EXIT_FAILURE);
146 }
147
148 static GSList *
149 get_scan_results(const GPtrArray *array)
150 {
151         GHashTable *config;
152         GSList *list = NULL;
153         struct if_ap *ifa;
154         guint i;
155         GValue *val;
156
157         for (i = 0; i < array->len; i++) {
158                 config = g_ptr_array_index(array, i);
159                 val = g_hash_table_lookup(config, "BSSID");
160                 if (val == NULL)
161                         continue;
162                 ifa = g_malloc0(sizeof(*ifa));
163                 ifa->bssid = g_strdup(g_value_get_string(val));
164                 val = g_hash_table_lookup(config, "Frequency");
165                 if (val)
166                         ifa->freq = g_value_get_int(val);
167                 val = g_hash_table_lookup(config, "Noise");
168                 if (val)
169                         ifa->noise = g_value_get_int(val);
170                 val = g_hash_table_lookup(config, "Level");
171                 if (val)
172                         ifa->level = g_value_get_int(val);
173                 val = g_hash_table_lookup(config, "Flags");
174                 if (val)
175                         ifa->flags = g_strdup(g_value_get_string(val));
176                 val = g_hash_table_lookup(config, "SSID");
177                 if (val)
178                         ifa->ssid = g_strdup(g_value_get_string(val));
179                 list = g_slist_append(list, ifa);
180         }
181         return list;
182 }
183
184 static struct if_msg *
185 make_if_msg(GHashTable *config)
186 {
187         GValue *val;
188         struct if_msg *ifm;
189
190         val = g_hash_table_lookup(config, "Interface");
191         if (val == NULL)
192                 return NULL;
193         ifm = g_malloc0(sizeof(*ifm));
194         ifm->name = g_strdup(g_value_get_string(val));
195         val = g_hash_table_lookup(config, "Reason");
196         if (val)
197                 ifm->reason = g_strdup(g_value_get_string(val));
198         val = g_hash_table_lookup(config, "Wireless");
199         if (val)
200                 ifm->wireless = g_value_get_boolean(val);
201         if (ifm->wireless) {
202                 val = g_hash_table_lookup(config, "SSID");
203                 if (val)
204                         ifm->ssid = g_strdup(g_value_get_string(val));
205         }
206         val = g_hash_table_lookup(config, "IPAddress");
207         if (val)
208                 ifm->ip.s_addr = g_value_get_uint(val);
209         val = g_hash_table_lookup(config, "SubnetCIDR");
210         if (val)
211                 ifm->cidr = g_value_get_uchar(val);
212         val = g_hash_table_lookup(config, "InterfaceOrder");
213         if (val) {
214                 g_strfreev(interface_order);
215                 interface_order = g_strsplit(g_value_get_string(val), " ", 0);
216         }
217         return ifm;
218 }
219
220 static gboolean
221 if_up(const struct if_msg *ifm)
222 {
223         const char *const *r;
224
225         for (r = up_reasons; *r; r++)
226                 if (g_strcmp0(*r, ifm->reason) == 0)
227                         return TRUE;
228         return FALSE;
229 }
230
231 static char *
232 print_if_msg(const struct if_msg *ifm)
233 {
234         char *msg, *p;
235         const char *reason = NULL;
236         size_t len;
237         gboolean showip, showssid;
238     
239         showip = TRUE;
240         showssid = FALSE;
241         if (if_up(ifm))
242                 reason = N_("Acquired address");
243         else {
244                 if (g_strcmp0(ifm->reason, "EXPIRE") == 0)
245                         reason = N_("Failed to renew");
246                 else if (g_strcmp0(ifm->reason, "CARRIER") == 0) {
247                         if (ifm->wireless) {
248                                 reason = N_("Asssociated with");
249                                 if (ifm->ssid != NULL)
250                                         showssid = TRUE;
251                         } else
252                                 reason = N_("Cable plugged in");
253                         showip = FALSE;
254                 } else if (g_strcmp0(ifm->reason, "NOCARRIER") == 0) {
255                         if (ifm->wireless) {
256                                 if (ifm->ssid != NULL || ifm->ip.s_addr != 0) {
257                                         reason = N_("Disassociated from");
258                                         showssid = TRUE;
259                                 } else
260                                     reason = N_("Not associated");
261                         } else
262                                 reason = N_("Cable unplugged");
263                         showip = FALSE;
264                 }
265         }
266         if (reason == NULL)
267                 reason = ifm->reason;
268         
269         len = strlen(ifm->name) + 3;
270         len += strlen(reason) + 1;
271         if (ifm->ip.s_addr != 0) {
272                 len += 16; /* 000. * 4 */
273                 if (ifm->cidr != 0)
274                         len += 3; /* /32 */
275         }
276         if (showssid)
277                 len += strlen(ifm->ssid) + 1;
278         msg = p = g_malloc(len);
279         p += g_snprintf(msg, len, "%s: %s", ifm->name, reason);
280         if (showssid)
281                 p += g_snprintf(p, len - (p - msg), " %s", ifm->ssid);
282         if (ifm->ip.s_addr != 0 && showip) {
283                 p += g_snprintf(p, len - (p - msg), " %s", inet_ntoa(ifm->ip));
284                 if (ifm->cidr != 0)
285                         g_snprintf(p, len - (p - msg), "/%d", ifm->cidr);
286         }
287         return msg;
288 }
289
290 static gint
291 if_msg_comparer(gconstpointer a, gconstpointer b)
292 {
293         const struct if_msg *ifa, *ifb;
294         const char *const *order;
295
296         ifa = (const struct if_msg *)a;
297         ifb = (const struct if_msg *)b;
298         for (order = (const char *const *)interface_order; *order; order++) {
299                 if (g_strcmp0(*order, ifa->name) == 0)
300                         return -1;
301                 if (g_strcmp0(*order, ifb->name) == 0)
302                         return 1;
303         }
304         return 0;
305 }
306
307 static gboolean
308 animate_carrier(_unused gpointer data)
309 {
310         if (ani_timer == 0)
311                 return FALSE;
312
313         switch(ani_counter++) {
314         case 0:
315                 gtk_status_icon_set_from_icon_name(status_icon, "network-transmit");
316                 break;
317         case 1:
318                 gtk_status_icon_set_from_icon_name(status_icon, "network-receive");
319                 break;
320         default:
321                 gtk_status_icon_set_from_icon_name(status_icon, "network-idle");
322                 ani_counter = 0;
323                 break;
324         }
325         return TRUE;
326 }
327
328 static gboolean
329 animate_online(_unused gpointer data)
330 {
331         if (ani_timer == 0)
332                 return FALSE;
333
334         if (ani_counter++ > 6) {
335                 ani_timer = 0;
336                 ani_counter = 0;
337                 return FALSE;
338         }
339
340         if (ani_counter % 2 == 0)
341                 gtk_status_icon_set_from_icon_name(status_icon, "network-idle");
342         else
343                 gtk_status_icon_set_from_icon_name(status_icon, "network-transmit-receive");
344         return TRUE;
345 }
346
347 static void
348 update_online(char **buffer)
349 {
350         gboolean ison, iscarrier;
351         char *msg, *msgs, *tmp;
352         const GList *gl;
353         const struct if_msg *ifm;
354
355         ison = iscarrier = FALSE;
356         msgs = NULL;
357         for (gl = interfaces; gl; gl = gl->next) {
358                 ifm = (const struct if_msg *)gl->data;
359                 if (if_up(ifm))
360                         ison = iscarrier = TRUE;
361                 if (!iscarrier && g_strcmp0(ifm->reason, "CARRIER") == 0)
362                         iscarrier = TRUE;
363                 msg = print_if_msg(ifm);
364                 if (msgs) {
365                         tmp = g_strconcat(msgs, "\n", msg, NULL);
366                         g_free(msgs);
367                         g_free(msg);
368                         msgs = tmp;
369                 } else
370                         msgs = msg;
371         }
372
373         if (online != ison || carrier != iscarrier) {
374                 online = ison;
375                 if (ani_timer != 0) {
376                         g_source_remove(ani_timer);
377                         ani_timer = 0;
378                         ani_counter = 0;
379                 }
380                 if (ison) {
381                         animate_online(NULL);
382                         ani_timer = g_timeout_add(300, animate_online, NULL);
383                 } else if (iscarrier) {
384                         animate_carrier(NULL);
385                         ani_timer = g_timeout_add(500, animate_carrier, NULL);
386                 } else {
387                         gtk_status_icon_set_from_icon_name(status_icon,
388                                                            "network-offline");
389                 }
390         }
391         gtk_status_icon_set_tooltip(status_icon, msgs);
392         if (buffer)
393                 *buffer = msgs;
394         else
395                 g_free(msgs);
396 }
397
398 void
399 notify_close(void)
400 {
401         if (nn != NULL)
402                 notify_notification_close(nn, NULL);
403 }
404
405 static void
406 notify_closed(void)
407 {
408         nn = NULL;
409 }
410
411 static void
412 notify(const char *title, const char *msg, const char *icon)
413 {
414         char **msgs, **m;
415
416         msgs = g_strsplit(msg, "\n", 0);
417         for (m = msgs; *m; m++)
418                 g_message("%s", *m);
419         g_strfreev(msgs);
420         if (nn != NULL)
421                 notify_notification_close(nn, NULL);
422         if (gtk_status_icon_get_visible(status_icon))
423                 nn = notify_notification_new_with_status_icon(title,
424                                                               msg,
425                                                               icon,
426                                                               status_icon);
427         else
428                 nn = notify_notification_new(title, msg, icon, NULL);
429         notify_notification_set_timeout(nn, 5000);
430         g_signal_connect(nn, "closed", G_CALLBACK(notify_closed), NULL);
431         notify_notification_show(nn, NULL);
432 }
433
434 static void
435 dhcpcd_event(_unused DBusGProxy *proxy, GHashTable *config, _unused void *data)
436 {
437         struct if_msg *ifm, *ifp;
438         gboolean rem;
439         GList *gl;
440         char *msg, *title;
441         const char *act, *net;
442         const char *const *r;
443         in_addr_t ipn;
444
445         ifm = make_if_msg(config);
446         if (ifm == NULL)
447                 return;
448
449         rem = ignore_if_msg(ifm);
450         ifp = NULL;
451         for (gl = interfaces; gl; gl = gl->next) {
452                 ifp = (struct if_msg *)gl->data;
453                 if (g_strcmp0(ifp->name, ifm->name) == 0) {
454                         free_if_msg(ifp);
455                         if (rem)
456                                 interfaces = g_list_delete_link(interfaces, gl);
457                         else
458                                 gl->data = ifm;
459                         break;
460                 }
461         }
462         if (ifp == NULL && !rem)
463                 interfaces = g_list_prepend(interfaces, ifm);
464         interfaces = g_list_sort(interfaces, if_msg_comparer);
465         update_online(NULL);
466
467         /* We should ignore renew and stop so we don't annoy the user */
468         if (g_strcmp0(ifm->reason, "RENEW") == 0 ||
469             g_strcmp0(ifm->reason, "STOP") == 0)
470                 return;
471
472         msg = print_if_msg(ifm);
473         title = NULL;
474         if (if_up(ifm))
475                 act = N_("Connected to ");
476         else
477                 act = NULL;
478         for (r = down_reasons; *r; r++) {
479                 if (g_strcmp0(*r, ifm->reason) == 0) {
480                         act = N_("Disconnected from ");
481                         break;
482                 }
483         }
484         if (act && ifm->ip.s_addr) {
485                 ipn = htonl(ifm->ip.s_addr);
486                 if (IN_LINKLOCAL(ipn))
487                         net = N_("private network");
488                 else if (IN_PRIVATE(ipn))
489                         net = N_("LAN");
490                 else
491                         net = N_("internet");
492                 title = g_strconcat(act, net, NULL);
493         }
494
495         if (title) {
496                 notify(title, msg, GTK_STOCK_NETWORK);
497                 g_free(title);
498         } else
499                 notify(N_("Interface event"), msg, GTK_STOCK_NETWORK);
500         g_free(msg);
501 }
502
503 static void
504 foreach_make_ifm(_unused gpointer key, gpointer value, _unused gpointer data)
505 {
506         struct if_msg *ifm;
507
508         ifm = make_if_msg((GHashTable *)value);
509         if (ignore_if_msg(ifm))
510                 g_free(ifm);
511         else if (ifm)
512                 interfaces = g_list_prepend(interfaces, ifm);
513 }
514
515 static void
516 dhcpcd_get_interfaces()
517 {
518         GHashTable *ifs;
519         GError *error = NULL;
520         GType otype;
521         char *msg;
522         GList *gl;
523         GSList *gsl;
524         GPtrArray *array;
525         struct if_msg *ifm;
526
527         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
528         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, otype);
529         if (!dbus_g_proxy_call(dbus, "GetInterfaces", &error,
530                                G_TYPE_INVALID,
531                                otype, &ifs, G_TYPE_INVALID))
532                 error_exit("GetInterfaces", error);
533         g_hash_table_foreach(ifs, foreach_make_ifm, NULL);
534         g_hash_table_unref(ifs);
535
536         /* Each interface config only remembers the last order when
537          * that interface was configured, so get the real order now. */
538         g_strfreev(interface_order);
539         interface_order = NULL;
540         if (!dbus_g_proxy_call(dbus, "ListInterfaces", &error,
541                                G_TYPE_INVALID,
542                                G_TYPE_STRV, &interface_order, G_TYPE_INVALID))
543                 error_exit("ListInterfaces", error);
544         interfaces = g_list_sort(interfaces, if_msg_comparer);
545
546         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
547         otype = dbus_g_type_get_collection("GPtrArray", otype);
548         for (gl = interfaces; gl; gl = gl->next) {
549                 ifm = (struct if_msg *)gl->data;
550                 if (!ifm->wireless)
551                         continue;
552                 if (!dbus_g_proxy_call(dbus, "GetScanResults", &error,
553                                        G_TYPE_STRING, ifm->name, G_TYPE_INVALID,
554                                        otype, &array, G_TYPE_INVALID)) 
555                 {
556                         g_message("GetScanResults: %s", error->message);
557                         g_clear_error(&error);
558                         continue;
559                 }
560                 for (gsl = ifm->scan_results; gsl; gsl = gsl->next)
561                         g_free(gsl->data);
562                 g_slist_free(ifm->scan_results);
563                 ifm->scan_results = get_scan_results(array);
564         }
565
566         msg = NULL;
567         update_online(&msg);
568         // GTK+ 2.16 msg = gtk_status_icon_get_tooltip_text(status_icon);
569         if (msg != NULL) {
570                 notify("Interface status", msg, GTK_STOCK_NETWORK);
571                 g_free(msg);
572         }
573 }
574
575 static void
576 check_status(const char *status)
577 {
578         static char *last = NULL;
579         GList *gl;
580         char *version;
581         const char *msg;
582         gboolean refresh;
583         GError *error = NULL;
584
585         g_message("Status changed to %s", status);
586         if (g_strcmp0(status, "down") == 0) {
587                 for (gl = interfaces; gl; gl = gl->next)
588                         free_if_msg((struct if_msg *)gl->data);
589                 g_list_free(interfaces);
590                 interfaces = NULL;
591                 update_online(NULL);
592                 msg = N_(last? "Connection to dhcpcd lost" : "dhcpcd not running");
593                 gtk_status_icon_set_tooltip(status_icon, msg);
594                 notify(_("No network"), msg, GTK_STOCK_NETWORK);
595         }
596
597         refresh = FALSE;
598         if (last == NULL) {
599                 if (g_strcmp0(status, "down") != 0)
600                         refresh = TRUE;
601         } else {
602                 if (g_strcmp0(status, last) == 0)
603                         return;
604                 if (g_strcmp0(last, "down") == 0)
605                         refresh = TRUE;
606                 g_free(last);
607         }
608         last = g_strdup(status);
609
610         if (!refresh)
611                 return;
612         if (!dbus_g_proxy_call(dbus, "GetDhcpcdVersion", &error,
613                                G_TYPE_INVALID,
614                                G_TYPE_STRING, &version, G_TYPE_INVALID))
615                 error_exit(_("GetDhcpcdVersion"), error);
616         g_message(_("Connected to %s-%s"), "dhcpcd", version);
617         g_free(version);
618         dhcpcd_get_interfaces();
619 }
620
621 static void
622 dhcpcd_status(_unused DBusGProxy *proxy, const char *status, _unused void *data)
623 {
624         check_status(status);
625 }
626
627 static void
628 dhcpcd_scan_results(_unused DBusGProxy *proxy, const char *iface, GPtrArray *array, _unused void *data)
629 {
630         struct if_msg *ifm;
631         GSList *gl;
632
633         ifm = find_if_msg(iface);
634         if (ifm == NULL)
635                 return;
636         g_message(_("%s: Received scan results"), ifm->name);
637         for (gl = ifm->scan_results; gl; gl = gl->next)
638                 free_if_ap((struct if_ap *)gl->data);
639         g_slist_free(ifm->scan_results);
640         ifm->scan_results = get_scan_results(array);
641 }
642
643 int
644 main(int argc, char *argv[])
645 {
646         DBusGConnection *bus;
647         GError *error = NULL;
648         char *version = NULL;
649         GType otype;
650         int tries = 5;
651
652                 
653         setlocale(LC_ALL, "");
654         bindtextdomain(PACKAGE, NULL);
655         bind_textdomain_codeset(PACKAGE, "UTF-8");
656         textdomain(PACKAGE); 
657
658         gtk_init(&argc, &argv);
659         g_set_application_name("dhcpcd Monitor");
660         status_icon = gtk_status_icon_new_from_icon_name("network-offline");
661         if (status_icon == NULL)
662                 status_icon = gtk_status_icon_new_from_stock(GTK_STOCK_DISCONNECT);
663         //network_offline = gtk_status_icon_get_pixbuf(status_icon);
664         
665         gtk_status_icon_set_tooltip(status_icon, _("Connecting to dhcpcd ..."));
666         gtk_status_icon_set_visible(status_icon, TRUE);
667
668         notify_init(PACKAGE);
669
670         g_message(_("Connecting to dbus ..."));
671         bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
672         if (bus == NULL || error != NULL)
673                 error_exit(_("Could not connect to system bus"), error);
674         dbus = dbus_g_proxy_new_for_name(bus,
675                                               DHCPCD_SERVICE,
676                                               DHCPCD_PATH,
677                                               DHCPCD_SERVICE);
678
679         g_message(_("Connecting to dhcpcd-dbus ..."));
680         while (--tries > 0) {
681                 g_clear_error(&error);
682                 if (dbus_g_proxy_call_with_timeout(dbus,
683                                                    "GetVersion",
684                                                    500,
685                                                    &error,
686                                                    G_TYPE_INVALID,
687                                                    G_TYPE_STRING,
688                                                    &version,
689                                                    G_TYPE_INVALID))
690                         break;
691         }
692         if (tries == 0)
693                 error_exit(_("GetVersion"), error);
694         g_message(_("Connected to %s-%s"), "dhcpcd-dbus", version);
695         g_free(version);
696
697         gtk_status_icon_set_tooltip(status_icon, _("Triggering dhcpcd ..."));
698         online = FALSE;
699         menu_init(status_icon);
700
701         if (!dbus_g_proxy_call(dbus, "GetStatus", &error,
702                                G_TYPE_INVALID,
703                                G_TYPE_STRING, &version, G_TYPE_INVALID))
704                 error_exit(_("GetStatus"), error);
705         check_status(version);
706         g_free(version);
707
708         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
709         dbus_g_proxy_add_signal(dbus, "Event",
710                                 otype, G_TYPE_INVALID);
711         dbus_g_proxy_connect_signal(dbus, "Event",
712                                     G_CALLBACK(dhcpcd_event),
713                                     bus, NULL);
714         dbus_g_proxy_add_signal(dbus, "StatusChanged",
715                                 G_TYPE_STRING, G_TYPE_INVALID);
716         dbus_g_proxy_connect_signal(dbus, "StatusChanged",
717                                     G_CALLBACK(dhcpcd_status),
718                                     bus, NULL);
719         dbus_g_proxy_add_signal(dbus, "ScanResults",
720                                 G_TYPE_STRING, G_TYPE_INVALID);
721         dbus_g_proxy_connect_signal(dbus, "ScanResults",
722                                     G_CALLBACK(dhcpcd_scan_results),
723                                     bus, NULL);
724
725         gtk_main();
726         return 0;
727 }