diff options
Diffstat (limited to '')
-rw-r--r-- | src/systemctl/systemctl-compat-halt.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c new file mode 100644 index 0000000..8e41bd6 --- /dev/null +++ b/src/systemctl/systemctl-compat-halt.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <getopt.h> +#include <unistd.h> + +#include "sd-daemon.h" + +#include "alloc-util.h" +#include "pretty-print.h" +#include "process-util.h" +#include "reboot-util.h" +#include "systemctl-compat-halt.h" +#include "systemctl-compat-telinit.h" +#include "systemctl-logind.h" +#include "systemctl-util.h" +#include "systemctl.h" +#include "terminal-util.h" +#include "utmp-wtmp.h" + +static int halt_help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("halt", "8", &link); + if (r < 0) + return log_oom(); + + 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" + "\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() + , link + ); + + 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, runlevel; + + assert(argc >= 0); + assert(argv); + + if (utmp_get_runlevel(&runlevel, NULL) >= 0) + if (IN_SET(runlevel, '0', '6')) + arg_force = 2; + + while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) + switch (c) { + + case ARG_HELP: + return halt_help(); + + case ARG_HALT: + arg_action = ACTION_HALT; + break; + + case 'p': + if (arg_action != ACTION_REBOOT) + arg_action = ACTION_POWEROFF; + break; + + case ARG_REBOOT: + arg_action = ACTION_REBOOT; + break; + + case 'f': + arg_force = 2; + break; + + case 'w': + arg_dry_run = true; + break; + + case 'd': + arg_no_wtmp = true; + break; + + case 'n': + arg_no_sync = true; + break; + + case ARG_NO_WALL: + arg_no_wall = true; + break; + + case 'i': + case 'h': + /* Compatibility nops */ + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) { + r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL, false); + if (r < 0) + return r; + } else if (optind < argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many arguments."); + + return 1; +} + +int halt_main(void) { + int r; + + r = logind_check_inhibitors(arg_action); + if (r < 0) + return r; + + /* Delayed shutdown requested, and was successful */ + if (arg_when > 0 && logind_schedule_shutdown() == 0) + return 0; + + /* No delay, or logind failed or is not at all available */ + if (geteuid() != 0) { + if (arg_dry_run || arg_force > 0) { + (void) must_be_root(); + return -EPERM; + } + + /* Try logind if we are a normal user and no special mode applies. Maybe polkit allows us to + * shutdown the machine. */ + if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_HALT)) { + r = logind_reboot(arg_action); + if (r >= 0) + return r; + if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) + /* Requested operation is not supported on the local system or already in + * progress */ + return r; + + /* on all other errors, try low-level operation */ + } + } + + /* In order to minimize the difference between operation with and without logind, we explicitly + * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */ + arg_no_block = true; + + if (!arg_dry_run && !arg_force) + return start_with_fallback(); + + assert(geteuid() == 0); + + if (!arg_no_wtmp) { + if (sd_booted() > 0) + log_debug("Not writing utmp record, assuming that systemd-update-utmp is used."); + else { + r = utmp_put_shutdown(); + if (r < 0) + log_warning_errno(r, "Failed to write utmp record: %m"); + } + } + + if (arg_dry_run) + return 0; + + r = halt_now(arg_action); + return log_error_errno(r, "Failed to reboot: %m"); +} |