'src/boot/bootctl-random-seed.h',
'src/boot/bootctl-reboot-to-firmware.c',
'src/boot/bootctl-reboot-to-firmware.h',
+ 'src/boot/bootctl-set-efivar.c',
+ 'src/boot/bootctl-set-efivar.h',
'src/boot/bootctl-systemd-efi-options.c',
'src/boot/bootctl-systemd-efi-options.h',
'src/boot/bootctl-util.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <uchar.h>
+#include <unistd.h>
+
+#include "bootctl.h"
+#include "bootctl-set-efivar.h"
+#include "efivars.h"
+#include "stdio-util.h"
+#include "utf8.h"
+#include "virt.h"
+
+static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
+ char utf8[DECIMAL_STR_MAX(usec_t)];
+ char16_t *encoded;
+ usec_t timeout;
+ int r;
+
+ assert(arg1);
+ assert(ret_timeout);
+ assert(ret_timeout_size);
+
+ if (streq(arg1, "menu-force"))
+ timeout = USEC_INFINITY;
+ else if (streq(arg1, "menu-hidden"))
+ timeout = 0;
+ else {
+ r = parse_time(arg1, &timeout, USEC_PER_SEC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
+ if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC)
+ log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
+ }
+
+ xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
+
+ encoded = utf8_to_utf16(utf8, strlen(utf8));
+ if (!encoded)
+ return log_oom();
+
+ *ret_timeout = encoded;
+ *ret_timeout_size = char16_strlen(encoded) * 2 + 2;
+ return 0;
+}
+
+static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target, size_t *ret_target_size) {
+ char16_t *encoded = NULL;
+ int r;
+
+ assert(arg1);
+ assert(ret_target);
+ assert(ret_target_size);
+
+ if (streq(arg1, "@current")) {
+ r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected), NULL, (void *) ret_target, ret_target_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySelected': %m");
+
+ } else if (streq(arg1, "@oneshot")) {
+ r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, (void *) ret_target, ret_target_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryOneShot': %m");
+
+ } else if (streq(arg1, "@default")) {
+ r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault), NULL, (void *) ret_target, ret_target_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
+
+ } else if (arg1[0] == '@' && !streq(arg1, "@saved"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
+ else {
+ encoded = utf8_to_utf16(arg1, strlen(arg1));
+ if (!encoded)
+ return log_oom();
+
+ *ret_target = encoded;
+ *ret_target_size = char16_strlen(encoded) * 2 + 2;
+ }
+
+ return 0;
+}
+
+int verb_set_efivar(int argc, char *argv[], void *userdata) {
+ int r;
+
+ if (arg_root)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Acting on %s, skipping EFI variable setup.",
+ arg_image ? "image" : "root directory");
+
+ if (!is_efi_boot())
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Not booted with UEFI.");
+
+ if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo)), F_OK) < 0) {
+ if (errno == ENOENT) {
+ log_error_errno(errno, "Not booted with a supported boot loader.");
+ return -EOPNOTSUPP;
+ }
+
+ return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
+ }
+
+ if (detect_container() > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "'%s' operation not supported in a container.",
+ argv[0]);
+
+ if (!arg_touch_variables)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "'%s' operation cannot be combined with --no-variables.",
+ argv[0]);
+
+ const char *variable;
+ int (* arg_parser)(const char *, char16_t **, size_t *);
+
+ if (streq(argv[0], "set-default")) {
+ variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
+ arg_parser = parse_loader_entry_target_arg;
+ } else if (streq(argv[0], "set-oneshot")) {
+ variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
+ arg_parser = parse_loader_entry_target_arg;
+ } else if (streq(argv[0], "set-timeout")) {
+ variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout);
+ arg_parser = parse_timeout;
+ } else if (streq(argv[0], "set-timeout-oneshot")) {
+ variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot);
+ arg_parser = parse_timeout;
+ } else
+ assert_not_reached();
+
+ if (isempty(argv[1])) {
+ r = efi_set_variable(variable, NULL, 0);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to remove EFI variable '%s': %m", variable);
+ } else {
+ _cleanup_free_ char16_t *value = NULL;
+ size_t value_size = 0;
+
+ r = arg_parser(argv[1], &value, &value_size);
+ if (r < 0)
+ return r;
+ r = efi_set_variable(variable, value, value_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable);
+ }
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_set_efivar(int argc, char *argv[], void *userdata);
#include "bootctl-random-seed.h"
#include "bootctl-reboot-to-firmware.h"
#include "bootctl-systemd-efi-options.h"
+#include "bootctl-set-efivar.h"
#include "bootctl-util.h"
#include "bootspec.h"
#include "build.h"
}
}
-static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
- char utf8[DECIMAL_STR_MAX(usec_t)];
- char16_t *encoded;
- usec_t timeout;
- int r;
-
- assert(arg1);
- assert(ret_timeout);
- assert(ret_timeout_size);
-
- if (streq(arg1, "menu-force"))
- timeout = USEC_INFINITY;
- else if (streq(arg1, "menu-hidden"))
- timeout = 0;
- else {
- r = parse_time(arg1, &timeout, USEC_PER_SEC);
- if (r < 0)
- return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
- if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC)
- log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
- }
-
- xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
-
- encoded = utf8_to_utf16(utf8, strlen(utf8));
- if (!encoded)
- return log_oom();
-
- *ret_timeout = encoded;
- *ret_timeout_size = char16_strlen(encoded) * 2 + 2;
- return 0;
-}
-
-static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target, size_t *ret_target_size) {
- char16_t *encoded = NULL;
- int r;
-
- assert(arg1);
- assert(ret_target);
- assert(ret_target_size);
-
- if (streq(arg1, "@current")) {
- r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected), NULL, (void *) ret_target, ret_target_size);
- if (r < 0)
- return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySelected': %m");
-
- } else if (streq(arg1, "@oneshot")) {
- r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, (void *) ret_target, ret_target_size);
- if (r < 0)
- return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryOneShot': %m");
-
- } else if (streq(arg1, "@default")) {
- r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault), NULL, (void *) ret_target, ret_target_size);
- if (r < 0)
- return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
-
- } else if (arg1[0] == '@' && !streq(arg1, "@saved"))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
- else {
- encoded = utf8_to_utf16(arg1, strlen(arg1));
- if (!encoded)
- return log_oom();
-
- *ret_target = encoded;
- *ret_target_size = char16_strlen(encoded) * 2 + 2;
- }
-
- return 0;
-}
-
-static int verb_set_efivar(int argc, char *argv[], void *userdata) {
- int r;
-
- if (arg_root)
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
- "Acting on %s, skipping EFI variable setup.",
- arg_image ? "image" : "root directory");
-
- if (!is_efi_boot())
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
- "Not booted with UEFI.");
-
- if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo)), F_OK) < 0) {
- if (errno == ENOENT) {
- log_error_errno(errno, "Not booted with a supported boot loader.");
- return -EOPNOTSUPP;
- }
-
- return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
- }
-
- if (detect_container() > 0)
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
- "'%s' operation not supported in a container.",
- argv[0]);
-
- if (!arg_touch_variables)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "'%s' operation cannot be combined with --no-variables.",
- argv[0]);
-
- const char *variable;
- int (* arg_parser)(const char *, char16_t **, size_t *);
-
- if (streq(argv[0], "set-default")) {
- variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
- arg_parser = parse_loader_entry_target_arg;
- } else if (streq(argv[0], "set-oneshot")) {
- variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
- arg_parser = parse_loader_entry_target_arg;
- } else if (streq(argv[0], "set-timeout")) {
- variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout);
- arg_parser = parse_timeout;
- } else if (streq(argv[0], "set-timeout-oneshot")) {
- variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot);
- arg_parser = parse_timeout;
- } else
- assert_not_reached();
-
- if (isempty(argv[1])) {
- r = efi_set_variable(variable, NULL, 0);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to remove EFI variable '%s': %m", variable);
- } else {
- _cleanup_free_ char16_t *value = NULL;
- size_t value_size = 0;
-
- r = arg_parser(argv[1], &value, &value_size);
- if (r < 0)
- return r;
- r = efi_set_variable(variable, value, value_size);
- if (r < 0)
- return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable);
- }
-
- return 0;
-}
-
static int bootctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },