| /* 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; |
| } |