Convert systemctl to option and verb macros (#42088)

This one was non-trivial, so it'd benefit from a close review.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-05-14 11:33:09 +02:00
committed by GitHub
14 changed files with 1016 additions and 1133 deletions
+12 -8
View File
@@ -1988,7 +1988,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<para>The argument is a comma-separated list of unit types such as <option>service</option> and
<option>socket</option>. When units are listed with <command>list-units</command>,
<command>list-dependencies</command>, <command>show</command>, or <command>status</command>,
only units of the specified types will be shown. By default, units of all types are shown.</para>
only units of the specified types will be shown. By default, units of all types are shown.
Use <option>--type=</option> to reset the filter.</para>
<para>As a special case, if one of the arguments is <option>help</option>, a list of allowed values
will be printed and the program will exit.</para>
@@ -2000,9 +2001,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<listitem>
<para>The argument is a comma-separated list of unit LOAD, SUB, or ACTIVE states. When listing
units with <command>list-units</command>, <command>list-dependencies</command>, <command>show</command>
or <command>status</command>, show only those in the specified states. Use <option>--state=failed</option>
or <option>--failed</option> to show only failed units.</para>
units with <command>list-units</command>, <command>list-dependencies</command>,
<command>show</command> or <command>status</command>, show only those in the specified states. Use
<option>--state=failed</option> or <option>--failed</option> to show only failed units. Use
<option>--state=</option> to reset the filter.</para>
<para>As a special case, if one of the arguments is <option>help</option>, a list of allowed values
will be printed and the program will exit.</para>
@@ -2576,10 +2578,12 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<constant>all</constant> as a shortcut for specifying all six resource types. If this option is not
specified defaults to the combination of <constant>cache</constant>, <constant>runtime</constant>
and <constant>fdstore</constant>, i.e. the three kinds of resources that are generally considered
to be redundant and can be reconstructed on next invocation. Note that the explicit removal of the
<constant>fdstore</constant> resource type is only useful if the
<varname>FileDescriptorStorePreserve=</varname> option is enabled, since the file descriptor store
is otherwise cleaned automatically when the unit is stopped.</para>
to be redundant and can be reconstructed on next invocation. The empty argument can be used to
reset to this default.</para>
<para>Note that the explicit removal of the <constant>fdstore</constant> resource type is only
useful if the <varname>FileDescriptorStorePreserve=</varname> option is enabled, since the file
descriptor store is otherwise cleaned automatically when the unit is stopped.</para>
<xi:include href="version-info.xml" xpointer="v243"/>
</listitem>
+1
View File
@@ -72,6 +72,7 @@
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
#define _alias_(x) typeof(x) __attribute__((alias(#x)))
#define _align_(x) __attribute__((__aligned__(x)))
#define _alignas_(x) __attribute__((__aligned__(alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void *))))
+35 -20
View File
@@ -39,6 +39,7 @@ static void shift_arg(char* argv[], int target, int source) {
}
static int partial_match_error(
const OptionParser *state,
const Option options[],
const Option options_end[],
const char *optname,
@@ -57,15 +58,17 @@ static int partial_match_error(
r = strv_extendf(&s, "--%s", option->long_code);
if (r < 0)
return log_error_errno(r, "Failed to format message: %m");
return log_full_errno(LOG_ERR + state->log_level_shift, r,
"Failed to format message: %m");
}
assert(strv_length(s) == n_partial_matches);
_cleanup_free_ char *p = strv_join_full(s, ", ", /* prefix= */ NULL, /* escape_separator= */ false);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' is ambiguous; possibilities: %s",
program_invocation_short_name, optname, strnull(p));
return log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' is ambiguous; possibilities: %s",
program_invocation_short_name, optname, strnull(p));
}
int option_parse(
@@ -84,9 +87,14 @@ int option_parse(
case OPTION_PARSER_INIT: {
assert(state->mode >= 0 && state->mode < _OPTION_PARSER_MODE_MAX);
/* Make sure the shifted log level is between LOG_EMERG/0 and LOG_DEBUG/7. */
assert(state->log_level_shift >= LOG_EMERG - LOG_ERR &&
state->log_level_shift <= LOG_DEBUG - LOG_ERR);
if (state->argc < 1) {
r = log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "argv cannot be empty");
r = log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(EUCLEAN),
"argv cannot be empty");
goto fail;
}
@@ -134,7 +142,9 @@ int option_parse(
goto done;
case OPTION_PARSER_FAILED:
return log_error_errno(SYNTHETIC_ERRNO(ESTALE), "Option parser failed before, refusing.");
return log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(ESTALE),
"Option parser failed before, refusing.");
default:
assert_not_reached();
@@ -203,7 +213,7 @@ int option_parse(
if (eq) {
optname = _optname = strndup(state->argv[state->optind], eq - state->argv[state->optind]);
if (!_optname) {
r = log_oom();
r = log_oom_full(LOG_ERR + state->log_level_shift);
goto fail;
}
@@ -219,13 +229,15 @@ int option_parse(
for (option = state->namespace_start;; option++) {
if (option >= state->namespace_end) {
if (n_partial_matches == 0) {
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: unrecognized option '%s'",
program_invocation_short_name, optname);
r = log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(EINVAL),
"%s: unrecognized option '%s'",
program_invocation_short_name, optname);
goto fail;
}
if (n_partial_matches > 1) {
r = partial_match_error(
state,
state->namespace_start,
state->namespace_end,
optname,
@@ -261,16 +273,17 @@ int option_parse(
char optchar = state->argv[state->optind][state->short_option_offset];
if (asprintf(&_optname, "-%c", optchar) < 0) {
r = log_oom();
r = log_oom_full(LOG_ERR + state->log_level_shift);
goto fail;
}
optname = _optname;
for (option = state->namespace_start;; option++) {
if (option >= state->namespace_end) {
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: unrecognized option '%s'",
program_invocation_short_name, optname);
r = log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(EINVAL),
"%s: unrecognized option '%s'",
program_invocation_short_name, optname);
goto fail;
}
@@ -295,16 +308,18 @@ int option_parse(
assert(option);
if (!handling_positional_arg && optval && !option_takes_arg(option)) {
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' doesn't allow an argument",
program_invocation_short_name, optname);
r = log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' doesn't allow an argument",
program_invocation_short_name, optname);
goto fail;
}
if (!handling_positional_arg && !optval && option_arg_required(option)) {
if (!state->argv[state->optind + 1]) {
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' requires an argument",
program_invocation_short_name, optname);
r = log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' requires an argument",
program_invocation_short_name, optname);
goto fail;
}
optval = state->argv[state->optind + 1];
+3
View File
@@ -200,6 +200,9 @@ typedef struct OptionParser {
char **argv; /* The argv array, possibly reordered. */
OptionParserMode mode;
const char *namespace; /* The namespace, may be NULL. */
int log_level_shift; /* The log level difference from the default of LOG_ERR.
* Allowed values are -3..4.
* Use 4 == LOG_DEBUG - LOG_ERR to log at debug level. */
const Option *namespace_start, *namespace_end; /* The range of options that are part of our namespace. */
+45 -5
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <stdio.h>
#include "bus-util.h"
@@ -15,6 +14,48 @@
#include "systemctl.h"
#include "systemctl-util.h"
static int verb_noop(int argc, char *argv[], uintptr_t _data, void *userdata) { return 0; }
_alias_(verb_noop)
verb_add_dependency,
verb_bind,
verb_cancel,
verb_cat,
verb_clean_or_freeze,
verb_edit,
verb_enable,
verb_get_default,
verb_import_environment,
verb_is_active,
verb_is_enabled,
verb_is_failed,
verb_is_system_running,
verb_kill,
verb_list_automounts,
verb_list_dependencies,
verb_list_jobs,
verb_list_machines,
verb_list_paths,
verb_list_sockets,
verb_list_timers,
verb_list_unit_files,
verb_list_units,
verb_log_setting,
verb_mount_image,
verb_preset_all,
verb_reset_failed,
verb_service_log_setting,
verb_service_watchdogs,
verb_set_default,
verb_set_environment,
verb_set_property,
verb_show,
verb_show_environment,
verb_start_special,
verb_start_system_special,
verb_switch_root,
verb_trivial_method,
verb_whoami;
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_strv_free_ char **argv = NULL;
_cleanup_close_ int orig_stdout_fd = -EBADF;
@@ -46,17 +87,16 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
log_warning_errno(orig_stdout_fd, "Failed to duplicate fd 1: %m");
else
assert_se(freopen("/dev/null", "w", stdout));
opterr = 0; /* do not print errors */
}
/* We need to reset some global state manually here since libfuzzer feeds a single process with
* multiple inputs, so we might carry over state from previous invocations that can trigger
* certain asserts. */
optind = 0; /* this tells the getopt machinery to reinitialize */
arg_transport = BUS_TRANSPORT_LOCAL;
r = systemctl_dispatch_parse_argv(strv_length(argv), argv);
r = systemctl_dispatch_parse_argv(strv_length(argv), argv,
/* log_level_shift= */ LOG_DEBUG - LOG_ERR,
/* remaining_args= */ NULL);
if (r < 0)
log_error_errno(r, "Failed to parse args: %m");
else
+56 -75
View File
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include "sd-daemon.h"
#include "alloc-util.h"
#include "ansi-color.h"
#include "format-table.h"
#include "help-util.h"
#include "log.h"
#include "pretty-print.h"
#include "options.h"
#include "process-util.h"
#include "reboot-util.h"
#include "systemctl.h"
@@ -17,126 +17,107 @@
#include "utmp-wtmp.h"
static int halt_help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("halt", "8", &link);
r = option_parser_get_help_table_ns("halt", &options);
if (r < 0)
return log_oom();
return r;
/* Note: if you are tempted to add new command line switches here, please do not. Let this
* compatibility command rest in peace. Its interface is not even owned by us as much as it is by
* sysvinit. If you add something new, add it to "systemctl halt", "systemctl reboot", "systemctl
* poweroff" instead. */
printf("%s [OPTIONS...]%s\n"
"\n%s%s the system.%s\n"
"\nOptions:\n"
" --help Show this help\n"
" --halt Halt the machine\n"
" -p --poweroff Switch off the machine\n"
" --reboot Reboot the machine\n"
" -f --force Force immediate halt/power-off/reboot\n"
" -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
" -d --no-wtmp Don't write wtmp record\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
"\n%sThis is a compatibility interface, please use the more powerful 'systemctl %s' command instead.%s\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
arg_action == ACTION_REBOOT ? " [ARG]" : "",
ansi_highlight(), arg_action == ACTION_REBOOT ? "Reboot" :
arg_action == ACTION_POWEROFF ? "Power off" :
"Halt", ansi_normal(),
ansi_highlight_red(), arg_action == ACTION_REBOOT ? "reboot" :
arg_action == ACTION_POWEROFF ? "poweroff" :
"halt", ansi_normal(),
link);
help_cmdline(arg_action == ACTION_REBOOT ? "[OPTIONS…] [ARG]" : "[OPTIONS…]");
help_abstract(arg_action == ACTION_REBOOT ? "Reboot the system." :
arg_action == ACTION_POWEROFF ? "Power off the system." :
"Halt the system.");
help_section("Options");
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\n%sThis is a compatibility interface, please use the more powerful 'systemctl %s' command instead.%s\n",
ansi_highlight_red(),
arg_action == ACTION_REBOOT ? "reboot" :
arg_action == ACTION_POWEROFF ? "poweroff" :
"halt",
ansi_normal());
help_man_page_reference("halt", "8");
return 0;
}
int halt_parse_argv(int argc, char *argv[]) {
enum {
ARG_HELP = 0x100,
ARG_HALT,
ARG_REBOOT,
ARG_NO_WALL
};
static const struct option options[] = {
{ "help", no_argument, NULL, ARG_HELP },
{ "halt", no_argument, NULL, ARG_HALT },
{ "poweroff", no_argument, NULL, 'p' },
{ "reboot", no_argument, NULL, ARG_REBOOT },
{ "force", no_argument, NULL, 'f' },
{ "wtmp-only", no_argument, NULL, 'w' },
{ "no-wtmp", no_argument, NULL, 'd' },
{ "no-sync", no_argument, NULL, 'n' },
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
{}
};
int c, r;
int halt_parse_argv(int argc, char *argv[], int log_level_shift) {
int r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0)
OptionParser opts = {
argc, argv,
.namespace = "halt",
.log_level_shift = log_level_shift,
};
FOREACH_OPTION_OR_RETURN(c, &opts)
switch (c) {
case ARG_HELP:
OPTION_NAMESPACE("halt"): {}
OPTION_LONG("help", NULL, "Show this help"):
return halt_help();
case ARG_HALT:
OPTION_LONG("halt", NULL, "Halt the machine"):
arg_action = ACTION_HALT;
break;
case 'p':
OPTION('p', "poweroff", NULL, "Switch off the machine"):
if (arg_action != ACTION_REBOOT)
arg_action = ACTION_POWEROFF;
break;
case ARG_REBOOT:
OPTION_LONG("reboot", NULL, "Reboot the machine"):
arg_action = ACTION_REBOOT;
break;
case 'f':
OPTION('f', "force", NULL, "Force immediate halt/power-off/reboot"):
arg_force = 2;
break;
case 'w':
OPTION('w', "wtmp-only", NULL, "Don't halt/power-off/reboot, just write wtmp record"):
arg_dry_run = true;
break;
case 'd':
OPTION('d', "no-wtmp", NULL, "Don't write wtmp record"):
arg_no_wtmp = true;
break;
case 'n':
arg_no_sync = true;
break;
case ARG_NO_WALL:
OPTION_LONG("no-wall", NULL, "Don't send wall message before halt/power-off/reboot"):
arg_no_wall = true;
break;
case 'i':
case 'h':
/* Compatibility nops */
/* Hidden compat-only options. */
OPTION('n', "no-sync", NULL, /* help= */ NULL):
arg_no_sync = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
OPTION_SHORT('i', NULL, /* help= */ NULL): {}
OPTION_SHORT('h', NULL, /* help= */ NULL):
/* Compatibility nops */
break;
}
if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL, false);
char **args = option_parser_get_args(&opts);
size_t n_args = option_parser_get_n_args(&opts);
if (arg_action == ACTION_REBOOT && n_args <= 1) {
r = update_reboot_parameter_and_warn(args[0], false);
if (r < 0)
return r;
} else if (optind < argc)
} else if (n_args > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Too many arguments.");
+1 -2
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
int halt_parse_argv(int argc, char *argv[]);
int halt_parse_argv(int argc, char *argv[], int log_level_shift);
int halt_main(void);
+60 -83
View File
@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include "alloc-util.h"
#include "ansi-color.h"
#include "format-table.h"
#include "help-util.h"
#include "log.h"
#include "options.h"
#include "parse-util.h"
#include "pretty-print.h"
#include "reboot-util.h"
#include "string-util.h"
#include "strv.h"
@@ -14,38 +14,31 @@
#include "time-util.h"
static int shutdown_help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("shutdown", "8", &link);
r = option_parser_get_help_table_ns("shutdown", &options);
if (r < 0)
return log_oom();
return r;
/* Note: if you are tempted to add new command line switches here, please do not. Let this
* compatibility command rest in peace. Its interface is not even owned by us as much as it is by
* sysvinit. If you add something new, add it to "systemctl halt", "systemctl reboot", "systemctl
* poweroff" instead. */
printf("%s [OPTIONS...] [TIME] [WALL...]\n"
"\n%sShut down the system.%s\n"
"\nOptions:\n"
" --help Show this help\n"
" -H --halt Halt the machine\n"
" -P --poweroff Power-off the machine\n"
" -r --reboot Reboot the machine\n"
" -h Equivalent to --poweroff, overridden by --halt\n"
" -k Don't halt/power-off/reboot, just send warnings\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
" -c Cancel a pending shutdown\n"
" --show Show pending shutdown\n"
"\n%sThis is a compatibility interface, please use the more powerful 'systemctl halt',\n"
"'systemctl poweroff', 'systemctl reboot' commands instead.%s\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(), ansi_normal(),
ansi_highlight_red(), ansi_normal(),
link);
help_cmdline("[OPTIONS…] [TIME] [WALL…]");
help_abstract("Shut down the system.");
help_section("Options");
r = table_print_or_warn(options);
if (r < 0)
return r;
printf("\n%sThis is a compatibility interface, please use the more powerful 'systemctl halt',\n"
"'systemctl poweroff', 'systemctl reboot' commands instead.%s\n",
ansi_highlight_red(), ansi_normal());
help_man_page_reference("shutdown", "8");
return 0;
}
@@ -123,105 +116,91 @@ static int parse_shutdown_time_spec(const char *t, usec_t *ret) {
return 0;
}
int shutdown_parse_argv(int argc, char *argv[]) {
enum {
ARG_HELP = 0x100,
ARG_NO_WALL,
ARG_SHOW
};
static const struct option options[] = {
{ "help", no_argument, NULL, ARG_HELP },
{ "halt", no_argument, NULL, 'H' },
{ "poweroff", no_argument, NULL, 'P' },
{ "reboot", no_argument, NULL, 'r' },
{ "kexec", no_argument, NULL, 'K' }, /* not documented extension */
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
{ "show", no_argument, NULL, ARG_SHOW },
{}
};
char **wall = NULL;
int c, r;
int shutdown_parse_argv(int argc, char *argv[], int log_level_shift) {
int r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "HPrhkKat:fFc", options, NULL)) >= 0)
OptionParser opts = {
argc, argv,
.namespace = "shutdown",
.log_level_shift = log_level_shift,
};
FOREACH_OPTION_OR_RETURN(c, &opts)
switch (c) {
case ARG_HELP:
OPTION_NAMESPACE("shutdown"): {}
OPTION_LONG("help", NULL, "Show this help"):
return shutdown_help();
case 'H':
OPTION('H', "halt", NULL, "Halt the machine"):
arg_action = ACTION_HALT;
break;
case 'P':
OPTION('P', "poweroff", NULL, "Power-off the machine"):
arg_action = ACTION_POWEROFF;
break;
case 'r':
OPTION('r', "reboot", NULL, "Reboot the machine"):
if (kexec_loaded())
arg_action = ACTION_KEXEC;
else
arg_action = ACTION_REBOOT;
break;
case 'K':
arg_action = ACTION_KEXEC;
break;
case 'h':
OPTION_SHORT('h', NULL, "Equivalent to --poweroff, overridden by --halt"):
if (arg_action != ACTION_HALT)
arg_action = ACTION_POWEROFF;
break;
case 'k':
OPTION_SHORT('k', NULL, "Don't halt/power-off/reboot, just send warnings"):
arg_dry_run = true;
break;
case ARG_NO_WALL:
OPTION_LONG("no-wall", NULL, "Don't send wall message before halt/power-off/reboot"):
arg_no_wall = true;
break;
case 'a':
case 't': /* Note that we also ignore any passed argument to -t, not just the -t itself */
case 'f':
case 'F':
/* Compatibility nops */
break;
case 'c':
OPTION_SHORT('c', NULL, "Cancel a pending shutdown"):
arg_action = ACTION_CANCEL_SHUTDOWN;
break;
case ARG_SHOW:
OPTION_LONG("show", NULL, "Show pending shutdown"):
arg_action = ACTION_SHOW_SHUTDOWN;
break;
case '?':
return -EINVAL;
/* Hidden compat options. */
OPTION('K', "kexec", NULL, /* help= */ NULL):
arg_action = ACTION_KEXEC;
break;
default:
assert_not_reached();
OPTION_SHORT('a', NULL, /* help= */ NULL): {} /* compatibility noops */
OPTION_SHORT('f', NULL, /* help= */ NULL): {}
OPTION_SHORT('F', NULL, /* help= */ NULL): {}
OPTION_SHORT('t', "ARG", /* help= */ NULL):
break;
}
if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
r = parse_shutdown_time_spec(argv[optind], &arg_when);
if (r < 0) {
log_error("Failed to parse time specification: %s", argv[optind]);
return r;
}
char **args = option_parser_get_args(&opts);
size_t n_args = option_parser_get_n_args(&opts);
if (n_args > 0 && arg_action != ACTION_CANCEL_SHUTDOWN) {
r = parse_shutdown_time_spec(args[0], &arg_when);
if (r < 0)
return log_error_errno(r, "Failed to parse time specification: %s", args[0]);
} else
arg_when = USEC_INFINITY; /* logind chooses on server side */
if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN)
char **wall = NULL;
if (n_args > 0 && arg_action == ACTION_CANCEL_SHUTDOWN)
/* No time argument for shutdown cancel */
wall = argv + optind;
else if (argc > optind + 1)
wall = args;
else if (n_args > 1)
/* We skip the time argument */
wall = argv + optind + 1;
wall = args + 1;
if (wall) {
char **copy = strv_copy(wall);
@@ -230,7 +209,5 @@ int shutdown_parse_argv(int argc, char *argv[]) {
strv_free_and_replace(arg_wall, copy);
}
optind = argc;
return 1;
}
+1 -1
View File
@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
int shutdown_parse_argv(int argc, char *argv[]);
int shutdown_parse_argv(int argc, char *argv[], int log_level_shift);
+5 -131
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <locale.h>
#include "dissect-image.h"
@@ -11,148 +10,23 @@
#include "main-func.h"
#include "mount-util.h"
#include "stat-util.h"
#include "systemctl.h"
#include "systemctl-add-dependency.h"
#include "systemctl-cancel-job.h"
#include "systemctl-clean-or-freeze.h"
#include "strv.h"
#include "systemctl-compat-halt.h"
#include "systemctl-daemon-reload.h"
#include "systemctl-edit.h"
#include "systemctl-enable.h"
#include "systemctl-is-active.h"
#include "systemctl-is-enabled.h"
#include "systemctl-is-system-running.h"
#include "systemctl-kill.h"
#include "systemctl-list-dependencies.h"
#include "systemctl-list-jobs.h"
#include "systemctl-list-machines.h"
#include "systemctl-list-unit-files.h"
#include "systemctl-list-units.h"
#include "systemctl-log-setting.h"
#include "systemctl-logind.h"
#include "systemctl-mount.h"
#include "systemctl-preset-all.h"
#include "systemctl-reset-failed.h"
#include "systemctl-service-watchdogs.h"
#include "systemctl-set-default.h"
#include "systemctl-set-environment.h"
#include "systemctl-set-property.h"
#include "systemctl-show.h"
#include "systemctl-start-special.h"
#include "systemctl-start-unit.h"
#include "systemctl-switch-root.h"
#include "systemctl-trivial-method.h"
#include "systemctl-util.h"
#include "systemctl-whoami.h"
#include "verbs.h"
#include "systemctl.h"
#include "virt.h"
static int systemctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, verb_list_units },
{ "list-unit-files", VERB_ANY, VERB_ANY, 0, verb_list_unit_files },
{ "list-automounts", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_automounts },
{ "list-paths", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_paths },
{ "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_sockets },
{ "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_timers },
{ "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_jobs },
{ "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_machines },
{ "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_trivial_method },
{ "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_cancel },
{ "start", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "stop", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "condstop", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with ALTLinux */
{ "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "enqueue-marked", 1, 1, VERB_ONLINE_ONLY, verb_start },
{ "reload-or-restart", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with systemctl <= 228 */
{ "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with SysV */
{ "condreload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with ALTLinux */
{ "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with RH */
{ "isolate", 2, 2, VERB_ONLINE_ONLY, verb_start },
{ "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_kill },
{ "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_clean_or_freeze },
{ "freeze", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_clean_or_freeze },
{ "thaw", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_clean_or_freeze },
{ "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_is_active },
{ "check", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_is_active }, /* deprecated alias of is-active */
{ "is-failed", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_is_failed },
{ "show", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_show },
{ "cat", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_cat },
{ "status", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_show },
{ "help", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_show },
{ "daemon-reload", 1, 1, VERB_ONLINE_ONLY, verb_daemon_reload },
{ "daemon-reexec", 1, 1, VERB_ONLINE_ONLY, verb_daemon_reload },
{ "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_log_setting },
{ "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_log_setting },
{ "service-log-level", 2, 3, VERB_ONLINE_ONLY, verb_service_log_setting },
{ "service-log-target", 2, 3, VERB_ONLINE_ONLY, verb_service_log_setting },
{ "service-watchdogs", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_service_watchdogs },
{ "show-environment", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_show_environment },
{ "set-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_set_environment },
{ "unset-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_set_environment },
{ "import-environment", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_import_environment },
{ "halt", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "poweroff", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "soft-reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "suspend-then-hibernate",VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "default", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_special },
{ "rescue", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "emergency", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "exit", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_start_special },
{ "reset-failed", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_reset_failed },
{ "enable", 2, VERB_ANY, 0, verb_enable },
{ "disable", 2, VERB_ANY, 0, verb_enable },
{ "is-enabled", 2, VERB_ANY, 0, verb_is_enabled },
{ "reenable", 2, VERB_ANY, 0, verb_enable },
{ "preset", 2, VERB_ANY, 0, verb_enable },
{ "preset-all", VERB_ANY, 1, 0, verb_preset_all },
{ "mask", 2, VERB_ANY, 0, verb_enable },
{ "unmask", 2, VERB_ANY, 0, verb_enable },
{ "link", 2, VERB_ANY, 0, verb_enable },
{ "revert", 2, VERB_ANY, 0, verb_enable },
{ "switch-root", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_switch_root },
{ "list-dependencies", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_dependencies },
{ "set-default", 2, 2, 0, verb_set_default },
{ "get-default", VERB_ANY, 1, 0, verb_get_default },
{ "set-property", 3, VERB_ANY, VERB_ONLINE_ONLY, verb_set_property },
{ "is-system-running", VERB_ANY, 1, 0, verb_is_system_running },
{ "add-wants", 3, VERB_ANY, 0, verb_add_dependency },
{ "add-requires", 3, VERB_ANY, 0, verb_add_dependency },
{ "edit", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_edit },
{ "bind", 3, 4, VERB_ONLINE_ONLY, verb_bind },
{ "mount-image", 4, 5, VERB_ONLINE_ONLY, verb_mount_image },
{ "whoami", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_whoami },
{}
};
const Verb *verb = verbs_find_verb(argv[optind], verbs, verbs + ELEMENTSOF(verbs) - 1);
if (verb && (verb->flags & VERB_ONLINE_ONLY) && arg_root)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Verb '%s' cannot be used with --root= or --image=.",
argv[optind] ?: verb->verb);
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 = STRV_EMPTY;
int r;
setlocale(LC_ALL, "");
log_setup();
r = systemctl_dispatch_parse_argv(argc, argv);
r = systemctl_dispatch_parse_argv(argc, argv, /* log_level_shift= */ 0, &args);
if (r <= 0)
goto finish;
@@ -200,7 +74,7 @@ static int run(int argc, char *argv[]) {
switch (arg_action) {
case ACTION_SYSTEMCTL:
r = systemctl_main(argc, argv);
r = systemctl_main(args);
break;
/* Legacy command aliases set arg_action. They provide some fallbacks, e.g. to tell sysvinit to
+795 -807
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -102,4 +102,5 @@ static inline const char* arg_job_mode(void) {
return _arg_job_mode ?: "replace";
}
int systemctl_dispatch_parse_argv(int argc, char *argv[]);
int systemctl_dispatch_parse_argv(int argc, char *argv[], int log_level_shift, char ***remaining_args);
int systemctl_main(char **args);
Binary file not shown.
Binary file not shown.