mstack-tool: convert to OPTION macros

Both the main parser and the util-linux mount-helper-mode parser
(invoked as mount.mstack) are converted with "systmed-mstack" and
"mount.mstack" as namespaces. The latter has no help.

For systemd-mstack, Commands are listed first, and then Options.
And --no-pager, --no-legend, --json= are moved to the end.

Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-04-30 00:07:29 +02:00
parent 6557a98705
commit d9fbe513a9

View File

@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <getopt.h>
#include <unistd.h>
#include "argv-util.h"
@@ -11,13 +10,14 @@
#include "extract-word.h"
#include "fd-util.h"
#include "format-table.h"
#include "help-util.h"
#include "image-policy.h"
#include "main-func.h"
#include "mount-util.h"
#include "mountpoint-util.h"
#include "mstack.h"
#include "options.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "string-util.h"
static enum {
@@ -41,191 +41,155 @@ STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_filter, image_filter_freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL, *commands = NULL;
int r;
r = terminal_urlify_man("systemd-mstack", "1", &link);
r = option_parser_get_help_table_ns("systemd-mstack", &options);
if (r < 0)
return log_oom();
return r;
printf("%1$s [OPTIONS...] WHAT\n"
"%1$s [OPTIONS...] --mount WHAT WHERE\n"
"%1$s [OPTIONS...] --umount WHERE\n"
"\n%5$sInspect or apply mount stack.%6$s\n\n"
"%3$sOptions:%4$s\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not print the column headers\n"
" --json=pretty|short|off Generate JSON output\n"
" -r --read-only Mount read-only\n"
" --mkdir Make mount directory before mounting, if missing\n"
" --rmdir Remove mount directory after unmounting\n"
" --image-policy=POLICY\n"
" Specify image dissection policy\n"
" --image-filter=FILTER\n"
" Specify image dissection filter\n"
"\n%3$sCommands:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -m --mount Mount the mstack to the specified directory\n"
" -M Shortcut for --mount --mkdir\n"
" -u --umount Unmount the image from the specified directory\n"
" -U Shortcut for --umount --rmdir\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
ansi_underline(), ansi_normal(),
ansi_highlight(), ansi_normal());
r = option_parser_get_help_table_full("systemd-mstack", "Commands", &commands);
if (r < 0)
return r;
(void) table_sync_column_widths(0, options, commands);
help_cmdline("[OPTIONS...] WHAT");
help_cmdline("[OPTIONS...] --mount WHAT WHERE");
help_cmdline("[OPTIONS...] --umount WHERE");
help_abstract("Inspect or apply mount stack.");
help_section("Commands");
r = table_print_or_warn(commands);
if (r < 0)
return r;
help_section("Options");
r = table_print_or_warn(options);
if (r < 0)
return r;
help_man_page_reference("systemd-mstack", "1");
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_JSON,
ARG_MKDIR,
ARG_RMDIR,
ARG_IMAGE_POLICY,
ARG_IMAGE_FILTER,
};
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 },
{ "mount", no_argument, NULL, 'm' },
{ "umount", no_argument, NULL, 'u' },
{ "json", required_argument, NULL, ARG_JSON },
{ "read-only", no_argument, NULL, 'r' },
{ "mkdir", no_argument, NULL, ARG_MKDIR },
{ "rmdir", no_argument, NULL, ARG_RMDIR },
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
{ "image-filter", required_argument, NULL, ARG_IMAGE_FILTER },
{}
};
int c, r;
int r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hmMuUr", options, NULL)) >= 0) {
OptionParser opts = { argc, argv, .namespace = "systemd-mstack" };
FOREACH_OPTION_OR_RETURN(c, &opts)
switch (c) {
case 'h':
return help();
OPTION_NAMESPACE("systemd-mstack"): {}
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
if (r <= 0)
return r;
break;
case 'r':
OPTION('r', "read-only", NULL, "Mount read-only"):
arg_mstack_flags |= MSTACK_RDONLY;
break;
case ARG_IMAGE_POLICY:
r = parse_image_policy_argument(optarg, &arg_image_policy);
OPTION_LONG("mkdir", NULL, "Make mount directory before mounting, if missing"):
arg_mstack_flags |= MSTACK_MKDIR;
break;
OPTION_LONG("rmdir", NULL, "Remove mount directory after unmounting"):
arg_rmdir = true;
break;
OPTION_LONG("image-policy", "POLICY", "Specify image dissection policy"):
r = parse_image_policy_argument(opts.arg, &arg_image_policy);
if (r < 0)
return r;
break;
case ARG_IMAGE_FILTER: {
OPTION_LONG("image-filter", "FILTER", "Specify image dissection filter"): {
_cleanup_(image_filter_freep) ImageFilter *f = NULL;
r = image_filter_parse(optarg, &f);
r = image_filter_parse(opts.arg, &f);
if (r < 0)
return log_error_errno(r, "Failed to parse image filter expression: %s", optarg);
return log_error_errno(r, "Failed to parse image filter expression: %s", opts.arg);
image_filter_free(arg_image_filter);
arg_image_filter = TAKE_PTR(f);
break;
}
case ARG_MKDIR:
arg_mstack_flags |= MSTACK_MKDIR;
OPTION_COMMON_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_RMDIR:
arg_rmdir = true;
OPTION_COMMON_NO_LEGEND:
arg_legend = false;
break;
case 'm':
OPTION_COMMON_JSON:
r = parse_json_argument(opts.arg, &arg_json_format_flags);
if (r <= 0)
return r;
break;
OPTION_GROUP("Commands"): {}
OPTION_COMMON_HELP:
return help();
OPTION_COMMON_VERSION:
return version();
OPTION('m', "mount", NULL, "Mount the mstack to the specified directory"):
arg_action = ACTION_MOUNT;
break;
case 'M':
/* Shortcut combination of --mkdir + --mount */
OPTION_SHORT('M', NULL, "Shortcut for --mount --mkdir"):
arg_action = ACTION_MOUNT;
arg_mstack_flags |= MSTACK_MKDIR;
break;
case 'u':
OPTION('u', "umount", NULL, "Unmount the image from the specified directory"):
arg_action = ACTION_UMOUNT;
break;
case 'U':
/* Shortcut combination of --rmdir + --umount */
OPTION_SHORT('U', NULL, "Shortcut for --umount --rmdir"):
arg_action = ACTION_UMOUNT;
arg_rmdir = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
}
char **args = option_parser_get_args(&opts);
size_t n_args = option_parser_get_n_args(&opts);
switch (arg_action) {
case ACTION_INSPECT:
if (optind + 1 != argc)
if (n_args != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one argument.");
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_what);
r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_what);
if (r < 0)
return r;
break;
case ACTION_MOUNT:
if (optind + 2 != argc)
if (n_args != 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected two arguments.");
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_what);
r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_what);
if (r < 0)
return r;
r = parse_path_argument(argv[optind+1], /* suppress_root= */ false, &arg_where);
r = parse_path_argument(args[1], /* suppress_root= */ false, &arg_where);
if (r < 0)
return r;
break;
case ACTION_UMOUNT:
if (optind + 1 != argc)
if (n_args != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one argument.");
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_where);
r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_where);
if (r < 0)
return r;
@@ -239,47 +203,49 @@ static int parse_argv(int argc, char *argv[]) {
}
static int parse_argv_as_mount_helper(int argc, char *argv[]) {
const char *options = NULL;
const char *mount_options = NULL;
bool fake = false;
int c, r;
int r;
/* Implements util-linux "external helper" command line interface, as per mount(8) man page. */
while ((c = getopt(argc, argv, "sfnvN:o:t:")) >= 0) {
OptionParser opts = { argc, argv, .namespace = "mount.mstack" };
FOREACH_OPTION_OR_RETURN(c, &opts)
switch (c) {
case 'f':
OPTION_NAMESPACE("mount.mstack"): {}
OPTION_SHORT('f', NULL, NULL):
fake = true;
break;
case 'o':
options = optarg;
OPTION_SHORT('o', "OPTIONS", NULL):
mount_options = opts.arg;
break;
case 't':
if (!streq(optarg, "mstack"))
log_debug("Unexpected file system type '%s', ignoring.", optarg);
OPTION_SHORT('t', "TYPE", NULL):
if (!streq(opts.arg, "mstack"))
log_debug("Unexpected file system type '%s', ignoring.", opts.arg);
break;
case 's': /* sloppy mount options */
case 'n': /* aka --no-mtab */
case 'v': /* aka --verbose */
log_debug("Ignoring option -%c, not implemented.", c);
OPTION_SHORT('s', NULL, NULL): {} /* sloppy mount options, fall-through */
OPTION_SHORT('n', NULL, NULL): {} /* aka --no-mtab, fall-through */
OPTION_SHORT('v', NULL, NULL): /* aka --verbose */
log_debug("Ignoring option -%c, not implemented.", opts.opt->short_code);
break;
case 'N': /* aka --namespace= */
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Option -%c is not implemented, refusing.", c);
case '?':
return -EINVAL;
OPTION_SHORT('N', "NAMESPACE", NULL): /* aka --namespace= */
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Option -%c is not implemented, refusing.", opts.opt->short_code);
}
}
if (optind + 2 != argc)
char **args = option_parser_get_args(&opts);
if (option_parser_get_n_args(&opts) != 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Expected an image file path and target directory as only argument.");
"Expected an image file path and target directory as arguments.");
for (const char *p = options;;) {
for (const char *p = mount_options;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, ",", EXTRACT_KEEP_QUOTE);
@@ -300,11 +266,11 @@ static int parse_argv_as_mount_helper(int argc, char *argv[]) {
if (fake)
return 0;
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_what);
r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_what);
if (r < 0)
return r;
r = parse_path_argument(argv[optind+1], /* suppress_root= */ false, &arg_where);
r = parse_path_argument(args[1], /* suppress_root= */ false, &arg_where);
if (r < 0)
return r;