1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "bootspec.h"
6 #include "bus-error.h"
7 #include "bus-locator.h"
8 #include "efivars.h"
9 #include "parse-util.h"
10 #include "path-util.h"
11 #include "process-util.h"
12 #include "reboot-util.h"
13 #include "systemctl-logind.h"
14 #include "systemctl-start-special.h"
15 #include "systemctl-start-unit.h"
16 #include "systemctl-trivial-method.h"
17 #include "systemctl-util.h"
18 #include "systemctl.h"
19
load_kexec_kernel(void)20 static int load_kexec_kernel(void) {
21 _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
22 _cleanup_free_ char *kernel = NULL, *initrd = NULL, *options = NULL;
23 const BootEntry *e;
24 pid_t pid;
25 int r;
26
27 if (kexec_loaded()) {
28 log_debug("Kexec kernel already loaded.");
29 return 0;
30 }
31
32 if (access(KEXEC, X_OK) < 0)
33 return log_error_errno(errno, KEXEC" is not available: %m");
34
35 r = boot_config_load_auto(&config, NULL, NULL);
36 if (r == -ENOKEY)
37 /* The call doesn't log about ENOKEY, let's do so here. */
38 return log_error_errno(r,
39 "No kexec kernel loaded and autodetection failed.\n%s",
40 is_efi_boot()
41 ? "Cannot automatically load kernel: ESP mount point not found."
42 : "Automatic loading works only on systems booted with EFI.");
43 if (r < 0)
44 return r;
45
46 r = boot_config_select_special_entries(&config);
47 if (r < 0)
48 return r;
49
50 e = boot_config_default_entry(&config);
51 if (!e)
52 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
53 "No boot loader entry suitable as default, refusing to guess.");
54
55 log_debug("Found default boot loader entry in file \"%s\"", e->path);
56
57 if (!e->kernel)
58 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
59 "Boot entry does not refer to Linux kernel, which is not supported currently.");
60 if (strv_length(e->initrd) > 1)
61 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
62 "Boot entry specifies multiple initrds, which is not supported currently.");
63
64 kernel = path_join(e->root, e->kernel);
65 if (!kernel)
66 return log_oom();
67
68 if (!strv_isempty(e->initrd)) {
69 initrd = path_join(e->root, e->initrd[0]);
70 if (!initrd)
71 return log_oom();
72 }
73
74 options = strv_join(e->options, " ");
75 if (!options)
76 return log_oom();
77
78 log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
79 "%s "KEXEC" --load \"%s\" --append \"%s\"%s%s%s",
80 arg_dry_run ? "Would run" : "Running",
81 kernel,
82 options,
83 initrd ? " --initrd \"" : NULL, strempty(initrd), initrd ? "\"" : "");
84 if (arg_dry_run)
85 return 0;
86
87 r = safe_fork("(kexec)", FORK_WAIT|FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
88 if (r < 0)
89 return r;
90 if (r == 0) {
91 const char* const args[] = {
92 KEXEC,
93 "--load", kernel,
94 "--append", options,
95 initrd ? "--initrd" : NULL, initrd,
96 NULL
97 };
98
99 /* Child */
100 execv(args[0], (char * const *) args);
101 _exit(EXIT_FAILURE);
102 }
103
104 return 0;
105 }
106
set_exit_code(uint8_t code)107 static int set_exit_code(uint8_t code) {
108 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
109 sd_bus *bus;
110 int r;
111
112 r = acquire_bus(BUS_MANAGER, &bus);
113 if (r < 0)
114 return r;
115
116 r = bus_call_method(bus, bus_systemd_mgr, "SetExitCode", &error, NULL, "y", code);
117 if (r < 0)
118 return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r));
119
120 return 0;
121 }
122
verb_start_special(int argc,char * argv[],void * userdata)123 int verb_start_special(int argc, char *argv[], void *userdata) {
124 bool termination_action; /* An action that terminates the manager, can be performed also by
125 * signal. */
126 enum action a;
127 int r;
128
129 assert(argv);
130
131 a = verb_to_action(argv[0]);
132
133 r = logind_check_inhibitors(a);
134 if (r < 0)
135 return r;
136
137 if (arg_force >= 2) {
138 r = must_be_root();
139 if (r < 0)
140 return r;
141 }
142
143 r = prepare_firmware_setup();
144 if (r < 0)
145 return r;
146
147 r = prepare_boot_loader_menu();
148 if (r < 0)
149 return r;
150
151 r = prepare_boot_loader_entry();
152 if (r < 0)
153 return r;
154
155 if (a == ACTION_REBOOT) {
156 const char *arg = NULL;
157
158 if (argc > 1) {
159 if (arg_reboot_argument)
160 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Both --reboot-argument= and positional argument passed to reboot command, refusing.");
161
162 log_notice("Positional argument to reboot command is deprecated, please use --reboot-argument= instead. Accepting anyway.");
163 arg = argv[1];
164 } else
165 arg = arg_reboot_argument;
166
167 if (arg) {
168 r = update_reboot_parameter_and_warn(arg, false);
169 if (r < 0)
170 return r;
171 }
172
173 } else if (a == ACTION_KEXEC) {
174 r = load_kexec_kernel();
175 if (r < 0 && arg_force >= 1)
176 log_notice("Failed to load kexec kernel, continuing without.");
177 else if (r < 0)
178 return r;
179
180 } else if (a == ACTION_EXIT && argc > 1) {
181 uint8_t code;
182
183 /* If the exit code is not given on the command line, don't reset it to zero: just keep it as
184 * it might have been set previously. */
185
186 r = safe_atou8(argv[1], &code);
187 if (r < 0)
188 return log_error_errno(r, "Invalid exit code.");
189
190 r = set_exit_code(code);
191 if (r < 0)
192 return r;
193 }
194
195 termination_action = IN_SET(a,
196 ACTION_HALT,
197 ACTION_POWEROFF,
198 ACTION_REBOOT);
199 if (termination_action && arg_force >= 2)
200 return halt_now(a);
201
202 if (arg_force >= 1 &&
203 (termination_action || IN_SET(a, ACTION_KEXEC, ACTION_EXIT)))
204 r = verb_trivial_method(argc, argv, userdata);
205 else {
206 /* First try logind, to allow authentication with polkit */
207 if (IN_SET(a,
208 ACTION_POWEROFF,
209 ACTION_REBOOT,
210 ACTION_KEXEC,
211 ACTION_HALT,
212 ACTION_SUSPEND,
213 ACTION_HIBERNATE,
214 ACTION_HYBRID_SLEEP,
215 ACTION_SUSPEND_THEN_HIBERNATE)) {
216
217 r = logind_reboot(a);
218 if (r >= 0)
219 return r;
220 if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
221 /* Requested operation requires auth, is not supported or already in progress */
222 return r;
223
224 /* On all other errors, try low-level operation. In order to minimize the difference
225 * between operation with and without logind, we explicitly enable non-blocking mode
226 * for this, as logind's shutdown operations are always non-blocking. */
227
228 arg_no_block = true;
229
230 } else if (IN_SET(a, ACTION_EXIT))
231 /* Since exit is so close in behaviour to power-off/reboot, let's also make
232 * it asynchronous, in order to not confuse the user needlessly with unexpected
233 * behaviour. */
234 arg_no_block = true;
235
236 r = verb_start(argc, argv, userdata);
237 }
238
239 if (termination_action && arg_force < 2 &&
240 IN_SET(r, -ENOENT, -ETIMEDOUT))
241 log_notice("It is possible to perform action directly, see discussion of --force --force in man:systemctl(1).");
242
243 return r;
244 }
245
verb_start_system_special(int argc,char * argv[],void * userdata)246 int verb_start_system_special(int argc, char *argv[], void *userdata) {
247 /* Like start_special above, but raises an error when running in user mode */
248
249 if (arg_scope != LOOKUP_SCOPE_SYSTEM)
250 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
251 "Bad action for %s mode.",
252 arg_scope == LOOKUP_SCOPE_GLOBAL ? "--global" : "--user");
253
254 return verb_start_special(argc, argv, userdata);
255 }
256