| /* 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"); |
| } |