#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/reboot.h>
#include "log/log.h"
#include "pb-protocol/pb-protocol.h"
#include "nc-sysinfo.h"
#include "nc-lang.h"
#include "nc-helpscreen.h"
+#include "nc-subset.h"
extern const struct help_text main_menu_help_text;
+static bool cui_detached = false;
+
static struct pmenu *main_menu_init(struct cui *cui);
+static bool lockdown_active(void)
+{
+ bool lockdown = false;
+ if (access(LOCKDOWN_FILE, F_OK) != -1)
+ lockdown = true;
+ return lockdown;
+}
+
static void cui_start(void)
{
initscr(); /* Initialize ncurses. */
* Petitboot to exit if they're left undefined */
define_key("\x1b\x5b\x35\x7e", KEY_PPAGE);
define_key("\x1b\x5b\x36\x7e", KEY_NPAGE);
+ define_key("\x1b\x5b\x31\x7e", KEY_HOME);
+ define_key("\x1b\x5b\x34\x7e", KEY_END);
define_key("\x1b\x4f\x48", KEY_HOME);
define_key("\x1b\x4f\x46", KEY_END);
define_key("OH", KEY_HOME);
define_key("OF", KEY_END);
+ define_key("\x1b\x5b\x41", KEY_UP);
+ define_key("\x1b\x5b\x42", KEY_DOWN);
+ define_key("\x1b\x5b\x33\x7e", KEY_DC);
while (getch() != ERR) /* flush stdin */
(void)0;
static void cui_atexit(void)
{
+ if (cui_detached)
+ return;
+
clear();
refresh();
endwin();
+
+ bool lockdown = lockdown_active();
+
+ while (lockdown) {
+ sync();
+ reboot(RB_AUTOBOOT);
+ }
}
/**
{
struct pmenu *menu = cui->main;
struct cui_opt_data *cod;
+ int idx, top, rows, cols;
static int user_idx = 0;
/* Was the edit cancelled? */
/* Re-attach the items array. */
set_menu_items(menu->ncm, menu->items);
+
+ /* If our index is above the current top row, align
+ * us to the new top. Otherwise, align us to the new
+ * bottom */
+ menu_format(cui->main->ncm, &rows, &cols);
+ top = top_row(cui->main->ncm);
+ idx = item_index(item->nci);
+
+ if (top >= idx)
+ top = idx;
+ else
+ top = idx < rows ? 0 : idx - rows + 1;
+
+ set_top_row(cui->main->ncm, top);
+ set_current_item(item->pmenu->ncm, item->nci);
+
nc_scr_post(&menu->scr);
} else {
cod = item->data;
cod->bd = talloc_steal(cod, bd);
- set_current_item(item->pmenu->ncm, item->nci);
out:
cui_set_current(cui, &cui->main->scr);
talloc_free(cui->boot_editor);
cui_set_current(cui, help_screen_scr(cui->help_screen));
}
+static void cui_subset_exit(struct cui *cui)
+{
+ cui_set_current(cui, subset_screen_return_scr(cui->subset_screen));
+ talloc_free(cui->subset_screen);
+ cui->subset_screen = NULL;
+}
+
+void cui_show_subset(struct cui *cui, const char *title,
+ void *arg)
+{
+ if (!cui->current)
+ return;
+
+ if (cui->subset_screen)
+ return;
+
+ cui->subset_screen = subset_screen_init(cui, cui->current,
+ title, arg, cui_subset_exit);
+
+ if (cui->subset_screen)
+ cui_set_current(cui, subset_screen_scr(cui->subset_screen));
+}
+
/**
* cui_set_current - Set the currently active screen and redraw it.
*/
break;
if (!cui->has_input) {
- pb_log("UI input received (key = %d), aborting "
- "default boot\n", c);
- discover_client_cancel_default(cui->client);
cui->has_input = true;
+ if (cui->client) {
+ pb_log("UI input received (key = %d), aborting "
+ "default boot\n", c);
+ discover_client_cancel_default(cui->client);
+ } else {
+ pb_log("UI input received (key = %d), aborting "
+ "once server connects\n", c);
+ }
}
if (process_global_keys(cui, c))
cod->bd->initrd = talloc_strdup(cod->bd, opt->initrd_file);
cod->bd->dtb = talloc_strdup(cod->bd, opt->dtb_file);
cod->bd->args = talloc_strdup(cod->bd, opt->boot_args);
+ cod->bd->args_sig_file = talloc_strdup(cod->bd, opt->args_sig_file);
/* This disconnects items array from menu. */
result = set_menu_items(cui->main->ncm, NULL);
pb_log(" image '%s'\n", cod->bd->image);
pb_log(" initrd '%s'\n", cod->bd->initrd);
pb_log(" args '%s'\n", cod->bd->args);
+ pb_log(" argsig '%s'\n", cod->bd->args_sig_file);
/* Re-attach the items array. */
result = set_menu_items(cui->main->ncm, cui->main->items);
/* If our index is above the current top row, align
* us to the new top. Otherwise, align us to the new
* bottom */
- top = top < idx ? idx - rows : idx;
+ top = top < idx ? idx - rows + 1 : idx;
set_top_row(cui->main->ncm, top);
set_current_item(cui->main->ncm, selected);
struct cui *cui = cui_from_arg(arg);
struct boot_option *opt;
unsigned int i;
+ int rows, cols, top, last;
int result;
pb_log("%s: %p %s\n", __func__, dev, dev->id);
result = set_menu_items(cui->main->ncm, cui->main->items);
+ /* Move cursor to 'Exit' menu entry */
+ menu_format(cui->main->ncm, &rows, &cols);
+ last = cui->main->item_count - 1;
+ set_current_item(cui->main->ncm, cui->main->items[last]);
+ if (!item_visible(cui->main->items[last])) {
+ top = last < rows ? 0 : last - rows + 1;
+ set_top_row(cui->main->ncm, top);
+ }
+
if (result)
pb_log("%s: set_menu_items failed: %d\n", __func__, result);
if (cui->sysinfo_screen)
sysinfo_screen_update(cui->sysinfo_screen, sysinfo);
+ if (cui->subset_screen)
+ subset_screen_update(cui->subset_screen);
+
/* ... and do the same with the config screen... */
if (cui->config_screen)
config_screen_update(cui->config_screen, cui->config, sysinfo);
if (config->lang)
cui_update_language(cui, config->lang);
+ if (cui->subset_screen)
+ subset_screen_update(cui->subset_screen);
+
if (cui->config_screen)
config_screen_update(cui->config_screen, config, cui->sysinfo);
static int menu_reinit_execute(struct pmenu_item *item)
{
- cui_send_reinit(cui_from_item(item));
+ if (cui_from_item(item)->client)
+ cui_send_reinit(cui_from_item(item));
return 0;
}
static int menu_add_url_execute(struct pmenu_item *item)
{
- cui_show_add_url(cui_from_item(item));
+ if (cui_from_item(item)->client)
+ cui_show_add_url(cui_from_item(item));
return 0;
}
struct pmenu_item *i;
struct pmenu *m;
int result;
+ bool lockdown = lockdown_active();
m = pmenu_init(cui, 7, cui_on_exit);
if (!m) {
i->on_execute = menu_add_url_execute;
pmenu_item_insert(m, i, 5);
- i = pmenu_item_create(m, _("Exit to shell"));
+ if (lockdown)
+ i = pmenu_item_create(m, _("Reboot"));
+ else
+ i = pmenu_item_create(m, _("Exit to shell"));
i->on_execute = pmenu_exit_cb;
pmenu_item_insert(m, i, 6);
.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.
+ *
+ * A timeout callback that attempts to connect to the discover server; on
+ * failure it registers itself with a one second timeout to try again.
+ * On success the cui->client struct will be set.
+ *
+ * Since this updates the status line when called it must not be called
+ * before the UI is ready.
+ */
+static int cui_server_wait(void *arg)
+{
+ struct cui *cui = cui_from_arg(arg);
+
+ if (cui->client) {
+ pb_debug("We already have a server!\n");
+ return 0;
+ }
+
+ /* We haven't yet connected to the server */
+ pb_log("Trying to connect...\n");
+ cui->client = discover_client_init(cui->waitset,
+ &cui_client_ops, cui);
+
+ if (!cui->client) {
+ waiter_register_timeout(cui->waitset, 1000, cui_server_wait,
+ cui);
+ nc_scr_status_printf(cui->current, "Info: Waiting for server");
+ } else {
+ nc_scr_status_printf(cui->current, "Info: Connected to server!");
+ talloc_steal(cui, cui->client);
+
+ if (cui->has_input) {
+ pb_log("Aborting default boot on server connect\n");
+ discover_client_cancel_default(cui->client);
+ }
+ }
+
+ return 0;
+}
+
/**
* cui_init - Setup the cui instance.
* @platform_info: A value for the struct cui platform_info member.
*/
struct cui *cui_init(void* platform_info,
- int (*js_map)(const struct js_event *e), int start_deamon)
+ int (*js_map)(const struct js_event *e), int start_daemon, int timeout)
{
struct cui *cui;
unsigned int i;
/* Loop here for scripts that just started the server. */
retry_start:
- for (i = start_deamon ? 2 : 10; i; i--) {
+ for (i = start_daemon ? 2 : 15; i && timeout; i--) {
cui->client = discover_client_init(cui->waitset,
&cui_client_ops, cui);
if (cui->client || !i)
sleep(1);
}
- if (!cui->client && start_deamon) {
+ if (!cui->client && start_daemon) {
int result;
- start_deamon = 0;
+ start_daemon = 0;
result = pb_start_daemon(cui);
goto fail_client_init;
}
- if (!cui->client) {
+ if (!cui->client && !timeout) {
+ /* Have the first timeout fire immediately so we can check
+ * for the server as soon as the UI is ready */
+ waiter_register_timeout(cui->waitset, 0,
+ cui_server_wait, cui);
+ } else if (!cui->client) {
pb_log("%s: discover_client_init failed.\n", __func__);
fprintf(stderr, _("%s: error: discover_client_init failed.\n"),
__func__);
int cui_run(struct cui *cui)
{
+ pid_t pid;
+
assert(main);
cui->current = &cui->main->scr;
cui_atexit();
+ 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");
+ }
+
return cui->abort ? 0 : -1;
}