]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
daf71ef6 LP |
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" | |
bc556335 DDM |
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); | |
daf71ef6 LP |
48 | |
49 | return 0; | |
50 | } | |
51 | ||
52 | int halt_parse_argv(int argc, char *argv[]) { | |
53 | enum { | |
54 | ARG_HELP = 0x100, | |
55 | ARG_HALT, | |
56 | ARG_REBOOT, | |
57 | ARG_NO_WALL | |
58 | }; | |
59 | ||
60 | static const struct option options[] = { | |
61 | { "help", no_argument, NULL, ARG_HELP }, | |
62 | { "halt", no_argument, NULL, ARG_HALT }, | |
63 | { "poweroff", no_argument, NULL, 'p' }, | |
64 | { "reboot", no_argument, NULL, ARG_REBOOT }, | |
65 | { "force", no_argument, NULL, 'f' }, | |
66 | { "wtmp-only", no_argument, NULL, 'w' }, | |
67 | { "no-wtmp", no_argument, NULL, 'd' }, | |
68 | { "no-sync", no_argument, NULL, 'n' }, | |
69 | { "no-wall", no_argument, NULL, ARG_NO_WALL }, | |
70 | {} | |
71 | }; | |
72 | ||
73 | int c, r, runlevel; | |
74 | ||
75 | assert(argc >= 0); | |
76 | assert(argv); | |
77 | ||
78 | if (utmp_get_runlevel(&runlevel, NULL) >= 0) | |
79 | if (IN_SET(runlevel, '0', '6')) | |
80 | arg_force = 2; | |
81 | ||
82 | while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) | |
83 | switch (c) { | |
84 | ||
85 | case ARG_HELP: | |
86 | return halt_help(); | |
87 | ||
88 | case ARG_HALT: | |
89 | arg_action = ACTION_HALT; | |
90 | break; | |
91 | ||
92 | case 'p': | |
93 | if (arg_action != ACTION_REBOOT) | |
94 | arg_action = ACTION_POWEROFF; | |
95 | break; | |
96 | ||
97 | case ARG_REBOOT: | |
98 | arg_action = ACTION_REBOOT; | |
99 | break; | |
100 | ||
101 | case 'f': | |
102 | arg_force = 2; | |
103 | break; | |
104 | ||
105 | case 'w': | |
106 | arg_dry_run = true; | |
107 | break; | |
108 | ||
109 | case 'd': | |
110 | arg_no_wtmp = true; | |
111 | break; | |
112 | ||
113 | case 'n': | |
114 | arg_no_sync = true; | |
115 | break; | |
116 | ||
117 | case ARG_NO_WALL: | |
118 | arg_no_wall = true; | |
119 | break; | |
120 | ||
121 | case 'i': | |
122 | case 'h': | |
123 | /* Compatibility nops */ | |
124 | break; | |
125 | ||
126 | case '?': | |
127 | return -EINVAL; | |
128 | ||
129 | default: | |
130 | assert_not_reached("Unhandled option"); | |
131 | } | |
132 | ||
133 | if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) { | |
134 | r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL, false); | |
135 | if (r < 0) | |
136 | return r; | |
137 | } else if (optind < argc) | |
138 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
139 | "Too many arguments."); | |
140 | ||
141 | return 1; | |
142 | } | |
143 | ||
144 | int halt_main(void) { | |
145 | int r; | |
146 | ||
147 | r = logind_check_inhibitors(arg_action); | |
148 | if (r < 0) | |
149 | return r; | |
150 | ||
151 | /* Delayed shutdown requested, and was successful */ | |
152 | if (arg_when > 0 && logind_schedule_shutdown() == 0) | |
153 | return 0; | |
154 | ||
155 | /* No delay, or logind failed or is not at all available */ | |
156 | if (geteuid() != 0) { | |
157 | if (arg_dry_run || arg_force > 0) { | |
158 | (void) must_be_root(); | |
159 | return -EPERM; | |
160 | } | |
161 | ||
162 | /* Try logind if we are a normal user and no special mode applies. Maybe polkit allows us to | |
163 | * shutdown the machine. */ | |
0d96caa5 | 164 | if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_KEXEC, ACTION_HALT)) { |
daf71ef6 LP |
165 | r = logind_reboot(arg_action); |
166 | if (r >= 0) | |
167 | return r; | |
168 | if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) | |
169 | /* Requested operation is not supported on the local system or already in | |
170 | * progress */ | |
171 | return r; | |
172 | ||
173 | /* on all other errors, try low-level operation */ | |
174 | } | |
175 | } | |
176 | ||
177 | /* In order to minimize the difference between operation with and without logind, we explicitly | |
178 | * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */ | |
179 | arg_no_block = true; | |
180 | ||
181 | if (!arg_dry_run && !arg_force) | |
182 | return start_with_fallback(); | |
183 | ||
184 | assert(geteuid() == 0); | |
185 | ||
186 | if (!arg_no_wtmp) { | |
187 | if (sd_booted() > 0) | |
188 | log_debug("Not writing utmp record, assuming that systemd-update-utmp is used."); | |
189 | else { | |
190 | r = utmp_put_shutdown(); | |
191 | if (r < 0) | |
192 | log_warning_errno(r, "Failed to write utmp record: %m"); | |
193 | } | |
194 | } | |
195 | ||
196 | if (arg_dry_run) | |
197 | return 0; | |
198 | ||
199 | r = halt_now(arg_action); | |
200 | return log_error_errno(r, "Failed to reboot: %m"); | |
201 | } |