Convert more programs to option+verb macros (#41974)
Just a handful are left! This is one of the final batches.
This commit is contained in:
+85
-145
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -14,6 +13,7 @@
|
||||
#include "sd-messages.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ansi-color.h"
|
||||
#include "build.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-locator.h"
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "glob-util.h"
|
||||
#include "help-util.h"
|
||||
#include "image-policy.h"
|
||||
#include "io-util.h"
|
||||
#include "journal-internal.h"
|
||||
@@ -39,12 +40,12 @@
|
||||
#include "loop-util.h"
|
||||
#include "main-func.h"
|
||||
#include "mount-util.h"
|
||||
#include "options.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidref.h"
|
||||
#include "pretty-print.h"
|
||||
#include "process-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "string-util.h"
|
||||
@@ -177,224 +178,166 @@ static int acquire_journal(sd_journal **ret, char **matches) {
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *verbs = NULL, *options = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("coredumpctl", "1", &link);
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
|
||||
"%5$sList or retrieve coredumps from the journal.%6$s\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" list [MATCHES...] List available coredumps (default)\n"
|
||||
" info [MATCHES...] Show detailed information about one or more coredumps\n"
|
||||
" dump [MATCHES...] Print first matching coredump to stdout\n"
|
||||
" debug [MATCHES...] Start a debugger for the first matching coredump\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version string\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"
|
||||
" --debugger=DEBUGGER Use the given debugger\n"
|
||||
" -A --debugger-arguments=ARGS Pass the given arguments to the debugger\n"
|
||||
" -n INT Show maximum number of rows\n"
|
||||
" -1 Show information about most recent entry only\n"
|
||||
" -S --since=DATE Only print coredumps since the date\n"
|
||||
" -U --until=DATE Only print coredumps until the date\n"
|
||||
" -r --reverse Show the newest entries first\n"
|
||||
" -F --field=FIELD List all values a certain field takes\n"
|
||||
" -o --output=FILE Write output to FILE\n"
|
||||
" --file=PATH Use journal file\n"
|
||||
" -D --directory=DIR Use journal files from directory\n\n"
|
||||
" -q --quiet Do not show info messages and privilege warning\n"
|
||||
" --all Look at all journal files instead of local ones\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\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(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, verbs, options);
|
||||
|
||||
help_cmdline("[OPTIONS…] COMMAND …");
|
||||
help_abstract("List or retrieve coredumps from the journal.");
|
||||
|
||||
help_section("Commands");
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("coredumpctl", "1");
|
||||
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_NO_PAGER,
|
||||
ARG_NO_LEGEND,
|
||||
ARG_JSON,
|
||||
ARG_DEBUGGER,
|
||||
ARG_FILE,
|
||||
ARG_ROOT,
|
||||
ARG_IMAGE,
|
||||
ARG_IMAGE_POLICY,
|
||||
ARG_ALL,
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
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 },
|
||||
{ "debugger", required_argument, NULL, ARG_DEBUGGER },
|
||||
{ "debugger-arguments", required_argument, NULL, 'A' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "field", required_argument, NULL, 'F' },
|
||||
{ "file", required_argument, NULL, ARG_FILE },
|
||||
{ "directory", required_argument, NULL, 'D' },
|
||||
{ "reverse", no_argument, NULL, 'r' },
|
||||
{ "since", required_argument, NULL, 'S' },
|
||||
{ "until", required_argument, NULL, 'U' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{ "all", no_argument, NULL, ARG_ALL },
|
||||
{}
|
||||
};
|
||||
static int parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
int r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(remaining_args);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hA:o:F:1D:rS:U:qn:", options, NULL)) >= 0)
|
||||
OptionParser opts = { argc, argv };
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
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_DEBUGGER:
|
||||
arg_debugger = optarg;
|
||||
OPTION_LONG("debugger", "DEBUGGER", "Use the given debugger"):
|
||||
arg_debugger = opts.arg;
|
||||
break;
|
||||
|
||||
case 'A': {
|
||||
OPTION('A', "debugger-arguments", "…", "Pass the given arguments to the debugger"): {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
r = strv_split_full(&l, optarg, WHITESPACE, EXTRACT_UNQUOTE);
|
||||
r = strv_split_full(&l, opts.arg, WHITESPACE, EXTRACT_UNQUOTE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse debugger arguments '%s': %m", optarg);
|
||||
return log_error_errno(r, "Failed to parse debugger arguments '%s': %m", opts.arg);
|
||||
strv_free_and_replace(arg_debugger_args, l);
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_FILE:
|
||||
r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
|
||||
OPTION_LONG("file", "PATH", "Use journal file"):
|
||||
r = glob_extend(&arg_file, opts.arg, GLOB_NOCHECK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add paths: %m");
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
OPTION('o', "output", "FILE", "Write output to FILE"):
|
||||
if (arg_output)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot set output more than once.");
|
||||
|
||||
arg_output = optarg;
|
||||
arg_output = opts.arg;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
r = parse_timestamp(optarg, &arg_since);
|
||||
OPTION('S', "since", "DATE", "Only print coredumps since the date"):
|
||||
r = parse_timestamp(opts.arg, &arg_since);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
|
||||
return log_error_errno(r, "Failed to parse timestamp '%s': %m", opts.arg);
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
r = parse_timestamp(optarg, &arg_until);
|
||||
OPTION('U', "until", "DATE", "Only print coredumps until the date"):
|
||||
r = parse_timestamp(opts.arg, &arg_until);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
|
||||
return log_error_errno(r, "Failed to parse timestamp '%s': %m", opts.arg);
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
OPTION('F', "field", "FIELD", "List all values a certain field takes"):
|
||||
if (arg_field)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot use --field/-F more than once.");
|
||||
arg_field = optarg;
|
||||
arg_field = opts.arg;
|
||||
break;
|
||||
|
||||
case '1':
|
||||
OPTION_SHORT('1', NULL, "Show information about most recent entry only"):
|
||||
arg_rows_max = 1;
|
||||
arg_reverse = true;
|
||||
break;
|
||||
|
||||
case 'n': {
|
||||
OPTION_SHORT('n', "INT", "Show at most this many rows"): {
|
||||
unsigned n;
|
||||
|
||||
r = safe_atou(optarg, &n);
|
||||
r = safe_atou(opts.arg, &n);
|
||||
if (r < 0 || n < 1)
|
||||
return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid numeric parameter to -n: %s", optarg);
|
||||
"Invalid numeric parameter to -n: %s", opts.arg);
|
||||
|
||||
arg_rows_max = n;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'D':
|
||||
arg_directory = optarg;
|
||||
OPTION('D', "directory", "DIR", "Use journal files from directory"):
|
||||
arg_directory = opts.arg;
|
||||
break;
|
||||
|
||||
case ARG_ROOT:
|
||||
r = parse_path_argument(optarg, false, &arg_root);
|
||||
OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
|
||||
r = parse_path_argument(opts.arg, false, &arg_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE:
|
||||
r = parse_path_argument(optarg, false, &arg_image);
|
||||
OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
|
||||
r = parse_path_argument(opts.arg, 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(opts.arg, &arg_image_policy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
OPTION('r', "reverse", NULL, "Show the newest entries first"):
|
||||
arg_reverse = true;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
OPTION('q', "quiet", NULL, "Do not show info messages and privilege warning"):
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case ARG_JSON:
|
||||
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||
OPTION_COMMON_JSON:
|
||||
r = parse_json_argument(opts.arg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case ARG_ALL:
|
||||
OPTION_LONG("all", NULL, "Look at all journal files instead of local ones"):
|
||||
arg_all = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
|
||||
@@ -405,6 +348,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if ((!!arg_directory + !!arg_image + !!arg_root) > 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root=, --image= or -D/--directory=, the combination of these options is not supported.");
|
||||
|
||||
*remaining_args = option_parser_get_args(&opts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -902,6 +846,10 @@ static int print_entry(
|
||||
return print_info(stdout, j, n_found > 0);
|
||||
}
|
||||
|
||||
VERB(verb_dump_list, "list", "[MATCHES…]", VERB_ANY, VERB_ANY, VERB_DEFAULT,
|
||||
"List available coredumps");
|
||||
VERB(verb_dump_list, "info", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
"Show detailed information about one or more coredumps");
|
||||
static int verb_dump_list(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
@@ -1146,6 +1094,8 @@ error:
|
||||
return r;
|
||||
}
|
||||
|
||||
VERB(verb_dump_core, "dump", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
"Print first matching coredump to stdout");
|
||||
static int verb_dump_core(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
@@ -1182,6 +1132,10 @@ static int verb_dump_core(int argc, char *argv[], uintptr_t _data, void *userdat
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB(verb_run_debug, "debug", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
"Start a debugger for the first matching coredump");
|
||||
VERB(verb_run_debug, "gdb", "[MATCHES…]", VERB_ANY, VERB_ANY, 0,
|
||||
/* help= */ NULL);
|
||||
static int verb_run_debug(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
static const struct sigaction sa = {
|
||||
.sa_sigaction = sigterm_process_group_handler,
|
||||
@@ -1372,30 +1326,16 @@ static int check_units_active(void) {
|
||||
return c;
|
||||
}
|
||||
|
||||
static int coredumpctl_main(int argc, char *argv[]) {
|
||||
|
||||
static const Verb verbs[] = {
|
||||
{ "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, verb_dump_list },
|
||||
{ "info", VERB_ANY, VERB_ANY, 0, verb_dump_list },
|
||||
{ "dump", VERB_ANY, VERB_ANY, 0, verb_dump_core },
|
||||
{ "debug", VERB_ANY, VERB_ANY, 0, verb_run_debug },
|
||||
{ "gdb", VERB_ANY, VERB_ANY, 0, verb_run_debug },
|
||||
{ "help", VERB_ANY, 1, 0, verb_help },
|
||||
{}
|
||||
};
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, NULL);
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
|
||||
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
|
||||
char **args = NULL;
|
||||
int r, units_active;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
@@ -1425,7 +1365,7 @@ static int run(int argc, char *argv[]) {
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = coredumpctl_main(argc, argv);
|
||||
r = dispatch_verb_with_args(args, NULL);
|
||||
|
||||
if (units_active > 0)
|
||||
printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
|
||||
|
||||
+146
-220
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "ansi-color.h"
|
||||
#include "argv-util.h"
|
||||
#include "build.h"
|
||||
#include "bus-error.h"
|
||||
@@ -20,15 +19,16 @@
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
#include "fstab-util.h"
|
||||
#include "help-util.h"
|
||||
#include "libmount-util.h"
|
||||
#include "main-func.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "options.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "polkit-agent.h"
|
||||
#include "pretty-print.h"
|
||||
#include "process-util.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "stat-util.h"
|
||||
@@ -108,296 +108,220 @@ static int parse_where(const char *input, char **ret_where) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
static int help(char *argv[]) {
|
||||
_cleanup_(table_unrefp) Table *options_common = NULL, *options_mount = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-mount", "1", &link);
|
||||
r = option_parser_get_help_table(&options_common);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
printf("systemd-mount [OPTIONS...] WHAT [WHERE]\n"
|
||||
"systemd-mount [OPTIONS...] --tmpfs [NAME] WHERE\n"
|
||||
"systemd-mount [OPTIONS...] --list\n"
|
||||
"%1$s [OPTIONS...] %7$sWHAT|WHERE...\n"
|
||||
"\n%5$sEstablish a mount or auto-mount point transiently.%6$s\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --no-block Do not wait until operation finished\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --no-legend Do not show the headers\n"
|
||||
" -l --full Do not ellipsize output\n"
|
||||
" --no-ask-password Do not prompt for password\n"
|
||||
" -q --quiet Suppress information messages during runtime\n"
|
||||
" --json=pretty|short|off Generate JSON output\n"
|
||||
" --user Run as user unit\n"
|
||||
" -H --host=[USER@]HOST Operate on remote host\n"
|
||||
" -M --machine=CONTAINER Operate on local container\n"
|
||||
" --discover Discover mount device metadata\n"
|
||||
" -t --type=TYPE File system type\n"
|
||||
" -o --options=OPTIONS Mount options\n"
|
||||
" --owner=USER Add uid= and gid= options for USER\n"
|
||||
" --fsck=no Don't run file system check before mount\n"
|
||||
" --description=TEXT Description for unit\n"
|
||||
" -p --property=NAME=VALUE Set mount unit property\n"
|
||||
" --automount=BOOL Create an automount point\n"
|
||||
" -A Same as --automount=yes\n"
|
||||
" --timeout-idle-sec=SEC Specify automount idle timeout\n"
|
||||
" --automount-property=NAME=VALUE\n"
|
||||
" Set automount unit property\n"
|
||||
" --bind-device Bind automount unit to device\n"
|
||||
" --list List mountable block devices\n"
|
||||
" -u --umount Unmount mount points\n"
|
||||
" -G --collect Unload unit after it stopped, even when failed\n"
|
||||
" -T --tmpfs Create a new tmpfs on the mount point\n"
|
||||
" --canonicalize=BOOL Controls whether to canonicalize path before\n"
|
||||
" operation\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
streq(program_invocation_short_name, "systemd-umount") ? "" : "--umount ");
|
||||
if (invoked_as(argv, "systemd-umount")) {
|
||||
help_cmdline("[OPTIONS…] WHAT|WHERE…");
|
||||
help_abstract("Unmount one or more mount points.");
|
||||
} else {
|
||||
help_cmdline("[OPTIONS…] WHAT [WHERE]");
|
||||
help_cmdline("[OPTIONS…] --tmpfs [NAME] WHERE");
|
||||
help_cmdline("[OPTIONS…] --list");
|
||||
help_cmdline("[OPTIONS…] --umount WHAT|WHERE…");
|
||||
help_abstract("Establish a mount or auto-mount point.");
|
||||
|
||||
r = option_parser_get_help_table_group("Mount options", &options_mount);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, options_common, options_mount);
|
||||
}
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options_common);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (options_mount) {
|
||||
r = table_print_or_warn(options_mount);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
help_man_page_reference("systemd-mount", "1");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_NO_BLOCK,
|
||||
ARG_NO_PAGER,
|
||||
ARG_NO_LEGEND,
|
||||
ARG_NO_ASK_PASSWORD,
|
||||
ARG_USER,
|
||||
ARG_SYSTEM,
|
||||
ARG_DISCOVER,
|
||||
ARG_MOUNT_TYPE,
|
||||
ARG_MOUNT_OPTIONS,
|
||||
ARG_OWNER,
|
||||
ARG_FSCK,
|
||||
ARG_DESCRIPTION,
|
||||
ARG_TIMEOUT_IDLE,
|
||||
ARG_AUTOMOUNT,
|
||||
ARG_AUTOMOUNT_PROPERTY,
|
||||
ARG_BIND_DEVICE,
|
||||
ARG_LIST,
|
||||
ARG_JSON,
|
||||
ARG_CANONICALIZE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "full", no_argument, NULL, 'l' },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "host", required_argument, NULL, 'H' },
|
||||
{ "machine", required_argument, NULL, 'M' },
|
||||
{ "discover", no_argument, NULL, ARG_DISCOVER },
|
||||
{ "type", required_argument, NULL, 't' },
|
||||
{ "options", required_argument, NULL, 'o' },
|
||||
{ "owner", required_argument, NULL, ARG_OWNER },
|
||||
{ "fsck", required_argument, NULL, ARG_FSCK },
|
||||
{ "description", required_argument, NULL, ARG_DESCRIPTION },
|
||||
{ "property", required_argument, NULL, 'p' },
|
||||
{ "automount", required_argument, NULL, ARG_AUTOMOUNT },
|
||||
{ "timeout-idle-sec", required_argument, NULL, ARG_TIMEOUT_IDLE },
|
||||
{ "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY },
|
||||
{ "bind-device", no_argument, NULL, ARG_BIND_DEVICE },
|
||||
{ "list", no_argument, NULL, ARG_LIST },
|
||||
{ "umount", no_argument, NULL, 'u' },
|
||||
{ "unmount", no_argument, NULL, 'u' }, /* Compat spelling */
|
||||
{ "collect", no_argument, NULL, 'G' },
|
||||
{ "tmpfs", no_argument, NULL, 'T' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "canonicalize", required_argument, NULL, ARG_CANONICALIZE },
|
||||
{},
|
||||
};
|
||||
|
||||
int r, c;
|
||||
static int parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
int r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(remaining_args);
|
||||
|
||||
if (invoked_as(argv, "systemd-umount"))
|
||||
arg_action = ACTION_UMOUNT;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:AuGlT", options, NULL)) >= 0)
|
||||
OptionParser opts = { argc, argv };
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
return help();
|
||||
OPTION_COMMON_HELP:
|
||||
return help(argv);
|
||||
|
||||
case ARG_VERSION:
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
case ARG_NO_BLOCK:
|
||||
arg_no_block = true;
|
||||
break;
|
||||
|
||||
case ARG_NO_PAGER:
|
||||
arg_pager_flags |= PAGER_DISABLE;
|
||||
break;
|
||||
|
||||
case ARG_NO_LEGEND:
|
||||
arg_legend = false;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
arg_full = true;
|
||||
break;
|
||||
|
||||
case ARG_NO_ASK_PASSWORD:
|
||||
arg_ask_password = false;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
OPTION_LONG("user", NULL, "Run as user unit"):
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
OPTION_LONG("system", NULL, /* help= */ NULL):
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
OPTION_COMMON_HOST:
|
||||
arg_transport = BUS_TRANSPORT_REMOTE;
|
||||
arg_host = optarg;
|
||||
arg_host = opts.arg;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
r = parse_machine_argument(optarg, &arg_host, &arg_transport);
|
||||
OPTION_COMMON_MACHINE:
|
||||
r = parse_machine_argument(opts.arg, &arg_host, &arg_transport);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_DISCOVER:
|
||||
OPTION_LONG("canonicalize", "BOOL",
|
||||
"Whether to canonicalize path before operation"):
|
||||
r = parse_boolean_argument("--canonicalize=", opts.arg, &arg_canonicalize);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
OPTION_LONG("no-block", NULL, "Do not wait until operation finished"):
|
||||
arg_no_block = true;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_NO_PAGER:
|
||||
arg_pager_flags |= PAGER_DISABLE;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_NO_LEGEND:
|
||||
arg_legend = false;
|
||||
break;
|
||||
|
||||
OPTION('l', "full", NULL, "Do not ellipsize output"):
|
||||
arg_full = true;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_NO_ASK_PASSWORD:
|
||||
arg_ask_password = false;
|
||||
break;
|
||||
|
||||
OPTION('q', "quiet", NULL, "Suppress informational messages during runtime"):
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
OPTION_COMMON_JSON:
|
||||
r = parse_json_argument(opts.arg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
OPTION_GROUP("Mount options"): {}
|
||||
|
||||
OPTION_LONG("discover", NULL, "Discover mount device metadata"):
|
||||
arg_discover = true;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
r = free_and_strdup_warn(&arg_mount_type, optarg);
|
||||
OPTION('t', "type", "TYPE", "File system type"):
|
||||
r = free_and_strdup_warn(&arg_mount_type, opts.arg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
r = free_and_strdup_warn(&arg_mount_options, optarg);
|
||||
OPTION('o', "options", "OPTIONS", "Mount options"):
|
||||
r = free_and_strdup_warn(&arg_mount_options, opts.arg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_OWNER: {
|
||||
const char *user = optarg;
|
||||
OPTION_LONG("owner", "USER", "Add uid= and gid= options for USER"): {
|
||||
const char *user = opts.arg;
|
||||
|
||||
r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r,
|
||||
r == -EBADMSG ? "UID or GID of user %s are invalid."
|
||||
: "Cannot use \"%s\" as owner: %m",
|
||||
optarg);
|
||||
opts.arg);
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_FSCK:
|
||||
r = parse_boolean_argument("--fsck=", optarg, &arg_fsck);
|
||||
OPTION_LONG("fsck", "BOOL", "Run a file system check before mount"):
|
||||
r = parse_boolean_argument("--fsck=", opts.arg, &arg_fsck);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_DESCRIPTION:
|
||||
r = free_and_strdup_warn(&arg_description, optarg);
|
||||
OPTION_LONG("description", "TEXT", "Description for unit"):
|
||||
r = free_and_strdup_warn(&arg_description, opts.arg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (strv_extend(&arg_property, optarg) < 0)
|
||||
OPTION('p', "property", "NAME=VALUE", "Set mount unit property"):
|
||||
if (strv_extend(&arg_property, opts.arg) < 0)
|
||||
return log_oom();
|
||||
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
OPTION_SHORT('A', NULL, "Same as --automount=yes"):
|
||||
arg_action = ACTION_AUTOMOUNT;
|
||||
break;
|
||||
|
||||
case ARG_AUTOMOUNT:
|
||||
r = parse_boolean_argument("--automount=", optarg, NULL);
|
||||
OPTION_LONG("automount", "BOOL", "Create an automount point"):
|
||||
r = parse_boolean_argument("--automount=", opts.arg, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_action = r ? ACTION_AUTOMOUNT : ACTION_MOUNT;
|
||||
break;
|
||||
|
||||
case ARG_TIMEOUT_IDLE:
|
||||
r = parse_sec(optarg, &arg_timeout_idle);
|
||||
OPTION_LONG("timeout-idle-sec", "SEC", "Specify automount idle timeout"):
|
||||
r = parse_sec(opts.arg, &arg_timeout_idle);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse timeout: %s", optarg);
|
||||
return log_error_errno(r, "Failed to parse timeout: %s", opts.arg);
|
||||
|
||||
arg_timeout_idle_set = true;
|
||||
break;
|
||||
|
||||
case ARG_AUTOMOUNT_PROPERTY:
|
||||
if (strv_extend(&arg_automount_property, optarg) < 0)
|
||||
OPTION_LONG("automount-property", "NAME=VALUE", "Set automount unit property"):
|
||||
if (strv_extend(&arg_automount_property, opts.arg) < 0)
|
||||
return log_oom();
|
||||
|
||||
break;
|
||||
|
||||
case ARG_BIND_DEVICE:
|
||||
OPTION_LONG("bind-device", NULL, "Bind automount unit to device"):
|
||||
arg_bind_device = true;
|
||||
break;
|
||||
|
||||
case ARG_LIST:
|
||||
OPTION_LONG("list", NULL, "List mountable block devices"):
|
||||
arg_action = ACTION_LIST;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
OPTION('u', "umount", NULL, "Unmount mount points"): {}
|
||||
OPTION_LONG("unmount", NULL, /* help= */ NULL): /* compat spelling */
|
||||
arg_action = ACTION_UMOUNT;
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
OPTION('G', "collect", NULL, "Unload unit after it stopped, even when failed"):
|
||||
arg_aggressive_gc = true;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
OPTION('T', "tmpfs", NULL, "Create a new tmpfs on the mount point"):
|
||||
arg_tmpfs = true;
|
||||
break;
|
||||
|
||||
case ARG_JSON:
|
||||
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case ARG_CANONICALIZE:
|
||||
r = parse_boolean_argument("--canonicalize=", optarg, &arg_canonicalize);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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);
|
||||
|
||||
if (arg_runtime_scope == RUNTIME_SCOPE_USER) {
|
||||
arg_ask_password = false;
|
||||
|
||||
@@ -407,7 +331,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (arg_action == ACTION_LIST) {
|
||||
if (optind < argc)
|
||||
if (n_args > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Too many arguments.");
|
||||
|
||||
@@ -415,22 +339,22 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Listing devices only supported locally.");
|
||||
} else if (arg_action == ACTION_UMOUNT) {
|
||||
if (optind >= argc)
|
||||
if (n_args == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"At least one argument required.");
|
||||
|
||||
if (arg_transport != BUS_TRANSPORT_LOCAL || !arg_canonicalize)
|
||||
for (int i = optind; i < argc; i++)
|
||||
if (!path_is_absolute(argv[i]))
|
||||
STRV_FOREACH(a, args)
|
||||
if (!path_is_absolute(*a))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Path must be absolute when operating remotely or when canonicalization is turned off: %s",
|
||||
argv[i]);
|
||||
*a);
|
||||
} else {
|
||||
if (optind >= argc)
|
||||
if (n_args == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"At least one argument required.");
|
||||
|
||||
if (argc > optind+2)
|
||||
if (n_args > 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"More than two arguments are not allowed.");
|
||||
|
||||
@@ -439,16 +363,16 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--discover cannot be used in conjunction with --tmpfs.");
|
||||
|
||||
if (argc <= optind+1) {
|
||||
if (n_args == 1) {
|
||||
arg_mount_what = strdup("tmpfs");
|
||||
if (!arg_mount_what)
|
||||
return log_oom();
|
||||
|
||||
r = parse_where(argv[optind], &arg_mount_where);
|
||||
r = parse_where(args[0], &arg_mount_where);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
arg_mount_what = strdup(argv[optind]);
|
||||
arg_mount_what = strdup(args[0]);
|
||||
if (!arg_mount_what)
|
||||
return log_oom();
|
||||
}
|
||||
@@ -463,12 +387,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_mount_type);
|
||||
} else {
|
||||
if (arg_mount_type && !fstype_is_blockdev_backed(arg_mount_type)) {
|
||||
arg_mount_what = strdup(argv[optind]);
|
||||
arg_mount_what = strdup(args[0]);
|
||||
if (!arg_mount_what)
|
||||
return log_oom();
|
||||
} else {
|
||||
_cleanup_free_ char *u = NULL;
|
||||
const char *p = argv[optind];
|
||||
const char *p = args[0];
|
||||
|
||||
if (arg_canonicalize) {
|
||||
u = fstab_node_to_udev_node(p);
|
||||
@@ -494,8 +418,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > optind+1) {
|
||||
r = parse_where(argv[optind+1], &arg_mount_where);
|
||||
if (n_args >= 2) {
|
||||
r = parse_where(args[1], &arg_mount_where);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (!arg_tmpfs)
|
||||
@@ -524,6 +448,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
*remaining_args = args;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1077,18 +1002,18 @@ static int umount_loop(sd_bus *bus, const char *backing_file) {
|
||||
return umount_by_device(bus, dev);
|
||||
}
|
||||
|
||||
static int action_umount(sd_bus *bus, int argc, char **argv) {
|
||||
static int action_umount(sd_bus *bus, char **args) {
|
||||
int r, ret = 0;
|
||||
|
||||
assert(bus);
|
||||
assert(argv);
|
||||
assert(argc > optind);
|
||||
assert(args);
|
||||
assert(!strv_isempty(args));
|
||||
|
||||
if (arg_transport != BUS_TRANSPORT_LOCAL || !arg_canonicalize) {
|
||||
for (int i = optind; i < argc; i++) {
|
||||
STRV_FOREACH(arg, args) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
r = path_simplify_alloc(argv[i], &p);
|
||||
r = path_simplify_alloc(*arg, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1097,10 +1022,10 @@ static int action_umount(sd_bus *bus, int argc, char **argv) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = optind; i < argc; i++) {
|
||||
STRV_FOREACH(arg, args) {
|
||||
_cleanup_free_ char *u = NULL, *p = NULL;
|
||||
|
||||
u = fstab_node_to_udev_node(argv[i]);
|
||||
u = fstab_node_to_udev_node(*arg);
|
||||
if (!u)
|
||||
return log_oom();
|
||||
|
||||
@@ -1113,7 +1038,7 @@ static int action_umount(sd_bus *bus, int argc, char **argv) {
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0)
|
||||
return log_error_errno(errno, "Can't stat '%s' (from %s): %m", p, argv[i]);
|
||||
return log_error_errno(errno, "Can't stat '%s' (from %s): %m", p, *arg);
|
||||
|
||||
r = is_mount_point_at(fd, /* path= */ NULL, /* flags= */ 0);
|
||||
fd = safe_close(fd); /* before continuing make sure the dir is not keeping anything busy */
|
||||
@@ -1135,7 +1060,7 @@ static int action_umount(sd_bus *bus, int argc, char **argv) {
|
||||
else
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown file type for unmounting: %s (from %s)",
|
||||
p, argv[i]);
|
||||
p, *arg);
|
||||
RET_GATHER(ret, r);
|
||||
}
|
||||
}
|
||||
@@ -1552,11 +1477,12 @@ static int list_devices(void) {
|
||||
|
||||
static int run(int argc, char* argv[]) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
char **args = NULL;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
@@ -1570,7 +1496,7 @@ static int run(int argc, char* argv[]) {
|
||||
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
|
||||
|
||||
if (arg_action == ACTION_UMOUNT)
|
||||
return action_umount(bus, argc, argv);
|
||||
return action_umount(bus, args);
|
||||
|
||||
if ((!arg_mount_type || fstype_is_blockdev_backed(arg_mount_type))
|
||||
&& !path_is_normalized(arg_mount_what))
|
||||
|
||||
+270
-331
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ansi-color.h"
|
||||
#include "build.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-locator.h"
|
||||
@@ -15,8 +14,11 @@
|
||||
#include "env-file.h"
|
||||
#include "format-table.h"
|
||||
#include "fs-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "help-util.h"
|
||||
#include "install.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "os-util.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
@@ -24,7 +26,6 @@
|
||||
#include "path-util.h"
|
||||
#include "polkit-agent.h"
|
||||
#include "portable.h"
|
||||
#include "pretty-print.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "verbs.h"
|
||||
@@ -155,8 +156,9 @@ static int extract_prefix(const char *path, char **ret) {
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
/* A slightly reduced version of what's permitted in unit names. With ':' and '\' are removed, as well as '_'
|
||||
* which we use as delimiter for the second part of the image string, which we ignore for now. */
|
||||
/* A slightly reduced version of what's permitted in unit names. With ':' and '\' are removed, as
|
||||
* well as '_' which we use as delimiter for the second part of the image string, which we ignore for
|
||||
* now. */
|
||||
if (!in_charset(name, ALPHANUMERICAL "-."))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -171,9 +173,9 @@ static int determine_matches(const char *image, char **l, bool allow_any, char *
|
||||
_cleanup_strv_free_ char **k = NULL;
|
||||
int r;
|
||||
|
||||
/* Determine the matches to apply. If the list is empty we derive the match from the image name. If the list
|
||||
* contains exactly the "-" we return a wildcard list (which is the empty list), but only if this is expressly
|
||||
* permitted. */
|
||||
/* Determine the matches to apply. If the list is empty we derive the match from the image name. If
|
||||
* the list contains exactly the "-" we return a wildcard list (which is the empty list), but only if
|
||||
* this is expressly permitted. */
|
||||
|
||||
if (strv_isempty(l)) {
|
||||
char *prefix;
|
||||
@@ -251,6 +253,82 @@ static int maybe_reload(sd_bus **bus) {
|
||||
return bus_service_manager_reload(*bus);
|
||||
}
|
||||
|
||||
VERB(verb_list_images, "list", NULL, VERB_ANY, 1, VERB_DEFAULT,
|
||||
"List available portable service images (default)");
|
||||
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;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_call_method(bus, bus_portable_mgr, "ListImages", &error, &reply, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r));
|
||||
|
||||
table = table_new("name", "type", "ro", "crtime", "mtime", "usage", "state");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
r = sd_bus_message_enter_container(reply, 'a', "(ssbtttso)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
for (;;) {
|
||||
const char *name, *type, *state;
|
||||
uint64_t crtime, mtime, usage;
|
||||
int ro_int;
|
||||
|
||||
r = sd_bus_message_read(reply, "(ssbtttso)", &name, &type, &ro_int, &crtime, &mtime, &usage, &state, NULL);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, name,
|
||||
TABLE_STRING, type,
|
||||
TABLE_BOOLEAN, ro_int,
|
||||
TABLE_SET_COLOR, ro_int ? ansi_highlight_red() : NULL,
|
||||
TABLE_TIMESTAMP, crtime,
|
||||
TABLE_TIMESTAMP, mtime,
|
||||
TABLE_SIZE, usage,
|
||||
TABLE_STRING, state,
|
||||
TABLE_SET_COLOR, !streq(state, "detached") ? ansi_highlight_green() : NULL);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (!table_isempty(table)) {
|
||||
r = table_set_sort(table, (size_t) 0);
|
||||
if (r < 0)
|
||||
return table_log_sort_error(r);
|
||||
|
||||
table_set_header(table, arg_legend);
|
||||
|
||||
r = table_print_or_warn(table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_legend) {
|
||||
if (table_isempty(table))
|
||||
printf("No images.\n");
|
||||
else
|
||||
printf("\n%zu images listed.\n", table_get_rows(table) - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd_bus_message **reply) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
@@ -292,6 +370,8 @@ static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB(verb_inspect_image, "inspect", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0,
|
||||
"Show details of specified portable service image");
|
||||
static int verb_inspect_image(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
@@ -956,14 +1036,14 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB(verb_attach_image, "attach", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0,
|
||||
"Attach the specified portable service image");
|
||||
static int verb_attach_image(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return attach_reattach_image(argc, argv, strv_isempty(arg_extension_images) && !arg_force ? "AttachImage" : "AttachImageWithExtensions");
|
||||
}
|
||||
|
||||
static int verb_reattach_image(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return attach_reattach_image(argc, argv, strv_isempty(arg_extension_images) && !arg_force ? "ReattachImage" : "ReattachImageWithExtensions");
|
||||
}
|
||||
|
||||
VERB(verb_detach_image, "detach", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0,
|
||||
"Detach the specified portable service image");
|
||||
static int verb_detach_image(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
@@ -1018,168 +1098,14 @@ static int verb_detach_image(int argc, char *argv[], uintptr_t _data, void *user
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_call_method(bus, bus_portable_mgr, "ListImages", &error, &reply, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r));
|
||||
|
||||
table = table_new("name", "type", "ro", "crtime", "mtime", "usage", "state");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
r = sd_bus_message_enter_container(reply, 'a', "(ssbtttso)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
for (;;) {
|
||||
const char *name, *type, *state;
|
||||
uint64_t crtime, mtime, usage;
|
||||
int ro_int;
|
||||
|
||||
r = sd_bus_message_read(reply, "(ssbtttso)", &name, &type, &ro_int, &crtime, &mtime, &usage, &state, NULL);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, name,
|
||||
TABLE_STRING, type,
|
||||
TABLE_BOOLEAN, ro_int,
|
||||
TABLE_SET_COLOR, ro_int ? ansi_highlight_red() : NULL,
|
||||
TABLE_TIMESTAMP, crtime,
|
||||
TABLE_TIMESTAMP, mtime,
|
||||
TABLE_SIZE, usage,
|
||||
TABLE_STRING, state,
|
||||
TABLE_SET_COLOR, !streq(state, "detached") ? ansi_highlight_green() : NULL);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (!table_isempty(table)) {
|
||||
r = table_set_sort(table, (size_t) 0);
|
||||
if (r < 0)
|
||||
return table_log_sort_error(r);
|
||||
|
||||
table_set_header(table, arg_legend);
|
||||
|
||||
r = table_print_or_warn(table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_legend) {
|
||||
if (table_isempty(table))
|
||||
printf("No images.\n");
|
||||
else
|
||||
printf("\n%zu images listed.\n", table_get_rows(table) - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_remove_image(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
int r, i;
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "RemoveImage");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "s", argv[i]);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* This is a slow operation, hence turn off any method call timeouts */
|
||||
r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_read_only_image(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_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
int b = true, r;
|
||||
|
||||
if (argc > 2) {
|
||||
b = parse_boolean(argv[2]);
|
||||
if (b < 0)
|
||||
return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
r = bus_call_method(bus, bus_portable_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_set_limit(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_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
uint64_t limit;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
|
||||
limit = UINT64_MAX;
|
||||
else {
|
||||
r = parse_size(argv[argc-1], 1024, &limit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
/* With two arguments changes the quota limit of the specified image */
|
||||
r = bus_call_method(bus, bus_portable_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit);
|
||||
else
|
||||
/* With one argument changes the pool quota limit */
|
||||
r = bus_call_method(bus, bus_portable_mgr, "SetPoolLimit", &error, NULL, "t", limit);
|
||||
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
VERB(verb_reattach_image, "reattach", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0,
|
||||
"Reattach the specified portable service image");
|
||||
static int verb_reattach_image(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return attach_reattach_image(argc, argv, strv_isempty(arg_extension_images) && !arg_force ? "ReattachImage" : "ReattachImageWithExtensions");
|
||||
}
|
||||
|
||||
VERB(verb_is_image_attached, "is-attached", "NAME|PATH", 2, 2, 0,
|
||||
"Query if portable service image is attached");
|
||||
static int verb_is_image_attached(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
@@ -1230,6 +1156,100 @@ static int verb_is_image_attached(int argc, char *argv[], uintptr_t _data, void
|
||||
return streq(state, "detached");
|
||||
}
|
||||
|
||||
VERB(verb_read_only_image, "read-only", "NAME|PATH [BOOL]", 2, 3, 0,
|
||||
"Mark or unmark portable service image read-only");
|
||||
static int verb_read_only_image(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_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
int b = true, r;
|
||||
|
||||
if (argc > 2) {
|
||||
b = parse_boolean(argv[2]);
|
||||
if (b < 0)
|
||||
return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
|
||||
}
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
r = bus_call_method(bus, bus_portable_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB(verb_remove_image, "remove", "NAME|PATH…", 2, VERB_ANY, 0,
|
||||
"Remove a portable service image");
|
||||
static int verb_remove_image(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
int r, i;
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "RemoveImage");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "s", argv[i]);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
/* This is a slow operation, hence turn off any method call timeouts */
|
||||
r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB(verb_set_limit, "set-limit", "[NAME|PATH] LIMIT", 2, 3, 0,
|
||||
"Set image or pool size limit (disk quota)");
|
||||
static int verb_set_limit(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_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
uint64_t limit;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
|
||||
limit = UINT64_MAX;
|
||||
else {
|
||||
r = parse_size(argv[argc-1], 1024, &limit);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
/* With two arguments changes the quota limit of the specified image */
|
||||
r = bus_call_method(bus, bus_portable_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit);
|
||||
else
|
||||
/* With one argument changes the pool quota limit */
|
||||
r = bus_call_method(bus, bus_portable_mgr, "SetPoolLimit", &error, NULL, "t", limit);
|
||||
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_profiles(void) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
@@ -1256,176 +1276,104 @@ static int dump_profiles(void) {
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *verbs = NULL, *options = NULL;
|
||||
int r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
r = terminal_urlify_man("portablectl", "1", &link);
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND ...\n\n"
|
||||
"%sAttach or detach portable services from the local system.%s\n"
|
||||
"\nCommands:\n"
|
||||
" list List available portable service images\n"
|
||||
" attach NAME|PATH [PREFIX...]\n"
|
||||
" Attach the specified portable service image\n"
|
||||
" detach NAME|PATH [PREFIX...]\n"
|
||||
" Detach the specified portable service image\n"
|
||||
" reattach NAME|PATH [PREFIX...]\n"
|
||||
" Reattach the specified portable service image\n"
|
||||
" inspect NAME|PATH [PREFIX...]\n"
|
||||
" Show details of specified portable service image\n"
|
||||
" is-attached NAME|PATH Query if portable service image is attached\n"
|
||||
" read-only NAME|PATH [BOOL] Mark or unmark portable service image read-only\n"
|
||||
" remove NAME|PATH... Remove a portable service image\n"
|
||||
" set-limit [NAME|PATH] Set image or pool size limit (disk quota)\n"
|
||||
"\nOptions:\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"
|
||||
" -q --quiet Suppress informational messages\n"
|
||||
" -p --profile=PROFILE Pick security profile for portable service\n"
|
||||
" --copy=copy|auto|symlink|mixed\n"
|
||||
" Pick copying or symlinking of resources\n"
|
||||
" --runtime Attach portable service until next reboot only\n"
|
||||
" --no-reload Don't reload the system and service manager\n"
|
||||
" --cat When inspecting include unit and os-release file\n"
|
||||
" contents\n"
|
||||
" --enable Immediately enable/disable the portable service\n"
|
||||
" after attach/detach\n"
|
||||
" --now Immediately start/stop the portable service after\n"
|
||||
" attach/before detach\n"
|
||||
" --no-block Don't block waiting for attach --now to complete\n"
|
||||
" --extension=PATH Extend the image with an overlay\n"
|
||||
" --force Skip 'already active' check when attaching or\n"
|
||||
" detaching an image (with extensions)\n"
|
||||
" --clean When detaching, also remove configuration, state,\n"
|
||||
" cache, logs or runtime data of the portable\n"
|
||||
" service(s)\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, verbs, options);
|
||||
|
||||
help_cmdline("[OPTIONS…] COMMAND …");
|
||||
help_abstract("Attach or detach portable services in the local system.");
|
||||
|
||||
help_section("Commands");
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("portablectl", "1");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return help();
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_NO_PAGER,
|
||||
ARG_NO_LEGEND,
|
||||
ARG_NO_ASK_PASSWORD,
|
||||
ARG_COPY,
|
||||
ARG_RUNTIME,
|
||||
ARG_NO_RELOAD,
|
||||
ARG_CAT,
|
||||
ARG_ENABLE,
|
||||
ARG_NOW,
|
||||
ARG_NO_BLOCK,
|
||||
ARG_EXTENSION,
|
||||
ARG_FORCE,
|
||||
ARG_CLEAN,
|
||||
ARG_USER,
|
||||
ARG_SYSTEM,
|
||||
};
|
||||
|
||||
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' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "profile", required_argument, NULL, 'p' },
|
||||
{ "copy", required_argument, NULL, ARG_COPY },
|
||||
{ "runtime", no_argument, NULL, ARG_RUNTIME },
|
||||
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
||||
{ "cat", no_argument, NULL, ARG_CAT },
|
||||
{ "enable", no_argument, NULL, ARG_ENABLE },
|
||||
{ "now", no_argument, NULL, ARG_NOW },
|
||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||
{ "extension", required_argument, NULL, ARG_EXTENSION },
|
||||
{ "force", no_argument, NULL, ARG_FORCE },
|
||||
{ "clean", no_argument, NULL, ARG_CLEAN },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{}
|
||||
};
|
||||
|
||||
int r, c;
|
||||
VERB_COMMON_HELP_HIDDEN(help);
|
||||
|
||||
static int parse_argv(int argc, char *argv[], char ***remaining_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(remaining_args);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hH:M:qp:", options, NULL)) >= 0)
|
||||
OptionParser opts = { argc, argv };
|
||||
int r;
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
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 = opts.arg;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
r = parse_machine_argument(optarg, &arg_host, &arg_transport);
|
||||
OPTION_COMMON_MACHINE:
|
||||
r = parse_machine_argument(opts.arg, &arg_host, &arg_transport);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
OPTION('q', "quiet", NULL, "Suppress informational messages"):
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (streq(optarg, "help"))
|
||||
OPTION('p', "profile", "PROFILE",
|
||||
"Pick security profile for portable service"):
|
||||
if (streq(opts.arg, "help"))
|
||||
return dump_profiles();
|
||||
|
||||
if (!filename_is_valid(optarg))
|
||||
if (!filename_is_valid(opts.arg))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unit profile name not valid: %s", optarg);
|
||||
"Unit profile name not valid: %s", opts.arg);
|
||||
|
||||
arg_profile = optarg;
|
||||
arg_profile = opts.arg;
|
||||
break;
|
||||
|
||||
case ARG_COPY:
|
||||
if (streq(optarg, "auto"))
|
||||
OPTION_LONG("copy", "MODE",
|
||||
"Pick copying or symlinking of resources "
|
||||
"(copy, auto, symlink, mixed)"):
|
||||
if (streq(opts.arg, "auto"))
|
||||
arg_copy_mode = NULL;
|
||||
else if (STR_IN_SET(optarg, "copy", "symlink", "mixed"))
|
||||
arg_copy_mode = optarg;
|
||||
else if (streq(optarg, "help")) {
|
||||
else if (STR_IN_SET(opts.arg, "copy", "symlink", "mixed"))
|
||||
arg_copy_mode = opts.arg;
|
||||
else if (streq(opts.arg, "help")) {
|
||||
puts("auto\n"
|
||||
"copy\n"
|
||||
"symlink\n"
|
||||
@@ -1433,90 +1381,81 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return 0;
|
||||
} else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to parse --copy= argument: %s", optarg);
|
||||
|
||||
"Failed to parse --copy= argument: %s", opts.arg);
|
||||
break;
|
||||
|
||||
case ARG_RUNTIME:
|
||||
OPTION_LONG("runtime", NULL,
|
||||
"Attach portable service until next reboot only"):
|
||||
arg_runtime = true;
|
||||
break;
|
||||
|
||||
case ARG_NO_RELOAD:
|
||||
OPTION_LONG("no-reload", NULL,
|
||||
"Don't reload the system and service manager"):
|
||||
arg_reload = false;
|
||||
break;
|
||||
|
||||
case ARG_CAT:
|
||||
OPTION_LONG("cat", NULL,
|
||||
"When inspecting include unit and os-release file contents"):
|
||||
arg_cat = true;
|
||||
break;
|
||||
|
||||
case ARG_ENABLE:
|
||||
OPTION_LONG("enable", NULL,
|
||||
"Immediately enable/disable the portable service after attach/detach"):
|
||||
arg_enable = true;
|
||||
break;
|
||||
|
||||
case ARG_NOW:
|
||||
OPTION_LONG("now", NULL,
|
||||
"Immediately start/stop the portable service after attach/before detach"):
|
||||
arg_now = true;
|
||||
break;
|
||||
|
||||
case ARG_NO_BLOCK:
|
||||
OPTION_LONG("no-block", NULL,
|
||||
"Don't block waiting for attach --now to complete"):
|
||||
arg_no_block = true;
|
||||
break;
|
||||
|
||||
case ARG_EXTENSION:
|
||||
r = strv_extend(&arg_extension_images, optarg);
|
||||
OPTION_LONG("extension", "PATH",
|
||||
"Extend the image with an overlay"):
|
||||
r = strv_extend(&arg_extension_images, opts.arg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
|
||||
case ARG_FORCE:
|
||||
OPTION_LONG("force", NULL,
|
||||
"Skip 'already active' check when attaching or detaching an image (with extensions)"):
|
||||
arg_force = true;
|
||||
break;
|
||||
|
||||
case ARG_CLEAN:
|
||||
OPTION_LONG("clean", NULL,
|
||||
"When detaching, also remove configuration, state, "
|
||||
"cache, logs or runtime data of the portable service(s)"):
|
||||
arg_clean = true;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
OPTION_LONG("user", NULL, /* help= */ NULL):
|
||||
arg_runtime_scope = RUNTIME_SCOPE_USER;
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
OPTION_LONG("system", NULL, /* help= */ NULL):
|
||||
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
*remaining_args = option_parser_get_args(&opts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
|
||||
{ "list", VERB_ANY, 1, VERB_DEFAULT, verb_list_images },
|
||||
{ "attach", 2, VERB_ANY, 0, verb_attach_image },
|
||||
{ "detach", 2, VERB_ANY, 0, verb_detach_image },
|
||||
{ "inspect", 2, VERB_ANY, 0, verb_inspect_image },
|
||||
{ "is-attached", 2, 2, 0, verb_is_image_attached },
|
||||
{ "read-only", 2, 3, 0, verb_read_only_image },
|
||||
{ "remove", 2, VERB_ANY, 0, verb_remove_image },
|
||||
{ "set-limit", 3, 3, 0, verb_set_limit },
|
||||
{ "reattach", 2, VERB_ANY, 0, verb_reattach_image },
|
||||
{}
|
||||
};
|
||||
|
||||
char **args = NULL;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, NULL);
|
||||
return dispatch_verb_with_args(args, NULL);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ansi-color.h"
|
||||
#include "build.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "pretty-print.h"
|
||||
#include "options.h"
|
||||
#include "report-cgroup.h"
|
||||
#include "varlink-io.systemd.Metrics.h"
|
||||
#include "varlink-util.h"
|
||||
@@ -46,63 +45,44 @@ static int vl_server(void) {
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *url = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-report-cgroup", "8", &url);
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
printf("%s [OPTIONS...]\n"
|
||||
"\n%sReport cgroup metrics.%s\n"
|
||||
"\n%sOptions:%s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
url);
|
||||
help_cmdline("[OPTIONS...]");
|
||||
help_abstract("Report cgroup metrics.");
|
||||
|
||||
help_section("Options");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-report-cgroup", "8");
|
||||
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, r;
|
||||
int r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
OptionParser opts = { argc, argv };
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
if (option_parser_get_n_args(&opts) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"This program takes no arguments.");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user