0141ba070ab5ab29fbbc7d8fcb8f3aca92b9e009
[petitboot] / ui / ncurses / nc-config.c
1 /*
2  *  Copyright (C) 2013 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #define _GNU_SOURCE
19
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <pb-config/pb-config.h>
25 #include <talloc/talloc.h>
26 #include <types/types.h>
27 #include <log/log.h>
28
29 #include "config.h"
30 #include "nc-cui.h"
31 #include "nc-config.h"
32 #include "nc-widgets.h"
33
34 #define N_FIELDS        18
35
36 enum net_conf_type {
37         NET_CONF_TYPE_DHCP_ALL,
38         NET_CONF_TYPE_DHCP_ONE,
39         NET_CONF_TYPE_STATIC,
40 };
41
42 struct config_screen {
43         struct nc_scr           scr;
44         struct cui              *cui;
45         struct nc_widgetset     *widgetset;
46         bool                    exit;
47         void                    (*on_exit)(struct cui *);
48
49         int                     label_x;
50         int                     field_x;
51         int                     network_config_y;
52
53         struct {
54                 struct nc_widget_checkbox       *autoboot_f;
55                 struct nc_widget_label          *autoboot_l;
56                 struct nc_widget_textbox        *timeout_f;
57                 struct nc_widget_label          *timeout_l;
58
59                 struct nc_widget_label          *network_l;
60                 struct nc_widget_select         *network_f;
61
62                 struct nc_widget_label          *iface_l;
63                 struct nc_widget_select         *iface_f;
64                 struct nc_widget_label          *ip_addr_l;
65                 struct nc_widget_textbox        *ip_addr_f;
66                 struct nc_widget_label          *ip_mask_l;
67                 struct nc_widget_textbox        *ip_mask_f;
68                 struct nc_widget_label          *gateway_l;
69                 struct nc_widget_textbox        *gateway_f;
70                 struct nc_widget_label          *dns_l;
71                 struct nc_widget_textbox        *dns_f;
72
73                 struct nc_widget_button         *ok_b;
74                 struct nc_widget_button         *cancel_b;
75         } widgets;
76 };
77
78 static struct config_screen *config_screen_from_scr(struct nc_scr *scr)
79 {
80         struct config_screen *config_screen;
81
82         assert(scr->sig == pb_config_screen_sig);
83         config_screen = (struct config_screen *)
84                 ((char *)scr - (size_t)&((struct config_screen *)0)->scr);
85         assert(config_screen->scr.sig == pb_config_screen_sig);
86         return config_screen;
87 }
88
89 static void config_screen_process_key(struct nc_scr *scr, int key)
90 {
91         struct config_screen *screen = config_screen_from_scr(scr);
92         bool handled;
93
94         handled = widgetset_process_key(screen->widgetset, key);
95         if (screen->exit)
96                 screen->on_exit(screen->cui);
97         else if (handled)
98                 wrefresh(screen->scr.main_ncw);
99 }
100
101 static void config_screen_resize(struct nc_scr *scr)
102 {
103         struct config_screen *screen = config_screen_from_scr(scr);
104         (void)screen;
105 }
106
107 static int config_screen_post(struct nc_scr *scr)
108 {
109         struct config_screen *screen = config_screen_from_scr(scr);
110         widgetset_post(screen->widgetset);
111         nc_scr_frame_draw(scr);
112         wrefresh(scr->main_ncw);
113         return 0;
114 }
115
116 static int config_screen_unpost(struct nc_scr *scr)
117 {
118         struct config_screen *screen = config_screen_from_scr(scr);
119         widgetset_unpost(screen->widgetset);
120         return 0;
121 }
122
123 struct nc_scr *config_screen_scr(struct config_screen *screen)
124 {
125         return &screen->scr;
126 }
127
128 static int screen_process_form(struct config_screen *screen)
129 {
130         const struct system_info *sysinfo = screen->cui->sysinfo;
131         struct config *config = talloc_zero(screen, struct config);
132         enum net_conf_type net_conf_type;
133         struct interface_config *iface;
134         char *str, *end;
135         int rc;
136
137         config_set_defaults(config);
138
139         config->autoboot_enabled =
140                 widget_checkbox_get_value(screen->widgets.autoboot_f);
141
142
143         str = widget_textbox_get_value(screen->widgets.timeout_f);
144         if (str) {
145                 unsigned long x;
146                 errno = 0;
147                 x = strtoul(str, &end, 10);
148                 if (!errno && end != str)
149                         config->autoboot_timeout_sec = x;
150         }
151
152         net_conf_type = widget_select_get_value(screen->widgets.network_f);
153
154         /* if we don't have any network interfaces, prevent per-interface
155          * configuration */
156         if (sysinfo->n_interfaces == 0)
157                 net_conf_type = NET_CONF_TYPE_DHCP_ALL;
158
159         if (net_conf_type == NET_CONF_TYPE_DHCP_ALL) {
160                 config->network.n_interfaces = 0;
161
162         } else {
163                 int idx;
164
165                 iface = talloc_zero(config, struct interface_config);
166                 config->network.n_interfaces = 1;
167                 config->network.interfaces = talloc_array(config,
168                                 struct interface_config *, 1);
169                 config->network.interfaces[0] = iface;
170
171                 /* copy hwaddr (from the sysinfo interface data) to
172                  * the configuration */
173                 idx = widget_select_get_value(screen->widgets.iface_f);
174                 memcpy(iface->hwaddr, sysinfo->interfaces[idx]->hwaddr,
175                                 sizeof(iface->hwaddr));
176         }
177
178         if (net_conf_type == NET_CONF_TYPE_DHCP_ONE) {
179                 iface->method = CONFIG_METHOD_DHCP;
180         }
181
182         if (net_conf_type == NET_CONF_TYPE_STATIC) {
183                 iface->method = CONFIG_METHOD_STATIC;
184                 iface->static_config.address = talloc_asprintf(iface, "%s/%s",
185                                 widget_textbox_get_value(
186                                         screen->widgets.ip_addr_f),
187                                 widget_textbox_get_value(
188                                         screen->widgets.ip_mask_f));
189                 iface->static_config.gateway = talloc_strdup(iface,
190                                 widget_textbox_get_value(
191                                         screen->widgets.gateway_f));
192         }
193
194         str = widget_textbox_get_value(screen->widgets.dns_f);
195         if (str && strlen(str)) {
196                 char *dns, *tmp;
197                 int i;
198
199                 for (;;) {
200                         dns = strtok_r(str, " \t", &tmp);
201
202                         if (!dns)
203                                 break;
204
205                         i = config->network.n_dns_servers++;
206                         config->network.dns_servers = talloc_realloc(config,
207                                         config->network.dns_servers,
208                                         const char *,
209                                         config->network.n_dns_servers);
210                         config->network.dns_servers[i] =
211                                 talloc_strdup(config, dns);
212
213                         str = NULL;
214                 }
215         }
216
217         rc = cui_send_config(screen->cui, config);
218         talloc_free(config);
219
220         if (rc)
221                 pb_log("cui_send_config failed!\n");
222         else
223                 pb_debug("config sent!\n");
224
225         return 0;
226 }
227
228 static void ok_click(void *arg)
229 {
230         struct config_screen *screen = arg;
231         screen_process_form(screen);
232         screen->exit = true;
233 }
234
235 static void cancel_click(void *arg)
236 {
237         struct config_screen *screen = arg;
238         screen->exit = true;
239 }
240
241 static int layout_pair(struct config_screen *screen, int y,
242                 struct nc_widget_label *label,
243                 struct nc_widget *field)
244 {
245         struct nc_widget *label_w = widget_label_base(label);
246         widget_move(label_w, y, screen->label_x);
247         widget_move(field, y, screen->field_x);
248         return max(widget_height(label_w), widget_height(field));
249 }
250
251 static void config_screen_layout_widgets(struct config_screen *screen,
252                 enum net_conf_type net_conf)
253 {
254         struct nc_widget *wl, *wf;
255         bool show;
256         int y, x;
257
258         y = 1;
259
260         y += layout_pair(screen, y, screen->widgets.autoboot_l,
261                         widget_checkbox_base(screen->widgets.autoboot_f));
262
263         y += layout_pair(screen, y, screen->widgets.timeout_l,
264                         widget_textbox_base(screen->widgets.timeout_f));
265
266         y += 1;
267
268         y += layout_pair(screen, y, screen->widgets.network_l,
269                         widget_select_base(screen->widgets.network_f));
270
271         y += 1;
272
273         /* conditionally show iface select */
274         wl = widget_label_base(screen->widgets.iface_l);
275         wf = widget_select_base(screen->widgets.iface_f);
276
277         show = net_conf == NET_CONF_TYPE_DHCP_ONE ||
278                 net_conf == NET_CONF_TYPE_STATIC;
279
280         widget_set_visible(wl, show);
281         widget_set_visible(wf, show);
282
283         if (show)
284                 y += layout_pair(screen, y, screen->widgets.iface_l, wf) + 1;
285
286         /* conditionally show static IP params */
287         show = net_conf == NET_CONF_TYPE_STATIC;
288
289         wl = widget_label_base(screen->widgets.ip_addr_l);
290         wf = widget_textbox_base(screen->widgets.ip_addr_f);
291         widget_set_visible(wl, show);
292         widget_set_visible(wf, show);
293         x = screen->field_x + widget_width(wf) + 1;
294
295         if (show)
296                 layout_pair(screen, y, screen->widgets.ip_addr_l, wf);
297
298         wl = widget_label_base(screen->widgets.ip_mask_l);
299         wf = widget_textbox_base(screen->widgets.ip_mask_f);
300         widget_set_visible(wl, show);
301         widget_set_visible(wf, show);
302
303         if (show) {
304                 widget_move(wl, y, x);
305                 widget_move(wf, y, x + 2);
306                 y += 1;
307         }
308
309         wl = widget_label_base(screen->widgets.gateway_l);
310         wf = widget_textbox_base(screen->widgets.gateway_f);
311         widget_set_visible(wl, show);
312         widget_set_visible(wf, show);
313
314         if (show)
315                 y += layout_pair(screen, y, screen->widgets.gateway_l, wf);
316
317         y += 1 + layout_pair(screen, y, screen->widgets.dns_l,
318                         widget_textbox_base(screen->widgets.dns_f));
319
320         widget_move(widget_button_base(screen->widgets.ok_b),
321                         y, screen->field_x);
322         widget_move(widget_button_base(screen->widgets.cancel_b),
323                         y, screen->field_x + 10);
324 }
325
326 static void config_screen_network_change(void *arg, int value)
327 {
328         struct config_screen *screen = arg;
329         widgetset_unpost(screen->widgetset);
330         config_screen_layout_widgets(screen, value);
331         widgetset_post(screen->widgetset);
332 }
333
334 static void config_screen_setup_widgets(struct config_screen *screen,
335                 const struct config *config,
336                 const struct system_info *sysinfo)
337 {
338         struct nc_widgetset *set = screen->widgetset;
339         unsigned int i;
340         char *str;
341
342         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
343                         == N_FIELDS);
344
345         screen->widgets.autoboot_l = widget_new_label(set, 0, 0, "Autoboot:");
346         screen->widgets.autoboot_f = widget_new_checkbox(set, 0, 0,
347                                         config->autoboot_enabled);
348
349         str = talloc_asprintf(screen, "%d", config->autoboot_timeout_sec);
350         screen->widgets.timeout_l = widget_new_label(set, 0, 0, "Timeout:");
351         screen->widgets.timeout_f = widget_new_textbox(set, 0, 0, 5, str);
352
353         screen->widgets.network_l = widget_new_label(set, 0, 0, "Network");
354         screen->widgets.network_f = widget_new_select(set, 0, 0, 50);
355
356         widget_select_add_option(screen->widgets.network_f,
357                                         NET_CONF_TYPE_DHCP_ALL,
358                                         "DHCP on all active interfaces",
359                                         true);
360         widget_select_add_option(screen->widgets.network_f,
361                                         NET_CONF_TYPE_DHCP_ONE,
362                                         "DHCP on a specific interface",
363                                         false);
364         widget_select_add_option(screen->widgets.network_f,
365                                         NET_CONF_TYPE_STATIC,
366                                         "Static IP configuration",
367                                         false);
368
369         widget_select_on_change(screen->widgets.network_f,
370                         config_screen_network_change, screen);
371
372         screen->widgets.iface_l = widget_new_label(set, 0, 0, "Device:");
373         screen->widgets.iface_f = widget_new_select(set, 0, 0, 20);
374
375         for (i = 0; i < sysinfo->n_interfaces; i++) {
376                 struct interface_info *info = sysinfo->interfaces[i];
377                 widget_select_add_option(screen->widgets.iface_f,
378                                                 i, info->name, false);
379         }
380
381         screen->widgets.ip_addr_l = widget_new_label(set, 0, 0, "IP/mask:");
382         screen->widgets.ip_addr_f = widget_new_textbox(set, 0, 0, 16, "");
383         screen->widgets.ip_mask_l = widget_new_label(set, 0, 0, "/");
384         screen->widgets.ip_mask_f = widget_new_textbox(set, 0, 0, 3, "");
385
386         screen->widgets.gateway_l = widget_new_label(set, 0, 0, "Gateway:");
387         screen->widgets.gateway_f = widget_new_textbox(set, 0, 0, 16, "");
388
389         screen->widgets.dns_l = widget_new_label(set, 0, 0, "DNS Server:");
390         screen->widgets.dns_f = widget_new_textbox(set, 0, 0, 16, "");
391
392         screen->widgets.ok_b = widget_new_button(set, 0, 0, 6, "OK",
393                         ok_click, screen);
394         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 6, "Cancel",
395                         cancel_click, screen);
396 }
397
398 struct config_screen *config_screen_init(struct cui *cui,
399                 const struct config *config,
400                 const struct system_info *sysinfo,
401                 void (*on_exit)(struct cui *))
402 {
403         struct config_screen *screen;
404
405         screen = talloc_zero(cui, struct config_screen);
406         nc_scr_init(&screen->scr, pb_config_screen_sig, 0,
407                         cui, config_screen_process_key,
408                         config_screen_post, config_screen_unpost,
409                         config_screen_resize);
410
411         screen->cui = cui;
412         screen->on_exit = on_exit;
413         screen->label_x = 2;
414         screen->field_x = 16;
415
416         screen->scr.frame.ltitle = talloc_strdup(screen,
417                         "Petitboot System Configuration");
418         screen->scr.frame.rtitle = NULL;
419         screen->scr.frame.help = talloc_strdup(screen,
420                         "tab=next, shift+tab=previous");
421         nc_scr_frame_draw(&screen->scr);
422
423         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
424                         screen->scr.sub_ncw);
425         config_screen_setup_widgets(screen, config, sysinfo);
426         config_screen_layout_widgets(screen, NET_CONF_TYPE_DHCP_ALL);
427
428         wrefresh(screen->scr.main_ncw);
429         scrollok(screen->scr.sub_ncw, true);
430
431         return screen;
432 }