summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2015-06-26 11:36:19 +0800
committerJeremy Kerr <jk@ozlabs.org>2015-06-26 11:39:39 +0800
commit37b4861cb01bf6bd9da41aa1b311a87b0d26fc25 (patch)
tree3330d1958b04c9923de090273a4c0be4ee36e577
parent83a3159f19f237fa994d2c0a16f97f711d02472c (diff)
parent7df003a0d222ae08bff62de4fefff1cf56628123 (diff)
downloadpetitboot-37b4861cb01bf6bd9da41aa1b311a87b0d26fc25.zip
petitboot-37b4861cb01bf6bd9da41aa1b311a87b0d26fc25.tar.gz
Merge remote-tracking rbanch sammj/master
Conflicts: discover/platform-powerpc.c discover/ipmi.h Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
-rw-r--r--discover/device-handler.c107
-rw-r--r--discover/ipmi.h11
-rw-r--r--discover/platform-powerpc.c191
-rw-r--r--discover/platform.c49
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/pb-config/pb-config.c26
-rw-r--r--lib/pb-protocol/pb-protocol.c55
-rw-r--r--lib/types/types.c71
-rw-r--r--lib/types/types.h37
-rw-r--r--ui/ncurses/Makefile.am4
-rw-r--r--ui/ncurses/nc-config-help.c27
-rw-r--r--ui/ncurses/nc-config.c370
-rw-r--r--ui/ncurses/nc-cui.c64
-rw-r--r--ui/ncurses/nc-cui.h3
-rw-r--r--ui/ncurses/nc-scr.h1
-rw-r--r--ui/ncurses/nc-subset.c289
-rw-r--r--ui/ncurses/nc-subset.h36
-rw-r--r--ui/ncurses/nc-widgets.c373
-rw-r--r--ui/ncurses/nc-widgets.h19
-rw-r--r--utils/pb-config.c11
20 files changed, 1465 insertions, 280 deletions
diff --git a/discover/device-handler.c b/discover/device-handler.c
index f053713..64095f1 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;
}
@@ -750,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 */
@@ -772,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;
@@ -782,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);
@@ -791,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;
@@ -800,19 +830,30 @@ 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);
+ 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);
- 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(ctx);
+ talloc_free(status);
+ talloc_free(ctx);
- return 0;
+ return 0;
}
static struct discover_boot_option *find_boot_option_by_id(
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 <stdbool.h>
#include <stdint.h>
+#include <types/types.h>
+
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 bda9368..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);
};
@@ -49,6 +50,7 @@ static const char *known_params[] = {
"petitboot,network",
"petitboot,timeout",
"petitboot,bootdev",
+ "petitboot,bootdevs",
"petitboot,language",
"petitboot,debug?",
NULL,
@@ -388,30 +390,129 @@ 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)
-
{
+ 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,
@@ -537,16 +638,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,
@@ -567,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);
@@ -592,6 +725,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;
@@ -681,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;
}
@@ -711,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];
@@ -876,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);
diff --git a/discover/platform.c b/discover/platform.c
index 04798ac..74e2a82 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;
@@ -79,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,
@@ -126,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/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/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.c b/lib/types/types.c
new file mode 100644
index 0000000..95a3a48
--- /dev/null
+++ b/lib/types/types.c
@@ -0,0 +1,71 @@
+#include <string.h>
+#include <types/types.h>
+#include <i18n/i18n.h>
+
+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) {
+ 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..e5c7e3e 100644
--- a/lib/types/types.h
+++ b/lib/types/types.h
@@ -13,6 +13,21 @@ 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);
+
struct device {
char *id;
enum device_type type;
@@ -109,13 +124,15 @@ 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,
+ BOOT_DEVICE_UUID
+ } boot_type;
+ union {
+ enum device_type type;
+ char *uuid;
+ };
};
struct config {
@@ -123,10 +140,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/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-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 \
diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c
index c45df34..911559d 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 32
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,16 +63,25 @@ struct config_screen {
int network_config_y;
enum net_conf_type net_conf_type;
- enum autoboot_type autoboot_type;
+
+ bool autoboot_enabled;
+ bool ipmi_override;
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;
+ 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;
@@ -151,7 +156,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 +170,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 +199,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;
@@ -236,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
@@ -367,37 +386,75 @@ 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_select_base(screen->widgets.boot_device_f);
- if (screen->autoboot_type == AUTOBOOT_ONE) {
+ 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);
+
+ 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);
+ 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);
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);
}
+ 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));
@@ -500,15 +557,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->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;
- screen->autoboot_type = value;
+ 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 +661,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 +696,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 +703,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);
@@ -652,6 +790,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);
@@ -818,12 +971,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 +1015,9 @@ 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,
_("Petitboot System Configuration"));
screen->scr.frame.rtitle = NULL;
diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c
index 3f1e0a2..56e7653 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;
@@ -69,6 +70,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;
}
@@ -173,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? */
@@ -211,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;
@@ -218,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);
@@ -318,6 +345,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.
*/
@@ -528,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);
@@ -552,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);
@@ -588,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);
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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <talloc/talloc.h>
+#include <types/types.h>
+#include <i18n/i18n.h>
+#include <log/log.h>
+
+#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 */
diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c
index f821929..3daced1 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;
@@ -704,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;
}
@@ -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);
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",
OpenPOWER on IntegriCloud