/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #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; }