ui/ncurses: clear DNS server setting before appending new servers
[petitboot] / ui / twin / main-generic.c
1 /*
2  * Petitboot twin bootloader
3  *
4  *  Copyright Geoff Levand <geoff@infradead.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; version 2 of the License.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #if defined(HAVE_CONFIG_H)
21 #include "config.h"
22 #endif
23
24 #include <assert.h>
25 #include <errno.h>
26 #include <getopt.h>
27 #include <signal.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/time.h>
31
32 #include "log/log.h"
33 #include "talloc/talloc.h"
34 #include "waiter/waiter.h"
35 #include "ui/common/timer.h"
36
37 #include "pbt-client.h"
38 #include "pbt-main.h"
39
40
41 static struct pbt_client *client_from_item(struct pbt_item *item)
42 {
43         return item->data;
44 }
45
46 static int exit_to_shell_cb(struct pbt_item *item)
47 {
48         struct pbt_client *client = client_from_item(item);
49
50         client->signal_data.abort = 1;
51         return 0;
52 }
53
54 static int edit_preferences_cb(struct pbt_item *item)
55 {
56         struct pbt_client *client = client_from_item(item);
57
58         (void)client;
59
60         pb_debug("%s: TODO\n", __func__);
61
62         return 0;
63 }
64
65 static struct pbt_item *setup_system_item(struct pbt_menu *menu,
66         struct pbt_client *client)
67 {
68         struct pbt_item *top_item;
69         struct pbt_item *sub_item;
70         struct pbt_quad q;
71
72         top_item = pbt_item_create_reduced(menu, "system", 0,
73                 PB_ARTWORK_PATH "/applications-system.png");
74
75         if (!top_item)
76                 goto fail_top_item_create;
77
78         /* sub_menu */
79
80         q.x = menu->window->pixmap->width;
81         q.y = 0;
82         q.width = menu->scr->tscreen->width - q.x;
83         q.height = menu->scr->tscreen->height;
84
85         top_item->sub_menu = pbt_menu_create(top_item, "system", menu->scr,
86                 menu, &q, &menu->layout);
87
88         if (!top_item->sub_menu)
89                 goto fail_sub_menu_create;
90
91         sub_item = pbt_item_create(top_item->sub_menu, "Preferences", 0,
92                 PB_ARTWORK_PATH "/configure.png", "Preferences",
93                 "Edit petitboot preferences");
94
95         if (!sub_item)
96                 goto fail_sub_item_0;
97
98         sub_item->on_execute = edit_preferences_cb;
99         sub_item->data = client;
100         pbt_menu_set_selected(top_item->sub_menu, sub_item);
101
102         sub_item = pbt_item_create(top_item->sub_menu, "Exit to Shell", 1,
103                 PB_ARTWORK_PATH "/utilities-terminal.png", "Exit to Shell",
104                 "Exit to a system shell prompt");
105
106         if (!sub_item)
107                 goto fail_sub_item_1;
108
109         sub_item->on_execute = exit_to_shell_cb;
110         sub_item->data = client;
111
112         top_item->sub_menu->n_items = 2;
113
114         /* Set shell item as default */
115
116         pbt_menu_set_selected(top_item->sub_menu, sub_item);
117
118         return top_item;
119
120 fail_sub_item_1:
121 fail_sub_item_0:
122 fail_sub_menu_create:
123 // FIXME: todo
124 fail_top_item_create:
125 // FIXME: need cleanup
126         assert(0);
127         return NULL;
128 }
129
130 static struct pbt_menu *menu_create(struct pbt_client *client)
131 {
132         static struct pbt_menu_layout layout = {
133                 .item_height = 64,
134                 .item_space = 10,
135                 .text_space = 5,
136                 .title = {.font_size = 30, .color = 0xff000000,},
137                 .text = {.font_size = 18, .color = 0xff800000,},
138         };
139
140         struct pbt_menu *device_menu;
141         struct pbt_item *system_item;
142         struct pbt_quad q;
143         twin_pixmap_t *icon;
144         const struct pbt_border *border;
145
146         assert(client->frame.scr);
147
148         icon = pbt_icon_load(NULL);
149
150         if (!icon)
151                 return NULL;
152
153         assert((unsigned int)icon->height == layout.item_height);
154
155         /* Create main (device) menu */
156
157         border = &pbt_right_border;
158
159         q.x = 0;
160         q.y = 0;
161         q.width = icon->width + 2 * layout.item_space + border->left
162                 + border->right;
163         q.height = client->frame.scr->tscreen->height;
164
165         device_menu = pbt_menu_create(client, "device", client->frame.scr, NULL,
166                 &q, &layout);
167
168         if (!device_menu)
169                 goto fail_menu;
170
171         //FIXME: move to accessors
172         device_menu->background_color = 0x80000000;
173         device_menu->border = *border;
174
175         /* Setup system item */
176
177         system_item = setup_system_item(device_menu, client);
178
179         if (!system_item)
180                 goto fail_system_item;
181
182         device_menu->n_items++;
183
184         /* Set system item as default */
185
186         pbt_menu_set_selected(device_menu, system_item);
187         pbt_menu_set_focus(device_menu, 1);
188         pbt_menu_show(device_menu, 1);
189
190         pbt_menu_redraw(device_menu);
191
192         return device_menu;
193
194 fail_system_item:
195         // FIXME: need cleanup
196 fail_menu:
197         assert(0);
198         return NULL;
199 }
200
201 static int run(struct pbt_client *client)
202 {
203         while (1) {
204                 int result = waiter_poll(client->waitset);
205
206                 if (result < 0) {
207                         pb_log("%s: poll: %s\n", __func__, strerror(errno));
208                         break;
209                 }
210
211                 if (client->signal_data.abort)
212                         break;
213
214                 while (client->signal_data.resize) {
215                         client->signal_data.resize = 0;
216                         pbt_client_resize(client);
217                 }
218         }
219
220         return 0;
221 }
222
223 static struct pb_signal_data *_signal_data;
224
225 static void set_signal_data(struct pb_signal_data *sd)
226 {
227         _signal_data = sd;
228 }
229
230 static struct pb_signal_data *get_signal_data(void)
231 {
232         return _signal_data;
233 }
234
235 static void sig_handler(int signum)
236 {
237         DBGS("%d\n", signum);
238
239         struct pb_signal_data *sd = get_signal_data();
240
241         if (!sd)
242                 return;
243
244         switch (signum) {
245         case SIGWINCH:
246                 sd->resize = 1;
247                 break;
248         default:
249                 assert(0 && "unknown sig");
250                 /* fall through */
251         case SIGINT:
252         case SIGHUP:
253         case SIGTERM:
254                 sd->abort = 1;
255                 break;
256         }
257 }
258
259 /**
260  * main - twin bootloader main routine.
261  */
262
263 int main(int argc, char *argv[])
264 {
265         static struct sigaction sa;
266         static struct pbt_opts opts;
267         int result;
268         int ui_result;
269         struct pbt_client *client;
270         FILE *log;
271
272         result = pbt_opts_parse(&opts, argc, argv);
273
274         if (result) {
275                 pbt_print_usage();
276                 return EXIT_FAILURE;
277         }
278
279         if (opts.show_help == pbt_opt_yes) {
280                 pbt_print_usage();
281                 return EXIT_SUCCESS;
282         }
283
284         if (opts.show_version == pbt_opt_yes) {
285                 pbt_print_version();
286                 return EXIT_SUCCESS;
287         }
288
289         log = stderr;
290         if (strcmp(opts.log_file, "-")) {
291                 FILE *log = fopen(opts.log_file, "a");
292                 if (!log)
293                         log = stderr;
294         }
295         pb_log_init(log);
296
297         pb_log("--- petitboot-twin ---\n");
298
299         sa.sa_handler = sig_handler;
300         result = sigaction(SIGALRM, &sa, NULL);
301         result += sigaction(SIGHUP, &sa, NULL);
302         result += sigaction(SIGINT, &sa, NULL);
303         result += sigaction(SIGTERM, &sa, NULL);
304         result += sigaction(SIGWINCH, &sa, NULL);
305
306         if (result) {
307                 pb_log("%s sigaction failed.\n", __func__);
308                 return EXIT_FAILURE;
309         }
310
311         client = pbt_client_init(opts.backend, 1024, 640, opts.start_daemon);
312
313         if (!client) {
314                 ui_result = EXIT_FAILURE;
315                 goto done;
316         }
317
318         set_signal_data(&client->signal_data);
319
320         client->frame.top_menu = menu_create(client);
321
322         if (!client->frame.top_menu) {
323                 ui_result = EXIT_FAILURE;
324                 goto done;
325         }
326
327         twin_screen_update(client->frame.scr->tscreen);
328         ui_result = run(client);
329
330 done:
331         talloc_free(client);
332
333         pb_log("--- end ---\n");
334
335         return ui_result ? EXIT_FAILURE : EXIT_SUCCESS;
336 }