2e065a73c3ae2b68dac698f77b14557a21a778b3
[dhcpcd-ui] / src / dhcpcd-gtk / prefs.c
1 /*
2  * dhcpcd-gtk
3  * Copyright 2009-2015 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 #include <sys/types.h>
28 #include <arpa/inet.h>
29 #include <net/if.h>
30
31 #include <errno.h>
32
33 #include "dhcpcd-gtk.h"
34
35 static GtkWidget *dialog, *blocks, *names, *controls, *clear, *rebind;
36 static GtkWidget *autoconf, *address, *router, *dns_servers, *dns_search;
37 static DHCPCD_OPTION *config;
38 static char *block, *name;
39 static DHCPCD_IF *iface;
40 static char **ifaces;
41
42 static void
43 config_err_dialog(DHCPCD_CONNECTION *con, bool writing, const char *txt)
44 {
45         GtkWidget *edialog;
46         char *t;
47
48         t = g_strconcat(_(writing ? "Error saving" : "Error reading"), " ",
49             dhcpcd_cffile(con), "\n\n", txt, NULL);
50         edialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
51             GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", t);
52         gtk_window_set_title(GTK_WINDOW(edialog), _("Config error"));
53         gtk_dialog_run(GTK_DIALOG(edialog));
54         gtk_widget_destroy(edialog);
55         g_free(t);
56 }
57
58 static void
59 show_config(DHCPCD_OPTION *conf)
60 {
61         const char *val;
62         bool autocnf;
63
64         if ((val = dhcpcd_config_get_static(conf, "ip_address=")) != NULL)
65                 autocnf = false;
66         else {
67                 if ((val = dhcpcd_config_get(conf, "inform")) == NULL &&
68                     (iface && iface->flags & IFF_POINTOPOINT))
69                         autocnf = false;
70                 else
71                         autocnf = true;
72         }
73         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autoconf), autocnf);
74         gtk_entry_set_text(GTK_ENTRY(address), val ? val : "");
75         val = dhcpcd_config_get_static(conf, "routers=");
76         gtk_entry_set_text(GTK_ENTRY(router), val ? val : "");
77         val = dhcpcd_config_get_static(conf, "domain_name_servers=");
78         gtk_entry_set_text(GTK_ENTRY(dns_servers), val ? val : "");
79         val = dhcpcd_config_get_static(conf, "domain_search=");
80         gtk_entry_set_text(GTK_ENTRY(dns_search), val ? val : "");
81 }
82
83 static char *
84 combo_active_text(GtkWidget *widget)
85 {
86         GtkListStore *store;
87         GtkTreeIter iter;
88         GValue val;
89         char *text;
90
91         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)));
92         if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
93                 return NULL;
94         memset(&val, 0, sizeof(val));
95         gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, 1, &val);
96         text = g_strdup(g_value_get_string(&val));
97         g_value_unset(&val);
98         return text;
99 }
100
101 static bool
102 set_option(DHCPCD_OPTION **conf, bool s, const char *opt, const char *val,
103         bool *ret)
104 {
105
106         if (s) {
107                 if (!dhcpcd_config_set_static(conf, opt, val))
108                         g_critical("dhcpcd_config_set_static: %s",
109                             strerror(errno));
110                 else
111                         return true;
112         } else {
113                 if (!dhcpcd_config_set(conf, opt, val))
114                         g_critical("dhcpcd_config_set: %s",
115                             strerror(errno));
116                 else
117                         return true;
118         }
119
120         if (ret)
121                 *ret = false;
122         return false;
123 }
124
125 static bool
126 make_config(DHCPCD_OPTION **conf)
127 {
128         const char *val, ns[] = "";
129         bool a, ret;
130
131         ret = true;
132         a = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(autoconf));
133         if (iface && iface->flags & IFF_POINTOPOINT)
134                 set_option(conf, true, "ip_address=", a ? NULL : ns, &ret);
135         else {
136                 val = gtk_entry_get_text(GTK_ENTRY(address));
137                 if (*val == '\0')
138                         val = NULL;
139                 set_option(conf, false, "inform", a ? val : NULL, &ret);
140                 set_option(conf, true, "ip_address=", a ? NULL : val, &ret);
141         }
142
143         val = gtk_entry_get_text(GTK_ENTRY(router));
144         if (a && *val == '\0')
145                 val = NULL;
146         set_option(conf, true, "routers=", val, &ret);
147
148         val = gtk_entry_get_text(GTK_ENTRY(dns_servers));
149         if (a && *val == '\0')
150                 val = NULL;
151         set_option(conf, true, "domain_name_servers=", val, &ret);
152
153         val = gtk_entry_get_text(GTK_ENTRY(dns_search));
154         if (a && *val == '\0')
155                 val = NULL;
156         set_option(conf, true, "domain_search=", val, &ret);
157
158         return ret;
159 }
160
161 static bool
162 write_config(DHCPCD_CONNECTION *con, DHCPCD_OPTION **conf)
163 {
164
165         if (make_config(conf) &&
166             !dhcpcd_config_write(con, block, name, *conf))
167         {
168                 const char *s;
169
170                 s = strerror(errno);
171                 g_warning("dhcpcd_config_write: %s", s);
172                 config_err_dialog(con, true, s);
173                 return false;
174         }
175         return true;
176 }
177
178 static GdkPixbuf *
179 load_icon(const char *iname)
180 {
181         int width, height;
182
183         if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height))
184                 return NULL;
185         return gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
186             iname, MIN(width, height), 0, NULL);
187 }
188
189 static void
190 set_name_active_icon(const char *iname)
191 {
192         GtkListStore *store;
193         GtkTreeIter iter;
194         GtkTreePath *path;
195         GdkPixbuf *pb;
196
197         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(names)));
198         if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(names), &iter))
199                 return;
200         pb = load_icon(iname);
201         if (pb) {
202                 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
203                 gtk_list_store_set(store, &iter, 0, pb, -1);
204                 g_object_unref(pb);
205                 gtk_tree_model_row_changed(GTK_TREE_MODEL(store), path, &iter);
206                 gtk_tree_path_free(path);
207         }
208 }
209
210 static GSList *
211 list_interfaces(DHCPCD_CONNECTION *con)
212 {
213         GSList *list;
214         char **i;
215
216         list = NULL;
217         dhcpcd_freev(ifaces);
218         ifaces = dhcpcd_interface_names_sorted(con);
219         for (i = ifaces; i && *i; i++)
220                 list = g_slist_append(list, *i);
221         return list;
222 }
223
224 static GSList *
225 list_ssids(WI_SCANS *scans)
226 {
227         GSList *list, *l;
228         WI_SCAN *w;
229         DHCPCD_WI_SCAN *wis;
230
231         list = NULL;
232         TAILQ_FOREACH(w, scans, next) {
233                 for (wis = w->scans; wis; wis = wis->next) {
234                         for (l = list; l; l = l->next)
235                                 if (g_strcmp0((const char *)l->data,
236                                         wis->ssid) == 0)
237                                         break;
238                         if (l == NULL)
239                                 list = g_slist_append(list, wis->ssid);
240                 }
241         }
242         return list;
243 }
244
245 static void
246 blocks_on_change(GtkWidget *widget, gpointer data)
247 {
248         DHCPCD_CONNECTION *con;
249         GtkListStore *store;
250         GtkTreeIter iter;
251         char **list, **lp;
252         const char *iname, *nn;
253         GSList *l, *new_names;
254         GdkPixbuf *pb;
255         int n;
256
257         con = (DHCPCD_CONNECTION *)data;
258         if (name) {
259                 write_config(con, &config);
260                 dhcpcd_config_free(config);
261                 config = NULL;
262                 show_config(config);
263                 g_free(block);
264                 g_free(name);
265                 name = NULL;
266         }
267         block = combo_active_text(widget);
268         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(names)));
269         gtk_list_store_clear(store);
270         list = dhcpcd_config_blocks(con, block);
271
272         if (g_strcmp0(block, "interface") == 0)
273                 new_names = list_interfaces(con);
274         else
275                 new_names = list_ssids(&wi_scans);
276
277         n = 0;
278         for (l = new_names; l; l = l->next) {
279                 nn = (const char *)l->data;
280                 if (list) {
281                         for (lp = list; *lp; lp++)
282                                 if (g_strcmp0(nn, *lp) == 0)
283                                         break;
284                         if (*lp)
285                                 iname = "document-save";
286                         else
287                                 iname = "document-new";
288                 } else
289                         iname = "document-new";
290                 pb = load_icon(iname);
291                 gtk_list_store_append(store, &iter);
292                 gtk_list_store_set(store, &iter, 0, pb, 1, nn, -1);
293                 g_object_unref(pb);
294                 n++;
295         }
296
297         for (lp = list; lp && *lp; lp++) {
298                 for (l = new_names; l; l = l->next)
299                         if (g_strcmp0((const char *)l->data, *lp) == 0)
300                                 break;
301                 if (l != NULL)
302                         continue;
303                 pb = load_icon("document-save");
304                 gtk_list_store_append(store, &iter);
305                 gtk_list_store_set(store, &iter, 0, pb, 1, *lp, -1);
306                 g_object_unref(pb);
307                 n++;
308         }
309         gtk_widget_set_sensitive(names, n);
310         g_slist_free(new_names);
311         dhcpcd_freev(list);
312 }
313
314 static void
315 names_on_change(_unused GtkWidget *widget, gpointer data)
316 {
317         DHCPCD_CONNECTION *con;
318         DHCPCD_IF *i;
319
320         con = (DHCPCD_CONNECTION *)data;
321         if (name) {
322                 write_config(con, &config);
323                 g_free(name);
324         }
325         name = combo_active_text(names);
326         dhcpcd_config_free(config);
327         iface = NULL;
328         if (g_strcmp0(block, "interface") == 0) {
329                 for (i = dhcpcd_interfaces(con); i; i = i->next)
330                         if (g_strcmp0(name, i->ifname) == 0) {
331                                 iface = i;
332                                 break;
333                         }
334         }
335         gtk_widget_set_sensitive(address,
336             !iface || (iface->flags & IFF_POINTOPOINT) == 0);
337         if (block && name) {
338                 errno = 0;
339                 config = dhcpcd_config_read(con, block, name);
340                 if (config == NULL && errno) {
341                         const char *s;
342
343                         s = strerror(errno);
344                         g_warning("dhcpcd_config_read: %s", s);
345                         config_err_dialog(con, false, s);
346                 }
347         } else
348                 config = NULL;
349         show_config(config);
350         gtk_widget_set_sensitive(controls, name ? true : false);
351         gtk_widget_set_sensitive(clear, name ? true : false);
352         gtk_widget_set_sensitive(rebind, name ? true : false);
353 }
354
355 static bool
356 valid_address(const char *val, bool allow_cidr)
357 {
358         char *addr, *p, *e;
359         struct in_addr in;
360         gint64 cidr;
361         bool retval;
362
363         addr = g_strdup(val);
364         if (allow_cidr) {
365                 p = strchr(addr, '/');
366                 if (p != NULL) {
367                         *p++ = '\0';
368                         errno = 0;
369                         e = NULL;
370                         cidr = g_ascii_strtoll(p, &e, 10);
371                         if (cidr < 0 || cidr > 32 ||
372                             errno != 0 || *e != '\0')
373                         {
374                                 retval = false;
375                                 goto out;
376                         }
377                 }
378         }
379         retval = inet_aton(addr, &in) == 0 ? false : true;
380
381 out:
382         g_free(addr);
383         return retval;
384 }
385
386 static bool
387 address_lost_focus(GtkEntry *entry)
388 {
389         const char *val;
390
391         val = gtk_entry_get_text(entry);
392         if (*val != '\0' && !valid_address(val, true))
393                 gtk_entry_set_text(entry, "");
394         return false;
395 }
396
397 static bool
398 entry_lost_focus(GtkEntry *entry)
399 {
400         const char *val;
401         char **a, **p;
402
403         val = gtk_entry_get_text(entry);
404         a = g_strsplit(val, " ", 0);
405         for (p = a; *p; p++) {
406                 if (**p != '\0' && !valid_address(*p, false)) {
407                         gtk_entry_set_text(entry, "");
408                         break;
409                 }
410         }
411         g_strfreev(a);
412         return false;
413 }
414
415 static void
416 on_clear(_unused GtkWidget *o, gpointer data)
417 {
418         DHCPCD_CONNECTION *con;
419
420         con = (DHCPCD_CONNECTION *)data;
421         dhcpcd_config_free(config);
422         config = NULL;
423         if (dhcpcd_config_write(con, block, name, config)) {
424                 set_name_active_icon("document-new");
425                 show_config(config);
426         } else
427                 g_critical("dhcpcd_config_write: %s", strerror(errno));
428 }
429
430 static void
431 on_rebind(_unused GObject *widget, gpointer data)
432 {
433         DHCPCD_CONNECTION *con;
434         DHCPCD_IF *i;
435
436         con = (DHCPCD_CONNECTION *)data;
437         if (write_config(con, &config)) {
438                 set_name_active_icon(config == NULL ?
439                     "document-new" : "document-save");
440                 show_config(config);
441                 if (g_strcmp0(block, "interface") == 0) {
442                         if (dhcpcd_rebind(con, iface->ifname) == -1)
443                                 g_critical("dhcpcd_rebind %s: %s",
444                                     iface->ifname, strerror(errno));
445                 } else {
446                         for (i = dhcpcd_interfaces(con); i; i = i->next) {
447                                 if (g_strcmp0(i->ssid, name) == 0) {
448                                         if (dhcpcd_rebind(con, i->ifname) == -1)
449                                                 g_critical(
450                                                     "dhcpcd_rebind %s: %s",
451                                                     i->ifname,
452                                                     strerror(errno));
453                                 }
454                         }
455                 }
456         }
457 }
458
459 static void
460 on_destroy(_unused GObject *o, gpointer data)
461 {
462
463         if (name != NULL) {
464                 write_config((DHCPCD_CONNECTION *)data, &config);
465                 g_free(block);
466                 g_free(name);
467                 block = name = NULL;
468         }
469         dhcpcd_config_free(config);
470         config = NULL;
471         dhcpcd_freev(ifaces);
472         ifaces = NULL;
473         dialog = NULL;
474
475 }
476
477 static void
478 prefs_close(void)
479 {
480         if (dialog != NULL) {
481                 gtk_widget_destroy(dialog);
482                 dialog = NULL;
483         }
484 }
485
486 void
487 prefs_abort(void)
488 {
489         g_free(name);
490         name = NULL;
491         prefs_close();
492 }
493
494 #if GTK_MAJOR_VERSION == 2
495 static GtkWidget *
496 gtk_separator_new(GtkOrientation o)
497 {
498
499         if (o == GTK_ORIENTATION_HORIZONTAL)
500                 return gtk_hseparator_new();
501         else
502                 return gtk_vseparator_new();
503 }
504 #endif
505
506 void
507 prefs_show(DHCPCD_CONNECTION *con)
508 {
509         GtkWidget *dialog_vbox, *hbox, *vbox, *table, *w;
510         GtkListStore *store;
511         GtkTreeIter iter;
512         GtkCellRenderer *rend;
513         GdkPixbuf *pb;
514
515         if (dialog) {
516                 gtk_window_present(GTK_WINDOW(dialog));
517                 return;
518         }
519
520         if (g_strcmp0(dhcpcd_status(con), "down") == 0)
521                 return;
522
523         dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
524         g_signal_connect(G_OBJECT(dialog), "destroy",
525             G_CALLBACK(on_destroy), con);
526
527         gtk_window_set_title(GTK_WINDOW(dialog), _("Network Preferences"));
528         gtk_window_set_resizable(GTK_WINDOW(dialog), false);
529         gtk_window_set_icon_name(GTK_WINDOW(dialog),
530             "preferences-system-network");
531         gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
532         gtk_window_set_type_hint(GTK_WINDOW(dialog),
533             GDK_WINDOW_TYPE_HINT_DIALOG);
534
535         dialog_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
536         gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
537         gtk_container_add(GTK_CONTAINER(dialog), dialog_vbox);
538
539         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
540         gtk_box_pack_start(GTK_BOX(dialog_vbox), hbox, false, false, 3);
541         w = gtk_label_new("Configure:");
542         gtk_box_pack_start(GTK_BOX(hbox), w, false, false, 3);
543         store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
544         pb = load_icon("network-wired");
545         gtk_list_store_append(store, &iter);
546         gtk_list_store_set(store, &iter, 0, pb, 1, "interface", -1);
547         g_object_unref(pb);
548         pb = load_icon("network-wireless");
549         gtk_list_store_append(store, &iter);
550         gtk_list_store_set(store, &iter, 0, pb, 1, "SSID", -1);
551         g_object_unref(pb);
552         blocks = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
553         rend = gtk_cell_renderer_pixbuf_new();
554         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, false);
555         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
556             rend, "pixbuf", 0);
557         rend = gtk_cell_renderer_text_new();
558         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, true);
559         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
560             rend, "text", 1);
561         gtk_combo_box_set_active(GTK_COMBO_BOX(blocks), 0);
562         gtk_box_pack_start(GTK_BOX(hbox), blocks, false, false, 3);
563         store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
564         names = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
565         rend = gtk_cell_renderer_pixbuf_new();
566         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, false);
567         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names),
568             rend, "pixbuf", 0);
569         rend = gtk_cell_renderer_text_new();
570         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, true);
571         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names), rend, "text", 1);
572         gtk_widget_set_sensitive(names, false);
573         gtk_box_pack_start(GTK_BOX(hbox), names, false, false, 3);
574         g_signal_connect(G_OBJECT(blocks), "changed",
575             G_CALLBACK(blocks_on_change), con);
576         g_signal_connect(G_OBJECT(names), "changed",
577             G_CALLBACK(names_on_change), con);
578
579         w = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
580         gtk_box_pack_start(GTK_BOX(dialog_vbox), w, true, false, 3);
581         controls = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
582         gtk_widget_set_sensitive(controls, false);
583         gtk_box_pack_start(GTK_BOX(dialog_vbox), controls, true, true, 0);
584         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
585         gtk_box_pack_start(GTK_BOX(controls), vbox, false, false, 0);
586         autoconf = gtk_check_button_new_with_label(
587                 _("Automatically configure empty options"));
588         gtk_box_pack_start(GTK_BOX(vbox), autoconf, false, false, 3);
589         table = gtk_table_new(6, 2, false);
590         gtk_box_pack_start(GTK_BOX(controls), table, false, false, 0);
591
592 #define attach_label(a, b, c, d, e)                                           \
593         do {                                                                  \
594                 gtk_misc_set_alignment(GTK_MISC(a), 0.0, 0.5);                \
595                 gtk_table_attach(GTK_TABLE(table), a, b, c, d, e,             \
596                     GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 3, 3);      \
597         } while (0)
598 #define attach_entry(a, b, c, d, e)                                           \
599         gtk_table_attach(GTK_TABLE(table), a, b, c, d, e,                     \
600             GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 3, 3);
601
602         w = gtk_label_new(_("IP Address:"));
603         address = gtk_entry_new();
604         gtk_entry_set_max_length(GTK_ENTRY(address), 18);
605         g_signal_connect(G_OBJECT(address), "focus-out-event",
606             G_CALLBACK(address_lost_focus), NULL);
607         attach_label(w, 0, 1, 0, 1);
608         attach_entry(address, 1, 2, 0, 1);
609
610         w = gtk_label_new(_("Router:"));
611         router = gtk_entry_new();
612         gtk_entry_set_max_length(GTK_ENTRY(router), 15);
613         g_signal_connect(G_OBJECT(router), "focus-out-event",
614             G_CALLBACK(entry_lost_focus), NULL);
615         attach_label(w, 0, 1, 2, 3);
616         attach_entry(router, 1, 2, 2, 3);
617
618         w = gtk_label_new(_("DNS Servers:"));
619         dns_servers = gtk_entry_new();
620         g_signal_connect(G_OBJECT(dns_servers), "focus-out-event",
621             G_CALLBACK(entry_lost_focus), NULL);
622         attach_label(w, 0, 1, 3, 4);
623         attach_entry(dns_servers, 1, 2, 3, 4);
624
625         w = gtk_label_new(_("DNS Search:"));
626         dns_search = gtk_entry_new();
627         attach_label(w, 0, 1, 4, 5);
628         attach_entry(dns_search, 1, 2, 4, 5);
629
630         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
631         gtk_box_pack_start(GTK_BOX(dialog_vbox), hbox, true, true, 3);
632         clear = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
633         gtk_widget_set_sensitive(clear, false);
634         gtk_box_pack_start(GTK_BOX(hbox), clear, true, true, 0);
635         g_signal_connect(G_OBJECT(clear), "clicked",
636             G_CALLBACK(on_clear), con);
637         rebind = gtk_button_new_with_mnemonic(_("_Rebind"));
638         gtk_widget_set_sensitive(rebind, false);
639         w = gtk_image_new_from_icon_name("application-x-executable",
640             GTK_ICON_SIZE_BUTTON);
641         gtk_button_set_image(GTK_BUTTON(rebind), w);
642         gtk_box_pack_start(GTK_BOX(hbox), rebind, true, true, 0);
643         g_signal_connect(G_OBJECT(rebind), "clicked",
644             G_CALLBACK(on_rebind), con);
645         w = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
646         gtk_box_pack_end(GTK_BOX(hbox), w, true, true, 0);
647         g_signal_connect(G_OBJECT(w), "clicked",
648             G_CALLBACK(prefs_close), NULL);
649
650         blocks_on_change(blocks, con);
651         show_config(NULL);
652         gtk_widget_show_all(dialog);
653
654         if (!dhcpcd_config_writeable(con))
655                 config_err_dialog(con, true,
656                     _("The dhcpcd configuration file is not writeable"));
657 }