Another batch of option+verb conversions (#41586)

This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-04-12 13:58:38 +02:00
committed by GitHub
13 changed files with 1161 additions and 1514 deletions
+240 -286
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <sys/stat.h>
#include "sd-varlink.h"
@@ -23,12 +22,14 @@
#include "efivars.h"
#include "escape.h"
#include "find-esp.h"
#include "format-table.h"
#include "image-policy.h"
#include "log.h"
#include "loop-util.h"
#include "main-func.h"
#include "mount-util.h"
#include "openssl-util.h"
#include "options.h"
#include "pager.h"
#include "parse-argument.h"
#include "path-util.h"
@@ -254,296 +255,270 @@ static int help(void) {
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] COMMAND ...\n"
"\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
"\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
" status Show status of installed boot loader and EFI variables\n"
" reboot-to-firmware [BOOL]\n"
" Query or set reboot-to-firmware EFI flag\n"
"\n%3$sBoot Loader Specification Commands:%4$s\n"
" list List boot loader entries\n"
" unlink ID Remove boot loader entry\n"
" cleanup Remove files in ESP not referenced in any boot entry\n"
"\n%3$sBoot Loader Interface Commands:%4$s\n"
" set-default ID Set default boot loader entry\n"
" set-oneshot ID Set default boot loader entry, for next boot only\n"
" set-sysfail ID Set boot loader entry used in case of a system failure\n"
" set-timeout SECONDS Set the menu timeout\n"
" set-timeout-oneshot SECONDS\n"
" Set the menu timeout for the next boot only\n"
"\n%3$ssystemd-boot Commands:%4$s\n"
" install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n"
" remove Remove systemd-boot from the ESP and EFI variables\n"
" is-installed Test whether systemd-boot is installed in the ESP\n"
" random-seed Initialize or refresh random seed in ESP and EFI\n"
" variables\n"
"\n%3$sKernel Image Commands:%4$s\n"
" kernel-identify KERNEL-IMAGE\n"
" Identify kernel image type\n"
" kernel-inspect KERNEL-IMAGE\n"
" Prints details about the kernel image\n"
"\n%3$sBlock Device Discovery Commands:%4$s\n"
" -p --print-esp-path Print path to the EFI System Partition mount point\n"
" -x --print-boot-path Print path to the $BOOT partition mount point\n"
" --print-loader-path\n"
" Print path to currently booted boot loader binary\n"
" --print-stub-path Print path to currently booted unified kernel binary\n"
" -R --print-root-device\n"
" Print path to the block device node backing the\n"
" root file system (returns e.g. /dev/nvme0n1p5)\n"
" -RR Print path to the whole disk block device node\n"
" backing the root FS (returns e.g. /dev/nvme0n1)\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
" --esp-path=PATH Path to the EFI System Partition (ESP)\n"
" --boot-path=PATH Path to the $BOOT partition\n"
" --root=PATH Operate on an alternate filesystem root\n"
" --image=PATH Operate on disk image as filesystem root\n"
" --image-policy=POLICY\n"
" Specify disk image dissection policy\n"
" --install-source=auto|image|host\n"
" Where to pick files when using --root=/--image=\n"
" --variables=yes|no\n"
" Whether to modify EFI variables\n"
" --random-seed=yes|no\n"
" Whether to create random-seed file during install\n"
" --no-pager Do not pipe output into a pager\n"
" --graceful Don't fail when the ESP cannot be found or EFI\n"
" variables cannot be written\n"
" -q --quiet Suppress output\n"
" --make-entry-directory=yes|no|auto\n"
" Create $BOOT/ENTRY-TOKEN/ directory\n"
" --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
" Entry token to use for this installation\n"
" --json=pretty|short|off\n"
" Generate JSON output\n"
" --all-architectures\n"
" Install all supported EFI architectures\n"
" --efi-boot-option-description=DESCRIPTION\n"
" Description of the entry in the boot option list\n"
" --efi-boot-option-description-with-device=yes\n"
" Suffix description with disk vendor/model/serial\n"
" --dry-run Dry run (unlink and cleanup)\n"
" --secure-boot-auto-enroll=yes|no\n"
" Set up secure boot auto-enrollment\n"
" --private-key=PATH|URI\n"
" Private key to use when setting up secure boot\n"
" auto-enrollment or an engine or provider specific\n"
" designation if --private-key-source= is used\n"
" --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
" Specify how to use KEY for --private-key=. Allows\n"
" an OpenSSL engine/provider to be used when setting\n"
" up secure boot auto-enrollment\n"
" --certificate=PATH|URI\n"
" PEM certificate to use when setting up Secure Boot\n"
" auto-enrollment, or a provider specific designation\n"
" if --certificate-source= is used\n"
" --certificate-source=file|provider:PROVIDER\n"
" Specify how to interpret the certificate from\n"
" --certificate=. Allows the certificate to be loaded\n"
" from an OpenSSL provider\n"
"\nSee the %2$s for details.\n",
static const char *const verb_groups[] = {
"Generic EFI Firmware/Boot Loader Commands",
"Boot Loader Specification Commands",
"Boot Loader Interface Commands",
"systemd-boot Commands",
"Kernel Image Commands",
};
static const char *const option_groups[] = {
"Block Device Discovery Commands",
"Options",
};
_cleanup_(table_unref_many) Table *verb_tables[ELEMENTSOF(verb_groups) + 1] = {};
_cleanup_(table_unref_many) Table *option_tables[ELEMENTSOF(option_groups) + 1] = {};
for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
r = verbs_get_help_table_group(verb_groups[i], &verb_tables[i]);
if (r < 0)
return r;
}
for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
r = option_parser_get_help_table_group(option_groups[i], &option_tables[i]);
if (r < 0)
return r;
}
(void) table_sync_column_widths(0,
verb_tables[0], verb_tables[1], verb_tables[2],
verb_tables[3], verb_tables[4],
option_tables[0], option_tables[1]);
printf("%s [OPTIONS...] COMMAND ...\n"
"\n%sControl EFI firmware boot settings and manage boot loader.%s\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal());
for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
printf("\n%s%s:%s\n", ansi_underline(), verb_groups[i], ansi_normal());
r = table_print_or_warn(verb_tables[i]);
if (r < 0)
return r;
}
for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
printf("\n%s%s:%s\n", ansi_underline(), option_groups[i], ansi_normal());
r = table_print_or_warn(option_tables[i]);
if (r < 0)
return r;
}
printf("\nSee the %s for details.\n", link);
return 0;
}
static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
return help();
}
VERB_COMMON_HELP(help);
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_ESP_PATH = 0x100,
ARG_BOOT_PATH,
ARG_ROOT,
ARG_IMAGE,
ARG_IMAGE_POLICY,
ARG_INSTALL_SOURCE,
ARG_VERSION,
ARG_VARIABLES,
ARG_NO_VARIABLES,
ARG_RANDOM_SEED,
ARG_NO_PAGER,
ARG_GRACEFUL,
ARG_MAKE_ENTRY_DIRECTORY,
ARG_ENTRY_TOKEN,
ARG_JSON,
ARG_ARCH_ALL,
ARG_EFI_BOOT_OPTION_DESCRIPTION,
ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE,
ARG_DRY_RUN,
ARG_PRINT_LOADER_PATH,
ARG_PRINT_STUB_PATH,
ARG_SECURE_BOOT_AUTO_ENROLL,
ARG_CERTIFICATE,
ARG_CERTIFICATE_SOURCE,
ARG_PRIVATE_KEY,
ARG_PRIVATE_KEY_SOURCE,
};
VERB_GROUP("Generic EFI Firmware/Boot Loader Commands");
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "esp-path", required_argument, NULL, ARG_ESP_PATH },
{ "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */
{ "boot-path", required_argument, NULL, ARG_BOOT_PATH },
{ "root", required_argument, NULL, ARG_ROOT },
{ "image", required_argument, NULL, ARG_IMAGE },
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
{ "install-source", required_argument, NULL, ARG_INSTALL_SOURCE },
{ "print-esp-path", no_argument, NULL, 'p' },
{ "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */
{ "print-boot-path", no_argument, NULL, 'x' },
{ "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH },
{ "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH },
{ "print-root-device", no_argument, NULL, 'R' },
{ "variables", required_argument, NULL, ARG_VARIABLES },
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, /* Compatibility alias */
{ "random-seed", required_argument, NULL, ARG_RANDOM_SEED },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
{ "quiet", no_argument, NULL, 'q' },
{ "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY },
{ "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */
{ "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN },
{ "json", required_argument, NULL, ARG_JSON },
{ "all-architectures", no_argument, NULL, ARG_ARCH_ALL },
{ "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
{ "efi-boot-option-description-with-device", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE },
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
{ "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
{ "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{}
};
VERB_SCOPE(, verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT,
"Show status of installed boot loader and EFI variables");
int c, r;
VERB_SCOPE(, verb_reboot_to_firmware, "reboot-to-firmware", "[BOOL]", VERB_ANY, 2, 0,
"Query or set reboot-to-firmware EFI flag");
VERB_GROUP("Boot Loader Specification Commands");
VERB_SCOPE_NOARG(, verb_list, "list",
"List boot loader entries");
VERB_SCOPE(, verb_unlink, "unlink", "ID", 2, 2, 0,
"Remove boot loader entry");
VERB_SCOPE_NOARG(, verb_cleanup, "cleanup",
"Remove files in ESP not referenced in any boot entry");
VERB_GROUP("Boot Loader Interface Commands");
VERB_SCOPE(, verb_set_efivar, "set-default", "ID", 2, 2, 0,
"Set default boot loader entry");
VERB_SCOPE(, verb_set_efivar, "set-oneshot", "ID", 2, 2, 0,
"Set default boot loader entry, for next boot only");
VERB_SCOPE(, verb_set_efivar, "set-sysfail", "ID", 2, 2, 0,
"Set boot loader entry used in case of a system failure");
VERB_SCOPE(, verb_set_efivar, "set-timeout", "SECONDS", 2, 2, 0,
"Set the menu timeout");
VERB_SCOPE(, verb_set_efivar, "set-timeout-oneshot", "SECONDS", 2, 2, 0,
"Set the menu timeout for the next boot only");
VERB_SCOPE(, verb_set_efivar, "set-preferred", "ID", 2, 2, 0,
/* help= */ NULL);
VERB_GROUP("systemd-boot Commands");
VERB_SCOPE(, verb_install, "install", NULL, VERB_ANY, 1, 0,
"Install systemd-boot to the ESP and EFI variables");
VERB_SCOPE(, verb_install, "update", NULL, VERB_ANY, 1, 0,
"Update systemd-boot in the ESP and EFI variables");
VERB_SCOPE_NOARG(, verb_remove, "remove",
"Remove systemd-boot from the ESP and EFI variables");
VERB_SCOPE_NOARG(, verb_is_installed, "is-installed",
"Test whether systemd-boot is installed in the ESP");
VERB_SCOPE_NOARG(, verb_random_seed, "random-seed",
"Initialize or refresh random seed in ESP and EFI variables");
VERB_GROUP("Kernel Image Commands");
VERB_SCOPE(, verb_kernel_identify, "kernel-identify", "KERNEL-IMAGE", 2, 2, 0,
"Identify kernel image type");
VERB_SCOPE(, verb_kernel_inspect, "kernel-inspect", "KERNEL-IMAGE", 2, 2, 0,
"Prints details about the kernel image");
static int parse_argv(int argc, char *argv[], char ***ret_args) {
int r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hpxRq", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_GROUP("Block Device Discovery Commands"):
break;
OPTION('p', "print-esp-path", NULL, "Print path to the EFI System Partition mount point"): {}
OPTION_LONG("print-path", NULL, /* help= */ NULL): /* Compatibility alias */
arg_print_esp_path = true;
break;
OPTION('x', "print-boot-path", NULL, "Print path to the $BOOT partition mount point"):
arg_print_dollar_boot_path = true;
break;
OPTION_LONG("print-loader-path", NULL, "Print path to currently booted boot loader binary"):
arg_print_loader_path = true;
break;
OPTION_LONG("print-stub-path", NULL, "Print path to currently booted unified kernel binary"):
arg_print_stub_path = true;
break;
OPTION('R', "print-root-device", NULL,
"Print path to the block device node backing the root file system"
" (returns e.g. /dev/nvme0n1p5)"): {}
OPTION_HELP_VERBATIM("-RR",
"Print path to the whole disk block device node backing the root FS"
" (returns e.g. /dev/nvme0n1)"):
arg_print_root_device++;
break;
OPTION_GROUP("Options"):
break;
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_ESP_PATH:
r = free_and_strdup(&arg_esp_path, optarg);
OPTION_LONG("esp-path", "PATH", "Path to the EFI System Partition (ESP)"): {}
OPTION_LONG("path", "PATH", /* help= */ NULL): /* Compatibility alias */
r = free_and_strdup(&arg_esp_path, arg);
if (r < 0)
return log_oom();
break;
case ARG_BOOT_PATH:
r = free_and_strdup(&arg_xbootldr_path, optarg);
OPTION_LONG("boot-path", "PATH", "Path to the $BOOT partition"):
r = free_and_strdup(&arg_xbootldr_path, arg);
if (r < 0)
return log_oom();
break;
case ARG_ROOT:
r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
r = parse_path_argument(arg, /* suppress_root= */ true, &arg_root);
if (r < 0)
return r;
break;
case ARG_IMAGE:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_image);
if (r < 0)
return r;
break;
case ARG_IMAGE_POLICY:
r = parse_image_policy_argument(optarg, &arg_image_policy);
OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
r = parse_image_policy_argument(arg, &arg_image_policy);
if (r < 0)
return r;
break;
case ARG_INSTALL_SOURCE: {
InstallSource is = install_source_from_string(optarg);
OPTION_LONG("install-source", "SOURCE",
"Where to pick files when using --root=/--image= (auto, image, host)"): {
InstallSource is = install_source_from_string(arg);
if (is < 0)
return log_error_errno(is, "Unexpected parameter for --install-source=: %s", optarg);
return log_error_errno(is, "Unexpected parameter for --install-source=: %s", arg);
arg_install_source = is;
break;
}
case 'p':
arg_print_esp_path = true;
break;
case 'x':
arg_print_dollar_boot_path = true;
break;
case ARG_PRINT_LOADER_PATH:
arg_print_loader_path = true;
break;
case ARG_PRINT_STUB_PATH:
arg_print_stub_path = true;
break;
case 'R':
arg_print_root_device++;
break;
case ARG_VARIABLES:
r = parse_tristate_argument_with_auto("--variables=", optarg, &arg_touch_variables);
OPTION_LONG("variables", "BOOL", "Whether to modify EFI variables"):
r = parse_tristate_argument_with_auto("--variables=", arg, &arg_touch_variables);
if (r < 0)
return r;
#if !ENABLE_EFI
if (arg_touch_variables > 0)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Compiled without support for EFI, --variables=%s cannot be specified.", optarg);
"Compiled without support for EFI, --variables=%s cannot be specified.", arg);
#endif
break;
case ARG_NO_VARIABLES:
OPTION_LONG("no-variables", NULL, /* help= */ NULL): /* Compatibility alias */
arg_touch_variables = false;
break;
case ARG_RANDOM_SEED:
r = parse_boolean_argument("--random-seed=", optarg, &arg_install_random_seed);
OPTION_LONG("random-seed", "BOOL", "Whether to create random-seed file during install"):
r = parse_boolean_argument("--random-seed=", arg, &arg_install_random_seed);
if (r < 0)
return r;
break;
case ARG_NO_PAGER:
OPTION_COMMON_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_GRACEFUL:
OPTION_LONG("graceful", NULL,
"Don't fail when the ESP cannot be found or EFI variables cannot be written"):
_arg_graceful = ARG_GRACEFUL_YES;
break;
case 'q':
OPTION('q', "quiet", NULL, "Suppress output"):
arg_quiet = true;
break;
case ARG_ENTRY_TOKEN:
r = parse_boot_entry_token_type(optarg, &arg_entry_token_type, &arg_entry_token);
OPTION_LONG("entry-token", "TOKEN",
"Entry token to use for this installation"
" (machine-id, os-id, os-image-id, auto, literal:…)"):
r = parse_boot_entry_token_type(arg, &arg_entry_token_type, &arg_entry_token);
if (r < 0)
return r;
break;
case ARG_MAKE_ENTRY_DIRECTORY:
if (streq(optarg, "auto")) /* retained for backwards compatibility */
OPTION_LONG("make-entry-directory", "yes|no|auto", "Create $BOOT/ENTRY-TOKEN/ directory"): {}
OPTION_LONG("make-machine-id-directory", "BOOL", /* help= */ NULL): /* Compatibility alias */
if (streq(arg, "auto")) /* retained for backwards compatibility */
arg_make_entry_directory = -1; /* yes if machine-id is permanent */
else {
r = parse_boolean_argument("--make-entry-directory=", optarg, NULL);
r = parse_boolean_argument("--make-entry-directory=", arg, NULL);
if (r < 0)
return r;
@@ -551,95 +526,99 @@ static int parse_argv(int argc, char *argv[]) {
}
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
OPTION_COMMON_JSON:
r = parse_json_argument(arg, &arg_json_format_flags);
if (r <= 0)
return r;
break;
case ARG_ARCH_ALL:
OPTION_LONG("all-architectures", NULL, "Install all supported EFI architectures"):
arg_arch_all = true;
break;
case ARG_EFI_BOOT_OPTION_DESCRIPTION:
if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) {
_cleanup_free_ char *escaped = cescape(optarg);
OPTION_LONG("efi-boot-option-description", "DESCRIPTION",
"Description of the entry in the boot option list"):
if (isempty(arg) || !(string_is_safe(arg) && utf8_is_valid(arg))) {
_cleanup_free_ char *escaped = cescape(arg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid --efi-boot-option-description=: %s", strna(escaped));
}
if (strlen(optarg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
if (strlen(arg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--efi-boot-option-description= too long: %zu > %zu",
strlen(optarg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
r = free_and_strdup_warn(&arg_efi_boot_option_description, optarg);
strlen(arg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
r = free_and_strdup_warn(&arg_efi_boot_option_description, arg);
if (r < 0)
return r;
break;
case ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE:
r = parse_boolean_argument("--efi-boot-option-description-with-device=", optarg, &arg_efi_boot_option_description_with_device);
OPTION_LONG("efi-boot-option-description-with-device", "BOOL",
"Suffix description with disk vendor/model/serial"):
r = parse_boolean_argument("--efi-boot-option-description-with-device=", arg,
&arg_efi_boot_option_description_with_device);
if (r < 0)
return r;
break;
case ARG_DRY_RUN:
OPTION_LONG("dry-run", NULL, "Dry run (unlink and cleanup)"):
arg_dry_run = true;
break;
case ARG_SECURE_BOOT_AUTO_ENROLL:
r = parse_boolean_argument("--secure-boot-auto-enroll=", optarg, &arg_secure_boot_auto_enroll);
OPTION_LONG("secure-boot-auto-enroll", "BOOL", "Set up secure boot auto-enrollment"):
r = parse_boolean_argument("--secure-boot-auto-enroll=", arg,
&arg_secure_boot_auto_enroll);
if (r < 0)
return r;
break;
case ARG_CERTIFICATE:
r = free_and_strdup_warn(&arg_certificate, optarg);
OPTION_LONG("private-key", "PATH|URI",
"Private key for Secure Boot auto-enrollment"):
r = free_and_strdup_warn(&arg_private_key, arg);
if (r < 0)
return r;
break;
case ARG_CERTIFICATE_SOURCE:
r = parse_openssl_certificate_source_argument(
optarg,
&arg_certificate_source,
&arg_certificate_source_type);
OPTION_LONG("private-key-source", "SOURCE",
"Specify how to use the private key "
"(file, provider:PROVIDER, engine:ENGINE)"):
r = parse_openssl_key_source_argument(arg,
&arg_private_key_source,
&arg_private_key_source_type);
if (r < 0)
return r;
break;
case ARG_PRIVATE_KEY: {
r = free_and_strdup_warn(&arg_private_key, optarg);
OPTION_LONG("certificate", "PATH|URI",
"PEM certificate to use when setting up Secure Boot auto-enrollment, "
"or a provider specific designation if --certificate-source= is used"):
r = free_and_strdup_warn(&arg_certificate, arg);
if (r < 0)
return r;
break;
OPTION_LONG("certificate-source", "SOURCE",
"Specify how to interpret the certificate from --certificate=. "
"Allows the certificate to be loaded from an OpenSSL provider "
"(file, provider:PROVIDER)"):
r = parse_openssl_certificate_source_argument(arg,
&arg_certificate_source,
&arg_certificate_source_type);
if (r < 0)
return r;
break;
}
case ARG_PRIVATE_KEY_SOURCE:
r = parse_openssl_key_source_argument(
optarg,
&arg_private_key_source,
&arg_private_key_source_type);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
char **args = option_parser_get_args(&state);
if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path cannot be combined.");
if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
if ((arg_root || arg_image) && args[0] && !STR_IN_SET(args[0], "status", "list",
"install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Options --root= and --image= are not supported with verb %s.",
argv[optind]);
args[0]);
if (arg_root && arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
@@ -647,7 +626,7 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_install_source != INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
if (arg_dry_run && args[0] && !STR_IN_SET(args[0], "unlink", "cleanup"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry-run is only supported with --unlink or --cleanup");
if (arg_secure_boot_auto_enroll) {
@@ -670,36 +649,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_pager_flags |= PAGER_DISABLE;
}
*ret_args = args;
return 1;
}
static int bootctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
{ "install", VERB_ANY, 1, 0, verb_install },
{ "update", VERB_ANY, 1, 0, verb_install },
{ "remove", VERB_ANY, 1, 0, verb_remove },
{ "is-installed", VERB_ANY, 1, 0, verb_is_installed },
{ "kernel-identify", 2, 2, 0, verb_kernel_identify },
{ "kernel-inspect", 2, 2, 0, verb_kernel_inspect },
{ "list", VERB_ANY, 1, 0, verb_list },
{ "unlink", 2, 2, 0, verb_unlink },
{ "cleanup", VERB_ANY, 1, 0, verb_cleanup },
{ "set-default", 2, 2, 0, verb_set_efivar },
{ "set-preferred", 2, 2, 0, verb_set_efivar },
{ "set-oneshot", 2, 2, 0, verb_set_efivar },
{ "set-timeout", 2, 2, 0, verb_set_efivar },
{ "set-timeout-oneshot", 2, 2, 0, verb_set_efivar },
{ "set-sysfail", 2, 2, 0, verb_set_efivar },
{ "random-seed", VERB_ANY, 1, 0, verb_random_seed },
{ "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
static int vl_server(void) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
int r;
@@ -740,7 +693,8 @@ static int run(int argc, char *argv[]) {
log_setup();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
@@ -800,7 +754,7 @@ static int run(int argc, char *argv[]) {
return log_oom();
}
return bootctl_main(argc, argv);
return dispatch_verb_with_args(args, NULL);
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
+177 -250
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <sys/mman.h>
#include "sd-device.h"
@@ -19,9 +18,11 @@
#include "cryptsetup-util.h"
#include "extract-word.h"
#include "fileio.h"
#include "format-table.h"
#include "libfido2-util.h"
#include "log.h"
#include "main-func.h"
#include "options.h"
#include "pager.h"
#include "parse-argument.h"
#include "parse-util.h"
@@ -30,6 +31,7 @@
#include "process-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "tpm2-pcr.h"
#include "tpm2-util.h"
@@ -231,207 +233,96 @@ static int help(void) {
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] [BLOCK-DEVICE]\n\n"
"%5$sEnroll a security token or authentication credential to a LUKS volume.%6$s\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not spawn a pager\n"
" --list-devices List candidate block devices to operate on\n"
" --wipe-slot=SLOT1,SLOT2,…\n"
" Wipe specified slots\n"
"\n%3$sUnlocking:%4$s\n"
" --unlock-key-file=PATH\n"
" Use a file to unlock the volume\n"
" --unlock-fido2-device=PATH\n"
" Use a FIDO2 device to unlock the volume\n"
" --unlock-tpm2-device=PATH\n"
" Use a TPM2 device to unlock the volume\n"
"\n%3$sSimple Enrollment:%4$s\n"
" --password Enroll a user-supplied password\n"
" --recovery-key Enroll a recovery key\n"
"\n%3$sPKCS#11 Enrollment:%4$s\n"
" --pkcs11-token-uri=URI|auto|list\n"
" Enroll a PKCS#11 security token or list them\n"
"\n%3$sFIDO2 Enrollment:%4$s\n"
" --fido2-device=PATH|auto|list\n"
" Enroll a FIDO2-HMAC security token or list them\n"
" --fido2-salt-file=PATH\n"
" Use salt from a file instead of generating one\n"
" --fido2-parameters-in-header=BOOL\n"
" Whether to store FIDO2 parameters in the LUKS2 header\n"
" --fido2-credential-algorithm=STRING\n"
" Specify COSE algorithm for FIDO2 credential\n"
" --fido2-with-client-pin=BOOL\n"
" Whether to require entering a PIN to unlock the volume\n"
" --fido2-with-user-presence=BOOL\n"
" Whether to require user presence to unlock the volume\n"
" --fido2-with-user-verification=BOOL\n"
" Whether to require user verification to unlock the volume\n"
"\n%3$sTPM2 Enrollment:%4$s\n"
" --tpm2-device=PATH|auto|list\n"
" Enroll a TPM2 device or list them\n"
" --tpm2-device-key=PATH\n"
" Enroll a TPM2 device using its public key\n"
" --tpm2-seal-key-handle=HANDLE\n"
" Specify handle of key to use for sealing\n"
" --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
" Specify TPM2 PCRs to seal against\n"
" --tpm2-public-key=PATH\n"
" Enroll signed TPM2 PCR policy against PEM public key\n"
" --tpm2-public-key-pcrs=PCR1+PCR2+PCR3+…\n"
" Enroll signed TPM2 PCR policy for specified TPM2 PCRs\n"
" --tpm2-signature=PATH\n"
" Validate public key enrollment works with JSON signature\n"
" file\n"
" --tpm2-pcrlock=PATH\n"
" Specify pcrlock policy to lock against\n"
" --tpm2-with-pin=BOOL\n"
" Whether to require entering a PIN to unlock the volume\n"
"\nSee the %2$s for details.\n",
static const char* const groups[] = {
NULL,
"Unlocking",
"Simple Enrollment",
"PKCS#11 Enrollment",
"FIDO2 Enrollment",
"TPM2 Enrollment",
};
_cleanup_(table_unref_many) Table *tables[ELEMENTSOF(groups) + 1] = {};
for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
r = option_parser_get_help_table_group(groups[i], &tables[i]);
if (r < 0)
return r;
}
(void) table_sync_column_widths(0, tables[0], tables[1], tables[2], tables[3], tables[4], tables[5]);
printf("%s [OPTIONS...] [BLOCK-DEVICE]\n\n"
"%sEnroll a security token or authentication credential to a LUKS volume.%s\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal());
for (size_t i = 0; i < ELEMENTSOF(groups); i++) {
printf("\n%s%s:%s\n", ansi_underline(), groups[i] ?: "Options", ansi_normal());
r = table_print_or_warn(tables[i]);
if (r < 0)
return r;
}
printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_PASSWORD,
ARG_RECOVERY_KEY,
ARG_UNLOCK_KEYFILE,
ARG_UNLOCK_FIDO2_DEVICE,
ARG_UNLOCK_TPM2_DEVICE,
ARG_PKCS11_TOKEN_URI,
ARG_FIDO2_DEVICE,
ARG_FIDO2_SALT_FILE,
ARG_FIDO2_PARAMETERS_IN_HEADER,
ARG_TPM2_DEVICE,
ARG_TPM2_DEVICE_KEY,
ARG_TPM2_SEAL_KEY_HANDLE,
ARG_TPM2_PCRS,
ARG_TPM2_PUBLIC_KEY,
ARG_TPM2_PUBLIC_KEY_PCRS,
ARG_TPM2_SIGNATURE,
ARG_TPM2_PCRLOCK,
ARG_TPM2_WITH_PIN,
ARG_WIPE_SLOT,
ARG_FIDO2_WITH_PIN,
ARG_FIDO2_WITH_UP,
ARG_FIDO2_WITH_UV,
ARG_FIDO2_CRED_ALG,
ARG_LIST_DEVICES,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "password", no_argument, NULL, ARG_PASSWORD },
{ "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
{ "unlock-key-file", required_argument, NULL, ARG_UNLOCK_KEYFILE },
{ "unlock-fido2-device", required_argument, NULL, ARG_UNLOCK_FIDO2_DEVICE },
{ "unlock-tpm2-device", required_argument, NULL, ARG_UNLOCK_TPM2_DEVICE },
{ "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
{ "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
{ "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
{ "fido2-salt-file", required_argument, NULL, ARG_FIDO2_SALT_FILE },
{ "fido2-parameters-in-header", required_argument, NULL, ARG_FIDO2_PARAMETERS_IN_HEADER },
{ "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
{ "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
{ "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
{ "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE },
{ "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
{ "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
{ "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
{ "tpm2-signature", required_argument, NULL, ARG_TPM2_SIGNATURE },
{ "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK },
{ "tpm2-with-pin", required_argument, NULL, ARG_TPM2_WITH_PIN },
{ "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
{ "list-devices", no_argument, NULL, ARG_LIST_DEVICES },
{}
};
bool auto_public_key_pcr_mask = true, auto_pcrlock = true;
int c, r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
int r;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_NO_PAGER:
OPTION_COMMON_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_FIDO2_WITH_PIN:
r = parse_boolean_argument("--fido2-with-client-pin=", optarg, NULL);
OPTION_LONG("list-devices", NULL,
"List candidate block devices to operate on"):
return blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_REQUIRE_LUKS,
/* ret_devices= */ NULL,
/* ret_n_devices= */ NULL);
OPTION_LONG("wipe-slot", "SLOT1,SLOT2,…",
"Wipe specified slots"):
r = parse_wipe_slot(arg);
if (r < 0)
return r;
SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, r);
break;
case ARG_FIDO2_WITH_UP:
r = parse_boolean_argument("--fido2-with-user-presence=", optarg, NULL);
if (r < 0)
return r;
OPTION_GROUP("Unlocking"): {}
SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, r);
break;
case ARG_FIDO2_WITH_UV:
r = parse_boolean_argument("--fido2-with-user-verification=", optarg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, r);
break;
case ARG_PASSWORD:
if (arg_enroll_type >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
arg_enroll_type = ENROLL_PASSWORD;
break;
case ARG_RECOVERY_KEY:
if (arg_enroll_type >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
arg_enroll_type = ENROLL_RECOVERY;
break;
case ARG_UNLOCK_KEYFILE:
OPTION_LONG("unlock-key-file", "PATH",
"Use a file to unlock the volume"):
if (arg_unlock_type != UNLOCK_PASSWORD)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple unlock methods specified at once, refusing.");
r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_unlock_keyfile);
r = parse_path_argument(arg, /* suppress_root= */ true, &arg_unlock_keyfile);
if (r < 0)
return r;
arg_unlock_type = UNLOCK_KEYFILE;
break;
case ARG_UNLOCK_FIDO2_DEVICE: {
OPTION_LONG("unlock-fido2-device", "PATH",
"Use a FIDO2 device to unlock the volume"): {
_cleanup_free_ char *device = NULL;
if (arg_unlock_type != UNLOCK_PASSWORD)
@@ -440,8 +331,8 @@ static int parse_argv(int argc, char *argv[]) {
assert(!arg_unlock_fido2_device);
if (!streq(optarg, "auto")) {
device = strdup(optarg);
if (!streq(arg, "auto")) {
device = strdup(arg);
if (!device)
return log_oom();
}
@@ -451,7 +342,8 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_UNLOCK_TPM2_DEVICE: {
OPTION_LONG("unlock-tpm2-device", "PATH",
"Use a TPM2 device to unlock the volume"): {
_cleanup_free_ char *device = NULL;
if (arg_unlock_type != UNLOCK_PASSWORD)
@@ -460,8 +352,8 @@ static int parse_argv(int argc, char *argv[]) {
assert(!arg_unlock_tpm2_device);
if (!streq(optarg, "auto")) {
device = strdup(optarg);
if (!streq(arg, "auto")) {
device = strdup(arg);
if (!device)
return log_oom();
}
@@ -471,25 +363,48 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_PKCS11_TOKEN_URI: {
OPTION_GROUP("Simple Enrollment"): {}
OPTION_LONG("password", NULL,
"Enroll a user-supplied password"):
if (arg_enroll_type >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
arg_enroll_type = ENROLL_PASSWORD;
break;
OPTION_LONG("recovery-key", NULL,
"Enroll a recovery key"):
if (arg_enroll_type >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
arg_enroll_type = ENROLL_RECOVERY;
break;
OPTION_GROUP("PKCS#11 Enrollment"): {}
OPTION_LONG("pkcs11-token-uri", "URI|auto|list",
"Enroll a PKCS#11 security token or list them"): {
_cleanup_free_ char *uri = NULL;
if (streq(optarg, "list"))
if (streq(arg, "list"))
return pkcs11_list_tokens();
if (arg_enroll_type >= 0 || arg_pkcs11_token_uri)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
if (streq(optarg, "auto")) {
if (streq(arg, "auto")) {
r = pkcs11_find_token_auto(&uri);
if (r < 0)
return r;
} else {
if (!pkcs11_uri_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
if (!pkcs11_uri_valid(arg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", arg);
uri = strdup(optarg);
uri = strdup(arg);
if (!uri)
return log_oom();
}
@@ -499,24 +414,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_FIDO2_CRED_ALG:
r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
if (r < 0)
return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
break;
OPTION_GROUP("FIDO2 Enrollment"): {}
case ARG_FIDO2_DEVICE: {
OPTION_LONG("fido2-device", "PATH|auto|list",
"Enroll a FIDO2-HMAC security token or list them"): {
_cleanup_free_ char *device = NULL;
if (streq(optarg, "list"))
if (streq(arg, "list"))
return fido2_list_devices();
if (arg_enroll_type >= 0 || arg_fido2_device)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
if (!streq(optarg, "auto")) {
device = strdup(optarg);
if (!streq(arg, "auto")) {
device = strdup(arg);
if (!device)
return log_oom();
}
@@ -526,32 +438,66 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_FIDO2_SALT_FILE:
r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_fido2_salt_file);
OPTION_LONG("fido2-salt-file", "PATH",
"Use salt from a file instead of generating one"):
r = parse_path_argument(arg, /* suppress_root= */ true, &arg_fido2_salt_file);
if (r < 0)
return r;
break;
case ARG_FIDO2_PARAMETERS_IN_HEADER:
r = parse_boolean_argument("--fido2-parameters-in-header=", optarg, &arg_fido2_parameters_in_header);
OPTION_LONG("fido2-parameters-in-header", "BOOL",
"Whether to store FIDO2 parameters in the LUKS2 header"):
r = parse_boolean_argument("--fido2-parameters-in-header=", arg, &arg_fido2_parameters_in_header);
if (r < 0)
return r;
break;
case ARG_TPM2_DEVICE: {
OPTION_LONG("fido2-credential-algorithm", "STRING",
"Specify COSE algorithm for FIDO2 credential"):
r = parse_fido2_algorithm(arg, &arg_fido2_cred_alg);
if (r < 0)
return log_error_errno(r, "Failed to parse COSE algorithm: %s", arg);
break;
OPTION_LONG("fido2-with-client-pin", "BOOL",
"Whether to require entering a PIN to unlock the volume"):
r = parse_boolean_argument("--fido2-with-client-pin=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, r);
break;
OPTION_LONG("fido2-with-user-presence", "BOOL",
"Whether to require user presence to unlock the volume"):
r = parse_boolean_argument("--fido2-with-user-presence=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, r);
break;
OPTION_LONG("fido2-with-user-verification", "BOOL",
"Whether to require user verification to unlock the volume"):
r = parse_boolean_argument("--fido2-with-user-verification=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, r);
break;
OPTION_GROUP("TPM2 Enrollment"): {}
OPTION_LONG("tpm2-device", "PATH|auto|list",
"Enroll a TPM2 device or list them"): {
_cleanup_free_ char *device = NULL;
if (streq(optarg, "list"))
if (streq(arg, "list"))
return tpm2_list_devices(/* legend= */ true, /* quiet= */ false);
if (arg_enroll_type >= 0 || arg_tpm2_device)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
if (!streq(optarg, "auto")) {
device = strdup(optarg);
if (!streq(arg, "auto")) {
device = strdup(arg);
if (!device)
return log_oom();
}
@@ -561,115 +507,96 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_TPM2_DEVICE_KEY:
OPTION_LONG("tpm2-device-key", "PATH",
"Enroll a TPM2 device using its public key"):
if (arg_enroll_type >= 0 || arg_tpm2_device_key)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Multiple operations specified at once, refusing.");
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key);
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_tpm2_device_key);
if (r < 0)
return r;
arg_enroll_type = ENROLL_TPM2;
break;
case ARG_TPM2_SEAL_KEY_HANDLE:
r = safe_atou32_full(optarg, 16, &arg_tpm2_seal_key_handle);
OPTION_LONG("tpm2-seal-key-handle", "HANDLE",
"Specify handle of key to use for sealing"):
r = safe_atou32_full(arg, 16, &arg_tpm2_seal_key_handle);
if (r < 0)
return log_error_errno(r, "Could not parse TPM2 seal key handle index '%s': %m", optarg);
return log_error_errno(r, "Could not parse TPM2 seal key handle index '%s': %m", arg);
break;
case ARG_TPM2_PCRS:
r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
OPTION_LONG("tpm2-pcrs", "PCR1+PCR2+PCR3+…",
"Specify TPM2 PCRs to seal against"):
r = tpm2_parse_pcr_argument_append(arg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
if (r < 0)
return r;
break;
case ARG_TPM2_PUBLIC_KEY:
OPTION_LONG("tpm2-public-key", "PATH",
"Enroll signed TPM2 PCR policy against PEM public key"):
/* an empty argument disables loading a public key */
if (isempty(optarg)) {
if (isempty(arg)) {
arg_tpm2_load_public_key = false;
arg_tpm2_public_key = mfree(arg_tpm2_public_key);
break;
}
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_public_key);
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_tpm2_public_key);
if (r < 0)
return r;
arg_tpm2_load_public_key = true;
break;
case ARG_TPM2_PUBLIC_KEY_PCRS:
OPTION_LONG("tpm2-public-key-pcrs", "PCR1+PCR2+PCR3+…",
"Enroll signed TPM2 PCR policy for specified TPM2 PCRs"):
auto_public_key_pcr_mask = false;
r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
r = tpm2_parse_pcr_argument_to_mask(arg, &arg_tpm2_public_key_pcr_mask);
if (r < 0)
return r;
break;
case ARG_TPM2_SIGNATURE:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_signature);
OPTION_LONG("tpm2-signature", "PATH",
"Validate public key enrollment works with JSON signature file"):
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_tpm2_signature);
if (r < 0)
return r;
break;
case ARG_TPM2_PCRLOCK:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
OPTION_LONG("tpm2-pcrlock", "PATH",
"Specify pcrlock policy to lock against"):
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
if (r < 0)
return r;
auto_pcrlock = false;
break;
case ARG_TPM2_WITH_PIN:
r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
if (r < 0)
return r;
break;
case ARG_WIPE_SLOT:
r = parse_wipe_slot(optarg);
OPTION_LONG("tpm2-with-pin", "BOOL",
"Whether to require entering a PIN to unlock the volume"):
r = parse_boolean_argument("--tpm2-with-pin=", arg, &arg_tpm2_pin);
if (r < 0)
return r;
break;
case ARG_LIST_DEVICES:
return blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_REQUIRE_LUKS,
/* ret_devices= */ NULL,
/* ret_n_devices= */ NULL);
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (argc > optind+1)
char **args = option_parser_get_args(&state);
if (strv_length(args) > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Too many arguments, refusing.");
if (optind < argc) {
r = parse_path_argument(argv[optind], false, &arg_node);
if (r < 0)
return r;
} else {
if (wipe_requested())
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Wiping requested and no block device node specified, refusing.");
if (args[0])
r = parse_path_argument(args[0], false, &arg_node);
else if (!wipe_requested())
r = determine_default_node();
if (r < 0)
return r;
}
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Wiping requested and no block device node specified, refusing.");
if (r < 0)
return r;
if (arg_enroll_type == ENROLL_FIDO2) {
if (arg_unlock_type == UNLOCK_FIDO2 && !(arg_fido2_device && arg_unlock_fido2_device))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"When both enrolling and unlocking with FIDO2 tokens, automatic discovery is unsupported. "
+47 -40
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -11,7 +10,6 @@
#include "sd-messages.h"
#include "alloc-util.h"
#include "argv-util.h"
#include "ask-password-api.h"
#include "build.h"
#include "cryptsetup-fido2.h"
@@ -27,6 +25,7 @@
#include "escape.h"
#include "extract-word.h"
#include "fileio.h"
#include "format-table.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "json-util.h"
@@ -36,6 +35,7 @@
#include "main-func.h"
#include "memory-util.h"
#include "nulstr-util.h"
#include "options.h"
#include "parse-util.h"
#include "path-util.h"
#include "pkcs11-util.h"
@@ -2451,61 +2451,69 @@ static int attach_luks_or_plain_or_bitlk(
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
r = terminal_urlify_man("systemd-cryptsetup", "8", &link);
if (r < 0)
return log_oom();
printf("%1$s attach VOLUME SOURCE-DEVICE [KEY-FILE] [CONFIG]\n"
"%1$s detach VOLUME\n\n"
"%2$sAttach or detach an encrypted block device.%3$s\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
"\nSee the %4$s for details.\n",
r = verbs_get_help_table(&verbs);
if (r < 0)
return r;
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
(void) table_sync_column_widths(0, verbs, options);
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"%sAttach or detach an encrypted block device.%s\n"
"\n%sCommands:%s\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal(),
link);
ansi_underline(),
ansi_normal());
r = table_print_or_warn(verbs);
if (r < 0)
return r;
printf("\n%sOptions:%s\n",
ansi_underline(),
ansi_normal());
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{}
};
int c;
VERB_COMMON_HELP_HIDDEN(help);
static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
assert(ret_args);
if (argv_looks_like_help(argc, argv))
return help();
OptionParser state = { argc, argv };
const char *arg;
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case '?':
return -EINVAL;
default:
assert_not_reached();
}
*ret_args = option_parser_get_args(&state);
return 1;
}
@@ -2588,6 +2596,8 @@ static int discover_key(const char *key_file, const char *volume, TokenType toke
return r;
}
VERB(verb_attach, "attach", "VOLUME SOURCE-DEVICE [KEY-FILE] [CONFIG]", 3, 5, 0,
"Attach an encrypted block device");
static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
_unused_ _cleanup_(remove_and_erasep) const char *destroy_key_file = NULL;
@@ -2828,6 +2838,8 @@ static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata)
return 0;
}
VERB(verb_detach, "detach", "VOLUME", 2, 2, 0,
"Detach an encrypted block device");
static int verb_detach(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
const char *volume = ASSERT_PTR(argv[1]);
@@ -2862,19 +2874,14 @@ static int run(int argc, char *argv[]) {
umask(0022);
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
cryptsetup_enable_logging(NULL);
static const Verb verbs[] = {
{ "attach", 3, 5, 0, verb_attach },
{ "detach", 2, 2, 0, verb_detach },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
return dispatch_verb_with_args(args, NULL);
}
DEFINE_MAIN_FUNCTION(run);
+38 -55
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <unistd.h>
#include "alloc-util.h"
@@ -10,12 +9,14 @@
#include "errno-util.h"
#include "extract-word.h"
#include "fd-util.h"
#include "format-table.h"
#include "fs-util.h"
#include "glyph-util.h"
#include "hashmap.h"
#include "log.h"
#include "main-func.h"
#include "nulstr-util.h"
#include "options.h"
#include "pager.h"
#include "parse-argument.h"
#include "path-util.h"
@@ -459,23 +460,26 @@ static int process_suffix_chop(const char *arg) {
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-delta", "1", &link);
if (r < 0)
return log_oom();
printf("%s [OPTIONS...] [SUFFIX...]\n\n"
"Find overridden configuration files.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --diff[=1|0] Show a diff when overridden files differ\n"
" -t --type=LIST... Only display a selected set of override types\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
link);
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
printf("%s [OPTIONS...] [SUFFIX...]\n\n"
"Find overridden configuration files.\n\n",
program_invocation_short_name);
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
return 0;
}
@@ -509,66 +513,45 @@ static int parse_flags(const char *flag_str, int flags) {
}
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_NO_PAGER = 0x100,
ARG_DIFF,
ARG_VERSION
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "diff", optional_argument, NULL, ARG_DIFF },
{ "type", required_argument, NULL, 't' },
{}
};
int c, r;
assert(argc >= 1);
static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
int r;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_NO_PAGER:
OPTION_COMMON_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case 't': {
int f;
f = parse_flags(optarg, arg_flags);
if (f < 0)
OPTION('t', "type", "TYPE...", "Only display a selected set of override types"):
r = parse_flags(arg, arg_flags);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse flags field.");
arg_flags = f;
arg_flags = r;
break;
}
case ARG_DIFF:
r = parse_boolean_argument("--diff", optarg, NULL);
OPTION_LONG_FLAGS(OPTION_OPTIONAL_ARG, "diff", "yes|no",
"Show a diff when overridden files differ"):
r = parse_boolean_argument("--diff", arg, NULL);
if (r < 0)
return r;
arg_diff = r;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
*ret_args = option_parser_get_args(&state);
return 1;
}
@@ -577,7 +560,8 @@ static int run(int argc, char *argv[]) {
log_setup();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
@@ -591,17 +575,16 @@ static int run(int argc, char *argv[]) {
pager_open(arg_pager_flags);
if (optind < argc) {
for (int i = optind; i < argc; i++) {
path_simplify(argv[i]);
if (!strv_isempty(args)) {
STRV_FOREACH(i, args) {
path_simplify(*i);
k = process_suffix_chop(argv[i]);
k = process_suffix_chop(*i);
if (k < 0)
r = k;
else
n_found += k;
}
} else {
k = process_suffixes(NULL);
if (k < 0)
+46 -67
View File
@@ -1,12 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <stdio.h>
#include "alloc-util.h"
#include "build.h"
#include "format-table.h"
#include "log.h"
#include "main-func.h"
#include "options.h"
#include "path-util.h"
#include "pretty-print.h"
#include "string-util.h"
@@ -26,107 +27,83 @@ static bool arg_instance = false;
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-escape", "1", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] [NAME...]\n\n"
"%3$sEscape strings for usage in systemd unit names.%4$s\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --suffix=SUFFIX Unit suffix to append to escaped strings\n"
" --template=TEMPLATE Insert strings as instance into template\n"
" --instance With --unescape, show just the instance part\n"
" -u --unescape Unescape strings\n"
" -m --mangle Mangle strings\n"
" -p --path When escaping/unescaping assume the string is a path\n"
"\nSee the %2$s for details.\n",
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
printf("%s [OPTIONS...] [NAME...]\n\n"
"%sEscape strings for usage in systemd unit names.%s\n\n",
program_invocation_short_name,
link,
ansi_highlight(),
ansi_normal());
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_SUFFIX,
ARG_TEMPLATE
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "suffix", required_argument, NULL, ARG_SUFFIX },
{ "template", required_argument, NULL, ARG_TEMPLATE },
{ "unescape", no_argument, NULL, 'u' },
{ "mangle", no_argument, NULL, 'm' },
{ "path", no_argument, NULL, 'p' },
{ "instance", no_argument, NULL, 'i' },
{}
};
int c;
static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hump", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_SUFFIX: {
UnitType t = unit_type_from_string(optarg);
OPTION_LONG("suffix", "SUFFIX", "Unit suffix to append to escaped strings"): {
UnitType t = unit_type_from_string(arg);
if (t < 0)
return log_error_errno(t, "Invalid unit suffix type \"%s\".", optarg);
return log_error_errno(t, "Invalid unit suffix type \"%s\".", arg);
arg_suffix = optarg;
arg_suffix = arg;
break;
}
case ARG_TEMPLATE:
if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE))
OPTION_LONG("template", "TEMPLATE", "Insert strings as instance into template"):
if (!unit_name_is_valid(arg, UNIT_NAME_TEMPLATE))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Template name %s is not valid.", optarg);
"Template name %s is not valid.", arg);
arg_template = optarg;
arg_template = arg;
break;
case 'u':
arg_action = ACTION_UNESCAPE;
break;
case 'm':
arg_action = ACTION_MANGLE;
break;
case 'p':
arg_path = true;
break;
case 'i':
OPTION_LONG("instance", NULL, "With --unescape, show just the instance part"):
arg_instance = true;
break;
case '?':
return -EINVAL;
OPTION('u', "unescape", NULL, "Unescape strings"):
arg_action = ACTION_UNESCAPE;
break;
default:
assert_not_reached();
OPTION('m', "mangle", NULL, "Mangle strings"):
arg_action = ACTION_MANGLE;
break;
OPTION('p', "path", NULL,
"When escaping/unescaping assume the string is a path"):
arg_path = true;
break;
}
if (optind >= argc)
if (option_parser_get_n_args(&state) == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Not enough arguments.");
@@ -154,6 +131,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--instance may not be combined with --template.");
*ret_args = option_parser_get_args(&state);
return 1;
}
@@ -162,11 +140,12 @@ static int run(int argc, char *argv[]) {
log_setup();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
STRV_FOREACH(i, argv + optind) {
STRV_FOREACH(i, args) {
_cleanup_free_ char *e = NULL;
switch (arg_action) {
@@ -267,7 +246,7 @@ static int run(int argc, char *argv[]) {
break;
}
if (i != argv + optind)
if (i != args)
fputc(' ', stdout);
fputs(e, stdout);
+78 -125
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <locale.h>
#include <unistd.h>
@@ -12,12 +11,14 @@
#include "discover-image.h"
#include "env-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "import-raw.h"
#include "import-tar.h"
#include "import-util.h"
#include "io-util.h"
#include "log.h"
#include "main-func.h"
#include "options.h"
#include "parse-argument.h"
#include "parse-util.h"
#include "path-util.h"
@@ -138,7 +139,8 @@ static void on_tar_finished(TarImport *import, int error, void *userdata) {
sd_event_exit(event, ABS(error));
}
static int verb_import_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
VERB(verb_tar, "tar", "FILE [NAME]", 2, 3, 0, "Import a TAR image");
static int verb_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(tar_import_unrefp) TarImport *import = NULL;
_cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
@@ -207,7 +209,8 @@ static void on_raw_finished(RawImport *import, int error, void *userdata) {
sd_event_exit(event, ABS(error));
}
static int verb_import_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
VERB(verb_raw, "raw", "FILE [NAME]", 2, 3, 0, "Import a RAW image");
static int verb_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(raw_import_unrefp) RawImport *import = NULL;
_cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
@@ -269,193 +272,152 @@ static int verb_import_raw(int argc, char *argv[], uintptr_t _data, void *userda
}
static int help(void) {
printf("%1$s [OPTIONS...] {COMMAND} ...\n"
"\n%4$sImport disk images.%5$s\n"
"\n%2$sCommands:%3$s\n"
" tar FILE [NAME] Import a TAR image\n"
" raw FILE [NAME] Import a RAW image\n"
"\n%2$sOptions:%3$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --force Force creation of image\n"
" --image-root=PATH Image root directory\n"
" --read-only Create a read-only image\n"
" --direct Import directly to specified file\n"
" --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
" instead of a directory\n"
" --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
" subvolume\n"
" --convert-qcow2=BOOL Controls whether to convert QCOW2 images to\n"
" regular disk images\n"
" --sync=BOOL Controls whether to sync() before completing\n"
" --offset=BYTES Offset to seek to in destination\n"
" --size-max=BYTES Maximum number of bytes to write to destination\n"
" --class=CLASS Select image class (machine, sysext, confext,\n"
" portable)\n"
" --system Operate in per-system mode\n"
" --user Operate in per-user mode\n",
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
r = verbs_get_help_table(&verbs);
if (r < 0)
return r;
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
(void) table_sync_column_widths(0, verbs, options);
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"%sImport disk images.%s\n"
"\n%sCommands:%s\n",
program_invocation_short_name,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal(),
ansi_underline(),
ansi_normal());
r = table_print_or_warn(verbs);
if (r < 0)
return r;
printf("\n%sOptions:%s\n",
ansi_underline(),
ansi_normal());
r = table_print_or_warn(options);
if (r < 0)
return r;
return 0;
}
static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
return help();
}
VERB_COMMON_HELP_HIDDEN(help);
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FORCE,
ARG_IMAGE_ROOT,
ARG_READ_ONLY,
ARG_DIRECT,
ARG_BTRFS_SUBVOL,
ARG_BTRFS_QUOTA,
ARG_CONVERT_QCOW2,
ARG_SYNC,
ARG_OFFSET,
ARG_SIZE_MAX,
ARG_CLASS,
ARG_SYSTEM,
ARG_USER,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "force", no_argument, NULL, ARG_FORCE },
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "direct", no_argument, NULL, ARG_DIRECT },
{ "btrfs-subvol", required_argument, NULL, ARG_BTRFS_SUBVOL },
{ "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
{ "convert-qcow2", required_argument, NULL, ARG_CONVERT_QCOW2 },
{ "sync", required_argument, NULL, ARG_SYNC },
{ "offset", required_argument, NULL, ARG_OFFSET },
{ "size-max", required_argument, NULL, ARG_SIZE_MAX },
{ "class", required_argument, NULL, ARG_CLASS },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{}
};
int r, c;
static int parse_argv(int argc, char *argv[], char ***ret_args) {
int r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_FORCE:
OPTION_LONG("force", NULL, "Force creation of image"):
arg_import_flags |= IMPORT_FORCE;
break;
case ARG_IMAGE_ROOT:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image_root);
OPTION_LONG("image-root", "PATH", "Image root directory"):
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_image_root);
if (r < 0)
return r;
break;
case ARG_READ_ONLY:
OPTION_LONG("read-only", NULL, "Create a read-only image"):
arg_import_flags |= IMPORT_READ_ONLY;
break;
case ARG_DIRECT:
OPTION_LONG("direct", NULL, "Import directly to specified file"):
arg_import_flags |= IMPORT_DIRECT;
break;
case ARG_BTRFS_SUBVOL:
r = parse_boolean_argument("--btrfs-subvol=", optarg, NULL);
OPTION_LONG("btrfs-subvol", "BOOL",
"Controls whether to create a btrfs subvolume instead of a directory"):
r = parse_boolean_argument("--btrfs-subvol=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
break;
case ARG_BTRFS_QUOTA:
r = parse_boolean_argument("--btrfs-quota=", optarg, NULL);
OPTION_LONG("btrfs-quota", "BOOL",
"Controls whether to set up quota for btrfs subvolume"):
r = parse_boolean_argument("--btrfs-quota=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
break;
case ARG_CONVERT_QCOW2:
r = parse_boolean_argument("--convert-qcow2=", optarg, NULL);
OPTION_LONG("convert-qcow2", "BOOL",
"Controls whether to convert QCOW2 images to regular disk images"):
r = parse_boolean_argument("--convert-qcow2=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_CONVERT_QCOW2, r);
break;
case ARG_SYNC:
r = parse_boolean_argument("--sync=", optarg, NULL);
OPTION_LONG("sync", "BOOL", "Controls whether to sync() before completing"):
r = parse_boolean_argument("--sync=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
break;
case ARG_OFFSET: {
OPTION_LONG("offset", "BYTES", "Offset to seek to in destination"): {
uint64_t u;
r = safe_atou64(optarg, &u);
r = safe_atou64(arg, &u);
if (r < 0)
return log_error_errno(r, "Failed to parse --offset= argument: %s", optarg);
return log_error_errno(r, "Failed to parse --offset= argument: %s", arg);
if (!FILE_SIZE_VALID(u))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", optarg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", arg);
arg_offset = u;
break;
}
case ARG_SIZE_MAX: {
OPTION_LONG("size-max", "BYTES", "Maximum number of bytes to write to destination"): {
uint64_t u;
r = parse_size(optarg, 1024, &u);
r = parse_size(arg, 1024, &u);
if (r < 0)
return log_error_errno(r, "Failed to parse --size-max= argument: %s", optarg);
return log_error_errno(r, "Failed to parse --size-max= argument: %s", arg);
if (!FILE_SIZE_VALID(u))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", optarg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", arg);
arg_size_max = u;
break;
}
case ARG_CLASS:
arg_class = image_class_from_string(optarg);
OPTION_LONG("class", "CLASS",
"Select image class (machine, sysext, confext, portable)"):
arg_class = image_class_from_string(arg);
if (arg_class < 0)
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", optarg);
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", arg);
break;
case ARG_SYSTEM:
OPTION_LONG("system", NULL, "Operate in per-system mode"):
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break;
case ARG_USER:
OPTION_LONG("user", NULL, "Operate in per-user mode"):
arg_runtime_scope = RUNTIME_SCOPE_USER;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
/* Make sure offset+size is still in the valid range if both set */
@@ -476,20 +438,10 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_runtime_scope == RUNTIME_SCOPE_USER)
arg_import_flags |= IMPORT_FOREIGN_UID;
*ret_args = option_parser_get_args(&state);
return 1;
}
static int import_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
{ "tar", 2, 3, 0, verb_import_tar },
{ "raw", 2, 3, 0, verb_import_raw },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
static void parse_env(void) {
int r;
@@ -523,13 +475,14 @@ static int run(int argc, char *argv[]) {
parse_env();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
(void) ignore_signals(SIGPIPE);
return import_main(argc, argv);
return dispatch_verb_with_args(args, NULL);
}
DEFINE_MAIN_FUNCTION(run);
+312 -385
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <locale.h>
#include <unistd.h>
@@ -20,6 +19,7 @@
#include "log.h"
#include "main-func.h"
#include "oci-util.h"
#include "options.h"
#include "os-util.h"
#include "pager.h"
#include "parse-argument.h"
@@ -261,6 +261,211 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
return -r;
}
VERB(verb_pull_tar, "pull-tar", "URL [NAME]", 2, 3, 0, "Download a TAR container image");
static int verb_pull_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
r = settle_image_class();
if (r < 0)
return r;
remote = argv[1];
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
r = tar_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable image name.",
local);
}
if (arg_image_class == IMAGE_MACHINE && (arg_import_flags & ~IMPORT_FORCE) == 0) {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
FLAGS_SET(arg_import_flags, IMPORT_FORCE));
} else {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTarEx");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssst",
remote,
local,
image_class_to_string(arg_image_class),
import_verify_to_string(arg_verify),
(uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
}
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
VERB(verb_pull_raw, "pull-raw", "URL [NAME]", 2, 3, 0, "Download a RAW container or VM image");
static int verb_pull_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
r = settle_image_class();
if (r < 0)
return r;
remote = argv[1];
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
r = raw_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable image name.",
local);
}
if (arg_image_class == IMAGE_MACHINE && (arg_import_flags & ~IMPORT_FORCE) == 0) {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
FLAGS_SET(arg_import_flags, IMPORT_FORCE));
} else {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRawEx");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssst",
remote,
local,
image_class_to_string(arg_image_class),
import_verify_to_string(arg_verify),
(uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
}
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
VERB(verb_pull_oci, "pull-oci", "REF [NAME]", 2, 3, 0, "Download an OCI container image");
static int verb_pull_oci(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
r = settle_image_class();
if (r < 0)
return r;
remote = argv[1];
_cleanup_free_ char *image = NULL;
r = oci_ref_parse(remote, /* ret_registry= */ NULL, &image, /* ret_tag= */ NULL);
if (r == -EINVAL)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Ref '%s' is not valid.", remote);
if (r < 0)
return log_error_errno(r, "Failed to determine if ref '%s' is valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = path_extract_filename(image, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of reference: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable image name.",
local);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullOci");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"ssst",
remote,
local,
image_class_to_string(arg_image_class),
(uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY));
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
VERB(verb_import_tar, "import-tar", "FILE [NAME]", 2, 3, 0, "Import a local TAR container image");
static int verb_import_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL, *fn = NULL;
@@ -340,6 +545,7 @@ static int verb_import_tar(int argc, char *argv[], uintptr_t _data, void *userda
return transfer_image_common(bus, m);
}
VERB(verb_import_raw, "import-raw", "FILE [NAME]", 2, 3, 0, "Import a local RAW container or VM image");
static int verb_import_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL, *fn = NULL;
@@ -419,6 +625,7 @@ static int verb_import_raw(int argc, char *argv[], uintptr_t _data, void *userda
return transfer_image_common(bus, m);
}
VERB(verb_import_fs, "import-fs", "DIRECTORY [NAME]", 2, 3, 0, "Import a local directory container image");
static int verb_import_fs(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
const char *local = NULL, *path = NULL;
@@ -506,6 +713,7 @@ static void determine_compression_from_filename(const char *p) {
arg_format = "zstd";
}
VERB(verb_export_tar, "export-tar", "NAME [FILE]", 2, 3, 0, "Export a TAR container image locally");
static int verb_export_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -EBADF;
@@ -565,6 +773,7 @@ static int verb_export_tar(int argc, char *argv[], uintptr_t _data, void *userda
return transfer_image_common(bus, m);
}
VERB(verb_export_raw, "export-raw", "NAME [FILE]", 2, 3, 0, "Export a RAW container or VM image locally");
static int verb_export_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -EBADF;
@@ -624,207 +833,7 @@ static int verb_export_raw(int argc, char *argv[], uintptr_t _data, void *userda
return transfer_image_common(bus, m);
}
static int verb_pull_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
r = settle_image_class();
if (r < 0)
return r;
remote = argv[1];
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
r = tar_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable image name.",
local);
}
if (arg_image_class == IMAGE_MACHINE && (arg_import_flags & ~IMPORT_FORCE) == 0) {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
FLAGS_SET(arg_import_flags, IMPORT_FORCE));
} else {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTarEx");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssst",
remote,
local,
image_class_to_string(arg_image_class),
import_verify_to_string(arg_verify),
(uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
}
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int verb_pull_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
r = settle_image_class();
if (r < 0)
return r;
remote = argv[1];
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
r = raw_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable image name.",
local);
}
if (arg_image_class == IMAGE_MACHINE && (arg_import_flags & ~IMPORT_FORCE) == 0) {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
FLAGS_SET(arg_import_flags, IMPORT_FORCE));
} else {
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRawEx");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssst",
remote,
local,
image_class_to_string(arg_image_class),
import_verify_to_string(arg_verify),
(uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
}
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int verb_pull_oci(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
r = settle_image_class();
if (r < 0)
return r;
remote = argv[1];
_cleanup_free_ char *image = NULL;
r = oci_ref_parse(remote, /* ret_registry= */ NULL, &image, /* ret_tag= */ NULL);
if (r == -EINVAL)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Ref '%s' is not valid.", remote);
if (r < 0)
return log_error_errno(r, "Failed to determine if ref '%s' is valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = path_extract_filename(image, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of reference: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable image name.",
local);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullOci");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"ssst",
remote,
local,
image_class_to_string(arg_image_class),
(uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY));
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
VERB(verb_list_transfers, "list-transfers", NULL, VERB_ANY, 1, VERB_DEFAULT, "Show list of transfers in progress");
static int verb_list_transfers(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -929,6 +938,7 @@ static int verb_list_transfers(int argc, char *argv[], uintptr_t _data, void *us
return 0;
}
VERB(verb_cancel_transfer, "cancel-transfer", "[ID...]", 2, VERB_ANY, 0, "Cancel a transfer");
static int verb_cancel_transfer(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = ASSERT_PTR(userdata);
@@ -951,6 +961,7 @@ static int verb_cancel_transfer(int argc, char *argv[], uintptr_t _data, void *u
return 0;
}
VERB_NOARG(verb_list_images, "list-images", "Show list of installed images");
static int verb_list_images(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -1050,6 +1061,7 @@ static int verb_list_images(int argc, char *argv[], uintptr_t _data, void *userd
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
pager_open(arg_pager_flags);
@@ -1058,265 +1070,179 @@ static int help(void) {
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
"%5$sDownload, import or export disk images%6$s\n"
"\n%3$sCommands:%4$s\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
" pull-oci REF [NAME] Download an OCI container image\n"
" import-tar FILE [NAME] Import a local TAR container image\n"
" import-raw FILE [NAME] Import a local RAW container or VM image\n"
" import-fs DIRECTORY [NAME] Import a local directory container image\n"
" export-tar NAME [FILE] Export a TAR container image locally\n"
" export-raw NAME [FILE] Export a RAW container or VM image locally\n"
" list-transfers Show list of transfers in progress\n"
" cancel-transfer [ID...] Cancel a transfer\n"
" list-images Show list of installed images\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" --no-ask-password Do not ask for system passwords\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" --system Connect to system machine manager\n"
" --user Connect to user machine manager\n"
" --read-only Create read-only image\n"
" -q --quiet Suppress output\n"
" --json=pretty|short|off Generate JSON output\n"
" -j Equvilant to --json=pretty on TTY, --json=short\n"
" otherwise\n"
" --verify=MODE Verification mode for downloaded images (no,\n"
" checksum, signature)\n"
" --format=xz|gzip|bzip2|zstd\n"
" Desired output format for export\n"
" --force Install image even if already exists\n"
" --class=TYPE Install as the specified TYPE\n"
" -m Install as --class=machine, machine image\n"
" -P Install as --class=portable,\n"
" portable service image\n"
" -S Install as --class=sysext, system extension image\n"
" -C Install as --class=confext,\n"
" configuration extension image\n"
" --keep-download=BOOL Control whether to keep pristine copy of download\n"
" -N Same as --keep-download=no\n"
"\nSee the %2$s for details.\n",
r = verbs_get_help_table(&verbs);
if (r < 0)
return r;
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
(void) table_sync_column_widths(0, verbs, options);
printf("%s [OPTIONS...] COMMAND ...\n\n"
"%sDownload, import or export disk images%s\n"
"\n%sCommands:%s\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal(),
ansi_underline(),
ansi_normal());
r = table_print_or_warn(verbs);
if (r < 0)
return r;
printf("\n%sOptions:%s\n",
ansi_underline(),
ansi_normal());
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
return 0;
}
static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
return help();
}
VERB_COMMON_HELP(help);
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_NO_ASK_PASSWORD,
ARG_READ_ONLY,
ARG_JSON,
ARG_VERIFY,
ARG_FORCE,
ARG_FORMAT,
ARG_CLASS,
ARG_KEEP_DOWNLOAD,
ARG_SYSTEM,
ARG_USER,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "json", required_argument, NULL, ARG_JSON },
{ "quiet", no_argument, NULL, 'q' },
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "force", no_argument, NULL, ARG_FORCE },
{ "format", required_argument, NULL, ARG_FORMAT },
{ "class", required_argument, NULL, ARG_CLASS },
{ "keep-download", required_argument, NULL, ARG_KEEP_DOWNLOAD },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{}
};
int c, r;
static int parse_argv(int argc, char *argv[], char ***ret_args) {
int r;
assert(argc >= 0);
assert(argv);
for (;;) {
c = getopt_long(argc, argv, "hH:M:jqmPSCN", options, NULL);
if (c < 0)
break;
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_NO_PAGER:
OPTION_COMMON_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
OPTION_COMMON_NO_LEGEND:
arg_legend = false;
break;
case ARG_NO_ASK_PASSWORD:
OPTION_COMMON_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
case 'H':
OPTION_COMMON_HOST:
arg_transport = BUS_TRANSPORT_REMOTE;
arg_host = optarg;
arg_host = arg;
break;
case 'M':
r = parse_machine_argument(optarg, &arg_host, &arg_transport);
OPTION_COMMON_MACHINE:
r = parse_machine_argument(arg, &arg_host, &arg_transport);
if (r < 0)
return r;
break;
case ARG_READ_ONLY:
OPTION_LONG("system", NULL, "Connect to system machine manager"):
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break;
OPTION_LONG("user", NULL, "Connect to user machine manager"):
arg_runtime_scope = RUNTIME_SCOPE_USER;
break;
OPTION_LONG("read-only", NULL, "Create read-only image"):
arg_import_flags |= IMPORT_READ_ONLY;
arg_import_flags_mask |= IMPORT_READ_ONLY;
break;
case 'q':
OPTION('q', "quiet", NULL, "Suppress output"):
arg_quiet = true;
break;
case ARG_VERIFY:
if (streq(optarg, "help"))
return DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX);
r = import_verify_from_string(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --verify= setting: %s", optarg);
arg_verify = r;
break;
case ARG_FORCE:
arg_import_flags |= IMPORT_FORCE;
arg_import_flags_mask |= IMPORT_FORCE;
break;
case ARG_FORMAT:
if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2", "zstd"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unknown format: %s", optarg);
arg_format = optarg;
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
OPTION_COMMON_JSON:
r = parse_json_argument(arg, &arg_json_format_flags);
if (r <= 0)
return r;
arg_legend = false;
break;
case 'j':
OPTION_COMMON_LOWERCASE_J:
arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
arg_legend = false;
break;
case ARG_CLASS:
arg_image_class = image_class_from_string(optarg);
if (arg_image_class < 0)
return log_error_errno(arg_image_class, "Failed to parse --class= parameter: %s", optarg);
OPTION_LONG("verify", "MODE",
"Verification mode for downloaded images (no, checksum, signature)"):
if (streq(arg, "help"))
return DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX);
r = import_verify_from_string(arg);
if (r < 0)
return log_error_errno(r, "Failed to parse --verify= setting: %s", arg);
arg_verify = r;
break;
case 'm':
OPTION_LONG("format", "FORMAT",
"Desired output format for export (zstd, xz, gzip, bzip2)"):
if (!STR_IN_SET(arg, "uncompressed", "xz", "gzip", "bzip2", "zstd"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unknown format: %s", arg);
arg_format = arg;
break;
OPTION_LONG("force", NULL, "Install image even if already exists"):
arg_import_flags |= IMPORT_FORCE;
arg_import_flags_mask |= IMPORT_FORCE;
break;
OPTION_LONG("class", "TYPE", "Install as the specified TYPE"):
arg_image_class = image_class_from_string(arg);
if (arg_image_class < 0)
return log_error_errno(arg_image_class, "Failed to parse --class= parameter: %s", arg);
break;
OPTION_SHORT('m', NULL, "Install as --class=machine, machine image"):
arg_image_class = IMAGE_MACHINE;
break;
case 'P':
OPTION_SHORT('P', NULL, "Install as --class=portable, portable service image"):
arg_image_class = IMAGE_PORTABLE;
break;
case 'S':
OPTION_SHORT('S', NULL, "Install as --class=sysext, system extension image"):
arg_image_class = IMAGE_SYSEXT;
break;
case 'C':
OPTION_SHORT('C', NULL, "Install as --class=confext, configuration extension image"):
arg_image_class = IMAGE_CONFEXT;
break;
case ARG_KEEP_DOWNLOAD:
r = parse_boolean(optarg);
OPTION_LONG("keep-download", "BOOL",
"Control whether to keep pristine copy of download"):
r = parse_boolean(arg);
if (r < 0)
return log_error_errno(r, "Failed to parse --keep-download= value: %s", optarg);
return log_error_errno(r, "Failed to parse --keep-download= value: %s", arg);
SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, r);
arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD;
break;
case 'N':
arg_import_flags_mask &= ~IMPORT_PULL_KEEP_DOWNLOAD;
OPTION_SHORT('N', NULL, "Same as --keep-download=no"):
arg_import_flags &= ~IMPORT_PULL_KEEP_DOWNLOAD;
arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD;
break;
case ARG_USER:
arg_runtime_scope = RUNTIME_SCOPE_USER;
break;
case ARG_SYSTEM:
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
}
*ret_args = option_parser_get_args(&state);
return 1;
}
static int importctl_main(int argc, char *argv[], sd_bus *bus) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
{ "import-tar", 2, 3, 0, verb_import_tar },
{ "import-raw", 2, 3, 0, verb_import_raw },
{ "import-fs", 2, 3, 0, verb_import_fs },
{ "export-tar", 2, 3, 0, verb_export_tar },
{ "export-raw", 2, 3, 0, verb_export_raw },
{ "pull-tar", 2, 3, 0, verb_pull_tar },
{ "pull-oci", 2, 3, 0, verb_pull_oci },
{ "pull-raw", 2, 3, 0, verb_pull_raw },
{ "list-transfers", VERB_ANY, 1, VERB_DEFAULT, verb_list_transfers },
{ "cancel-transfer", 2, VERB_ANY, 0, verb_cancel_transfer },
{ "list-images", VERB_ANY, 1, 0, verb_list_images },
{}
};
return dispatch_verb(argc, argv, verbs, bus);
}
static int run(int argc, char *argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
@@ -1324,7 +1250,8 @@ static int run(int argc, char *argv[]) {
setlocale(LC_ALL, "");
log_setup();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
@@ -1334,7 +1261,7 @@ static int run(int argc, char *argv[]) {
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
return importctl_main(argc, argv, bus);
return dispatch_verb_with_args(args, bus);
}
DEFINE_MAIN_FUNCTION(run);
+98 -164
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <locale.h>
#include <stdio.h>
@@ -11,6 +10,7 @@
#include "build.h"
#include "discover-image.h"
#include "env-util.h"
#include "format-table.h"
#include "hexdecoct.h"
#include "import-common.h"
#include "import-util.h"
@@ -19,6 +19,7 @@
#include "log.h"
#include "main-func.h"
#include "oci-util.h"
#include "options.h"
#include "parse-argument.h"
#include "parse-util.h"
#include "path-util.h"
@@ -117,7 +118,8 @@ static void on_tar_finished(TarPull *pull, int error, void *userdata) {
sd_event_exit(event, ABS(error));
}
static int verb_pull_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
VERB(verb_tar, "tar", "URL [NAME]", 2, 3, 0, "Download a TAR image");
static int verb_tar(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
@@ -187,7 +189,8 @@ static void on_raw_finished(RawPull *pull, int error, void *userdata) {
sd_event_exit(event, ABS(error));
}
static int verb_pull_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
VERB(verb_raw, "raw", "URL [NAME]", 2, 3, 0, "Download a RAW image");
static int verb_raw(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
@@ -256,7 +259,8 @@ static void on_oci_finished(OciPull *pull, int error, void *userdata) {
sd_event_exit(event, ABS(error));
}
static int verb_pull_oci(int argc, char *argv[], uintptr_t _data, void *userdata) {
VERB(verb_oci, "oci", "REF [NAME]", 2, 3, 0, "Download an OCI image");
static int verb_oci(int argc, char *argv[], uintptr_t _data, void *userdata) {
int r;
const char *ref = argv[1];
@@ -312,134 +316,79 @@ static int verb_pull_oci(int argc, char *argv[], uintptr_t _data, void *userdata
}
static int help(void) {
printf("%1$s [OPTIONS...] {COMMAND} ...\n"
"\n%4$sDownload disk images.%5$s\n"
"\n%2$sCommands:%3$s\n"
" tar URL [NAME] Download a TAR image\n"
" raw URL [NAME] Download a RAW image\n"
" oci REF [NAME] Download an OCI image\n"
"\n%2$sOptions:%3$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --force Force creation of image\n"
" --verify=MODE Verify downloaded image, one of: 'no',\n"
" 'checksum', 'signature' or literal SHA256 hash\n"
" --settings=BOOL Download settings file with image\n"
" --roothash=BOOL Download root hash file with image\n"
" --roothash-signature=BOOL\n"
" Download root hash signature file with image\n"
" --verity=BOOL Download verity file with image\n"
" --image-root=PATH Image root directory\n"
" --read-only Create a read-only image\n"
" --direct Download directly to specified file\n"
" --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
" instead of a directory\n"
" --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
" subvolume\n"
" --convert-qcow2=BOOL Controls whether to convert QCOW2 images to\n"
" regular disk images\n"
" --sync=BOOL Controls whether to sync() before completing\n"
" --offset=BYTES Offset to seek to in destination\n"
" --size-max=BYTES Maximum number of bytes to write to destination\n"
" --class=CLASS Select image class (machine, sysext, confext,\n"
" portable)\n"
" --keep-download=BOOL Keep a copy pristine copy of the downloaded file\n"
" around\n"
" --system Operate in per-system mode\n"
" --user Operate in per-user mode\n",
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
r = verbs_get_help_table(&verbs);
if (r < 0)
return r;
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
(void) table_sync_column_widths(0, verbs, options);
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"%sDownload disk images.%s\n"
"\n%sCommands:%s\n",
program_invocation_short_name,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal(),
ansi_underline(),
ansi_normal());
r = table_print_or_warn(verbs);
if (r < 0)
return r;
printf("\n%sOptions:%s\n",
ansi_underline(),
ansi_normal());
r = table_print_or_warn(options);
if (r < 0)
return r;
return 0;
}
static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
return help();
}
VERB_COMMON_HELP_HIDDEN(help);
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FORCE,
ARG_IMAGE_ROOT,
ARG_VERIFY,
ARG_SETTINGS,
ARG_ROOTHASH,
ARG_ROOTHASH_SIGNATURE,
ARG_VERITY,
ARG_READ_ONLY,
ARG_DIRECT,
ARG_BTRFS_SUBVOL,
ARG_BTRFS_QUOTA,
ARG_CONVERT_QCOW2,
ARG_SYNC,
ARG_OFFSET,
ARG_SIZE_MAX,
ARG_CLASS,
ARG_KEEP_DOWNLOAD,
ARG_SYSTEM,
ARG_USER,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "force", no_argument, NULL, ARG_FORCE },
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "settings", required_argument, NULL, ARG_SETTINGS },
{ "roothash", required_argument, NULL, ARG_ROOTHASH },
{ "roothash-signature", required_argument, NULL, ARG_ROOTHASH_SIGNATURE },
{ "verity", required_argument, NULL, ARG_VERITY },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "direct", no_argument, NULL, ARG_DIRECT },
{ "btrfs-subvol", required_argument, NULL, ARG_BTRFS_SUBVOL },
{ "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
{ "convert-qcow2", required_argument, NULL, ARG_CONVERT_QCOW2 },
{ "sync", required_argument, NULL, ARG_SYNC },
{ "offset", required_argument, NULL, ARG_OFFSET },
{ "size-max", required_argument, NULL, ARG_SIZE_MAX },
{ "class", required_argument, NULL, ARG_CLASS },
{ "keep-download", required_argument, NULL, ARG_KEEP_DOWNLOAD },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{}
};
int c, r;
static int parse_argv(int argc, char *argv[], char ***ret_args) {
int r;
bool auto_settings = true, auto_keep_download = true;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_FORCE:
OPTION_LONG("force", NULL, "Force creation of image"):
arg_import_flags |= IMPORT_FORCE;
break;
case ARG_IMAGE_ROOT:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image_root);
OPTION_LONG("image-root", "PATH", "Image root directory"):
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_image_root);
if (r < 0)
return r;
break;
case ARG_VERIFY: {
OPTION_LONG("verify", "MODE",
"Verify downloaded image, one of: 'no', 'checksum', 'signature' or literal SHA256 hash"): {
ImportVerify v;
v = import_verify_from_string(optarg);
v = import_verify_from_string(arg);
if (v < 0) {
_cleanup_free_ void *h = NULL;
size_t n;
@@ -447,10 +396,10 @@ static int parse_argv(int argc, char *argv[]) {
/* If this is not a valid verification mode, maybe it's a literally specified
* SHA256 hash? We can handle that too... */
r = unhexmem(optarg, &h, &n);
r = unhexmem(arg, &h, &n);
if (r < 0 || n == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid verification setting: %s", optarg);
"Invalid verification setting: %s", arg);
if (n != 32)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"64 hex character SHA256 hash required when specifying explicit checksum, %zu specified", n * 2);
@@ -466,8 +415,8 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_SETTINGS:
r = parse_boolean_argument("--settings=", optarg, NULL);
OPTION_LONG("settings", "BOOL", "Download settings file with image"):
r = parse_boolean_argument("--settings=", arg, NULL);
if (r < 0)
return r;
@@ -475,8 +424,8 @@ static int parse_argv(int argc, char *argv[]) {
auto_settings = false;
break;
case ARG_ROOTHASH:
r = parse_boolean_argument("--roothash=", optarg, NULL);
OPTION_LONG("roothash", "BOOL", "Download root hash file with image"):
r = parse_boolean_argument("--roothash=", arg, NULL);
if (r < 0)
return r;
@@ -487,118 +436,113 @@ static int parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_import_flags, IMPORT_PULL_ROOTHASH_SIGNATURE, false);
break;
case ARG_ROOTHASH_SIGNATURE:
r = parse_boolean_argument("--roothash-signature=", optarg, NULL);
OPTION_LONG("roothash-signature", "BOOL",
"Download root hash signature file with image"):
r = parse_boolean_argument("--roothash-signature=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_PULL_ROOTHASH_SIGNATURE, r);
break;
case ARG_VERITY:
r = parse_boolean_argument("--verity=", optarg, NULL);
OPTION_LONG("verity", "BOOL", "Download verity file with image"):
r = parse_boolean_argument("--verity=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_PULL_VERITY, r);
break;
case ARG_READ_ONLY:
OPTION_LONG("read-only", NULL, "Create a read-only image"):
arg_import_flags |= IMPORT_READ_ONLY;
break;
case ARG_DIRECT:
OPTION_LONG("direct", NULL, "Download directly to specified file"):
arg_import_flags |= IMPORT_DIRECT;
arg_import_flags &= ~(IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY);
break;
case ARG_BTRFS_SUBVOL:
r = parse_boolean_argument("--btrfs-subvol=", optarg, NULL);
OPTION_LONG("btrfs-subvol", "BOOL",
"Controls whether to create a btrfs subvolume instead of a directory"):
r = parse_boolean_argument("--btrfs-subvol=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
break;
case ARG_BTRFS_QUOTA:
r = parse_boolean_argument("--btrfs-quota=", optarg, NULL);
OPTION_LONG("btrfs-quota", "BOOL",
"Controls whether to set up quota for btrfs subvolume"):
r = parse_boolean_argument("--btrfs-quota=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
break;
case ARG_CONVERT_QCOW2:
r = parse_boolean_argument("--convert-qcow2=", optarg, NULL);
OPTION_LONG("convert-qcow2", "BOOL",
"Controls whether to convert QCOW2 images to regular disk images"):
r = parse_boolean_argument("--convert-qcow2=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_CONVERT_QCOW2, r);
break;
case ARG_SYNC:
r = parse_boolean_argument("--sync=", optarg, NULL);
OPTION_LONG("sync", "BOOL", "Controls whether to sync() before completing"):
r = parse_boolean_argument("--sync=", arg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
break;
case ARG_OFFSET: {
OPTION_LONG("offset", "BYTES", "Offset to seek to in destination"): {
uint64_t u;
r = safe_atou64(optarg, &u);
r = safe_atou64(arg, &u);
if (r < 0)
return log_error_errno(r, "Failed to parse --offset= argument: %s", optarg);
return log_error_errno(r, "Failed to parse --offset= argument: %s", arg);
if (!FILE_SIZE_VALID(u))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", optarg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", arg);
arg_offset = u;
break;
}
case ARG_SIZE_MAX: {
OPTION_LONG("size-max", "BYTES", "Maximum number of bytes to write to destination"): {
uint64_t u;
r = parse_size(optarg, 1024, &u);
r = parse_size(arg, 1024, &u);
if (r < 0)
return log_error_errno(r, "Failed to parse --size-max= argument: %s", optarg);
return log_error_errno(r, "Failed to parse --size-max= argument: %s", arg);
if (!FILE_SIZE_VALID(u))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", optarg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", arg);
arg_size_max = u;
break;
}
case ARG_CLASS:
arg_class = image_class_from_string(optarg);
OPTION_LONG("class", "CLASS",
"Select image class (machine, sysext, confext, portable)"):
arg_class = image_class_from_string(arg);
if (arg_class < 0)
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", optarg);
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", arg);
break;
case ARG_KEEP_DOWNLOAD:
r = parse_boolean(optarg);
OPTION_LONG("keep-download", "BOOL",
"Keep a pristine copy of the downloaded file around"):
r = parse_boolean(arg);
if (r < 0)
return log_error_errno(r, "Failed to parse --keep-download= argument: %s", optarg);
return log_error_errno(r, "Failed to parse --keep-download= argument: %s", arg);
SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, r);
auto_keep_download = false;
break;
case ARG_SYSTEM:
OPTION_LONG("system", NULL, "Operate in per-system mode"):
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break;
case ARG_USER:
OPTION_LONG("user", NULL, "Operate in per-user mode"):
arg_runtime_scope = RUNTIME_SCOPE_USER;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
/* Make sure offset+size is still in the valid range if both set */
@@ -631,6 +575,7 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_runtime_scope == RUNTIME_SCOPE_USER)
arg_import_flags |= IMPORT_FOREIGN_UID;
*ret_args = option_parser_get_args(&state);
return 1;
}
@@ -659,18 +604,6 @@ static void parse_env(void) {
log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
}
static int pull_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
{ "tar", 2, 3, 0, verb_pull_tar },
{ "raw", 2, 3, 0, verb_pull_raw },
{ "oci", 2, 3, 0, verb_pull_oci },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
static int run(int argc, char *argv[]) {
int r;
@@ -679,13 +612,14 @@ static int run(int argc, char *argv[]) {
parse_env();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
(void) ignore_signals(SIGPIPE);
return pull_main(argc, argv);
return dispatch_verb_with_args(args, NULL);
}
DEFINE_MAIN_FUNCTION(run);
+29 -45
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <linux/vt.h>
#include <stdio.h>
#include <sys/ioctl.h>
@@ -11,10 +10,12 @@
#include "build.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-table.h"
#include "io-util.h"
#include "log.h"
#include "logs-show.h"
#include "main-func.h"
#include "options.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "qrcode-util.h"
@@ -29,29 +30,32 @@ STATIC_DESTRUCTOR_REGISTER(arg_tty, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-bsod", "8", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...]\n\n"
"%5$sFilter the journal to fetch the first message from the current boot with an%6$s\n"
"%5$semergency log level and display it as a string and a QR code.%6$s\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -c --continuous Make systemd-bsod wait continuously\n"
" for changes in the journal\n"
" --tty=TTY Specify path to TTY to use\n"
"\nSee the %2$s for details.\n",
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
printf("%s [OPTIONS...]\n\n"
"%sFilter the journal to fetch the first message from the current boot with an\n"
"emergency log level and display it as a string and a QR code.%s\n"
"\n%sOptions:%s\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal(),
ansi_underline(),
ansi_normal());
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
return 0;
}
@@ -239,55 +243,35 @@ cleanup:
return r;
}
static int parse_argv(int argc, char * argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_TTY,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "continuous", no_argument, NULL, 'c' },
{ "tty", required_argument, NULL, ARG_TTY },
{}
};
int c, r;
static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hc", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
int r;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case 'c':
OPTION('c', "continuous", NULL, "Continuously wait for changes in the journal"):
arg_continuous = true;
break;
case ARG_TTY:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tty);
OPTION_LONG("tty", "TTY", "Specify path to TTY to use"):
r = parse_path_argument(arg, /* suppress_root= */ false, &arg_tty);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (optind < argc)
if (option_parser_get_n_args(&state) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s takes no argument.",
program_invocation_short_name);
+42 -62
View File
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -12,12 +11,15 @@
#include "build.h"
#include "env-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "format-util.h"
#include "log.h"
#include "main-func.h"
#include "options.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
static const char *arg_identifier = NULL;
@@ -28,104 +30,81 @@ static bool arg_level_prefix = true;
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-cat", "1", &link);
if (r < 0)
return log_oom();
printf("%s [OPTIONS...] COMMAND ...\n"
"\n%sExecute process with stdout/stderr connected to the journal.%s\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -t --identifier=STRING Set syslog identifier\n"
" -p --priority=PRIORITY Set priority value (0..7)\n"
" --stderr-priority=PRIORITY Set priority value (0..7) used for stderr\n"
" --level-prefix=BOOL Control whether level prefix shall be parsed\n"
" --namespace=NAMESPACE Connect to specified journal namespace\n"
"\nSee the %s for details.\n",
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
printf("%s [OPTIONS...] COMMAND ...\n\n"
"%sExecute process with stdout/stderr connected to the journal.%s\n\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal(),
link);
ansi_normal());
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_STDERR_PRIORITY,
ARG_LEVEL_PREFIX,
ARG_NAMESPACE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "identifier", required_argument, NULL, 't' },
{ "priority", required_argument, NULL, 'p' },
{ "stderr-priority", required_argument, NULL, ARG_STDERR_PRIORITY },
{ "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX },
{ "namespace", required_argument, NULL, ARG_NAMESPACE },
{}
};
int c, r;
static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
/* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
* that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
optind = 0;
while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0)
OptionParser state = { argc, argv, /* stop_at_first_nonoption= */ true };
const char *arg;
int r;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
help();
return 0;
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case 't':
arg_identifier = empty_to_null(optarg);
OPTION('t', "identifier", "STRING", "Set syslog identifier"):
arg_identifier = empty_to_null(arg);
break;
case 'p':
arg_priority = log_level_from_string(optarg);
OPTION('p', "priority", "PRIORITY", "Set priority value (0..7)"):
arg_priority = log_level_from_string(arg);
if (arg_priority < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse priority value.");
break;
case ARG_STDERR_PRIORITY:
arg_stderr_priority = log_level_from_string(optarg);
OPTION_LONG("stderr-priority", "PRIORITY",
"Set priority value (0..7) used for stderr"):
arg_stderr_priority = log_level_from_string(arg);
if (arg_stderr_priority < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to parse stderr priority value.");
break;
case ARG_LEVEL_PREFIX:
r = parse_boolean_argument("--level-prefix=", optarg, &arg_level_prefix);
OPTION_LONG("level-prefix", "BOOL",
"Control whether level prefix shall be parsed"):
r = parse_boolean_argument("--level-prefix=", arg, &arg_level_prefix);
if (r < 0)
return r;
break;
case ARG_NAMESPACE:
arg_namespace = empty_to_null(optarg);
OPTION_LONG("namespace", "NAMESPACE",
"Connect to specified journal namespace"):
arg_namespace = empty_to_null(arg);
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
*ret_args = option_parser_get_args(&state);
return 1;
}
@@ -135,7 +114,8 @@ static int run(int argc, char *argv[]) {
log_setup();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
@@ -157,7 +137,7 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to rearrange stdout/stderr: %m");
if (argc <= optind)
if (strv_isempty(args))
(void) execlp("cat", "cat", NULL);
else {
struct stat st;
@@ -171,7 +151,7 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to set environment variable JOURNAL_STREAM: %m");
(void) execvp(argv[optind], argv + optind);
(void) execvp(args[0], args);
}
r = -errno;
+34 -25
View File
@@ -21,8 +21,9 @@ static bool option_arg_required(const Option *opt) {
static bool option_is_metadata(const Option *opt) {
/* A metadata entry that is not a real option, like the group marker */
return FLAGS_SET(ASSERT_PTR(opt)->flags, OPTION_GROUP_MARKER) ||
FLAGS_SET(ASSERT_PTR(opt)->flags, OPTION_HELP_ENTRY);
return ASSERT_PTR(opt)->flags & (OPTION_GROUP_MARKER |
OPTION_HELP_ENTRY |
OPTION_HELP_ENTRY_VERBATIM);
}
static void shift_arg(char* argv[], int target, int source) {
@@ -313,30 +314,38 @@ int _option_parser_get_help_table(
/* No help string — we do not show the option */
continue;
char sc[3] = " ";
if (opt->short_code != 0)
xsprintf(sc, "-%c", opt->short_code);
_cleanup_free_ char *s = NULL;
/* We indent the option string by two spaces. We could set the minimum cell width and
* right-align for a similar result, but that'd be more work. This is only used for
* display.
*
* "=" is shown only when a long option is defined: -l --long=ARG, --long=ARG, -s ARG.
*/
bool need_eq = option_takes_arg(opt) && opt->long_code;
bool need_quote = opt->metavar && strchr(opt->metavar, ' ');
_cleanup_free_ char *s = strjoin(
" ",
sc,
" ",
opt->long_code ? "--" : "",
strempty(opt->long_code),
option_arg_optional(opt) ? "[" : "",
need_eq ? "=" : "",
need_quote ? "'" : "",
strempty(opt->metavar),
need_quote ? "'" : "",
option_arg_optional(opt) ? "]" : "");
if (FLAGS_SET(opt->flags, OPTION_HELP_ENTRY_VERBATIM)) {
assert(opt->long_code);
s = strjoin(" ",
opt->long_code);
} else {
char sc[3] = " ";
if (opt->short_code != 0)
xsprintf(sc, "-%c", opt->short_code);
/* We indent the option string by two spaces. We could set the minimum cell width and
* right-align for a similar result, but that'd be more work. This is only used for
* display.
*
* "=" is shown only when a long option is defined: -l --long=ARG, --long=ARG, -s ARG.
*/
bool need_eq = option_takes_arg(opt) && opt->long_code;
bool need_quote = opt->metavar && strchr(opt->metavar, ' ');
s = strjoin(" ",
sc,
" ",
opt->long_code ? "--" : "",
strempty(opt->long_code),
option_arg_optional(opt) ? "[" : "",
need_eq ? "=" : "",
need_quote ? "'" : "",
strempty(opt->metavar),
need_quote ? "'" : "",
option_arg_optional(opt) ? "]" : "");
}
if (!s)
return log_oom();
+6 -4
View File
@@ -5,10 +5,11 @@
#include "shared-forward.h"
typedef enum OptionFlags {
OPTION_OPTIONAL_ARG = 1U << 0, /* Same as optional_argument in getopt */
OPTION_STOPS_PARSING = 1U << 1, /* This option acts like "--" */
OPTION_GROUP_MARKER = 1U << 2, /* Fake option entry to separate groups */
OPTION_HELP_ENTRY = 1U << 3, /* Fake option entry to insert an additional help line */
OPTION_OPTIONAL_ARG = 1U << 0, /* Same as optional_argument in getopt */
OPTION_STOPS_PARSING = 1U << 1, /* This option acts like "--" */
OPTION_GROUP_MARKER = 1U << 2, /* Fake option entry to separate groups */
OPTION_HELP_ENTRY = 1U << 3, /* Fake option entry to insert an additional help line */
OPTION_HELP_ENTRY_VERBATIM = 1U << 4, /* Same, but use the long_code in the first column as written */
} OptionFlags;
typedef struct Option {
@@ -49,6 +50,7 @@ typedef struct Option {
#define OPTION_LONG_FLAGS(fl, lc, mv, h) OPTION_FULL(fl, /* sc= */ 0, lc, mv, h)
#define OPTION_SHORT(sc, mv, h) OPTION(sc, /* lc= */ NULL, mv, h)
#define OPTION_SHORT_FLAGS(fl, sc, mv, h) OPTION_FULL(fl, sc, /* lc= */ NULL, mv, h)
#define OPTION_HELP_VERBATIM(lc, h) OPTION_FULL(OPTION_HELP_ENTRY_VERBATIM, /* sc= */ 0, lc, /* mv= */ NULL, h)
#define OPTION_COMMON_HELP \
OPTION('h', "help", NULL, "Show this help")
+14 -6
View File
@@ -39,19 +39,27 @@ typedef struct {
.help = h, \
}
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
/* Forward-define function d. scope specifies the scope, e.g. static. */
#define VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, dat, h) \
DISABLE_WARNING_REDUNDANT_DECLS \
static int d(int, char**, uintptr_t, void*); \
scope int d(int, char**, uintptr_t, void*); \
REENABLE_WARNING \
_VERB_DATA(d, v, a, amin, amax, f, dat, h)
/* The same as VERB_SCOPE_FULL with scope hardwired to 'static'. */
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
VERB_SCOPE_FULL(static, d, v, a, amin, amax, f, dat, h)
/* The same as VERB_FULL, but without the data argument */
/* The same as VERB_SCOPE_FULL/VERB_FULL, but without the data argument. */
#define VERB_SCOPE(scope, d, v, a, amin, amax, f, h) \
VERB_SCOPE_FULL(scope, d, v, a, amin, amax, f, /* dat= */ 0, h)
#define VERB(d, v, a, amin, amax, f, h) \
VERB_FULL(d, v, a, amin, amax, f, /* dat= */ 0, h)
VERB_SCOPE(static, d, v, a, amin, amax, f, h)
/* Simplified VERB for parameters that take no argument */
/* Simplified VERB_SCOPE/VERB for verbs that take no argument. */
#define VERB_SCOPE_NOARG(scope, d, v, h) \
VERB_SCOPE(scope, d, v, /* a= */ NULL, /* amin= */ VERB_ANY, /* amax= */ 1, /* f= */ 0, h)
#define VERB_NOARG(d, v, h) \
VERB(d, v, /* a= */ NULL, /* amin= */ VERB_ANY, /* amax= */ 1, /* f= */ 0, h)
VERB_SCOPE_NOARG(static, d, v, h)
/* Magic entry in the table (which will not be returned) that designates the start of the group <gr>.
* The macro works as a separator between groups and must be between other VERB* stanzas. */