2 * Copyright (C) 2009 Sony Computer Entertainment Inc.
3 * Copyright 2009 Sony Corp.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <sys/ioctl.h>
28 #include "pb-protocol/pb-protocol.h"
29 #include "talloc/talloc.h"
30 #include "waiter/waiter.h"
31 #include "ui/common/discover-client.h"
34 static struct cui_opt_data *cod_from_item(struct pmenu_item *item)
40 * cui_abort - Signal the main cui program loop to exit.
42 * Sets cui.abort, which causes the cui_run() routine to return.
45 void cui_abort(struct cui *cui)
47 pb_log("%s: exiting\n", __func__);
52 * cui_resize - Signal the main cui program loop to resize
57 void cui_resize(struct cui *cui)
59 pb_log("%s: resizing\n", __func__);
64 * cui_make_item_name - Format the menu item name srting.
66 * Returns a talloc string.
69 static char *cui_make_item_name(struct pmenu_item *i, struct cui_opt_data *cod)
76 name = talloc_asprintf(i, "%s:", cod->name);
79 name = talloc_asprintf_append(name, " %s", cod->bd->image);
82 name = talloc_asprintf_append(name, " initrd=%s",
86 name = talloc_asprintf_append(name, " %s", cod->bd->args);
93 * cui_on_exit - A generic main menu ESC callback.
96 void cui_on_exit(struct pmenu *menu)
98 cui_abort(cui_from_pmenu(menu));
102 * cui_run_cmd - A generic cb to run the supplied command.
105 int cui_run_cmd(struct pmenu_item *item)
108 struct cui *cui = cui_from_item(item);
109 const char *const *cmd_argv = item->data;
111 nc_scr_status_printf(cui->current, "Running %s...", cmd_argv[0]);
115 result = pb_run_cmd(cmd_argv, 1, 0);
118 redrawwin(cui->current->main_ncw);
121 pb_log("%s: failed: '%s'\n", __func__, cmd_argv[0]);
122 nc_scr_status_printf(cui->current, "Failed: %s", cmd_argv[0]);
129 * cui_boot - A generic cb to run kexec.
132 static int cui_boot(struct pmenu_item *item)
135 struct cui *cui = cui_from_item(item);
136 struct cui_opt_data *cod = cod_from_item(item);
138 assert(cui->current == &cui->main->scr);
140 pb_log("%s: %s\n", __func__, cod->name);
141 nc_scr_status_printf(cui->current, "Booting %s...", cod->name);
145 result = discover_client_boot(cui->client, NULL, cod->opt, cod->bd);
148 redrawwin(cui->current->main_ncw);
151 nc_scr_status_printf(cui->current,
152 "Failed: boot %s", cod->bd->image);
159 * cui_boot_editor_on_exit - The boot_editor on_exit callback.
162 static void cui_boot_editor_on_exit(struct boot_editor *boot_editor, enum boot_editor_result boot_editor_result,
163 struct pb_boot_data *bd)
165 struct cui *cui = cui_from_arg(boot_editor->scr.ui_ctx);
167 if (boot_editor_result == boot_editor_update) {
168 struct pmenu_item *i = pmenu_find_selected(cui->main);
169 struct cui_opt_data *cod = cod_from_item(i);
175 talloc_free(cod->bd);
178 name = cui_make_item_name(i, cod);
179 pmenu_item_replace(i, name);
181 /* FIXME: need to make item visible somehow */
182 set_current_item(cui->main->ncm, i->nci);
184 pb_log("%s: updating opt '%s'\n", __func__, cod->name);
185 pb_log(" image '%s'\n", cod->bd->image);
186 pb_log(" initrd '%s'\n", cod->bd->initrd);
187 pb_log(" args '%s'\n", cod->bd->args);
190 cui_set_current(cui, &cui->main->scr);
192 talloc_free(boot_editor);
195 int cui_boot_editor_run(struct pmenu_item *item)
197 struct cui *cui = cui_from_item(item);
198 struct cui_opt_data *cod = cod_from_item(item);
199 struct boot_editor *boot_editor;
201 boot_editor = boot_editor_init(cui, cod->bd, cui_boot_editor_on_exit);
202 cui_set_current(cui, &boot_editor->scr);
208 * cui_set_current - Set the currently active screen and redraw it.
211 struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr)
215 DBGS("%p -> %p\n", cui->current, scr);
217 assert(cui->current != scr);
223 cui->current->post(cui->current);
229 * cui_process_key - Process input on stdin.
232 static int cui_process_key(void *arg)
234 struct cui *cui = cui_from_arg(arg);
236 assert(cui->current);
238 ui_timer_disable(&cui->timer);
239 cui->current->process_key(cui->current);
245 * cui_process_js - Process joystick events.
248 static int cui_process_js(void *arg)
250 struct cui *cui = cui_from_arg(arg);
253 c = pjs_process_event(cui->pjs);
257 cui_process_key(arg);
264 * cui_handle_timeout - Handle the timeout.
267 static void cui_handle_timeout(struct ui_timer *timer)
269 struct cui *cui = cui_from_timer(timer);
270 struct pmenu_item *i = pmenu_find_selected(cui->main);
274 struct cui_opt_data *cod = cod_from_item(i);
275 assert(cod && (cod->opt_hash == cui->default_item));
282 * cui_handle_resize - Handle the term resize.
285 static void cui_handle_resize(struct cui *cui)
289 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
290 pb_log("%s: ioctl failed: %s\n", __func__, strerror(errno));
294 pb_log("%s: {%u,%u}\n", __func__, ws.ws_row, ws.ws_col);
296 wclear(cui->current->main_ncw);
297 resize_term(ws.ws_row, ws.ws_col);
298 cui->current->resize(cui->current);
300 /* For some reason this makes ncurses redraw the screen */
302 redrawwin(cui->current->main_ncw);
303 wrefresh(cui->current->main_ncw);
307 * cui_on_open - Open new item callback.
310 void cui_on_open(struct pmenu *menu)
312 unsigned int insert_pt;
313 struct pmenu_item *i;
314 struct cui_opt_data *cod;
316 menu->scr.unpost(&menu->scr);
318 /* This disconnects items array from menu. */
320 set_menu_items(menu->ncm, NULL);
322 /* Insert new items at insert_pt. */
324 insert_pt = pmenu_grow(menu, 1);
325 i = pmenu_item_alloc(menu);
327 i->on_edit = cui_boot_editor_run;
328 i->on_execute = cui_boot;
329 i->data = cod = talloc_zero(i, struct cui_opt_data);
331 cod->name = talloc_asprintf(i, "User item %u:", insert_pt);
332 cod->bd = talloc_zero(i, struct pb_boot_data);
334 pmenu_item_setup(menu, i, insert_pt, talloc_strdup(i, cod->name));
336 /* Re-attach the items array. */
338 set_menu_items(menu->ncm, menu->items);
340 menu->scr.post(&menu->scr);
341 set_current_item(menu->ncm, i->nci);
347 * cui_device_add - Client device_add callback.
349 * Creates menu_items for all the device boot_options and inserts those
350 * menu_items into the main menu. Redraws the main menu if it is active.
353 static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
356 struct cui *cui = cui_from_arg(arg);
357 struct cui_opt_data *cod;
358 unsigned int insert_pt;
359 struct pmenu_item *i;
364 pb_log("%s: %p %s\n", __func__, opt, opt->id);
366 selected = current_item(cui->main->ncm);
368 if (cui->current == &cui->main->scr)
369 cui->current->unpost(cui->current);
371 /* This disconnects items array from menu. */
373 result = set_menu_items(cui->main->ncm, NULL);
376 pb_log("%s: set_menu_items failed: %d\n", __func__, result);
378 /* Insert new items at insert_pt. */
379 insert_pt = pmenu_grow(cui->main, 1);
381 /* Save the item in opt->ui_info for cui_device_remove() */
383 opt->ui_info = i = pmenu_item_alloc(cui->main);
385 i->on_edit = cui_boot_editor_run;
386 i->on_execute = cui_boot;
387 i->data = cod = talloc(i, struct cui_opt_data);
391 cod->opt_hash = pb_opt_hash(dev, opt);
392 cod->name = opt->name;
393 cod->bd = talloc(i, struct pb_boot_data);
395 cod->bd->image = talloc_strdup(cod->bd, opt->boot_image_file);
396 cod->bd->initrd = talloc_strdup(cod->bd, opt->initrd_file);
397 cod->bd->args = talloc_strdup(cod->bd, opt->boot_args);
399 name = cui_make_item_name(i, cod);
400 pmenu_item_setup(cui->main, i, insert_pt, name);
402 pb_log("%s: adding opt '%s'\n", __func__, cod->name);
403 pb_log(" image '%s'\n", cod->bd->image);
404 pb_log(" initrd '%s'\n", cod->bd->initrd);
405 pb_log(" args '%s'\n", cod->bd->args);
407 /* If this is the default_item select it and start timer. */
408 if (cod->opt_hash == cui->default_item) {
410 ui_timer_kick(&cui->timer);
413 /* Re-attach the items array. */
414 result = set_menu_items(cui->main->ncm, cui->main->items);
417 pb_log("%s: set_menu_items failed: %d\n", __func__, result);
420 pb_log("%s\n", __func__);
421 pmenu_dump_items(cui->main->items,
422 item_count(cui->main->ncm) + 1);
425 /* FIXME: need to make item visible somehow */
426 menu_driver(cui->main->ncm, REQ_SCR_UPAGE);
427 menu_driver(cui->main->ncm, REQ_SCR_DPAGE);
428 set_current_item(cui->main->ncm, selected);
430 if (cui->current == &cui->main->scr)
431 cui->current->post(cui->current);
437 * cui_device_remove - Client device remove callback.
439 * Removes all the menu_items for the device from the main menu and redraws the
440 * main menu if it is active.
443 static void cui_device_remove(struct device *dev, void *arg)
445 struct cui *cui = cui_from_arg(arg);
447 struct boot_option *opt;
449 pb_log("%s: %p %s\n", __func__, dev, dev->id);
451 if (cui->current == &cui->main->scr)
452 cui->current->unpost(cui->current);
454 /* This disconnects items array from menu. */
456 result = set_menu_items(cui->main->ncm, NULL);
459 pb_log("%s: set_menu_items failed: %d\n", __func__, result);
461 list_for_each_entry(&dev->boot_options, opt, list) {
462 struct pmenu_item *i = pmenu_item_from_arg(opt->ui_info);
463 struct cui_opt_data *cod = cod_from_item(i);
465 assert(pb_protocol_device_cmp(dev, cod->dev));
466 pmenu_remove(cui->main, i);
468 /* If this is the default_item disable timer. */
470 if (cod->opt_hash == cui->default_item)
471 ui_timer_disable(&cui->timer);
474 /* Re-attach the items array. */
476 result = set_menu_items(cui->main->ncm, cui->main->items);
479 pb_log("%s: set_menu_items failed: %d\n", __func__, result);
482 pb_log("%s\n", __func__);
483 pmenu_dump_items(cui->main->items,
484 item_count(cui->main->ncm) + 1);
487 if (cui->current == &cui->main->scr)
488 cui->current->post(cui->current);
491 static void cui_update_status(struct boot_status *status, void *arg)
493 struct cui *cui = cui_from_arg(arg);
495 nc_scr_status_printf(cui->current,
497 status->type == BOOT_STATUS_ERROR ? "Error" : "Info",
502 static struct discover_client_ops cui_client_ops = {
504 .boot_option_add = cui_boot_option_add,
505 .device_remove = cui_device_remove,
506 .update_status = cui_update_status,
510 * cui_init - Setup the cui instance.
511 * @platform_info: A value for the struct cui platform_info member.
513 * Returns a pointer to a struct cui on success, or NULL on error.
515 * Allocates the cui instance, sets up the client and stdin waiters, and
516 * sets up the ncurses menu screen.
519 struct cui *cui_init(void* platform_info,
520 int (*js_map)(const struct js_event *e), int start_deamon)
525 cui = talloc_zero(NULL, struct cui);
528 pb_log("%s: alloc cui failed.\n", __func__);
529 fprintf(stderr, "%s: alloc cui failed.\n", __func__);
533 cui->c_sig = pb_cui_sig;
534 cui->platform_info = platform_info;
535 cui->timer.handle_timeout = cui_handle_timeout;
536 cui->waitset = waitset_create(cui);
538 /* Loop here for scripts that just started the server. */
541 for (i = start_deamon ? 2 : 10; i; i--) {
542 cui->client = discover_client_init(cui->waitset,
543 &cui_client_ops, cui);
544 if (cui->client || !i)
546 pb_log("%s: waiting for server %d\n", __func__, i);
550 if (!cui->client && start_deamon) {
555 result = pb_start_daemon();
560 pb_log("%s: discover_client_init failed.\n", __func__);
561 fprintf(stderr, "%s: error: discover_client_init failed.\n",
563 fprintf(stderr, "could not start pb-discover, the petitboot "
565 goto fail_client_init;
569 pb_log("%s: discover_client_init failed.\n", __func__);
570 fprintf(stderr, "%s: error: discover_client_init failed.\n",
572 fprintf(stderr, "check that pb-discover, "
573 "the petitboot daemon is running.\n");
574 goto fail_client_init;
580 waiter_register(cui->waitset, STDIN_FILENO, WAIT_IN,
581 cui_process_key, cui);
585 cui->pjs = pjs_init(cui, js_map);
588 waiter_register(cui->waitset, pjs_get_fd(cui->pjs),
589 WAIT_IN, cui_process_js, cui);
601 * cui_run - The main cui program loop.
602 * @cui: The cui instance.
603 * @main: The menu to use as the main menu.
605 * Runs the cui engine. Does not return until indicated to do so by some
606 * user action, or an error occurs. Frees the cui object on return.
607 * Returns 0 on success (return to shell), -1 on error (should restart).
610 int cui_run(struct cui *cui, struct pmenu *main, unsigned int default_item)
615 cui->current = &cui->main->scr;
616 cui->default_item = default_item;
618 cui->current->post(cui->current);
621 int result = waiter_poll(cui->waitset);
623 if (result < 0 && errno != EINTR) {
624 pb_log("%s: poll: %s\n", __func__, strerror(errno));
631 ui_timer_process_sig(&cui->timer);
633 while (cui->resize) {
635 cui_handle_resize(cui);
641 return cui->abort ? 0 : -1;