[PATCH 0/4] bg_printenv: new cli arguments

95 views
Skip to first unread message

Michael Adler

unread,
Nov 3, 2021, 4:41:18 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
Hi all,

as promised a while back [1], here is my extension of bg_printenv. It didn't make it in time for the 0.9 release, but
I first had to set up some automated testing [2] to prevent regressions.

(There is still a blank spot in our test coverage, i.e. cli tests which do not use the envfile parameter. This is a bit
tricky, since it requires setting up a (loopback) device and I'm not sure if this would work in CI. These kind of tests
I have performed manually. Maybe I can revisit this topic if we can agree on the patches.)

Looking forward to any feedback.

Kind regards,
Michael

[1] https://www.mail-archive.com/efibootg...@googlegroups.com/msg01319.html
[2] https://www.mail-archive.com/efibootg...@googlegroups.com/msg01370.html


Michael Adler (4):
gitignore: ignore test binaries
clang-format: tweaks for current code style
bg_setenv: decouple bg_setenv and bg_printenv
bg_printenv: new cli arguments

.clang-format | 2 +
.gitignore | 7 +
tests/bg_setenv.bats | 40 +++
tools/bg_setenv.c | 605 ++++++++++++++++++++++++++++++-------------
4 files changed, 476 insertions(+), 178 deletions(-)

--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 4:41:19 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
Signed-off-by: Michael Adler <michae...@siemens.com>
---
.gitignore | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/.gitignore b/.gitignore
index 45f7cc2..1943fe0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -85,3 +85,10 @@ m4/lt*
version.h
libtool
.libs
+
+# Test binaries
+test_bgenv_init_retval
+test_ebgenv_api
+test_ebgenv_api_internal
+test_probe_config_file
+test_probe_config_partitions
--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 4:41:20 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
* `ContinuationIndentWidth: 8` ensures that in certain scenarios (e.g.
array of structs, defined over multiple lines) consistently uses tabs
instead of spaces for alignment.
* `AlignConsecutiveMacros` improves the formatting of preprocessor
instructions, see [1].

[1] https://bugs.llvm.org/show_bug.cgi?id=20637

Signed-off-by: Michael Adler <michae...@siemens.com>
---
.clang-format | 2 ++
1 file changed, 2 insertions(+)

diff --git a/.clang-format b/.clang-format
index e433bf7..49dee99 100644
--- a/.clang-format
+++ b/.clang-format
@@ -14,3 +14,5 @@ PointerAlignment: Right
ReflowComments: true
SortIncludes: false
UseTab: Always
+ContinuationIndentWidth: 8
+AlignConsecutiveMacros: true
--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 4:41:21 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
Currently, the implementations of bg_setenv and bg_printenv are tightly
coupled with each other, making it (unnecessarily) difficult to grasp,
modify and extend the existing control flow.
This is a refactoring and does not introduce any new functionality.

Details:

* move bg_printenv code into its own method
* move cli arguments into struct, thereby keeping the global namespace
clean
* split bg_setenv/bg_printenv parsers
* do not rely on parsing order for arg validation, i.e. first parse and
then perform validation check

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 311 ++++++++++++++++++++++++++++------------------
1 file changed, 189 insertions(+), 122 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index e564219..3f1620c 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -24,7 +24,17 @@
static char doc[] =
"bg_setenv/bg_printenv - Environment tool for the EFI Boot Guard";

+// clang-format off
+#define BG_CLI_OPTIONS_COMMON \
+ {"filepath", 'f', "ENVFILE", 0, \
+ "Environment to use. Expects a file name, " \
+ "usually called BGENV.DAT."}, \
+ {"verbose", 'v', 0, 0, "Be verbose"}, \
+ {"version", 'V', 0, 0, "Print version"}
+// clang-format on
+
static struct argp_option options_setenv[] = {
+ BG_CLI_OPTIONS_COMMON,
{"preserve", 'P', 0, 0, "Preserve existing entries"},
{"kernel", 'k', "KERNEL", 0, "Set kernel to load"},
{"args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"},
@@ -33,34 +43,45 @@ static struct argp_option options_setenv[] = {
"the one with the smallest revision value above zero is updated."},
{"revision", 'r', "REVISION", 0, "Set revision value"},
{"ustate", 's', "USTATE", 0, "Set update status for environment"},
- {"filepath", 'f', "ENVFILE", 0,
- "Output environment to file. Expects an output file name, "
- "usually called BGENV.DAT."},
{"watchdog", 'w', "WATCHDOG_TIMEOUT", 0, "Watchdog timeout in seconds"},
{"confirm", 'c', 0, 0, "Confirm working environment"},
{"update", 'u', 0, 0, "Automatically update oldest revision"},
- {"verbose", 'v', 0, 0, "Be verbose"},
{"uservar", 'x', "KEY=VAL", 0,
"Set user-defined string variable. For setting multiple variables, "
"use this option multiple times."},
{"in_progress", 'i', "IN_PROGRESS", 0,
"Set in_progress variable to simulate a running update process."},
- {"version", 'V', 0, 0, "Print version"},
- {}
+ {},
};

static struct argp_option options_printenv[] = {
- {"filepath", 'f', "ENVFILE", 0,
- "Read environment from file. Expects a valid EFI Boot Guard "
- "environment file."},
- {"verbose", 'v', 0, 0, "Be verbose"},
- {"version", 'V', 0, 0, "Print version"},
- {}
+ BG_CLI_OPTIONS_COMMON,
+ {},
+};
+
+/* Common arguments used by both bg_setenv and bg_printenv. */
+struct arguments_common {
+ char *envfilepath;
+ bool verbosity;
};

-struct arguments {
- bool printenv;
+/* Arguments used by bg_setenv. */
+struct arguments_setenv {
+ struct arguments_common common;
int which_part;
+ /* auto update feature automatically updates partition with
+ * oldest environment revision (lowest value)
+ */
+ bool auto_update;
+ bool part_specified;
+ /* whether to keep existing entries in BGENV before applying new
+ * settings */
+ bool preserve_env;
+};
+
+/* Arguments used by bg_printenv. */
+struct arguments_printenv {
+ struct arguments_common common;
};

typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;
@@ -163,20 +184,6 @@ static void journal_process_action(BGENV *env, struct env_action *action)
}
}

-/* auto update feature automatically updates partition with
- * oldest environment revision (lowest value)
- */
-static bool auto_update = false;
-
-static bool part_specified = false;
-
-static bool verbosity = false;
-
-static char *envfilepath = NULL;
-
-/* whether to keep existing entries in BGENV before applying the new settings */
-static bool preserve_env = false;
-
static char *ustatemap[] = {"OK", "INSTALLED", "TESTING", "FAILED", "UNKNOWN"};

static uint8_t str2ustate(char *str)
@@ -239,9 +246,61 @@ static int parse_int(char *arg)
return (int) i;
}

-static error_t parse_opt(int key, char *arg, struct argp_state *state)
+static error_t parse_common_opt(int key, char *arg, bool compat_mode,
+ struct arguments_common *arguments)
{
- struct arguments *arguments = state->input;
+ bool found = false;
+ switch (key) {
+ case 'f':
+ found = true;
+ free(arguments->envfilepath);
+ arguments->envfilepath = NULL;
+
+ if (compat_mode) {
+ /* compat mode, permitting "bg_setenv -f <dir>" */
+ struct stat sb;
+
+ int res = stat(arg, &sb);
+ if (res == 0 && S_ISDIR(sb.st_mode)) {
+ fprintf(stderr,
+ "WARNING: Using -f to specify only the "
+ "ouptut directory is deprecated.\n");
+ res = asprintf(&arguments->envfilepath, "%s/%s",
+ arg, FAT_ENV_FILENAME);
+ if (res == -1) {
+ return ENOMEM;
+ }
+ }
+ }
+
+ if (!arguments->envfilepath) {
+ arguments->envfilepath = strdup(arg);
+ if (!arguments->envfilepath) {
+ return ENOMEM;
+ }
+ }
+ break;
+ case 'v':
+ found = true;
+ /* Set verbosity in this program */
+ arguments->verbosity = true;
+ /* Set verbosity in the library */
+ bgenv_be_verbose(true);
+ break;
+ case 'V':
+ found = true;
+ fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
+ exit(0);
+ }
+ if (!found) {
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
+{
+ struct arguments_setenv *arguments = state->input;
int i, res;
char *tmp;
error_t e = 0;
@@ -278,7 +337,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
fprintf(stdout, "Updating config partition #%d\n", i);
arguments->which_part = i;
- part_specified = true;
+ arguments->part_specified = true;
} else {
fprintf(stderr,
"Selected partition out of range. Valid range: "
@@ -361,34 +420,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
"watchdog_timeout_sec", 0,
(uint8_t *)arg, strlen(arg) + 1);
break;
- case 'f':
- free(envfilepath);
- envfilepath = NULL;
-
- /* compat mode, permitting "bg_setenv -f <dir>" */
- if (!arguments->printenv) {
- struct stat sb;
-
- res = stat(arg, &sb);
- if (res == 0 && S_ISDIR(sb.st_mode)) {
- fprintf(stderr,
- "WARNING: Using -f to specify only the "
- "ouptut directory is deprecated.\n");
- res = asprintf(&envfilepath, "%s/%s", arg,
- FAT_ENV_FILENAME);
- if (res == -1) {
- return ENOMEM;
- }
- }
- }
-
- if (!envfilepath) {
- envfilepath = strdup(arg);
- if (!envfilepath) {
- return ENOMEM;
- }
- }
- break;
case 'c':
VERBOSE(stdout,
"Confirming environment to work. Removing boot-once "
@@ -397,38 +428,22 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
(uint8_t *)"0", 2);
break;
case 'u':
- if (part_specified) {
- fprintf(stderr,
- "Error, both automatic and manual partition "
- "selection. Cannot use -p and -u "
- "simultaneously.\n");
- return 1;
- }
- auto_update = true;
- break;
- case 'v':
- /* Set verbosity in this program */
- verbosity = true;
- /* Set verbosity in the library */
- bgenv_be_verbose(true);
+ arguments->auto_update = true;
break;
case 'x':
/* Set user-defined variable(s) */
e = set_uservars(arg);
break;
case 'P':
- preserve_env = true;
+ arguments->preserve_env = true;
break;
- case 'V':
- fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
- exit(0);
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
argp_usage(state);
break;
default:
- return ARGP_ERR_UNKNOWN;
+ return parse_common_opt(key, arg, true, &arguments->common);
}

if (e) {
@@ -437,6 +452,23 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
return e;
}

+static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
+{
+ struct arguments_printenv *arguments = state->input;
+
+ switch (key) {
+ case ARGP_KEY_ARG:
+ /* too many arguments - program terminates with call to
+ * argp_usage with non-zero return code */
+ argp_usage(state);
+ break;
+ default:
+ return parse_common_opt(key, arg, false, &arguments->common);
+ }
+
+ return 0;
+}
+
static void dump_uservars(uint8_t *udata)
{
char *key, *value;
@@ -527,7 +559,7 @@ static void dump_env(BG_ENVDATA *env)
fprintf(stdout, "\n\n");
}

-static void update_environment(BGENV *env)
+static void update_environment(BGENV *env, bool verbosity)
{
if (verbosity) {
fprintf(stdout, "Processing journal...\n");
@@ -602,7 +634,8 @@ static int printenv_from_file(char *envfilepath) {
}
}

-static int dumpenv_to_file(char *envfilepath) {
+static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
+{
/* execute journal and write to file */
int result = 0;
BGENV env;
@@ -616,7 +649,7 @@ static int dumpenv_to_file(char *envfilepath) {
return 1;
}

- update_environment(&env);
+ update_environment(&env, verbosity);
if (verbosity) {
dump_env(env.data);
}
@@ -643,52 +676,83 @@ static int dumpenv_to_file(char *envfilepath) {
return result;
}

-int main(int argc, char **argv)
+/* This is the entrypoint for the command bg_printenv. */
+static error_t bg_printenv(int argc, char **argv)
{
- static struct argp argp_setenv = {options_setenv, parse_opt, NULL, doc};
- static struct argp argp_printenv = {options_printenv, parse_opt, NULL,
- doc};
- static struct argp *argp;
-
- bool write_mode = (bool)strstr(argv[0], "bg_setenv");
- if (write_mode) {
- argp = &argp_setenv;
-
- if (argc < 2) {
- printf("No task to perform. Please specify at least one"
- " optional argument. See --help for further"
- " information.\n");
- return 1;
- }
+ struct argp argp_printenv = {
+ .options = options_printenv,
+ .parser = parse_printenv_opt,
+ .doc = doc,
+ };

- } else {
- argp = &argp_printenv;
+ struct arguments_setenv arguments;
+ memset(&arguments, 0, sizeof(struct arguments_setenv));
+
+ error_t e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
+ if (e) {
+ return e;
+ }
+ if (arguments.common.envfilepath) {
+ int result = printenv_from_file(arguments.common.envfilepath);
+ free(arguments.common.envfilepath);
+ return result;
+ }
+
+ /* not in file mode */
+ if (!bgenv_init()) {
+ fprintf(stderr, "Error initializing FAT environment.\n");
+ return 1;
}

- struct arguments arguments;
- arguments.printenv = !write_mode;
- arguments.which_part = 0;
+ dump_envs();
+ bgenv_finalize();
+ return 0;
+}
+
+/* This is the entrypoint for the command bg_setenv. */
+static error_t bg_setenv(int argc, char **argv)
+{
+ if (argc < 2) {
+ printf("No task to perform. Please specify at least one"
+ " optional argument. See --help for further"
+ " information.\n");
+ return 1;
+ }
+
+ struct argp argp_setenv = {
+ .options = options_setenv,
+ .parser = parse_setenv_opt,
+ .doc = doc,
+ };
+
+ struct arguments_setenv arguments;
+ memset(&arguments, 0, sizeof(struct arguments_setenv));

STAILQ_INIT(&head);

error_t e;
- e = argp_parse(argp, argc, argv, 0, 0, &arguments);
+ e = argp_parse(&argp_setenv, argc, argv, 0, 0, &arguments);
if (e) {
return e;
}

+ if (arguments.auto_update && arguments.part_specified) {
+ fprintf(stderr, "Error, both automatic and manual partition "
+ "selection. Cannot use -p and -u "
+ "simultaneously.\n");
+ return 1;
+ }
+
int result = 0;

/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
- if (envfilepath) {
- if (write_mode) {
- result = dumpenv_to_file(envfilepath);
- } else {
- result = printenv_from_file(envfilepath);
- }
- free(envfilepath);
+ if (arguments.common.envfilepath) {
+ result = dumpenv_to_file(arguments.common.envfilepath,
+ arguments.common.verbosity,
+ arguments.preserve_env);
+ free(arguments.common.envfilepath);
return result;
}

@@ -698,20 +762,14 @@ int main(int argc, char **argv)
return 1;
}

- if (!write_mode) {
- dump_envs();
- bgenv_finalize();
- return 0;
- }
-
- if (verbosity) {
+ if (arguments.common.verbosity) {
dump_envs();
}

BGENV *env_new = NULL;
BGENV *env_current;

- if (auto_update) {
+ if (arguments.auto_update) {
/* clone latest environment */

env_current = bgenv_open_latest();
@@ -729,7 +787,7 @@ int main(int argc, char **argv)
result = 1;
goto cleanup;
}
- if (verbosity) {
+ if (arguments.common.verbosity) {
fprintf(stdout,
"Updating environment with revision %u\n",
env_new->data->revision);
@@ -748,7 +806,7 @@ int main(int argc, char **argv)

bgenv_close(env_current);
} else {
- if (part_specified) {
+ if (arguments.part_specified) {
env_new = bgenv_open_by_index(arguments.which_part);
} else {
env_new = bgenv_open_latest();
@@ -761,9 +819,9 @@ int main(int argc, char **argv)
}
}

- update_environment(env_new);
+ update_environment(env_new, arguments.common.verbosity);

- if (verbosity) {
+ if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
dump_env(env_new->data);
@@ -780,3 +838,12 @@ cleanup:
bgenv_finalize();
return result;
}
+
+int main(int argc, char **argv)
+{
+ if (strstr(argv[0], "bg_setenv")) {
+ return bg_setenv(argc, argv);
+ } else {
+ return bg_printenv(argc, argv);
+ }
+}
--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 4:41:22 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
The following new command-line arguments are introduced:

--current: print values from the current env
--output: list of fields to print
--raw: raw output mode, e.g. for shell scripting purposes
--part: set environment partition to use

The motivation for this change is to have a proper API to determine the
current ustate. Previously, one had to parse the output of bg_printenv
and rely on an unspecified interface (e.g. there is no guarantee that it
will work with the next release of efibootguard). Now there are even
tests for exactly this use-case.

Examples:

* Query the current ustate (and nothing else):

$ bg_printenv --current --output ustate

* Use raw output mode for shell scripting:

$ source <(bg_printenv --current --output kernel,ustate --raw)
$ echo "kernel: $KERNEL, ustate: $USTATE"

If you only care aboutt the ustate, you could also do the following:

$ USTATE=$(bg_printenv --current --output ustate --raw)
$ echo "ustate: $USTATE"

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tests/bg_setenv.bats | 40 ++++++
tools/bg_setenv.c | 326 +++++++++++++++++++++++++++++++++----------
2 files changed, 294 insertions(+), 72 deletions(-)

diff --git a/tests/bg_setenv.bats b/tests/bg_setenv.bats
index 87779e6..9dc7260 100755
--- a/tests/bg_setenv.bats
+++ b/tests/bg_setenv.bats
@@ -114,3 +114,43 @@ foo = bar" ]]
run md5sum "$envfile"
[[ "$output" =~ ^a24b154a48e1f33b79b87e0fa5eff8a1\s*.* ]]
}
+
+@test "bg_printenv ustate" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate
+ [[ "$output" = "Values:
+ustate: 0 (OK)" ]]
+}
+
+@test "bg_printenv ustate raw" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate --raw
+ [[ "$output" = "USTATE=0" ]]
+}
+
+@test "bg_printenv multiple fields raw" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate,kernel,kernelargs --raw
+ [[ "$output" = "KERNEL=C:BOOT:kernel.efi
+KERNELARGS=root=/dev/sda
+USTATE=0" ]]
+}
+
+@test "bg_printenv with all fields is the same as omitting fields" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ expected_output=$(bg_printenv "--filepath=$envfile")
+ run bg_printenv "--filepath=$envfile" --output in_progress,revision,kernel,kernelargs,watchdog_timeout,ustate,user
+ [[ "$output" = "$expected_output" ]]
+}
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 3f1620c..4498742 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -5,6 +5,7 @@
*
* Authors:
* Andreas Reichel <andreas.r...@siemens.com>
+ * Michael Adler <michae...@siemens.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
@@ -29,6 +30,9 @@ static char doc[] =
{"filepath", 'f', "ENVFILE", 0, \
"Environment to use. Expects a file name, " \
"usually called BGENV.DAT."}, \
+ {"part", 'p', "ENV_PART", 0, \
+ "Set environment partition to use. If no partition is specified, " \
+ "the one with the smallest revision value above zero is used."}, \
{"verbose", 'v', 0, 0, "Be verbose"}, \
{"version", 'V', 0, 0, "Print version"}
// clang-format on
@@ -38,9 +42,6 @@ static struct argp_option options_setenv[] = {
{"preserve", 'P', 0, 0, "Preserve existing entries"},
{"kernel", 'k', "KERNEL", 0, "Set kernel to load"},
{"args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"},
- {"part", 'p', "ENV_PART", 0,
- "Set environment partition to update. If no partition is specified, "
- "the one with the smallest revision value above zero is updated."},
{"revision", 'r', "REVISION", 0, "Set revision value"},
{"ustate", 's', "USTATE", 0, "Set update status for environment"},
{"watchdog", 'w', "WATCHDOG_TIMEOUT", 0, "Watchdog timeout in seconds"},
@@ -56,6 +57,14 @@ static struct argp_option options_setenv[] = {

static struct argp_option options_printenv[] = {
BG_CLI_OPTIONS_COMMON,
+ {"current", 'c', 0, 0,
+ "Only print values from the current environment"},
+ {"output", 'o', "LIST", 0,
+ "Comma-separated list of fields which are printed. "
+ "Available fields: in_progress, revision, kernel, kernelargs, "
+ "watchdog_timeout, ustate, user. "
+ "If omitted, all available fields are printed."},
+ {"raw", 'r', 0, 0, "Raw output mode, e.g. for shell scripting"},
{},
};

@@ -63,17 +72,19 @@ static struct argp_option options_printenv[] = {
struct arguments_common {
char *envfilepath;
bool verbosity;
+ /* which partition to operate on; a negative value means no partition
+ * was specified. */
+ int which_part;
+ bool part_specified;
};

/* Arguments used by bg_setenv. */
struct arguments_setenv {
struct arguments_common common;
- int which_part;
/* auto update feature automatically updates partition with
* oldest environment revision (lowest value)
*/
bool auto_update;
- bool part_specified;
/* whether to keep existing entries in BGENV before applying new
* settings */
bool preserve_env;
@@ -82,8 +93,23 @@ struct arguments_setenv {
/* Arguments used by bg_printenv. */
struct arguments_printenv {
struct arguments_common common;
+ bool current;
+ int output_fields;
+ bool raw;
};

+/* Bitmasks */
+#define MASK_IN_PROGRESS (1 << 0)
+#define MASK_REVISION (1 << 1)
+#define MASK_KERNEL (1 << 2)
+#define MASK_KERNELARGS (1 << 3)
+#define MASK_WDOG_TIMEOUT (1 << 4)
+#define MASK_USTATE (1 << 5)
+#define MASK_USER (1 << 6)
+#define MASK_ALL \
+ (MASK_IN_PROGRESS | MASK_REVISION | MASK_KERNEL | MASK_KERNELARGS | \
+ MASK_WDOG_TIMEOUT | MASK_USTATE | MASK_USER)
+
typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;

struct stailhead *headp;
@@ -250,6 +276,7 @@ static error_t parse_common_opt(int key, char *arg, bool compat_mode,
struct arguments_common *arguments)
{
bool found = false;
+ int i;
switch (key) {
case 'f':
found = true;
@@ -280,6 +307,24 @@ static error_t parse_common_opt(int key, char *arg, bool compat_mode,
}
}
break;
+ case 'p':
+ found = true;
+ i = parse_int(arg);
+ if (errno) {
+ fprintf(stderr, "Invalid number specified for -p.\n");
+ return 1;
+ }
+ if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
+ arguments->which_part = i;
+ arguments->part_specified = true;
+ } else {
+ fprintf(stderr,
+ "Selected partition out of range. Valid range: "
+ "0..%d.\n",
+ ENV_NUM_CONFIG_PARTS - 1);
+ return 1;
+ }
+ break;
case 'v':
found = true;
/* Set verbosity in this program */
@@ -328,23 +373,6 @@ static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
e = journal_add_action(ENV_TASK_SET, "kernelparams", 0,
(uint8_t *)arg, strlen(arg) + 1);
break;
- case 'p':
- i = parse_int(arg);
- if (errno) {
- fprintf(stderr, "Invalid number specified for -p.\n");
- return 1;
- }
- if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
- fprintf(stdout, "Updating config partition #%d\n", i);
- arguments->which_part = i;
- arguments->part_specified = true;
- } else {
- fprintf(stderr,
- "Selected partition out of range. Valid range: "
- "0..%d.\n", ENV_NUM_CONFIG_PARTS - 1);
- return 1;
- }
- break;
case 's':
i = parse_int(arg);
if (errno) {
@@ -452,11 +480,50 @@ static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
return e;
}

+static error_t parse_output_fields(char *fields, int *output_fields)
+{
+ char *token;
+ /* reset */
+ *output_fields = 0;
+ while ((token = strsep(&fields, ","))) {
+ if (*token == '\0') continue;
+ if (strcmp(token, "in_progress") == 0) {
+ *output_fields |= MASK_IN_PROGRESS;
+ } else if (strcmp(token, "revision") == 0) {
+ *output_fields |= MASK_REVISION;
+ } else if (strcmp(token, "kernel") == 0) {
+ *output_fields |= MASK_KERNEL;
+ } else if (strcmp(token, "kernelargs") == 0) {
+ *output_fields |= MASK_KERNELARGS;
+ } else if (strcmp(token, "watchdog_timeout") == 0) {
+ *output_fields |= MASK_WDOG_TIMEOUT;
+ } else if (strcmp(token, "ustate") == 0) {
+ *output_fields |= MASK_USTATE;
+ } else if (strcmp(token, "user") == 0) {
+ *output_fields |= MASK_USER;
+ } else {
+ fprintf(stderr, "Unknown output field: %s\n", token);
+ return 1;
+ }
+ }
+ return 0;
+}
+
static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
{
struct arguments_printenv *arguments = state->input;
+ error_t e = 0;

switch (key) {
+ case 'c':
+ arguments->current = true;
+ break;
+ case 'o':
+ e = parse_output_fields(arg, &arguments->output_fields);
+ break;
+ case 'r':
+ arguments->raw = true;
+ break;
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
@@ -466,10 +533,10 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
return parse_common_opt(key, arg, false, &arguments->common);
}

- return 0;
+ return e;
}

-static void dump_uservars(uint8_t *udata)
+static void dump_uservars(uint8_t *udata, bool raw)
{
char *key, *value;
uint64_t type;
@@ -477,13 +544,17 @@ static void dump_uservars(uint8_t *udata)
uint64_t val_unum;
int64_t val_snum;

+ if (!raw) {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "user variables:\n");
+ }
while (*udata) {
bgenv_map_uservar(udata, &key, &type, (uint8_t **)&value,
&rsize, &dsize);
- fprintf(stdout, "%s ", key);
+ fprintf(stdout, "%s", key);
type &= USERVAR_STANDARD_TYPE_MASK;
if (type == USERVAR_TYPE_STRING_ASCII) {
- fprintf(stdout, "= %s\n", value);
+ fprintf(stdout, raw ? "=%s\n" : " = %s\n", value);
} else if (type >= USERVAR_TYPE_UINT8 &&
type <= USERVAR_TYPE_UINT64) {
switch(type) {
@@ -500,8 +571,8 @@ static void dump_uservars(uint8_t *udata)
val_unum = *((uint64_t *) value);
break;
}
- fprintf(stdout, "= %llu\n",
- (long long unsigned int) val_unum);
+ fprintf(stdout, raw ? "=%llu\n" : " = %llu\n",
+ (long long unsigned int)val_unum);
} else if (type >= USERVAR_TYPE_SINT8 &&
type <= USERVAR_TYPE_SINT64) {
switch(type) {
@@ -518,19 +589,20 @@ static void dump_uservars(uint8_t *udata)
val_snum = *((int64_t *) value);
break;
}
- fprintf(stdout, "= %lld\n",
- (long long signed int) val_snum);
+ fprintf(stdout, raw ? "=%lld\n" : " = %lld\n",
+ (long long signed int)val_snum);
} else {
switch(type) {
case USERVAR_TYPE_CHAR:
- fprintf(stdout, "= %c\n", (char) *value);
+ fprintf(stdout, raw ? "=%c\n" : " = %c\n",
+ (char)*value);
break;
case USERVAR_TYPE_BOOL:
- fprintf(stdout, "= %s\n",
- (bool) *value ? "true" : "false");
+ fprintf(stdout, raw ? "=%s\n" : " = %s\n",
+ (bool)*value ? "true" : "false");
break;
default:
- fprintf(stdout, "( Type is not printable )\n");
+ fprintf(stdout, " ( Type is not printable )\n");
}
}

@@ -538,25 +610,67 @@ static void dump_uservars(uint8_t *udata)
}
}

-static void dump_env(BG_ENVDATA *env)
+static void dump_env(BG_ENVDATA *env, bool raw, int output_fields)
{
char buffer[ENV_STRING_LENGTH];
- fprintf(stdout, "Values:\n");
- fprintf(stdout,
- "in_progress: %s\n",env->in_progress ? "yes" : "no");
- fprintf(stdout, "revision: %u\n", env->revision);
- fprintf(stdout,
- "kernel: %s\n", str16to8(buffer, env->kernelfile));
- fprintf(stdout,
- "kernelargs: %s\n", str16to8(buffer, env->kernelparams));
- fprintf(stdout,
- "watchdog timeout: %u seconds\n", env->watchdog_timeout_sec);
- fprintf(stdout, "ustate: %u (%s)\n", (uint8_t)env->ustate,
- ustate2str(env->ustate));
- fprintf(stdout, "\n");
- fprintf(stdout, "user variables:\n");
- dump_uservars(env->userdata);
- fprintf(stdout, "\n\n");
+ if (!raw) {
+ fprintf(stdout, "Values:\n");
+ }
+ if (output_fields & MASK_IN_PROGRESS) {
+ if (raw) {
+ fprintf(stdout, "IN_PROGRESS=%d\n", env->in_progress);
+ } else {
+ fprintf(stdout, "in_progress: %s\n",
+ env->in_progress ? "yes" : "no");
+ }
+ }
+ if (output_fields & MASK_REVISION) {
+ if (raw) {
+ fprintf(stdout, "REVISION=%d\n", env->revision);
+ } else {
+ fprintf(stdout, "revision: %u\n",
+ env->revision);
+ }
+ }
+ if (output_fields & MASK_KERNEL) {
+ char *kernelfile = str16to8(buffer, env->kernelfile);
+ if (raw) {
+ fprintf(stdout, "KERNEL=%s\n", kernelfile);
+ } else {
+ fprintf(stdout, "kernel: %s\n", kernelfile);
+ }
+ }
+ if (output_fields & MASK_KERNELARGS) {
+ char *kernelargs = str16to8(buffer, env->kernelparams);
+ if (raw) {
+ fprintf(stdout, "KERNELARGS=%s\n", kernelargs);
+ } else {
+ fprintf(stdout, "kernelargs: %s\n", kernelargs);
+ }
+ }
+ if (output_fields & MASK_WDOG_TIMEOUT) {
+ if (raw) {
+ fprintf(stdout, "WATCHDOG_TIMEOUT=%u\n",
+ env->watchdog_timeout_sec);
+ } else {
+ fprintf(stdout, "watchdog timeout: %u seconds\n",
+ env->watchdog_timeout_sec);
+ }
+ }
+ if (output_fields & MASK_USTATE) {
+ if (raw) {
+ fprintf(stdout, "USTATE=%u\n", env->ustate);
+ } else {
+ fprintf(stdout, "ustate: %u (%s)\n",
+ (uint8_t)env->ustate, ustate2str(env->ustate));
+ }
+ }
+ if (output_fields & MASK_USER) {
+ dump_uservars(env->userdata, raw);
+ }
+ if (!raw) {
+ fprintf(stdout, "\n\n");
+ }
}

static void update_environment(BGENV *env, bool verbosity)
@@ -578,11 +692,13 @@ static void update_environment(BGENV *env, bool verbosity)

}

-static void dump_envs(void)
+static void dump_envs(bool raw, int output_fields)
{
for (int i = 0; i < ENV_NUM_CONFIG_PARTS; i++) {
- fprintf(stdout, "\n----------------------------\n");
- fprintf(stdout, " Config Partition #%d ", i);
+ if (!raw) {
+ fprintf(stdout, "\n----------------------------\n");
+ fprintf(stdout, " Config Partition #%d ", i);
+ }
BGENV *env = bgenv_open_by_index(i);
if (!env) {
fprintf(stderr, "Error, could not read environment "
@@ -590,11 +706,33 @@ static void dump_envs(void)
i);
return;
}
- dump_env(env->data);
+ dump_env(env->data, raw, output_fields);
bgenv_close(env);
}
}

+static void dump_latest_env(bool raw, int output_fields)
+{
+ BGENV *env = bgenv_open_latest();
+ if (!env) {
+ fprintf(stderr, "Failed to retrieve latest environment.\n");
+ return;
+ }
+ dump_env(env->data, raw, output_fields);
+ bgenv_close(env);
+}
+
+static void dump_env_by_index(uint32_t index, bool raw, int output_fields)
+{
+ BGENV *env = bgenv_open_by_index(index);
+ if (!env) {
+ fprintf(stderr, "Failed to retrieve latest environment.\n");
+ return;
+ }
+ dump_env(env->data, raw, output_fields);
+ bgenv_close(env);
+}
+
static bool get_env(char *configfilepath, BG_ENVDATA *data)
{
FILE *config;
@@ -620,13 +758,14 @@ static bool get_env(char *configfilepath, BG_ENVDATA *data)
return result;
}

-static int printenv_from_file(char *envfilepath) {
+static int printenv_from_file(char *envfilepath, bool raw, int output_fields)
+{
int success = 0;
BG_ENVDATA data;

success = get_env(envfilepath, &data);
if (success) {
- dump_env(&data);
+ dump_env(&data, raw, output_fields);
return 0;
} else {
fprintf(stderr, "Error reading environment file.\n");
@@ -651,7 +790,7 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)

update_environment(&env, verbosity);
if (verbosity) {
- dump_env(env.data);
+ dump_env(env.data, false, MASK_ALL);
}
FILE *of = fopen(envfilepath, "wb");
if (of) {
@@ -679,23 +818,49 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
/* This is the entrypoint for the command bg_printenv. */
static error_t bg_printenv(int argc, char **argv)
{
+ error_t e = 0;
struct argp argp_printenv = {
.options = options_printenv,
.parser = parse_printenv_opt,
.doc = doc,
};

- struct arguments_setenv arguments;
- memset(&arguments, 0, sizeof(struct arguments_setenv));
+ struct arguments_printenv arguments = {
+ .output_fields = MASK_ALL,
+ };

- error_t e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
+ e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
if (e) {
return e;
}
- if (arguments.common.envfilepath) {
- int result = printenv_from_file(arguments.common.envfilepath);
- free(arguments.common.envfilepath);
- return result;
+
+ const struct arguments_common *common = &arguments.common;
+
+ /* count the number of arguments which result in bg_printenv
+ * operating on a single partition; to avoid ambiguity, we only
+ * allow one such argument. */
+ int counter = 0;
+ if (common->envfilepath) ++counter;
+ if (common->part_specified) ++counter;
+ if (arguments.current) ++counter;
+ if (counter > 1) {
+ fprintf(stderr, "Error, only one of -f/-c/-p can be set.\n");
+ return 1;
+ }
+ if (arguments.raw && counter != 1) {
+ /* raw mode makes only sense if applied to a single
+ * partition */
+ fprintf(stderr, "Error, raw is set but "
+ "current/filepath/which_part is not set. "
+ "Must use -r and -c/-f/-p simultaneously.\n");
+ return 1;
+ }
+
+ if (common->envfilepath) {
+ e = printenv_from_file(common->envfilepath, arguments.raw,
+ arguments.output_fields);
+ free(common->envfilepath);
+ return e;
}

/* not in file mode */
@@ -704,9 +869,23 @@ static error_t bg_printenv(int argc, char **argv)
return 1;
}

- dump_envs();
+ if (arguments.current) {
+ if (!arguments.raw) {
+ fprintf(stdout, "Using latest config partition\n");
+ }
+ dump_latest_env(arguments.raw, arguments.output_fields);
+ } else if (common->part_specified) {
+ if (!arguments.raw) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
+ }
+ dump_env_by_index(common->which_part, arguments.raw,
+ arguments.output_fields);
+ } else {
+ dump_envs(arguments.raw, arguments.output_fields);
+ }
bgenv_finalize();
- return 0;
+ return e;
}

/* This is the entrypoint for the command bg_setenv. */
@@ -736,7 +915,7 @@ static error_t bg_setenv(int argc, char **argv)
return e;
}

- if (arguments.auto_update && arguments.part_specified) {
+ if (arguments.auto_update && arguments.common.part_specified) {
fprintf(stderr, "Error, both automatic and manual partition "
"selection. Cannot use -p and -u "
"simultaneously.\n");
@@ -763,7 +942,7 @@ static error_t bg_setenv(int argc, char **argv)
}

if (arguments.common.verbosity) {
- dump_envs();
+ dump_envs(false, MASK_ALL);
}

BGENV *env_new = NULL;
@@ -806,8 +985,11 @@ static error_t bg_setenv(int argc, char **argv)

bgenv_close(env_current);
} else {
- if (arguments.part_specified) {
- env_new = bgenv_open_by_index(arguments.which_part);
+ if (arguments.common.part_specified) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
+ env_new = bgenv_open_by_index(
+ arguments.common.which_part);
} else {
env_new = bgenv_open_latest();
}
@@ -824,7 +1006,7 @@ static error_t bg_setenv(int argc, char **argv)
if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
- dump_env(env_new->data);
+ dump_env(env_new->data, false, MASK_ALL);
}
if (!bgenv_write(env_new)) {
fprintf(stderr, "Error storing environment.\n");
--
2.33.0

Jan Kiszka

unread,
Nov 3, 2021, 5:07:21 AM11/3/21
to Michael Adler, efibootg...@googlegroups.com
On 03.11.21 09:40, Michael Adler wrote:
> Hi all,
>
> as promised a while back [1], here is my extension of bg_printenv. It didn't make it in time for the 0.9 release, but
> I first had to set up some automated testing [2] to prevent regressions.
>
> (There is still a blank spot in our test coverage, i.e. cli tests which do not use the envfile parameter. This is a bit
> tricky, since it requires setting up a (loopback) device and I'm not sure if this would work in CI. These kind of tests
> I have performed manually. Maybe I can revisit this topic if we can agree on the patches.)
>
> Looking forward to any feedback.

Thanks for the patches! 3 and 4 look rather large, and both do multiple
things at once. Can they by reasonably split along those things? Would
simplify review.

Jan

--
Siemens AG, T RDA IOT
Corporate Competence Center Embedded Linux

Michael Adler

unread,
Nov 3, 2021, 8:27:19 AM11/3/21
to Jan Kiszka, efibootg...@googlegroups.com
> Can they by reasonably split along those things? Would simplify review.

Yes, I will submit new versions of patches 3 and 4.

Michael

--
Michael Adler

Siemens AG
T RDA IOT SES-DE
Otto-Hahn-Ring 6
81739 München, Deutschland

Siemens Aktiengesellschaft: Vorsitzender des Aufsichtsrats: Jim Hagemann Snabe; Vorstand: Roland Busch, Vorsitzender; Klaus Helmrich, Cedrik Neike, Matthias Rebellius, Ralf P. Thomas, Judith Wiese; Sitz der Gesellschaft: Berlin und München, Deutschland; Registergericht: Berlin-Charlottenburg, HRB 12300, München, HRB 6684; WEEE-Reg.-Nr. DE 23691322

Michael Adler

unread,
Nov 3, 2021, 10:57:42 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
As requested, I have made more granular commits:

* Patches 1-2 are the same as in v1
* Patch 3 is split into patches 3-6
* Patch 4 is split into 7-9

This includes all patches from v1, i.e. the v1 thread is obsolete now.
I hope it's easier to review now (I know it's quite a lot).

If there are any questions, please do not hesitate to ask.

Michael Adler (9):
gitignore: ignore test binaries
clang-format: tweaks for current code style
bg_setenv: refactor: move arguments into struct
bg_setenv: refactor: decouple bg_printenv
bg_setenv: refactor: first parse, then validate
bg_setenv: refactor: split bg_setenv/bg_printenv parsers
bg_printenv: added --current and --part parameters
bg_printenv: added --output parameter
bg_printenv: added raw output mode

.clang-format | 2 +
.gitignore | 7 +
tests/bg_setenv.bats | 40 +++
tools/bg_setenv.c | 603 ++++++++++++++++++++++++++++++-------------
4 files changed, 474 insertions(+), 178 deletions(-)

--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 10:57:43 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
Signed-off-by: Michael Adler <michae...@siemens.com>
---

Michael Adler

unread,
Nov 3, 2021, 10:57:44 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
* `ContinuationIndentWidth: 8` ensures that in certain scenarios (e.g.
array of structs, defined over multiple lines) consistently uses tabs
instead of spaces for alignment.
* `AlignConsecutiveMacros` improves the formatting of preprocessor
instructions, see [1].

[1] https://bugs.llvm.org/show_bug.cgi?id=20637

Signed-off-by: Michael Adler <michae...@siemens.com>
---

Michael Adler

unread,
Nov 3, 2021, 10:57:45 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
This reduces global state and keeps the global namespace clean.

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 78 ++++++++++++++++++++++-------------------------
1 file changed, 37 insertions(+), 41 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index e564219..3e5a5c3 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -61,6 +61,16 @@ static struct argp_option options_printenv[] = {
struct arguments {
bool printenv;
int which_part;
+ /* auto update feature automatically updates partition with
+ * oldest environment revision (lowest value)
+ */
+ bool auto_update;
+ bool part_specified;
+ bool verbosity;
+ char *envfilepath;
+ /* whether to keep existing entries in BGENV before applying the new
+ * settings */
+ bool preserve_env;
};

typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;
@@ -163,20 +173,6 @@ static void journal_process_action(BGENV *env, struct env_action *action)
}
}

-/* auto update feature automatically updates partition with
- * oldest environment revision (lowest value)
- */
-static bool auto_update = false;
-
-static bool part_specified = false;
-
-static bool verbosity = false;
-
-static char *envfilepath = NULL;
-
-/* whether to keep existing entries in BGENV before applying the new settings */
-static bool preserve_env = false;
-
static char *ustatemap[] = {"OK", "INSTALLED", "TESTING", "FAILED", "UNKNOWN"};

static uint8_t str2ustate(char *str)
@@ -278,7 +274,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
fprintf(stdout, "Updating config partition #%d\n", i);
arguments->which_part = i;
- part_specified = true;
+ arguments->part_specified = true;
} else {
fprintf(stderr,
"Selected partition out of range. Valid range: "
@@ -362,8 +358,8 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
(uint8_t *)arg, strlen(arg) + 1);
break;
case 'f':
- free(envfilepath);
- envfilepath = NULL;
+ free(arguments->envfilepath);
+ arguments->envfilepath = NULL;

/* compat mode, permitting "bg_setenv -f <dir>" */
if (!arguments->printenv) {
@@ -374,7 +370,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
fprintf(stderr,
"WARNING: Using -f to specify only the "
"ouptut directory is deprecated.\n");
- res = asprintf(&envfilepath, "%s/%s", arg,
+ res = asprintf(&arguments->envfilepath, "%s/%s", arg,
FAT_ENV_FILENAME);
if (res == -1) {
return ENOMEM;
@@ -382,9 +378,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
}
}

- if (!envfilepath) {
- envfilepath = strdup(arg);
- if (!envfilepath) {
+ if (!arguments->envfilepath) {
+ arguments->envfilepath = strdup(arg);
+ if (!arguments->envfilepath) {
return ENOMEM;
}
}
@@ -397,18 +393,18 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
(uint8_t *)"0", 2);
break;
case 'u':
- if (part_specified) {
+ if (arguments->part_specified) {
fprintf(stderr,
"Error, both automatic and manual partition "
"selection. Cannot use -p and -u "
"simultaneously.\n");
return 1;
}
- auto_update = true;
+ arguments->auto_update = true;
break;
case 'v':
/* Set verbosity in this program */
- verbosity = true;
+ arguments->verbosity = true;
/* Set verbosity in the library */
bgenv_be_verbose(true);
break;
@@ -417,7 +413,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
e = set_uservars(arg);
break;
case 'P':
- preserve_env = true;
+ arguments->preserve_env = true;
break;
case 'V':
fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
@@ -527,7 +523,7 @@ static void dump_env(BG_ENVDATA *env)
fprintf(stdout, "\n\n");
}

-static void update_environment(BGENV *env)
+static void update_environment(BGENV *env, bool verbosity)
{
if (verbosity) {
fprintf(stdout, "Processing journal...\n");
@@ -602,7 +598,7 @@ static int printenv_from_file(char *envfilepath) {
}
}

-static int dumpenv_to_file(char *envfilepath) {
+static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env) {
/* execute journal and write to file */
int result = 0;
BGENV env;
@@ -616,7 +612,7 @@ static int dumpenv_to_file(char *envfilepath) {
return 1;
}

- update_environment(&env);
+ update_environment(&env, verbosity);
if (verbosity) {
dump_env(env.data);
}
@@ -665,9 +661,7 @@ int main(int argc, char **argv)
argp = &argp_printenv;
}

- struct arguments arguments;
- arguments.printenv = !write_mode;
- arguments.which_part = 0;
+ struct arguments arguments = {.printenv = !write_mode, .which_part = 0};

STAILQ_INIT(&head);

@@ -682,13 +676,15 @@ int main(int argc, char **argv)
/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
- if (envfilepath) {
+ if (arguments.envfilepath) {
if (write_mode) {
- result = dumpenv_to_file(envfilepath);
+ result = dumpenv_to_file(arguments.envfilepath,
+ arguments.verbosity,
+ arguments.preserve_env);
} else {
- result = printenv_from_file(envfilepath);
+ result = printenv_from_file(arguments.envfilepath);
}
- free(envfilepath);
+ free(arguments.envfilepath);
return result;
}

@@ -704,14 +700,14 @@ int main(int argc, char **argv)
return 0;
}

- if (verbosity) {
+ if (arguments.verbosity) {
dump_envs();
}

BGENV *env_new = NULL;
BGENV *env_current;

- if (auto_update) {
+ if (arguments.auto_update) {
/* clone latest environment */

env_current = bgenv_open_latest();
@@ -729,7 +725,7 @@ int main(int argc, char **argv)
result = 1;
goto cleanup;
}
- if (verbosity) {
+ if (arguments.verbosity) {
fprintf(stdout,
"Updating environment with revision %u\n",
env_new->data->revision);
@@ -748,7 +744,7 @@ int main(int argc, char **argv)

bgenv_close(env_current);
} else {
- if (part_specified) {
+ if (arguments.part_specified) {
env_new = bgenv_open_by_index(arguments.which_part);
} else {
env_new = bgenv_open_latest();
@@ -761,9 +757,9 @@ int main(int argc, char **argv)
}
}

- update_environment(env_new);
+ update_environment(env_new, arguments.verbosity);

- if (verbosity) {
+ if (arguments.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
dump_env(env_new->data);
--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 10:57:46 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
Currently, the implementations of bg_setenv and bg_printenv are tightly
coupled with each other, making it unnecessarily difficult to grasp,
modify and extend the existing control flow.

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 33 ++++++++++++++++++++-------------
1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 3e5a5c3..ec0aa31 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -639,6 +639,19 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
return result;
}

+/* This is the entrypoint for the command bg_printenv. */
+static int bg_printenv(const struct arguments *arguments)
+{
+ if (arguments->envfilepath) {
+ int result = printenv_from_file(arguments->envfilepath);
+ free(arguments->envfilepath);
+ return result;
+ }
+ dump_envs();
+ bgenv_finalize();
+ return 0;
+}
+
int main(int argc, char **argv)
{
static struct argp argp_setenv = {options_setenv, parse_opt, NULL, doc};
@@ -671,19 +684,19 @@ int main(int argc, char **argv)
return e;
}

+ if (!write_mode) {
+ return bg_printenv(&arguments);
+ }
+
int result = 0;

/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
if (arguments.envfilepath) {
- if (write_mode) {
- result = dumpenv_to_file(arguments.envfilepath,
- arguments.verbosity,
- arguments.preserve_env);
- } else {
- result = printenv_from_file(arguments.envfilepath);
- }
+ result = dumpenv_to_file(arguments.envfilepath,
+ arguments.verbosity,
+ arguments.preserve_env);
free(arguments.envfilepath);
return result;
}
@@ -694,12 +707,6 @@ int main(int argc, char **argv)
return 1;
}

- if (!write_mode) {
- dump_envs();
- bgenv_finalize();
- return 0;
- }
-
if (arguments.verbosity) {
dump_envs();
}
--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 10:57:48 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
Do not rely on parsing order for arg validation.

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index ec0aa31..3e76156 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -393,13 +393,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
(uint8_t *)"0", 2);
break;
case 'u':
- if (arguments->part_specified) {
- fprintf(stderr,
- "Error, both automatic and manual partition "
- "selection. Cannot use -p and -u "
- "simultaneously.\n");
- return 1;
- }
arguments->auto_update = true;
break;
case 'v':
@@ -690,6 +683,13 @@ int main(int argc, char **argv)

int result = 0;

+ if (arguments.auto_update && arguments.part_specified) {
+ fprintf(stderr, "Error, both automatic and manual partition "
+ "selection. Cannot use -p and -u "
+ "simultaneously.\n");
+ return 1;
+ }
+
/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 10:57:49 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
bg_setenv and bg_printenv have different command-line interfaces. Thus
it is natural to have separate parsers and more importantly, store the
arguments of each cli into its own struct, thereby avoiding potential
confusion when processing the arguments later on.

* `struct arguments` is split into `arguments_setenv` and
`arguments_printenv`
* `parse_opt` is split into `parse_common_opt`, `parse_setenv_opt` and
`parse_printenv_opt`

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 258 +++++++++++++++++++++++++++++-----------------
1 file changed, 161 insertions(+), 97 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 3e76156..de1a622 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -24,7 +24,17 @@
static char doc[] =
"bg_setenv/bg_printenv - Environment tool for the EFI Boot Guard";

+// clang-format off
+#define BG_CLI_OPTIONS_COMMON \
+ {"filepath", 'f', "ENVFILE", 0, \
+ "Environment to use. Expects a file name, " \
+ "usually called BGENV.DAT."}, \
+ {"verbose", 'v', 0, 0, "Be verbose"}, \
+ {"version", 'V', 0, 0, "Print version"}
+// clang-format on
+
static struct argp_option options_setenv[] = {
+ BG_CLI_OPTIONS_COMMON,
{"preserve", 'P', 0, 0, "Preserve existing entries"},
{"kernel", 'k', "KERNEL", 0, "Set kernel to load"},
{"args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"},
@@ -33,46 +43,47 @@ static struct argp_option options_setenv[] = {
"the one with the smallest revision value above zero is updated."},
{"revision", 'r', "REVISION", 0, "Set revision value"},
{"ustate", 's', "USTATE", 0, "Set update status for environment"},
- {"filepath", 'f', "ENVFILE", 0,
- "Output environment to file. Expects an output file name, "
- "usually called BGENV.DAT."},
{"watchdog", 'w', "WATCHDOG_TIMEOUT", 0, "Watchdog timeout in seconds"},
{"confirm", 'c', 0, 0, "Confirm working environment"},
{"update", 'u', 0, 0, "Automatically update oldest revision"},
- {"verbose", 'v', 0, 0, "Be verbose"},
{"uservar", 'x', "KEY=VAL", 0,
"Set user-defined string variable. For setting multiple variables, "
"use this option multiple times."},
{"in_progress", 'i', "IN_PROGRESS", 0,
"Set in_progress variable to simulate a running update process."},
- {"version", 'V', 0, 0, "Print version"},
- {}
+ {},
};

static struct argp_option options_printenv[] = {
- {"filepath", 'f', "ENVFILE", 0,
- "Read environment from file. Expects a valid EFI Boot Guard "
- "environment file."},
- {"verbose", 'v', 0, 0, "Be verbose"},
- {"version", 'V', 0, 0, "Print version"},
- {}
+ BG_CLI_OPTIONS_COMMON,
+ {},
+};
+
+/* Common arguments used by both bg_setenv and bg_printenv. */
+struct arguments_common {
+ char *envfilepath;
+ bool verbosity;
};

-struct arguments {
- bool printenv;
+/* Arguments used by bg_setenv. */
+struct arguments_setenv {
+ struct arguments_common common;
int which_part;
/* auto update feature automatically updates partition with
* oldest environment revision (lowest value)
*/
bool auto_update;
bool part_specified;
- bool verbosity;
- char *envfilepath;
- /* whether to keep existing entries in BGENV before applying the new
+ /* whether to keep existing entries in BGENV before applying new
* settings */
bool preserve_env;
};

+/* Arguments used by bg_printenv. */
+struct arguments_printenv {
+ struct arguments_common common;
+};
+
typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;

struct stailhead *headp;
@@ -235,9 +246,61 @@ static int parse_int(char *arg)
return (int) i;
}

-static error_t parse_opt(int key, char *arg, struct argp_state *state)
+static error_t parse_common_opt(int key, char *arg, bool compat_mode,
+ struct arguments_common *arguments)
+{
+ bool found = false;
+ switch (key) {
+ case 'f':
+ found = true;
+ free(arguments->envfilepath);
+ arguments->envfilepath = NULL;
+
+ if (compat_mode) {
+ /* compat mode, permitting "bg_setenv -f <dir>" */
+ struct stat sb;
+
+ int res = stat(arg, &sb);
+ if (res == 0 && S_ISDIR(sb.st_mode)) {
+ fprintf(stderr,
+ "WARNING: Using -f to specify only the "
+ "ouptut directory is deprecated.\n");
+ res = asprintf(&arguments->envfilepath, "%s/%s",
+ arg, FAT_ENV_FILENAME);
+ if (res == -1) {
+ return ENOMEM;
+ }
+ }
+ }
+
+ if (!arguments->envfilepath) {
+ arguments->envfilepath = strdup(arg);
+ if (!arguments->envfilepath) {
+ return ENOMEM;
+ }
+ }
+ break;
+ case 'v':
+ found = true;
+ /* Set verbosity in this program */
+ arguments->verbosity = true;
+ /* Set verbosity in the library */
+ bgenv_be_verbose(true);
+ break;
+ case 'V':
+ found = true;
+ fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
+ exit(0);
+ }
+ if (!found) {
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
{
- struct arguments *arguments = state->input;
+ struct arguments_setenv *arguments = state->input;
int i, res;
char *tmp;
error_t e = 0;
@@ -357,34 +420,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
"watchdog_timeout_sec", 0,
(uint8_t *)arg, strlen(arg) + 1);
break;
- case 'f':
- free(arguments->envfilepath);
- arguments->envfilepath = NULL;
-
- /* compat mode, permitting "bg_setenv -f <dir>" */
- if (!arguments->printenv) {
- struct stat sb;
-
- res = stat(arg, &sb);
- if (res == 0 && S_ISDIR(sb.st_mode)) {
- fprintf(stderr,
- "WARNING: Using -f to specify only the "
- "ouptut directory is deprecated.\n");
- res = asprintf(&arguments->envfilepath, "%s/%s", arg,
- FAT_ENV_FILENAME);
- if (res == -1) {
- return ENOMEM;
- }
- }
- }
-
- if (!arguments->envfilepath) {
- arguments->envfilepath = strdup(arg);
- if (!arguments->envfilepath) {
- return ENOMEM;
- }
- }
- break;
case 'c':
VERBOSE(stdout,
"Confirming environment to work. Removing boot-once "
@@ -395,12 +430,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
case 'u':
arguments->auto_update = true;
break;
- case 'v':
- /* Set verbosity in this program */
- arguments->verbosity = true;
- /* Set verbosity in the library */
- bgenv_be_verbose(true);
- break;
case 'x':
/* Set user-defined variable(s) */
e = set_uservars(arg);
@@ -408,16 +437,13 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
case 'P':
arguments->preserve_env = true;
break;
- case 'V':
- fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
- exit(0);
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
argp_usage(state);
break;
default:
- return ARGP_ERR_UNKNOWN;
+ return parse_common_opt(key, arg, true, &arguments->common);
}

if (e) {
@@ -426,6 +452,23 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
return e;
}

+static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
+{
+ struct arguments_printenv *arguments = state->input;
+
+ switch (key) {
+ case ARGP_KEY_ARG:
+ /* too many arguments - program terminates with call to
+ * argp_usage with non-zero return code */
+ argp_usage(state);
+ break;
+ default:
+ return parse_common_opt(key, arg, false, &arguments->common);
+ }
+
+ return 0;
+}
+
static void dump_uservars(uint8_t *udata)
{
char *key, *value;
@@ -591,7 +634,8 @@ static int printenv_from_file(char *envfilepath) {
}
}

-static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env) {
+static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
+{
/* execute journal and write to file */
int result = 0;
BGENV env;
@@ -633,56 +677,65 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
}

/* This is the entrypoint for the command bg_printenv. */
-static int bg_printenv(const struct arguments *arguments)
+static error_t bg_printenv(int argc, char **argv)
{
- if (arguments->envfilepath) {
- int result = printenv_from_file(arguments->envfilepath);
- free(arguments->envfilepath);
+ struct argp argp_printenv = {
+ .options = options_printenv,
+ .parser = parse_printenv_opt,
+ .doc = doc,
+ };
+
+ struct arguments_printenv arguments;
+ memset(&arguments, 0, sizeof(struct arguments_printenv));
+
+ error_t e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
+ if (e) {
+ return e;
+ }
+ if (arguments.common.envfilepath) {
+ int result = printenv_from_file(arguments.common.envfilepath);
+ free(arguments.common.envfilepath);
return result;
}
+
+ /* not in file mode */
+ if (!bgenv_init()) {
+ fprintf(stderr, "Error initializing FAT environment.\n");
+ return 1;
+ }
+
dump_envs();
bgenv_finalize();
return 0;
}

-int main(int argc, char **argv)
+/* This is the entrypoint for the command bg_setenv. */
+static error_t bg_setenv(int argc, char **argv)
{
- static struct argp argp_setenv = {options_setenv, parse_opt, NULL, doc};
- static struct argp argp_printenv = {options_printenv, parse_opt, NULL,
- doc};
- static struct argp *argp;
-
- bool write_mode = (bool)strstr(argv[0], "bg_setenv");
- if (write_mode) {
- argp = &argp_setenv;
-
- if (argc < 2) {
- printf("No task to perform. Please specify at least one"
- " optional argument. See --help for further"
- " information.\n");
- return 1;
- }
-
- } else {
- argp = &argp_printenv;
+ if (argc < 2) {
+ printf("No task to perform. Please specify at least one"
+ " optional argument. See --help for further"
+ " information.\n");
+ return 1;
}

- struct arguments arguments = {.printenv = !write_mode, .which_part = 0};
+ struct argp argp_setenv = {
+ .options = options_setenv,
+ .parser = parse_setenv_opt,
+ .doc = doc,
+ };
+
+ struct arguments_setenv arguments;
+ memset(&arguments, 0, sizeof(struct arguments_setenv));

STAILQ_INIT(&head);

error_t e;
- e = argp_parse(argp, argc, argv, 0, 0, &arguments);
+ e = argp_parse(&argp_setenv, argc, argv, 0, 0, &arguments);
if (e) {
return e;
}

- if (!write_mode) {
- return bg_printenv(&arguments);
- }
-
- int result = 0;
-
if (arguments.auto_update && arguments.part_specified) {
fprintf(stderr, "Error, both automatic and manual partition "
"selection. Cannot use -p and -u "
@@ -690,14 +743,16 @@ int main(int argc, char **argv)
return 1;
}

+ int result = 0;
+
/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
- if (arguments.envfilepath) {
- result = dumpenv_to_file(arguments.envfilepath,
- arguments.verbosity,
+ if (arguments.common.envfilepath) {
+ result = dumpenv_to_file(arguments.common.envfilepath,
+ arguments.common.verbosity,
arguments.preserve_env);
- free(arguments.envfilepath);
+ free(arguments.common.envfilepath);
return result;
}

@@ -707,7 +762,7 @@ int main(int argc, char **argv)
return 1;
}

- if (arguments.verbosity) {
+ if (arguments.common.verbosity) {
dump_envs();
}

@@ -732,7 +787,7 @@ int main(int argc, char **argv)
result = 1;
goto cleanup;
}
- if (arguments.verbosity) {
+ if (arguments.common.verbosity) {
fprintf(stdout,
"Updating environment with revision %u\n",
env_new->data->revision);
@@ -764,9 +819,9 @@ int main(int argc, char **argv)
}
}

- update_environment(env_new, arguments.verbosity);
+ update_environment(env_new, arguments.common.verbosity);

- if (arguments.verbosity) {
+ if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
dump_env(env_new->data);
@@ -783,3 +838,12 @@ cleanup:

Michael Adler

unread,
Nov 3, 2021, 10:57:50 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
This introduces the following new command-line arguments:

--current: print values from the current env
--part: read from a specific environment partition

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 119 ++++++++++++++++++++++++++++++++++------------
1 file changed, 89 insertions(+), 30 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index de1a622..5ce4809 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -5,6 +5,7 @@
*
* Authors:
* Andreas Reichel <andreas.r...@siemens.com>
+ * Michael Adler <michae...@siemens.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
@@ -29,6 +30,9 @@ static char doc[] =
{"filepath", 'f', "ENVFILE", 0, \
"Environment to use. Expects a file name, " \
"usually called BGENV.DAT."}, \
+ {"part", 'p', "ENV_PART", 0, \
+ "Set environment partition to use. If no partition is specified, " \
+ "the one with the smallest revision value above zero is used."}, \
{"verbose", 'v', 0, 0, "Be verbose"}, \
{"version", 'V', 0, 0, "Print version"}
// clang-format on
@@ -38,9 +42,6 @@ static struct argp_option options_setenv[] = {
{"preserve", 'P', 0, 0, "Preserve existing entries"},
{"kernel", 'k', "KERNEL", 0, "Set kernel to load"},
{"args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"},
- {"part", 'p', "ENV_PART", 0,
- "Set environment partition to update. If no partition is specified, "
- "the one with the smallest revision value above zero is updated."},
{"revision", 'r', "REVISION", 0, "Set revision value"},
{"ustate", 's', "USTATE", 0, "Set update status for environment"},
{"watchdog", 'w', "WATCHDOG_TIMEOUT", 0, "Watchdog timeout in seconds"},
@@ -56,6 +57,8 @@ static struct argp_option options_setenv[] = {

static struct argp_option options_printenv[] = {
BG_CLI_OPTIONS_COMMON,
+ {"current", 'c', 0, 0,
+ "Only print values from the current environment"},
{},
};

@@ -63,17 +66,19 @@ static struct argp_option options_printenv[] = {
struct arguments_common {
char *envfilepath;
bool verbosity;
+ /* which partition to operate on; a negative value means no partition
+ * was specified. */
+ int which_part;
+ bool part_specified;
};

/* Arguments used by bg_setenv. */
struct arguments_setenv {
struct arguments_common common;
- int which_part;
/* auto update feature automatically updates partition with
* oldest environment revision (lowest value)
*/
bool auto_update;
- bool part_specified;
/* whether to keep existing entries in BGENV before applying new
* settings */
bool preserve_env;
@@ -82,6 +87,7 @@ struct arguments_setenv {
/* Arguments used by bg_printenv. */
struct arguments_printenv {
struct arguments_common common;
+ bool current;
};

typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;
@@ -250,6 +256,7 @@ static error_t parse_common_opt(int key, char *arg, bool compat_mode,
struct arguments_common *arguments)
{
bool found = false;
+ int i;
switch (key) {
case 'f':
found = true;
@@ -280,6 +287,24 @@ static error_t parse_common_opt(int key, char *arg, bool compat_mode,
}
}
break;
+ case 'p':
+ found = true;
+ i = parse_int(arg);
+ if (errno) {
+ fprintf(stderr, "Invalid number specified for -p.\n");
+ return 1;
+ }
+ if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
+ arguments->which_part = i;
+ arguments->part_specified = true;
+ } else {
+ fprintf(stderr,
+ "Selected partition out of range. Valid range: "
+ "0..%d.\n",
+ ENV_NUM_CONFIG_PARTS - 1);
+ return 1;
+ }
+ break;
case 'v':
found = true;
/* Set verbosity in this program */
@@ -328,23 +353,6 @@ static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
e = journal_add_action(ENV_TASK_SET, "kernelparams", 0,
(uint8_t *)arg, strlen(arg) + 1);
break;
- case 'p':
- i = parse_int(arg);
- if (errno) {
- fprintf(stderr, "Invalid number specified for -p.\n");
- return 1;
- }
- if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
- fprintf(stdout, "Updating config partition #%d\n", i);
- arguments->which_part = i;
- arguments->part_specified = true;
- } else {
- fprintf(stderr,
- "Selected partition out of range. Valid range: "
- "0..%d.\n", ENV_NUM_CONFIG_PARTS - 1);
- return 1;
- }
- break;
case 's':
i = parse_int(arg);
if (errno) {
@@ -457,6 +465,9 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
struct arguments_printenv *arguments = state->input;

switch (key) {
+ case 'c':
+ arguments->current = true;
+ break;
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
@@ -595,6 +606,28 @@ static void dump_envs(void)
}
}

+static void dump_latest_env(void)
+{
+ BGENV *env = bgenv_open_latest();
+ if (!env) {
+ fprintf(stderr, "Failed to retrieve latest environment.\n");
+ return;
+ }
+ dump_env(env->data);
+ bgenv_close(env);
+}
+
+static void dump_env_by_index(uint32_t index)
+{
+ BGENV *env = bgenv_open_by_index(index);
+ if (!env) {
+ fprintf(stderr, "Failed to retrieve latest environment.\n");
+ return;
+ }
+ dump_env(env->data);
+ bgenv_close(env);
+}
+
static bool get_env(char *configfilepath, BG_ENVDATA *data)
{
FILE *config;
@@ -692,10 +725,25 @@ static error_t bg_printenv(int argc, char **argv)
if (e) {
return e;
}
- if (arguments.common.envfilepath) {
- int result = printenv_from_file(arguments.common.envfilepath);
- free(arguments.common.envfilepath);
- return result;
+
+ const struct arguments_common *common = &arguments.common;
+
+ /* count the number of arguments which result in bg_printenv
+ * operating on a single partition; to avoid ambiguity, we only
+ * allow one such argument. */
+ int counter = 0;
+ if (common->envfilepath) ++counter;
+ if (common->part_specified) ++counter;
+ if (arguments.current) ++counter;
+ if (counter > 1) {
+ fprintf(stderr, "Error, only one of -c/-f/-p can be set.\n");
+ return 1;
+ }
+
+ if (common->envfilepath) {
+ e = printenv_from_file(common->envfilepath);
+ free(common->envfilepath);
+ return e;
}

/* not in file mode */
@@ -704,7 +752,17 @@ static error_t bg_printenv(int argc, char **argv)
return 1;
}

- dump_envs();
+ if (arguments.current) {
+ fprintf(stdout, "Using latest config partition\n");
+ dump_latest_env();
+ } else if (common->part_specified) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
+ dump_env_by_index(common->which_part);
+ } else {
+ dump_envs();
+ }
+
bgenv_finalize();
return 0;
}
@@ -736,7 +794,7 @@ static error_t bg_setenv(int argc, char **argv)
return e;
}

- if (arguments.auto_update && arguments.part_specified) {
+ if (arguments.auto_update && arguments.common.part_specified) {
fprintf(stderr, "Error, both automatic and manual partition "
"selection. Cannot use -p and -u "
"simultaneously.\n");
@@ -806,8 +864,9 @@ static error_t bg_setenv(int argc, char **argv)

bgenv_close(env_current);
} else {
- if (arguments.part_specified) {
- env_new = bgenv_open_by_index(arguments.which_part);
+ if (arguments.common.part_specified) {
+ env_new = bgenv_open_by_index(
+ arguments.common.which_part);
} else {
env_new = bgenv_open_latest();
}
--
2.33.0

Michael Adler

unread,
Nov 3, 2021, 10:57:50 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
This allows to specify a list of fields which are printed.

Example:

$ bg_printenv --current --output ustate
Using latest config partition
Values:
ustate: 0 (OK)

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tests/bg_setenv.bats | 20 +++++++
tools/bg_setenv.c | 132 ++++++++++++++++++++++++++++++++-----------
2 files changed, 119 insertions(+), 33 deletions(-)

diff --git a/tests/bg_setenv.bats b/tests/bg_setenv.bats
index 87779e6..f4739fd 100755
--- a/tests/bg_setenv.bats
+++ b/tests/bg_setenv.bats
@@ -114,3 +114,23 @@ foo = bar" ]]
run md5sum "$envfile"
[[ "$output" =~ ^a24b154a48e1f33b79b87e0fa5eff8a1\s*.* ]]
}
+
+@test "bg_printenv ustate" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate
+ [[ "$output" = "Values:
+ustate: 0 (OK)" ]]
+}
+
+@test "bg_printenv with all fields is the same as omitting fields" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ expected_output=$(bg_printenv "--filepath=$envfile")
+ run bg_printenv "--filepath=$envfile" --output in_progress,revision,kernel,kernelargs,watchdog_timeout,ustate,user
+ [[ "$output" = "$expected_output" ]]
+}
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 5ce4809..2f6105f 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -59,6 +59,11 @@ static struct argp_option options_printenv[] = {
BG_CLI_OPTIONS_COMMON,
{"current", 'c', 0, 0,
"Only print values from the current environment"},
+ {"output", 'o', "LIST", 0,
+ "Comma-separated list of fields which are printed. "
+ "Available fields: in_progress, revision, kernel, kernelargs, "
+ "watchdog_timeout, ustate, user. "
+ "If omitted, all available fields are printed."},
{},
};

@@ -88,8 +93,19 @@ struct arguments_setenv {
struct arguments_printenv {
struct arguments_common common;
bool current;
+ /* a bitset to decide which fields are printed */
+ int output_fields;
};

+#define MASK_IN_PROGRESS (1 << 0)
+#define MASK_REVISION (1 << 1)
+#define MASK_KERNEL (1 << 2)
+#define MASK_KERNELARGS (1 << 3)
+#define MASK_WDOG_TIMEOUT (1 << 4)
+#define MASK_USTATE (1 << 5)
+#define MASK_USER (1 << 6)
+#define MASK_ALL 0x7F /* binary: 1111111 */
+
typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;

struct stailhead *headp;
@@ -460,14 +476,47 @@ static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
return e;
}

+static error_t parse_output_fields(char *fields, int *output_fields)
+{
+ char *token;
+ /* reset */
+ *output_fields = 0;
+ while ((token = strsep(&fields, ","))) {
+ if (*token == '\0') continue;
+ if (strcmp(token, "in_progress") == 0) {
+ *output_fields |= MASK_IN_PROGRESS;
+ } else if (strcmp(token, "revision") == 0) {
+ *output_fields |= MASK_REVISION;
+ } else if (strcmp(token, "kernel") == 0) {
+ *output_fields |= MASK_KERNEL;
+ } else if (strcmp(token, "kernelargs") == 0) {
+ *output_fields |= MASK_KERNELARGS;
+ } else if (strcmp(token, "watchdog_timeout") == 0) {
+ *output_fields |= MASK_WDOG_TIMEOUT;
+ } else if (strcmp(token, "ustate") == 0) {
+ *output_fields |= MASK_USTATE;
+ } else if (strcmp(token, "user") == 0) {
+ *output_fields |= MASK_USER;
+ } else {
+ fprintf(stderr, "Unknown output field: %s\n", token);
+ return 1;
+ }
+ }
+ return 0;
+}
+
static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
{
struct arguments_printenv *arguments = state->input;
+ error_t e = 0;

switch (key) {
case 'c':
arguments->current = true;
break;
+ case 'o':
+ e = parse_output_fields(arg, &arguments->output_fields);
+ break;
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
@@ -477,7 +526,7 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
return parse_common_opt(key, arg, false, &arguments->common);
}

- return 0;
+ return e;
}

static void dump_uservars(uint8_t *udata)
@@ -549,24 +598,38 @@ static void dump_uservars(uint8_t *udata)
}
}

-static void dump_env(BG_ENVDATA *env)
+static void dump_env(BG_ENVDATA *env, int output_fields)
{
char buffer[ENV_STRING_LENGTH];
fprintf(stdout, "Values:\n");
- fprintf(stdout,
- "in_progress: %s\n",env->in_progress ? "yes" : "no");
- fprintf(stdout, "revision: %u\n", env->revision);
- fprintf(stdout,
- "kernel: %s\n", str16to8(buffer, env->kernelfile));
- fprintf(stdout,
- "kernelargs: %s\n", str16to8(buffer, env->kernelparams));
- fprintf(stdout,
- "watchdog timeout: %u seconds\n", env->watchdog_timeout_sec);
- fprintf(stdout, "ustate: %u (%s)\n", (uint8_t)env->ustate,
- ustate2str(env->ustate));
- fprintf(stdout, "\n");
- fprintf(stdout, "user variables:\n");
- dump_uservars(env->userdata);
+ if (output_fields & MASK_IN_PROGRESS) {
+ fprintf(stdout, "in_progress: %s\n",
+ env->in_progress ? "yes" : "no");
+ }
+ if (output_fields & MASK_REVISION) {
+ fprintf(stdout, "revision: %u\n", env->revision);
+ }
+ if (output_fields & MASK_KERNEL) {
+ fprintf(stdout, "kernel: %s\n",
+ str16to8(buffer, env->kernelfile));
+ }
+ if (output_fields & MASK_KERNELARGS) {
+ fprintf(stdout, "kernelargs: %s\n",
+ str16to8(buffer, env->kernelparams));
+ }
+ if (output_fields & MASK_WDOG_TIMEOUT) {
+ fprintf(stdout, "watchdog timeout: %u seconds\n",
+ env->watchdog_timeout_sec);
+ }
+ if (output_fields & MASK_USTATE) {
+ fprintf(stdout, "ustate: %u (%s)\n",
+ (uint8_t)env->ustate, ustate2str(env->ustate));
+ }
+ if (output_fields & MASK_USER) {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "user variables:\n");
+ dump_uservars(env->userdata);
+ }
fprintf(stdout, "\n\n");
}

@@ -589,7 +652,7 @@ static void update_environment(BGENV *env, bool verbosity)

}

-static void dump_envs(void)
+static void dump_envs(int output_fields)
{
for (int i = 0; i < ENV_NUM_CONFIG_PARTS; i++) {
fprintf(stdout, "\n----------------------------\n");
@@ -601,30 +664,30 @@ static void dump_envs(void)
i);
return;
}
- dump_env(env->data);
+ dump_env(env->data, output_fields);
bgenv_close(env);
}
}

-static void dump_latest_env(void)
+static void dump_latest_env(int output_fields)
{
BGENV *env = bgenv_open_latest();
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data);
+ dump_env(env->data, output_fields);
bgenv_close(env);
}

-static void dump_env_by_index(uint32_t index)
+static void dump_env_by_index(uint32_t index, int output_fields)
{
BGENV *env = bgenv_open_by_index(index);
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data);
+ dump_env(env->data, output_fields);
bgenv_close(env);
}

@@ -653,13 +716,14 @@ static bool get_env(char *configfilepath, BG_ENVDATA *data)
return result;
}

-static int printenv_from_file(char *envfilepath) {
+static int printenv_from_file(char *envfilepath, int output_fields)
+{
int success = 0;
BG_ENVDATA data;

success = get_env(envfilepath, &data);
if (success) {
- dump_env(&data);
+ dump_env(&data, output_fields);
return 0;
} else {
fprintf(stderr, "Error reading environment file.\n");
@@ -684,7 +748,7 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)

update_environment(&env, verbosity);
if (verbosity) {
- dump_env(env.data);
+ dump_env(env.data, MASK_ALL);
}
FILE *of = fopen(envfilepath, "wb");
if (of) {
@@ -718,8 +782,9 @@ static error_t bg_printenv(int argc, char **argv)
.doc = doc,
};

- struct arguments_printenv arguments;
- memset(&arguments, 0, sizeof(struct arguments_printenv));
+ struct arguments_printenv arguments = {
+ .output_fields = MASK_ALL,
+ };

error_t e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
if (e) {
@@ -741,7 +806,8 @@ static error_t bg_printenv(int argc, char **argv)
}

if (common->envfilepath) {
- e = printenv_from_file(common->envfilepath);
+ e = printenv_from_file(common->envfilepath,
+ arguments.output_fields);
free(common->envfilepath);
return e;
}
@@ -754,13 +820,13 @@ static error_t bg_printenv(int argc, char **argv)

if (arguments.current) {
fprintf(stdout, "Using latest config partition\n");
- dump_latest_env();
+ dump_latest_env(arguments.output_fields);
} else if (common->part_specified) {
fprintf(stdout, "Using config partition #%d\n",
arguments.common.which_part);
- dump_env_by_index(common->which_part);
+ dump_env_by_index(common->which_part, arguments.output_fields);
} else {
- dump_envs();
+ dump_envs(arguments.output_fields);
}

bgenv_finalize();
@@ -821,7 +887,7 @@ static error_t bg_setenv(int argc, char **argv)
}

if (arguments.common.verbosity) {
- dump_envs();
+ dump_envs(MASK_ALL);
}

BGENV *env_new = NULL;
@@ -883,7 +949,7 @@ static error_t bg_setenv(int argc, char **argv)
if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
- dump_env(env_new->data);
+ dump_env(env_new->data, MASK_ALL);

Michael Adler

unread,
Nov 3, 2021, 10:57:52 AM11/3/21
to efibootg...@googlegroups.com, Michael Adler
Adding --raw to the bg_printenv command-line enables raw output mode
which can be used for shell scripting purposes.

Examples:

* Bring the fields as variables into your shell:

$ source <(bg_printenv --current --output kernel,ustate --raw)
$ echo "kernel: $KERNEL, ustate: $USTATE"
kernel: C:BOOTA:linux.efi, ustate: 0

* Assing the current ustate to a variable:

$ USTATE=$(bg_printenv --current --output ustate --raw)
$ echo "$USTATE"
USTATE=0

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tests/bg_setenv.bats | 20 ++++++
tools/bg_setenv.c | 151 +++++++++++++++++++++++++++++--------------
2 files changed, 123 insertions(+), 48 deletions(-)

diff --git a/tests/bg_setenv.bats b/tests/bg_setenv.bats
index f4739fd..67cee8f 100755
--- a/tests/bg_setenv.bats
+++ b/tests/bg_setenv.bats
@@ -134,3 +134,23 @@ ustate: 0 (OK)" ]]
run bg_printenv "--filepath=$envfile" --output in_progress,revision,kernel,kernelargs,watchdog_timeout,ustate,user
[[ "$output" = "$expected_output" ]]
}
+
+@test "bg_printenv ustate raw" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate --raw
+ [[ "$output" = "USTATE=0" ]]
+}
+
+@test "bg_printenv multiple fields raw" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate,kernel,kernelargs --raw
+ [[ "$output" = "KERNEL=C:BOOT:kernel.efi
+KERNELARGS=root=/dev/sda
+USTATE=0" ]]
+}
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 2f6105f..4b9884f 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -64,6 +64,7 @@ static struct argp_option options_printenv[] = {
"Available fields: in_progress, revision, kernel, kernelargs, "
"watchdog_timeout, ustate, user. "
"If omitted, all available fields are printed."},
+ {"raw", 'r', 0, 0, "Raw output mode, e.g. for shell scripting"},
{},
};

@@ -95,6 +96,7 @@ struct arguments_printenv {
bool current;
/* a bitset to decide which fields are printed */
int output_fields;
+ bool raw;
};

#define MASK_IN_PROGRESS (1 << 0)
@@ -517,6 +519,9 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
case 'o':
e = parse_output_fields(arg, &arguments->output_fields);
break;
+ case 'r':
+ arguments->raw = true;
+ break;
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
@@ -529,7 +534,7 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
return e;
}

-static void dump_uservars(uint8_t *udata)
+static void dump_uservars(uint8_t *udata, bool raw)
{
char *key, *value;
uint64_t type;
@@ -540,10 +545,10 @@ static void dump_uservars(uint8_t *udata)
while (*udata) {
bgenv_map_uservar(udata, &key, &type, (uint8_t **)&value,
&rsize, &dsize);
- fprintf(stdout, "%s ", key);
+ fprintf(stdout, "%s", key);
type &= USERVAR_STANDARD_TYPE_MASK;
if (type == USERVAR_TYPE_STRING_ASCII) {
- fprintf(stdout, "= %s\n", value);
+ fprintf(stdout, raw ? "=%s\n" : " = %s\n", value);
} else if (type >= USERVAR_TYPE_UINT8 &&
type <= USERVAR_TYPE_UINT64) {
switch(type) {
@@ -560,8 +565,8 @@ static void dump_uservars(uint8_t *udata)
val_unum = *((uint64_t *) value);
break;
}
- fprintf(stdout, "= %llu\n",
- (long long unsigned int) val_unum);
+ fprintf(stdout, raw ? "=%llu\n" : " = %llu\n",
+ (long long unsigned int)val_unum);
} else if (type >= USERVAR_TYPE_SINT8 &&
type <= USERVAR_TYPE_SINT64) {
switch(type) {
@@ -578,19 +583,20 @@ static void dump_uservars(uint8_t *udata)
val_snum = *((int64_t *) value);
break;
}
- fprintf(stdout, "= %lld\n",
- (long long signed int) val_snum);
+ fprintf(stdout, raw ? "=%lld\n" : " = %lld\n",
+ (long long signed int)val_snum);
} else {
switch(type) {
case USERVAR_TYPE_CHAR:
- fprintf(stdout, "= %c\n", (char) *value);
+ fprintf(stdout, raw ? "=%c\n" : " = %c\n",
+ (char)*value);
break;
case USERVAR_TYPE_BOOL:
- fprintf(stdout, "= %s\n",
- (bool) *value ? "true" : "false");
+ fprintf(stdout, raw ? "=%s\n" : " = %s\n",
+ (bool)*value ? "true" : "false");
break;
default:
- fprintf(stdout, "( Type is not printable )\n");
+ fprintf(stdout, " ( Type is not printable )\n");
}
}

@@ -598,39 +604,71 @@ static void dump_uservars(uint8_t *udata)
}
}

-static void dump_env(BG_ENVDATA *env, int output_fields)
+static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
{
char buffer[ENV_STRING_LENGTH];
- fprintf(stdout, "Values:\n");
+ if (!raw) {
+ fprintf(stdout, "Values:\n");
+ }
if (output_fields & MASK_IN_PROGRESS) {
- fprintf(stdout, "in_progress: %s\n",
- env->in_progress ? "yes" : "no");
+ if (raw) {
+ fprintf(stdout, "IN_PROGRESS=%d\n", env->in_progress);
+ } else {
+ fprintf(stdout, "in_progress: %s\n",
+ env->in_progress ? "yes" : "no");
+ }
}
if (output_fields & MASK_REVISION) {
- fprintf(stdout, "revision: %u\n", env->revision);
+ if (raw) {
+ fprintf(stdout, "REVISION=%d\n", env->revision);
+ } else {
+ fprintf(stdout, "revision: %u\n",
+ env->revision);
+ }
}
if (output_fields & MASK_KERNEL) {
- fprintf(stdout, "kernel: %s\n",
- str16to8(buffer, env->kernelfile));
+ char *kernelfile = str16to8(buffer, env->kernelfile);
+ if (raw) {
+ fprintf(stdout, "KERNEL=%s\n", kernelfile);
+ } else {
+ fprintf(stdout, "kernel: %s\n", kernelfile);
+ }
}
if (output_fields & MASK_KERNELARGS) {
- fprintf(stdout, "kernelargs: %s\n",
- str16to8(buffer, env->kernelparams));
+ char *kernelargs = str16to8(buffer, env->kernelparams);
+ if (raw) {
+ fprintf(stdout, "KERNELARGS=%s\n", kernelargs);
+ } else {
+ fprintf(stdout, "kernelargs: %s\n", kernelargs);
+ }
}
if (output_fields & MASK_WDOG_TIMEOUT) {
- fprintf(stdout, "watchdog timeout: %u seconds\n",
- env->watchdog_timeout_sec);
+ if (raw) {
+ fprintf(stdout, "WATCHDOG_TIMEOUT=%u\n",
+ env->watchdog_timeout_sec);
+ } else {
+ fprintf(stdout, "watchdog timeout: %u seconds\n",
+ env->watchdog_timeout_sec);
+ }
}
if (output_fields & MASK_USTATE) {
- fprintf(stdout, "ustate: %u (%s)\n",
- (uint8_t)env->ustate, ustate2str(env->ustate));
+ if (raw) {
+ fprintf(stdout, "USTATE=%u\n", env->ustate);
+ } else {
+ fprintf(stdout, "ustate: %u (%s)\n",
+ (uint8_t)env->ustate, ustate2str(env->ustate));
+ }
}
if (output_fields & MASK_USER) {
- fprintf(stdout, "\n");
- fprintf(stdout, "user variables:\n");
- dump_uservars(env->userdata);
+ if (!raw) {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "user variables:\n");
+ }
+ dump_uservars(env->userdata, raw);
+ }
+ if (!raw) {
+ fprintf(stdout, "\n\n");
}
- fprintf(stdout, "\n\n");
}

static void update_environment(BGENV *env, bool verbosity)
@@ -652,11 +690,13 @@ static void update_environment(BGENV *env, bool verbosity)

}

-static void dump_envs(int output_fields)
+static void dump_envs(int output_fields, bool raw)
{
for (int i = 0; i < ENV_NUM_CONFIG_PARTS; i++) {
- fprintf(stdout, "\n----------------------------\n");
- fprintf(stdout, " Config Partition #%d ", i);
+ if (!raw) {
+ fprintf(stdout, "\n----------------------------\n");
+ fprintf(stdout, " Config Partition #%d ", i);
+ }
BGENV *env = bgenv_open_by_index(i);
if (!env) {
fprintf(stderr, "Error, could not read environment "
@@ -664,30 +704,30 @@ static void dump_envs(int output_fields)
i);
return;
}
- dump_env(env->data, output_fields);
+ dump_env(env->data, output_fields, raw);
bgenv_close(env);
}
}

-static void dump_latest_env(int output_fields)
+static void dump_latest_env(int output_fields, bool raw)
{
BGENV *env = bgenv_open_latest();
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data, output_fields);
+ dump_env(env->data, output_fields, raw);
bgenv_close(env);
}

-static void dump_env_by_index(uint32_t index, int output_fields)
+static void dump_env_by_index(uint32_t index, int output_fields, bool raw)
{
BGENV *env = bgenv_open_by_index(index);
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data, output_fields);
+ dump_env(env->data, output_fields, raw);
bgenv_close(env);
}

@@ -716,14 +756,14 @@ static bool get_env(char *configfilepath, BG_ENVDATA *data)
return result;
}

-static int printenv_from_file(char *envfilepath, int output_fields)
+static int printenv_from_file(char *envfilepath, int output_fields, bool raw)
{
int success = 0;
BG_ENVDATA data;

success = get_env(envfilepath, &data);
if (success) {
- dump_env(&data, output_fields);
+ dump_env(&data, output_fields, raw);
return 0;
} else {
fprintf(stderr, "Error reading environment file.\n");
@@ -748,7 +788,7 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)

update_environment(&env, verbosity);
if (verbosity) {
- dump_env(env.data, MASK_ALL);
+ dump_env(env.data, MASK_ALL, false);
}
FILE *of = fopen(envfilepath, "wb");
if (of) {
@@ -804,10 +844,18 @@ static error_t bg_printenv(int argc, char **argv)
fprintf(stderr, "Error, only one of -c/-f/-p can be set.\n");
return 1;
}
+ if (arguments.raw && counter != 1) {
+ /* raw mode makes only sense if applied to a single
+ * partition */
+ fprintf(stderr, "Error, raw is set but "
+ "current/filepath/which_part is not set. "
+ "Must use -r and -c/-f/-p simultaneously.\n");
+ return 1;
+ }

if (common->envfilepath) {
e = printenv_from_file(common->envfilepath,
- arguments.output_fields);
+ arguments.output_fields, arguments.raw);
free(common->envfilepath);
return e;
}
@@ -819,14 +867,19 @@ static error_t bg_printenv(int argc, char **argv)
}

if (arguments.current) {
- fprintf(stdout, "Using latest config partition\n");
- dump_latest_env(arguments.output_fields);
+ if (!arguments.raw) {
+ fprintf(stdout, "Using latest config partition\n");
+ }
+ dump_latest_env(arguments.output_fields, arguments.raw);
} else if (common->part_specified) {
- fprintf(stdout, "Using config partition #%d\n",
- arguments.common.which_part);
- dump_env_by_index(common->which_part, arguments.output_fields);
+ if (!arguments.raw) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
+ }
+ dump_env_by_index(common->which_part, arguments.output_fields,
+ arguments.raw);
} else {
- dump_envs(arguments.output_fields);
+ dump_envs(arguments.output_fields, arguments.raw);
}

bgenv_finalize();
@@ -887,7 +940,7 @@ static error_t bg_setenv(int argc, char **argv)
}

if (arguments.common.verbosity) {
- dump_envs(MASK_ALL);
+ dump_envs(MASK_ALL, false);
}

BGENV *env_new = NULL;
@@ -931,6 +984,8 @@ static error_t bg_setenv(int argc, char **argv)
bgenv_close(env_current);
} else {
if (arguments.common.part_specified) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
env_new = bgenv_open_by_index(
arguments.common.which_part);
} else {
@@ -949,7 +1004,7 @@ static error_t bg_setenv(int argc, char **argv)
if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
- dump_env(env_new->data, MASK_ALL);
+ dump_env(env_new->data, MASK_ALL, false);

Jan Kiszka

unread,
Nov 3, 2021, 11:13:26 AM11/3/21
to Michael Adler, efibootg...@googlegroups.com
Can't that be solved differently, e.g. by excluding macros from clang
checks upfront? Automatic format checking is nice, but if it starts to
require cluttering the code with exception tags...

Jan Kiszka

unread,
Nov 3, 2021, 11:13:36 AM11/3/21
to Michael Adler, efibootg...@googlegroups.com
On 03.11.21 15:57, Michael Adler wrote:
> This introduces the following new command-line arguments:
>
> --current: print values from the current env
> --part: read from a specific environment partition
>

Why this is better than what we have, that is missing here.

Jan Kiszka

unread,
Nov 3, 2021, 11:14:11 AM11/3/21
to Michael Adler, efibootg...@googlegroups.com
On 03.11.21 15:57, Michael Adler wrote:
> This allows to specify a list of fields which are printed.
>
> Example:
>
> $ bg_printenv --current --output ustate
> Using latest config partition
> Values:
> ustate: 0 (OK)
>

Also here: Use case description missing. I hope it's not "simpler
machine parsing".

Jan Kiszka

unread,
Nov 3, 2021, 11:27:36 AM11/3/21
to Michael Adler, efibootg...@googlegroups.com
On 03.11.21 15:57, Michael Adler wrote:
> Adding --raw to the bg_printenv command-line enables raw output mode
> which can be used for shell scripting purposes.
>
> Examples:
>
> * Bring the fields as variables into your shell:
>
> $ source <(bg_printenv --current --output kernel,ustate --raw)
> $ echo "kernel: $KERNEL, ustate: $USTATE"
> kernel: C:BOOTA:linux.efi, ustate: 0
>
> * Assing the current ustate to a variable:
>
> $ USTATE=$(bg_printenv --current --output ustate --raw)
> $ echo "$USTATE"
> USTATE=0
>

Ok, then the reason for --output is to enable --raw, machine parsing.
Then it's enough to reference that in the previous patch.

Jan Kiszka

unread,
Nov 3, 2021, 11:29:25 AM11/3/21
to Michael Adler, efibootg...@googlegroups.com
On 03.11.21 15:57, Michael Adler wrote:
> This reduces global state and keeps the global namespace clean.
>
> Signed-off-by: Michael Adler <michae...@siemens.com>
> ---
> tools/bg_setenv.c | 78 ++++++++++++++++++++++-------------------------
> 1 file changed, 37 insertions(+), 41 deletions(-)
>
> diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
> index e564219..3e5a5c3 100644
> --- a/tools/bg_setenv.c
> +++ b/tools/bg_setenv.c
> @@ -61,6 +61,16 @@ static struct argp_option options_printenv[] = {
> struct arguments {
> bool printenv;
> int which_part;
> + /* auto update feature automatically updates partition with
> + * oldest environment revision (lowest value)
> + */
> + bool auto_update;
> + bool part_specified;
> + bool verbosity;
> + char *envfilepath;
> + /* whether to keep existing entries in BGENV before applying the new
> + * settings */

Nit: different comment style, compared to above. Maybe rather use this
one consistently.

Jan

> + bool preserve_env;
> };
>
> typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;

Michael Adler

unread,
Nov 4, 2021, 5:24:50 AM11/4/21
to Jan Kiszka, efibootg...@googlegroups.com
> Nit: different comment style, compared to above. Maybe rather use this
> one consistently.

[x] Fixed

(I had just moved this code but you are right, of course. Sadly, I couldn't find an option to *enforce* this style via clang-format.)

Michael Adler

unread,
Nov 4, 2021, 5:24:51 AM11/4/21
to Jan Kiszka, efibootg...@googlegroups.com
> Can't that be solved differently, e.g. by excluding macros from clang

I'm not aware of such an option. However, I found a different solution, see patch v3.

Michael Adler

unread,
Nov 4, 2021, 5:24:52 AM11/4/21
to Jan Kiszka, efibootg...@googlegroups.com
> Ok, then the reason for --output is to enable --raw, machine parsing.
> Then it's enough to reference that in the previous patch.

[x] Done

Michael Adler

unread,
Nov 4, 2021, 5:24:53 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
Below is the delta to the v2 patch series.
Apart from that, a few commit messages have been improved, as suggested in the v2 review.

diff --git a/.clang-format b/.clang-format
index 49dee99..420c4ff 100644
--- a/.clang-format
+++ b/.clang-format
@@ -16,3 +16,7 @@ SortIncludes: false
UseTab: Always
ContinuationIndentWidth: 8
AlignConsecutiveMacros: true
+# Typical macros are expressions, and require a semi-colon to be added; sometimes this is not the case, and this allows
+# to make clang-format aware of such cases.
+StatementMacros:
+ - OPT
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 4b9884f..7a234cf 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -25,46 +25,52 @@
static char doc[] =
"bg_setenv/bg_printenv - Environment tool for the EFI Boot Guard";

-// clang-format off
+#define OPT(name, key, arg, flags, doc) \
+ { \
+ name, key, arg, flags, doc \
+ }
+
#define BG_CLI_OPTIONS_COMMON \
- {"filepath", 'f', "ENVFILE", 0, \
- "Environment to use. Expects a file name, " \
- "usually called BGENV.DAT."}, \
- {"part", 'p', "ENV_PART", 0, \
- "Set environment partition to use. If no partition is specified, " \
- "the one with the smallest revision value above zero is used."}, \
- {"verbose", 'v', 0, 0, "Be verbose"}, \
- {"version", 'V', 0, 0, "Print version"}
-// clang-format on
+ OPT("filepath", 'f', "ENVFILE", 0, \
+ "Environment to use. Expects a file name, " \
+ "usually called BGENV.DAT.") \
+ , OPT("part", 'p', "ENV_PART", 0, \
+ "Set environment partition to update. If no partition is " \
+ "specified, " \
+ "the one with the smallest revision value above zero is " \
+ "updated.") \
+ , OPT("verbose", 'v', 0, 0, "Be verbose") \
+ , OPT("version", 'V', 0, 0, "Print version")

static struct argp_option options_setenv[] = {
BG_CLI_OPTIONS_COMMON,
- {"preserve", 'P', 0, 0, "Preserve existing entries"},
- {"kernel", 'k', "KERNEL", 0, "Set kernel to load"},
- {"args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"},
- {"revision", 'r', "REVISION", 0, "Set revision value"},
- {"ustate", 's', "USTATE", 0, "Set update status for environment"},
- {"watchdog", 'w', "WATCHDOG_TIMEOUT", 0, "Watchdog timeout in seconds"},
- {"confirm", 'c', 0, 0, "Confirm working environment"},
- {"update", 'u', 0, 0, "Automatically update oldest revision"},
- {"uservar", 'x', "KEY=VAL", 0,
- "Set user-defined string variable. For setting multiple variables, "
- "use this option multiple times."},
- {"in_progress", 'i', "IN_PROGRESS", 0,
- "Set in_progress variable to simulate a running update process."},
+ OPT("preserve", 'P', 0, 0, "Preserve existing entries"),
+ OPT("kernel", 'k', "KERNEL", 0, "Set kernel to load"),
+ OPT("args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"),
+ OPT("revision", 'r', "REVISION", 0, "Set revision value"),
+ OPT("ustate", 's', "USTATE", 0, "Set update status for environment"),
+ OPT("watchdog", 'w', "WATCHDOG_TIMEOUT", 0,
+ "Watchdog timeout in seconds"),
+ OPT("confirm", 'c', 0, 0, "Confirm working environment"),
+ OPT("update", 'u', 0, 0, "Automatically update oldest revision"),
+ OPT("uservar", 'x', "KEY=VAL", 0,
+ "Set user-defined string variable. For setting multiple variables, "
+ "use this option multiple times."),
+ OPT("in_progress", 'i', "IN_PROGRESS", 0,
+ "Set in_progress variable to simulate a running update process."),
{},
};

static struct argp_option options_printenv[] = {
BG_CLI_OPTIONS_COMMON,
- {"current", 'c', 0, 0,
- "Only print values from the current environment"},
- {"output", 'o', "LIST", 0,
- "Comma-separated list of fields which are printed. "
- "Available fields: in_progress, revision, kernel, kernelargs, "
- "watchdog_timeout, ustate, user. "
- "If omitted, all available fields are printed."},
- {"raw", 'r', 0, 0, "Raw output mode, e.g. for shell scripting"},
+ OPT("current", 'c', 0, 0,
+ "Only print values from the current environment"),
+ OPT("output", 'o', "LIST", 0,
+ "Comma-separated list of fields which are printed. "
+ "Available fields: in_progress, revision, kernel, kernelargs, "
+ "watchdog_timeout, ustate, user. "
+ "If omitted, all available fields are printed."),
+ OPT("raw", 'r', 0, 0, "Raw output mode, e.g. for shell scripting"),
{},
};

@@ -82,32 +88,34 @@ struct arguments_common {
struct arguments_setenv {
struct arguments_common common;
/* auto update feature automatically updates partition with
- * oldest environment revision (lowest value)
- */
+ * oldest environment revision (lowest value) */
bool auto_update;
/* whether to keep existing entries in BGENV before applying new
* settings */
bool preserve_env;
};

+struct fields {
+ unsigned int in_progress : 1;
+ unsigned int revision : 1;
+ unsigned int kernel : 1;
+ unsigned int kernelargs : 1;
+ unsigned int wdog_timeout : 1;
+ unsigned int ustate : 1;
+ unsigned int user : 1;
+};
+
+static const struct fields ALL_FIELDS = {1, 1, 1, 1, 1, 1, 1};
+
/* Arguments used by bg_printenv. */
struct arguments_printenv {
struct arguments_common common;
bool current;
/* a bitset to decide which fields are printed */
- int output_fields;
+ struct fields output_fields;
bool raw;
};

-#define MASK_IN_PROGRESS (1 << 0)
-#define MASK_REVISION (1 << 1)
-#define MASK_KERNEL (1 << 2)
-#define MASK_KERNELARGS (1 << 3)
-#define MASK_WDOG_TIMEOUT (1 << 4)
-#define MASK_USTATE (1 << 5)
-#define MASK_USER (1 << 6)
-#define MASK_ALL 0x7F /* binary: 1111111 */
-
typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;

struct stailhead *headp;
@@ -478,27 +486,26 @@ static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
return e;
}

-static error_t parse_output_fields(char *fields, int *output_fields)
+static error_t parse_output_fields(char *fields, struct fields *output_fields)
{
char *token;
- /* reset */
- *output_fields = 0;
+ memset(output_fields, 0, sizeof(struct fields));
while ((token = strsep(&fields, ","))) {
if (*token == '\0') continue;
if (strcmp(token, "in_progress") == 0) {
- *output_fields |= MASK_IN_PROGRESS;
+ output_fields->in_progress = true;
} else if (strcmp(token, "revision") == 0) {
- *output_fields |= MASK_REVISION;
+ output_fields->revision = true;
} else if (strcmp(token, "kernel") == 0) {
- *output_fields |= MASK_KERNEL;
+ output_fields->kernel = true;
} else if (strcmp(token, "kernelargs") == 0) {
- *output_fields |= MASK_KERNELARGS;
+ output_fields->kernelargs = true;
} else if (strcmp(token, "watchdog_timeout") == 0) {
- *output_fields |= MASK_WDOG_TIMEOUT;
+ output_fields->wdog_timeout = true;
} else if (strcmp(token, "ustate") == 0) {
- *output_fields |= MASK_USTATE;
+ output_fields->ustate = true;
} else if (strcmp(token, "user") == 0) {
- *output_fields |= MASK_USER;
+ output_fields->user = true;
} else {
fprintf(stderr, "Unknown output field: %s\n", token);
return 1;
@@ -604,13 +611,13 @@ static void dump_uservars(uint8_t *udata, bool raw)
}
}

-static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
+static void dump_env(BG_ENVDATA *env, struct fields output_fields, bool raw)
{
char buffer[ENV_STRING_LENGTH];
if (!raw) {
fprintf(stdout, "Values:\n");
}
- if (output_fields & MASK_IN_PROGRESS) {
+ if (output_fields.in_progress) {
if (raw) {
fprintf(stdout, "IN_PROGRESS=%d\n", env->in_progress);
} else {
@@ -618,7 +625,7 @@ static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
env->in_progress ? "yes" : "no");
}
}
- if (output_fields & MASK_REVISION) {
+ if (output_fields.revision) {
if (raw) {
fprintf(stdout, "REVISION=%d\n", env->revision);
} else {
@@ -626,7 +633,7 @@ static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
env->revision);
}
}
- if (output_fields & MASK_KERNEL) {
+ if (output_fields.kernel) {
char *kernelfile = str16to8(buffer, env->kernelfile);
if (raw) {
fprintf(stdout, "KERNEL=%s\n", kernelfile);
@@ -634,7 +641,7 @@ static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
fprintf(stdout, "kernel: %s\n", kernelfile);
}
}
- if (output_fields & MASK_KERNELARGS) {
+ if (output_fields.kernelargs) {
char *kernelargs = str16to8(buffer, env->kernelparams);
if (raw) {
fprintf(stdout, "KERNELARGS=%s\n", kernelargs);
@@ -642,7 +649,7 @@ static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
fprintf(stdout, "kernelargs: %s\n", kernelargs);
}
}
- if (output_fields & MASK_WDOG_TIMEOUT) {
+ if (output_fields.wdog_timeout) {
if (raw) {
fprintf(stdout, "WATCHDOG_TIMEOUT=%u\n",
env->watchdog_timeout_sec);
@@ -651,7 +658,7 @@ static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
env->watchdog_timeout_sec);
}
}
- if (output_fields & MASK_USTATE) {
+ if (output_fields.ustate) {
if (raw) {
fprintf(stdout, "USTATE=%u\n", env->ustate);
} else {
@@ -659,7 +666,7 @@ static void dump_env(BG_ENVDATA *env, int output_fields, bool raw)
(uint8_t)env->ustate, ustate2str(env->ustate));
}
}
- if (output_fields & MASK_USER) {
+ if (output_fields.user) {
if (!raw) {
fprintf(stdout, "\n");
fprintf(stdout, "user variables:\n");
@@ -690,7 +697,7 @@ static void update_environment(BGENV *env, bool verbosity)

}

-static void dump_envs(int output_fields, bool raw)
+static void dump_envs(struct fields output_fields, bool raw)
{
for (int i = 0; i < ENV_NUM_CONFIG_PARTS; i++) {
if (!raw) {
@@ -709,7 +716,7 @@ static void dump_envs(int output_fields, bool raw)
}
}

-static void dump_latest_env(int output_fields, bool raw)
+static void dump_latest_env(struct fields output_fields, bool raw)
{
BGENV *env = bgenv_open_latest();
if (!env) {
@@ -720,7 +727,8 @@ static void dump_latest_env(int output_fields, bool raw)
bgenv_close(env);
}

-static void dump_env_by_index(uint32_t index, int output_fields, bool raw)
+static void dump_env_by_index(uint32_t index, struct fields output_fields,
+ bool raw)
{
BGENV *env = bgenv_open_by_index(index);
if (!env) {
@@ -756,7 +764,8 @@ static bool get_env(char *configfilepath, BG_ENVDATA *data)
return result;
}

-static int printenv_from_file(char *envfilepath, int output_fields, bool raw)
+static int printenv_from_file(char *envfilepath, struct fields output_fields,
+ bool raw)
{
int success = 0;
BG_ENVDATA data;
@@ -788,7 +797,7 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)

update_environment(&env, verbosity);
if (verbosity) {
- dump_env(env.data, MASK_ALL, false);
+ dump_env(env.data, ALL_FIELDS, false);
}
FILE *of = fopen(envfilepath, "wb");
if (of) {
@@ -823,7 +832,7 @@ static error_t bg_printenv(int argc, char **argv)
};

struct arguments_printenv arguments = {
- .output_fields = MASK_ALL,
+ .output_fields = ALL_FIELDS,
};

error_t e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
@@ -940,7 +949,7 @@ static error_t bg_setenv(int argc, char **argv)
}

if (arguments.common.verbosity) {
- dump_envs(MASK_ALL, false);
+ dump_envs(ALL_FIELDS, false);
}

BGENV *env_new = NULL;
@@ -1004,7 +1013,7 @@ static error_t bg_setenv(int argc, char **argv)
if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
- dump_env(env_new->data, MASK_ALL, false);
+ dump_env(env_new->data, ALL_FIELDS, false);

Michael Adler

unread,
Nov 4, 2021, 5:24:54 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
Signed-off-by: Michael Adler <michae...@siemens.com>
---
.gitignore | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/.gitignore b/.gitignore
index 45f7cc2..1943fe0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -85,3 +85,10 @@ m4/lt*
version.h
libtool
.libs
+
+# Test binaries
+test_bgenv_init_retval
+test_ebgenv_api
+test_ebgenv_api_internal
+test_probe_config_file
+test_probe_config_partitions
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:24:55 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
* `ContinuationIndentWidth: 8` ensures that in certain scenarios (e.g.
array of structs, defined over multiple lines) consistently uses tabs
instead of spaces for alignment.
* `AlignConsecutiveMacros` improves the formatting of preprocessor
instructions, see [1].

[1] https://bugs.llvm.org/show_bug.cgi?id=20637

Signed-off-by: Michael Adler <michae...@siemens.com>
---
.clang-format | 2 ++
1 file changed, 2 insertions(+)

diff --git a/.clang-format b/.clang-format
index e433bf7..49dee99 100644
--- a/.clang-format
+++ b/.clang-format
@@ -14,3 +14,5 @@ PointerAlignment: Right
ReflowComments: true
SortIncludes: false
UseTab: Always
+ContinuationIndentWidth: 8
+AlignConsecutiveMacros: true
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:24:56 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
This reduces global state and keeps the global namespace clean.

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 80 ++++++++++++++++++++++-------------------------
1 file changed, 38 insertions(+), 42 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index e564219..99da52b 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -61,6 +61,15 @@ static struct argp_option options_printenv[] = {
struct arguments {
bool printenv;
int which_part;
+ /* auto update feature automatically updates partition with
+ * oldest environment revision (lowest value) */
+ bool auto_update;
+ bool part_specified;
+ bool verbosity;
+ char *envfilepath;
+ /* whether to keep existing entries in BGENV before applying the new
+ * settings */
+ bool preserve_env;
};

typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;
@@ -163,20 +172,6 @@ static void journal_process_action(BGENV *env, struct env_action *action)
}
}

-/* auto update feature automatically updates partition with
- * oldest environment revision (lowest value)
- */
-static bool auto_update = false;
-
-static bool part_specified = false;
-
-static bool verbosity = false;
-
-static char *envfilepath = NULL;
-
-/* whether to keep existing entries in BGENV before applying the new settings */
-static bool preserve_env = false;
-
static char *ustatemap[] = {"OK", "INSTALLED", "TESTING", "FAILED", "UNKNOWN"};

static uint8_t str2ustate(char *str)
@@ -278,7 +273,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
fprintf(stdout, "Updating config partition #%d\n", i);
arguments->which_part = i;
- part_specified = true;
+ arguments->part_specified = true;
} else {
fprintf(stderr,
"Selected partition out of range. Valid range: "
@@ -362,8 +357,8 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
(uint8_t *)arg, strlen(arg) + 1);
break;
case 'f':
- free(envfilepath);
- envfilepath = NULL;
+ free(arguments->envfilepath);
+ arguments->envfilepath = NULL;

/* compat mode, permitting "bg_setenv -f <dir>" */
if (!arguments->printenv) {
@@ -374,17 +369,17 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
fprintf(stderr,
"WARNING: Using -f to specify only the "
"ouptut directory is deprecated.\n");
- res = asprintf(&envfilepath, "%s/%s", arg,
- FAT_ENV_FILENAME);
+ res = asprintf(&arguments->envfilepath, "%s/%s",
+ arg, FAT_ENV_FILENAME);
if (res == -1) {
return ENOMEM;
}
}
}

- if (!envfilepath) {
- envfilepath = strdup(arg);
- if (!envfilepath) {
+ if (!arguments->envfilepath) {
+ arguments->envfilepath = strdup(arg);
+ if (!arguments->envfilepath) {
return ENOMEM;
}
}
@@ -397,18 +392,18 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
(uint8_t *)"0", 2);
break;
case 'u':
- if (part_specified) {
+ if (arguments->part_specified) {
fprintf(stderr,
"Error, both automatic and manual partition "
"selection. Cannot use -p and -u "
"simultaneously.\n");
return 1;
}
- auto_update = true;
+ arguments->auto_update = true;
break;
case 'v':
/* Set verbosity in this program */
- verbosity = true;
+ arguments->verbosity = true;
/* Set verbosity in the library */
bgenv_be_verbose(true);
break;
@@ -417,7 +412,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
e = set_uservars(arg);
break;
case 'P':
- preserve_env = true;
+ arguments->preserve_env = true;
break;
case 'V':
fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
@@ -527,7 +522,7 @@ static void dump_env(BG_ENVDATA *env)
fprintf(stdout, "\n\n");
}

-static void update_environment(BGENV *env)
+static void update_environment(BGENV *env, bool verbosity)
{
if (verbosity) {
fprintf(stdout, "Processing journal...\n");
@@ -602,7 +597,8 @@ static int printenv_from_file(char *envfilepath) {
}
}

-static int dumpenv_to_file(char *envfilepath) {
+static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
+{
/* execute journal and write to file */
int result = 0;
BGENV env;
@@ -616,7 +612,7 @@ static int dumpenv_to_file(char *envfilepath) {
return 1;
}

- update_environment(&env);
+ update_environment(&env, verbosity);
if (verbosity) {
dump_env(env.data);
}
@@ -665,9 +661,7 @@ int main(int argc, char **argv)
argp = &argp_printenv;
}

- struct arguments arguments;
- arguments.printenv = !write_mode;
- arguments.which_part = 0;
+ struct arguments arguments = {.printenv = !write_mode, .which_part = 0};

STAILQ_INIT(&head);

@@ -682,13 +676,15 @@ int main(int argc, char **argv)
/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
- if (envfilepath) {
+ if (arguments.envfilepath) {
if (write_mode) {
- result = dumpenv_to_file(envfilepath);
+ result = dumpenv_to_file(arguments.envfilepath,
+ arguments.verbosity,
+ arguments.preserve_env);
} else {
- result = printenv_from_file(envfilepath);
+ result = printenv_from_file(arguments.envfilepath);
}
- free(envfilepath);
+ free(arguments.envfilepath);
return result;
}

@@ -704,14 +700,14 @@ int main(int argc, char **argv)
return 0;
}

- if (verbosity) {
+ if (arguments.verbosity) {
dump_envs();
}

BGENV *env_new = NULL;
BGENV *env_current;

- if (auto_update) {
+ if (arguments.auto_update) {
/* clone latest environment */

env_current = bgenv_open_latest();
@@ -729,7 +725,7 @@ int main(int argc, char **argv)
result = 1;
goto cleanup;
}
- if (verbosity) {
+ if (arguments.verbosity) {
fprintf(stdout,
"Updating environment with revision %u\n",
env_new->data->revision);
@@ -748,7 +744,7 @@ int main(int argc, char **argv)

bgenv_close(env_current);
} else {
- if (part_specified) {
+ if (arguments.part_specified) {
env_new = bgenv_open_by_index(arguments.which_part);
} else {
env_new = bgenv_open_latest();
@@ -761,9 +757,9 @@ int main(int argc, char **argv)
}
}

- update_environment(env_new);
+ update_environment(env_new, arguments.verbosity);

- if (verbosity) {
+ if (arguments.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
dump_env(env_new->data);
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:24:57 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
Currently, the implementations of bg_setenv and bg_printenv are tightly
coupled with each other, making it unnecessarily difficult to grasp,
modify and extend the existing control flow.

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 33 ++++++++++++++++++++-------------
1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 99da52b..c0144c1 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -639,6 +639,19 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
return result;
}

+/* This is the entrypoint for the command bg_printenv. */
+static int bg_printenv(const struct arguments *arguments)
+{
+ if (arguments->envfilepath) {
+ int result = printenv_from_file(arguments->envfilepath);
+ free(arguments->envfilepath);
+ return result;
+ }
+ dump_envs();
+ bgenv_finalize();
+ return 0;
+}
+
int main(int argc, char **argv)
{
static struct argp argp_setenv = {options_setenv, parse_opt, NULL, doc};
@@ -671,19 +684,19 @@ int main(int argc, char **argv)
return e;
}

+ if (!write_mode) {
+ return bg_printenv(&arguments);
+ }
+
int result = 0;

/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
if (arguments.envfilepath) {
- if (write_mode) {
- result = dumpenv_to_file(arguments.envfilepath,
- arguments.verbosity,
- arguments.preserve_env);
- } else {
- result = printenv_from_file(arguments.envfilepath);
- }
+ result = dumpenv_to_file(arguments.envfilepath,
+ arguments.verbosity,
+ arguments.preserve_env);
free(arguments.envfilepath);
return result;
}
@@ -694,12 +707,6 @@ int main(int argc, char **argv)
return 1;
}

- if (!write_mode) {
- dump_envs();
- bgenv_finalize();
- return 0;
- }
-
if (arguments.verbosity) {
dump_envs();
}
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:24:59 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
Do not rely on parsing order for arg validation.

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index c0144c1..9607926 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -392,13 +392,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
(uint8_t *)"0", 2);
break;
case 'u':
- if (arguments->part_specified) {
- fprintf(stderr,
- "Error, both automatic and manual partition "
- "selection. Cannot use -p and -u "
- "simultaneously.\n");
- return 1;
- }
arguments->auto_update = true;
break;
case 'v':
@@ -690,6 +683,13 @@ int main(int argc, char **argv)

int result = 0;

+ if (arguments.auto_update && arguments.part_specified) {
+ fprintf(stderr, "Error, both automatic and manual partition "
+ "selection. Cannot use -p and -u "
+ "simultaneously.\n");
+ return 1;
+ }
+
/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:25:01 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
bg_setenv and bg_printenv have different command-line interfaces. Thus
it is natural to have separate parsers and more importantly, store the
arguments of each cli into its own struct, thereby avoiding potential
confusion when processing the arguments later on.

* `struct arguments` is split into `arguments_setenv` and
`arguments_printenv`
* `parse_opt` is split into `parse_common_opt`, `parse_setenv_opt` and
`parse_printenv_opt`

Signed-off-by: Michael Adler <michae...@siemens.com>
---
.clang-format | 4 +
tools/bg_setenv.c | 292 ++++++++++++++++++++++++++++------------------
2 files changed, 184 insertions(+), 112 deletions(-)

diff --git a/.clang-format b/.clang-format
index 49dee99..420c4ff 100644
--- a/.clang-format
+++ b/.clang-format
@@ -16,3 +16,7 @@ SortIncludes: false
UseTab: Always
ContinuationIndentWidth: 8
AlignConsecutiveMacros: true
+# Typical macros are expressions, and require a semi-colon to be added; sometimes this is not the case, and this allows
+# to make clang-format aware of such cases.
+StatementMacros:
+ - OPT
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 9607926..370eeeb 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -24,54 +24,70 @@
static char doc[] =
"bg_setenv/bg_printenv - Environment tool for the EFI Boot Guard";

+#define OPT(name, key, arg, flags, doc) \
+ { \
+ name, key, arg, flags, doc \
+ }
+
+#define BG_CLI_OPTIONS_COMMON \
+ OPT("filepath", 'f', "ENVFILE", 0, \
+ "Environment to use. Expects a file name, " \
+ "usually called BGENV.DAT.") \
+ , OPT("verbose", 'v', 0, 0, "Be verbose") \
+ , OPT("version", 'V', 0, 0, "Print version")
+
static struct argp_option options_setenv[] = {
- {"preserve", 'P', 0, 0, "Preserve existing entries"},
- {"kernel", 'k', "KERNEL", 0, "Set kernel to load"},
- {"args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"},
- {"part", 'p', "ENV_PART", 0,
- "Set environment partition to update. If no partition is specified, "
- "the one with the smallest revision value above zero is updated."},
- {"revision", 'r', "REVISION", 0, "Set revision value"},
- {"ustate", 's', "USTATE", 0, "Set update status for environment"},
- {"filepath", 'f', "ENVFILE", 0,
- "Output environment to file. Expects an output file name, "
- "usually called BGENV.DAT."},
- {"watchdog", 'w', "WATCHDOG_TIMEOUT", 0, "Watchdog timeout in seconds"},
- {"confirm", 'c', 0, 0, "Confirm working environment"},
- {"update", 'u', 0, 0, "Automatically update oldest revision"},
- {"verbose", 'v', 0, 0, "Be verbose"},
- {"uservar", 'x', "KEY=VAL", 0,
- "Set user-defined string variable. For setting multiple variables, "
- "use this option multiple times."},
- {"in_progress", 'i', "IN_PROGRESS", 0,
- "Set in_progress variable to simulate a running update process."},
- {"version", 'V', 0, 0, "Print version"},
- {}
+ BG_CLI_OPTIONS_COMMON,
+ OPT("preserve", 'P', 0, 0, "Preserve existing entries"),
+ OPT("kernel", 'k', "KERNEL", 0, "Set kernel to load"),
+ OPT("args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"),
+ OPT("part", 'p', "ENV_PART", 0,
+ "Set environment partition to update. If no partition is "
+ "specified, "
+ "the one with the smallest revision value above zero is updated."),
+ OPT("revision", 'r', "REVISION", 0, "Set revision value"),
+ OPT("ustate", 's', "USTATE", 0, "Set update status for environment"),
+ OPT("watchdog", 'w', "WATCHDOG_TIMEOUT", 0,
+ "Watchdog timeout in seconds"),
+ OPT("confirm", 'c', 0, 0, "Confirm working environment"),
+ OPT("update", 'u', 0, 0, "Automatically update oldest revision"),
+ OPT("uservar", 'x', "KEY=VAL", 0,
+ "Set user-defined string variable. For setting multiple variables, "
+ "use this option multiple times."),
+ OPT("in_progress", 'i', "IN_PROGRESS", 0,
+ "Set in_progress variable to simulate a running update process."),
+ {},
};

static struct argp_option options_printenv[] = {
- {"filepath", 'f', "ENVFILE", 0,
- "Read environment from file. Expects a valid EFI Boot Guard "
- "environment file."},
- {"verbose", 'v', 0, 0, "Be verbose"},
- {"version", 'V', 0, 0, "Print version"},
- {}
+ BG_CLI_OPTIONS_COMMON,
+ {},
};

-struct arguments {
- bool printenv;
+/* Common arguments used by both bg_setenv and bg_printenv. */
+struct arguments_common {
+ char *envfilepath;
+ bool verbosity;
+};
+
+/* Arguments used by bg_setenv. */
+struct arguments_setenv {
+ struct arguments_common common;
int which_part;
/* auto update feature automatically updates partition with
* oldest environment revision (lowest value) */
bool auto_update;
bool part_specified;
- bool verbosity;
- char *envfilepath;
- /* whether to keep existing entries in BGENV before applying the new
+ /* whether to keep existing entries in BGENV before applying new
* settings */
bool preserve_env;
};

+/* Arguments used by bg_printenv. */
+struct arguments_printenv {
+ struct arguments_common common;
+};
+
typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;

struct stailhead *headp;
@@ -234,9 +250,61 @@ static int parse_int(char *arg)
return (int) i;
}

-static error_t parse_opt(int key, char *arg, struct argp_state *state)
+static error_t parse_common_opt(int key, char *arg, bool compat_mode,
+ struct arguments_common *arguments)
+{
+ bool found = false;
+ switch (key) {
+ case 'f':
+ found = true;
+ free(arguments->envfilepath);
+ arguments->envfilepath = NULL;
+
+ if (compat_mode) {
+ /* compat mode, permitting "bg_setenv -f <dir>" */
+ struct stat sb;
+
+ int res = stat(arg, &sb);
+ if (res == 0 && S_ISDIR(sb.st_mode)) {
+ fprintf(stderr,
+ "WARNING: Using -f to specify only the "
+ "ouptut directory is deprecated.\n");
+ res = asprintf(&arguments->envfilepath, "%s/%s",
+ arg, FAT_ENV_FILENAME);
+ if (res == -1) {
+ return ENOMEM;
+ }
+ }
+ }
+
+ if (!arguments->envfilepath) {
+ arguments->envfilepath = strdup(arg);
+ if (!arguments->envfilepath) {
+ return ENOMEM;
+ }
+ }
+ break;
+ case 'v':
+ found = true;
+ /* Set verbosity in this program */
+ arguments->verbosity = true;
+ /* Set verbosity in the library */
+ bgenv_be_verbose(true);
+ break;
+ case 'V':
+ found = true;
+ fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
+ exit(0);
+ }
+ if (!found) {
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
{
- struct arguments *arguments = state->input;
+ struct arguments_setenv *arguments = state->input;
int i, res;
char *tmp;
error_t e = 0;
@@ -356,34 +424,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
"watchdog_timeout_sec", 0,
(uint8_t *)arg, strlen(arg) + 1);
break;
- case 'f':
- free(arguments->envfilepath);
- arguments->envfilepath = NULL;
-
- /* compat mode, permitting "bg_setenv -f <dir>" */
- if (!arguments->printenv) {
- struct stat sb;
-
- res = stat(arg, &sb);
- if (res == 0 && S_ISDIR(sb.st_mode)) {
- fprintf(stderr,
- "WARNING: Using -f to specify only the "
- "ouptut directory is deprecated.\n");
- res = asprintf(&arguments->envfilepath, "%s/%s",
- arg, FAT_ENV_FILENAME);
- if (res == -1) {
- return ENOMEM;
- }
- }
- }
-
- if (!arguments->envfilepath) {
- arguments->envfilepath = strdup(arg);
- if (!arguments->envfilepath) {
- return ENOMEM;
- }
- }
- break;
case 'c':
VERBOSE(stdout,
"Confirming environment to work. Removing boot-once "
@@ -394,12 +434,6 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
case 'u':
arguments->auto_update = true;
break;
- case 'v':
- /* Set verbosity in this program */
- arguments->verbosity = true;
- /* Set verbosity in the library */
- bgenv_be_verbose(true);
- break;
case 'x':
/* Set user-defined variable(s) */
e = set_uservars(arg);
@@ -407,16 +441,13 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
case 'P':
arguments->preserve_env = true;
break;
- case 'V':
- fprintf(stdout, "EFI Boot Guard %s\n", EFIBOOTGUARD_VERSION);
- exit(0);
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
argp_usage(state);
break;
default:
- return ARGP_ERR_UNKNOWN;
+ return parse_common_opt(key, arg, true, &arguments->common);
}

if (e) {
@@ -425,6 +456,23 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
return e;
}

+static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
+{
+ struct arguments_printenv *arguments = state->input;
+
+ switch (key) {
+ case ARGP_KEY_ARG:
+ /* too many arguments - program terminates with call to
+ * argp_usage with non-zero return code */
+ argp_usage(state);
+ break;
+ default:
+ return parse_common_opt(key, arg, false, &arguments->common);
+ }
+
+ return 0;
+}
+
static void dump_uservars(uint8_t *udata)
{
char *key, *value;
@@ -633,56 +681,65 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)
}

/* This is the entrypoint for the command bg_printenv. */
-static int bg_printenv(const struct arguments *arguments)
+static error_t bg_printenv(int argc, char **argv)
{
- if (arguments->envfilepath) {
- int result = printenv_from_file(arguments->envfilepath);
- free(arguments->envfilepath);
+ struct argp argp_printenv = {
+ .options = options_printenv,
+ .parser = parse_printenv_opt,
+ .doc = doc,
+ };
+
+ struct arguments_printenv arguments;
+ memset(&arguments, 0, sizeof(struct arguments_printenv));
+
+ error_t e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
+ if (e) {
+ return e;
+ }
+ if (arguments.common.envfilepath) {
+ int result = printenv_from_file(arguments.common.envfilepath);
+ free(arguments.common.envfilepath);
return result;
}
+
+ /* not in file mode */
+ if (!bgenv_init()) {
+ fprintf(stderr, "Error initializing FAT environment.\n");
+ return 1;
+ }
+
dump_envs();
bgenv_finalize();
return 0;
}

-int main(int argc, char **argv)
+/* This is the entrypoint for the command bg_setenv. */
+static error_t bg_setenv(int argc, char **argv)
{
- static struct argp argp_setenv = {options_setenv, parse_opt, NULL, doc};
- static struct argp argp_printenv = {options_printenv, parse_opt, NULL,
- doc};
- static struct argp *argp;
-
- bool write_mode = (bool)strstr(argv[0], "bg_setenv");
- if (write_mode) {
- argp = &argp_setenv;
-
- if (argc < 2) {
- printf("No task to perform. Please specify at least one"
- " optional argument. See --help for further"
- " information.\n");
- return 1;
- }
-
- } else {
- argp = &argp_printenv;
+ if (argc < 2) {
+ printf("No task to perform. Please specify at least one"
+ " optional argument. See --help for further"
+ " information.\n");
+ return 1;
}

- struct arguments arguments = {.printenv = !write_mode, .which_part = 0};
+ struct argp argp_setenv = {
+ .options = options_setenv,
+ .parser = parse_setenv_opt,
+ .doc = doc,
+ };
+
+ struct arguments_setenv arguments;
+ memset(&arguments, 0, sizeof(struct arguments_setenv));

STAILQ_INIT(&head);

error_t e;
- e = argp_parse(argp, argc, argv, 0, 0, &arguments);
+ e = argp_parse(&argp_setenv, argc, argv, 0, 0, &arguments);
if (e) {
return e;
}

- if (!write_mode) {
- return bg_printenv(&arguments);
- }
-
- int result = 0;
-
if (arguments.auto_update && arguments.part_specified) {
fprintf(stderr, "Error, both automatic and manual partition "
"selection. Cannot use -p and -u "
@@ -690,14 +747,16 @@ int main(int argc, char **argv)
return 1;
}

+ int result = 0;
+
/* arguments are parsed, journal is filled */

/* is output to file or input from file ? */
- if (arguments.envfilepath) {
- result = dumpenv_to_file(arguments.envfilepath,
- arguments.verbosity,
+ if (arguments.common.envfilepath) {
+ result = dumpenv_to_file(arguments.common.envfilepath,
+ arguments.common.verbosity,
arguments.preserve_env);
- free(arguments.envfilepath);
+ free(arguments.common.envfilepath);
return result;
}

@@ -707,7 +766,7 @@ int main(int argc, char **argv)
return 1;
}

- if (arguments.verbosity) {
+ if (arguments.common.verbosity) {
dump_envs();
}

@@ -732,7 +791,7 @@ int main(int argc, char **argv)
result = 1;
goto cleanup;
}
- if (arguments.verbosity) {
+ if (arguments.common.verbosity) {
fprintf(stdout,
"Updating environment with revision %u\n",
env_new->data->revision);
@@ -764,9 +823,9 @@ int main(int argc, char **argv)
}
}

- update_environment(env_new, arguments.verbosity);
+ update_environment(env_new, arguments.common.verbosity);

- if (arguments.verbosity) {
+ if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
dump_env(env_new->data);
@@ -783,3 +842,12 @@ cleanup:
bgenv_finalize();
return result;
}
+
+int main(int argc, char **argv)
+{
+ if (strstr(argv[0], "bg_setenv")) {
+ return bg_setenv(argc, argv);
+ } else {
+ return bg_printenv(argc, argv);
+ }
+}
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:25:01 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
This introduces the following new command-line arguments:

--current: print values from the current env
--part: read from a specific environment partition

These are primarily useful for 'raw' output mode, which will be
introduced in the following commits.

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tools/bg_setenv.c | 122 ++++++++++++++++++++++++++++++++++------------
1 file changed, 91 insertions(+), 31 deletions(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 370eeeb..cb81a29 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -5,6 +5,7 @@
*
* Authors:
* Andreas Reichel <andreas.r...@siemens.com>
+ * Michael Adler <michae...@siemens.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
@@ -33,6 +34,11 @@ static char doc[] =
OPT("filepath", 'f', "ENVFILE", 0, \
"Environment to use. Expects a file name, " \
"usually called BGENV.DAT.") \
+ , OPT("part", 'p', "ENV_PART", 0, \
+ "Set environment partition to update. If no partition is " \
+ "specified, " \
+ "the one with the smallest revision value above zero is " \
+ "updated.") \
, OPT("verbose", 'v', 0, 0, "Be verbose") \
, OPT("version", 'V', 0, 0, "Print version")

@@ -41,10 +47,6 @@ static struct argp_option options_setenv[] = {
OPT("preserve", 'P', 0, 0, "Preserve existing entries"),
OPT("kernel", 'k', "KERNEL", 0, "Set kernel to load"),
OPT("args", 'a', "KERNEL_ARGS", 0, "Set kernel arguments"),
- OPT("part", 'p', "ENV_PART", 0,
- "Set environment partition to update. If no partition is "
- "specified, "
- "the one with the smallest revision value above zero is updated."),
OPT("revision", 'r', "REVISION", 0, "Set revision value"),
OPT("ustate", 's', "USTATE", 0, "Set update status for environment"),
OPT("watchdog", 'w', "WATCHDOG_TIMEOUT", 0,
@@ -61,6 +63,8 @@ static struct argp_option options_setenv[] = {

static struct argp_option options_printenv[] = {
BG_CLI_OPTIONS_COMMON,
+ OPT("current", 'c', 0, 0,
+ "Only print values from the current environment"),
{},
};

@@ -68,16 +72,18 @@ static struct argp_option options_printenv[] = {
struct arguments_common {
char *envfilepath;
bool verbosity;
+ /* which partition to operate on; a negative value means no partition
+ * was specified. */
+ int which_part;
+ bool part_specified;
};

/* Arguments used by bg_setenv. */
struct arguments_setenv {
struct arguments_common common;
- int which_part;
/* auto update feature automatically updates partition with
* oldest environment revision (lowest value) */
bool auto_update;
- bool part_specified;
/* whether to keep existing entries in BGENV before applying new
* settings */
bool preserve_env;
@@ -86,6 +92,7 @@ struct arguments_setenv {
/* Arguments used by bg_printenv. */
struct arguments_printenv {
struct arguments_common common;
+ bool current;
};

typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;
@@ -254,6 +261,7 @@ static error_t parse_common_opt(int key, char *arg, bool compat_mode,
struct arguments_common *arguments)
{
bool found = false;
+ int i;
switch (key) {
case 'f':
found = true;
@@ -284,6 +292,24 @@ static error_t parse_common_opt(int key, char *arg, bool compat_mode,
}
}
break;
+ case 'p':
+ found = true;
+ i = parse_int(arg);
+ if (errno) {
+ fprintf(stderr, "Invalid number specified for -p.\n");
+ return 1;
+ }
+ if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
+ arguments->which_part = i;
+ arguments->part_specified = true;
+ } else {
+ fprintf(stderr,
+ "Selected partition out of range. Valid range: "
+ "0..%d.\n",
+ ENV_NUM_CONFIG_PARTS - 1);
+ return 1;
+ }
+ break;
case 'v':
found = true;
/* Set verbosity in this program */
@@ -332,23 +358,6 @@ static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
e = journal_add_action(ENV_TASK_SET, "kernelparams", 0,
(uint8_t *)arg, strlen(arg) + 1);
break;
- case 'p':
- i = parse_int(arg);
- if (errno) {
- fprintf(stderr, "Invalid number specified for -p.\n");
- return 1;
- }
- if (i >= 0 && i < ENV_NUM_CONFIG_PARTS) {
- fprintf(stdout, "Updating config partition #%d\n", i);
- arguments->which_part = i;
- arguments->part_specified = true;
- } else {
- fprintf(stderr,
- "Selected partition out of range. Valid range: "
- "0..%d.\n", ENV_NUM_CONFIG_PARTS - 1);
- return 1;
- }
- break;
case 's':
i = parse_int(arg);
if (errno) {
@@ -461,6 +470,9 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
struct arguments_printenv *arguments = state->input;

switch (key) {
+ case 'c':
+ arguments->current = true;
+ break;
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
@@ -599,6 +611,28 @@ static void dump_envs(void)
}
}

+static void dump_latest_env(void)
+{
+ BGENV *env = bgenv_open_latest();
+ if (!env) {
+ fprintf(stderr, "Failed to retrieve latest environment.\n");
+ return;
+ }
+ dump_env(env->data);
+ bgenv_close(env);
+}
+
+static void dump_env_by_index(uint32_t index)
+{
+ BGENV *env = bgenv_open_by_index(index);
+ if (!env) {
+ fprintf(stderr, "Failed to retrieve latest environment.\n");
+ return;
+ }
+ dump_env(env->data);
+ bgenv_close(env);
+}
+
static bool get_env(char *configfilepath, BG_ENVDATA *data)
{
FILE *config;
@@ -696,10 +730,25 @@ static error_t bg_printenv(int argc, char **argv)
if (e) {
return e;
}
- if (arguments.common.envfilepath) {
- int result = printenv_from_file(arguments.common.envfilepath);
- free(arguments.common.envfilepath);
- return result;
+
+ const struct arguments_common *common = &arguments.common;
+
+ /* count the number of arguments which result in bg_printenv
+ * operating on a single partition; to avoid ambiguity, we only
+ * allow one such argument. */
+ int counter = 0;
+ if (common->envfilepath) ++counter;
+ if (common->part_specified) ++counter;
+ if (arguments.current) ++counter;
+ if (counter > 1) {
+ fprintf(stderr, "Error, only one of -c/-f/-p can be set.\n");
+ return 1;
+ }
+
+ if (common->envfilepath) {
+ e = printenv_from_file(common->envfilepath);
+ free(common->envfilepath);
+ return e;
}

/* not in file mode */
@@ -708,7 +757,17 @@ static error_t bg_printenv(int argc, char **argv)
return 1;
}

- dump_envs();
+ if (arguments.current) {
+ fprintf(stdout, "Using latest config partition\n");
+ dump_latest_env();
+ } else if (common->part_specified) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
+ dump_env_by_index(common->which_part);
+ } else {
+ dump_envs();
+ }
+
bgenv_finalize();
return 0;
}
@@ -740,7 +799,7 @@ static error_t bg_setenv(int argc, char **argv)
return e;
}

- if (arguments.auto_update && arguments.part_specified) {
+ if (arguments.auto_update && arguments.common.part_specified) {
fprintf(stderr, "Error, both automatic and manual partition "
"selection. Cannot use -p and -u "
"simultaneously.\n");
@@ -810,8 +869,9 @@ static error_t bg_setenv(int argc, char **argv)

bgenv_close(env_current);
} else {
- if (arguments.part_specified) {
- env_new = bgenv_open_by_index(arguments.which_part);
+ if (arguments.common.part_specified) {
+ env_new = bgenv_open_by_index(
+ arguments.common.which_part);
} else {
env_new = bgenv_open_latest();
}
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:25:02 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
The --output parameter allows to specify a list of fields which are
printed. This is primarily useful for 'raw' output mode, which will be
introduced in the following commits.

Example:

$ bg_printenv --current --output ustate
Using latest config partition
Values:
ustate: 0 (OK)

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tests/bg_setenv.bats | 20 +++++++
tools/bg_setenv.c | 134 ++++++++++++++++++++++++++++++++-----------
2 files changed, 121 insertions(+), 33 deletions(-)

diff --git a/tests/bg_setenv.bats b/tests/bg_setenv.bats
index 87779e6..f4739fd 100755
--- a/tests/bg_setenv.bats
+++ b/tests/bg_setenv.bats
@@ -114,3 +114,23 @@ foo = bar" ]]
run md5sum "$envfile"
[[ "$output" =~ ^a24b154a48e1f33b79b87e0fa5eff8a1\s*.* ]]
}
+
+@test "bg_printenv ustate" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate
+ [[ "$output" = "Values:
+ustate: 0 (OK)" ]]
+}
+
+@test "bg_printenv with all fields is the same as omitting fields" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ expected_output=$(bg_printenv "--filepath=$envfile")
+ run bg_printenv "--filepath=$envfile" --output in_progress,revision,kernel,kernelargs,watchdog_timeout,ustate,user
+ [[ "$output" = "$expected_output" ]]
+}
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index cb81a29..148078e 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -65,6 +65,11 @@ static struct argp_option options_printenv[] = {
BG_CLI_OPTIONS_COMMON,
OPT("current", 'c', 0, 0,
"Only print values from the current environment"),
+ OPT("output", 'o', "LIST", 0,
+ "Comma-separated list of fields which are printed. "
+ "Available fields: in_progress, revision, kernel, kernelargs, "
+ "watchdog_timeout, ustate, user. "
+ "If omitted, all available fields are printed."),
{},
};

@@ -89,10 +94,24 @@ struct arguments_setenv {
bool preserve_env;
};

+struct fields {
+ unsigned int in_progress : 1;
+ unsigned int revision : 1;
+ unsigned int kernel : 1;
+ unsigned int kernelargs : 1;
+ unsigned int wdog_timeout : 1;
+ unsigned int ustate : 1;
+ unsigned int user : 1;
+};
+
+static const struct fields ALL_FIELDS = {1, 1, 1, 1, 1, 1, 1};
+
/* Arguments used by bg_printenv. */
struct arguments_printenv {
struct arguments_common common;
bool current;
+ /* a bitset to decide which fields are printed */
+ struct fields output_fields;
};

typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;
@@ -465,14 +484,46 @@ static error_t parse_setenv_opt(int key, char *arg, struct argp_state *state)
return e;
}

+static error_t parse_output_fields(char *fields, struct fields *output_fields)
+{
+ char *token;
+ memset(output_fields, 0, sizeof(struct fields));
+ while ((token = strsep(&fields, ","))) {
+ if (*token == '\0') continue;
+ if (strcmp(token, "in_progress") == 0) {
+ output_fields->in_progress = true;
+ } else if (strcmp(token, "revision") == 0) {
+ output_fields->revision = true;
+ } else if (strcmp(token, "kernel") == 0) {
+ output_fields->kernel = true;
+ } else if (strcmp(token, "kernelargs") == 0) {
+ output_fields->kernelargs = true;
+ } else if (strcmp(token, "watchdog_timeout") == 0) {
+ output_fields->wdog_timeout = true;
+ } else if (strcmp(token, "ustate") == 0) {
+ output_fields->ustate = true;
+ } else if (strcmp(token, "user") == 0) {
+ output_fields->user = true;
+ } else {
+ fprintf(stderr, "Unknown output field: %s\n", token);
+ return 1;
+ }
+ }
+ return 0;
+}
+
static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
{
struct arguments_printenv *arguments = state->input;
+ error_t e = 0;

switch (key) {
case 'c':
arguments->current = true;
break;
+ case 'o':
+ e = parse_output_fields(arg, &arguments->output_fields);
+ break;
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
@@ -482,7 +533,7 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
return parse_common_opt(key, arg, false, &arguments->common);
}

- return 0;
+ return e;
}

static void dump_uservars(uint8_t *udata)
@@ -554,24 +605,38 @@ static void dump_uservars(uint8_t *udata)
}
}

-static void dump_env(BG_ENVDATA *env)
+static void dump_env(BG_ENVDATA *env, struct fields output_fields)
{
char buffer[ENV_STRING_LENGTH];
fprintf(stdout, "Values:\n");
- fprintf(stdout,
- "in_progress: %s\n",env->in_progress ? "yes" : "no");
- fprintf(stdout, "revision: %u\n", env->revision);
- fprintf(stdout,
- "kernel: %s\n", str16to8(buffer, env->kernelfile));
- fprintf(stdout,
- "kernelargs: %s\n", str16to8(buffer, env->kernelparams));
- fprintf(stdout,
- "watchdog timeout: %u seconds\n", env->watchdog_timeout_sec);
- fprintf(stdout, "ustate: %u (%s)\n", (uint8_t)env->ustate,
- ustate2str(env->ustate));
- fprintf(stdout, "\n");
- fprintf(stdout, "user variables:\n");
- dump_uservars(env->userdata);
+ if (output_fields.in_progress) {
+ fprintf(stdout, "in_progress: %s\n",
+ env->in_progress ? "yes" : "no");
+ }
+ if (output_fields.revision) {
+ fprintf(stdout, "revision: %u\n", env->revision);
+ }
+ if (output_fields.kernel) {
+ fprintf(stdout, "kernel: %s\n",
+ str16to8(buffer, env->kernelfile));
+ }
+ if (output_fields.kernelargs) {
+ fprintf(stdout, "kernelargs: %s\n",
+ str16to8(buffer, env->kernelparams));
+ }
+ if (output_fields.wdog_timeout) {
+ fprintf(stdout, "watchdog timeout: %u seconds\n",
+ env->watchdog_timeout_sec);
+ }
+ if (output_fields.ustate) {
+ fprintf(stdout, "ustate: %u (%s)\n",
+ (uint8_t)env->ustate, ustate2str(env->ustate));
+ }
+ if (output_fields.user) {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "user variables:\n");
+ dump_uservars(env->userdata);
+ }
fprintf(stdout, "\n\n");
}

@@ -594,7 +659,7 @@ static void update_environment(BGENV *env, bool verbosity)

}

-static void dump_envs(void)
+static void dump_envs(struct fields output_fields)
{
for (int i = 0; i < ENV_NUM_CONFIG_PARTS; i++) {
fprintf(stdout, "\n----------------------------\n");
@@ -606,30 +671,30 @@ static void dump_envs(void)
i);
return;
}
- dump_env(env->data);
+ dump_env(env->data, output_fields);
bgenv_close(env);
}
}

-static void dump_latest_env(void)
+static void dump_latest_env(struct fields output_fields)
{
BGENV *env = bgenv_open_latest();
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data);
+ dump_env(env->data, output_fields);
bgenv_close(env);
}

-static void dump_env_by_index(uint32_t index)
+static void dump_env_by_index(uint32_t index, struct fields output_fields)
{
BGENV *env = bgenv_open_by_index(index);
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data);
+ dump_env(env->data, output_fields);
bgenv_close(env);
}

@@ -658,13 +723,14 @@ static bool get_env(char *configfilepath, BG_ENVDATA *data)
return result;
}

-static int printenv_from_file(char *envfilepath) {
+static int printenv_from_file(char *envfilepath, struct fields output_fields)
+{
int success = 0;
BG_ENVDATA data;

success = get_env(envfilepath, &data);
if (success) {
- dump_env(&data);
+ dump_env(&data, output_fields);
return 0;
} else {
fprintf(stderr, "Error reading environment file.\n");
@@ -689,7 +755,7 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)

update_environment(&env, verbosity);
if (verbosity) {
- dump_env(env.data);
+ dump_env(env.data, ALL_FIELDS);
}
FILE *of = fopen(envfilepath, "wb");
if (of) {
@@ -723,8 +789,9 @@ static error_t bg_printenv(int argc, char **argv)
.doc = doc,
};

- struct arguments_printenv arguments;
- memset(&arguments, 0, sizeof(struct arguments_printenv));
+ struct arguments_printenv arguments = {
+ .output_fields = ALL_FIELDS,
+ };

error_t e = argp_parse(&argp_printenv, argc, argv, 0, 0, &arguments);
if (e) {
@@ -746,7 +813,8 @@ static error_t bg_printenv(int argc, char **argv)
}

if (common->envfilepath) {
- e = printenv_from_file(common->envfilepath);
+ e = printenv_from_file(common->envfilepath,
+ arguments.output_fields);
free(common->envfilepath);
return e;
}
@@ -759,13 +827,13 @@ static error_t bg_printenv(int argc, char **argv)

if (arguments.current) {
fprintf(stdout, "Using latest config partition\n");
- dump_latest_env();
+ dump_latest_env(arguments.output_fields);
} else if (common->part_specified) {
fprintf(stdout, "Using config partition #%d\n",
arguments.common.which_part);
- dump_env_by_index(common->which_part);
+ dump_env_by_index(common->which_part, arguments.output_fields);
} else {
- dump_envs();
+ dump_envs(arguments.output_fields);
}

bgenv_finalize();
@@ -826,7 +894,7 @@ static error_t bg_setenv(int argc, char **argv)
}

if (arguments.common.verbosity) {
- dump_envs();
+ dump_envs(ALL_FIELDS);
}

BGENV *env_new = NULL;
@@ -888,7 +956,7 @@ static error_t bg_setenv(int argc, char **argv)
if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
- dump_env(env_new->data);
+ dump_env(env_new->data, ALL_FIELDS);
}
if (!bgenv_write(env_new)) {
fprintf(stderr, "Error storing environment.\n");
--
2.33.1

Michael Adler

unread,
Nov 4, 2021, 5:25:03 AM11/4/21
to efibootg...@googlegroups.com, Michael Adler
Adding --raw to the bg_printenv command-line enables raw output mode
which can be used for shell scripting purposes.

Examples:

* Bring the fields as variables into your shell:

$ source <(bg_printenv --current --output kernel,ustate --raw)
$ echo "kernel: $KERNEL, ustate: $USTATE"
kernel: C:BOOTA:linux.efi, ustate: 0

* Assing the current ustate to a variable:

$ USTATE=$(bg_printenv --current --output ustate --raw)
$ echo "$USTATE"
USTATE=0

Signed-off-by: Michael Adler <michae...@siemens.com>
---
tests/bg_setenv.bats | 20 ++++++
tools/bg_setenv.c | 153 +++++++++++++++++++++++++++++--------------
2 files changed, 125 insertions(+), 48 deletions(-)

diff --git a/tests/bg_setenv.bats b/tests/bg_setenv.bats
index f4739fd..67cee8f 100755
--- a/tests/bg_setenv.bats
+++ b/tests/bg_setenv.bats
@@ -134,3 +134,23 @@ ustate: 0 (OK)" ]]
run bg_printenv "--filepath=$envfile" --output in_progress,revision,kernel,kernelargs,watchdog_timeout,ustate,user
[[ "$output" = "$expected_output" ]]
}
+
+@test "bg_printenv ustate raw" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate --raw
+ [[ "$output" = "USTATE=0" ]]
+}
+
+@test "bg_printenv multiple fields raw" {
+ local envfile
+ envfile="$(mktemp -d)/BGENV.DAT"
+
+ create_sample_bgenv "$envfile"
+ run bg_printenv "--filepath=$envfile" --output ustate,kernel,kernelargs --raw
+ [[ "$output" = "KERNEL=C:BOOT:kernel.efi
+KERNELARGS=root=/dev/sda
+USTATE=0" ]]
+}
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 148078e..7a234cf 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -70,6 +70,7 @@ static struct argp_option options_printenv[] = {
"Available fields: in_progress, revision, kernel, kernelargs, "
"watchdog_timeout, ustate, user. "
"If omitted, all available fields are printed."),
+ OPT("raw", 'r', 0, 0, "Raw output mode, e.g. for shell scripting"),
{},
};

@@ -112,6 +113,7 @@ struct arguments_printenv {
bool current;
/* a bitset to decide which fields are printed */
struct fields output_fields;
+ bool raw;
};

typedef enum { ENV_TASK_SET, ENV_TASK_DEL } BGENV_TASK;
@@ -524,6 +526,9 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
case 'o':
e = parse_output_fields(arg, &arguments->output_fields);
break;
+ case 'r':
+ arguments->raw = true;
+ break;
case ARGP_KEY_ARG:
/* too many arguments - program terminates with call to
* argp_usage with non-zero return code */
@@ -536,7 +541,7 @@ static error_t parse_printenv_opt(int key, char *arg, struct argp_state *state)
return e;
}

-static void dump_uservars(uint8_t *udata)
+static void dump_uservars(uint8_t *udata, bool raw)
{
char *key, *value;
uint64_t type;
@@ -547,10 +552,10 @@ static void dump_uservars(uint8_t *udata)
while (*udata) {
bgenv_map_uservar(udata, &key, &type, (uint8_t **)&value,
&rsize, &dsize);
- fprintf(stdout, "%s ", key);
+ fprintf(stdout, "%s", key);
type &= USERVAR_STANDARD_TYPE_MASK;
if (type == USERVAR_TYPE_STRING_ASCII) {
- fprintf(stdout, "= %s\n", value);
+ fprintf(stdout, raw ? "=%s\n" : " = %s\n", value);
} else if (type >= USERVAR_TYPE_UINT8 &&
type <= USERVAR_TYPE_UINT64) {
switch(type) {
@@ -567,8 +572,8 @@ static void dump_uservars(uint8_t *udata)
val_unum = *((uint64_t *) value);
break;
}
- fprintf(stdout, "= %llu\n",
- (long long unsigned int) val_unum);
+ fprintf(stdout, raw ? "=%llu\n" : " = %llu\n",
+ (long long unsigned int)val_unum);
} else if (type >= USERVAR_TYPE_SINT8 &&
type <= USERVAR_TYPE_SINT64) {
switch(type) {
@@ -585,19 +590,20 @@ static void dump_uservars(uint8_t *udata)
val_snum = *((int64_t *) value);
break;
}
- fprintf(stdout, "= %lld\n",
- (long long signed int) val_snum);
+ fprintf(stdout, raw ? "=%lld\n" : " = %lld\n",
+ (long long signed int)val_snum);
} else {
switch(type) {
case USERVAR_TYPE_CHAR:
- fprintf(stdout, "= %c\n", (char) *value);
+ fprintf(stdout, raw ? "=%c\n" : " = %c\n",
+ (char)*value);
break;
case USERVAR_TYPE_BOOL:
- fprintf(stdout, "= %s\n",
- (bool) *value ? "true" : "false");
+ fprintf(stdout, raw ? "=%s\n" : " = %s\n",
+ (bool)*value ? "true" : "false");
break;
default:
- fprintf(stdout, "( Type is not printable )\n");
+ fprintf(stdout, " ( Type is not printable )\n");
}
}

@@ -605,39 +611,71 @@ static void dump_uservars(uint8_t *udata)
}
}

-static void dump_env(BG_ENVDATA *env, struct fields output_fields)
+static void dump_env(BG_ENVDATA *env, struct fields output_fields, bool raw)
{
char buffer[ENV_STRING_LENGTH];
- fprintf(stdout, "Values:\n");
+ if (!raw) {
+ fprintf(stdout, "Values:\n");
+ }
if (output_fields.in_progress) {
- fprintf(stdout, "in_progress: %s\n",
- env->in_progress ? "yes" : "no");
+ if (raw) {
+ fprintf(stdout, "IN_PROGRESS=%d\n", env->in_progress);
+ } else {
+ fprintf(stdout, "in_progress: %s\n",
+ env->in_progress ? "yes" : "no");
+ }
}
if (output_fields.revision) {
- fprintf(stdout, "revision: %u\n", env->revision);
+ if (raw) {
+ fprintf(stdout, "REVISION=%d\n", env->revision);
+ } else {
+ fprintf(stdout, "revision: %u\n",
+ env->revision);
+ }
}
if (output_fields.kernel) {
- fprintf(stdout, "kernel: %s\n",
- str16to8(buffer, env->kernelfile));
+ char *kernelfile = str16to8(buffer, env->kernelfile);
+ if (raw) {
+ fprintf(stdout, "KERNEL=%s\n", kernelfile);
+ } else {
+ fprintf(stdout, "kernel: %s\n", kernelfile);
+ }
}
if (output_fields.kernelargs) {
- fprintf(stdout, "kernelargs: %s\n",
- str16to8(buffer, env->kernelparams));
+ char *kernelargs = str16to8(buffer, env->kernelparams);
+ if (raw) {
+ fprintf(stdout, "KERNELARGS=%s\n", kernelargs);
+ } else {
+ fprintf(stdout, "kernelargs: %s\n", kernelargs);
+ }
}
if (output_fields.wdog_timeout) {
- fprintf(stdout, "watchdog timeout: %u seconds\n",
- env->watchdog_timeout_sec);
+ if (raw) {
+ fprintf(stdout, "WATCHDOG_TIMEOUT=%u\n",
+ env->watchdog_timeout_sec);
+ } else {
+ fprintf(stdout, "watchdog timeout: %u seconds\n",
+ env->watchdog_timeout_sec);
+ }
}
if (output_fields.ustate) {
- fprintf(stdout, "ustate: %u (%s)\n",
- (uint8_t)env->ustate, ustate2str(env->ustate));
+ if (raw) {
+ fprintf(stdout, "USTATE=%u\n", env->ustate);
+ } else {
+ fprintf(stdout, "ustate: %u (%s)\n",
+ (uint8_t)env->ustate, ustate2str(env->ustate));
+ }
}
if (output_fields.user) {
- fprintf(stdout, "\n");
- fprintf(stdout, "user variables:\n");
- dump_uservars(env->userdata);
+ if (!raw) {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "user variables:\n");
+ }
+ dump_uservars(env->userdata, raw);
+ }
+ if (!raw) {
+ fprintf(stdout, "\n\n");
}
- fprintf(stdout, "\n\n");
}

static void update_environment(BGENV *env, bool verbosity)
@@ -659,11 +697,13 @@ static void update_environment(BGENV *env, bool verbosity)

}

-static void dump_envs(struct fields output_fields)
+static void dump_envs(struct fields output_fields, bool raw)
{
for (int i = 0; i < ENV_NUM_CONFIG_PARTS; i++) {
- fprintf(stdout, "\n----------------------------\n");
- fprintf(stdout, " Config Partition #%d ", i);
+ if (!raw) {
+ fprintf(stdout, "\n----------------------------\n");
+ fprintf(stdout, " Config Partition #%d ", i);
+ }
BGENV *env = bgenv_open_by_index(i);
if (!env) {
fprintf(stderr, "Error, could not read environment "
@@ -671,30 +711,31 @@ static void dump_envs(struct fields output_fields)
i);
return;
}
- dump_env(env->data, output_fields);
+ dump_env(env->data, output_fields, raw);
bgenv_close(env);
}
}

-static void dump_latest_env(struct fields output_fields)
+static void dump_latest_env(struct fields output_fields, bool raw)
{
BGENV *env = bgenv_open_latest();
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data, output_fields);
+ dump_env(env->data, output_fields, raw);
bgenv_close(env);
}

-static void dump_env_by_index(uint32_t index, struct fields output_fields)
+static void dump_env_by_index(uint32_t index, struct fields output_fields,
+ bool raw)
{
BGENV *env = bgenv_open_by_index(index);
if (!env) {
fprintf(stderr, "Failed to retrieve latest environment.\n");
return;
}
- dump_env(env->data, output_fields);
+ dump_env(env->data, output_fields, raw);
bgenv_close(env);
}

@@ -723,14 +764,15 @@ static bool get_env(char *configfilepath, BG_ENVDATA *data)
return result;
}

-static int printenv_from_file(char *envfilepath, struct fields output_fields)
+static int printenv_from_file(char *envfilepath, struct fields output_fields,
+ bool raw)
{
int success = 0;
BG_ENVDATA data;

success = get_env(envfilepath, &data);
if (success) {
- dump_env(&data, output_fields);
+ dump_env(&data, output_fields, raw);
return 0;
} else {
fprintf(stderr, "Error reading environment file.\n");
@@ -755,7 +797,7 @@ static int dumpenv_to_file(char *envfilepath, bool verbosity, bool preserve_env)

update_environment(&env, verbosity);
if (verbosity) {
- dump_env(env.data, ALL_FIELDS);
+ dump_env(env.data, ALL_FIELDS, false);
}
FILE *of = fopen(envfilepath, "wb");
if (of) {
@@ -811,10 +853,18 @@ static error_t bg_printenv(int argc, char **argv)
fprintf(stderr, "Error, only one of -c/-f/-p can be set.\n");
return 1;
}
+ if (arguments.raw && counter != 1) {
+ /* raw mode makes only sense if applied to a single
+ * partition */
+ fprintf(stderr, "Error, raw is set but "
+ "current/filepath/which_part is not set. "
+ "Must use -r and -c/-f/-p simultaneously.\n");
+ return 1;
+ }

if (common->envfilepath) {
e = printenv_from_file(common->envfilepath,
- arguments.output_fields);
+ arguments.output_fields, arguments.raw);
free(common->envfilepath);
return e;
}
@@ -826,14 +876,19 @@ static error_t bg_printenv(int argc, char **argv)
}

if (arguments.current) {
- fprintf(stdout, "Using latest config partition\n");
- dump_latest_env(arguments.output_fields);
+ if (!arguments.raw) {
+ fprintf(stdout, "Using latest config partition\n");
+ }
+ dump_latest_env(arguments.output_fields, arguments.raw);
} else if (common->part_specified) {
- fprintf(stdout, "Using config partition #%d\n",
- arguments.common.which_part);
- dump_env_by_index(common->which_part, arguments.output_fields);
+ if (!arguments.raw) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
+ }
+ dump_env_by_index(common->which_part, arguments.output_fields,
+ arguments.raw);
} else {
- dump_envs(arguments.output_fields);
+ dump_envs(arguments.output_fields, arguments.raw);
}

bgenv_finalize();
@@ -894,7 +949,7 @@ static error_t bg_setenv(int argc, char **argv)
}

if (arguments.common.verbosity) {
- dump_envs(ALL_FIELDS);
+ dump_envs(ALL_FIELDS, false);
}

BGENV *env_new = NULL;
@@ -938,6 +993,8 @@ static error_t bg_setenv(int argc, char **argv)
bgenv_close(env_current);
} else {
if (arguments.common.part_specified) {
+ fprintf(stdout, "Using config partition #%d\n",
+ arguments.common.which_part);
env_new = bgenv_open_by_index(
arguments.common.which_part);
} else {
@@ -956,7 +1013,7 @@ static error_t bg_setenv(int argc, char **argv)
if (arguments.common.verbosity) {
fprintf(stdout, "New environment data:\n");
fprintf(stdout, "---------------------\n");
- dump_env(env_new->data, ALL_FIELDS);
+ dump_env(env_new->data, ALL_FIELDS, false);
}
if (!bgenv_write(env_new)) {
fprintf(stderr, "Error storing environment.\n");
--
2.33.1

Jan Kiszka

unread,
Nov 7, 2021, 3:29:26 AM11/7/21
to Michael Adler, efibootg...@googlegroups.com
Thanks, applied.

Jan

Jan Kiszka

unread,
Nov 7, 2021, 3:50:59 AM11/7/21
to Michael Adler, efibootg...@googlegroups.com
On 04.11.21 10:24, Michael Adler wrote:
%u - fixed in next.

Jan
Reply all
Reply to author
Forward
0 new messages