]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptenroll: convert to the new option parser
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Sat, 11 Apr 2026 09:26:05 +0000 (11:26 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Sat, 11 Apr 2026 11:30:42 +0000 (13:30 +0200)
--help is the same, apart from linewrapping.

Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
src/cryptenroll/cryptenroll.c

index 5640a3b9feebc252e5499e3cabc1a3317183516a..e9bb27c254886a34c0cb9a3333ecb60fe64672e7 100644 (file)
@@ -1,6 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <getopt.h>
 #include <sys/mman.h>
 
 #include "sd-device.h"
 #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,178 +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_LIST_DEVICES:
+                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);
 
-                case ARG_WIPE_SLOT:
-                        r = parse_wipe_slot(optarg);
+                OPTION_LONG("wipe-slot", "SLOT1,SLOT2,…",
+                            "Wipe specified slots"):
+                        r = parse_wipe_slot(arg);
                         if (r < 0)
                                 return r;
                         break;
 
-                case ARG_UNLOCK_KEYFILE:
+                OPTION_GROUP("Unlocking"): {}
+
+                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)
@@ -411,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();
                         }
@@ -422,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)
@@ -431,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();
                         }
@@ -442,7 +363,10 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
-                case ARG_PASSWORD:
+                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.");
@@ -450,7 +374,8 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_enroll_type = ENROLL_PASSWORD;
                         break;
 
-                case ARG_RECOVERY_KEY:
+                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.");
@@ -458,25 +383,28 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_enroll_type = ENROLL_RECOVERY;
                         break;
 
-                case ARG_PKCS11_TOKEN_URI: {
+                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();
                         }
@@ -486,18 +414,21 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
-                case ARG_FIDO2_DEVICE: {
+                OPTION_GROUP("FIDO2 Enrollment"): {}
+
+                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();
                         }
@@ -507,62 +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_FIDO2_CRED_ALG:
-                        r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
+                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", optarg);
+                                return log_error_errno(r, "Failed to parse COSE algorithm: %s", arg);
                         break;
 
-                case ARG_FIDO2_WITH_PIN:
-                        r = parse_boolean_argument("--fido2-with-client-pin=", optarg, NULL);
+                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;
 
-                case ARG_FIDO2_WITH_UP:
-                        r = parse_boolean_argument("--fido2-with-user-presence=", optarg, NULL);
+                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;
 
-                case ARG_FIDO2_WITH_UV:
-                        r = parse_boolean_argument("--fido2-with-user-verification=", optarg, NULL);
+                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;
 
-                case ARG_TPM2_DEVICE: {
+                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();
                         }
@@ -572,104 +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);
+                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 '?':
-                        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. "