mirror of
https://github.com/systemd/systemd.git
synced 2024-10-27 01:55:22 +03:00
bootctl: split out "set-efivar" verbs, too
This commit is contained in:
parent
6e975890a6
commit
6d8ca25766
@ -2611,6 +2611,8 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
|
||||
'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',
|
||||
|
149
src/boot/bootctl-set-efivar.c
Normal file
149
src/boot/bootctl-set-efivar.c
Normal file
@ -0,0 +1,149 @@
|
||||
/* 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;
|
||||
}
|
4
src/boot/bootctl-set-efivar.h
Normal file
4
src/boot/bootctl-set-efivar.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int verb_set_efivar(int argc, char *argv[], void *userdata);
|
@ -18,6 +18,7 @@
|
||||
#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"
|
||||
@ -2181,144 +2182,6 @@ static int verb_is_installed(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
}
|
||||
|
||||
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 },
|
||||
|
Loading…
Reference in New Issue
Block a user