#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
+#include <locale.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/reboot.h>
static struct pmenu *main_menu_init(struct cui *cui);
static struct pmenu *plugin_menu_init(struct cui *cui);
+static void cui_cancel_autoboot_on_exit(struct cui *cui);
+
+static struct {
+ int key;
+ struct autoboot_option opt;
+} autoboot_override_keys[] = {
+ { KEY_F(10), {
+ .boot_type = BOOT_DEVICE_TYPE,
+ .type = DEVICE_TYPE_DISK,
+ },
+ },
+ { KEY_F(11), {
+ .boot_type = BOOT_DEVICE_TYPE,
+ .type = DEVICE_TYPE_USB,
+ },
+ },
+ { KEY_F(12), {
+ .boot_type = BOOT_DEVICE_TYPE,
+ .type = DEVICE_TYPE_NETWORK,
+ },
+ },
+};
+
static bool lockdown_active(void)
{
+#if defined(SIGNED_BOOT) && defined(HARD_LOCKDOWN)
+ return true;
+#else
bool lockdown = false;
if (access(LOCKDOWN_FILE, F_OK) != -1)
lockdown = true;
return lockdown;
+#endif
+}
+
+static void cui_set_curses_options(bool curses_mode)
+{
+ if (curses_mode) {
+ cbreak(); /* Disable line buffering. */
+ noecho(); /* Disable getch() echo. */
+ nonl(); /* Disable new-line translation. */
+ intrflush(stdscr, FALSE); /* Disable interrupt flush. */
+ curs_set(0); /* Make cursor invisible */
+ nodelay(stdscr, TRUE); /* Enable non-blocking getch() */
+ } else {
+ nocbreak(); /* Enable line buffering. */
+ echo(); /* Enable getch() echo. */
+ nl(); /* Enable new-line translation. */
+ intrflush(stdscr, TRUE); /* Enable interrupt flush. */
+ curs_set(1); /* Make cursor visible */
+ nodelay(stdscr, FALSE); /* Disable non-blocking getch() */
+ }
}
static void cui_start(void)
{
initscr(); /* Initialize ncurses. */
- cbreak(); /* Disable line buffering. */
- noecho(); /* Disable getch() echo. */
keypad(stdscr, TRUE); /* Enable num keypad keys. */
- nonl(); /* Disable new-line translation. */
- intrflush(stdscr, FALSE); /* Disable interrupt flush. */
- curs_set(0); /* Make cursor invisible */
- nodelay(stdscr, TRUE); /* Enable non-blocking getch() */
+ cui_set_curses_options(true);
/* We may be operating with an incorrect $TERM type; in this case
* the keymappings will be slightly broken. We want at least
define_key("\x1b\x4f\x46", KEY_END);
define_key("OH", KEY_HOME);
define_key("OF", KEY_END);
+
+ /* Arrow keys in normal cursor mode */
define_key("\x1b\x5b\x41", KEY_UP);
define_key("\x1b\x5b\x42", KEY_DOWN);
+ define_key("\x1b\x5b\x43", KEY_RIGHT);
+ define_key("\x1b\x5b\x44", KEY_LEFT);
+ /* Arrow keys in "application" cursor mode */
+ define_key("\x1b\x4f\x41", KEY_UP);
+ define_key("\x1b\x4f\x42", KEY_DOWN);
+ define_key("\x1b\x4f\x43", KEY_RIGHT);
+ define_key("\x1b\x4f\x44", KEY_LEFT);
+
define_key("\x1b\x5b\x33\x7e", KEY_DC);
while (getch() != ERR) /* flush stdin */
void cui_abort(struct cui *cui)
{
- pb_log("%s: exiting\n", __func__);
+ pb_log_fn("exiting\n");
cui->abort = 1;
}
struct cui *cui = cui_from_pmenu(menu);
char *sh_cmd;
+ cui_cancel_autoboot_on_exit(cui);
+
sh_cmd = talloc_asprintf(cui,
"echo \"Exiting petitboot. Type 'exit' to return.\";\
echo \"You may run 'pb-sos' to gather diagnostic data\";\
talloc_free(sh_cmd);
}
+/**
+ * cui_abort_on_exit - Force an exit of the main loop on menu exit.
+ * This is mainly for lockdown situations where
+ * the exit then triggers an expected reboot.
+ */
+void cui_abort_on_exit(struct pmenu *menu)
+{
+ struct cui *cui = cui_from_pmenu(menu);
+ cui->abort = 1;
+}
+
/**
* cui_run_cmd - A generic cb to run the supplied command.
*/
nc_scr_post(cui->current);
if (result) {
- pb_log("%s: failed: '%s'\n", __func__, cmd_argv[0]);
+ pb_log_fn("failed: '%s'\n", cmd_argv[0]);
nc_scr_status_printf(cui->current, _("Failed: %s"),
cmd_argv[0]);
}
return old;
}
+static bool set_temp_autoboot_opt(struct cui *cui, struct autoboot_option *opt)
+{
+ cui->autoboot_opt = opt;
+ if (cui->client)
+ discover_client_send_temp_autoboot(cui->client, opt);
+
+ return true;
+}
+
+static bool key_cancels_boot(int key)
+{
+ unsigned int i;
+
+ if (key == 0xc)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(autoboot_override_keys); i++)
+ if (key == autoboot_override_keys[i].key)
+ return false;
+
+ return true;
+}
+
static bool process_global_keys(struct cui *cui, int key)
{
+ unsigned int i;
+
switch (key) {
case 0xc:
if (cui->current && cui->current->main_ncw)
wrefresh(curscr);
return true;
}
+
+ /* check for autoboot override keys */
+ for (i = 0; i < ARRAY_SIZE(autoboot_override_keys); i++) {
+ if (key != autoboot_override_keys[i].key)
+ continue;
+
+ pb_log("Sending temporary autoboot override\n");
+ set_temp_autoboot_opt(cui, &autoboot_override_keys[i].opt);
+ return true;
+ }
+
return false;
}
}
}
- if (!cui->has_input) {
+ if (cui->preboot_mode) {
+ /* Turn curses options back on if the user interacts */
+ cui->preboot_mode = false;
+ cui_set_curses_options(true);
+ }
+
+ if (!cui->has_input && key_cancels_boot(c)) {
cui->has_input = true;
if (cui->client) {
pb_log("UI input received (key = %d), aborting "
struct winsize ws;
if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
- pb_log("%s: ioctl failed: %s\n", __func__, strerror(errno));
+ pb_log_fn("ioctl failed: %s\n", strerror(errno));
return;
}
result = set_menu_items(menu->ncm, NULL);
if (result)
- pb_log("%s: set_menu_items failed: %d\n", __func__, result);
+ pb_log_fn("set_menu_items failed: %d\n", result);
/* Insert new items at insert_pt. */
if (dev_hdr) {
}
if (plugin_option) {
- pb_log("%s: adding plugin '%s'\n", __func__, cod->name);
+ pb_log_fn("adding plugin '%s'\n", cod->name);
pb_log(" file '%s'\n", cod->pd->plugin_file);
} else {
- pb_log("%s: adding opt '%s'\n", __func__, cod->name);
+ pb_log_fn("adding opt '%s'\n", cod->name);
pb_log(" image '%s'\n", cod->bd->image);
pb_log(" initrd '%s'\n", cod->bd->initrd);
pb_log(" args '%s'\n", cod->bd->args);
}
result = set_menu_items(cui->main->ncm, cui->main->items);
if (result)
- pb_log("%s: set_menu_items failed: %d\n", __func__, result);
+ pb_log_fn("set_menu_items failed: %d\n", result);
}
/* Re-attach the items array. */
result = set_menu_items(menu->ncm, menu->items);
if (result)
- pb_log("%s: set_menu_items failed: %d\n", __func__, result);
+ pb_log_fn("set_menu_items failed: %d\n", result);
if (0) {
pb_log("%s\n", __func__);
int rows, cols, top, last;
int result;
- pb_log("%s: %p %s\n", __func__, dev, dev->id);
+ pb_log_fn("%p %s\n", dev, dev->id);
if (cui->current == &cui->main->scr)
nc_scr_unpost(cui->current);
result |= set_menu_items(cui->plugin_menu->ncm, NULL);
if (result)
- pb_log("%s: set_menu_items failed: %d\n", __func__, result);
+ pb_log_fn("set_menu_items failed: %d\n", result);
list_for_each_entry(&dev->boot_options, opt, list) {
struct pmenu_item *item = pmenu_item_from_arg(opt->ui_info);
}
if (result)
- pb_log("%s: set_menu_items failed: %d\n", __func__, result);
+ pb_log_fn("set_menu_items failed: %d\n", result);
if (0) {
pb_log("%s\n", __func__);
statuslog_append_steal(cui, cui->statuslog, status);
/* Ignore status messages from the backlog */
- if (!status->backlog)
- nc_scr_status_printf(cui->current, "%s", status->message);
+ if (status->backlog)
+ return;
+
+ nc_scr_status_printf(cui->current, "%s", status->message);
+
+ if (cui->preboot_mode &&
+ (!status->boot_active || status->type == STATUS_ERROR)) {
+ cui_set_curses_options(true);
+ cui->preboot_mode = false;
+ } else {
+ cui->preboot_mode = status->boot_active &&
+ status->type == STATUS_INFO;
+ if (cui->preboot_mode)
+ cui_set_curses_options(false);
+ }
}
/*
* If this option was faked above move the context under
* the item so it is cleaned up later in cui_plugins_remove().
*/
- if (strncmp(cod->opt->id, "dummy", strlen("dummy") == 0 &&
- cod->dev->type == DEVICE_TYPE_UNKNOWN)) {
+ if (strcmp(cod->opt->id, "dummy") == 0 &&
+ cod->dev->type == DEVICE_TYPE_UNKNOWN) {
talloc_steal(item, cod->dev);
talloc_steal(item, cod->opt);
}
int result;
bool lockdown = lockdown_active();
- m = pmenu_init(cui, 9, cui_on_exit);
+ m = pmenu_init(cui, 9, lockdown ? cui_abort_on_exit : cui_on_exit);
if (!m) {
- pb_log("%s: failed\n", __func__);
+ pb_log_fn("failed\n");
return NULL;
}
+ m->n_hot_keys = 1;
+ m->hot_keys = talloc_array(m, hot_key_fn, m->n_hot_keys);
+ if (!m->hot_keys) {
+ pb_log_fn("failed to allocate hot_keys\n");
+ talloc_free(m);
+ return NULL;
+ }
+ m->hot_keys[0] = pmenu_main_hot_keys;
m->on_new = cui_item_new;
m->scr.frame.ltitle = talloc_asprintf(m,
int result;
m = pmenu_init(cui, 2, cui_plugin_menu_exit);
- m->on_new = cui_item_new;
m->scr.frame.ltitle = talloc_asprintf(m, _("Petitboot Plugins"));
- m->scr.frame.rtitle = talloc_asprintf(m, NULL);
+ m->scr.frame.rtitle = talloc_asprintf(m, "%s", "");
m->scr.frame.help = talloc_strdup(m,
_("Enter=install, e=details, x=exit, h=help"));
m->scr.frame.status = talloc_asprintf(m,
.update_config = cui_update_config,
};
-/* cui_server_wait_on_exit - On exit spin until the server is available.
- *
- * If the program exits before connecting to the server autoboot won't be
- * cancelled even though there has been keyboard activity. This function is
- * called by a child process which will spin until the server is connected and
- * told to cancel autoboot.
- *
- * Processes exiting from this function will not carry out the cui_atexit()
- * steps.
- */
-static void cui_server_wait_on_exit(struct cui *cui)
-{
- cui_detached = true;
-
- while (!cui->client) {
- cui->client = discover_client_init(cui->waitset,
- &cui_client_ops, cui);
- if (!cui->client)
- sleep(1);
- }
-
- talloc_steal(cui, cui->client);
- discover_client_cancel_default(cui->client);
-}
-
/* cui_server_wait - Connect to the discover server.
* @arg: Pointer to the cui instance.
*
pb_log("Aborting default boot on pb-discover connect\n");
discover_client_cancel_default(cui->client);
}
+
+ if (cui->autoboot_opt) {
+ pb_log("Sending autoboot override on pb-discover connect\n");
+ discover_client_send_temp_autoboot(cui->client,
+ cui->autoboot_opt);
+ }
}
return 0;
cui = talloc_zero(NULL, struct cui);
if (!cui) {
- pb_log("%s: alloc cui failed.\n", __func__);
+ pb_log_fn("alloc cui failed.\n");
fprintf(stderr, _("%s: alloc cui failed.\n"), __func__);
goto fail_alloc;
}
&cui_client_ops, cui);
if (cui->client || !i)
break;
- pb_log("%s: waiting for server %d\n", __func__, i);
+ pb_log_fn("waiting for server %d\n", i);
sleep(1);
}
if (!result)
goto retry_start;
- pb_log("%s: discover_client_init failed.\n", __func__);
+ pb_log_fn("discover_client_init failed.\n");
fprintf(stderr, _("%s: error: discover_client_init failed.\n"),
__func__);
fprintf(stderr, _("could not start pb-discover, the petitboot "
waiter_register_timeout(cui->waitset, 0,
cui_server_wait, cui);
} else if (!cui->client) {
- pb_log("%s: discover_client_init failed.\n", __func__);
+ pb_log_fn("discover_client_init failed.\n");
fprintf(stderr, _("%s: error: discover_client_init failed.\n"),
__func__);
fprintf(stderr, _("check that pb-discover, "
return NULL;
}
+/**
+ * cui_cancel_autoboot_on_exit - On exit spin until the server is available.
+ *
+ * If the program exits before connecting to the server autoboot won't be
+ * cancelled even though there has been keyboard activity. A child is forked
+ * which will spin until the server is connected and told to cancel autoboot.
+ */
+static void cui_cancel_autoboot_on_exit(struct cui *cui)
+{
+ pid_t pid;
+
+ if (!cui->client) {
+ /* Fork a child to tell the server to cancel autoboot */
+ pid = fork();
+ if (!pid) {
+ cui_detached = true;
+
+ /* Loop until connection established */
+ while (!cui->client) {
+ cui->client = discover_client_init(cui->waitset,
+ &cui_client_ops, cui);
+ if (!cui->client)
+ sleep(1);
+ }
+
+ talloc_steal(cui, cui->client);
+ discover_client_cancel_default(cui->client);
+ exit(EXIT_SUCCESS);
+ }
+ if (pid < 0)
+ pb_log("Failed to fork child on exit: %m\n");
+ } else
+ discover_client_cancel_default(cui->client);
+}
+
/**
* cui_run - The main cui program loop.
* @cui: The cui instance.
int cui_run(struct cui *cui)
{
- pid_t pid;
-
- assert(main);
+ assert(cui);
+ assert(cui->main);
cui->current = &cui->main->scr;
cui->default_item = 0;
int result = waiter_poll(cui->waitset);
if (result < 0) {
- pb_log("%s: poll: %s\n", __func__, strerror(errno));
+ pb_log_fn("poll: %s\n", strerror(errno));
break;
}
}
}
- cui_atexit();
+ cui_cancel_autoboot_on_exit(cui);
- if (!cui->client) {
- /* Fork a child to tell the server to cancel autoboot */
- pid = fork();
- if (!pid) {
- cui_server_wait_on_exit(cui);
- exit(EXIT_SUCCESS);
- }
- if (pid < 0)
- pb_log("Failed to fork child on exit: %m\n");
- }
+ cui_atexit();
return cui->abort ? 0 : -1;
}