From 6600e73fe891d5006c34f694e97e37eefceb921d Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 21 Apr 2015 16:17:57 +1000 Subject: discover/platform-powerpc: Set IPMI OS boot sensor This is to indicate to a BMC that we have initiated OS boot. This patch manually parses the device tree for the sensor information. In the future this could be replaced by libfdt or similar. Discover the id of your OS Boot sensor: $ sudo ipmitool sensor get "OS Boot" Locating sensor record... Sensor ID : OS Boot (0x5a) Entity ID : 35.0 (Operating System) Sensor Type (Discrete): OS Boot (0x1f) Sensor Reading : 0h Event Message Control : Per-threshold Assertion Events : OS Boot [boot completed - device not specified] Assertions Enabled : OS Boot [A: boot completed] [C: boot completed] [PXE boot completed] [Diagnostic boot completed] [CD-ROM boot completed] [ROM boot completed] [boot completed - device not specified] [Installation started] [Installation completed] [Installation aborted] [Installation failed] OEM : 0 In this case it is 0x1f. Note that the sesnor is currently asserted iwth boot completed - device not specified. Test by clearing all assertions in the OS Boot sensor: $ sudo ipmitool raw 0x04 0x30 0x5a 0x30 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 $ sudo ipmitool sensor get "OS Boot" Locating sensor record... Sensor ID : OS Boot (0x5a) Entity ID : 35.0 (Operating System) Sensor Type (Discrete): OS Boot (0x1f) Sensor Reading : 0h Event Message Control : Per-threshold Assertions Enabled : OS Boot [A: boot completed] [C: boot completed] [PXE boot completed] [Diagnostic boot completed] [CD-ROM boot completed] [ROM boot completed] [boot completed - device not specified] [Installation started] [Installation completed] [Installation aborted] [Installation failed] OEM : 0 Then reboot your system. The assertion event should once more say "boot completed - device not specified". Signed-off-by: Joel Stanley --- discover/Makefile.am | 2 ++ discover/dt.c | 61 +++++++++++++++++++++++++++++++++++++++++++++ discover/dt.h | 8 ++++++ discover/ipmi.h | 8 +++++- discover/platform-powerpc.c | 45 ++++++++++++++++++++++++++++++++- 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 discover/dt.c create mode 100644 discover/dt.h diff --git a/discover/Makefile.am b/discover/Makefile.am index 1e4df0b..7808110 100644 --- a/discover/Makefile.am +++ b/discover/Makefile.am @@ -71,6 +71,8 @@ discover_platform_ro_SOURCES = \ discover/platform.h \ discover/ipmi.c \ discover/ipmi.h \ + discover/dt.c \ + discover/dt.h \ discover/platform-powerpc.c discover_platform_ro_LINK = \ diff --git a/discover/dt.c b/discover/dt.c new file mode 100644 index 0000000..a7383e1 --- /dev/null +++ b/discover/dt.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "dt.h" + +static int filter_sensors(const struct dirent *ent) +{ + /* Check for prefix "sensor@" */ + return strncmp(ent->d_name, "sensor@", strlen("sensor@")) == 0; +} + +int get_ipmi_sensor(void *t, enum ipmi_sensor_ids sensor_id) +{ + int rc, len, n; + struct dirent **namelist; + char *buf, *filename; + const char sensor_dir[] = "/proc/device-tree/bmc/sensors/"; + + n = scandir(sensor_dir, &namelist, filter_sensors, alphasort); + if (n <= 0) + return -1; + + while (n--) { + filename = talloc_asprintf(t, "%s%s/ipmi-sensor-type", + sensor_dir, namelist[n]->d_name); + rc = read_file(t, filename, &buf, &len); + if (rc == 0 && len == 4 && + __be32_to_cpu(*(uint32_t *)buf) == sensor_id) + break; + free(namelist[n]); + } + if (n < 0) { + rc = -1; + goto out; + } + + filename = talloc_asprintf(t, "%s%s/reg", sensor_dir, + namelist[n]->d_name); + /* Free the rest of the scandir strings, if there are any */ + do { + free(namelist[n]); + } while (n-- > 0); + + rc = read_file(t, filename, &buf, &len); + if (rc != 0 || len != 4) { + rc = -1; + goto out; + } + + rc = __be32_to_cpu(*(uint32_t *)buf); + +out: + free(namelist); + return rc; +} diff --git a/discover/dt.h b/discover/dt.h new file mode 100644 index 0000000..4692d04 --- /dev/null +++ b/discover/dt.h @@ -0,0 +1,8 @@ +#ifndef _DT_H +#define _DT_H + +#include "ipmi.h" + +int get_ipmi_sensor(void *t, enum ipmi_sensor_ids sensor_id); + +#endif /* _IPMI_H */ diff --git a/discover/ipmi.h b/discover/ipmi.h index e60ff61..83f2910 100644 --- a/discover/ipmi.h +++ b/discover/ipmi.h @@ -5,12 +5,14 @@ #include enum ipmi_netfn { - IPMI_NETFN_CHASSIS = 0x0, + IPMI_NETFN_CHASSIS = 0x0, + IPMI_NETFN_SE = 0x04, }; enum ipmi_cmd { IPMI_CMD_CHASSIS_SET_SYSTEM_BOOT_OPTIONS = 0x08, IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS = 0x09, + IPMI_CMD_SENSOR_SET = 0x30, }; enum ipmi_bootdev { @@ -22,6 +24,10 @@ enum ipmi_bootdev { IPMI_BOOTDEV_SETUP = 0x6, }; +enum ipmi_sensor_ids { + IPMI_SENSOR_ID_OS_BOOT = 0x1F, +}; + struct ipmi; bool ipmi_present(void); diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index a293ce9..4cc91fa 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -17,6 +17,7 @@ #include "platform.h" #include "ipmi.h" +#include "dt.h" static const char *partition = "common"; static const char *sysparams_dir = "/sys/firmware/opal/sysparams/"; @@ -39,6 +40,8 @@ struct platform_powerpc { uint8_t *bootdev, bool *persistent); int (*clear_ipmi_bootdev)( struct platform_powerpc *platform); + int (*set_os_boot_sensor)( + struct platform_powerpc *platform); }; static const char *known_params[] = { @@ -792,6 +795,42 @@ static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform, return 0; } +static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform) +{ + int sensor_number; + uint16_t resp_len; + uint8_t resp[1]; + uint8_t req[] = { + 0x00, /* sensor number: os boot */ + 0x10, /* operation: set assertion bits */ + 0x00, /* sensor reading: none */ + 0x40, /* assertion mask lsb: set state 6 */ + 0x00, /* assertion mask msb: none */ + 0x00, /* deassertion mask lsb: none */ + 0x00, /* deassertion mask msb: none */ + 0x00, /* event data 1: none */ + 0x00, /* event data 2: none */ + 0x00, /* event data 3: none */ + }; + + sensor_number = get_ipmi_sensor(platform, IPMI_SENSOR_ID_OS_BOOT); + if (sensor_number < 0) { + pb_log("Couldn't find OS boot sensor in device tree\n"); + return -1; + } + + req[0] = sensor_number; + + resp_len = sizeof(resp); + + ipmi_transaction(platform->ipmi, IPMI_NETFN_SE, + IPMI_CMD_SENSOR_SET, + req, sizeof(req), + resp, &resp_len, + ipmi_timeout); return 0; + + return 0; +} static int load_config(struct platform *p, struct config *config) { @@ -838,6 +877,9 @@ static void pre_boot(struct platform *p, const struct config *config) if (!config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev) platform->clear_ipmi_bootdev(platform); + + if (platform->set_os_boot_sensor) + platform->set_os_boot_sensor(platform); } static int get_sysinfo(struct platform *p, struct system_info *sysinfo) @@ -875,7 +917,7 @@ static bool probe(struct platform *p, void *ctx) if (!S_ISDIR(statbuf.st_mode)) return false; - platform = talloc(ctx, struct platform_powerpc); + platform = talloc_zero(ctx, struct platform_powerpc); list_init(&platform->params); p->platform_data = platform; @@ -885,6 +927,7 @@ static bool probe(struct platform *p, void *ctx) platform->ipmi = ipmi_open(platform); platform->get_ipmi_bootdev = get_ipmi_bootdev_ipmi; platform->clear_ipmi_bootdev = clear_ipmi_bootdev_ipmi; + platform->set_os_boot_sensor = set_ipmi_os_boot_sensor; } else if (!stat(sysparams_dir, &statbuf)) { pb_debug("platform: using sysparams for IPMI paramters\n"); -- cgit v1.2.1 From 2598810aea74bf2f614d9e7b7cbb7c086a1fdc95 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 16 Apr 2015 11:53:20 +1000 Subject: ui/ncurses: Define Home, End, and Page Up/Down keys --- ui/ncurses/nc-cui.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 3f1e0a2..8e31fe2 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -69,6 +69,16 @@ static void cui_start(void) */ define_key("\x1b[Z", KEY_BTAB); + /* We'll define a few other keys too since they're commonly + * used for navigation but the escape character will cause + * 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\x4f\x48", KEY_HOME); + define_key("\x1b\x4f\x46", KEY_END); + define_key("OH", KEY_HOME); + define_key("OF", KEY_END); + while (getch() != ERR) /* flush stdin */ (void)0; } -- cgit v1.2.1 From 11996807cd4e79e35742aa4c4e19c6500141dfcc Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Thu, 30 Apr 2015 16:02:03 +1000 Subject: discover: Set OS Boot sensor with 0xa9 If we don't set all the things, we only get an assertion event, without the state being asserted. Despite the IPMI spec suggesting we only needed to set 0x10, aka the assertion bits, testing reveals we want to set all of the things to cause the BMC to assert the state. Tested-by: Nick Bofferding Signed-off-by: Joel Stanley Signed-off-by: Samuel Mendoza-Jonas --- discover/platform-powerpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index 4cc91fa..bda9368 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -802,7 +802,7 @@ static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform) uint8_t resp[1]; uint8_t req[] = { 0x00, /* sensor number: os boot */ - 0x10, /* operation: set assertion bits */ + 0xA9, /* operation: set everything */ 0x00, /* sensor reading: none */ 0x40, /* assertion mask lsb: set state 6 */ 0x00, /* assertion mask msb: none */ -- cgit v1.2.1 From 366ff957d2900eae6d26ad2f002b735302e7eb41 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Mon, 15 Dec 2014 14:57:35 +1100 Subject: lib: Define autoboot_options, device_type helpers Add the new autoboot_option struct, and helper functions for working with device_type enums. device_type_name() returns exact strings as used by platform code to read/write nvram params, so device_type_display_name() is added for use in user-visible strings. Signed-off-by: Samuel Mendoza-Jonas --- discover/platform.c | 17 ----------------- lib/Makefile.am | 1 + lib/types/types.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/types/types.h | 15 +++++++++++++++ 4 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 lib/types/types.c diff --git a/discover/platform.c b/discover/platform.c index 04798ac..4451589 100644 --- a/discover/platform.c +++ b/discover/platform.c @@ -17,23 +17,6 @@ static struct config *config; static const char *kernel_cmdline_debug = "petitboot.debug"; -static const char *device_type_name(enum device_type type) -{ - switch (type) { - case DEVICE_TYPE_DISK: - return "disk"; - case DEVICE_TYPE_OPTICAL: - return "optical"; - case DEVICE_TYPE_NETWORK: - return "network"; - case DEVICE_TYPE_ANY: - return "any"; - case DEVICE_TYPE_UNKNOWN: - default: - return "unknown"; - } -} - static void dump_config(struct config *config) { unsigned int i; diff --git a/lib/Makefile.am b/lib/Makefile.am index fbf2ee2..b39cc9b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -38,6 +38,7 @@ lib_libpbcore_la_SOURCES = \ lib/pb-config/pb-config.h \ lib/process/process.c \ lib/process/process.h \ + lib/types/types.c \ lib/types/types.h \ lib/talloc/talloc.c \ lib/talloc/talloc.h \ diff --git a/lib/types/types.c b/lib/types/types.c new file mode 100644 index 0000000..059e52a --- /dev/null +++ b/lib/types/types.c @@ -0,0 +1,51 @@ +#include +#include +#include + +const char *device_type_display_name(enum device_type type) +{ + switch (type) { + case DEVICE_TYPE_DISK: + return _("Disk"); + case DEVICE_TYPE_OPTICAL: + return _("Optical"); + case DEVICE_TYPE_NETWORK: + return _("Network"); + case DEVICE_TYPE_ANY: + return _("Any"); + case DEVICE_TYPE_UNKNOWN: + default: + return _("Unknown"); + } +} + +const char *device_type_name(enum device_type type) +{ + switch (type) { + case DEVICE_TYPE_DISK: + return "disk"; + case DEVICE_TYPE_OPTICAL: + return "optical"; + case DEVICE_TYPE_NETWORK: + return "network"; + case DEVICE_TYPE_ANY: + return "any"; + case DEVICE_TYPE_UNKNOWN: + default: + return "unknown"; + } +} + +enum device_type find_device_type(const char *str) +{ + if (!strncmp(str, "disk", strlen("disk"))) + return DEVICE_TYPE_DISK; + if (!strncmp(str, "optical", strlen("optical"))) + return DEVICE_TYPE_OPTICAL; + if (!strncmp(str, "network", strlen("network"))) + return DEVICE_TYPE_NETWORK; + if (!strncmp(str, "any", strlen("any"))) + return DEVICE_TYPE_ANY; + + return DEVICE_TYPE_UNKNOWN; +} diff --git a/lib/types/types.h b/lib/types/types.h index f543b7f..e22dbc3 100644 --- a/lib/types/types.h +++ b/lib/types/types.h @@ -13,6 +13,10 @@ enum device_type { DEVICE_TYPE_UNKNOWN, }; +const char *device_type_display_name(enum device_type type); +const char *device_type_name(enum device_type type); +enum device_type find_device_type(const char *str); + struct device { char *id; enum device_type type; @@ -118,6 +122,17 @@ struct boot_priority { enum device_type type; }; +struct autoboot_option { + enum { + BOOT_DEVICE_TYPE, + BOOT_DEVICE_UUID + } boot_type; + union { + enum device_type type; + char *uuid; + }; +}; + struct config { bool autoboot_enabled; unsigned int autoboot_timeout_sec; -- cgit v1.2.1 From 633b04ede5aad3933c6d90895e806638cc4d004c Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 15:30:20 +1100 Subject: discover: Add support for multiple bootdev arguments To support multiple autoboot options while retaining backwards compatability, interpret the petitboot,bootdev parameter as optionally having several space-separated values. Signed-off-by: Samuel Mendoza-Jonas --- discover/platform-powerpc.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index bda9368..895f0ec 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -388,6 +388,47 @@ static void populate_network_config(struct platform_powerpc *platform, talloc_free(val); } +static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt) +{ + char *delim = strchr(*pos, ' '); + int len, prefix = 0, rc = -1; + enum device_type type; + + if (!strncmp(*pos, "uuid:", strlen("uuid:"))) { + prefix = strlen("uuid:"); + opt->boot_type = BOOT_DEVICE_UUID; + rc = 0; + } else if (!strncmp(*pos, "mac:", strlen("mac:"))) { + prefix = strlen("mac:"); + opt->boot_type = BOOT_DEVICE_UUID; + rc = 0; + } else { + type = find_device_type(*pos); + if (type != DEVICE_TYPE_UNKNOWN) { + opt->type = type; + opt->boot_type = BOOT_DEVICE_TYPE; + rc = 0; + } + } + + if (opt->boot_type == BOOT_DEVICE_UUID) { + if (delim) + len = (int)(delim - *pos) - prefix; + else + len = strlen(*pos); + + opt->uuid = talloc_strndup(ctx, *pos + prefix, len); + } + + /* Always advance pointer to next option or end */ + if (delim) + *pos = delim + 1; + else + *pos += strlen(*pos); + + return rc; +} + static void populate_bootdev_config(struct platform_powerpc *platform, struct config *config) -- cgit v1.2.1 From 60252961c21a0f102e69373f2bea36f8af495c29 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 14:45:47 +1100 Subject: ui/ncurses: Add nc_widget_subset The new nc_widget_subset is similar to nc_widget_select, but hides added options until they are made active. This allows the widget to have multiple options selected and ordered without cluttering the screen. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-widgets.c | 365 +++++++++++++++++++++++++++++++++++++++++++++++- ui/ncurses/nc-widgets.h | 19 +++ 2 files changed, 383 insertions(+), 1 deletion(-) diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c index f821929..5c67d82 100644 --- a/ui/ncurses/nc-widgets.c +++ b/ui/ncurses/nc-widgets.c @@ -61,6 +61,7 @@ #define to_textbox(w) container_of(w, struct nc_widget_textbox, widget) #define to_button(w) container_of(w, struct nc_widget_button, widget) #define to_select(w) container_of(w, struct nc_widget_select, widget) +#define to_subset(w) container_of(w, struct nc_widget_subset, widget) static const char *checkbox_checked_str = "[*]"; static const char *checkbox_unchecked_str = "[ ]"; @@ -112,6 +113,24 @@ struct nc_widget_textbox { struct nc_widget widget; }; +struct nc_widget_subset { + struct nc_widget widget; + int *active; + int n_active; + struct subset_option { + char *str; + int val; + FIELD *field; + } *options; + int n_options; + int top, left, size; + struct nc_widgetset *set; + void (*on_change)(void *, int); + void *on_change_arg; + void (*screen_cb)(void *, + struct nc_widget_subset *, int); +}; + struct nc_widget_select { struct nc_widget widget; struct select_option { @@ -140,6 +159,21 @@ static bool key_is_select(int key) return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER; } +static bool key_is_minus(int key) +{ + return key == 055; +} + +static bool key_is_left(int key) +{ + return key == KEY_LEFT; +} + +static bool key_is_right(int key) +{ + return key == KEY_RIGHT; +} + static bool process_key_nop(struct nc_widget *widget __attribute__((unused)), FORM *form __attribute((unused)), int key __attribute__((unused))) @@ -155,6 +189,11 @@ static void field_set_visible(FIELD *field, bool visible) set_field_opts(field, opts); } +static bool field_visible(FIELD *field) +{ + return (field_opts(field) & O_VISIBLE) == O_VISIBLE; +} + static void field_move(FIELD *field, int y, int x) { move_field(field, y, x); @@ -414,6 +453,329 @@ void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox) set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type); } +static void subset_update_order(struct nc_widget_subset *subset) +{ + char *str; + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + str = talloc_asprintf(subset, "(%d) %s", + i, subset->options[val].str); + set_field_buffer(subset->options[val].field, 0, + str); + talloc_free(str); + } +} + +static void widget_focus_change(struct nc_widget *widget, FIELD *field, + bool focussed); + +static void subset_delete_active(struct nc_widget_subset *subset, int idx) +{ + bool last = idx == (subset->n_active - 1); + struct nc_widgetset *set = subset->set; + struct nc_widget *widget; + size_t rem; + int i, val; + + /* Shift field focus to nearest active option or next visible field */ + if (subset->n_active > 1) { + if (last) + val = subset->active[idx - 1]; + else + val = subset->active[idx + 1]; + set->cur_field = subset->options[val].field; + } else { + for (i = 0; i < set->n_fields; i++) + if (field_visible(set->fields[i])) { + set->cur_field = set->fields[i]; + break; + } + } + + set_current_field(set->form, set->cur_field); + widget = field_userptr(set->cur_field); + widget_focus_change(widget, set->cur_field, true); + if (set->widget_focus) + set->widget_focus(widget, set->widget_focus_arg); + + /* Update active array */ + rem = sizeof(int) * (subset->n_active - idx - 1); + val = subset->active[idx]; + field_set_visible(subset->options[val].field, false); + if (rem) + memmove(&subset->active[idx], &subset->active[idx + 1], rem); + subset->n_active--; + subset->active = talloc_realloc(subset, subset->active, + int, subset->n_active); + + subset->widget.height = subset->n_active; +} + +static bool subset_process_key(struct nc_widget *w, FORM *form, int key) +{ + struct nc_widget_subset *subset = to_subset(w); + int i, val, opt_idx = -1; + FIELD *field; + + if (!key_is_minus(key) && !key_is_left(key) && !key_is_right(key)) + return false; + + field = current_field(form); + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + if (subset->options[val].field == field) { + opt_idx = i; + break; + } + } + + if (opt_idx < 0) + return false; + + if (key_is_minus(key)) + subset_delete_active(subset, opt_idx); + + if (key_is_left(key)){ + if (opt_idx == 0) + return true; + + val = subset->active[opt_idx]; + subset->active[opt_idx] = subset->active[opt_idx - 1]; + subset->active[opt_idx - 1] = val; + } + + if (key_is_right(key)){ + if (opt_idx >= subset->n_active - 1) + return true; + + val = subset->active[opt_idx]; + subset->active[opt_idx] = subset->active[opt_idx + 1]; + subset->active[opt_idx + 1] = val; + } + + subset_update_order(subset); + + if (subset->on_change) + subset->on_change(subset->on_change_arg, 0); + + return true; +} + +static void subset_set_visible(struct nc_widget *widget, bool visible) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + field_set_visible(subset->options[val].field, visible); + } +} + +static void subset_move(struct nc_widget *widget, int y, int x) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + field_move(subset->options[val].field, y + i , x); + } +} + +static void subset_field_focus(struct nc_widget *widget, FIELD *field) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + if (field == subset->options[val].field) { + widget->focus_y = i; + return; + } + } +} + +static int subset_destructor(void *ptr) +{ + struct nc_widget_subset *subset = ptr; + int i; + + for (i = 0; i < subset->n_options; i++) + free_field(subset->options[i].field); + + return 0; +} + +struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set, + int y, int x, int len, void *screen_cb) +{ + struct nc_widget_subset *subset; + + subset = talloc_zero(set, struct nc_widget_subset); + subset->widget.width = len; + subset->widget.height = 0; + subset->widget.x = x; + subset->widget.y = y; + subset->widget.process_key = subset_process_key; + subset->widget.set_visible = subset_set_visible; + subset->widget.move = subset_move; + subset->widget.field_focus = subset_field_focus; + subset->widget.focussed_attr = A_REVERSE; + subset->widget.unfocussed_attr = A_NORMAL; + subset->top = y; + subset->left = x; + subset->size = len; + subset->set = set; + subset->n_active = subset->n_options = 0; + subset->active = NULL; + subset->options = NULL; + subset->screen_cb = screen_cb; + + talloc_set_destructor(subset, subset_destructor); + + return subset; +} + +void widget_subset_add_option(struct nc_widget_subset *subset, const char *text) +{ + FIELD *f; + int i; + + i = subset->n_options++; + subset->options = talloc_realloc(subset, subset->options, + struct subset_option, i + 1); + + subset->options[i].str = talloc_strdup(subset->options, text); + + subset->options[i].field = f = new_field(1, subset->size, subset->top + i, + subset->left, 0, 0); + + field_opts_off(f, O_WRAP | O_EDIT); + set_field_userptr(f, &subset->widget); + set_field_buffer(f, 0, subset->options[i].str); + field_set_visible(f, false); + widgetset_add_field(subset->set, f); +} + +void widget_subset_make_active(struct nc_widget_subset *subset, int idx) +{ + int i; + + for (i = 0; i < subset->n_active; i++) + if (subset->active[i] == idx) { + pb_debug("%s: Index %d already active\n", __func__, idx); + return; + } + + i = subset->n_active++; + subset->widget.height = subset->n_active; + subset->active = talloc_realloc(subset, subset->active, + int, i + 1); + subset->active[i] = idx; + + subset_update_order(subset); +} + +int widget_subset_get_order(void *ctx, unsigned int **order, + struct nc_widget_subset *subset) +{ + unsigned int *buf = talloc_array(ctx, unsigned int, subset->n_active); + int i; + + for (i = 0; i < subset->n_active; i++) + buf[i] = subset->active[i]; + + *order = buf; + return i; +} + +void widget_subset_show_inactive(struct nc_widget_subset *subset, + struct nc_widget_select *select) +{ + bool active = false, first = true; + int i, j; + + for (i = 0; i < subset->n_options; i++) { + active = false; + for (j = 0; j < subset->n_active; j++) + if (subset->active[j] == i) + active = true; + + if (active) + continue; + + widget_select_add_option(select, i, + subset->options[i].str, first); + if (first) + first = false; + } +} + +int widget_subset_n_inactive(struct nc_widget_subset *subset) +{ + return subset->n_options - subset->n_active; +} + +int widget_subset_height(struct nc_widget_subset *subset) +{ + return subset->n_active; +} + +void widget_subset_on_change(struct nc_widget_subset *subset, + void (*on_change)(void *, int), void *arg) +{ + subset->on_change = on_change; + subset->on_change_arg = arg; +} + +void widget_subset_drop_options(struct nc_widget_subset *subset) +{ + struct nc_widgetset *set = subset->set; + int i; + + for (i = 0; i < subset->n_options; i++) { + FIELD *field = subset->options[i].field; + widgetset_remove_field(set, field); + if (field == set->cur_field) + set->cur_field = NULL; + free_field(subset->options[i].field); + } + + talloc_free(subset->options); + talloc_free(subset->active); + subset->options = NULL; + subset->active = NULL; + subset->n_options = 0; + subset->n_active = 0; + subset->widget.height = 0; + subset->widget.focus_y = 0; +} + +void widget_subset_clear_active(struct nc_widget_subset *subset) +{ + int i; + + for (i = 0; i < subset->n_options; i++) + field_set_visible(subset->options[i].field, false); + + talloc_free(subset->active); + subset->active = NULL; + subset->n_active = 0; + subset->widget.height = 0; + subset->widget.focus_y = 0; +} + +void widget_subset_callback(void *arg, + struct nc_widget_subset *subset, int idx) +{ + subset->screen_cb(arg, subset, idx); +} + static void select_option_change(struct select_option *opt, bool selected) { const char *str; @@ -679,7 +1041,7 @@ struct nc_widget_button *widget_new_button(struct nc_widgetset *set, return button; } -static void widget_focus_change(struct nc_widget *widget, FIELD *field, +void widget_focus_change(struct nc_widget *widget, FIELD *field, bool focussed) { int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr; @@ -856,6 +1218,7 @@ static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field) DECLARE_BASEFN(textbox); DECLARE_BASEFN(checkbox); +DECLARE_BASEFN(subset); DECLARE_BASEFN(select); DECLARE_BASEFN(label); DECLARE_BASEFN(button); diff --git a/ui/ncurses/nc-widgets.h b/ui/ncurses/nc-widgets.h index 09c3b1f..4b67da7 100644 --- a/ui/ncurses/nc-widgets.h +++ b/ui/ncurses/nc-widgets.h @@ -29,6 +29,8 @@ struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set, int y, int x, bool checked); struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set, int y, int x, int len, char *str); +struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set, + int y, int x, int len, void *screen_cb); struct nc_widget_select *widget_new_select(struct nc_widgetset *set, int y, int x, int len); struct nc_widget_button *widget_new_button(struct nc_widgetset *set, @@ -41,6 +43,12 @@ void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox, void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox); void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox); +void widget_subset_add_option(struct nc_widget_subset *subset, const char *text); +void widget_subset_make_active(struct nc_widget_subset *subset, int idx); + +void widget_subset_on_change(struct nc_widget_subset *subset, + void (*on_change)(void *arg, int value), void *arg); + void widget_select_add_option(struct nc_widget_select *select, int value, const char *text, bool selected); @@ -49,6 +57,16 @@ void widget_select_on_change(struct nc_widget_select *select, char *widget_textbox_get_value(struct nc_widget_textbox *textbox); bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox); +int widget_subset_get_order(void *ctx, unsigned int **order, + struct nc_widget_subset *subset); +void widget_subset_show_inactive(struct nc_widget_subset *subset, + struct nc_widget_select *select); +int widget_subset_n_inactive(struct nc_widget_subset *subset); +int widget_subset_height(struct nc_widget_subset *subset); +void widget_subset_drop_options(struct nc_widget_subset *subset); +void widget_subset_clear_active(struct nc_widget_subset *subset); +void widget_subset_callback(void *arg, + struct nc_widget_subset *subset, int idx); int widget_select_get_value(struct nc_widget_select *select); int widget_select_height(struct nc_widget_select *select); void widget_select_drop_options(struct nc_widget_select *select); @@ -56,6 +74,7 @@ void widget_select_drop_options(struct nc_widget_select *select); /* generic widget API */ struct nc_widget *widget_textbox_base(struct nc_widget_textbox *textbox); struct nc_widget *widget_checkbox_base(struct nc_widget_checkbox *checkbox); +struct nc_widget *widget_subset_base(struct nc_widget_subset *subset); struct nc_widget *widget_select_base(struct nc_widget_select *select); struct nc_widget *widget_label_base(struct nc_widget_label *label); struct nc_widget *widget_button_base(struct nc_widget_button *button); -- cgit v1.2.1 From 6bb1301835dbd6ff54e1becb5b684fedb0e4caaf Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 14:48:38 +1100 Subject: ui/ncurses: Add nc-subset selection screen The nc-subset screen is intended to be used as a sub-screen from the current screen (eg. nc-config) which passes a pointer to a nc_widget_subset struct. The nc-subset screen allows the user to select an option from a list of 'inactive' options, before returning control back to the current screen. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/Makefile.am | 4 +- ui/ncurses/nc-cui.c | 24 ++++ ui/ncurses/nc-cui.h | 3 + ui/ncurses/nc-scr.h | 1 + ui/ncurses/nc-subset.c | 289 +++++++++++++++++++++++++++++++++++++++++++++++++ ui/ncurses/nc-subset.h | 36 ++++++ 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 ui/ncurses/nc-subset.c create mode 100644 ui/ncurses/nc-subset.h diff --git a/ui/ncurses/Makefile.am b/ui/ncurses/Makefile.am index 9a4e1e4..265ae69 100644 --- a/ui/ncurses/Makefile.am +++ b/ui/ncurses/Makefile.am @@ -47,7 +47,9 @@ ui_ncurses_libpbnc_la_SOURCES = \ ui/ncurses/nc-widgets.h \ ui/ncurses/nc-add-url.c \ ui/ncurses/nc-add-url.h \ - ui/ncurses/nc-add-url-help.c + ui/ncurses/nc-add-url-help.c \ + ui/ncurses/nc-subset.c \ + ui/ncurses/nc-subset.h sbin_PROGRAMS += ui/ncurses/petitboot-nc diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 8e31fe2..2fc779e 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -41,6 +41,7 @@ #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; @@ -328,6 +329,29 @@ void cui_show_help(struct cui *cui, const char *title, 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. */ diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h index 694ebd1..24a0761 100644 --- a/ui/ncurses/nc-cui.h +++ b/ui/ncurses/nc-cui.h @@ -63,6 +63,7 @@ struct cui { struct boot_editor *boot_editor; struct lang_screen *lang_screen; struct help_screen *help_screen; + struct subset_screen *subset_screen; struct pjs *pjs; void *platform_info; unsigned int default_item; @@ -81,6 +82,8 @@ void cui_show_config(struct cui *cui); void cui_show_lang(struct cui *cui); void cui_show_help(struct cui *cui, const char *title, const struct help_text *text); +void cui_show_subset(struct cui *cui, const char *title, + void *arg); void cui_show_add_url(struct cui *cui); int cui_send_config(struct cui *cui, struct config *config); int cui_send_url(struct cui *cui, char *url); diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h index ed87517..be99b48 100644 --- a/ui/ncurses/nc-scr.h +++ b/ui/ncurses/nc-scr.h @@ -48,6 +48,7 @@ enum pb_nc_sig { pb_config_screen_sig = 666, pb_lang_screen_sig = 777, pb_add_url_screen_sig = 888, + pb_subset_screen_sig = 101, pb_removed_sig = -999, }; diff --git a/ui/ncurses/nc-subset.c b/ui/ncurses/nc-subset.c new file mode 100644 index 0000000..d90ed20 --- /dev/null +++ b/ui/ncurses/nc-subset.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include "nc-cui.h" +#include "nc-subset.h" + +#define N_FIELDS 3 + +struct subset_screen { + struct nc_scr scr; + struct cui *cui; + struct nc_scr *return_scr; + struct nc_widgetset *widgetset; + WINDOW *pad; + struct nc_widget_subset *options; + + bool exit; + void (*on_exit)(struct cui *); + + int scroll_y; + + int label_x; + int field_x; + + struct { + struct nc_widget_select *options_f; + + struct nc_widget_button *ok_b; + struct nc_widget_button *cancel_b; + } widgets; +}; + +struct nc_scr *subset_screen_return_scr(struct subset_screen *screen) +{ + return screen->return_scr; +} + +static struct subset_screen *subset_screen_from_scr(struct nc_scr *scr) +{ + struct subset_screen *subset_screen; + + assert(scr->sig == pb_subset_screen_sig); + subset_screen = (struct subset_screen *) + ((char *)scr - (size_t)&((struct subset_screen *)0)->scr); + assert(subset_screen->scr.sig == pb_subset_screen_sig); + return subset_screen; +} + +static void pad_refresh(struct subset_screen *screen) +{ + int y, x, rows, cols; + + getmaxyx(screen->scr.sub_ncw, rows, cols); + getbegyx(screen->scr.sub_ncw, y, x); + + prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols); +} + +static void subset_screen_process_key(struct nc_scr *scr, int key) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + bool handled; + + handled = widgetset_process_key(screen->widgetset, key); + + if (!handled) { + switch (key) { + case 'x': + case 27: /* esc */ + screen->exit = true; + break; + } + } + + if (screen->exit) + screen->on_exit(screen->cui); + else if (handled) + pad_refresh(screen); +} + +static int subset_screen_post(struct nc_scr *scr) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + widgetset_post(screen->widgetset); + nc_scr_frame_draw(scr); + redrawwin(scr->main_ncw); + wrefresh(scr->main_ncw); + pad_refresh(screen); + return 0; +} + +static int subset_screen_unpost(struct nc_scr *scr) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + widgetset_unpost(screen->widgetset); + return 0; +} + +struct nc_scr *subset_screen_scr(struct subset_screen *screen) +{ + return &screen->scr; +} + +static void ok_click(void *arg) +{ + struct subset_screen *screen = arg; + int idx = widget_select_get_value(screen->widgets.options_f); + widget_subset_callback(screen->return_scr, screen->options, idx); + screen->exit = true; +} + +static void cancel_click(void *arg) +{ + struct subset_screen *screen = arg; + screen->exit = true; +} + +static void subset_screen_layout_widgets(struct subset_screen *screen) +{ + int y = 2; + + /* select */ + widget_move(widget_select_base(screen->widgets.options_f), + y, screen->label_x); + y+= widget_height(widget_select_base(screen->widgets.options_f)); + + /* ok, cancel */ + y += 1; + + widget_move(widget_button_base(screen->widgets.ok_b), + y, screen->field_x + 12); + widget_move(widget_button_base(screen->widgets.cancel_b), + y, screen->field_x + 24); +} + +static void subset_screen_option_select(void *arg, int value) +{ + struct subset_screen *screen = arg; + widgetset_unpost(screen->widgetset); + subset_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); + (void)value; +} + +static void subset_screen_setup_widgets(struct subset_screen *screen) +{ + struct nc_widgetset *set = screen->widgetset; + struct nc_widget_subset *subset = screen->options; + + build_assert(sizeof(screen->widgets) / sizeof(struct widget *) + == N_FIELDS); + + screen->widgets.options_f = widget_new_select(set, 0, 0, + COLS - (2 * screen->label_x)); + + widget_select_on_change(screen->widgets.options_f, + subset_screen_option_select, screen); + + widget_subset_show_inactive(subset, screen->widgets.options_f); + + screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"), + ok_click, screen); + screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"), + cancel_click, screen); +} + +static void subset_screen_widget_focus(struct nc_widget *widget, void *arg) +{ + struct subset_screen *screen = arg; + int w_y, s_max; + + w_y = widget_y(widget) + widget_focus_y(widget); + s_max = getmaxy(screen->scr.sub_ncw) - 1; + + if (w_y < screen->scroll_y) + screen->scroll_y = w_y; + + else if (w_y + screen->scroll_y + 1 > s_max) + screen->scroll_y = 1 + w_y - s_max; + + else + return; + + pad_refresh(screen); +} + +static void subset_screen_draw(struct subset_screen *screen) +{ + bool repost = false; + int height; + + /* Size of pad = top space + number of available options */ + height = 1 + N_FIELDS + widget_subset_n_inactive(screen->options); + + if (!screen->pad || getmaxy(screen->pad) < height) { + if (screen->pad) + delwin(screen->pad); + screen->pad = newpad(height, COLS); + } + + if (screen->widgetset) { + widgetset_unpost(screen->widgetset); + talloc_free(screen->widgetset); + repost = true; + } + + screen->widgetset = widgetset_create(screen, screen->scr.main_ncw, + screen->pad); + widgetset_set_widget_focus(screen->widgetset, + subset_screen_widget_focus, screen); + + subset_screen_setup_widgets(screen); + subset_screen_layout_widgets(screen); + + if (repost) + widgetset_post(screen->widgetset); +} + +static int subset_screen_destroy(void *arg) +{ + struct subset_screen *screen = arg; + if (screen->pad) + delwin(screen->pad); + return 0; +} + +struct subset_screen *subset_screen_init(struct cui *cui, + struct nc_scr *current_scr, + const char *title_suffix, + void *subset, + void (*on_exit)(struct cui *)) +{ + struct subset_screen *screen; + + screen = talloc_zero(cui, struct subset_screen); + talloc_set_destructor(screen, subset_screen_destroy); + + screen->cui = cui; + screen->on_exit = on_exit; + screen->options = (struct nc_widget_subset *) subset; + screen->label_x = 2; + screen->field_x = 22; + + screen->return_scr = current_scr; + + nc_scr_init(&screen->scr, pb_subset_screen_sig, 0, + cui, subset_screen_process_key, + subset_screen_post, subset_screen_unpost, + NULL); + + screen->scr.frame.ltitle = talloc_strdup(screen, + title_suffix); + screen->scr.frame.rtitle = NULL; + screen->scr.frame.help = talloc_strdup(screen, + _("tab=next, shift+tab=previous, x=exit")); + + scrollok(screen->scr.sub_ncw, true); + + subset_screen_draw(screen); + + return screen; +} diff --git a/ui/ncurses/nc-subset.h b/ui/ncurses/nc-subset.h new file mode 100644 index 0000000..e967866 --- /dev/null +++ b/ui/ncurses/nc-subset.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NC_SUBSET_H +#define _NC_SUBSET_H + +#include "nc-cui.h" +#include "nc-widgets.h" + +struct subset_screen; + +struct subset_screen *subset_screen_init(struct cui *cui, + struct nc_scr *current_scr, + const char *title_suffix, + void *subset, + void (*on_exit)(struct cui *)); + +struct nc_scr *subset_screen_scr(struct subset_screen *screen); +struct nc_scr *subset_screen_return_scr(struct subset_screen *screen); +void subset_screen_update(struct subset_screen *screen); + +#endif /* defined _NC_SUBSET_H */ -- cgit v1.2.1 From ec12bbd7bbfb3dd31dbd987ecff463542d54542f Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 15:25:15 +1100 Subject: ui/ncurses: Use sorted field navigation Changing the visual order of widgets will cause their associated fields to be out of order relative to the widgetset field array. Rather than manually resorting the array, use sorted navigation to move according to a field's visual positon Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-widgets.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c index 5c67d82..3daced1 100644 --- a/ui/ncurses/nc-widgets.c +++ b/ui/ncurses/nc-widgets.c @@ -1066,19 +1066,19 @@ bool widgetset_process_key(struct nc_widgetset *set, int key) tab = true; /* fall through */ case KEY_UP: - req = REQ_PREV_FIELD; + req = REQ_SPREV_FIELD; break; case '\t': tab = true; /* fall through */ case KEY_DOWN: - req = REQ_NEXT_FIELD; + req = REQ_SNEXT_FIELD; break; case KEY_PPAGE: - req = REQ_FIRST_FIELD; + req = REQ_SFIRST_FIELD; break; case KEY_NPAGE: - req = REQ_LAST_FIELD; + req = REQ_SLAST_FIELD; break; } -- cgit v1.2.1 From a3e3b66fbcb18174fc54ffd771ef3a38c9f8defd Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 16:12:14 +1100 Subject: petitboot: Implement ordered boot options Move petitboot to a more familiar 'boot-order' based autoboot system. The discover server now reads multiple values from the petitboot,bootdev parameter and adds them in order to config->autoboot_opts. Boot priority is determined by the options' position in the list. On the client, nc-config now recognises the new boot order, and allows the user to add, remove, and reorder the devices in the list. Signed-off-by: Samuel Mendoza-Jonas --- discover/device-handler.c | 55 +++---- discover/platform-powerpc.c | 121 +++++++++++++--- discover/platform.c | 32 ++--- lib/pb-config/pb-config.c | 26 ++-- lib/pb-protocol/pb-protocol.c | 55 ++++--- lib/types/types.h | 15 +- ui/ncurses/nc-config.c | 326 ++++++++++++++++++++++++++++-------------- utils/pb-config.c | 11 +- 8 files changed, 419 insertions(+), 222 deletions(-) diff --git a/discover/device-handler.c b/discover/device-handler.c index f053713..487f2e7 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -40,8 +40,7 @@ enum default_priority { DEFAULT_PRIORITY_REMOTE = 1, - DEFAULT_PRIORITY_LOCAL_UUID = 2, - DEFAULT_PRIORITY_LOCAL_FIRST = 3, + DEFAULT_PRIORITY_LOCAL_FIRST = 2, DEFAULT_PRIORITY_LOCAL_LAST = 0xfe, DEFAULT_PRIORITY_DISABLED = 0xff, }; @@ -462,11 +461,27 @@ static bool ipmi_device_type_matches(enum ipmi_bootdev ipmi_type, return false; } -static bool priority_matches(struct boot_priority *prio, - struct discover_boot_option *opt) +static int autoboot_option_priority(const struct config *config, + struct discover_boot_option *opt) { - return prio->type == opt->device->device->type || - prio->type == DEVICE_TYPE_ANY; + enum device_type type = opt->device->device->type; + const char *uuid = opt->device->uuid; + struct autoboot_option *auto_opt; + unsigned int i; + + for (i = 0; i < config->n_autoboot_opts; i++) { + auto_opt = &config->autoboot_opts[i]; + if (auto_opt->boot_type == BOOT_DEVICE_UUID) + if (!strcmp(auto_opt->uuid, uuid)) + return DEFAULT_PRIORITY_LOCAL_FIRST + i; + + if (auto_opt->boot_type == BOOT_DEVICE_TYPE) + if (auto_opt->type == type || + auto_opt->type == DEVICE_TYPE_ANY) + return DEFAULT_PRIORITY_LOCAL_FIRST + i; + } + + return -1; } /* @@ -478,8 +493,6 @@ static enum default_priority default_option_priority( struct discover_boot_option *opt) { const struct config *config; - const char *dev_str; - unsigned int i; config = config_get(); @@ -498,25 +511,17 @@ static enum default_priority default_option_priority( return DEFAULT_PRIORITY_DISABLED; } - /* Next, allow matching by device UUID. If we have one set but it - * doesn't match, disallow the default entirely */ - dev_str = config->boot_device; - if (dev_str && dev_str[0]) { - if (!strcmp(opt->device->uuid, dev_str)) - return DEFAULT_PRIORITY_LOCAL_UUID; - - pb_debug("handler: disabled default priority due to " - "non-matching UUID\n"); - return DEFAULT_PRIORITY_DISABLED; - } - - /* Lastly, use the local priorities */ - for (i = 0; i < config->n_boot_priorities; i++) { - struct boot_priority *prio = &config->boot_priorities[i]; - if (priority_matches(prio, opt)) - return DEFAULT_PRIORITY_LOCAL_FIRST + prio->priority; + /* Next, try to match the option against the user-defined autoboot + * options, either by device UUID or type. */ + if (config->n_autoboot_opts) { + int boot_match = autoboot_option_priority(config, opt); + if (boot_match > 0) + return boot_match; } + /* If the option didn't match any entry in the array, it is disabled */ + pb_debug("handler: disabled default priority due to " + "non-matching UUID or type\n"); return DEFAULT_PRIORITY_DISABLED; } diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index 895f0ec..bbffb60 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -49,6 +49,7 @@ static const char *known_params[] = { "petitboot,network", "petitboot,timeout", "petitboot,bootdev", + "petitboot,bootdevs", "petitboot,language", "petitboot,debug?", NULL, @@ -431,28 +432,86 @@ static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt) static void populate_bootdev_config(struct platform_powerpc *platform, struct config *config) - { + struct autoboot_option *opt, *new = NULL; + char *pos, *end, *old_dev = NULL; + const char delim = ' '; + unsigned int n_new = 0; const char *val; + bool conflict; - config->boot_device = NULL; - + /* Check for old-style bootdev */ val = get_param(platform, "petitboot,bootdev"); - if (!val || !strlen(val)) - return; - - if (!strncmp(val, "uuid:", strlen("uuid:"))) { - config->boot_device = talloc_strdup(config, + if (val && strlen(val)) { + pos = talloc_strdup(config, val); + if (!strncmp(val, "uuid:", strlen("uuid:"))) + old_dev = talloc_strdup(config, val + strlen("uuid:")); - - } else if (!strncmp(val, "mac:", strlen("mac:"))) { - config->boot_device = talloc_strdup(config, + else if (!strncmp(val, "mac:", strlen("mac:"))) + old_dev = talloc_strdup(config, val + strlen("mac:")); + } + /* Check for ordered bootdevs */ + val = get_param(platform, "petitboot,bootdevs"); + if (!val || !strlen(val)) { + pos = end = NULL; } else { - pb_log("bootdev config is in an unknown format " - "(expected uuid:... or mac:...)"); + pos = talloc_strdup(config, val); + end = strchr(pos, '\0'); + } + + while (pos && pos < end) { + opt = talloc(config, struct autoboot_option); + + if (read_bootdev(config, &pos, opt)) { + pb_log("bootdev config is in an unknown format " + "(expected uuid:... or mac:...)"); + talloc_free(opt); + if (strchr(pos, delim)) + continue; + return; + } + + new = talloc_realloc(config, new, struct autoboot_option, + n_new + 1); + new[n_new] = *opt; + n_new++; + talloc_free(opt); + } + + if (!n_new && !old_dev) { + /* If autoboot has been disabled, clear the default options */ + if (!config->autoboot_enabled) { + talloc_free(config->autoboot_opts); + config->n_autoboot_opts = 0; + } + return; + } + + conflict = old_dev && (!n_new || + new[0].boot_type == BOOT_DEVICE_TYPE || + /* Canonical UUIDs are 36 characters long */ + strncmp(new[0].uuid, old_dev, 36)); + + if (!conflict) { + talloc_free(config->autoboot_opts); + config->autoboot_opts = new; + config->n_autoboot_opts = n_new; + return; + } + + /* + * Difference detected, defer to old format in case it has been updated + * recently + */ + pb_debug("Old autoboot bootdev detected\n"); + talloc_free(config->autoboot_opts); + config->autoboot_opts = talloc(config, struct autoboot_option); + config->autoboot_opts[0].boot_type = BOOT_DEVICE_UUID; + config->autoboot_opts[0].uuid = talloc_strdup(config, old_dev); + config->n_autoboot_opts = 1; } static void populate_config(struct platform_powerpc *platform, @@ -578,16 +637,40 @@ static void update_network_config(struct platform_powerpc *platform, static void update_bootdev_config(struct platform_powerpc *platform, struct config *config) { - char *val, *tmp = NULL; + char *val = NULL, *boot_str = NULL, *tmp = NULL, *first = NULL; + struct autoboot_option *opt; + const char delim = ' '; + unsigned int i; - if (!config->boot_device) - val = ""; + if (!config->n_autoboot_opts) + first = val = ""; + else if (config->autoboot_opts[0].boot_type == BOOT_DEVICE_UUID) + first = talloc_asprintf(config, "uuid:%s", + config->autoboot_opts[0].uuid); else - tmp = val = talloc_asprintf(platform, - "uuid:%s", config->boot_device); + first = ""; + + for (i = 0; i < config->n_autoboot_opts; i++) { + opt = &config->autoboot_opts[i]; + switch (opt->boot_type) { + case BOOT_DEVICE_TYPE: + boot_str = talloc_asprintf(config, "%s%c", + device_type_name(opt->type), + delim); + break; + case BOOT_DEVICE_UUID: + boot_str = talloc_asprintf(config, "uuid:%s%c", + opt->uuid, delim); + break; + } + tmp = val = talloc_asprintf_append(val, boot_str); + } - update_string_config(platform, "petitboot,bootdev", val); + update_string_config(platform, "petitboot,bootdevs", val); + update_string_config(platform, "petitboot,bootdev", first); talloc_free(tmp); + if (boot_str) + talloc_free(boot_str); } static int update_config(struct platform_powerpc *platform, diff --git a/discover/platform.c b/discover/platform.c index 4451589..74e2a82 100644 --- a/discover/platform.c +++ b/discover/platform.c @@ -62,16 +62,13 @@ static void dump_config(struct config *config) for (i = 0; i < config->network.n_dns_servers; i++) pb_log(" dns server %s\n", config->network.dns_servers[i]); - if (config->boot_device) - pb_log(" boot device %s\n", config->boot_device); - - if (config->n_boot_priorities) - pb_log(" boot priority order:\n"); - - for (i = 0; i < config->n_boot_priorities; i++) { - struct boot_priority *prio = &config->boot_priorities[i]; - pb_log(" %10s: %d\n", device_type_name(prio->type), - prio->priority); + for (i = 0; i < config->n_autoboot_opts; i++) { + if (config->autoboot_opts[i].boot_type == BOOT_DEVICE_TYPE) + pb_log(" boot device %d: %s\n", i, + device_type_name(config->autoboot_opts[i].type)); + else + pb_log(" boot device %d: uuid: %s\n", + i, config->autoboot_opts[i].uuid); } pb_log(" IPMI boot device 0x%02x%s\n", config->ipmi_bootdev, @@ -109,17 +106,16 @@ void config_set_defaults(struct config *config) config->network.n_interfaces = 0; config->network.dns_servers = NULL; config->network.n_dns_servers = 0; - config->boot_device = NULL; config->safe_mode = false; config->lang = NULL; - config->n_boot_priorities = 2; - config->boot_priorities = talloc_array(config, struct boot_priority, - config->n_boot_priorities); - config->boot_priorities[0].type = DEVICE_TYPE_NETWORK; - config->boot_priorities[0].priority = 0; - config->boot_priorities[1].type = DEVICE_TYPE_ANY; - config->boot_priorities[1].priority = 1; + config->n_autoboot_opts = 2; + config->autoboot_opts = talloc_array(config, struct autoboot_option, + config->n_autoboot_opts); + config->autoboot_opts[0].boot_type = BOOT_DEVICE_TYPE; + config->autoboot_opts[0].type = DEVICE_TYPE_NETWORK; + config->autoboot_opts[1].boot_type = BOOT_DEVICE_TYPE; + config->autoboot_opts[1].type = DEVICE_TYPE_ANY; config->ipmi_bootdev = 0; config->ipmi_bootdev_persistent = false; diff --git a/lib/pb-config/pb-config.c b/lib/pb-config/pb-config.c index a2272f4..98a6078 100644 --- a/lib/pb-config/pb-config.c +++ b/lib/pb-config/pb-config.c @@ -59,21 +59,21 @@ struct config *config_copy(void *ctx, const struct config *src) dest->network.dns_servers[i] = talloc_strdup(dest, src->network.dns_servers[i]); - dest->n_boot_priorities = src->n_boot_priorities; - dest->boot_priorities = talloc_array(dest, struct boot_priority, - src->n_boot_priorities); - - for (i = 0; i < src->n_boot_priorities; i++) { - dest->boot_priorities[i].priority = - src->boot_priorities[i].priority; - dest->boot_priorities[i].type = src->boot_priorities[i].type; + dest->n_autoboot_opts = src->n_autoboot_opts; + dest->autoboot_opts = talloc_array(dest, struct autoboot_option, + dest->n_autoboot_opts); + + for (i = 0; i < src->n_autoboot_opts; i++) { + dest->autoboot_opts[i].boot_type = + src->autoboot_opts[i].boot_type; + if (src->autoboot_opts[i].boot_type == BOOT_DEVICE_TYPE) + dest->autoboot_opts[i].type = + src->autoboot_opts[i].type; + else + dest->autoboot_opts[i].uuid = + talloc_strdup(dest, src->autoboot_opts[i].uuid); } - if (src->boot_device && strlen(src->boot_device)) - dest->boot_device = talloc_strdup(dest, src->boot_device); - else - dest->boot_device = NULL; - dest->ipmi_bootdev = src->ipmi_bootdev; dest->ipmi_bootdev_persistent = src->ipmi_bootdev_persistent; diff --git a/lib/pb-protocol/pb-protocol.c b/lib/pb-protocol/pb-protocol.c index 4398248..69ea35d 100644 --- a/lib/pb-protocol/pb-protocol.c +++ b/lib/pb-protocol/pb-protocol.c @@ -280,9 +280,13 @@ int pb_protocol_config_len(const struct config *config) len += 4 + optional_strlen(config->network.dns_servers[i]); len += 4; - len += config->n_boot_priorities * 8; - - len += 4 + optional_strlen(config->boot_device); + for (i = 0; i < config->n_autoboot_opts; i++) { + if (config->autoboot_opts[i].boot_type == BOOT_DEVICE_TYPE) + len += 4 + 4; + else + len += 4 + 4 + + optional_strlen(config->autoboot_opts[i].uuid); + } len += 4 + 4; /* ipmi_bootdev, ipmi_bootdev_persistent */ @@ -477,19 +481,22 @@ int pb_protocol_serialise_config(const struct config *config, config->network.dns_servers[i]); } - *(uint32_t *)pos = __cpu_to_be32(config->n_boot_priorities); + *(uint32_t *)pos = __cpu_to_be32(config->n_autoboot_opts); pos += 4; - for (i = 0; i < config->n_boot_priorities; i++) { - *(uint32_t *)pos = - __cpu_to_be32(config->boot_priorities[i].type); - pos += 4; + for (i = 0; i < config->n_autoboot_opts; i++) { *(uint32_t *)pos = - __cpu_to_be32(config->boot_priorities[i].priority); + __cpu_to_be32(config->autoboot_opts[i].boot_type); pos += 4; + if (config->autoboot_opts[i].boot_type == BOOT_DEVICE_TYPE) { + *(uint32_t *)pos = + __cpu_to_be32(config->autoboot_opts[i].type); + pos += 4; + } else { + pos += pb_protocol_serialise_string(pos, + config->autoboot_opts[i].uuid); + } } - pos += pb_protocol_serialise_string(pos, config->boot_device); - *(uint32_t *)pos = __cpu_to_be32(config->ipmi_bootdev); pos += 4; *(uint32_t *)pos = config->ipmi_bootdev_persistent; @@ -925,24 +932,26 @@ int pb_protocol_deserialise_config(struct config *config, config->network.dns_servers[i] = str; } - if (read_u32(&pos, &len, &config->n_boot_priorities)) + if (read_u32(&pos, &len, &config->n_autoboot_opts)) goto out; - config->boot_priorities = talloc_array(config, struct boot_priority, - config->n_boot_priorities); + config->autoboot_opts = talloc_array(config, struct autoboot_option, + config->n_autoboot_opts); - for (i = 0; i < config->n_boot_priorities; i++) { + for (i = 0; i < config->n_autoboot_opts; i++) { if (read_u32(&pos, &len, &tmp)) goto out; - config->boot_priorities[i].priority = (int)tmp; - if (read_u32(&pos, &len, &tmp)) - goto out; - config->boot_priorities[i].type = tmp; + config->autoboot_opts[i].boot_type = (int)tmp; + if (config->autoboot_opts[i].boot_type == BOOT_DEVICE_TYPE) { + if (read_u32(&pos, &len, &tmp)) + goto out; + config->autoboot_opts[i].type = tmp; + } else { + if (read_string(config, &pos, &len, &str)) + goto out; + config->autoboot_opts[i].uuid = str; + } } - if (read_string(config, &pos, &len, &str)) - goto out; - config->boot_device = str; - if (read_u32(&pos, &len, &config->ipmi_bootdev)) goto out; if (read_u32(&pos, &len, &tmp)) diff --git a/lib/types/types.h b/lib/types/types.h index e22dbc3..3bb8c9b 100644 --- a/lib/types/types.h +++ b/lib/types/types.h @@ -113,15 +113,6 @@ struct network_config { unsigned int n_dns_servers; }; -struct boot_priority { - /* Boot options with higher priority values will take precedence over - * lower values. Negative priorities signify "don't boot this by - * default". - */ - int priority; - enum device_type type; -}; - struct autoboot_option { enum { BOOT_DEVICE_TYPE, @@ -138,10 +129,8 @@ struct config { unsigned int autoboot_timeout_sec; struct network_config network; - struct boot_priority *boot_priorities; - unsigned int n_boot_priorities; - - char *boot_device; + struct autoboot_option *autoboot_opts; + unsigned int n_autoboot_opts; unsigned int ipmi_bootdev; bool ipmi_bootdev_persistent; diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c index c45df34..3156b9b 100644 --- a/ui/ncurses/nc-config.c +++ b/ui/ncurses/nc-config.c @@ -33,16 +33,10 @@ #include "nc-config.h" #include "nc-widgets.h" -#define N_FIELDS 26 +#define N_FIELDS 29 extern struct help_text config_help_text; -enum autoboot_type { - AUTOBOOT_ANY, - AUTOBOOT_ONE, - AUTOBOOT_DISABLED, -}; - enum net_conf_type { NET_CONF_TYPE_DHCP_ALL, NET_CONF_TYPE_DHCP_ONE, @@ -57,7 +51,9 @@ struct config_screen { bool exit; bool show_help; + bool show_subset; bool need_redraw; + void (*on_exit)(struct cui *); int scroll_y; @@ -67,12 +63,16 @@ struct config_screen { int network_config_y; enum net_conf_type net_conf_type; - enum autoboot_type autoboot_type; + + bool autoboot_enabled; struct { - struct nc_widget_select *autoboot_f; - struct nc_widget_label *autoboot_l; - struct nc_widget_select *boot_device_f; + struct nc_widget_label *boot_order_l; + struct nc_widget_subset *boot_order_f; + struct nc_widget_label *boot_empty_l; + struct nc_widget_button *boot_add_b; + struct nc_widget_button *boot_none_b; + struct nc_widget_button *boot_any_b; struct nc_widget_textbox *timeout_f; struct nc_widget_label *timeout_l; struct nc_widget_label *timeout_help_l; @@ -151,7 +151,7 @@ static void config_screen_process_key(struct nc_scr *scr, int key) cui_show_help(screen->cui, _("System Configuration"), &config_help_text); - } else if (handled) { + } else if (handled && !screen->show_subset) { pad_refresh(screen); } } @@ -165,6 +165,7 @@ static void config_screen_resize(struct nc_scr *scr) static int config_screen_post(struct nc_scr *scr) { struct config_screen *screen = config_screen_from_scr(scr); + screen->show_subset = false; widgetset_post(screen->widgetset); nc_scr_frame_draw(scr); if (screen->need_redraw) { @@ -193,40 +194,48 @@ static int screen_process_form(struct config_screen *screen) const struct system_info *sysinfo = screen->cui->sysinfo; enum net_conf_type net_conf_type; struct interface_config *iface; - char *str, *end, *uuid; + char *str, *end; struct config *config; - int rc, idx; + int i, n_boot_opts, rc, idx; + unsigned int *order; + char mac[20]; config = config_copy(screen, screen->cui->config); - screen->autoboot_type = - widget_select_get_value(screen->widgets.autoboot_f); - - config->autoboot_enabled = screen->autoboot_type != AUTOBOOT_DISABLED; - - uuid = NULL; - if (screen->autoboot_type == AUTOBOOT_ONE) { - char mac[20]; - - /* if idx is -1 here, we have an unknown UUID selected. - * Otherwise, it's a blockdev index (idx <= n_blockdevs) or an - * interface index. - */ - idx = widget_select_get_value(screen->widgets.boot_device_f); - if (idx >= (int)sysinfo->n_blockdevs) { - struct interface_info *info = sysinfo-> - interfaces[idx - sysinfo->n_blockdevs]; - mac_str(info->hwaddr, info->hwaddr_size, - mac, sizeof(mac)); - uuid = mac; - } else if (idx != -1) { - uuid = sysinfo->blockdevs[idx]->uuid; + talloc_free(config->autoboot_opts); + config->n_autoboot_opts = 0; + + n_boot_opts = widget_subset_get_order(config, &order, + screen->widgets.boot_order_f); + + config->autoboot_enabled = n_boot_opts > 0; + + config->n_autoboot_opts = n_boot_opts; + config->autoboot_opts = talloc_array(config, struct autoboot_option, + n_boot_opts); + + for (i = 0; i < n_boot_opts; i++) { + if (order[i] < sysinfo->n_blockdevs) { + /* disk uuid */ + config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID; + config->autoboot_opts[i].uuid = talloc_strdup(config, + sysinfo->blockdevs[order[i]]->uuid); + } else if(order[i] < (sysinfo->n_blockdevs + sysinfo->n_interfaces)) { + /* net uuid */ + order[i] -= sysinfo->n_blockdevs; + config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID; + mac_str(sysinfo->interfaces[order[i]]->hwaddr, + sysinfo->interfaces[order[i]]->hwaddr_size, + mac, sizeof(mac)); + config->autoboot_opts[i].uuid = talloc_strdup(config, mac); + } else { + /* device type */ + order[i] -= (sysinfo->n_blockdevs + sysinfo->n_interfaces); + config->autoboot_opts[i].boot_type = BOOT_DEVICE_TYPE; + config->autoboot_opts[i].type = order[i]; } } - talloc_free(config->boot_device); - config->boot_device = uuid ? talloc_strdup(config, uuid) : NULL; - str = widget_textbox_get_value(screen->widgets.timeout_f); if (str) { unsigned long x; @@ -367,35 +376,55 @@ static void config_screen_layout_widgets(struct config_screen *screen) help_x = screen->field_x + 2 + widget_width(widget_textbox_base(screen->widgets.dns_f)); - y += layout_pair(screen, y, screen->widgets.autoboot_l, - widget_select_base(screen->widgets.autoboot_f)); + y += 1; + + wl = widget_label_base(screen->widgets.boot_order_l); + widget_set_visible(wl, true); + widget_move(wl, y, screen->label_x); + + wf = widget_subset_base(screen->widgets.boot_order_f); + widget_move(wf, y, screen->field_x); + wl = widget_label_base(screen->widgets.boot_empty_l); + widget_move(wl, y, screen->field_x); - wf = widget_select_base(screen->widgets.boot_device_f); - if (screen->autoboot_type == AUTOBOOT_ONE) { + if (widget_subset_height(screen->widgets.boot_order_f)) { widget_set_visible(wf, true); - widget_move(wf, y, screen->field_x + 3); + widget_set_visible(wl, false); y += widget_height(wf); } else { + widget_set_visible(wl, true); widget_set_visible(wf, false); + y += 1; } y += 1; + widget_move(widget_button_base(screen->widgets.boot_add_b), + y, screen->field_x); + widget_move(widget_button_base(screen->widgets.boot_any_b), + y, screen->field_x + 12); + widget_move(widget_button_base(screen->widgets.boot_none_b), + y, screen->field_x + 30); + + wf = widget_button_base(screen->widgets.boot_add_b); + if (widget_subset_n_inactive(screen->widgets.boot_order_f)) + widget_set_visible(wf, true); + else + widget_set_visible(wf, false); + + y += 2; + wf = widget_textbox_base(screen->widgets.timeout_f); wl = widget_label_base(screen->widgets.timeout_l); wh = widget_label_base(screen->widgets.timeout_help_l); - if (screen->autoboot_type != AUTOBOOT_DISABLED) { - widget_set_visible(wl, true); - widget_set_visible(wf, true); - widget_set_visible(wh, true); + widget_set_visible(wl, screen->autoboot_enabled); + widget_set_visible(wf, screen->autoboot_enabled); + if (screen->autoboot_enabled) { + widget_set_visible(wh, screen->autoboot_enabled); widget_move(wl, y, screen->label_x); widget_move(wf, y, screen->field_x); widget_move(wh, y, screen->field_x + widget_width(wf) + 1); y += 2; - } else { - widget_set_visible(wl, false); - widget_set_visible(wf, false); - widget_set_visible(wh, false); } y += layout_pair(screen, y, screen->widgets.network_l, @@ -500,15 +529,69 @@ static void config_screen_network_change(void *arg, int value) widgetset_post(screen->widgetset); } -static void config_screen_autoboot_change(void *arg, int value) +static void config_screen_boot_order_change(void *arg, int value) +{ + (void)value; + struct config_screen *screen = arg; + widgetset_unpost(screen->widgetset); + config_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); +} + +static void config_screen_add_device(void *arg) { struct config_screen *screen = arg; - screen->autoboot_type = value; + + screen->show_subset = true; + cui_show_subset(screen->cui, _("Select an option"), + screen->widgets.boot_order_f); +} + +static void config_screen_autoboot_none(void *arg) +{ + struct config_screen *screen = arg; + struct nc_widget_subset *subset = screen->widgets.boot_order_f; + + widget_subset_clear_active(subset); + screen->autoboot_enabled = false; + widgetset_unpost(screen->widgetset); config_screen_layout_widgets(screen); widgetset_post(screen->widgetset); } +static void config_screen_autoboot_any(void *arg) +{ + struct config_screen *screen = arg; + const struct system_info *sysinfo = screen->cui->sysinfo; + struct nc_widget_subset *subset = screen->widgets.boot_order_f; + int idx; + + widget_subset_clear_active(subset); + + idx = sysinfo->n_blockdevs + sysinfo->n_interfaces + DEVICE_TYPE_ANY; + + widget_subset_make_active(screen->widgets.boot_order_f, idx); + + screen->autoboot_enabled = true; + + widgetset_unpost(screen->widgetset); + config_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); +} + +static void config_screen_update_subset(void *arg, + struct nc_widget_subset *subset, int idx) +{ + struct config_screen *screen = arg; + + if (idx >= 0) + widget_subset_make_active(subset, idx); + if (!screen->autoboot_enabled) + screen->autoboot_enabled = true; + config_screen_layout_widgets(screen); +} + static struct interface_config *first_active_interface( const struct config *config) { @@ -550,6 +633,31 @@ static void config_screen_setup_empty(struct config_screen *screen) cancel_click, screen); } +static int find_autoboot_idx(const struct system_info *sysinfo, + struct autoboot_option *opt) +{ + unsigned int i; + + if (opt->boot_type == BOOT_DEVICE_TYPE) + return sysinfo->n_blockdevs + sysinfo->n_interfaces + opt->type; + + for (i = 0; i < sysinfo->n_blockdevs; i++) { + if (!strcmp(sysinfo->blockdevs[i]->uuid, opt->uuid)) + return i; + } + + for (i = 0; i < sysinfo->n_interfaces; i++) { + struct interface_info *info = sysinfo->interfaces[i]; + char mac[20]; + + mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac)); + + if (!strcmp(mac, opt->uuid)) + return sysinfo->n_blockdevs + i; + } + + return -1; +} static void config_screen_setup_widgets(struct config_screen *screen, const struct config *config, @@ -560,7 +668,6 @@ static void config_screen_setup_widgets(struct config_screen *screen, char *str, *ip, *mask, *gw; enum net_conf_type type; unsigned int i; - bool found; build_assert(sizeof(screen->widgets) / sizeof(struct widget *) == N_FIELDS); @@ -568,81 +675,84 @@ static void config_screen_setup_widgets(struct config_screen *screen, type = screen->net_conf_type; ifcfg = first_active_interface(config); - screen->widgets.autoboot_l = widget_new_label(set, 0, 0, - _("Autoboot:")); - screen->widgets.autoboot_f = widget_new_select(set, 0, 0, 55); - - widget_select_on_change(screen->widgets.autoboot_f, - config_screen_autoboot_change, screen); - - screen->widgets.boot_device_f = widget_new_select(set, 0, 0, 55); - - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_DISABLED, - _("Don't autoboot"), - screen->autoboot_type == - AUTOBOOT_DISABLED); - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_ANY, - _("Autoboot from any " - "disk/network device"), - screen->autoboot_type == - AUTOBOOT_ANY); - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_ONE, - _("Only autoboot from a specific " - "disk/network device"), - screen->autoboot_type == - AUTOBOOT_ONE); - - found = false; + screen->widgets.boot_add_b = widget_new_button(set, 0, 0, 10, + _("Add Device"), config_screen_add_device, + screen); + + screen->widgets.boot_none_b = widget_new_button(set, 0, 0, 10, + _("Clear"), + config_screen_autoboot_none, screen); + + screen->widgets.boot_any_b = widget_new_button(set, 0, 0, 16, + _("Clear & Boot Any"), config_screen_autoboot_any, + screen); + + screen->widgets.boot_order_l = widget_new_label(set, 0, 0, + _("Boot order:")); + screen->widgets.boot_order_f = widget_new_subset(set, 0, 0, + COLS - screen->field_x, + config_screen_update_subset); + screen->widgets.boot_empty_l = widget_new_label(set, 0, 0, + _("(None)")); + + widget_subset_on_change(screen->widgets.boot_order_f, + config_screen_boot_order_change, screen); for (i = 0; i < sysinfo->n_blockdevs; i++) { struct blockdev_info *bd = sysinfo->blockdevs[i]; - bool selected; char *label; - selected = config->boot_device && - !strcmp(config->boot_device, bd->uuid); - if (selected) - found = true; - label = talloc_asprintf(screen, _("disk: %s [uuid: %s]"), bd->name, bd->uuid); - widget_select_add_option(screen->widgets.boot_device_f, i, - label, selected); + widget_subset_add_option(screen->widgets.boot_order_f, label); } for (i = 0; i < sysinfo->n_interfaces; i++) { struct interface_info *info = sysinfo->interfaces[i]; char *label, mac[20]; - bool selected; mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac)); - selected = config->boot_device && - !strcmp(config->boot_device, mac); - if (selected) - found = true; label = talloc_asprintf(screen, _("net: %s [mac: %s]"), info->name, mac); - widget_select_add_option(screen->widgets.boot_device_f, - i + sysinfo->n_blockdevs, - label, selected); + widget_subset_add_option(screen->widgets.boot_order_f, label); } - if (screen->autoboot_type == AUTOBOOT_ONE && !found) { + for (i = DEVICE_TYPE_NETWORK; i < DEVICE_TYPE_NETWORK + 4; i++) { char *label; - label = talloc_asprintf(screen, _("Unknown UUID: %s"), - config->boot_device); + if (i == DEVICE_TYPE_ANY) + label = talloc_asprintf(screen, _("Any Device")); + else + label = talloc_asprintf(screen, _("Any %s device"), + device_type_display_name(i)); - widget_select_add_option(screen->widgets.boot_device_f, -1, - label, true); + widget_subset_add_option(screen->widgets.boot_order_f, label); } + screen->autoboot_enabled = config->n_autoboot_opts; + for (i = 0; i < config->n_autoboot_opts; i++) { + struct autoboot_option *opt = &config->autoboot_opts[i]; + int idx; + + idx = find_autoboot_idx(sysinfo, opt); + + if (idx >= 0) { + widget_subset_make_active(screen->widgets.boot_order_f, + idx); + } else { + if (opt->boot_type == BOOT_DEVICE_TYPE) + pb_log("%s: Unknown autoboot option: %d\n", + __func__, opt->type); + else + pb_log("%s: Unknown autoboot UUID: %s\n", + __func__, opt->uuid); + } + } + + str = talloc_asprintf(screen, "%d", config->autoboot_timeout_sec); screen->widgets.timeout_l = widget_new_label(set, 0, 0, _("Timeout:")); screen->widgets.timeout_f = widget_new_textbox(set, 0, 0, 5, str); @@ -818,12 +928,6 @@ static void config_screen_draw(struct config_screen *screen, config_screen_setup_empty(screen); } else { screen->net_conf_type = find_net_conf_type(config); - if (!config->autoboot_enabled) - screen->autoboot_type = AUTOBOOT_DISABLED; - else - screen->autoboot_type = config->boot_device ? - AUTOBOOT_ONE : AUTOBOOT_ANY; - config_screen_setup_widgets(screen, config, sysinfo); config_screen_layout_widgets(screen); } @@ -868,6 +972,8 @@ struct config_screen *config_screen_init(struct cui *cui, screen->label_x = 2; screen->field_x = 17; + screen->show_subset = false; + screen->scr.frame.ltitle = talloc_strdup(screen, _("Petitboot System Configuration")); screen->scr.frame.rtitle = NULL; diff --git a/utils/pb-config.c b/utils/pb-config.c index 3bd670c..009bec7 100644 --- a/utils/pb-config.c +++ b/utils/pb-config.c @@ -61,7 +61,16 @@ static void print_one_config(void *ctx, const char *req, const char *name, static void print_config(void *ctx, struct config *config, const char *var) { - print_one_config(ctx, var, "bootdev", "%s", config->boot_device); + unsigned int i; + + for (i = 0; i < config->n_autoboot_opts; i++) { + if (config->autoboot_opts[i].boot_type == BOOT_DEVICE_TYPE) + print_one_config(ctx, var, "bootdev", "%s", + device_type_name(config->autoboot_opts[i].type)); + else + print_one_config(ctx, var, "bootdev", "%s", + config->autoboot_opts[i].uuid); + } print_one_config(ctx, var, "autoboot", "%s", config->autoboot_enabled ? "enabled" : "disabled"); print_one_config(ctx, var, "timeout", "%d", -- cgit v1.2.1 From 893cfce3e9ea91a68e678829fb8e7e37102e2b41 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 29 Jan 2015 16:06:38 +1100 Subject: ui/ncurses: Update config screen help text Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-config-help.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/ui/ncurses/nc-config-help.c b/ui/ncurses/nc-config-help.c index c977d1a..22ced3c 100644 --- a/ui/ncurses/nc-config-help.c +++ b/ui/ncurses/nc-config-help.c @@ -1,23 +1,26 @@ #include "nc-helpscreen.h" struct help_text config_help_text = define_help_text("\ -Autoboot: There are three possible options for automatic-boot hehaviour:\n" +Autoboot: Specify which devices to autoboot from.\n" "\n" -"Don't autoboot: boot options will be listed in the petitboot menu, but none \ -will be booted automatically. User interaction will be required to continue \ -past the petitboot menu. Use this option if you want the machine to wait for \ -an explicit boot selection, or want to interact with petitboot before \ -booting the system\n" +"By selecting the 'Add Device' button new devices can be added to the autoboot \ +list, either by UUID, MAC address, or device type. Once added to the boot \ +order, the priority of devices can be changed with the 'left' and 'right' keys \ +Devices can be individually removed from the boot order with the minus key. \ +Use this option if you have multiple operating system images installed.\n" "\n" -"Autoboot from any disk/network device: any boot option that is marked as a \ -default (by bootloader configuration) will be booted automatically after a \ +"To autoboot from any device, select the 'Clear & Boot Any' button. \ +In this case, any boot option that is marked as a default \ +(by bootloader configuration) will be booted automatically after a \ timeout. Use this option if you want to quickly boot your system without \ changing any boot option settings. This is the typical configuration.\n" "\n" -"Only autoboot from a specific disk/network device: only boot options \ -from a single device (specifed here) will be booted automatically after a \ -timeout. Use this option if you have multiple operating system images \ -installed.\n" +"To disable autoboot, select the 'Clear' button, which will clear the boot \ +order. \ +With autoboot disabled, user interaction will be required to continue past \ +the petitboot menu. Use this option if you want the machine to wait for an \ +explicit boot selection, or want to interact with petitboot before booting \ +the system\n" "\n" "Timeout: Specify the length of time, in seconds, that the main menu will be \ displayed before the default boot option is started. This option is only \ -- cgit v1.2.1 From b9af039e99527159aad905c3395fa974b9621f8e Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 8 Apr 2015 09:45:43 +1000 Subject: lib: Move ipmi_bootdev enum to types.h Move the ipmi_bootdev definition to types.h to support returning descriptive strings to the system configuration UI. Signed-off-by: Samuel Mendoza-Jonas --- discover/ipmi.h | 11 ++--------- discover/platform-powerpc.c | 1 + lib/types/types.c | 20 ++++++++++++++++++++ lib/types/types.h | 11 +++++++++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/discover/ipmi.h b/discover/ipmi.h index 83f2910..3b11683 100644 --- a/discover/ipmi.h +++ b/discover/ipmi.h @@ -4,6 +4,8 @@ #include #include +#include + enum ipmi_netfn { IPMI_NETFN_CHASSIS = 0x0, IPMI_NETFN_SE = 0x04, @@ -15,15 +17,6 @@ enum ipmi_cmd { IPMI_CMD_SENSOR_SET = 0x30, }; -enum ipmi_bootdev { - IPMI_BOOTDEV_NONE = 0x00, - IPMI_BOOTDEV_NETWORK = 0x01, - IPMI_BOOTDEV_DISK = 0x2, - IPMI_BOOTDEV_SAFE = 0x3, - IPMI_BOOTDEV_CDROM = 0x5, - IPMI_BOOTDEV_SETUP = 0x6, -}; - enum ipmi_sensor_ids { IPMI_SENSOR_ID_OS_BOOT = 0x1F, }; diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index bbffb60..d777bc0 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -716,6 +716,7 @@ static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev, case IPMI_BOOTDEV_DISK: case IPMI_BOOTDEV_NETWORK: case IPMI_BOOTDEV_CDROM: + default: break; case IPMI_BOOTDEV_SETUP: config->autoboot_enabled = false; diff --git a/lib/types/types.c b/lib/types/types.c index 059e52a..95a3a48 100644 --- a/lib/types/types.c +++ b/lib/types/types.c @@ -2,6 +2,26 @@ #include #include +const char *ipmi_bootdev_display_name(enum ipmi_bootdev bootdev) +{ + switch (bootdev) { + case IPMI_BOOTDEV_NONE: + return _("None"); + case IPMI_BOOTDEV_NETWORK: + return _("Network"); + case IPMI_BOOTDEV_DISK: + return _("Disk"); + case IPMI_BOOTDEV_SAFE: + return _("Safe Mode"); + case IPMI_BOOTDEV_CDROM: + return _("Optical"); + case IPMI_BOOTDEV_SETUP: + return _("Setup Mode"); + default: + return _("Unknown"); + } +} + const char *device_type_display_name(enum device_type type) { switch (type) { diff --git a/lib/types/types.h b/lib/types/types.h index 3bb8c9b..e5c7e3e 100644 --- a/lib/types/types.h +++ b/lib/types/types.h @@ -13,6 +13,17 @@ enum device_type { DEVICE_TYPE_UNKNOWN, }; +enum ipmi_bootdev { + IPMI_BOOTDEV_NONE = 0x00, + IPMI_BOOTDEV_NETWORK = 0x01, + IPMI_BOOTDEV_DISK = 0x2, + IPMI_BOOTDEV_SAFE = 0x3, + IPMI_BOOTDEV_CDROM = 0x5, + IPMI_BOOTDEV_SETUP = 0x6, + IPMI_BOOTDEV_INVALID = 0xff, +}; + +const char *ipmi_bootdev_display_name(enum ipmi_bootdev bootdev); const char *device_type_display_name(enum device_type type); const char *device_type_name(enum device_type type); enum device_type find_device_type(const char *str); -- cgit v1.2.1 From f497317729e7b2823a953f00c8301d192cda8824 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Tue, 6 Jan 2015 13:35:11 +1100 Subject: ui/ncurses: Display current ipmi bootdev settings Display the current IPMI bootdev override if it exists, and allow the user to clear it from the configuration screen. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-config.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c index 3156b9b..6164873 100644 --- a/ui/ncurses/nc-config.c +++ b/ui/ncurses/nc-config.c @@ -33,7 +33,7 @@ #include "nc-config.h" #include "nc-widgets.h" -#define N_FIELDS 29 +#define N_FIELDS 32 extern struct help_text config_help_text; @@ -65,6 +65,7 @@ struct config_screen { enum net_conf_type net_conf_type; bool autoboot_enabled; + bool ipmi_override; struct { struct nc_widget_label *boot_order_l; @@ -77,6 +78,10 @@ struct config_screen { struct nc_widget_label *timeout_l; struct nc_widget_label *timeout_help_l; + struct nc_widget_label *ipmi_type_l; + struct nc_widget_label *ipmi_clear_l; + struct nc_widget_checkbox *ipmi_clear_cb; + struct nc_widget_label *network_l; struct nc_widget_select *network_f; @@ -245,6 +250,11 @@ static int screen_process_form(struct config_screen *screen) config->autoboot_timeout_sec = x; } + if (screen->ipmi_override) + if (widget_checkbox_get_value(screen->widgets.ipmi_clear_cb)) + config->ipmi_bootdev = IPMI_BOOTDEV_INVALID; + + net_conf_type = widget_select_get_value(screen->widgets.network_f); /* if we don't have any network interfaces, prevent per-interface @@ -427,6 +437,23 @@ static void config_screen_layout_widgets(struct config_screen *screen) y += 2; } + if (screen->ipmi_override) { + wl = widget_label_base(screen->widgets.ipmi_type_l); + widget_set_visible(wl, true); + widget_move(wl, y, screen->label_x); + y += 1; + + wl = widget_label_base(screen->widgets.ipmi_clear_l); + wf = widget_checkbox_base(screen->widgets.ipmi_clear_cb); + widget_set_visible(wl, true); + widget_set_visible(wf, true); + widget_move(wl, y, screen->label_x); + widget_move(wf, y, screen->field_x); + y += 1; + } + + y += 2; + y += layout_pair(screen, y, screen->widgets.network_l, widget_select_base(screen->widgets.network_f)); @@ -762,6 +789,21 @@ static void config_screen_setup_widgets(struct config_screen *screen, widget_textbox_set_fixed_size(screen->widgets.timeout_f); widget_textbox_set_validator_integer(screen->widgets.timeout_f, 0, 999); + if (config->ipmi_bootdev) { + char *label = talloc_asprintf(screen, + _("%s IPMI boot option: %s"), + config->ipmi_bootdev_persistent ? + "Persistent" : "Temporary", + ipmi_bootdev_display_name(config->ipmi_bootdev)); + screen->widgets.ipmi_type_l = widget_new_label(set, 0, 0, + label); + screen->widgets.ipmi_clear_l = widget_new_label(set, 0, 0, + _("Clear option:")); + screen->widgets.ipmi_clear_cb = widget_new_checkbox(set, 0, 0, + false); + screen->ipmi_override = true; + } + screen->widgets.network_l = widget_new_label(set, 0, 0, _("Network:")); screen->widgets.network_f = widget_new_select(set, 0, 0, 50); @@ -972,6 +1014,7 @@ struct config_screen *config_screen_init(struct cui *cui, screen->label_x = 2; screen->field_x = 17; + screen->ipmi_override = false; screen->show_subset = false; screen->scr.frame.ltitle = talloc_strdup(screen, -- cgit v1.2.1 From 7946d9b12cec7b49334fc0db6bc23087f2cea8ad Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 9 Apr 2015 15:27:01 +1000 Subject: discover: Clear default-boot-device when invalidated Update the clear_ipmi_bootdev functions to optionally invalidate the next- or default-boot-device for their respective machines. If a client invalidates the ipmi_bootdev in the config, invalidate the respective bootdev. Signed-off-by: Samuel Mendoza-Jonas --- discover/platform-powerpc.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index d777bc0..b4f2a77 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -39,7 +39,8 @@ struct platform_powerpc { struct platform_powerpc *platform, uint8_t *bootdev, bool *persistent); int (*clear_ipmi_bootdev)( - struct platform_powerpc *platform); + struct platform_powerpc *platform, + bool persistent); int (*set_os_boot_sensor)( struct platform_powerpc *platform); }; @@ -691,6 +692,14 @@ static int update_config(struct platform_powerpc *platform, val = tmp = talloc_asprintf(platform, "%d", config->autoboot_timeout_sec); + if (config->ipmi_bootdev == IPMI_BOOTDEV_INVALID && + platform->clear_ipmi_bootdev) { + platform->clear_ipmi_bootdev(platform, + config->ipmi_bootdev_persistent); + config->ipmi_bootdev = IPMI_BOOTDEV_NONE; + config->ipmi_bootdev_persistent = false; + } + update_string_config(platform, "petitboot,timeout", val); if (tmp) talloc_free(tmp); @@ -806,10 +815,16 @@ static int write_bootdev_sysparam(const char *name, uint8_t val) } static int clear_ipmi_bootdev_sysparams( - struct platform_powerpc *platform __attribute__((unused))) + struct platform_powerpc *platform __attribute__((unused)), + bool persistent) { - /* invalidate next-boot-device setting */ - write_bootdev_sysparam("next-boot-device", 0xff); + if (persistent) { + /* invalidate default-boot-device setting */ + write_bootdev_sysparam("default-boot-device", 0xff); + } else { + /* invalidate next-boot-device setting */ + write_bootdev_sysparam("next-boot-device", 0xff); + } return 0; } @@ -836,7 +851,8 @@ static int get_ipmi_bootdev_sysparams( return 0; } -static int clear_ipmi_bootdev_ipmi(struct platform_powerpc *platform) +static int clear_ipmi_bootdev_ipmi(struct platform_powerpc *platform, + bool persistent __attribute__((unused))) { uint16_t resp_len; uint8_t resp[1]; @@ -1001,7 +1017,7 @@ static void pre_boot(struct platform *p, const struct config *config) struct platform_powerpc *platform = to_platform_powerpc(p); if (!config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev) - platform->clear_ipmi_bootdev(platform); + platform->clear_ipmi_bootdev(platform, false); if (platform->set_os_boot_sensor) platform->set_os_boot_sensor(platform); -- cgit v1.2.1 From 120855d64d783aa3b3b0af23923da0169319c338 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 30 Apr 2015 14:38:43 +1000 Subject: discover: Display devices currently being parsed Send a short message to the UI to inform the user a device is being parsed for boot options. This helps slightly in environments when the UI appears well before devices are available for parsing, giving the user an indication that work is still being done. Signed-off-by: Samuel Mendoza-Jonas --- discover/device-handler.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/discover/device-handler.c b/discover/device-handler.c index 487f2e7..d95a37c 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -755,8 +755,16 @@ int device_handler_discover(struct device_handler *handler, struct discover_device *dev) { struct discover_context *ctx; + struct boot_status *status; int rc; + status = talloc_zero(handler, struct boot_status); + status->type = BOOT_STATUS_INFO; + status->message = talloc_asprintf(status, "Processing %s device %s", + device_type_display_name(dev->device->type), + dev->device->id); + boot_status(handler, status); + process_boot_option_queue(handler); /* create our context */ @@ -777,6 +785,11 @@ int device_handler_discover(struct device_handler *handler, device_handler_discover_context_commit(handler, ctx); out: + status->message = talloc_asprintf(status,"Processing %s complete\n", + dev->device->id); + boot_status(handler, status); + + talloc_free(status); talloc_free(ctx); return 0; @@ -787,6 +800,13 @@ int device_handler_dhcp(struct device_handler *handler, struct discover_device *dev, struct event *event) { struct discover_context *ctx; + struct boot_status *status; + + status = talloc_zero(handler, struct boot_status); + status->type = BOOT_STATUS_INFO; + status->message = talloc_asprintf(status, "Processing dhcp event on %s", + dev->device->id); + boot_status(handler, status); /* create our context */ ctx = device_handler_discover_context_create(handler, dev); @@ -796,6 +816,11 @@ int device_handler_dhcp(struct device_handler *handler, device_handler_discover_context_commit(handler, ctx); + status->message = talloc_asprintf(status,"Processing %s complete\n", + dev->device->id); + boot_status(handler, status); + + talloc_free(status); talloc_free(ctx); return 0; @@ -806,6 +831,12 @@ int device_handler_conf(struct device_handler *handler, struct discover_device *dev, struct pb_url *url) { struct discover_context *ctx; + struct boot_status *status; + + status = talloc_zero(handler, struct boot_status); + status->type = BOOT_STATUS_INFO; + status->message = talloc_asprintf(status, "Processing user config"); + boot_status(handler, status); /* create our context */ ctx = device_handler_discover_context_create(handler, dev); @@ -815,6 +846,11 @@ int device_handler_conf(struct device_handler *handler, device_handler_discover_context_commit(handler, ctx); + status->message = talloc_asprintf(status, + "Processing user config complete"); + boot_status(handler, status); + + talloc_free(status); talloc_free(ctx); return 0; -- cgit v1.2.1 From a7307c0962b4de0e6459ea449939b79dfe1705cd Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Thu, 30 Apr 2015 16:38:31 +1000 Subject: discover: Fix whitespace in discover_handler_conf Signed-off-by: Samuel Mendoza-Jonas --- discover/device-handler.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/discover/device-handler.c b/discover/device-handler.c index d95a37c..64095f1 100644 --- a/discover/device-handler.c +++ b/discover/device-handler.c @@ -830,7 +830,7 @@ int device_handler_dhcp(struct device_handler *handler, int device_handler_conf(struct device_handler *handler, struct discover_device *dev, struct pb_url *url) { - struct discover_context *ctx; + struct discover_context *ctx; struct boot_status *status; status = talloc_zero(handler, struct boot_status); @@ -838,22 +838,22 @@ int device_handler_conf(struct device_handler *handler, status->message = talloc_asprintf(status, "Processing user config"); boot_status(handler, status); - /* create our context */ - ctx = device_handler_discover_context_create(handler, dev); - ctx->conf_url = url; + /* create our context */ + ctx = device_handler_discover_context_create(handler, dev); + ctx->conf_url = url; - iterate_parsers(ctx); + iterate_parsers(ctx); - device_handler_discover_context_commit(handler, ctx); + device_handler_discover_context_commit(handler, ctx); status->message = talloc_asprintf(status, "Processing user config complete"); boot_status(handler, status); talloc_free(status); - talloc_free(ctx); + talloc_free(ctx); - return 0; + return 0; } static struct discover_boot_option *find_boot_option_by_id( -- cgit v1.2.1 From a1b17961420c2d95a62d9003b75431f706e96753 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Fri, 8 May 2015 13:42:00 +1000 Subject: ui/ncurses: Toggle visibility of timeout help label Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c index 6164873..911559d 100644 --- a/ui/ncurses/nc-config.c +++ b/ui/ncurses/nc-config.c @@ -429,6 +429,7 @@ static void config_screen_layout_widgets(struct config_screen *screen) wh = widget_label_base(screen->widgets.timeout_help_l); widget_set_visible(wl, screen->autoboot_enabled); widget_set_visible(wf, screen->autoboot_enabled); + widget_set_visible(wh, screen->autoboot_enabled); if (screen->autoboot_enabled) { widget_set_visible(wh, screen->autoboot_enabled); widget_move(wl, y, screen->label_x); -- cgit v1.2.1 From 7df003a0d222ae08bff62de4fefff1cf56628123 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Mon, 20 Apr 2015 17:12:04 +1000 Subject: ui/ncurses: Properly reposition cursor after menu update The currently selected item in the main menu can be set to an item off the visible portion of the menu after the additional or removal of a boot option. Update the currently selected item and/or the current view such that the item remains in the visible area. Signed-off-by: Samuel Mendoza-Jonas --- ui/ncurses/nc-cui.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 2fc779e..56e7653 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -184,6 +184,7 @@ static void cui_boot_editor_on_exit(struct cui *cui, { struct pmenu *menu = cui->main; struct cui_opt_data *cod; + int idx, top, rows, cols; static int user_idx = 0; /* Was the edit cancelled? */ @@ -222,6 +223,22 @@ static void cui_boot_editor_on_exit(struct cui *cui, /* 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; @@ -229,7 +246,6 @@ static void cui_boot_editor_on_exit(struct cui *cui, 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); @@ -562,7 +578,7 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt, /* 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); @@ -586,6 +602,7 @@ static void cui_device_remove(struct device *dev, void *arg) 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); @@ -622,6 +639,15 @@ static void cui_device_remove(struct device *dev, void *arg) 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); -- cgit v1.2.1