Fix crashing.
[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(struct if_msg *ifm)
150 {
151         GType otype;
152         GError *error;
153         GPtrArray *array;
154         GHashTable *config;
155         GSList *list = NULL;
156         struct if_ap *ifa;
157         guint i;
158         GValue *val;
159
160         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
161         otype = dbus_g_type_get_collection("GPtrArray", otype);
162
163         if (!dbus_g_proxy_call(dbus, "GetScanResults", &error,
164                                G_TYPE_STRING, ifm->name, G_TYPE_INVALID,
165                                otype, &array, G_TYPE_INVALID))
166                 error_exit(_("GetScanResults"), error);
167
168         for (i = 0; i < array->len; i++) {
169                 config = g_ptr_array_index(array, i);
170                 val = g_hash_table_lookup(config, "BSSID");
171                 if (val == NULL)
172                         continue;
173                 ifa = g_malloc0(sizeof(*ifa));
174                 ifa->bssid = g_strdup(g_value_get_string(val));
175                 val = g_hash_table_lookup(config, "Frequency");
176                 if (val)
177                         ifa->freq = g_value_get_int(val);
178                 val = g_hash_table_lookup(config, "Noise");
179                 if (val)
180                         ifa->noise = g_value_get_int(val);
181                 val = g_hash_table_lookup(config, "Level");
182                 if (val)
183                         ifa->level = g_value_get_int(val);
184                 val = g_hash_table_lookup(config, "Flags");
185                 if (val)
186                         ifa->flags = g_strdup(g_value_get_string(val));
187                 val = g_hash_table_lookup(config, "SSID");
188                 if (val)
189                         ifa->ssid = g_strdup(g_value_get_string(val));
190                 list = g_slist_append(list, ifa);
191         }
192         return list;
193 }
194
195 static struct if_msg *
196 make_if_msg(GHashTable *config)
197 {
198         GValue *val;
199         struct if_msg *ifm;
200
201         val = g_hash_table_lookup(config, "Interface");
202         if (val == NULL)
203                 return NULL;
204         ifm = g_malloc0(sizeof(*ifm));
205         ifm->name = g_strdup(g_value_get_string(val));
206         val = g_hash_table_lookup(config, "Reason");
207         if (val)
208                 ifm->reason = g_strdup(g_value_get_string(val));
209         val = g_hash_table_lookup(config, "Wireless");
210         if (val)
211                 ifm->wireless = g_value_get_boolean(val);
212         if (ifm->wireless) {
213                 val = g_hash_table_lookup(config, "SSID");
214                 if (val)
215                         ifm->ssid = g_strdup(g_value_get_string(val));
216         }
217         val = g_hash_table_lookup(config, "IPAddress");
218         if (val)
219                 ifm->ip.s_addr = g_value_get_uint(val);
220         val = g_hash_table_lookup(config, "SubnetCIDR");
221         if (val)
222                 ifm->cidr = g_value_get_uchar(val);
223         val = g_hash_table_lookup(config, "InterfaceOrder");
224         if (val) {
225                 g_strfreev(interface_order);
226                 interface_order = g_strsplit(g_value_get_string(val), " ", 0);
227         }
228         return ifm;
229 }
230
231 static gboolean
232 if_up(const struct if_msg *ifm)
233 {
234         const char *const *r;
235
236         for (r = up_reasons; *r; r++)
237                 if (g_strcmp0(*r, ifm->reason) == 0)
238                         return TRUE;
239         return FALSE;
240 }
241
242 static char *
243 print_if_msg(const struct if_msg *ifm)
244 {
245         char *msg, *p;
246         const char *reason = NULL;
247         size_t len;
248         gboolean showip, showssid;
249     
250         showip = TRUE;
251         showssid = FALSE;
252         if (if_up(ifm))
253                 reason = N_("Acquired address");
254         else {
255                 if (g_strcmp0(ifm->reason, "EXPIRE") == 0)
256                         reason = N_("Failed to renew");
257                 else if (g_strcmp0(ifm->reason, "CARRIER") == 0) {
258                         if (ifm->wireless) {
259                                 reason = N_("Asssociated with");
260                                 if (ifm->ssid != NULL)
261                                         showssid = TRUE;
262                         } else
263                                 reason = N_("Cable plugged in");
264                         showip = FALSE;
265                 } else if (g_strcmp0(ifm->reason, "NOCARRIER") == 0) {
266                         if (ifm->wireless) {
267                                 if (ifm->ssid != NULL || ifm->ip.s_addr != 0) {
268                                         reason = N_("Disassociated from");
269                                         showssid = TRUE;
270                                 } else
271                                     reason = N_("Not associated");
272                         } else
273                                 reason = N_("Cable unplugged");
274                         showip = FALSE;
275                 }
276         }
277         if (reason == NULL)
278                 reason = ifm->reason;
279         
280         len = strlen(ifm->name) + 3;
281         len += strlen(reason) + 1;
282         if (ifm->ip.s_addr != 0) {
283                 len += 16; /* 000. * 4 */
284                 if (ifm->cidr != 0)
285                         len += 3; /* /32 */
286         }
287         if (showssid)
288                 len += strlen(ifm->ssid) + 1;
289         msg = p = g_malloc(len);
290         p += g_snprintf(msg, len, "%s: %s", ifm->name, reason);
291         if (showssid)
292                 p += g_snprintf(p, len - (p - msg), " %s", ifm->ssid);
293         if (ifm->ip.s_addr != 0 && showip) {
294                 p += g_snprintf(p, len - (p - msg), " %s", inet_ntoa(ifm->ip));
295                 if (ifm->cidr != 0)
296                         g_snprintf(p, len - (p - msg), "/%d", ifm->cidr);
297         }
298         return msg;
299 }
300
301 static gint
302 if_msg_comparer(gconstpointer a, gconstpointer b)
303 {
304         const struct if_msg *ifa, *ifb;
305         const char *const *order;
306
307         ifa = (const struct if_msg *)a;
308         ifb = (const struct if_msg *)b;
309         for (order = (const char *const *)interface_order; *order; order++) {
310                 if (g_strcmp0(*order, ifa->name) == 0)
311                         return -1;
312                 if (g_strcmp0(*order, ifb->name) == 0)
313                         return 1;
314         }
315         return 0;
316 }
317
318 static gboolean
319 animate_carrier(_unused gpointer data)
320 {
321         if (ani_timer == 0)
322                 return FALSE;
323
324         switch(ani_counter++) {
325         case 0:
326                 gtk_status_icon_set_from_icon_name(status_icon, "network-transmit");
327                 break;
328         case 1:
329                 gtk_status_icon_set_from_icon_name(status_icon, "network-receive");
330                 break;
331         default:
332                 gtk_status_icon_set_from_icon_name(status_icon, "network-idle");
333                 ani_counter = 0;
334                 break;
335         }
336         return TRUE;
337 }
338
339 static gboolean
340 animate_online(_unused gpointer data)
341 {
342         if (ani_timer == 0)
343                 return FALSE;
344
345         if (ani_counter++ > 6) {
346                 ani_timer = 0;
347                 ani_counter = 0;
348                 return FALSE;
349         }
350
351         if (ani_counter % 2 == 0)
352                 gtk_status_icon_set_from_icon_name(status_icon, "network-idle");
353         else
354                 gtk_status_icon_set_from_icon_name(status_icon, "network-transmit-receive");
355         return TRUE;
356 }
357
358 static void
359 update_online(char **buffer)
360 {
361         gboolean ison, iscarrier;
362         char *msg, *msgs, *tmp;
363         const GList *gl;
364         const struct if_msg *ifm;
365
366         ison = iscarrier = FALSE;
367         msgs = NULL;
368         for (gl = interfaces; gl; gl = gl->next) {
369                 ifm = (const struct if_msg *)gl->data;
370                 if (if_up(ifm))
371                         ison = iscarrier = TRUE;
372                 if (!iscarrier && g_strcmp0(ifm->reason, "CARRIER") == 0)
373                         iscarrier = TRUE;
374                 msg = print_if_msg(ifm);
375                 if (msgs) {
376                         tmp = g_strconcat(msgs, "\n", msg, NULL);
377                         g_free(msgs);
378                         g_free(msg);
379                         msgs = tmp;
380                 } else
381                         msgs = msg;
382         }
383
384         if (online != ison || carrier != iscarrier) {
385                 online = ison;
386                 if (ani_timer != 0) {
387                         g_source_remove(ani_timer);
388                         ani_timer = 0;
389                         ani_counter = 0;
390                 }
391                 if (ison) {
392                         animate_online(NULL);
393                         ani_timer = g_timeout_add(300, animate_online, NULL);
394                 } else if (iscarrier) {
395                         animate_carrier(NULL);
396                         ani_timer = g_timeout_add(500, animate_carrier, NULL);
397                 } else {
398                         gtk_status_icon_set_from_icon_name(status_icon,
399                                                            "network-offline");
400                 }
401         }
402         gtk_status_icon_set_tooltip(status_icon, msgs);
403         if (buffer)
404                 *buffer = msgs;
405         else
406                 g_free(msgs);
407 }
408
409 void
410 notify_close(void)
411 {
412         if (nn != NULL)
413                 notify_notification_close(nn, NULL);
414 }
415
416 static void
417 notify_closed(void)
418 {
419         nn = NULL;
420 }
421
422 static void
423 notify(const char *title, const char *msg, const char *icon)
424 {
425         char **msgs, **m;
426
427         msgs = g_strsplit(msg, "\n", 0);
428         for (m = msgs; *m; m++)
429                 g_message("%s", *m);
430         g_strfreev(msgs);
431         if (nn != NULL)
432                 notify_notification_close(nn, NULL);
433         if (gtk_status_icon_get_visible(status_icon))
434                 nn = notify_notification_new_with_status_icon(title,
435                                                               msg,
436                                                               icon,
437                                                               status_icon);
438         else
439                 nn = notify_notification_new(title, msg, icon, NULL);
440         notify_notification_set_timeout(nn, 5000);
441         g_signal_connect(nn, "closed", G_CALLBACK(notify_closed), NULL);
442         notify_notification_show(nn, NULL);
443 }
444
445 static void
446 dhcpcd_event(_unused DBusGProxy *proxy, GHashTable *config, _unused void *data)
447 {
448         struct if_msg *ifm, *ifp;
449         gboolean rem;
450         GList *gl;
451         char *msg, *title;
452         const char *act, *net;
453         const char *const *r;
454         in_addr_t ipn;
455
456         ifm = make_if_msg(config);
457         if (ifm == NULL)
458                 return;
459
460         rem = ignore_if_msg(ifm);
461         ifp = NULL;
462         for (gl = interfaces; gl; gl = gl->next) {
463                 ifp = (struct if_msg *)gl->data;
464                 if (g_strcmp0(ifp->name, ifm->name) == 0) {
465                         free_if_msg(ifp);
466                         if (rem)
467                                 interfaces = g_list_delete_link(interfaces, gl);
468                         else
469                                 gl->data = ifm;
470                         break;
471                 }
472         }
473         if (ifp == NULL && !rem)
474                 interfaces = g_list_prepend(interfaces, ifm);
475         interfaces = g_list_sort(interfaces, if_msg_comparer);
476         update_online(NULL);
477
478         /* We should ignore renew and stop so we don't annoy the user */
479         if (g_strcmp0(ifm->reason, "RENEW") == 0 ||
480             g_strcmp0(ifm->reason, "STOP") == 0)
481                 return;
482
483         msg = print_if_msg(ifm);
484         title = NULL;
485         if (if_up(ifm))
486                 act = N_("Connected to ");
487         else
488                 act = NULL;
489         for (r = down_reasons; *r; r++) {
490                 if (g_strcmp0(*r, ifm->reason) == 0) {
491                         act = N_("Disconnected from ");
492                         break;
493                 }
494         }
495         if (act && ifm->ip.s_addr) {
496                 ipn = htonl(ifm->ip.s_addr);
497                 if (IN_LINKLOCAL(ipn))
498                         net = N_("private network");
499                 else if (IN_PRIVATE(ipn))
500                         net = N_("LAN");
501                 else
502                         net = N_("internet");
503                 title = g_strconcat(act, net, NULL);
504         }
505
506         if (title) {
507                 notify(title, msg, GTK_STOCK_NETWORK);
508                 g_free(title);
509         } else
510                 notify(N_("Interface event"), msg, GTK_STOCK_NETWORK);
511         g_free(msg);
512 }
513
514 static void
515 foreach_make_ifm(_unused gpointer key, gpointer value, _unused gpointer data)
516 {
517         struct if_msg *ifm;
518
519         ifm = make_if_msg((GHashTable *)value);
520         if (ignore_if_msg(ifm))
521                 g_free(ifm);
522         else if (ifm)
523                 interfaces = g_list_prepend(interfaces, ifm);
524 }
525
526 static void
527 dhcpcd_get_interfaces()
528 {
529         GHashTable *ifs;
530         GError *error = NULL;
531         GType otype;
532         char *msg;
533         GList *gl;
534         GSList *gsl;
535         GPtrArray *array;
536         struct if_msg *ifm;
537
538         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
539         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, otype);
540         if (!dbus_g_proxy_call(dbus, "GetInterfaces", &error,
541                                G_TYPE_INVALID,
542                                otype, &ifs, G_TYPE_INVALID))
543                 error_exit("GetInterfaces", error);
544         g_hash_table_foreach(ifs, foreach_make_ifm, NULL);
545         g_hash_table_unref(ifs);
546
547         /* Each interface config only remembers the last order when
548          * that interface was configured, so get the real order now. */
549         g_strfreev(interface_order);
550         interface_order = NULL;
551         if (!dbus_g_proxy_call(dbus, "ListInterfaces", &error,
552                                G_TYPE_INVALID,
553                                G_TYPE_STRV, &interface_order, G_TYPE_INVALID))
554                 error_exit("ListInterfaces", error);
555         interfaces = g_list_sort(interfaces, if_msg_comparer);
556
557         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
558         otype = dbus_g_type_get_collection("GPtrArray", otype);
559         for (gl = interfaces; gl; gl = gl->next) {
560                 ifm = (struct if_msg *)gl->data;
561                 if (!ifm->wireless)
562                         continue;
563                 if (!dbus_g_proxy_call(dbus, "GetScanResults", &error,
564                                        G_TYPE_STRING, ifm->name, G_TYPE_INVALID,
565                                        otype, &array, G_TYPE_INVALID)) 
566                 {
567                         g_message("GetScanResults: %s", error->message);
568                         g_clear_error(&error);
569                         continue;
570                 }
571                 for (gsl = ifm->scan_results; gsl; gsl = gsl->next)
572                         g_free(gsl->data);
573                 g_slist_free(ifm->scan_results);
574                 ifm->scan_results = get_scan_results(ifm);
575         }
576
577         msg = NULL;
578         update_online(&msg);
579         // GTK+ 2.16 msg = gtk_status_icon_get_tooltip_text(status_icon);
580         if (msg != NULL) {
581                 notify("Interface status", msg, GTK_STOCK_NETWORK);
582                 g_free(msg);
583         }
584 }
585
586 static void
587 check_status(const char *status)
588 {
589         static char *last = NULL;
590         GList *gl;
591         char *version;
592         const char *msg;
593         gboolean refresh;
594         GError *error = NULL;
595
596         g_message("Status changed to %s", status);
597         if (g_strcmp0(status, "down") == 0) {
598                 for (gl = interfaces; gl; gl = gl->next)
599                         free_if_msg((struct if_msg *)gl->data);
600                 g_list_free(interfaces);
601                 interfaces = NULL;
602                 update_online(NULL);
603                 msg = N_(last? "Connection to dhcpcd lost" : "dhcpcd not running");
604                 gtk_status_icon_set_tooltip(status_icon, msg);
605                 notify(_("No network"), msg, GTK_STOCK_NETWORK);
606         }
607
608         refresh = FALSE;
609         if (last == NULL) {
610                 if (g_strcmp0(status, "down") != 0)
611                         refresh = TRUE;
612         } else {
613                 if (g_strcmp0(status, last) == 0)
614                         return;
615                 if (g_strcmp0(last, "down") == 0)
616                         refresh = TRUE;
617                 g_free(last);
618         }
619         last = g_strdup(status);
620
621         if (!refresh)
622                 return;
623         if (!dbus_g_proxy_call(dbus, "GetDhcpcdVersion", &error,
624                                G_TYPE_INVALID,
625                                G_TYPE_STRING, &version, G_TYPE_INVALID))
626                 error_exit(_("GetDhcpcdVersion"), error);
627         g_message(_("Connected to %s-%s"), "dhcpcd", version);
628         g_free(version);
629         dhcpcd_get_interfaces();
630 }
631
632 static void
633 dhcpcd_status(_unused DBusGProxy *proxy, const char *status, _unused void *data)
634 {
635         check_status(status);
636 }
637
638 static void
639 dhcpcd_scan_results(_unused DBusGProxy *proxy, const char *iface, _unused void *data)
640 {
641         struct if_msg *ifm;
642         GSList *gl;
643
644         ifm = find_if_msg(iface);
645         if (ifm == NULL)
646                 return;
647         g_message(_("%s: Received scan results"), ifm->name);
648         for (gl = ifm->scan_results; gl; gl = gl->next)
649                 free_if_ap((struct if_ap *)gl->data);
650         g_slist_free(ifm->scan_results);
651         ifm->scan_results = get_scan_results(ifm);
652 }
653
654 int
655 main(int argc, char *argv[])
656 {
657         DBusGConnection *bus;
658         GError *error = NULL;
659         char *version = NULL;
660         GType otype;
661         int tries = 5;
662
663                 
664         setlocale(LC_ALL, "");
665         bindtextdomain(PACKAGE, NULL);
666         bind_textdomain_codeset(PACKAGE, "UTF-8");
667         textdomain(PACKAGE); 
668
669         gtk_init(&argc, &argv);
670         g_set_application_name("dhcpcd Monitor");
671         status_icon = gtk_status_icon_new_from_icon_name("network-offline");
672         if (status_icon == NULL)
673                 status_icon = gtk_status_icon_new_from_stock(GTK_STOCK_DISCONNECT);
674         //network_offline = gtk_status_icon_get_pixbuf(status_icon);
675         
676         gtk_status_icon_set_tooltip(status_icon, _("Connecting to dhcpcd ..."));
677         gtk_status_icon_set_visible(status_icon, TRUE);
678
679         notify_init(PACKAGE);
680
681         g_message(_("Connecting to dbus ..."));
682         bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
683         if (bus == NULL || error != NULL)
684                 error_exit(_("Could not connect to system bus"), error);
685         dbus = dbus_g_proxy_new_for_name(bus,
686                                               DHCPCD_SERVICE,
687                                               DHCPCD_PATH,
688                                               DHCPCD_SERVICE);
689
690         g_message(_("Connecting to dhcpcd-dbus ..."));
691         while (--tries > 0) {
692                 g_clear_error(&error);
693                 if (dbus_g_proxy_call_with_timeout(dbus,
694                                                    "GetVersion",
695                                                    500,
696                                                    &error,
697                                                    G_TYPE_INVALID,
698                                                    G_TYPE_STRING,
699                                                    &version,
700                                                    G_TYPE_INVALID))
701                         break;
702         }
703         if (tries == 0)
704                 error_exit(_("GetVersion"), error);
705         g_message(_("Connected to %s-%s"), "dhcpcd-dbus", version);
706         g_free(version);
707
708         gtk_status_icon_set_tooltip(status_icon, _("Triggering dhcpcd ..."));
709         online = FALSE;
710         menu_init(status_icon);
711
712         if (!dbus_g_proxy_call(dbus, "GetStatus", &error,
713                                G_TYPE_INVALID,
714                                G_TYPE_STRING, &version, G_TYPE_INVALID))
715                 error_exit(_("GetStatus"), error);
716         check_status(version);
717         g_free(version);
718
719         otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
720         dbus_g_proxy_add_signal(dbus, "Event",
721                                 otype, G_TYPE_INVALID);
722         dbus_g_proxy_connect_signal(dbus, "Event",
723                                     G_CALLBACK(dhcpcd_event),
724                                     bus, NULL);
725         dbus_g_proxy_add_signal(dbus, "StatusChanged",
726                                 G_TYPE_STRING, G_TYPE_INVALID);
727         dbus_g_proxy_connect_signal(dbus, "StatusChanged",
728                                     G_CALLBACK(dhcpcd_status),
729                                     bus, NULL);
730         dbus_g_proxy_add_signal(dbus, "ScanResults",
731                                 G_TYPE_STRING, G_TYPE_INVALID);
732         dbus_g_proxy_connect_signal(dbus, "ScanResults",
733                                     G_CALLBACK(dhcpcd_scan_results),
734                                     bus, NULL);
735
736         gtk_main();
737         return 0;
738 }