ui/ncurses: Add network configuration
[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
21 #include <string.h>
22
23 #include <talloc/talloc.h>
24 #include <types/types.h>
25 #include <log/log.h>
26
27 #include "config.h"
28 #include "nc-cui.h"
29 #include "nc-config.h"
30 #include "nc-widgets.h"
31
32 #define N_FIELDS        18
33
34 enum net_conf_type {
35         NET_CONF_TYPE_DHCP_ALL,
36         NET_CONF_TYPE_DHCP_ONE,
37         NET_CONF_TYPE_STATIC,
38 };
39
40 struct config_screen {
41         struct nc_scr           scr;
42         struct cui              *cui;
43         struct nc_widgetset     *widgetset;
44         bool                    exit;
45         void                    (*on_exit)(struct cui *);
46
47         int                     label_x;
48         int                     field_x;
49         int                     network_config_y;
50
51         struct {
52                 struct nc_widget_checkbox       *autoboot_f;
53                 struct nc_widget_label          *autoboot_l;
54                 struct nc_widget_textbox        *timeout_f;
55                 struct nc_widget_label          *timeout_l;
56
57                 struct nc_widget_label          *network_l;
58                 struct nc_widget_select         *network_f;
59
60                 struct nc_widget_label          *iface_l;
61                 struct nc_widget_select         *iface_f;
62                 struct nc_widget_label          *ip_addr_l;
63                 struct nc_widget_textbox        *ip_addr_f;
64                 struct nc_widget_label          *ip_mask_l;
65                 struct nc_widget_textbox        *ip_mask_f;
66                 struct nc_widget_label          *gateway_l;
67                 struct nc_widget_textbox        *gateway_f;
68                 struct nc_widget_label          *dns_l;
69                 struct nc_widget_textbox        *dns_f;
70
71                 struct nc_widget_button         *ok_b;
72                 struct nc_widget_button         *cancel_b;
73         } widgets;
74 };
75
76 static struct config_screen *config_screen_from_scr(struct nc_scr *scr)
77 {
78         struct config_screen *config_screen;
79
80         assert(scr->sig == pb_config_screen_sig);
81         config_screen = (struct config_screen *)
82                 ((char *)scr - (size_t)&((struct config_screen *)0)->scr);
83         assert(config_screen->scr.sig == pb_config_screen_sig);
84         return config_screen;
85 }
86
87 static void config_screen_process_key(struct nc_scr *scr, int key)
88 {
89         struct config_screen *screen = config_screen_from_scr(scr);
90         bool handled;
91
92         handled = widgetset_process_key(screen->widgetset, key);
93         if (screen->exit)
94                 screen->on_exit(screen->cui);
95         else if (handled)
96                 wrefresh(screen->scr.main_ncw);
97 }
98
99 static void config_screen_resize(struct nc_scr *scr)
100 {
101         struct config_screen *screen = config_screen_from_scr(scr);
102         (void)screen;
103 }
104
105 static int config_screen_post(struct nc_scr *scr)
106 {
107         struct config_screen *screen = config_screen_from_scr(scr);
108         widgetset_post(screen->widgetset);
109         nc_scr_frame_draw(scr);
110         wrefresh(scr->main_ncw);
111         return 0;
112 }
113
114 static int config_screen_unpost(struct nc_scr *scr)
115 {
116         struct config_screen *screen = config_screen_from_scr(scr);
117         widgetset_unpost(screen->widgetset);
118         return 0;
119 }
120
121 struct nc_scr *config_screen_scr(struct config_screen *screen)
122 {
123         return &screen->scr;
124 }
125
126 static void ok_click(void *arg)
127 {
128         struct config_screen *screen = arg;
129         /* todo: save config */
130         screen->on_exit(screen->cui);
131 }
132
133 static void cancel_click(void *arg)
134 {
135         struct config_screen *screen = arg;
136         screen->exit = true;
137 }
138
139 static int layout_pair(struct config_screen *screen, int y,
140                 struct nc_widget_label *label,
141                 struct nc_widget *field)
142 {
143         struct nc_widget *label_w = widget_label_base(label);
144         widget_move(label_w, y, screen->label_x);
145         widget_move(field, y, screen->field_x);
146         return max(widget_height(label_w), widget_height(field));
147 }
148
149 static void config_screen_layout_widgets(struct config_screen *screen,
150                 enum net_conf_type net_conf)
151 {
152         struct nc_widget *wl, *wf;
153         bool show;
154         int y, x;
155
156         y = 1;
157
158         y += layout_pair(screen, y, screen->widgets.autoboot_l,
159                         widget_checkbox_base(screen->widgets.autoboot_f));
160
161         y += layout_pair(screen, y, screen->widgets.timeout_l,
162                         widget_textbox_base(screen->widgets.timeout_f));
163
164         y += 1;
165
166         y += layout_pair(screen, y, screen->widgets.network_l,
167                         widget_select_base(screen->widgets.network_f));
168
169         y += 1;
170
171         /* conditionally show iface select */
172         wl = widget_label_base(screen->widgets.iface_l);
173         wf = widget_select_base(screen->widgets.iface_f);
174
175         show = net_conf == NET_CONF_TYPE_DHCP_ONE ||
176                 net_conf == NET_CONF_TYPE_STATIC;
177
178         widget_set_visible(wl, show);
179         widget_set_visible(wf, show);
180
181         if (show)
182                 y += layout_pair(screen, y, screen->widgets.iface_l, wf) + 1;
183
184         /* conditionally show static IP params */
185         show = net_conf == NET_CONF_TYPE_STATIC;
186
187         wl = widget_label_base(screen->widgets.ip_addr_l);
188         wf = widget_textbox_base(screen->widgets.ip_addr_f);
189         widget_set_visible(wl, show);
190         widget_set_visible(wf, show);
191         x = screen->field_x + widget_width(wf) + 1;
192
193         if (show)
194                 layout_pair(screen, y, screen->widgets.ip_addr_l, wf);
195
196         wl = widget_label_base(screen->widgets.ip_mask_l);
197         wf = widget_textbox_base(screen->widgets.ip_mask_f);
198         widget_set_visible(wl, show);
199         widget_set_visible(wf, show);
200
201         if (show) {
202                 widget_move(wl, y, x);
203                 widget_move(wf, y, x + 2);
204                 y += 1;
205         }
206
207         wl = widget_label_base(screen->widgets.gateway_l);
208         wf = widget_textbox_base(screen->widgets.gateway_f);
209         widget_set_visible(wl, show);
210         widget_set_visible(wf, show);
211
212         if (show)
213                 y += layout_pair(screen, y, screen->widgets.gateway_l, wf);
214
215         wl = widget_label_base(screen->widgets.dns_l);
216         wf = widget_textbox_base(screen->widgets.dns_f);
217         widget_set_visible(wl, show);
218         widget_set_visible(wf, show);
219
220         if (show)
221                 y += 1 + layout_pair(screen, y, screen->widgets.dns_l, wf);
222
223         widget_move(widget_button_base(screen->widgets.ok_b),
224                         y, screen->field_x);
225         widget_move(widget_button_base(screen->widgets.cancel_b),
226                         y, screen->field_x + 10);
227 }
228
229 static void config_screen_network_change(void *arg, int value)
230 {
231         struct config_screen *screen = arg;
232         widgetset_unpost(screen->widgetset);
233         config_screen_layout_widgets(screen, value);
234         widgetset_post(screen->widgetset);
235 }
236
237 static void config_screen_setup_widgets(struct config_screen *screen,
238                 const struct config *config,
239                 const struct system_info *sysinfo)
240 {
241         struct nc_widgetset *set = screen->widgetset;
242         unsigned int i;
243         char *str;
244
245         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
246                         == N_FIELDS);
247
248         screen->widgets.autoboot_l = widget_new_label(set, 0, 0, "Autoboot:");
249         screen->widgets.autoboot_f = widget_new_checkbox(set, 0, 0,
250                                         config->autoboot_enabled);
251
252         str = talloc_asprintf(screen, "%d", config->autoboot_timeout_sec);
253         screen->widgets.timeout_l = widget_new_label(set, 0, 0, "Timeout:");
254         screen->widgets.timeout_f = widget_new_textbox(set, 0, 0, 5, str);
255
256         screen->widgets.network_l = widget_new_label(set, 0, 0, "Network");
257         screen->widgets.network_f = widget_new_select(set, 0, 0, 50);
258
259         widget_select_add_option(screen->widgets.network_f,
260                                         NET_CONF_TYPE_DHCP_ALL,
261                                         "DHCP on all active interfaces",
262                                         true);
263         widget_select_add_option(screen->widgets.network_f,
264                                         NET_CONF_TYPE_DHCP_ONE,
265                                         "DHCP on a specific interface",
266                                         false);
267         widget_select_add_option(screen->widgets.network_f,
268                                         NET_CONF_TYPE_STATIC,
269                                         "Static IP configuration",
270                                         false);
271
272         widget_select_on_change(screen->widgets.network_f,
273                         config_screen_network_change, screen);
274
275         screen->widgets.iface_l = widget_new_label(set, 0, 0, "Device:");
276         screen->widgets.iface_f = widget_new_select(set, 0, 0, 20);
277
278         for (i = 0; i < sysinfo->n_interfaces; i++) {
279                 struct interface_info *info = sysinfo->interfaces[i];
280                 widget_select_add_option(screen->widgets.iface_f,
281                                                 i, info->name, false);
282         }
283
284         screen->widgets.ip_addr_l = widget_new_label(set, 0, 0, "IP/mask:");
285         screen->widgets.ip_addr_f = widget_new_textbox(set, 0, 0, 16, "");
286         screen->widgets.ip_mask_l = widget_new_label(set, 0, 0, "/");
287         screen->widgets.ip_mask_f = widget_new_textbox(set, 0, 0, 3, "");
288
289         screen->widgets.gateway_l = widget_new_label(set, 0, 0, "Gateway:");
290         screen->widgets.gateway_f = widget_new_textbox(set, 0, 0, 16, "");
291
292         screen->widgets.dns_l = widget_new_label(set, 0, 0, "DNS Server:");
293         screen->widgets.dns_f = widget_new_textbox(set, 0, 0, 16, "");
294
295         screen->widgets.ok_b = widget_new_button(set, 0, 0, 6, "OK",
296                         ok_click, screen);
297         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 6, "Cancel",
298                         cancel_click, screen);
299 }
300
301 struct config_screen *config_screen_init(struct cui *cui,
302                 const struct config *config,
303                 const struct system_info *sysinfo,
304                 void (*on_exit)(struct cui *))
305 {
306         struct config_screen *screen;
307
308         screen = talloc_zero(cui, struct config_screen);
309         nc_scr_init(&screen->scr, pb_config_screen_sig, 0,
310                         cui, config_screen_process_key,
311                         config_screen_post, config_screen_unpost,
312                         config_screen_resize);
313
314         screen->cui = cui;
315         screen->on_exit = on_exit;
316         screen->label_x = 2;
317         screen->field_x = 16;
318
319         screen->scr.frame.ltitle = talloc_strdup(screen,
320                         "Petitboot System Configuration");
321         screen->scr.frame.rtitle = NULL;
322         screen->scr.frame.help = talloc_strdup(screen,
323                         "tab=next, shift+tab=previous");
324         nc_scr_frame_draw(&screen->scr);
325
326         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
327                         screen->scr.sub_ncw);
328         config_screen_setup_widgets(screen, config, sysinfo);
329         config_screen_layout_widgets(screen, NET_CONF_TYPE_DHCP_ALL);
330
331         wrefresh(screen->scr.main_ncw);
332         scrollok(screen->scr.sub_ncw, true);
333
334         return screen;
335 }