]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/systemctl/systemctl-compat-halt.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / systemctl / systemctl-compat-halt.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4 #include <unistd.h>
5
6 #include "sd-daemon.h"
7
8 #include "alloc-util.h"
9 #include "pretty-print.h"
10 #include "process-util.h"
11 #include "reboot-util.h"
12 #include "systemctl-compat-halt.h"
13 #include "systemctl-compat-telinit.h"
14 #include "systemctl-logind.h"
15 #include "systemctl-util.h"
16 #include "systemctl.h"
17 #include "terminal-util.h"
18 #include "utmp-wtmp.h"
19
20 static int halt_help(void) {
21 _cleanup_free_ char *link = NULL;
22 int r;
23
24 r = terminal_urlify_man("halt", "8", &link);
25 if (r < 0)
26 return log_oom();
27
28 printf("%s [OPTIONS...]%s\n"
29 "\n%s%s the system.%s\n"
30 "\nOptions:\n"
31 " --help Show this help\n"
32 " --halt Halt the machine\n"
33 " -p --poweroff Switch off the machine\n"
34 " --reboot Reboot the machine\n"
35 " -f --force Force immediate halt/power-off/reboot\n"
36 " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
37 " -d --no-wtmp Don't write wtmp record\n"
38 " --no-wall Don't send wall message before halt/power-off/reboot\n"
39 "\nSee the %s for details.\n"
40 , program_invocation_short_name
41 , arg_action == ACTION_REBOOT ? " [ARG]" : ""
42 , ansi_highlight()
43 , arg_action == ACTION_REBOOT ? "Reboot" :
44 arg_action == ACTION_POWEROFF ? "Power off" :
45 "Halt"
46 , ansi_normal()
47 , link
48 );
49
50 return 0;
51 }
52
53 int halt_parse_argv(int argc, char *argv[]) {
54 enum {
55 ARG_HELP = 0x100,
56 ARG_HALT,
57 ARG_REBOOT,
58 ARG_NO_WALL
59 };
60
61 static const struct option options[] = {
62 { "help", no_argument, NULL, ARG_HELP },
63 { "halt", no_argument, NULL, ARG_HALT },
64 { "poweroff", no_argument, NULL, 'p' },
65 { "reboot", no_argument, NULL, ARG_REBOOT },
66 { "force", no_argument, NULL, 'f' },
67 { "wtmp-only", no_argument, NULL, 'w' },
68 { "no-wtmp", no_argument, NULL, 'd' },
69 { "no-sync", no_argument, NULL, 'n' },
70 { "no-wall", no_argument, NULL, ARG_NO_WALL },
71 {}
72 };
73
74 int c, r, runlevel;
75
76 assert(argc >= 0);
77 assert(argv);
78
79 if (utmp_get_runlevel(&runlevel, NULL) >= 0)
80 if (IN_SET(runlevel, '0', '6'))
81 arg_force = 2;
82
83 while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0)
84 switch (c) {
85
86 case ARG_HELP:
87 return halt_help();
88
89 case ARG_HALT:
90 arg_action = ACTION_HALT;
91 break;
92
93 case 'p':
94 if (arg_action != ACTION_REBOOT)
95 arg_action = ACTION_POWEROFF;
96 break;
97
98 case ARG_REBOOT:
99 arg_action = ACTION_REBOOT;
100 break;
101
102 case 'f':
103 arg_force = 2;
104 break;
105
106 case 'w':
107 arg_dry_run = true;
108 break;
109
110 case 'd':
111 arg_no_wtmp = true;
112 break;
113
114 case 'n':
115 arg_no_sync = true;
116 break;
117
118 case ARG_NO_WALL:
119 arg_no_wall = true;
120 break;
121
122 case 'i':
123 case 'h':
124 /* Compatibility nops */
125 break;
126
127 case '?':
128 return -EINVAL;
129
130 default:
131 assert_not_reached("Unhandled option");
132 }
133
134 if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
135 r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL, false);
136 if (r < 0)
137 return r;
138 } else if (optind < argc)
139 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
140 "Too many arguments.");
141
142 return 1;
143 }
144
145 int halt_main(void) {
146 int r;
147
148 r = logind_check_inhibitors(arg_action);
149 if (r < 0)
150 return r;
151
152 /* Delayed shutdown requested, and was successful */
153 if (arg_when > 0 && logind_schedule_shutdown() == 0)
154 return 0;
155
156 /* No delay, or logind failed or is not at all available */
157 if (geteuid() != 0) {
158 if (arg_dry_run || arg_force > 0) {
159 (void) must_be_root();
160 return -EPERM;
161 }
162
163 /* Try logind if we are a normal user and no special mode applies. Maybe polkit allows us to
164 * shutdown the machine. */
165 if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_HALT)) {
166 r = logind_reboot(arg_action);
167 if (r >= 0)
168 return r;
169 if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
170 /* Requested operation is not supported on the local system or already in
171 * progress */
172 return r;
173
174 /* on all other errors, try low-level operation */
175 }
176 }
177
178 /* In order to minimize the difference between operation with and without logind, we explicitly
179 * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */
180 arg_no_block = true;
181
182 if (!arg_dry_run && !arg_force)
183 return start_with_fallback();
184
185 assert(geteuid() == 0);
186
187 if (!arg_no_wtmp) {
188 if (sd_booted() > 0)
189 log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
190 else {
191 r = utmp_put_shutdown();
192 if (r < 0)
193 log_warning_errno(r, "Failed to write utmp record: %m");
194 }
195 }
196
197 if (arg_dry_run)
198 return 0;
199
200 r = halt_now(arg_action);
201 return log_error_errno(r, "Failed to reboot: %m");
202 }