From a3e3b66fbcb18174fc54ffd771ef3a38c9f8defd Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Wed, 21 Jan 2015 16:12:14 +1100 Subject: [PATCH] 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", -- 2.39.2