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:
+12
-8
@@ -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>
|
||||
|
||||
@@ -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
@@ -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];
|
||||
|
||||
@@ -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. */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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.
Reference in New Issue
Block a user