diff options
Diffstat (limited to 'src/hci')
88 files changed, 1681 insertions, 859 deletions
diff --git a/src/hci/commands/autoboot_cmd.c b/src/hci/commands/autoboot_cmd.c index 56f39a1ce..a61333a9d 100644 --- a/src/hci/commands/autoboot_cmd.c +++ b/src/hci/commands/autoboot_cmd.c @@ -30,6 +30,7 @@ #include <usr/autoboot.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -73,9 +74,4 @@ static int autoboot_exec ( int argc, char **argv ) { } /** Booting commands */ -struct command autoboot_commands[] __command = { - { - .name = "autoboot", - .exec = autoboot_exec, - }, -}; +COMMAND ( autoboot, autoboot_exec ); diff --git a/src/hci/commands/cert_cmd.c b/src/hci/commands/cert_cmd.c index 24b18bf5c..ebd9a25cd 100644 --- a/src/hci/commands/cert_cmd.c +++ b/src/hci/commands/cert_cmd.c @@ -22,8 +22,10 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> +#include <string.h> #include <errno.h> #include <getopt.h> #include <ipxe/x509.h> @@ -288,17 +290,6 @@ static int certfree_exec ( int argc, char **argv ) { } /** Certificate management commands */ -struct command certmgmt_commands[] __command = { - { - .name = "certstat", - .exec = certstat_exec, - }, - { - .name = "certstore", - .exec = certstore_exec, - }, - { - .name = "certfree", - .exec = certfree_exec, - }, -}; +COMMAND ( certstat, certstat_exec ); +COMMAND ( certstore, certstore_exec ); +COMMAND ( certfree, certfree_exec ); diff --git a/src/hci/commands/config_cmd.c b/src/hci/commands/config_cmd.c index ad415e045..cc21ad3fe 100644 --- a/src/hci/commands/config_cmd.c +++ b/src/hci/commands/config_cmd.c @@ -31,6 +31,7 @@ #include <ipxe/settings_ui.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -79,7 +80,4 @@ static int config_exec ( int argc, char **argv ) { } /** Configuration UI commands */ -struct command config_command __command = { - .name = "config", - .exec = config_exec, -}; +COMMAND ( config, config_exec ); diff --git a/src/hci/commands/console_cmd.c b/src/hci/commands/console_cmd.c index bd5647261..479e14efc 100644 --- a/src/hci/commands/console_cmd.c +++ b/src/hci/commands/console_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -253,17 +254,6 @@ static int cpair_exec ( int argc, char **argv ) { } /** Console management commands */ -struct command console_commands[] __command = { - { - .name = "console", - .exec = console_exec, - }, - { - .name = "colour", - .exec = colour_exec, - }, - { - .name = "cpair", - .exec = cpair_exec, - }, -}; +COMMAND ( console, console_exec ); +COMMAND ( colour, colour_exec ); +COMMAND ( cpair, cpair_exec ); diff --git a/src/hci/commands/dhcp_cmd.c b/src/hci/commands/dhcp_cmd.c index 45a922b51..ccc115b87 100644 --- a/src/hci/commands/dhcp_cmd.c +++ b/src/hci/commands/dhcp_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <stdint.h> @@ -92,13 +93,5 @@ static int pxebs_exec ( int argc, char **argv ) { } /** DHCP management commands */ -struct command dhcp_commands[] __command = { - { - .name = "dhcp", - .exec = ifconf_exec, /* synonym for "ifconf" */ - }, - { - .name = "pxebs", - .exec = pxebs_exec, - }, -}; +COMMAND ( dhcp, ifconf_exec ); /* synonym for "ifconf" */ +COMMAND ( pxebs, pxebs_exec ); diff --git a/src/hci/commands/digest_cmd.c b/src/hci/commands/digest_cmd.c index 71308064f..4d7da0385 100644 --- a/src/hci/commands/digest_cmd.c +++ b/src/hci/commands/digest_cmd.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <string.h> @@ -26,10 +27,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/command.h> #include <ipxe/parseopt.h> #include <ipxe/image.h> +#include <ipxe/settings.h> #include <ipxe/crypto.h> #include <ipxe/md5.h> #include <ipxe/sha1.h> #include <usr/imgmgmt.h> +#include <hci/digest_cmd.h> /** @file * @@ -38,10 +41,16 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** "digest" options */ -struct digest_options {}; +struct digest_options { + /** Setting */ + struct named_setting setting; +}; /** "digest" option list */ -static struct option_descriptor digest_opts[] = {}; +static struct option_descriptor digest_opts[] = { + OPTION_DESC ( "set", 's', required_argument, struct digest_options, + setting, parse_autovivified_setting ), +}; /** "digest" command descriptor */ static struct command_descriptor digest_cmd = @@ -56,54 +65,62 @@ static struct command_descriptor digest_cmd = * @v digest Digest algorithm * @ret rc Return status code */ -static int digest_exec ( int argc, char **argv, - struct digest_algorithm *digest ) { +int digest_exec ( int argc, char **argv, struct digest_algorithm *digest ) { struct digest_options opts; struct image *image; - uint8_t digest_ctx[digest->ctxsize]; - uint8_t digest_out[digest->digestsize]; - uint8_t buf[128]; - size_t offset; - size_t len; - size_t frag_len; + uint8_t ctx[digest->ctxsize]; + uint8_t out[digest->digestsize]; + unsigned int j; int i; - unsigned j; int rc; /* Parse options */ if ( ( rc = parse_options ( argc, argv, &digest_cmd, &opts ) ) != 0 ) return rc; + /* Use default setting type, if not specified */ + if ( ! opts.setting.setting.type ) + opts.setting.setting.type = &setting_type_hexraw; + + /* Calculate digests for each image */ for ( i = optind ; i < argc ; i++ ) { /* Acquire image */ if ( ( rc = imgacquire ( argv[i], 0, &image ) ) != 0 ) - continue; - offset = 0; - len = image->len; - - /* calculate digest */ - digest_init ( digest, digest_ctx ); - while ( len ) { - frag_len = len; - if ( frag_len > sizeof ( buf ) ) - frag_len = sizeof ( buf ); - copy_from_user ( buf, image->data, offset, frag_len ); - digest_update ( digest, digest_ctx, buf, frag_len ); - len -= frag_len; - offset += frag_len; + return rc; + + /* Calculate digest */ + digest_init ( digest, ctx ); + digest_update ( digest, ctx, image->data, image->len ); + digest_final ( digest, ctx, out ); + + /* Display or store digest as directed */ + if ( opts.setting.settings ) { + + /* Store digest */ + if ( ( rc = store_setting ( opts.setting.settings, + &opts.setting.setting, out, + sizeof ( out ) ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + opts.setting.setting.name, + strerror ( rc ) ); + return rc; + } + + } else { + + /* Print digest */ + for ( j = 0 ; j < sizeof ( out ) ; j++ ) + printf ( "%02x", out[j] ); + printf ( " %s\n", image->name ); } - digest_final ( digest, digest_ctx, digest_out ); - - for ( j = 0 ; j < sizeof ( digest_out ) ; j++ ) - printf ( "%02x", digest_out[j] ); - - printf ( " %s\n", image->name ); } return 0; } +/* Include "md5sum" and "sha1sum" commands unconditionally */ + static int md5sum_exec ( int argc, char **argv ) { return digest_exec ( argc, argv, &md5_algorithm ); } @@ -112,12 +129,9 @@ static int sha1sum_exec ( int argc, char **argv ) { return digest_exec ( argc, argv, &sha1_algorithm ); } -struct command md5sum_command __command = { - .name = "md5sum", - .exec = md5sum_exec, -}; +COMMAND ( md5sum, md5sum_exec ); +COMMAND ( sha1sum, sha1sum_exec ); -struct command sha1sum_command __command = { - .name = "sha1sum", - .exec = sha1sum_exec, -}; +/* Drag in commands for any other enabled algorithms */ +REQUIRING_SYMBOL ( digest_exec ); +REQUIRE_OBJECT ( config_digest_cmd ); diff --git a/src/hci/commands/menu_cmd.c b/src/hci/commands/dynui_cmd.c index c8692f1a7..b70211050 100644 --- a/src/hci/commands/menu_cmd.c +++ b/src/hci/commands/dynui_cmd.c @@ -22,10 +22,11 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * - * Menu commands + * Dynamic user interface commands * */ @@ -34,7 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> #include <getopt.h> -#include <ipxe/menu.h> +#include <ipxe/dynui.h> #include <ipxe/command.h> #include <ipxe/parseopt.h> #include <ipxe/settings.h> @@ -42,42 +43,42 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FEATURE ( FEATURE_MISC, "Menu", DHCP_EB_FEATURE_MENU, 1 ); -/** "menu" options */ -struct menu_options { +/** "dynui" options */ +struct dynui_options { /** Name */ char *name; /** Delete */ int delete; }; -/** "menu" option list */ -static struct option_descriptor menu_opts[] = { +/** "dynui" option list */ +static struct option_descriptor dynui_opts[] = { OPTION_DESC ( "name", 'n', required_argument, - struct menu_options, name, parse_string ), + struct dynui_options, name, parse_string ), OPTION_DESC ( "delete", 'd', no_argument, - struct menu_options, delete, parse_flag ), + struct dynui_options, delete, parse_flag ), }; -/** "menu" command descriptor */ -static struct command_descriptor menu_cmd = - COMMAND_DESC ( struct menu_options, menu_opts, 0, MAX_ARGUMENTS, +/** "dynui" command descriptor */ +static struct command_descriptor dynui_cmd = + COMMAND_DESC ( struct dynui_options, dynui_opts, 0, MAX_ARGUMENTS, "[<title>]" ); /** - * The "menu" command + * The "dynui" command * * @v argc Argument count * @v argv Argument list * @ret rc Return status code */ -static int menu_exec ( int argc, char **argv ) { - struct menu_options opts; - struct menu *menu; +static int dynui_exec ( int argc, char **argv ) { + struct dynui_options opts; + struct dynamic_ui *dynui; char *title; int rc; /* Parse options */ - if ( ( rc = parse_options ( argc, argv, &menu_cmd, &opts ) ) != 0 ) + if ( ( rc = parse_options ( argc, argv, &dynui_cmd, &opts ) ) != 0 ) goto err_parse_options; /* Parse title */ @@ -87,21 +88,21 @@ static int menu_exec ( int argc, char **argv ) { goto err_parse_title; } - /* Create menu */ - menu = create_menu ( opts.name, title ); - if ( ! menu ) { + /* Create dynamic user interface */ + dynui = create_dynui ( opts.name, title ); + if ( ! dynui ) { rc = -ENOMEM; - goto err_create_menu; + goto err_create_dynui; } - /* Destroy menu, if applicable */ + /* Destroy dynamic user interface, if applicable */ if ( opts.delete ) - destroy_menu ( menu ); + destroy_dynui ( dynui ); /* Success */ rc = 0; - err_create_menu: + err_create_dynui: free ( title ); err_parse_title: err_parse_options: @@ -110,12 +111,14 @@ static int menu_exec ( int argc, char **argv ) { /** "item" options */ struct item_options { - /** Menu name */ - char *menu; + /** Dynamic user interface name */ + char *dynui; /** Shortcut key */ unsigned int key; /** Use as default */ int is_default; + /** Value is a secret */ + int is_secret; /** Use as a separator */ int is_gap; /** Don't show; hotkey only */ @@ -125,11 +128,15 @@ struct item_options { /** "item" option list */ static struct option_descriptor item_opts[] = { OPTION_DESC ( "menu", 'm', required_argument, - struct item_options, menu, parse_string ), + struct item_options, dynui, parse_string ), + OPTION_DESC ( "form", 'f', required_argument, + struct item_options, dynui, parse_string ), OPTION_DESC ( "key", 'k', required_argument, struct item_options, key, parse_key ), OPTION_DESC ( "default", 'd', no_argument, struct item_options, is_default, parse_flag ), + OPTION_DESC ( "secret", 's', no_argument, + struct item_options, is_secret, parse_flag ), OPTION_DESC ( "gap", 'g', no_argument, struct item_options, is_gap, parse_flag ), OPTION_DESC ( "hidden", 'i', no_argument, @@ -139,7 +146,7 @@ static struct option_descriptor item_opts[] = { /** "item" command descriptor */ static struct command_descriptor item_cmd = COMMAND_DESC ( struct item_options, item_opts, 0, MAX_ARGUMENTS, - "[<label> [<text>]]" ); + "[<name> [<text>]]" ); /** * The "item" command @@ -150,9 +157,10 @@ static struct command_descriptor item_cmd = */ static int item_exec ( int argc, char **argv ) { struct item_options opts; - struct menu *menu; - struct menu_item *item; - char *label = NULL; + struct dynamic_ui *dynui; + struct dynamic_item *item; + unsigned int flags = 0; + char *name = NULL; char *text = NULL; int rc; @@ -160,12 +168,12 @@ static int item_exec ( int argc, char **argv ) { if ( ( rc = parse_options ( argc, argv, &item_cmd, &opts ) ) != 0 ) goto err_parse_options; - /* Parse label, if present */ + /* Parse name, if present */ if ( ! opts.is_gap ) - label = argv[optind++]; /* May be NULL */ + name = argv[optind++]; /* May be NULL */ /* Hidden entry without label and shortcut is pointless */ - if ( opts.is_hidden && ( ! opts.key || ! label || ! *label ) ) { + if ( opts.is_hidden && ( ! opts.key || ! name || ! *name ) ) { rc = -EINVAL; goto err_invalid_argument; } @@ -179,23 +187,29 @@ static int item_exec ( int argc, char **argv ) { } } - /* Identify menu */ - if ( ( rc = parse_menu ( opts.menu, &menu ) ) != 0 ) - goto err_parse_menu; - - /* Add menu item */ - item = add_menu_item ( menu, label, ( text ? text : "" ), - opts.key, opts.is_default, opts.is_hidden ); + /* Identify dynamic user interface */ + if ( ( rc = parse_dynui ( opts.dynui, &dynui ) ) != 0 ) + goto err_parse_dynui; + + /* Add dynamic user interface item */ + if ( opts.is_default ) + flags |= DYNUI_DEFAULT; + if ( opts.is_secret ) + flags |= DYNUI_SECRET; + if ( opts.is_hidden ) + flags |= DYNUI_HIDDEN; + item = add_dynui_item ( dynui, name, ( text ? text : "" ), flags, + opts.key ); if ( ! item ) { rc = -ENOMEM; - goto err_add_menu_item; + goto err_add_dynui_item; } /* Success */ rc = 0; - err_add_menu_item: - err_parse_menu: + err_add_dynui_item: + err_parse_dynui: free ( text ); err_parse_text: err_invalid_argument: @@ -205,24 +219,28 @@ static int item_exec ( int argc, char **argv ) { /** "choose" options */ struct choose_options { - /** Menu name */ - char *menu; - /** Timeout */ + /** Dynamic user interface name */ + char *dynui; + /** Initial timeout */ unsigned long timeout; + /** Post-activity timeout */ + unsigned long retimeout; /** Default selection */ char *select; - /** Keep menu */ + /** Keep dynamic user interface */ int keep; }; /** "choose" option list */ static struct option_descriptor choose_opts[] = { OPTION_DESC ( "menu", 'm', required_argument, - struct choose_options, menu, parse_string ), + struct choose_options, dynui, parse_string ), OPTION_DESC ( "default", 'd', required_argument, struct choose_options, select, parse_string ), OPTION_DESC ( "timeout", 't', required_argument, struct choose_options, timeout, parse_timeout ), + OPTION_DESC ( "retimeout", 'r', required_argument, + struct choose_options, retimeout, parse_timeout ), OPTION_DESC ( "keep", 'k', no_argument, struct choose_options, keep, parse_flag ), }; @@ -241,8 +259,8 @@ static struct command_descriptor choose_cmd = static int choose_exec ( int argc, char **argv ) { struct choose_options opts; struct named_setting setting; - struct menu *menu; - struct menu_item *item; + struct dynamic_ui *dynui; + struct dynamic_item *item; int rc; /* Parse options */ @@ -254,12 +272,13 @@ static int choose_exec ( int argc, char **argv ) { &setting ) ) != 0 ) goto err_parse_setting; - /* Identify menu */ - if ( ( rc = parse_menu ( opts.menu, &menu ) ) != 0 ) - goto err_parse_menu; + /* Identify dynamic user interface */ + if ( ( rc = parse_dynui ( opts.dynui, &dynui ) ) != 0 ) + goto err_parse_dynui; - /* Show menu */ - if ( ( rc = show_menu ( menu, opts.timeout, opts.select, &item ) ) != 0) + /* Show as menu */ + if ( ( rc = show_menu ( dynui, opts.timeout, opts.retimeout, + opts.select, &item ) ) != 0 ) goto err_show_menu; /* Apply default type if necessary */ @@ -268,7 +287,7 @@ static int choose_exec ( int argc, char **argv ) { /* Store setting */ if ( ( rc = storef_setting ( setting.settings, &setting.setting, - item->label ) ) != 0 ) { + item->name ) ) != 0 ) { printf ( "Could not store \"%s\": %s\n", setting.setting.name, strerror ( rc ) ); goto err_store; @@ -279,27 +298,74 @@ static int choose_exec ( int argc, char **argv ) { err_store: err_show_menu: - /* Destroy menu, if applicable */ + /* Destroy dynamic user interface, if applicable */ if ( ! opts.keep ) - destroy_menu ( menu ); - err_parse_menu: + destroy_dynui ( dynui ); + err_parse_dynui: err_parse_setting: err_parse_options: return rc; } -/** Menu commands */ -struct command menu_commands[] __command = { - { - .name = "menu", - .exec = menu_exec, - }, - { - .name = "item", - .exec = item_exec, - }, - { - .name = "choose", - .exec = choose_exec, - }, +/** "present" options */ +struct present_options { + /** Dynamic user interface name */ + char *dynui; + /** Keep dynamic user interface */ + int keep; }; + +/** "present" option list */ +static struct option_descriptor present_opts[] = { + OPTION_DESC ( "form", 'f', required_argument, + struct present_options, dynui, parse_string ), + OPTION_DESC ( "keep", 'k', no_argument, + struct present_options, keep, parse_flag ), +}; + +/** "present" command descriptor */ +static struct command_descriptor present_cmd = + COMMAND_DESC ( struct present_options, present_opts, 0, 0, NULL ); + +/** + * The "present" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int present_exec ( int argc, char **argv ) { + struct present_options opts; + struct dynamic_ui *dynui; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &present_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Identify dynamic user interface */ + if ( ( rc = parse_dynui ( opts.dynui, &dynui ) ) != 0 ) + goto err_parse_dynui; + + /* Show as form */ + if ( ( rc = show_form ( dynui ) ) != 0 ) + goto err_show_form; + + /* Success */ + rc = 0; + + err_show_form: + /* Destroy dynamic user interface, if applicable */ + if ( ! opts.keep ) + destroy_dynui ( dynui ); + err_parse_dynui: + err_parse_options: + return rc; +} + +/** Dynamic user interface commands */ +COMMAND ( menu, dynui_exec ); +COMMAND ( form, dynui_exec ); +COMMAND ( item, item_exec ); +COMMAND ( choose, choose_exec ); +COMMAND ( present, present_exec ); diff --git a/src/hci/commands/efimap_cmd.c b/src/hci/commands/efimap_cmd.c index c183cbc30..7e8bdb19f 100644 --- a/src/hci/commands/efimap_cmd.c +++ b/src/hci/commands/efimap_cmd.c @@ -69,9 +69,4 @@ static int efimap_exec ( int argc, char **argv ) { } /** EFIMAP command */ -struct command efimap_commands[] __command = { - { - .name = "efimap", - .exec = efimap_exec, - }, -}; +COMMAND ( efimap, efimap_exec ); diff --git a/src/hci/commands/fcmgmt_cmd.c b/src/hci/commands/fcmgmt_cmd.c index 97f10f4dd..c03ebb05f 100644 --- a/src/hci/commands/fcmgmt_cmd.c +++ b/src/hci/commands/fcmgmt_cmd.c @@ -207,13 +207,5 @@ static int fcels_exec ( int argc, char **argv ) { } /** Fibre Channel management commands */ -struct command fcmgmt_commands[] __command = { - { - .name = "fcstat", - .exec = fcstat_exec, - }, - { - .name = "fcels", - .exec = fcels_exec, - }, -}; +COMMAND ( fcstat, fcstat_exec ); +COMMAND ( fcels, fcels_exec ); diff --git a/src/hci/commands/fdt_cmd.c b/src/hci/commands/fdt_cmd.c new file mode 100644 index 000000000..7cd39279b --- /dev/null +++ b/src/hci/commands/fdt_cmd.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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; either version 2 of the + * License, or any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <getopt.h> +#include <ipxe/command.h> +#include <ipxe/parseopt.h> +#include <usr/imgmgmt.h> +#include <usr/fdtmgmt.h> + +/** @file + * + * Flattened Device Tree commands + * + */ + +/** "fdt" options */ +struct fdt_options { + /** Download timeout */ + unsigned long timeout; +}; + +/** "fdt" option list */ +static struct option_descriptor fdt_opts[] = { + OPTION_DESC ( "timeout", 't', required_argument, + struct fdt_options, timeout, parse_timeout ), +}; + +/** "fdt" command descriptor */ +static struct command_descriptor fdt_cmd = + COMMAND_DESC ( struct fdt_options, fdt_opts, 0, 1, "[<uri>]" ); + +/** + * The "fdt" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int fdt_exec ( int argc, char **argv ) { + struct fdt_options opts; + struct image *image = NULL; + char *name_uri; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &fdt_cmd, &opts ) ) != 0 ) + goto err_parse; + + /* Parse name/URI string */ + name_uri = argv[optind]; + + /* Acquire image, if applicable */ + if ( name_uri && ( ( rc = imgacquire ( name_uri, opts.timeout, + &image ) ) != 0 ) ) { + goto err_image; + } + + /* (Un)register as FDT */ + if ( ( rc = imgfdt ( image ) ) != 0 ) + goto err_fdt; + + err_fdt: + err_image: + err_parse: + return rc; +} + +/** Flattened Device Tree commands */ +COMMAND ( fdt, fdt_exec ); diff --git a/src/hci/commands/gdbstub_cmd.c b/src/hci/commands/gdbstub_cmd.c index c4a831e7a..ba5edde3a 100644 --- a/src/hci/commands/gdbstub_cmd.c +++ b/src/hci/commands/gdbstub_cmd.c @@ -107,9 +107,4 @@ static int gdbstub_exec ( int argc, char **argv ) { } /** GDB stub commands */ -struct command gdbstub_commands[] __command = { - { - .name = "gdbstub", - .exec = gdbstub_exec, - }, -}; +COMMAND ( gdbstub, gdbstub_exec ); diff --git a/src/hci/commands/ibmgmt_cmd.c b/src/hci/commands/ibmgmt_cmd.c index 1154d749e..be8b58cc2 100644 --- a/src/hci/commands/ibmgmt_cmd.c +++ b/src/hci/commands/ibmgmt_cmd.c @@ -71,9 +71,4 @@ static int ibstat_exec ( int argc, char **argv ) { } /** Infiniband commands */ -struct command ibmgmt_commands[] __command = { - { - .name = "ibstat", - .exec = ibstat_exec, - }, -}; +COMMAND ( ibstat, ibstat_exec ); diff --git a/src/hci/commands/ifmgmt_cmd.c b/src/hci/commands/ifmgmt_cmd.c index 591cb3da8..f4b9fef3a 100644 --- a/src/hci/commands/ifmgmt_cmd.c +++ b/src/hci/commands/ifmgmt_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <errno.h> @@ -303,25 +304,8 @@ static int iflinkwait_exec ( int argc, char **argv ) { } /** Interface management commands */ -struct command ifmgmt_commands[] __command = { - { - .name = "ifopen", - .exec = ifopen_exec, - }, - { - .name = "ifclose", - .exec = ifclose_exec, - }, - { - .name = "ifstat", - .exec = ifstat_exec, - }, - { - .name = "ifconf", - .exec = ifconf_exec, - }, - { - .name = "iflinkwait", - .exec = iflinkwait_exec, - }, -}; +COMMAND ( ifopen, ifopen_exec ); +COMMAND ( ifclose, ifclose_exec ); +COMMAND ( ifstat, ifstat_exec ); +COMMAND ( ifconf, ifconf_exec ); +COMMAND ( iflinkwait, iflinkwait_exec ); diff --git a/src/hci/commands/image_archive_cmd.c b/src/hci/commands/image_archive_cmd.c index a2212aecf..6b907830e 100644 --- a/src/hci/commands/image_archive_cmd.c +++ b/src/hci/commands/image_archive_cmd.c @@ -97,9 +97,4 @@ static int imgextract_exec ( int argc, char **argv ) { } /** Archive image commands */ -struct command image_archive_commands[] __command = { - { - .name = "imgextract", - .exec = imgextract_exec, - }, -}; +COMMAND ( imgextract, imgextract_exec ); diff --git a/src/hci/commands/image_cmd.c b/src/hci/commands/image_cmd.c index bf97b4deb..aaed0ea9b 100644 --- a/src/hci/commands/image_cmd.c +++ b/src/hci/commands/image_cmd.c @@ -22,10 +22,12 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <errno.h> #include <getopt.h> #include <ipxe/image.h> @@ -392,54 +394,22 @@ static int imgfree_exec ( int argc, char **argv ) { return imgmulti_exec ( argc, argv, unregister_image ); } -/** Image management commands */ -struct command image_commands[] __command = { - { - .name = "imgfetch", - .exec = imgfetch_exec, - }, - { - .name = "module", - .exec = imgfetch_exec, /* synonym for "imgfetch" */ - }, - { - .name = "initrd", - .exec = imgfetch_exec, /* synonym for "imgfetch" */ - }, - { - .name = "kernel", - .exec = imgselect_exec, /* synonym for "imgselect" */ - }, - { - .name = "chain", - .exec = imgexec_exec, /* synonym for "imgexec" */ - }, - { - .name = "imgselect", - .exec = imgselect_exec, - }, - { - .name = "imgload", - .exec = imgselect_exec, /* synonym for "imgselect" */ - }, - { - .name = "imgargs", - .exec = imgargs_exec, - }, - { - .name = "imgexec", - .exec = imgexec_exec, - }, - { - .name = "boot", /* synonym for "imgexec" */ - .exec = imgexec_exec, - }, - { - .name = "imgstat", - .exec = imgstat_exec, - }, - { - .name = "imgfree", - .exec = imgfree_exec, - }, -}; +/* "imgfetch" and synonyms */ +COMMAND ( imgfetch, imgfetch_exec ); +COMMAND ( module, imgfetch_exec ); +COMMAND ( initrd, imgfetch_exec ); + +/* "imgselect" and synonyms */ +COMMAND ( imgselect, imgselect_exec ); +COMMAND ( imgload, imgselect_exec ); +COMMAND ( kernel, imgselect_exec ); + +/* "imgexec" and synonyms */ +COMMAND ( imgexec, imgexec_exec ); +COMMAND ( chain, imgexec_exec ); +COMMAND ( boot, imgexec_exec ); + +/* Other image management commands */ +COMMAND ( imgargs, imgargs_exec ); +COMMAND ( imgstat, imgstat_exec ); +COMMAND ( imgfree, imgfree_exec ); diff --git a/src/hci/commands/image_crypt_cmd.c b/src/hci/commands/image_crypt_cmd.c new file mode 100644 index 000000000..54568cc28 --- /dev/null +++ b/src/hci/commands/image_crypt_cmd.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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; either version 2 of the + * License, or any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <getopt.h> +#include <ipxe/image.h> +#include <ipxe/command.h> +#include <ipxe/parseopt.h> +#include <usr/imgmgmt.h> +#include <usr/imgcrypt.h> + +/** @file + * + * Image encryption management commands + * + */ + +/** "imgdecrypt" options */ +struct imgdecrypt_options { + /** Decrypted image name */ + char *name; + /** Keep envelope after decryption */ + int keep; + /** Download timeout */ + unsigned long timeout; +}; + +/** "imgdecrypt" option list */ +static struct option_descriptor imgdecrypt_opts[] = { + OPTION_DESC ( "name", 'n', required_argument, + struct imgdecrypt_options, name, parse_string ), + OPTION_DESC ( "keep", 'k', no_argument, + struct imgdecrypt_options, keep, parse_flag ), + OPTION_DESC ( "timeout", 't', required_argument, + struct imgdecrypt_options, timeout, parse_timeout), +}; + +/** "imgdecrypt" command descriptor */ +static struct command_descriptor imgdecrypt_cmd = + COMMAND_DESC ( struct imgdecrypt_options, imgdecrypt_opts, 2, 2, + "<uri|image> <envelope uri|image>" ); + +/** + * The "imgdecrypt" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int imgdecrypt_exec ( int argc, char **argv ) { + struct imgdecrypt_options opts; + const char *image_name_uri; + const char *envelope_name_uri; + struct image *image; + struct image *envelope; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &imgdecrypt_cmd, &opts ) ) != 0) + return rc; + + /* Parse image name/URI string */ + image_name_uri = argv[optind]; + + /* Parse envelope name/URI string */ + envelope_name_uri = argv[ optind + 1 ]; + + /* Acquire the image */ + if ( ( rc = imgacquire ( image_name_uri, opts.timeout, &image ) ) != 0 ) + goto err_acquire_image; + + /* Acquire the envelope image */ + if ( ( rc = imgacquire ( envelope_name_uri, opts.timeout, + &envelope ) ) != 0 ) + goto err_acquire_envelope; + + /* Decrypt image */ + if ( ( rc = imgdecrypt ( image, envelope, opts.name ) ) != 0 ) { + printf ( "Could not decrypt: %s\n", strerror ( rc ) ); + goto err_decrypt; + } + + /* Success */ + rc = 0; + + err_decrypt: + /* Discard envelope unless --keep was specified */ + if ( ! opts.keep ) + unregister_image ( envelope ); + err_acquire_envelope: + err_acquire_image: + return rc; +} + +/** Image encryption management commands */ +COMMAND ( imgdecrypt, imgdecrypt_exec ); diff --git a/src/hci/commands/image_mem_cmd.c b/src/hci/commands/image_mem_cmd.c index c8bfab1ad..60c0bf92a 100644 --- a/src/hci/commands/image_mem_cmd.c +++ b/src/hci/commands/image_mem_cmd.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <getopt.h> +#include <ipxe/uaccess.h> #include <ipxe/command.h> #include <ipxe/parseopt.h> #include <usr/imgmgmt.h> @@ -81,16 +82,11 @@ static int imgmem_exec ( int argc, char **argv ) { return rc; /* Create image */ - if ( ( rc = imgmem ( opts.name, phys_to_user ( data ), len ) ) != 0 ) + if ( ( rc = imgmem ( opts.name, phys_to_virt ( data ), len ) ) != 0 ) return rc; return 0; } /** Read memory command */ -struct command imgmem_commands[] __command = { - { - .name = "imgmem", - .exec = imgmem_exec, - }, -}; +COMMAND ( imgmem, imgmem_exec ); diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index b34378f93..a8ec5784e 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -22,9 +22,11 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdio.h> +#include <string.h> #include <getopt.h> #include <ipxe/image.h> #include <ipxe/command.h> @@ -162,13 +164,5 @@ static int imgverify_exec ( int argc, char **argv ) { } /** Image trust management commands */ -struct command image_trust_commands[] __command = { - { - .name = "imgtrust", - .exec = imgtrust_exec, - }, - { - .name = "imgverify", - .exec = imgverify_exec, - }, -}; +COMMAND ( imgtrust, imgtrust_exec ); +COMMAND ( imgverify, imgverify_exec ); diff --git a/src/hci/commands/ipstat_cmd.c b/src/hci/commands/ipstat_cmd.c index 763e4dfd6..fc454c57d 100644 --- a/src/hci/commands/ipstat_cmd.c +++ b/src/hci/commands/ipstat_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <getopt.h> @@ -66,9 +67,4 @@ static int ipstat_exec ( int argc, char **argv ) { } /** Routing table management commands */ -struct command ipstat_commands[] __command = { - { - .name = "ipstat", - .exec = ipstat_exec, - }, -}; +COMMAND ( ipstat, ipstat_exec ); diff --git a/src/hci/commands/iwmgmt_cmd.c b/src/hci/commands/iwmgmt_cmd.c index b61ee8c7b..b430353d9 100644 --- a/src/hci/commands/iwmgmt_cmd.c +++ b/src/hci/commands/iwmgmt_cmd.c @@ -113,13 +113,5 @@ static int iwlist_exec ( int argc, char **argv ) { } /** Wireless interface management commands */ -struct command iwmgmt_commands[] __command = { - { - .name = "iwstat", - .exec = iwstat_exec, - }, - { - .name = "iwlist", - .exec = iwlist_exec, - }, -}; +COMMAND ( iwstat, iwstat_exec ); +COMMAND ( iwlist, iwlist_exec ); diff --git a/src/hci/commands/login_cmd.c b/src/hci/commands/login_cmd.c index 6ed76c6f2..faa8d9852 100644 --- a/src/hci/commands/login_cmd.c +++ b/src/hci/commands/login_cmd.c @@ -29,6 +29,7 @@ #include <ipxe/login_ui.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -78,7 +79,4 @@ static int login_exec ( int argc, char **argv ) { } /** Login commands */ -struct command login_command __command = { - .name = "login", - .exec = login_exec, -}; +COMMAND ( login, login_exec ); diff --git a/src/hci/commands/lotest_cmd.c b/src/hci/commands/lotest_cmd.c index 393b3c36e..ee3b0d3b5 100644 --- a/src/hci/commands/lotest_cmd.c +++ b/src/hci/commands/lotest_cmd.c @@ -100,7 +100,4 @@ static int lotest_exec ( int argc, char **argv ) { } /** Loopback testing commands */ -struct command lotest_command __command = { - .name = "lotest", - .exec = lotest_exec, -}; +COMMAND ( lotest, lotest_exec ); diff --git a/src/hci/commands/neighbour_cmd.c b/src/hci/commands/neighbour_cmd.c index 816e87357..870024ee0 100644 --- a/src/hci/commands/neighbour_cmd.c +++ b/src/hci/commands/neighbour_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -65,9 +66,4 @@ static int nstat_exec ( int argc, char **argv ) { } /** Neighbour management commands */ -struct command neighbour_commands[] __command = { - { - .name = "nstat", - .exec = nstat_exec, - }, -}; +COMMAND ( nstat, nstat_exec ); diff --git a/src/hci/commands/nslookup_cmd.c b/src/hci/commands/nslookup_cmd.c index 265afdc3d..b13127dd4 100644 --- a/src/hci/commands/nslookup_cmd.c +++ b/src/hci/commands/nslookup_cmd.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <getopt.h> @@ -73,7 +74,4 @@ static int nslookup_exec ( int argc, char **argv ) { } /** The "nslookup" command */ -struct command nslookup_command __command = { - .name = "nslookup", - .exec = nslookup_exec, -}; +COMMAND ( nslookup, nslookup_exec ); diff --git a/src/hci/commands/ntp_cmd.c b/src/hci/commands/ntp_cmd.c index 8f741a512..d7604227a 100644 --- a/src/hci/commands/ntp_cmd.c +++ b/src/hci/commands/ntp_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <string.h> @@ -75,7 +76,4 @@ static int ntp_exec ( int argc, char **argv ) { } /** NTP command */ -struct command ntp_command __command = { - .name = "ntp", - .exec = ntp_exec, -}; +COMMAND ( ntp, ntp_exec ); diff --git a/src/hci/commands/nvo_cmd.c b/src/hci/commands/nvo_cmd.c index 6ad7e7428..70086afce 100644 --- a/src/hci/commands/nvo_cmd.c +++ b/src/hci/commands/nvo_cmd.c @@ -34,6 +34,7 @@ #include <readline/readline.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -356,25 +357,8 @@ static int inc_exec ( int argc, char **argv ) { } /** Non-volatile option commands */ -struct command nvo_commands[] __command = { - { - .name = "show", - .exec = show_exec, - }, - { - .name = "set", - .exec = set_exec, - }, - { - .name = "clear", - .exec = clear_exec, - }, - { - .name = "read", - .exec = read_exec, - }, - { - .name = "inc", - .exec = inc_exec, - }, -}; +COMMAND ( show, show_exec ); +COMMAND ( set, set_exec ); +COMMAND ( clear, clear_exec ); +COMMAND ( read, read_exec ); +COMMAND ( inc, inc_exec ); diff --git a/src/hci/commands/param_cmd.c b/src/hci/commands/param_cmd.c index dad99f840..ed57c5eaa 100644 --- a/src/hci/commands/param_cmd.c +++ b/src/hci/commands/param_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -163,13 +164,5 @@ static int param_exec ( int argc, char **argv ) { } /** Request parameter commands */ -struct command param_commands[] __command = { - { - .name = "params", - .exec = params_exec, - }, - { - .name = "param", - .exec = param_exec, - }, -}; +COMMAND ( params, params_exec ); +COMMAND ( param, param_exec ); diff --git a/src/hci/commands/pci_cmd.c b/src/hci/commands/pci_cmd.c index 5bae66fbe..2e9505752 100644 --- a/src/hci/commands/pci_cmd.c +++ b/src/hci/commands/pci_cmd.c @@ -22,6 +22,7 @@ */ #include <stdio.h> +#include <string.h> #include <errno.h> #include <getopt.h> #include <ipxe/pci.h> @@ -29,6 +30,7 @@ #include <ipxe/parseopt.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -114,9 +116,4 @@ static int pciscan_exec ( int argc, char **argv ) { } /** PCI commands */ -struct command pci_commands[] __command = { - { - .name = "pciscan", - .exec = pciscan_exec, - }, -}; +COMMAND ( pciscan, pciscan_exec ); diff --git a/src/hci/commands/ping_cmd.c b/src/hci/commands/ping_cmd.c index ab271e75a..e132fb457 100644 --- a/src/hci/commands/ping_cmd.c +++ b/src/hci/commands/ping_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdlib.h> @@ -107,7 +108,4 @@ static int ping_exec ( int argc, char **argv ) { } /** Ping command */ -struct command ping_command __command = { - .name = "ping", - .exec = ping_exec, -}; +COMMAND ( ping, ping_exec ); diff --git a/src/hci/commands/poweroff_cmd.c b/src/hci/commands/poweroff_cmd.c index afdf12dde..63aeb3d5b 100644 --- a/src/hci/commands/poweroff_cmd.c +++ b/src/hci/commands/poweroff_cmd.c @@ -29,6 +29,7 @@ #include <ipxe/reboot.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -70,7 +71,4 @@ static int poweroff_exec ( int argc, char **argv ) { } /** "poweroff" command */ -struct command poweroff_command __command = { - .name = "poweroff", - .exec = poweroff_exec, -}; +COMMAND ( poweroff, poweroff_exec ); diff --git a/src/hci/commands/profstat_cmd.c b/src/hci/commands/profstat_cmd.c index dc6f649e3..3303ebcf3 100644 --- a/src/hci/commands/profstat_cmd.c +++ b/src/hci/commands/profstat_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <getopt.h> @@ -66,9 +67,4 @@ static int profstat_exec ( int argc, char **argv ) { } /** Profiling commands */ -struct command profstat_commands[] __command = { - { - .name = "profstat", - .exec = profstat_exec, - }, -}; +COMMAND ( profstat, profstat_exec ); diff --git a/src/hci/commands/reboot_cmd.c b/src/hci/commands/reboot_cmd.c index 45d54cc2c..daef92dc0 100644 --- a/src/hci/commands/reboot_cmd.c +++ b/src/hci/commands/reboot_cmd.c @@ -27,6 +27,7 @@ #include <ipxe/reboot.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -38,12 +39,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct reboot_options { /** Perform a warm reboot */ int warm; + /** Reboot to firmware setup */ + int setup; }; /** "reboot" option list */ static struct option_descriptor reboot_opts[] = { OPTION_DESC ( "warm", 'w', no_argument, struct reboot_options, warm, parse_flag ), + OPTION_DESC ( "setup", 's', no_argument, + struct reboot_options, setup, parse_flag ), }; /** "reboot" command descriptor */ @@ -59,6 +64,7 @@ static struct command_descriptor reboot_cmd = */ static int reboot_exec ( int argc, char **argv ) { struct reboot_options opts; + int flags = 0; int rc; /* Parse options */ @@ -66,13 +72,14 @@ static int reboot_exec ( int argc, char **argv ) { return rc; /* Reboot system */ - reboot ( opts.warm ); + if ( opts.warm ) + flags |= REBOOT_WARM; + if ( opts.setup ) + flags |= REBOOT_SETUP; + reboot ( flags ); return 0; } /** "reboot" command */ -struct command reboot_command __command = { - .name = "reboot", - .exec = reboot_exec, -}; +COMMAND ( reboot, reboot_exec ); diff --git a/src/hci/commands/route_cmd.c b/src/hci/commands/route_cmd.c index 8aa535363..ff841ec15 100644 --- a/src/hci/commands/route_cmd.c +++ b/src/hci/commands/route_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <getopt.h> @@ -66,9 +67,4 @@ static int route_exec ( int argc, char **argv ) { } /** Routing table management commands */ -struct command route_commands[] __command = { - { - .name = "route", - .exec = route_exec, - }, -}; +COMMAND ( route, route_exec ); diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c index 6ab9e8844..7bc60e641 100644 --- a/src/hci/commands/sanboot_cmd.c +++ b/src/hci/commands/sanboot_cmd.c @@ -32,6 +32,7 @@ #include <usr/autoboot.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -204,17 +205,6 @@ static int sanunhook_exec ( int argc, char **argv ) { } /** SAN commands */ -struct command sanboot_commands[] __command = { - { - .name = "sanhook", - .exec = sanhook_exec, - }, - { - .name = "sanboot", - .exec = sanboot_exec, - }, - { - .name = "sanunhook", - .exec = sanunhook_exec, - }, -}; +COMMAND ( sanhook, sanhook_exec ); +COMMAND ( sanboot, sanboot_exec ); +COMMAND ( sanunhook, sanunhook_exec ); diff --git a/src/hci/commands/shim_cmd.c b/src/hci/commands/shim_cmd.c index 11956290a..1566af4e9 100644 --- a/src/hci/commands/shim_cmd.c +++ b/src/hci/commands/shim_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <getopt.h> #include <ipxe/command.h> @@ -123,9 +124,4 @@ static int shim_exec ( int argc, char **argv ) { } /** Shim commands */ -struct command shim_commands[] __command = { - { - .name = "shim", - .exec = shim_exec, - }, -}; +COMMAND ( shim, shim_exec ); diff --git a/src/hci/commands/sync_cmd.c b/src/hci/commands/sync_cmd.c index 54799d422..e3b97298c 100644 --- a/src/hci/commands/sync_cmd.c +++ b/src/hci/commands/sync_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <stdio.h> @@ -77,7 +78,4 @@ static int sync_exec ( int argc, char **argv ) { } /** Sync commands */ -struct command sync_command __command = { - .name = "sync", - .exec = sync_exec, -}; +COMMAND ( sync, sync_exec ); diff --git a/src/hci/commands/time_cmd.c b/src/hci/commands/time_cmd.c index 08148bf38..2199321c7 100644 --- a/src/hci/commands/time_cmd.c +++ b/src/hci/commands/time_cmd.c @@ -21,6 +21,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <stdlib.h> @@ -77,7 +78,4 @@ static int time_exec ( int argc, char **argv ) { } /** "time" command */ -struct command time_command __command = { - .name = "time", - .exec = time_exec, -}; +COMMAND ( time, time_exec ); diff --git a/src/hci/commands/usb_cmd.c b/src/hci/commands/usb_cmd.c new file mode 100644 index 000000000..17affc0d7 --- /dev/null +++ b/src/hci/commands/usb_cmd.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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; either version 2 of the + * License, or any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#include <ipxe/usb.h> +#include <ipxe/command.h> +#include <ipxe/parseopt.h> + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +/** @file + * + * USB commands + * + */ + +/** "usbscan" options */ +struct usbscan_options {}; + +/** "usbscan" option list */ +static struct option_descriptor usbscan_opts[] = {}; + +/** "usbscan" command descriptor */ +static struct command_descriptor usbscan_cmd = + COMMAND_DESC ( struct usbscan_options, usbscan_opts, 1, 1, + "<setting>" ); + +/** + * "usbscan" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int usbscan_exec ( int argc, char **argv ) { + struct usbscan_options opts; + struct named_setting setting; + struct usb_device *usb; + unsigned long prev; + uint16_t busdev; + int len; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &usbscan_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse setting name */ + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; + + /* Determine starting bus:dev.fn address */ + if ( ( len = fetchn_setting ( setting.settings, &setting.setting, + NULL, &setting.setting, &prev ) ) < 0 ) { + /* Setting not yet defined: start searching from 00:00 */ + busdev = 0; + } else { + /* Setting is defined: start searching from next location */ + busdev = ( prev + 1 ); + if ( ! busdev ) { + rc = -ENOENT; + goto err_end; + } + } + + /* Find next existent USB device */ + if ( ( rc = usb_find_next ( &usb, &busdev ) ) != 0 ) + goto err_find_next; + + /* Apply default type if necessary. Use ":uint16" rather than + * ":hex" to allow for easy inclusion within a + * "${usb/${location}....}" constructed setting. + */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_uint16; + + /* Store setting */ + if ( ( rc = storen_setting ( setting.settings, &setting.setting, + busdev ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); + goto err_store; + } + + err_store: + err_end: + err_find_next: + err_parse_setting: + err_parse_options: + return rc; +} + +/** USB commands */ +COMMAND ( usbscan, usbscan_exec ); diff --git a/src/hci/commands/vlan_cmd.c b/src/hci/commands/vlan_cmd.c index 8a2f0c749..69aef9f3c 100644 --- a/src/hci/commands/vlan_cmd.c +++ b/src/hci/commands/vlan_cmd.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <stdlib.h> @@ -131,13 +132,5 @@ static int vdestroy_exec ( int argc, char **argv ) { } /** VLAN commands */ -struct command vlan_commands[] __command = { - { - .name = "vcreate", - .exec = vcreate_exec, - }, - { - .name = "vdestroy", - .exec = vdestroy_exec, - }, -}; +COMMAND ( vcreate, vcreate_exec ); +COMMAND ( vdestroy, vdestroy_exec ); diff --git a/src/hci/editstring.c b/src/hci/editstring.c index 8cbce0767..f88b81f7f 100644 --- a/src/hci/editstring.c +++ b/src/hci/editstring.c @@ -22,10 +22,13 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <assert.h> +#include <errno.h> #include <string.h> #include <ctype.h> +#include <stdlib.h> #include <ipxe/keys.h> #include <ipxe/editstring.h> @@ -35,17 +38,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -static void insert_delete ( struct edit_string *string, size_t delete_len, - const char *insert_text ) - __attribute__ (( nonnull (1) )); -static void insert_character ( struct edit_string *string, - unsigned int character ) __nonnull; -static void delete_character ( struct edit_string *string ) __nonnull; -static void backspace ( struct edit_string *string ) __nonnull; -static void previous_word ( struct edit_string *string ) __nonnull; -static void kill_word ( struct edit_string *string ) __nonnull; -static void kill_sol ( struct edit_string *string ) __nonnull; -static void kill_eol ( struct edit_string *string ) __nonnull; +static __attribute__ (( nonnull ( 1 ) )) int +insert_delete ( struct edit_string *string, size_t delete_len, + const char *insert_text ); +static __nonnull int insert_character ( struct edit_string *string, + unsigned int character ); +static __nonnull void delete_character ( struct edit_string *string ); +static __nonnull void backspace ( struct edit_string *string ); +static __nonnull void previous_word ( struct edit_string *string ); +static __nonnull void kill_word ( struct edit_string *string ); +static __nonnull void kill_sol ( struct edit_string *string ); +static __nonnull void kill_eol ( struct edit_string *string ); /** * Insert and/or delete text within an editable string @@ -53,35 +56,57 @@ static void kill_eol ( struct edit_string *string ) __nonnull; * @v string Editable string * @v delete_len Length of text to delete from current cursor position * @v insert_text Text to insert at current cursor position, or NULL + * @ret rc Return status code */ -static void insert_delete ( struct edit_string *string, size_t delete_len, - const char *insert_text ) { - size_t old_len, max_delete_len, insert_len, max_insert_len, new_len; +static int insert_delete ( struct edit_string *string, size_t delete_len, + const char *insert_text ) { + size_t old_len, max_delete_len, move_len, insert_len, new_len; + char *buf; + char *tmp; + + /* Prepare edit history */ + string->mod_start = string->cursor; + string->mod_end = string->cursor; /* Calculate lengths */ - old_len = strlen ( string->buf ); + buf = *(string->buf); + old_len = ( buf ? strlen ( buf ) : 0 ); assert ( string->cursor <= old_len ); max_delete_len = ( old_len - string->cursor ); if ( delete_len > max_delete_len ) delete_len = max_delete_len; + move_len = ( max_delete_len - delete_len ); insert_len = ( insert_text ? strlen ( insert_text ) : 0 ); - max_insert_len = ( ( string->len - 1 ) - ( old_len - delete_len ) ); - if ( insert_len > max_insert_len ) - insert_len = max_insert_len; new_len = ( old_len - delete_len + insert_len ); - /* Fill in edit history */ - string->mod_start = string->cursor; - string->mod_end = ( ( new_len > old_len ) ? new_len : old_len ); + /* Delete existing text */ + memmove ( ( buf + string->cursor ), + ( buf + string->cursor + delete_len ), move_len ); + + /* Reallocate string, ignoring failures if shrinking */ + tmp = realloc ( buf, ( new_len + 1 /* NUL */ ) ); + if ( tmp ) { + buf = tmp; + *(string->buf) = buf; + } else if ( ( new_len > old_len ) || ( ! buf ) ) { + return -ENOMEM; + } - /* Move data following the cursor */ - memmove ( ( string->buf + string->cursor + insert_len ), - ( string->buf + string->cursor + delete_len ), - ( max_delete_len + 1 - delete_len ) ); + /* Create space for inserted text */ + memmove ( ( buf + string->cursor + insert_len ), + ( buf + string->cursor ), move_len ); /* Copy inserted text to cursor position */ - memcpy ( ( string->buf + string->cursor ), insert_text, insert_len ); + memcpy ( ( buf + string->cursor ), insert_text, insert_len ); string->cursor += insert_len; + + /* Terminate string */ + buf[new_len] = '\0'; + + /* Update edit history */ + string->mod_end = ( ( new_len > old_len ) ? new_len : old_len ); + + return 0; } /** @@ -89,11 +114,13 @@ static void insert_delete ( struct edit_string *string, size_t delete_len, * * @v string Editable string * @v character Character to insert + * @ret rc Return status code */ -static void insert_character ( struct edit_string *string, +static int insert_character ( struct edit_string *string, unsigned int character ) { char insert_text[2] = { character, '\0' }; - insert_delete ( string, 0, insert_text ); + + return insert_delete ( string, 0, insert_text ); } /** @@ -102,7 +129,10 @@ static void insert_character ( struct edit_string *string, * @v string Editable string */ static void delete_character ( struct edit_string *string ) { - insert_delete ( string, 1, NULL ); + int rc; + + rc = insert_delete ( string, 1, NULL ); + assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) ); } /** @@ -111,6 +141,7 @@ static void delete_character ( struct edit_string *string ) { * @v string Editable string */ static void backspace ( struct edit_string *string ) { + if ( string->cursor > 0 ) { string->cursor--; delete_character ( string ); @@ -123,14 +154,16 @@ static void backspace ( struct edit_string *string ) { * @v string Editable string */ static void previous_word ( struct edit_string *string ) { - while ( string->cursor && - isspace ( string->buf[ string->cursor - 1 ] ) ) { - string->cursor--; + const char *buf = *(string->buf); + size_t cursor = string->cursor; + + while ( cursor && isspace ( buf[ cursor - 1 ] ) ) { + cursor--; } - while ( string->cursor && - ( ! isspace ( string->buf[ string->cursor - 1 ] ) ) ) { - string->cursor--; + while ( cursor && ( ! isspace ( buf[ cursor - 1 ] ) ) ) { + cursor--; } + string->cursor = cursor; } /** @@ -140,8 +173,11 @@ static void previous_word ( struct edit_string *string ) { */ static void kill_word ( struct edit_string *string ) { size_t old_cursor = string->cursor; + int rc; + previous_word ( string ); - insert_delete ( string, ( old_cursor - string->cursor ), NULL ); + rc = insert_delete ( string, ( old_cursor - string->cursor ), NULL ); + assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) ); } /** @@ -151,8 +187,11 @@ static void kill_word ( struct edit_string *string ) { */ static void kill_sol ( struct edit_string *string ) { size_t old_cursor = string->cursor; + int rc; + string->cursor = 0; - insert_delete ( string, old_cursor, NULL ); + rc = insert_delete ( string, old_cursor, NULL ); + assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) ); } /** @@ -161,18 +200,36 @@ static void kill_sol ( struct edit_string *string ) { * @v string Editable string */ static void kill_eol ( struct edit_string *string ) { - insert_delete ( string, ~( ( size_t ) 0 ), NULL ); + int rc; + + rc = insert_delete ( string, ~( ( size_t ) 0 ), NULL ); + assert ( ( rc == 0 ) || ( *(string->buf) == NULL ) ); } /** * Replace editable string * * @v string Editable string - * @v replacement Replacement string + * @v replacement Replacement string, or NULL to empty the string + * @ret rc Return status code + * + * Replace the entire content of the editable string and update the + * edit history to allow the caller to bring the display into sync + * with the string content. + * + * This function does not itself update the display in any way. + * + * Upon success, the string buffer is guaranteed to be non-NULL (even + * if the replacement string is NULL or empty). + * + * Errors may safely be ignored if it is deemed that subsequently + * failing to update the display will provide sufficient feedback to + * the user. */ -void replace_string ( struct edit_string *string, const char *replacement ) { +int replace_string ( struct edit_string *string, const char *replacement ) { + string->cursor = 0; - insert_delete ( string, ~( ( size_t ) 0 ), replacement ); + return insert_delete ( string, ~( ( size_t ) 0 ), replacement ); } /** @@ -180,21 +237,26 @@ void replace_string ( struct edit_string *string, const char *replacement ) { * * @v string Editable string * @v key Key pressed by user - * @ret key Key returned to application, or zero + * @ret key Key returned to application, zero, or negative error * * Handles keypresses and updates the content of the editable string. * Basic line editing facilities (delete/insert/cursor) are supported. * If edit_string() understands and uses the keypress it will return * zero, otherwise it will return the original key. * - * This function does not update the display in any way. - * * The string's edit history will be updated to allow the caller to * efficiently bring the display into sync with the string content. + * + * This function does not itself update the display in any way. + * + * Errors may safely be ignored if it is deemed that subsequently + * failing to update the display will provide sufficient feedback to + * the user. */ int edit_string ( struct edit_string *string, int key ) { + const char *buf = *(string->buf); + size_t len = ( buf ? strlen ( buf ) : 0 ); int retval = 0; - size_t len = strlen ( string->buf ); /* Prepare edit history */ string->last_cursor = string->cursor; @@ -204,7 +266,7 @@ int edit_string ( struct edit_string *string, int key ) { /* Interpret key */ if ( ( key >= 0x20 ) && ( key <= 0x7e ) ) { /* Printable character; insert at current position */ - insert_character ( string, key ); + retval = insert_character ( string, key ); } else switch ( key ) { case KEY_BACKSPACE: /* Backspace */ diff --git a/src/hci/jumpscroll.c b/src/hci/jumpscroll.c index dd6bcac2b..c6ee5bda0 100644 --- a/src/hci/jumpscroll.c +++ b/src/hci/jumpscroll.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * Jump scrolling @@ -39,7 +40,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v key Key pressed by user * @ret move Scroller movement, or zero */ -int jump_scroll_key ( struct jump_scroller *scroll, int key ) { +unsigned int jump_scroll_key ( struct jump_scroller *scroll, int key ) { + unsigned int flags = 0; + int16_t delta; /* Sanity checks */ assert ( scroll->rows != 0 ); @@ -52,20 +55,32 @@ int jump_scroll_key ( struct jump_scroller *scroll, int key ) { /* Handle key, if applicable */ switch ( key ) { case KEY_UP: - return -1; + delta = -1; + break; + case TAB: + flags = SCROLL_WRAP; + /* fall through */ case KEY_DOWN: - return +1; + delta = +1; + break; case KEY_PPAGE: - return ( scroll->first - scroll->current - 1 ); + delta = ( scroll->first - scroll->current - 1 ); + break; case KEY_NPAGE: - return ( scroll->first - scroll->current + scroll->rows ); + delta = ( scroll->first - scroll->current + scroll->rows ); + break; case KEY_HOME: - return -( scroll->count ); + delta = -( scroll->count ); + break; case KEY_END: - return +( scroll->count ); + delta = +( scroll->count ); + break; default: - return 0; + delta = 0; + break; } + + return ( SCROLL ( delta ) | flags ); } /** @@ -75,7 +90,9 @@ int jump_scroll_key ( struct jump_scroller *scroll, int key ) { * @v move Scroller movement * @ret move Continuing scroller movement (if applicable) */ -int jump_scroll_move ( struct jump_scroller *scroll, int move ) { +unsigned int jump_scroll_move ( struct jump_scroller *scroll, + unsigned int move ) { + int16_t delta = SCROLL_DELTA ( move ); int current = scroll->current; int last = ( scroll->count - 1 ); @@ -84,30 +101,35 @@ int jump_scroll_move ( struct jump_scroller *scroll, int move ) { assert ( scroll->count != 0 ); /* Move to the new current item */ - current += move; + current += delta; + + /* Default to continuing movement in the same direction */ + delta = ( ( delta >= 0 ) ? +1 : -1 ); /* Check for start/end of list */ - if ( current < 0 ) { - /* We have attempted to move before the start of the - * list. Move to the start of the list and continue - * moving forwards (if applicable). - */ - scroll->current = 0; - return +1; - } else if ( current > last ) { - /* We have attempted to move after the end of the - * list. Move to the end of the list and continue - * moving backwards (if applicable). + if ( ( current >= 0 ) && ( current <= last ) ) { + /* We are still within the list. Update the current + * item and continue moving in the same direction (if + * applicable). */ - scroll->current = last; - return -1; + scroll->current = current; } else { - /* Update the current item and continue moving in the - * same direction (if applicable). + /* We have attempted to move outside the list. If we + * are wrapping around, then continue in the same + * direction (if applicable), otherwise reverse. */ - scroll->current = current; - return ( ( move > 0 ) ? +1 : -1 ); + if ( ! ( move & SCROLL_WRAP ) ) + delta = -delta; + + /* Move to start or end of list as appropriate */ + if ( delta >= 0 ) { + scroll->current = 0; + } else { + scroll->current = last; + } } + + return ( SCROLL ( delta ) | ( move & SCROLL_FLAGS ) ); } /** diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index ad4792b9b..dba123b76 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_by.c b/src/hci/keymap/keymap_by.c index 9af6c966d..78b1ca84c 100644 --- a/src/hci/keymap/keymap_by.c +++ b/src/hci/keymap/keymap_by.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_cf.c b/src/hci/keymap/keymap_cf.c index 7ecfc4448..62d5d1bcc 100644 --- a/src/hci/keymap/keymap_cf.c +++ b/src/hci/keymap/keymap_cf.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_cz.c b/src/hci/keymap/keymap_cz.c index dd793a8d0..1ecf80978 100644 --- a/src/hci/keymap/keymap_cz.c +++ b/src/hci/keymap/keymap_cz.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index fb1136dc7..c32421ddc 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index 45cdeb25d..401bf715b 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index c1c55b348..3bfcae49a 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_et.c b/src/hci/keymap/keymap_et.c index 4d763266f..e859f3b91 100644 --- a/src/hci/keymap/keymap_et.c +++ b/src/hci/keymap/keymap_et.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index 8bcd0c393..54eab6586 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index 23a0837fb..5718f5d3b 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_gr.c b/src/hci/keymap/keymap_gr.c index 4826c26c2..fdd142490 100644 --- a/src/hci/keymap/keymap_gr.c +++ b/src/hci/keymap/keymap_gr.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_hu.c b/src/hci/keymap/keymap_hu.c index 771671408..251a70ca7 100644 --- a/src/hci/keymap/keymap_hu.c +++ b/src/hci/keymap/keymap_hu.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_il.c b/src/hci/keymap/keymap_il.c index b11e7ce71..9a8c46ec4 100644 --- a/src/hci/keymap/keymap_il.c +++ b/src/hci/keymap/keymap_il.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index bb14ae1bd..3a938050c 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_lt.c b/src/hci/keymap/keymap_lt.c index f8e60a5c5..1091203ac 100644 --- a/src/hci/keymap/keymap_lt.c +++ b/src/hci/keymap/keymap_lt.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_mk.c b/src/hci/keymap/keymap_mk.c index 4b90ef799..e6b845dfc 100644 --- a/src/hci/keymap/keymap_mk.c +++ b/src/hci/keymap/keymap_mk.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_mt.c b/src/hci/keymap/keymap_mt.c index 0997bfe93..be6dde3a9 100644 --- a/src/hci/keymap/keymap_mt.c +++ b/src/hci/keymap/keymap_mt.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_nl.c b/src/hci/keymap/keymap_nl.c index 97c7e8b5d..fef5ac798 100644 --- a/src/hci/keymap/keymap_nl.c +++ b/src/hci/keymap/keymap_nl.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_no-latin1.c b/src/hci/keymap/keymap_no-latin1.c index 80df84889..9db333bcd 100644 --- a/src/hci/keymap/keymap_no-latin1.c +++ b/src/hci/keymap/keymap_no-latin1.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index 0a624c3ab..aafcf1723 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_pl.c b/src/hci/keymap/keymap_pl.c index a76181fbc..f1ad81327 100644 --- a/src/hci/keymap/keymap_pl.c +++ b/src/hci/keymap/keymap_pl.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index 3133c1561..8fcfb3bcf 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_ro.c b/src/hci/keymap/keymap_ro.c index 620450001..7767bf2fb 100644 --- a/src/hci/keymap/keymap_ro.c +++ b/src/hci/keymap/keymap_ro.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_ru.c b/src/hci/keymap/keymap_ru.c index 2aafcf9bd..2a025f815 100644 --- a/src/hci/keymap/keymap_ru.c +++ b/src/hci/keymap/keymap_ru.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_se.c b/src/hci/keymap/keymap_se.c index 2bac96985..fad24fffe 100644 --- a/src/hci/keymap/keymap_se.c +++ b/src/hci/keymap/keymap_se.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_sg.c b/src/hci/keymap/keymap_sg.c index c4200099a..8fd943309 100644 --- a/src/hci/keymap/keymap_sg.c +++ b/src/hci/keymap/keymap_sg.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_sr-latin.c b/src/hci/keymap/keymap_sr-latin.c index 7e55714a2..f6b8eb7a6 100644 --- a/src/hci/keymap/keymap_sr-latin.c +++ b/src/hci/keymap/keymap_sr-latin.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_ua.c b/src/hci/keymap/keymap_ua.c index 44e82cb2d..bbd390079 100644 --- a/src/hci/keymap/keymap_ua.c +++ b/src/hci/keymap/keymap_ua.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_uk.c b/src/hci/keymap/keymap_uk.c index 28cf7aac4..a1f23a7a8 100644 --- a/src/hci/keymap/keymap_uk.c +++ b/src/hci/keymap/keymap_uk.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/keymap/keymap_us.c b/src/hci/keymap/keymap_us.c index b8e604a48..d178f189a 100644 --- a/src/hci/keymap/keymap_us.c +++ b/src/hci/keymap/keymap_us.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( PUBLIC_DOMAIN ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/keymap.h> diff --git a/src/hci/mucurses/ansi_screen.c b/src/hci/mucurses/ansi_screen.c index 1cf3309dd..7c607b5cc 100644 --- a/src/hci/mucurses/ansi_screen.c +++ b/src/hci/mucurses/ansi_screen.c @@ -4,6 +4,7 @@ #include <ipxe/console.h> FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); static void ansiscr_reset(struct _curses_screen *scr) __nonnull; static void ansiscr_movetoyx(struct _curses_screen *scr, diff --git a/src/hci/mucurses/clear.c b/src/hci/mucurses/clear.c index 2054f72cc..d93e9630e 100644 --- a/src/hci/mucurses/clear.c +++ b/src/hci/mucurses/clear.c @@ -9,6 +9,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * Clear a window to the bottom from current cursor position diff --git a/src/hci/mucurses/cursor.h b/src/hci/mucurses/cursor.h index 2e0c896a6..6f47becae 100644 --- a/src/hci/mucurses/cursor.h +++ b/src/hci/mucurses/cursor.h @@ -8,6 +8,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); struct cursor_pos { unsigned int y, x; diff --git a/src/hci/mucurses/mucurses.c b/src/hci/mucurses/mucurses.c index 98a8a2c59..7f1779e8f 100644 --- a/src/hci/mucurses/mucurses.c +++ b/src/hci/mucurses/mucurses.c @@ -8,6 +8,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); static void _wupdcurs ( WINDOW *win ) __nonnull; void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull; diff --git a/src/hci/mucurses/mucurses.h b/src/hci/mucurses/mucurses.h index 270394787..dc6187741 100644 --- a/src/hci/mucurses/mucurses.h +++ b/src/hci/mucurses/mucurses.h @@ -8,6 +8,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #define WRAP 0 #define NOWRAP 1 diff --git a/src/hci/mucurses/print.c b/src/hci/mucurses/print.c index e8831c58f..f7e0c8483 100644 --- a/src/hci/mucurses/print.c +++ b/src/hci/mucurses/print.c @@ -11,6 +11,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * Add a single-byte character and rendition to a window and advance diff --git a/src/hci/mucurses/widgets/editbox.c b/src/hci/mucurses/widgets/editbox.c index 210de4481..5dab3ac5c 100644 --- a/src/hci/mucurses/widgets/editbox.c +++ b/src/hci/mucurses/widgets/editbox.c @@ -22,9 +22,11 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <assert.h> +#include <ipxe/ansicol.h> #include <ipxe/editbox.h> /** @file @@ -36,38 +38,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EDITBOX_MIN_CHARS 3 /** - * Initialise text box widget - * - * @v box Editable text box widget - * @v buf Text buffer - * @v len Size of text buffer - * @v win Containing window - * @v row Row - * @v col Starting column - * @v width Width - * @v flags Flags - */ -void init_editbox ( struct edit_box *box, char *buf, size_t len, - WINDOW *win, unsigned int row, unsigned int col, - unsigned int width, unsigned int flags ) { - memset ( box, 0, sizeof ( *box ) ); - init_editstring ( &box->string, buf, len ); - box->string.cursor = strlen ( buf ); - box->win = ( win ? win : stdscr ); - box->row = row; - box->col = col; - box->width = width; - box->flags = flags; -} - -/** * Draw text box widget * - * @v box Editable text box widget - * + * @v widget Text widget */ -void draw_editbox ( struct edit_box *box ) { - size_t width = box->width; +static void draw_editbox ( struct widget *widget ) { + struct edit_box *box = container_of ( widget, struct edit_box, widget ); + const char *content = *(box->string.buf); + size_t width = widget->width; char buf[ width + 1 ]; signed int cursor_offset, underflow, overflow, first; size_t len; @@ -90,18 +68,37 @@ void draw_editbox ( struct edit_box *box ) { /* Construct underscore-padded string portion */ memset ( buf, '_', width ); buf[width] = '\0'; - len = ( strlen ( box->string.buf ) - first ); + len = ( content ? ( strlen ( content ) - first ) : 0 ); if ( len > width ) len = width; - if ( box->flags & EDITBOX_STARS ) { + if ( widget->flags & WIDGET_SECRET ) { memset ( buf, '*', len ); } else { - memcpy ( buf, ( box->string.buf + first ), len ); + memcpy ( buf, ( content + first ), len ); } /* Print box content and move cursor */ - if ( ! box->win ) - box->win = stdscr; - mvwprintw ( box->win, box->row, box->col, "%s", buf ); - wmove ( box->win, box->row, ( box->col + cursor_offset ) ); + color_set ( CPAIR_EDIT, NULL ); + mvprintw ( widget->row, widget->col, "%s", buf ); + move ( widget->row, ( widget->col + cursor_offset ) ); + color_set ( CPAIR_NORMAL, NULL ); } + +/** + * Edit text box widget + * + * @v widget Text widget + * @v key Key pressed by user + * @ret key Key returned to application, or zero + */ +static int edit_editbox ( struct widget *widget, int key ) { + struct edit_box *box = container_of ( widget, struct edit_box, widget ); + + return edit_string ( &box->string, key ); +} + +/** Text box widget operations */ +struct widget_operations editbox_operations = { + .draw = draw_editbox, + .edit = edit_editbox, +}; diff --git a/src/hci/mucurses/winattrs.c b/src/hci/mucurses/winattrs.c index 97a5a18b3..e78025543 100644 --- a/src/hci/mucurses/winattrs.c +++ b/src/hci/mucurses/winattrs.c @@ -7,6 +7,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * Get the background rendition attributes for a window diff --git a/src/hci/mucurses/wininit.c b/src/hci/mucurses/wininit.c index dd84d2f1d..1b651123e 100644 --- a/src/hci/mucurses/wininit.c +++ b/src/hci/mucurses/wininit.c @@ -8,6 +8,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * Initialise console environment diff --git a/src/hci/readline.c b/src/hci/readline.c index ecc72d43f..3d0330a62 100644 --- a/src/hci/readline.c +++ b/src/hci/readline.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <string.h> @@ -38,8 +39,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#define READLINE_MAX 1024 - /** * Synchronise console with edited string * @@ -49,7 +48,8 @@ static void sync_console ( struct edit_string *string ) { unsigned int mod_start = string->mod_start; unsigned int mod_end = string->mod_end; unsigned int cursor = string->last_cursor; - size_t len = strlen ( string->buf ); + const char *buf = *(string->buf); + size_t len = strlen ( buf ); /* Expand region back to old cursor position if applicable */ if ( mod_start > string->last_cursor ) @@ -67,7 +67,7 @@ static void sync_console ( struct edit_string *string ) { /* Print modified region */ while ( cursor < mod_end ) { - putchar ( ( cursor >= len ) ? ' ' : string->buf[cursor] ); + putchar ( ( cursor >= len ) ? ' ' : buf[cursor] ); cursor++; } @@ -259,15 +259,11 @@ int readline_history ( const char *prompt, const char *prefill, struct readline_history *history, unsigned long timeout, char **line ) { struct edit_string string; - char *buf; int key; int move_by; const char *new_string; int rc; - /* Avoid returning uninitialised data on error */ - *line = NULL; - /* Display prompt, if applicable */ if ( prompt ) printf ( "%s", prompt ); @@ -275,20 +271,15 @@ int readline_history ( const char *prompt, const char *prefill, /* Ensure cursor is visible */ printf ( "\033[?25h" ); - /* Allocate buffer and initialise editable string */ - buf = zalloc ( READLINE_MAX ); - if ( ! buf ) { - rc = -ENOMEM; - goto done; - } + /* Initialise editable string */ + *line = NULL; memset ( &string, 0, sizeof ( string ) ); - init_editstring ( &string, buf, READLINE_MAX ); + init_editstring ( &string, line ); - /* Prefill string, if applicable */ - if ( prefill ) { - replace_string ( &string, prefill ); - sync_console ( &string ); - } + /* Prefill string */ + if ( ( rc = replace_string ( &string, prefill ) ) != 0 ) + goto error; + sync_console ( &string ); while ( 1 ) { @@ -296,7 +287,7 @@ int readline_history ( const char *prompt, const char *prefill, key = getkey ( timeout ); if ( key < 0 ) { rc = -ETIMEDOUT; - goto done; + goto error; } timeout = 0; @@ -307,17 +298,11 @@ int readline_history ( const char *prompt, const char *prefill, switch ( key ) { case CR: case LF: - /* Shrink string (ignoring failures) */ - *line = realloc ( buf, - ( strlen ( buf ) + 1 /* NUL */ ) ); - if ( ! *line ) - *line = buf; - buf = NULL; rc = 0; goto done; case CTRL_C: rc = -ECANCELED; - goto done; + goto error; case KEY_UP: move_by = 1; break; @@ -325,13 +310,13 @@ int readline_history ( const char *prompt, const char *prefill, move_by = -1; break; default: - /* Do nothing */ + /* Do nothing for unrecognised keys or edit errors */ break; } /* Handle history movement, if applicable */ if ( move_by && history ) { - new_string = history_move ( history, move_by, buf ); + new_string = history_move ( history, move_by, *line ); if ( new_string ) { replace_string ( &string, new_string ); sync_console ( &string ); @@ -339,9 +324,11 @@ int readline_history ( const char *prompt, const char *prefill, } } + error: + free ( *line ); + *line = NULL; done: putchar ( '\n' ); - free ( buf ); if ( history ) { if ( *line && (*line)[0] ) history_append ( history, *line ); diff --git a/src/hci/shell.c b/src/hci/shell.c index 8ecf73a6f..cc7910eb8 100644 --- a/src/hci/shell.c +++ b/src/hci/shell.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdlib.h> @@ -72,10 +73,7 @@ static int help_exec ( int argc __unused, char **argv __unused ) { } /** "help" command */ -struct command help_command __command = { - .name = "help", - .exec = help_exec, -}; +COMMAND ( help, help_exec ); /** * Start command shell @@ -137,7 +135,4 @@ static int shell_exec ( int argc, char **argv ) { } /** "shell" command */ -struct command shell_command __command = { - .name = "shell", - .exec = shell_exec, -}; +COMMAND ( shell, shell_exec ); diff --git a/src/hci/strerror.c b/src/hci/strerror.c index 1bba8c620..48091b413 100644 --- a/src/hci/strerror.c +++ b/src/hci/strerror.c @@ -20,6 +20,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * Find error description diff --git a/src/hci/tui/form_ui.c b/src/hci/tui/form_ui.c new file mode 100644 index 000000000..7d8026f16 --- /dev/null +++ b/src/hci/tui/form_ui.c @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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; either version 2 of the + * License, or any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +/** @file + * + * Text widget forms + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/ansicol.h> +#include <ipxe/dynui.h> +#include <ipxe/jumpscroll.h> +#include <ipxe/settings.h> +#include <ipxe/editbox.h> +#include <ipxe/message.h> + +/** Form title row */ +#define TITLE_ROW 1U + +/** Starting control row */ +#define START_ROW 3U + +/** Ending control row */ +#define END_ROW ( LINES - 3U ) + +/** Instructions row */ +#define INSTRUCTION_ROW ( LINES - 2U ) + +/** Padding between instructions */ +#define INSTRUCTION_PAD " " + +/** Input field width */ +#define INPUT_WIDTH ( COLS / 2U ) + +/** Input field column */ +#define INPUT_COL ( ( COLS - INPUT_WIDTH ) / 2U ) + +/** A form */ +struct form { + /** Dynamic user interface */ + struct dynamic_ui *dynui; + /** Jump scroller */ + struct jump_scroller scroll; + /** Array of form controls */ + struct form_control *controls; +}; + +/** A form control */ +struct form_control { + /** Dynamic user interface item */ + struct dynamic_item *item; + /** Settings block */ + struct settings *settings; + /** Setting */ + struct setting setting; + /** Label row */ + unsigned int row; + /** Editable text box */ + struct edit_box editbox; + /** Modifiable setting name */ + char *name; + /** Modifiable setting value */ + char *value; + /** Most recent error in saving */ + int rc; +}; + +/** + * Allocate form + * + * @v dynui Dynamic user interface + * @ret form Form, or NULL on error + */ +static struct form * alloc_form ( struct dynamic_ui *dynui ) { + struct form *form; + struct form_control *control; + struct dynamic_item *item; + char *name; + size_t len; + + /* Calculate total length */ + len = sizeof ( *form ); + list_for_each_entry ( item, &dynui->items, list ) { + len += sizeof ( *control ); + if ( item->name ) + len += ( strlen ( item->name ) + 1 /* NUL */ ); + } + + /* Allocate and initialise structure */ + form = zalloc ( len ); + if ( ! form ) + return NULL; + control = ( ( ( void * ) form ) + sizeof ( *form ) ); + name = ( ( ( void * ) control ) + + ( dynui->count * sizeof ( *control ) ) ); + form->dynui = dynui; + form->controls = control; + list_for_each_entry ( item, &dynui->items, list ) { + control->item = item; + if ( item->name ) { + control->name = name; + name = ( stpcpy ( name, item->name ) + 1 /* NUL */ ); + } + control++; + } + assert ( ( ( void * ) name ) == ( ( ( void * ) form ) + len ) ); + + return form; +} + +/** + * Free form + * + * @v form Form + */ +static void free_form ( struct form *form ) { + unsigned int i; + + /* Free input value buffers */ + for ( i = 0 ; i < form->dynui->count ; i++ ) + free ( form->controls[i].value ); + + /* Free form */ + free ( form ); +} + +/** + * Assign form rows + * + * @v form Form + * @ret rc Return status code + */ +static int layout_form ( struct form *form ) { + struct form_control *control; + struct dynamic_item *item; + unsigned int labels = 0; + unsigned int inputs = 0; + unsigned int pad_control = 0; + unsigned int pad_label = 0; + unsigned int minimum; + unsigned int remaining; + unsigned int between; + unsigned int row; + unsigned int flags; + unsigned int i; + + /* Count labels and inputs */ + for ( i = 0 ; i < form->dynui->count ; i++ ) { + control = &form->controls[i]; + item = control->item; + if ( item->text[0] ) + labels++; + if ( item->name ) { + if ( ! inputs ) + form->scroll.current = i; + inputs++; + if ( item->flags & DYNUI_DEFAULT ) + form->scroll.current = i; + form->scroll.count = ( i + 1 ); + } + } + form->scroll.rows = form->scroll.count; + DBGC ( form, "FORM %p has %d controls (%d labels, %d inputs)\n", + form, form->dynui->count, labels, inputs ); + + /* Refuse to create forms with no inputs */ + if ( ! inputs ) + return -EINVAL; + + /* Calculate minimum number of rows */ + minimum = ( labels + ( inputs * 2 /* edit box and error message */ ) ); + remaining = ( END_ROW - START_ROW ); + DBGC ( form, "FORM %p has %d (of %d) usable rows\n", + form, remaining, LINES ); + if ( minimum > remaining ) + return -ERANGE; + remaining -= minimum; + + /* Insert blank row between controls, if space exists */ + between = ( form->dynui->count - 1 ); + if ( between <= remaining ) { + pad_control = 1; + remaining -= between; + DBGC ( form, "FORM %p padding between controls\n", form ); + } + + /* Insert blank row after label, if space exists */ + if ( labels <= remaining ) { + pad_label = 1; + remaining -= labels; + DBGC ( form, "FORM %p padding after labels\n", form ); + } + + /* Centre on screen */ + DBGC ( form, "FORM %p has %d spare rows\n", form, remaining ); + row = ( START_ROW + ( remaining / 2 ) ); + + /* Position each control */ + for ( i = 0 ; i < form->dynui->count ; i++ ) { + control = &form->controls[i]; + item = control->item; + if ( item->text[0] ) { + control->row = row; + row++; /* Label text */ + row += pad_label; + } + if ( item->name ) { + flags = ( ( item->flags & DYNUI_SECRET ) ? + WIDGET_SECRET : 0 ); + init_editbox ( &control->editbox, row, INPUT_COL, + INPUT_WIDTH, flags, &control->value ); + row++; /* Edit box */ + row++; /* Error message (if any) */ + } + row += pad_control; + } + assert ( row <= END_ROW ); + + return 0; +} + +/** + * Draw form + * + * @v form Form + */ +static void draw_form ( struct form *form ) { + struct form_control *control; + unsigned int i; + + /* Clear screen */ + color_set ( CPAIR_NORMAL, NULL ); + erase(); + + /* Draw title, if any */ + attron ( A_BOLD ); + if ( form->dynui->title ) + msg ( TITLE_ROW, "%s", form->dynui->title ); + attroff ( A_BOLD ); + + /* Draw controls */ + for ( i = 0 ; i < form->dynui->count ; i++ ) { + control = &form->controls[i]; + + /* Draw label, if any */ + if ( control->row ) + msg ( control->row, "%s", control->item->text ); + + /* Draw input, if any */ + if ( control->name ) + draw_widget ( &control->editbox.widget ); + } + + /* Draw instructions */ + msg ( INSTRUCTION_ROW, "%s", "Ctrl-X - save changes" + INSTRUCTION_PAD "Ctrl-C - discard changes" ); +} + +/** + * Draw (or clear) error messages + * + * @v form Form + */ +static void draw_errors ( struct form *form ) { + struct form_control *control; + unsigned int row; + unsigned int i; + + /* Draw (or clear) errors */ + for ( i = 0 ; i < form->dynui->count ; i++ ) { + control = &form->controls[i]; + + /* Skip non-input controls */ + if ( ! control->name ) + continue; + + /* Draw or clear error message as appropriate */ + row = ( control->editbox.widget.row + 1 ); + if ( control->rc != 0 ) { + color_set ( CPAIR_ALERT, NULL ); + msg ( row, " %s ", strerror ( control->rc ) ); + color_set ( CPAIR_NORMAL, NULL ); + } else { + clearmsg ( row ); + } + } +} + +/** + * Parse setting names + * + * @v form Form + * @ret rc Return status code + */ +static int parse_names ( struct form *form ) { + struct form_control *control; + unsigned int i; + int rc; + + /* Parse all setting names */ + for ( i = 0 ; i < form->dynui->count ; i++ ) { + control = &form->controls[i]; + + /* Skip labels */ + if ( ! control->name ) { + DBGC ( form, "FORM %p item %d is a label\n", form, i ); + continue; + } + + /* Parse setting name */ + DBGC ( form, "FORM %p item %d is for %s\n", + form, i, control->name ); + if ( ( rc = parse_setting_name ( control->name, + autovivify_child_settings, + &control->settings, + &control->setting ) ) != 0 ) + return rc; + + /* Apply default type if necessary */ + if ( ! control->setting.type ) + control->setting.type = &setting_type_string; + } + + return 0; +} + +/** + * Load current input values + * + * @v form Form + */ +static void load_values ( struct form *form ) { + struct form_control *control; + unsigned int i; + + /* Fetch all current setting values */ + for ( i = 0 ; i < form->dynui->count ; i++ ) { + control = &form->controls[i]; + if ( ! control->name ) + continue; + fetchf_setting_copy ( control->settings, &control->setting, + NULL, &control->setting, + &control->value ); + } +} + +/** + * Store current input values + * + * @v form Form + * @ret rc Return status code + */ +static int save_values ( struct form *form ) { + struct form_control *control; + unsigned int i; + int rc = 0; + + /* Store all current setting values */ + for ( i = 0 ; i < form->dynui->count ; i++ ) { + control = &form->controls[i]; + if ( ! control->name ) + continue; + control->rc = storef_setting ( control->settings, + &control->setting, + control->value ); + if ( control->rc != 0 ) + rc = control->rc; + } + + return rc; +} + +/** + * Submit form + * + * @v form Form + * @ret rc Return status code + */ +static int submit_form ( struct form *form ) { + int rc; + + /* Attempt to save values */ + rc = save_values ( form ); + + /* Draw (or clear) errors */ + draw_errors ( form ); + + return rc; +} + +/** + * Form main loop + * + * @v form Form + * @ret rc Return status code + */ +static int form_loop ( struct form *form ) { + struct jump_scroller *scroll = &form->scroll; + struct form_control *control; + struct dynamic_item *item; + unsigned int move; + unsigned int i; + int key; + int rc; + + /* Main loop */ + while ( 1 ) { + + /* Draw current input */ + control = &form->controls[scroll->current]; + draw_widget ( &control->editbox.widget ); + + /* Process keypress */ + key = edit_widget ( &control->editbox.widget, getkey ( 0 ) ); + + /* Handle scroll keys */ + move = jump_scroll_key ( &form->scroll, key ); + + /* Handle special keys */ + switch ( key ) { + case CTRL_C: + case ESC: + /* Cancel form */ + return -ECANCELED; + case KEY_ENTER: + /* Attempt to do the most intuitive thing when + * Enter is pressed. If we are on the last + * input, then submit the form. If we are + * editing an input which failed, then + * resubmit the form. Otherwise, move to the + * next input. + */ + if ( ( control->rc == 0 ) && + ( scroll->current < ( scroll->count - 1 ) ) ) { + move = SCROLL_DOWN; + break; + } + /* fall through */ + case CTRL_X: + /* Submit form */ + if ( ( rc = submit_form ( form ) ) == 0 ) + return 0; + /* If current input is not the problem, move + * to the first input that needs fixing. + */ + if ( control->rc == 0 ) { + for ( i = 0 ; i < form->dynui->count ; i++ ) { + if ( form->controls[i].rc != 0 ) { + scroll->current = i; + break; + } + } + } + break; + default: + /* Move to input with matching shortcut key, if any */ + item = dynui_shortcut ( form->dynui, key ); + if ( item && ( item->flags & DYNUI_HIDDEN ) == 0 ) { + scroll->current = item->index; + if ( ! item->name ) + move = SCROLL_DOWN; + } + break; + } + + /* Move selection, if applicable */ + while ( move ) { + move = jump_scroll_move ( &form->scroll, move ); + control = &form->controls[scroll->current]; + if ( control->name ) + break; + } + } +} + +/** + * Show form + * + * @v dynui Dynamic user interface + * @ret rc Return status code + */ +int show_form ( struct dynamic_ui *dynui ) { + struct form *form; + int rc; + + /* Allocate and initialise structure */ + form = alloc_form ( dynui ); + if ( ! form ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Parse setting names and load current values */ + if ( ( rc = parse_names ( form ) ) != 0 ) + goto err_parse_names; + load_values ( form ); + + /* Lay out form on screen */ + if ( ( rc = layout_form ( form ) ) != 0 ) + goto err_layout; + + /* Draw initial form */ + initscr(); + start_color(); + draw_form ( form ); + + /* Run main loop */ + if ( ( rc = form_loop ( form ) ) != 0 ) + goto err_loop; + + err_loop: + color_set ( CPAIR_NORMAL, NULL ); + endwin(); + err_layout: + err_parse_names: + free_form ( form ); + err_alloc: + return rc; +} diff --git a/src/hci/tui/login_ui.c b/src/hci/tui/login_ui.c index 56fc2fa97..e265f81b0 100644 --- a/src/hci/tui/login_ui.c +++ b/src/hci/tui/login_ui.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -29,117 +30,56 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#include <string.h> -#include <errno.h> -#include <curses.h> -#include <ipxe/console.h> -#include <ipxe/settings.h> -#include <ipxe/editbox.h> -#include <ipxe/keys.h> -#include <ipxe/ansicol.h> +#include <ipxe/dynui.h> #include <ipxe/login_ui.h> -/* Screen layout */ -#define USERNAME_LABEL_ROW ( ( LINES / 2U ) - 4U ) -#define USERNAME_ROW ( ( LINES / 2U ) - 2U ) -#define PASSWORD_LABEL_ROW ( ( LINES / 2U ) + 2U ) -#define PASSWORD_ROW ( ( LINES / 2U ) + 4U ) -#define LABEL_COL ( ( COLS / 2U ) - 4U ) -#define EDITBOX_COL ( ( COLS / 2U ) - 10U ) -#define EDITBOX_WIDTH 20U +static struct dynamic_item username; +static struct dynamic_item password; -int login_ui ( int nouser ) { - char username[64]; - char password[64]; - struct edit_box username_box; - struct edit_box password_box; - struct edit_box *current_box = nouser ? &password_box : &username_box; - int key; - int rc = -EINPROGRESS; - - /* Fetch current setting values */ - fetch_string_setting ( NULL, &username_setting, username, - sizeof ( username ) ); - fetch_string_setting ( NULL, &password_setting, password, - sizeof ( password ) ); - - /* Initialise UI */ - initscr(); - start_color(); - init_editbox ( &username_box, username, sizeof ( username ), NULL, - USERNAME_ROW, EDITBOX_COL, EDITBOX_WIDTH, 0 ); - init_editbox ( &password_box, password, sizeof ( password ), NULL, - PASSWORD_ROW, EDITBOX_COL, EDITBOX_WIDTH, - EDITBOX_STARS ); - - /* Draw initial UI */ - color_set ( CPAIR_NORMAL, NULL ); - erase(); - attron ( A_BOLD ); - if ( ! nouser ) { - mvprintw ( USERNAME_LABEL_ROW, LABEL_COL, "Username:" ); - } - mvprintw ( PASSWORD_LABEL_ROW, LABEL_COL, "Password:" ); - attroff ( A_BOLD ); - color_set ( CPAIR_EDIT, NULL ); - if ( ! nouser ) { - draw_editbox ( &username_box ); - } - draw_editbox ( &password_box ); +static struct dynamic_ui login = { + .items = { + .prev = &password.list, + .next = &username.list, + }, + .hidden_items = { + .prev = &login.hidden_items, + .next = &login.hidden_items, + }, + .count = 2, +}; - /* Main loop */ - while ( rc == -EINPROGRESS ) { +static struct dynamic_item username = { + .list = { + .prev = &login.items, + .next = &password.list, + }, + .name = "username", + .text = "Username", + .index = 0, +}; - draw_editbox ( current_box ); +static struct dynamic_item password = { + .list = { + .prev = &username.list, + .next = &login.items, + }, + .name = "password", + .text = "Password", + .index = 1, + .flags = DYNUI_SECRET, +}; - key = getkey ( 0 ); - switch ( key ) { - case KEY_DOWN: - current_box = &password_box; - break; - case KEY_UP: - if ( ! nouser ) { - current_box = &username_box; - } - break; - case TAB: - if ( ! nouser ) { - current_box = ( ( current_box == &username_box ) ? - &password_box : &username_box ); - } - break; - case KEY_ENTER: - if ( current_box == &username_box ) { - current_box = &password_box; - } else { - rc = 0; - } - break; - case CTRL_C: - case ESC: - rc = -ECANCELED; - break; - default: - edit_editbox ( current_box, key ); - break; - } +int login_ui ( int nouser ) { + if ( nouser ) { + password.index = 0; + password.list.prev = &login.items; + login.count = 1; + login.items.next = &password.list; + } else { + password.index = 1; + password.list.prev = &username.list; + login.count = 2; + login.items.next = &username.list; } - - /* Terminate UI */ - color_set ( CPAIR_NORMAL, NULL ); - erase(); - endwin(); - - if ( rc != 0 ) - return rc; - - /* Store settings */ - if ( ( rc = store_setting ( NULL, &username_setting, username, - strlen ( username ) ) ) != 0 ) - return rc; - if ( ( rc = store_setting ( NULL, &password_setting, password, - strlen ( password ) ) ) != 0 ) - return rc; - - return 0; + return show_form ( &login ); } diff --git a/src/hci/tui/menu_ui.c b/src/hci/tui/menu_ui.c index cb4edbbc8..e8672ed08 100644 --- a/src/hci/tui/menu_ui.c +++ b/src/hci/tui/menu_ui.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -37,7 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/console.h> #include <ipxe/ansicol.h> #include <ipxe/jumpscroll.h> -#include <ipxe/menu.h> +#include <ipxe/dynui.h> /* Screen layout */ #define TITLE_ROW 1U @@ -49,40 +50,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A menu user interface */ struct menu_ui { - /** Menu */ - struct menu *menu; + /** Dynamic user interface */ + struct dynamic_ui *dynui; /** Jump scroller */ struct jump_scroller scroll; - /** Timeout (0=indefinite) */ + /** Remaining timeout (0=indefinite) */ unsigned long timeout; + /** Post-activity timeout (0=indefinite) */ + unsigned long retimeout; }; /** - * Return a numbered menu item - * - * @v menu Menu - * @v index Index - * @ret item Menu item, or NULL - */ -static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) { - struct menu_item *item; - - list_for_each_entry ( item, &menu->items, list ) { - if ( index-- == 0 ) - return item; - } - - return NULL; -} - -/** * Draw a numbered menu item * * @v ui Menu user interface * @v index Index */ static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) { - struct menu_item *item; + struct dynamic_item *item; unsigned int row_offset; char buf[ MENU_COLS + 1 /* NUL */ ]; char timeout_buf[6]; /* "(xxx)" + NUL */ @@ -95,11 +80,11 @@ static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) { move ( ( MENU_ROW + row_offset ), MENU_COL ); /* Get menu item */ - item = menu_item ( ui->menu, index ); + item = dynui_item ( ui->dynui, index ); if ( item ) { /* Draw separators in a different colour */ - if ( ! item->label ) + if ( ! item->name ) color_set ( CPAIR_SEPARATOR, NULL ); /* Highlight if this is the selected item */ @@ -171,13 +156,12 @@ static void draw_menu_items ( struct menu_ui *ui ) { * @ret selected Selected item * @ret rc Return status code */ -static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { - struct menu_item *item; +static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) { + struct dynamic_item *item; unsigned long timeout; unsigned int previous; + unsigned int move; int key; - int i; - int move; int chosen = 0; int rc = 0; @@ -192,15 +176,15 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { ui->timeout -= timeout; /* Get key */ - move = 0; + move = SCROLL_NONE; key = getkey ( timeout ); if ( key < 0 ) { /* Choose default if we finally time out */ if ( ui->timeout == 0 ) chosen = 1; } else { - /* Cancel any timeout */ - ui->timeout = 0; + /* Reset timeout after activity */ + ui->timeout = ui->retimeout; /* Handle scroll keys */ move = jump_scroll_key ( &ui->scroll, key ); @@ -216,31 +200,17 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { chosen = 1; break; default: - i = 0; - - /* Check for shortcut. Visible items */ - list_for_each_entry ( item, &ui->menu->items, - list ) { - if ( ! ( item->shortcut && - ( item->shortcut == key ) ) ) { - i++; - continue; + item = dynui_shortcut ( ui->dynui, key ); + if ( item ) { + ui->scroll.current = item->index; + if ( item->flags & DYNUI_HIDDEN ) { + *selected = item; + goto hidden_entry_selected; } - ui->scroll.current = i; - if ( item->label ) { + if ( item->name ) { chosen = 1; } else { - move = +1; - } - } - - /* Hidden items */ - list_for_each_entry ( item, &ui->menu->hidden_items, - list ) { - if ( item->shortcut && - ( item->shortcut == key ) ) { - *selected = item; - goto hidden_entry_selected; + move = SCROLL_DOWN; } } break; @@ -250,8 +220,8 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /* Move selection, if applicable */ while ( move ) { move = jump_scroll_move ( &ui->scroll, move ); - item = menu_item ( ui->menu, ui->scroll.current ); - if ( item->label ) + item = dynui_item ( ui->dynui, ui->scroll.current ); + if ( item->name ) break; } @@ -264,9 +234,9 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { } /* Record selection */ - item = menu_item ( ui->menu, ui->scroll.current ); + item = dynui_item ( ui->dynui, ui->scroll.current ); assert ( item != NULL ); - assert ( item->label != NULL ); + assert ( item->name != NULL ); *selected = item; } while ( ( rc == 0 ) && ! chosen ); @@ -278,43 +248,47 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /** * Show menu * - * @v menu Menu - * @v timeout Timeout period, in ticks (0=indefinite) + * @v dynui Dynamic user interface + * @v timeout Initial timeout period, in ticks (0=indefinite) + * @v retimeout Post-activity timeout period, in ticks (0=indefinite) * @ret selected Selected item * @ret rc Return status code */ -int show_menu ( struct menu *menu, unsigned long timeout, - const char *select, struct menu_item **selected ) { - struct menu_item *item; +int show_menu ( struct dynamic_ui *dynui, unsigned long timeout, + unsigned long retimeout, const char *select, + struct dynamic_item **selected ) { + struct dynamic_item *item; struct menu_ui ui; char buf[ MENU_COLS + 1 /* NUL */ ]; - int labelled_count = 0; + int named_count = 0; int rc; /* Initialise UI */ memset ( &ui, 0, sizeof ( ui ) ); - ui.menu = menu; + ui.dynui = dynui; ui.scroll.rows = MENU_ROWS; ui.timeout = timeout; - list_for_each_entry ( item, &menu->items, list ) { - if ( item->label ) { - if ( ! labelled_count ) + ui.retimeout = retimeout; + + list_for_each_entry ( item, &dynui->items, list ) { + if ( item->name ) { + if ( ! named_count ) ui.scroll.current = ui.scroll.count; - labelled_count++; + named_count++; if ( select ) { - if ( strcmp ( select, item->label ) == 0 ) + if ( strcmp ( select, item->name ) == 0 ) ui.scroll.current = ui.scroll.count; } else { - if ( item->is_default ) + if ( item->flags & DYNUI_DEFAULT ) ui.scroll.current = ui.scroll.count; } } ui.scroll.count++; } - if ( ! labelled_count ) { - /* Menus with no labelled items cannot be selected - * from, and will seriously confuse the navigation - * logic. Refuse to display any such menus. + if ( ! named_count ) { + /* Menus with no named items cannot be selected from, + * and will seriously confuse the navigation logic. + * Refuse to display any such menus. */ return -ENOENT; } @@ -328,7 +302,7 @@ int show_menu ( struct menu *menu, unsigned long timeout, /* Draw initial content */ attron ( A_BOLD ); - snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title ); + snprintf ( buf, sizeof ( buf ), "%s", ui.dynui->title ); mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf ); attroff ( A_BOLD ); jump_scroll ( &ui.scroll ); diff --git a/src/hci/tui/message.c b/src/hci/tui/message.c new file mode 100644 index 000000000..89c6f7703 --- /dev/null +++ b/src/hci/tui/message.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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; either version 2 of the + * License, or any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +/** @file + * + * Message printing + * + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdarg.h> +#include <unistd.h> +#include <ipxe/ansicol.h> +#include <ipxe/message.h> + +/** + * Print message centred on specified row + * + * @v row Row + * @v fmt printf() format string + * @v args printf() argument list + */ +static void vmsg ( unsigned int row, const char *fmt, va_list args ) { + char buf[COLS]; + size_t len; + + len = vsnprintf ( buf, sizeof ( buf ), fmt, args ); + mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf ); +} + +/** + * Print message centred on specified row + * + * @v row Row + * @v fmt printf() format string + * @v .. printf() arguments + */ +void msg ( unsigned int row, const char *fmt, ... ) { + va_list args; + + va_start ( args, fmt ); + vmsg ( row, fmt, args ); + va_end ( args ); +} + +/** + * Clear message on specified row + * + * @v row Row + */ +void clearmsg ( unsigned int row ) { + move ( row, 0 ); + clrtoeol(); +} + +/** + * Show alert message + * + * @v row Row + * @v fmt printf() format string + * @v args printf() argument list + */ +static void valert ( unsigned int row, const char *fmt, va_list args ) { + + clearmsg ( row ); + color_set ( CPAIR_ALERT, NULL ); + vmsg ( row, fmt, args ); + sleep ( 2 ); + color_set ( CPAIR_NORMAL, NULL ); + clearmsg ( row ); +} + +/** + * Show alert message + * + * @v row Row + * @v fmt printf() format string + * @v ... printf() arguments + */ +void alert ( unsigned int row, const char *fmt, ... ) { + va_list args; + + va_start ( args, fmt ); + valert ( row, fmt, args ); + va_end ( args ); +} diff --git a/src/hci/tui/settings_ui.c b/src/hci/tui/settings_ui.c index be421cc0a..a069c527d 100644 --- a/src/hci/tui/settings_ui.c +++ b/src/hci/tui/settings_ui.c @@ -22,9 +22,11 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <stdarg.h> +#include <stdlib.h> #include <unistd.h> #include <string.h> #include <curses.h> @@ -34,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/keys.h> #include <ipxe/ansicol.h> #include <ipxe/jumpscroll.h> +#include <ipxe/message.h> #include <ipxe/settings_ui.h> #include <config/branding.h> @@ -58,12 +61,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); char start[0]; \ char pad1[1]; \ union { \ - char settings[ cols - 1 - 1 - 1 - 1 ]; \ + struct { \ + char name[ cols - 1 - 1 - 1 - 1 - 1 ]; \ + char pad2[1]; \ + } __attribute__ (( packed )) settings; \ struct { \ char name[15]; \ char pad2[1]; \ char value[ cols - 1 - 15 - 1 - 1 - 1 - 1 ]; \ - } setting; \ + } __attribute__ (( packed )) setting; \ } u; \ char pad3[1]; \ char nul; \ @@ -92,8 +98,8 @@ struct settings_ui_row { struct edit_box editbox; /** Editing in progress flag */ int editing; - /** Buffer for setting's value */ - char value[256]; /* enough size for a DHCP string */ + /** Dynamically allocated buffer for setting's value */ + char *buf; }; /** A settings user interface */ @@ -121,24 +127,22 @@ static unsigned int select_setting_row ( struct settings_ui *ui, struct setting *previous = NULL; unsigned int count = 0; + /* Free any previous setting value */ + free ( ui->row.buf ); + ui->row.buf = NULL; + /* Initialise structure */ memset ( &ui->row, 0, sizeof ( ui->row ) ); ui->row.row = ( SETTINGS_LIST_ROW + index - ui->scroll.first ); /* Include parent settings block, if applicable */ - if ( ui->settings->parent && ( count++ == index ) ) { + if ( ui->settings->parent && ( count++ == index ) ) ui->row.settings = ui->settings->parent; - snprintf ( ui->row.value, sizeof ( ui->row.value ), - "../" ); - } /* Include any child settings blocks, if applicable */ list_for_each_entry ( settings, &ui->settings->children, siblings ) { - if ( count++ == index ) { + if ( count++ == index ) ui->row.settings = settings; - snprintf ( ui->row.value, sizeof ( ui->row.value ), - "%s/", settings->name ); - } } /* Include any applicable settings */ @@ -155,18 +159,18 @@ static unsigned int select_setting_row ( struct settings_ui *ui, /* Read current setting value and origin */ if ( count++ == index ) { - fetchf_setting ( ui->settings, setting, &ui->row.origin, - &ui->row.setting, ui->row.value, - sizeof ( ui->row.value ) ); + fetchf_setting_copy ( ui->settings, setting, + &ui->row.origin, + &ui->row.setting, &ui->row.buf ); } } /* Initialise edit box */ - init_editbox ( &ui->row.editbox, ui->row.value, - sizeof ( ui->row.value ), NULL, ui->row.row, + memset ( &ui->row.editbox, 0, sizeof ( ui->row.editbox ) ); + init_editbox ( &ui->row.editbox, ui->row.row, ( SETTINGS_LIST_COL + offsetof ( typeof ( *text ), u.setting.value ) ), - sizeof ( text->u.setting.value ), 0 ); + sizeof ( text->u.setting.value ), 0, &ui->row.buf ); return count; } @@ -197,7 +201,7 @@ static size_t string_copy ( char *dest, const char *src, size_t len ) { static void draw_setting_row ( struct settings_ui *ui ) { SETTING_ROW_TEXT ( COLS ) text; unsigned int curs_offset; - char *value; + const char *value; /* Fill row with spaces */ memset ( &text, ' ', sizeof ( text ) ); @@ -207,10 +211,12 @@ static void draw_setting_row ( struct settings_ui *ui ) { if ( ui->row.settings ) { /* Construct space-padded name */ - curs_offset = ( offsetof ( typeof ( text ), u.settings ) + - string_copy ( text.u.settings, - ui->row.value, - sizeof ( text.u.settings ) ) ); + value = ( ( ui->row.settings == ui->settings->parent ) ? + ".." : ui->row.settings->name ); + curs_offset = string_copy ( text.u.settings.name, value, + sizeof ( text.u.settings.name ) ); + text.u.settings.name[curs_offset] = '/'; + curs_offset += offsetof ( typeof ( text ), u.settings ); } else { @@ -221,12 +227,12 @@ static void draw_setting_row ( struct settings_ui *ui ) { sizeof ( text.u.setting.name ) ); /* Construct space-padded value */ - value = ui->row.value; - if ( ! *value ) + value = ui->row.buf; + if ( ! ( value && value[0] ) ) value = "<not specified>"; - curs_offset = ( offsetof ( typeof ( text ), u.setting.value ) + - string_copy ( text.u.setting.value, value, - sizeof ( text.u.setting.value ))); + curs_offset = string_copy ( text.u.setting.value, value, + sizeof ( text.u.setting.value ) ); + curs_offset += offsetof ( typeof ( text ), u.setting.value ); } /* Print row */ @@ -247,7 +253,7 @@ static void draw_setting_row ( struct settings_ui *ui ) { static int edit_setting ( struct settings_ui *ui, int key ) { assert ( ui->row.setting.name != NULL ); ui->row.editing = 1; - return edit_editbox ( &ui->row.editbox, key ); + return edit_widget ( &ui->row.editbox.widget, key ); } /** @@ -257,76 +263,7 @@ static int edit_setting ( struct settings_ui *ui, int key ) { */ static int save_setting ( struct settings_ui *ui ) { assert ( ui->row.setting.name != NULL ); - return storef_setting ( ui->settings, &ui->row.setting, ui->row.value ); -} - -/** - * Print message centred on specified row - * - * @v row Row - * @v fmt printf() format string - * @v args printf() argument list - */ -static void vmsg ( unsigned int row, const char *fmt, va_list args ) { - char buf[COLS]; - size_t len; - - len = vsnprintf ( buf, sizeof ( buf ), fmt, args ); - mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf ); -} - -/** - * Print message centred on specified row - * - * @v row Row - * @v fmt printf() format string - * @v .. printf() arguments - */ -static void msg ( unsigned int row, const char *fmt, ... ) { - va_list args; - - va_start ( args, fmt ); - vmsg ( row, fmt, args ); - va_end ( args ); -} - -/** - * Clear message on specified row - * - * @v row Row - */ -static void clearmsg ( unsigned int row ) { - move ( row, 0 ); - clrtoeol(); -} - -/** - * Print alert message - * - * @v fmt printf() format string - * @v args printf() argument list - */ -static void valert ( const char *fmt, va_list args ) { - clearmsg ( ALERT_ROW ); - color_set ( CPAIR_ALERT, NULL ); - vmsg ( ALERT_ROW, fmt, args ); - sleep ( 2 ); - color_set ( CPAIR_NORMAL, NULL ); - clearmsg ( ALERT_ROW ); -} - -/** - * Print alert message - * - * @v fmt printf() format string - * @v ... printf() arguments - */ -static void alert ( const char *fmt, ... ) { - va_list args; - - va_start ( args, fmt ); - valert ( fmt, args ); - va_end ( args ); + return storef_setting ( ui->settings, &ui->row.setting, ui->row.buf ); } /** @@ -443,8 +380,8 @@ static void select_settings ( struct settings_ui *ui, static int main_loop ( struct settings *settings ) { struct settings_ui ui; unsigned int previous; + unsigned int move; int redraw = 1; - int move; int key; int rc; @@ -474,17 +411,17 @@ static int main_loop ( struct settings *settings ) { assert ( ui.row.setting.name != NULL ); /* Redraw edit box */ - color_set ( CPAIR_EDIT, NULL ); - draw_editbox ( &ui.row.editbox ); - color_set ( CPAIR_NORMAL, NULL ); + draw_widget ( &ui.row.editbox.widget ); /* Process keypress */ key = edit_setting ( &ui, getkey ( 0 ) ); switch ( key ) { case CR: case LF: - if ( ( rc = save_setting ( &ui ) ) != 0 ) - alert ( " %s ", strerror ( rc ) ); + if ( ( rc = save_setting ( &ui ) ) != 0 ) { + alert ( ALERT_ROW, " %s ", + strerror ( rc ) ); + } /* Fall through */ case CTRL_C: select_setting_row ( &ui, ui.scroll.current ); @@ -521,12 +458,13 @@ static int main_loop ( struct settings *settings ) { break; if ( ( rc = delete_setting ( ui.settings, &ui.row.setting ) ) != 0 ){ - alert ( " %s ", strerror ( rc ) ); + alert ( ALERT_ROW, " %s ", strerror ( rc ) ); } select_setting_row ( &ui, ui.scroll.current ); redraw = 1; break; case CTRL_X: + select_setting_row ( &ui, -1U ); return 0; case CR: case LF: |
