c668bde12ae0255ceba9a73d4034abccc0b04cf1
[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 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <pb-config/pb-config.h>
27 #include <talloc/talloc.h>
28 #include <types/types.h>
29 #include <log/log.h>
30 #include <i18n/i18n.h>
31
32 #include "nc-cui.h"
33 #include "nc-config.h"
34 #include "nc-widgets.h"
35
36 #define N_FIELDS        44
37
38 extern struct help_text config_help_text;
39
40 enum net_conf_type {
41         NET_CONF_TYPE_DHCP_ALL,
42         NET_CONF_TYPE_DHCP_ONE,
43         NET_CONF_TYPE_STATIC,
44 };
45
46 struct config_screen {
47         struct nc_scr           scr;
48         struct cui              *cui;
49         struct nc_widgetset     *widgetset;
50         WINDOW                  *pad;
51
52         bool                    exit;
53         bool                    show_help;
54         bool                    show_subset;
55         bool                    need_redraw;
56         bool                    need_update;
57
58         void                    (*on_exit)(struct cui *);
59
60         int                     scroll_y;
61
62         int                     label_x;
63         int                     field_x;
64         int                     network_config_y;
65
66         enum net_conf_type      net_conf_type;
67
68         bool                    autoboot_enabled;
69         bool                    ipmi_override;
70         bool                    net_override;
71
72         struct {
73                 struct nc_widget_label          *autoboot_l;
74                 struct nc_widget_select         *autoboot_f;
75                 struct nc_widget_label          *boot_order_l;
76                 struct nc_widget_subset         *boot_order_f;
77                 struct nc_widget_label          *boot_empty_l;
78                 struct nc_widget_button         *boot_add_b;
79                 struct nc_widget_button         *boot_none_b;
80                 struct nc_widget_button         *boot_any_b;
81                 struct nc_widget_textbox        *timeout_f;
82                 struct nc_widget_label          *timeout_l;
83                 struct nc_widget_label          *timeout_help_l;
84
85                 struct nc_widget_label          *ipmi_type_l;
86                 struct nc_widget_label          *ipmi_clear_l;
87                 struct nc_widget_checkbox       *ipmi_clear_cb;
88
89                 struct nc_widget_label          *network_l;
90                 struct nc_widget_select         *network_f;
91
92                 struct nc_widget_label          *iface_l;
93                 struct nc_widget_select         *iface_f;
94                 struct nc_widget_label          *ip_addr_l;
95                 struct nc_widget_textbox        *ip_addr_f;
96                 struct nc_widget_label          *ip_mask_l;
97                 struct nc_widget_textbox        *ip_mask_f;
98                 struct nc_widget_label          *ip_addr_mask_help_l;
99                 struct nc_widget_label          *gateway_l;
100                 struct nc_widget_textbox        *gateway_f;
101                 struct nc_widget_label          *gateway_help_l;
102                 struct nc_widget_label          *url_l;
103                 struct nc_widget_textbox        *url_f;
104                 struct nc_widget_label          *url_help_l;
105                 struct nc_widget_label          *dns_l;
106                 struct nc_widget_textbox        *dns_f;
107                 struct nc_widget_label          *dns_dhcp_help_l;
108                 struct nc_widget_label          *dns_help_l;
109
110                 struct nc_widget_label          *allow_write_l;
111                 struct nc_widget_select         *allow_write_f;
112                 struct nc_widget_label          *boot_console_l;
113                 struct nc_widget_select         *boot_console_f;
114                 struct nc_widget_label          *current_console_l;
115
116                 struct nc_widget_label          *net_override_l;
117                 struct nc_widget_label          *safe_mode;
118                 struct nc_widget_button         *ok_b;
119                 struct nc_widget_button         *help_b;
120                 struct nc_widget_button         *cancel_b;
121         } widgets;
122 };
123
124 static struct config_screen *config_screen_from_scr(struct nc_scr *scr)
125 {
126         struct config_screen *config_screen;
127
128         assert(scr->sig == pb_config_screen_sig);
129         config_screen = (struct config_screen *)
130                 ((char *)scr - (size_t)&((struct config_screen *)0)->scr);
131         assert(config_screen->scr.sig == pb_config_screen_sig);
132         return config_screen;
133 }
134
135 static void pad_refresh(struct config_screen *screen)
136 {
137         int y, x, rows, cols;
138
139         getmaxyx(screen->scr.sub_ncw, rows, cols);
140         getbegyx(screen->scr.sub_ncw, y, x);
141
142         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
143 }
144
145 static void config_screen_process_key(struct nc_scr *scr, int key)
146 {
147         struct config_screen *screen = config_screen_from_scr(scr);
148         bool handled;
149
150         handled = widgetset_process_key(screen->widgetset, key);
151
152         if (!handled) {
153                 switch (key) {
154                 case 'x':
155                 case 27: /* esc */
156                         screen->exit = true;
157                         break;
158                 case 'h':
159                         screen->show_help = true;
160                         break;
161                 }
162         }
163
164         if (screen->exit) {
165                 screen->on_exit(screen->cui);
166
167         } else if (screen->show_help) {
168                 screen->show_help = false;
169                 screen->need_redraw = true;
170                 cui_show_help(screen->cui, _("System Configuration"),
171                                 &config_help_text);
172
173         } else if (handled && !screen->show_subset) {
174                 pad_refresh(screen);
175         }
176 }
177
178 static void config_screen_resize(struct nc_scr *scr)
179 {
180         struct config_screen *screen = config_screen_from_scr(scr);
181         (void)screen;
182 }
183
184 static int config_screen_unpost(struct nc_scr *scr)
185 {
186         struct config_screen *screen = config_screen_from_scr(scr);
187         widgetset_unpost(screen->widgetset);
188         return 0;
189 }
190
191 struct nc_scr *config_screen_scr(struct config_screen *screen)
192 {
193         return &screen->scr;
194 }
195
196 static int screen_process_form(struct config_screen *screen)
197 {
198         const struct system_info *sysinfo = screen->cui->sysinfo;
199         enum net_conf_type net_conf_type;
200         struct interface_config *iface;
201         bool allow_write, autoboot;
202         char *str, *end;
203         struct config *config;
204         int i, n_boot_opts, rc;
205         unsigned int *order, idx;
206         char mac[20];
207
208         config = config_copy(screen, screen->cui->config);
209
210         talloc_free(config->autoboot_opts);
211         config->n_autoboot_opts = 0;
212
213         n_boot_opts = widget_subset_get_order(config, &order,
214                                               screen->widgets.boot_order_f);
215
216         autoboot = widget_select_get_value(screen->widgets.autoboot_f);
217         config->autoboot_enabled = autoboot && n_boot_opts;
218
219         config->n_autoboot_opts = n_boot_opts;
220         config->autoboot_opts = talloc_array(config, struct autoboot_option,
221                                              n_boot_opts);
222
223         for (i = 0; i < n_boot_opts; i++) {
224                 if (order[i] < sysinfo->n_blockdevs) {
225                         /* disk uuid */
226                         config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID;
227                         config->autoboot_opts[i].uuid = talloc_strdup(config,
228                                                         sysinfo->blockdevs[order[i]]->uuid);
229                 } else if(order[i] < (sysinfo->n_blockdevs + sysinfo->n_interfaces)) {
230                         /* net uuid */
231                         order[i] -= sysinfo->n_blockdevs;
232                         config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID;
233                         mac_str(sysinfo->interfaces[order[i]]->hwaddr,
234                                 sysinfo->interfaces[order[i]]->hwaddr_size,
235                                 mac, sizeof(mac));
236                         config->autoboot_opts[i].uuid = talloc_strdup(config, mac);
237                 } else {
238                         /* device type */
239                         order[i] -= (sysinfo->n_blockdevs + sysinfo->n_interfaces);
240                         config->autoboot_opts[i].boot_type = BOOT_DEVICE_TYPE;
241                         config->autoboot_opts[i].type = order[i];
242                 }
243         }
244
245         str = widget_textbox_get_value(screen->widgets.timeout_f);
246         if (str) {
247                 unsigned long x;
248                 errno = 0;
249                 x = strtoul(str, &end, 10);
250                 if (!errno && end != str)
251                         config->autoboot_timeout_sec = x;
252         }
253
254         if (screen->ipmi_override)
255                 if (widget_checkbox_get_value(screen->widgets.ipmi_clear_cb))
256                         config->ipmi_bootdev = IPMI_BOOTDEV_INVALID;
257
258
259         net_conf_type = widget_select_get_value(screen->widgets.network_f);
260
261         /* if we don't have any network interfaces, prevent per-interface
262          * configuration */
263         if (sysinfo->n_interfaces == 0)
264                 net_conf_type = NET_CONF_TYPE_DHCP_ALL;
265
266         if (net_conf_type == NET_CONF_TYPE_DHCP_ALL) {
267                 config->network.n_interfaces = 0;
268
269         } else {
270                 iface = talloc_zero(config, struct interface_config);
271                 config->network.n_interfaces = 1;
272                 config->network.interfaces = talloc_array(config,
273                                 struct interface_config *, 1);
274                 config->network.interfaces[0] = iface;
275
276                 /* copy hwaddr (from the sysinfo interface data) to
277                  * the configuration */
278                 idx = widget_select_get_value(screen->widgets.iface_f);
279                 memcpy(iface->hwaddr, sysinfo->interfaces[idx]->hwaddr,
280                                 sizeof(iface->hwaddr));
281         }
282
283         if (net_conf_type == NET_CONF_TYPE_DHCP_ONE) {
284                 iface->method = CONFIG_METHOD_DHCP;
285         }
286
287         if (net_conf_type == NET_CONF_TYPE_STATIC) {
288                 char *ip, *mask, *gateway, *url;
289
290                 ip = widget_textbox_get_value(screen->widgets.ip_addr_f);
291                 mask = widget_textbox_get_value(screen->widgets.ip_mask_f);
292                 gateway = widget_textbox_get_value(screen->widgets.gateway_f);
293                 url = widget_textbox_get_value(screen->widgets.url_f);
294
295                 if (!ip || !*ip || !mask || !*mask) {
296                         screen->scr.frame.status =
297                                 _("No IP / mask values are set");
298                         nc_scr_frame_draw(&screen->scr);
299                         talloc_free(config);
300                         return -1;
301                 }
302
303                 iface->method = CONFIG_METHOD_STATIC;
304                 iface->static_config.address = talloc_asprintf(iface, "%s/%s",
305                                 ip, mask);
306                 iface->static_config.gateway = talloc_strdup(iface, gateway);
307                 iface->static_config.url = talloc_strdup(iface, url);
308         }
309
310         str = widget_textbox_get_value(screen->widgets.dns_f);
311         talloc_free(config->network.dns_servers);
312         config->network.dns_servers = NULL;
313         config->network.n_dns_servers = 0;
314
315         if (str && strlen(str)) {
316                 char *dns, *tmp;
317                 int i;
318
319                 for (;;) {
320                         dns = strtok_r(str, " \t", &tmp);
321
322                         if (!dns)
323                                 break;
324
325                         i = config->network.n_dns_servers++;
326                         config->network.dns_servers = talloc_realloc(config,
327                                         config->network.dns_servers,
328                                         const char *,
329                                         config->network.n_dns_servers);
330                         config->network.dns_servers[i] =
331                                 talloc_strdup(config, dns);
332
333                         str = NULL;
334                 }
335         }
336
337         allow_write = widget_select_get_value(screen->widgets.allow_write_f);
338         if (allow_write != config->allow_writes)
339                 config->allow_writes = allow_write;
340
341         if (config->n_consoles) {
342                 idx = widget_select_get_value(screen->widgets.boot_console_f);
343                 if (!config->boot_console) {
344                         config->boot_console = talloc_strdup(config,
345                                                         config->consoles[idx]);
346                 } else if (strncmp(config->boot_console, config->consoles[idx],
347                                 strlen(config->boot_console)) != 0) {
348                         talloc_free(config->boot_console);
349                         config->boot_console = talloc_strdup(config,
350                                                         config->consoles[idx]);
351                 }
352         }
353
354         config->safe_mode = false;
355         rc = cui_send_config(screen->cui, config);
356         talloc_free(config);
357
358         if (rc)
359                 pb_log("cui_send_config failed!\n");
360         else
361                 pb_debug("config sent!\n");
362
363         return 0;
364 }
365
366 static void ok_click(void *arg)
367 {
368         struct config_screen *screen = arg;
369         if (screen_process_form(screen))
370                 /* errors are written to the status line, so we'll need
371                  * to refresh */
372                 wrefresh(screen->scr.main_ncw);
373         else
374                 screen->exit = true;
375 }
376
377 static void help_click(void *arg)
378 {
379         struct config_screen *screen = arg;
380         screen->show_help = true;
381 }
382
383 static void cancel_click(void *arg)
384 {
385         struct config_screen *screen = arg;
386         screen->exit = true;
387 }
388
389 static int layout_pair(struct config_screen *screen, int y,
390                 struct nc_widget_label *label,
391                 struct nc_widget *field)
392 {
393         struct nc_widget *label_w = widget_label_base(label);
394         widget_move(label_w, y, screen->label_x);
395         widget_move(field, y, screen->field_x);
396         return max(widget_height(label_w), widget_height(field));
397 }
398
399 static void config_screen_layout_widgets(struct config_screen *screen)
400 {
401         struct nc_widget *wl, *wf, *wh;
402         int y, x, help_x;
403         bool show;
404
405         y = 1;
406         /* currently, the longest label we have is the DNS-servers
407          * widget, so layout our screen based on that */
408         help_x = screen->field_x + 2 +
409                 widget_width(widget_textbox_base(screen->widgets.dns_f));
410
411         wl = widget_label_base(screen->widgets.autoboot_l);
412         widget_set_visible(wl, true);
413         widget_move(wl, y, screen->label_x);
414
415         wf = widget_select_base(screen->widgets.autoboot_f);
416         widget_set_visible(wf, true);
417         widget_move(wf, y, screen->field_x);
418         y += widget_height(wf);
419
420         show = screen->autoboot_enabled;
421
422         if (show)
423                 y += 1;
424
425         wl = widget_label_base(screen->widgets.boot_order_l);
426         widget_set_visible(wl, show);
427         widget_move(wl, y, screen->label_x);
428
429         wf = widget_subset_base(screen->widgets.boot_order_f);
430         widget_move(wf, y, screen->field_x);
431         wl = widget_label_base(screen->widgets.boot_empty_l);
432         widget_move(wl, y, screen->field_x);
433
434         if (widget_subset_height(screen->widgets.boot_order_f)) {
435                 widget_set_visible(wl, false);
436                 widget_set_visible(wf, show);
437                 y += show ? widget_height(wf) : 0;
438         } else {
439                 widget_set_visible(wl, show);
440                 widget_set_visible(wf, false);
441                 y += show ? 1 : 0;
442         }
443
444         if (show) {
445                 y += 1;
446                 widget_move(widget_button_base(screen->widgets.boot_add_b),
447                                 y++, screen->field_x);
448                 widget_move(widget_button_base(screen->widgets.boot_any_b),
449                                 y++, screen->field_x);
450                 widget_move(widget_button_base(screen->widgets.boot_none_b),
451                                 y, screen->field_x);
452         }
453
454         wf = widget_button_base(screen->widgets.boot_add_b);
455         if (widget_subset_n_inactive(screen->widgets.boot_order_f) && show)
456                 widget_set_visible(wf, true);
457         else
458                 widget_set_visible(wf, false);
459
460         if (show)
461                 y += 2;
462
463         widget_set_visible(widget_button_base(screen->widgets.boot_any_b), show);
464         widget_set_visible(widget_button_base(screen->widgets.boot_none_b), show);
465
466         wf = widget_textbox_base(screen->widgets.timeout_f);
467         wl = widget_label_base(screen->widgets.timeout_l);
468         wh = widget_label_base(screen->widgets.timeout_help_l);
469         widget_set_visible(wl, screen->autoboot_enabled);
470         widget_set_visible(wf, screen->autoboot_enabled);
471         widget_set_visible(wh, screen->autoboot_enabled);
472         if (screen->autoboot_enabled) {
473                 widget_set_visible(wh, screen->autoboot_enabled);
474                 widget_move(wl, y, screen->label_x);
475                 widget_move(wf, y, screen->field_x);
476                 widget_move(wh, y, screen->field_x + widget_width(wf) + 1);
477                 y += 2;
478         } else
479                 y += 1;
480
481         if (screen->ipmi_override) {
482                 wl = widget_label_base(screen->widgets.ipmi_type_l);
483                 widget_set_visible(wl, true);
484                 widget_move(wl, y, screen->label_x);
485                 y += 1;
486
487                 wl = widget_label_base(screen->widgets.ipmi_clear_l);
488                 wf = widget_checkbox_base(screen->widgets.ipmi_clear_cb);
489                 widget_set_visible(wl, true);
490                 widget_set_visible(wf, true);
491                 widget_move(wl, y, screen->label_x);
492                 widget_move(wf, y, screen->field_x);
493                 y += 1;
494         }
495
496         y += 1;
497
498         y += layout_pair(screen, y, screen->widgets.network_l,
499                         widget_select_base(screen->widgets.network_f));
500
501         y += 1;
502
503         /* conditionally show iface select */
504         wl = widget_label_base(screen->widgets.iface_l);
505         wf = widget_select_base(screen->widgets.iface_f);
506
507         show = screen->net_conf_type == NET_CONF_TYPE_DHCP_ONE ||
508                 screen->net_conf_type == NET_CONF_TYPE_STATIC;
509
510         widget_set_visible(wl, show);
511         widget_set_visible(wf, show);
512
513         if (show)
514                 y += layout_pair(screen, y, screen->widgets.iface_l, wf) + 1;
515
516         /* conditionally show static IP params */
517         show = screen->net_conf_type == NET_CONF_TYPE_STATIC;
518
519         wl = widget_label_base(screen->widgets.ip_addr_l);
520         wf = widget_textbox_base(screen->widgets.ip_addr_f);
521         widget_set_visible(wl, show);
522         widget_set_visible(wf, show);
523         x = screen->field_x + widget_width(wf) + 1;
524
525         if (show)
526                 layout_pair(screen, y, screen->widgets.ip_addr_l, wf);
527
528         wl = widget_label_base(screen->widgets.ip_mask_l);
529         wf = widget_textbox_base(screen->widgets.ip_mask_f);
530         widget_set_visible(wl, show);
531         widget_set_visible(wf, show);
532
533         if (show) {
534                 widget_move(wl, y, x);
535                 widget_move(wf, y, x + 2);
536         }
537
538         /* help for IP/mask */
539         wh = widget_label_base(screen->widgets.ip_addr_mask_help_l);
540         widget_set_visible(wh, show);
541         if (show) {
542                 widget_move(wh, y, help_x);
543                 y++;
544         }
545
546         wl = widget_label_base(screen->widgets.gateway_l);
547         wf = widget_textbox_base(screen->widgets.gateway_f);
548         wh = widget_label_base(screen->widgets.gateway_help_l);
549         widget_set_visible(wl, show);
550         widget_set_visible(wf, show);
551         widget_set_visible(wh, show);
552
553         if (show) {
554                 layout_pair(screen, y, screen->widgets.gateway_l, wf);
555                 widget_move(wh, y, help_x);
556                 y++;
557         }
558
559         wl = widget_label_base(screen->widgets.url_l);
560         wf = widget_textbox_base(screen->widgets.url_f);
561         wh = widget_label_base(screen->widgets.url_help_l);
562         widget_set_visible(wl, show);
563         widget_set_visible(wf, show);
564         widget_set_visible(wh, show);
565
566         if (show) {
567                 layout_pair(screen, y, screen->widgets.url_l, wf);
568                 widget_move(wh, y, help_x);
569                 y++;
570         }
571
572         wh = widget_label_base(screen->widgets.dns_help_l);
573         layout_pair(screen, y, screen->widgets.dns_l,
574                         widget_textbox_base(screen->widgets.dns_f));
575         widget_move(wh, y, help_x);
576         y++;
577
578         /* we show the DNS/DHCP help if we're configuring DHCP */
579         show = screen->net_conf_type != NET_CONF_TYPE_STATIC;
580         wl = widget_label_base(screen->widgets.dns_dhcp_help_l);
581         widget_set_visible(wl, show);
582         if (show) {
583                 widget_move(wl, y, screen->field_x);
584                 y += 1;
585         }
586
587         y += 1;
588
589         layout_pair(screen, y, screen->widgets.allow_write_l,
590                     widget_select_base(screen->widgets.allow_write_f));
591         y += widget_height(widget_select_base(screen->widgets.allow_write_f));
592
593         y += 1;
594
595         if (widget_height(widget_select_base(screen->widgets.boot_console_f))) {
596                 layout_pair(screen, y, screen->widgets.boot_console_l,
597                             widget_select_base(screen->widgets.boot_console_f));
598                 y += widget_height(widget_select_base(screen->widgets.boot_console_f));
599                 widget_move(widget_label_base(screen->widgets.current_console_l),
600                         y, screen->field_x);
601                 y += 2;
602         } else {
603                 widget_set_visible(widget_label_base(
604                                         screen->widgets.boot_console_l), false);
605                 widget_set_visible(widget_select_base(
606                                         screen->widgets.boot_console_f), false);
607                 widget_set_visible(widget_label_base(
608                                         screen->widgets.current_console_l), false);
609         }
610
611         if (screen->net_override) {
612                 widget_move(widget_label_base(screen->widgets.net_override_l),
613                                 y, screen->label_x);
614                 widget_set_visible(widget_label_base(screen->widgets.net_override_l),
615                                         true);
616                 y += 1;
617         }
618
619         if (screen->cui->config->safe_mode) {
620                 widget_move(widget_label_base(screen->widgets.safe_mode),
621                         y, screen->label_x);
622                 widget_set_visible(widget_label_base(screen->widgets.safe_mode),
623                                         true);
624                 y += 1;
625         }
626
627         widget_move(widget_button_base(screen->widgets.ok_b),
628                         y, screen->field_x);
629         widget_move(widget_button_base(screen->widgets.help_b),
630                         y, screen->field_x + 14);
631         widget_move(widget_button_base(screen->widgets.cancel_b),
632                         y, screen->field_x + 28);
633 }
634
635 static void config_screen_network_change(void *arg, int value)
636 {
637         struct config_screen *screen = arg;
638         screen->net_conf_type = value;
639         widgetset_unpost(screen->widgetset);
640         config_screen_layout_widgets(screen);
641         widgetset_post(screen->widgetset);
642 }
643
644 static void config_screen_boot_order_change(void *arg, int value)
645 {
646         (void)value;
647         struct config_screen *screen = arg;
648         widgetset_unpost(screen->widgetset);
649         config_screen_layout_widgets(screen);
650         widgetset_post(screen->widgetset);
651 }
652
653 static void config_screen_autoboot_change(void *arg, int value)
654 {
655         struct config_screen *screen = arg;
656         screen->autoboot_enabled = !!value;
657         widgetset_unpost(screen->widgetset);
658         config_screen_layout_widgets(screen);
659         widgetset_post(screen->widgetset);
660 }
661
662 static void config_screen_add_device(void *arg)
663 {
664         struct config_screen *screen = arg;
665
666         screen->show_subset = true;
667         cui_show_subset(screen->cui, _("Select a boot device to add"),
668                         screen->widgets.boot_order_f);
669 }
670
671 static void config_screen_autoboot_none(void *arg)
672 {
673         struct config_screen *screen = arg;
674         struct nc_widget_subset *subset = screen->widgets.boot_order_f;
675
676         widget_subset_clear_active(subset);
677
678         widgetset_unpost(screen->widgetset);
679         config_screen_layout_widgets(screen);
680         widgetset_post(screen->widgetset);
681 }
682
683 static void config_screen_autoboot_any(void *arg)
684 {
685         struct config_screen *screen = arg;
686         const struct system_info *sysinfo = screen->cui->sysinfo;
687         struct nc_widget_subset *subset = screen->widgets.boot_order_f;
688         int idx;
689
690         widget_subset_clear_active(subset);
691
692         idx = sysinfo->n_blockdevs + sysinfo->n_interfaces + DEVICE_TYPE_ANY;
693
694         widget_subset_make_active(screen->widgets.boot_order_f, idx);
695
696         screen->autoboot_enabled = true;
697
698         widgetset_unpost(screen->widgetset);
699         config_screen_layout_widgets(screen);
700         widgetset_post(screen->widgetset);
701 }
702
703 static void config_screen_update_subset(void *arg,
704                         struct nc_widget_subset *subset, int idx)
705 {
706         struct config_screen *screen = arg;
707
708         if (idx >= 0)
709                 widget_subset_make_active(subset, idx);
710         if (!screen->autoboot_enabled)
711                 screen->autoboot_enabled = true;
712         config_screen_layout_widgets(screen);
713 }
714
715 static struct interface_config *first_active_interface(
716                 const struct config *config)
717 {
718         unsigned int i;
719
720         for (i = 0; i < config->network.n_interfaces; i++) {
721                 if (config->network.interfaces[i]->ignore)
722                         continue;
723                 return config->network.interfaces[i];
724         }
725         return NULL;
726 }
727
728 static enum net_conf_type find_net_conf_type(const struct config *config)
729 {
730         struct interface_config *ifcfg;
731
732         ifcfg = first_active_interface(config);
733
734         if (!ifcfg)
735                 return NET_CONF_TYPE_DHCP_ALL;
736
737         else if (ifcfg->method == CONFIG_METHOD_DHCP)
738                 return NET_CONF_TYPE_DHCP_ONE;
739
740         else if (ifcfg->method == CONFIG_METHOD_STATIC)
741                 return NET_CONF_TYPE_STATIC;
742
743         assert(0);
744         return NET_CONF_TYPE_DHCP_ALL;
745 }
746
747 static void config_screen_setup_empty(struct config_screen *screen)
748 {
749         widget_new_label(screen->widgetset, 2, screen->field_x,
750                         _("Waiting for configuration data..."));
751         screen->widgets.cancel_b = widget_new_button(screen->widgetset,
752                         4, screen->field_x, 9, _("Cancel"),
753                         cancel_click, screen);
754 }
755
756 static int find_autoboot_idx(const struct system_info *sysinfo,
757                 struct autoboot_option *opt)
758 {
759         unsigned int i;
760
761         if (opt->boot_type == BOOT_DEVICE_TYPE)
762                 return sysinfo->n_blockdevs + sysinfo->n_interfaces + opt->type;
763
764         for (i = 0; i < sysinfo->n_blockdevs; i++) {
765                 if (!strcmp(sysinfo->blockdevs[i]->uuid, opt->uuid))
766                         return i;
767         }
768
769         for (i = 0; i < sysinfo->n_interfaces; i++) {
770                 struct interface_info *info = sysinfo->interfaces[i];
771                 char mac[20];
772
773                 mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac));
774
775                 if (!strcmp(mac, opt->uuid))
776                         return sysinfo->n_blockdevs + i;
777         }
778
779         return -1;
780 }
781
782 static void config_screen_setup_widgets(struct config_screen *screen,
783                 const struct config *config,
784                 const struct system_info *sysinfo)
785 {
786         struct nc_widgetset *set = screen->widgetset;
787         struct interface_config *ifcfg;
788         char *str, *ip, *mask, *gw, *url, *tty;
789         enum net_conf_type type;
790         unsigned int i;
791         int add_len, clear_len, any_len, min_len = 20;
792         bool found;
793
794         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
795                         == N_FIELDS);
796
797         type = screen->net_conf_type;
798         ifcfg = first_active_interface(config);
799
800         screen->autoboot_enabled = config->autoboot_enabled;
801
802         screen->widgets.autoboot_l = widget_new_label(set, 0, 0,
803                                         _("Autoboot:"));
804         screen->widgets.autoboot_f = widget_new_select(set, 0, 0,
805                                         COLS - screen->field_x - 1);
806
807         widget_select_add_option(screen->widgets.autoboot_f, 0, _("Disabled"),
808                                  !screen->autoboot_enabled);
809         widget_select_add_option(screen->widgets.autoboot_f, 1, _("Enabled"),
810                                  screen->autoboot_enabled);
811
812         widget_select_on_change(screen->widgets.autoboot_f,
813                         config_screen_autoboot_change, screen);
814
815         add_len = max(min_len, strncols(_("Add Device")));
816         clear_len = max(min_len, strncols(_("Clear")));
817         any_len = max(min_len, strncols(_("Clear & Boot Any")));
818
819         screen->widgets.boot_add_b = widget_new_button(set, 0, 0, add_len,
820                                         _("Add Device"),
821                                         config_screen_add_device, screen);
822
823         screen->widgets.boot_none_b = widget_new_button(set, 0, 0, clear_len,
824                                         _("Clear"),
825                                         config_screen_autoboot_none, screen);
826
827         screen->widgets.boot_any_b = widget_new_button(set, 0, 0, any_len,
828                                         _("Clear & Boot Any"),
829                                         config_screen_autoboot_any, screen);
830
831         screen->widgets.boot_order_l = widget_new_label(set, 0, 0,
832                                         _("Boot Order:"));
833         screen->widgets.boot_order_f = widget_new_subset(set, 0, 0,
834                                         COLS - screen->field_x,
835                                         config_screen_update_subset);
836         screen->widgets.boot_empty_l = widget_new_label(set, 0, 0,
837                                         _("(None)"));
838
839         widget_subset_on_change(screen->widgets.boot_order_f,
840                         config_screen_boot_order_change, screen);
841
842         for (i = 0; i < sysinfo->n_blockdevs; i++) {
843                 struct blockdev_info *bd = sysinfo->blockdevs[i];
844                 char *label;
845
846                 label = talloc_asprintf(screen, _("disk: %s [uuid: %s]"),
847                                 bd->name, bd->uuid);
848
849                 widget_subset_add_option(screen->widgets.boot_order_f, label);
850         }
851
852         for (i = 0; i < sysinfo->n_interfaces; i++) {
853                 struct interface_info *info = sysinfo->interfaces[i];
854                 char *label, mac[20];
855
856                 mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac));
857
858                 label = talloc_asprintf(screen, _("net:  %s [mac: %s]"),
859                                 info->name, mac);
860
861                 widget_subset_add_option(screen->widgets.boot_order_f, label);
862         }
863
864         for (i = DEVICE_TYPE_NETWORK; i < DEVICE_TYPE_UNKNOWN; i++) {
865                 char *label;
866
867                 if (i == DEVICE_TYPE_ANY)
868                         label = talloc_asprintf(screen, _("Any Device"));
869                 else
870                         label = talloc_asprintf(screen, _("Any %s device"),
871                                                 device_type_display_name(i));
872
873                 widget_subset_add_option(screen->widgets.boot_order_f, label);
874         }
875
876         for (i = 0; i < config->n_autoboot_opts; i++) {
877                 struct autoboot_option *opt = &config->autoboot_opts[i];
878                 int idx;
879
880                 idx = find_autoboot_idx(sysinfo, opt);
881
882                 if (idx >= 0) {
883                         widget_subset_make_active(screen->widgets.boot_order_f,
884                                                   idx);
885                 } else {
886                         if (opt->boot_type == BOOT_DEVICE_TYPE)
887                                 pb_log("%s: Unknown autoboot option: %d\n",
888                                        __func__, opt->type);
889                         else
890                                 pb_log("%s: Unknown autoboot UUID: %s\n",
891                                        __func__, opt->uuid);
892                 }
893         }
894
895
896         str = talloc_asprintf(screen, "%d", config->autoboot_timeout_sec);
897         screen->widgets.timeout_l = widget_new_label(set, 0, 0, _("Timeout:"));
898         screen->widgets.timeout_f = widget_new_textbox(set, 0, 0, 5, str);
899         screen->widgets.timeout_help_l = widget_new_label(set, 0, 0,
900                                         _("seconds"));
901
902         widget_textbox_set_fixed_size(screen->widgets.timeout_f);
903         widget_textbox_set_validator_integer(screen->widgets.timeout_f, 0, 999);
904
905         if (config->ipmi_bootdev) {
906                 char *label = talloc_asprintf(screen,
907                                 _("%s IPMI boot option: %s"),
908                                 config->ipmi_bootdev_persistent ?
909                                 "Persistent" : "Temporary",
910                                 ipmi_bootdev_display_name(config->ipmi_bootdev));
911                 screen->widgets.ipmi_type_l = widget_new_label(set, 0, 0,
912                                                         label);
913                 screen->widgets.ipmi_clear_l = widget_new_label(set, 0, 0,
914                                                         _("Clear option:"));
915                 screen->widgets.ipmi_clear_cb = widget_new_checkbox(set, 0, 0,
916                                                         false);
917                 screen->ipmi_override = true;
918         }
919
920         screen->widgets.network_l = widget_new_label(set, 0, 0, _("Network:"));
921         screen->widgets.network_f = widget_new_select(set, 0, 0,
922                                                 COLS - screen->field_x - 1);
923
924         widget_select_add_option(screen->widgets.network_f,
925                                         NET_CONF_TYPE_DHCP_ALL,
926                                         _("DHCP on all active interfaces"),
927                                         type == NET_CONF_TYPE_DHCP_ALL);
928         widget_select_add_option(screen->widgets.network_f,
929                                         NET_CONF_TYPE_DHCP_ONE,
930                                         _("DHCP on a specific interface"),
931                                         type == NET_CONF_TYPE_DHCP_ONE);
932         widget_select_add_option(screen->widgets.network_f,
933                                         NET_CONF_TYPE_STATIC,
934                                         _("Static IP configuration"),
935                                         type == NET_CONF_TYPE_STATIC);
936
937         widget_select_on_change(screen->widgets.network_f,
938                         config_screen_network_change, screen);
939
940         screen->widgets.iface_l = widget_new_label(set, 0, 0, _("Device:"));
941         screen->widgets.iface_f = widget_new_select(set, 0, 0, 50);
942
943         for (i = 0; i < sysinfo->n_interfaces; i++) {
944                 struct interface_info *info = sysinfo->interfaces[i];
945                 char str[50], mac[20];
946                 bool is_default;
947
948                 is_default = ifcfg && !memcmp(ifcfg->hwaddr, info->hwaddr,
949                                         sizeof(ifcfg->hwaddr));
950
951                 mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac));
952                 snprintf(str, sizeof(str), "%s [%s, %s]", info->name, mac,
953                                 info->link ? _("link up") : _("link down"));
954
955                 widget_select_add_option(screen->widgets.iface_f,
956                                                 i, str, is_default);
957         }
958
959         url = gw = ip = mask = NULL;
960         if (ifcfg && ifcfg->method == CONFIG_METHOD_STATIC) {
961                 char *sep;
962
963                 str = talloc_strdup(screen, ifcfg->static_config.address);
964                 sep = strchr(str, '/');
965                 ip = str;
966
967                 if (sep) {
968                         *sep = '\0';
969                         mask = sep + 1;
970                 }
971                 gw = ifcfg->static_config.gateway;
972                 url = ifcfg->static_config.url;
973         }
974
975         screen->net_override = ifcfg && ifcfg->override;
976         if (screen->net_override) {
977                 screen->widgets.net_override_l = widget_new_label(set, 0, 0,
978                         _("Network Override Active! 'OK' will overwrite interface config"));
979         }
980
981         screen->widgets.ip_addr_l = widget_new_label(set, 0, 0, _("IP/mask:"));
982         screen->widgets.ip_addr_f = widget_new_textbox(set, 0, 0, 16, ip);
983         screen->widgets.ip_mask_l = widget_new_label(set, 0, 0, "/");
984         screen->widgets.ip_mask_f = widget_new_textbox(set, 0, 0, 3, mask);
985         screen->widgets.ip_addr_mask_help_l =
986                 widget_new_label(set, 0, 0, _("(eg. 192.168.0.10 / 24)"));
987
988         widget_textbox_set_fixed_size(screen->widgets.ip_addr_f);
989         widget_textbox_set_fixed_size(screen->widgets.ip_mask_f);
990         widget_textbox_set_validator_ipv4(screen->widgets.ip_addr_f);
991         widget_textbox_set_validator_integer(screen->widgets.ip_mask_f, 1, 31);
992
993         screen->widgets.gateway_l = widget_new_label(set, 0, 0, _("Gateway:"));
994         screen->widgets.gateway_f = widget_new_textbox(set, 0, 0, 16, gw);
995         screen->widgets.gateway_help_l =
996                 widget_new_label(set, 0, 0, _("(eg. 192.168.0.1)"));
997
998         widget_textbox_set_fixed_size(screen->widgets.gateway_f);
999         widget_textbox_set_validator_ipv4(screen->widgets.gateway_f);
1000
1001         screen->widgets.url_l = widget_new_label(set, 0, 0, _("URL:"));
1002         screen->widgets.url_f = widget_new_textbox(set, 0, 0, 32, url);
1003         screen->widgets.url_help_l =
1004                 widget_new_label(set, 0, 0, _("(eg. tftp://)"));
1005
1006         str = talloc_strdup(screen, "");
1007         for (i = 0; i < config->network.n_dns_servers; i++) {
1008                 str = talloc_asprintf_append(str, "%s%s",
1009                                 (i == 0) ? "" : " ",
1010                                 config->network.dns_servers[i]);
1011         }
1012
1013         screen->widgets.dns_l = widget_new_label(set, 0, 0,
1014                                         _("DNS Server(s):"));
1015         screen->widgets.dns_f = widget_new_textbox(set, 0, 0, 32, str);
1016         screen->widgets.dns_help_l =
1017                 widget_new_label(set, 0, 0, _("(eg. 192.168.0.2)"));
1018
1019         widget_textbox_set_validator_ipv4_multi(screen->widgets.dns_f);
1020
1021         screen->widgets.dns_dhcp_help_l = widget_new_label(set, 0, 0,
1022                         _("(if not provided by DHCP server)"));
1023
1024         if (config->safe_mode)
1025                 screen->widgets.safe_mode = widget_new_label(set, 0, 0,
1026                          _("Selecting 'OK' will exit safe mode"));
1027
1028         screen->widgets.allow_write_l = widget_new_label(set, 0, 0,
1029                         _("Disk R/W:"));
1030         screen->widgets.allow_write_f = widget_new_select(set, 0, 0,
1031                                                 COLS - screen->field_x - 1);
1032
1033         widget_select_add_option(screen->widgets.allow_write_f, 0,
1034                                 _("Prevent all writes to disk"),
1035                                 !config->allow_writes);
1036
1037         widget_select_add_option(screen->widgets.allow_write_f, 1,
1038                                 _("Allow bootloader scripts to modify disks"),
1039                                 config->allow_writes);
1040
1041         screen->widgets.boot_console_l = widget_new_label(set, 0, 0,
1042                         _("Boot console:"));
1043         screen->widgets.boot_console_f = widget_new_select(set, 0, 0,
1044                                                 COLS - screen->field_x - 1);
1045
1046         for (i = 0; i < config->n_consoles; i++){
1047                 found = config->boot_console &&
1048                         strncmp(config->boot_console, config->consoles[i],
1049                                 strlen(config->boot_console)) == 0;
1050                 widget_select_add_option(screen->widgets.boot_console_f, i,
1051                                         config->consoles[i], found);
1052         }
1053
1054         tty = talloc_asprintf(screen, _("Current interface: %s"),
1055                                 ttyname(STDIN_FILENO));
1056         screen->widgets.current_console_l = widget_new_label(set, 0 , 0, tty);
1057
1058         screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"),
1059                         ok_click, screen);
1060         screen->widgets.help_b = widget_new_button(set, 0, 0, 10, _("Help"),
1061                         help_click, screen);
1062         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"),
1063                         cancel_click, screen);
1064 }
1065
1066 static void config_screen_widget_focus(struct nc_widget *widget, void *arg)
1067 {
1068         struct config_screen *screen = arg;
1069         int w_y, w_height, w_focus, s_max, adjust;
1070
1071         w_height = widget_height(widget);
1072         w_focus = widget_focus_y(widget);
1073         w_y = widget_y(widget) + w_focus;
1074         s_max = getmaxy(screen->scr.sub_ncw) - 1;
1075
1076         if (w_y < screen->scroll_y)
1077                 screen->scroll_y = w_y;
1078
1079         else if (w_y + screen->scroll_y + 1 > s_max) {
1080                 /* Fit as much of the widget into the screen as possible */
1081                 adjust = min(s_max - 1, w_height - w_focus);
1082                 if (w_y + adjust >= screen->scroll_y + s_max)
1083                         screen->scroll_y = max(0, 1 + w_y + adjust - s_max);
1084         } else
1085                 return;
1086
1087         pad_refresh(screen);
1088 }
1089
1090 static void config_screen_draw(struct config_screen *screen,
1091                 const struct config *config,
1092                 const struct system_info *sysinfo)
1093 {
1094         bool repost = false;
1095         int height;
1096
1097         /* The size of the pad we'll need depends on the number of interfaces.
1098          *
1099          * We use N_FIELDS (which is quite conservative, as some fields share
1100          * a line) as a base, then:
1101          * - add 6 (as the network select & boot device select fields take 3
1102          *   lines each),
1103          * - add n_interfaces for every field in the network select field, and
1104          * - add (n_blockdevs + n_interfaces) for every field in the boot device
1105          *   select field
1106          */
1107         height = N_FIELDS + 6;
1108         if (sysinfo) {
1109                 height += sysinfo->n_interfaces;
1110                 height += (sysinfo->n_blockdevs + sysinfo->n_interfaces);
1111         }
1112         if (!screen->pad || getmaxy(screen->pad) < height) {
1113                 if (screen->pad)
1114                         delwin(screen->pad);
1115                 screen->pad = newpad(height, COLS + 10);
1116         }
1117
1118         if (screen->widgetset) {
1119                 widgetset_unpost(screen->widgetset);
1120                 talloc_free(screen->widgetset);
1121                 repost = true;
1122         }
1123
1124         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
1125                         screen->pad);
1126         widgetset_set_widget_focus(screen->widgetset,
1127                         config_screen_widget_focus, screen);
1128
1129         if (!config || !sysinfo) {
1130                 config_screen_setup_empty(screen);
1131         } else {
1132                 screen->net_conf_type = find_net_conf_type(config);
1133                 config_screen_setup_widgets(screen, config, sysinfo);
1134                 config_screen_layout_widgets(screen);
1135         }
1136
1137         if (repost)
1138                 widgetset_post(screen->widgetset);
1139 }
1140
1141 void config_screen_update(struct config_screen *screen,
1142                 const struct config *config,
1143                 const struct system_info *sysinfo)
1144 {
1145         if (screen->cui->current != config_screen_scr(screen)) {
1146                 screen->need_update = true;
1147                 return;
1148         }
1149
1150         config_screen_draw(screen, config, sysinfo);
1151         pad_refresh(screen);
1152 }
1153
1154 static int config_screen_post(struct nc_scr *scr)
1155 {
1156         struct config_screen *screen = config_screen_from_scr(scr);
1157         screen->show_subset = false;
1158
1159         if (screen->need_update) {
1160                 config_screen_draw(screen, screen->cui->config,
1161                                    screen->cui->sysinfo);
1162                 screen->need_update = false;
1163         } else {
1164                 widgetset_post(screen->widgetset);
1165         }
1166
1167         nc_scr_frame_draw(scr);
1168         if (screen->need_redraw) {
1169                 redrawwin(scr->main_ncw);
1170                 screen->need_redraw = false;
1171         }
1172         wrefresh(screen->scr.main_ncw);
1173         pad_refresh(screen);
1174         return 0;
1175 }
1176
1177 static int config_screen_destroy(void *arg)
1178 {
1179         struct config_screen *screen = arg;
1180         if (screen->pad)
1181                 delwin(screen->pad);
1182         return 0;
1183 }
1184
1185 struct config_screen *config_screen_init(struct cui *cui,
1186                 const struct config *config,
1187                 const struct system_info *sysinfo,
1188                 void (*on_exit)(struct cui *))
1189 {
1190         struct config_screen *screen;
1191
1192         screen = talloc_zero(cui, struct config_screen);
1193         talloc_set_destructor(screen, config_screen_destroy);
1194         nc_scr_init(&screen->scr, pb_config_screen_sig, 0,
1195                         cui, config_screen_process_key,
1196                         config_screen_post, config_screen_unpost,
1197                         config_screen_resize);
1198
1199         screen->cui = cui;
1200         screen->on_exit = on_exit;
1201         screen->need_redraw = false;
1202         screen->need_update = false;
1203         screen->label_x = 2;
1204         screen->field_x = 17;
1205
1206         screen->ipmi_override = false;
1207         screen->show_subset = false;
1208
1209         screen->scr.frame.ltitle = talloc_strdup(screen,
1210                         _("Petitboot System Configuration"));
1211         screen->scr.frame.rtitle = NULL;
1212         screen->scr.frame.help = talloc_strdup(screen,
1213                         _("tab=next, shift+tab=previous, x=exit, h=help"));
1214         nc_scr_frame_draw(&screen->scr);
1215
1216         scrollok(screen->scr.sub_ncw, true);
1217
1218         config_screen_draw(screen, config, sysinfo);
1219
1220         return screen;
1221 }