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