summaryrefslogtreecommitdiffstats
path: root/src/systemctl/systemctl-compat-halt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/systemctl/systemctl-compat-halt.c')
-rw-r--r--src/systemctl/systemctl-compat-halt.c203
1 files changed, 203 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..4f6e304
--- /dev/null
+++ b/src/systemctl/systemctl-compat-halt.c
@@ -0,0 +1,203 @@
+/* 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-start-unit.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();
+
+ /* 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);
+
+ 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);
+
+ /* called in sysvinit system as last command in shutdown/reboot so this is always forceful */
+ 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();
+ }
+
+ 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;
+
+ if (arg_force == 0) {
+ /* always try logind first */
+ if (arg_when > 0)
+ r = logind_schedule_shutdown(arg_action);
+ else {
+ r = logind_check_inhibitors(arg_action);
+ if (r < 0)
+ return r;
+
+ r = logind_reboot(arg_action);
+ }
+ if (r >= 0)
+ return r;
+ if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ /* Requested operation requires auth, 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)
+ return start_with_fallback();
+ }
+
+ if (geteuid() != 0) {
+ (void) must_be_root();
+ return -EPERM;
+ }
+
+ 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 %s: %m", action_table[arg_action].verb);
+}