]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/systemctl/systemctl-start-special.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[thirdparty/systemd.git] / src / systemctl / systemctl-start-special.c
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
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, /* skip_efivars= */ false);
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_SIGTERM|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
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
123 int verb_start_special(int argc, char *argv[], void *userdata) {
124 bool termination_action; /* An action that terminates the system, can be performed also by signal. */
125 enum action a;
126 int r;
127
128 assert(argv);
129
130 a = verb_to_action(argv[0]);
131
132 r = logind_check_inhibitors(a);
133 if (r < 0)
134 return r;
135
136 if (arg_force >= 2) {
137 r = must_be_root();
138 if (r < 0)
139 return r;
140 }
141
142 termination_action = IN_SET(a, ACTION_HALT, ACTION_POWEROFF, ACTION_REBOOT);
143
144 if (termination_action) {
145 r = prepare_firmware_setup();
146 if (r < 0)
147 return r;
148
149 r = prepare_boot_loader_menu();
150 if (r < 0)
151 return r;
152
153 r = prepare_boot_loader_entry();
154 if (r < 0)
155 return r;
156 }
157
158 if (a == ACTION_REBOOT) {
159 if (arg_reboot_argument) {
160 r = update_reboot_parameter_and_warn(arg_reboot_argument, false);
161 if (r < 0)
162 return r;
163 }
164
165 } else if (a == ACTION_KEXEC) {
166 r = load_kexec_kernel();
167 if (r < 0 && arg_force >= 1)
168 log_notice("Failed to load kexec kernel, continuing without.");
169 else if (r < 0)
170 return r;
171
172 } else if (a == ACTION_EXIT && argc > 1) {
173 uint8_t code;
174
175 /* If the exit code is not given on the command line, don't reset it to zero: just keep it as
176 * it might have been set previously. */
177
178 r = safe_atou8(argv[1], &code);
179 if (r < 0)
180 return log_error_errno(r, "Invalid exit code.");
181
182 r = set_exit_code(code);
183 if (r < 0)
184 return r;
185 }
186
187 if (termination_action && arg_force >= 2)
188 return halt_now(a);
189
190 if (arg_force >= 1 &&
191 (termination_action || IN_SET(a, ACTION_KEXEC, ACTION_EXIT)))
192 r = verb_trivial_method(argc, argv, userdata);
193 else {
194 /* First try logind, to allow authentication with polkit */
195 switch (a) {
196
197 case ACTION_POWEROFF:
198 case ACTION_REBOOT:
199 case ACTION_KEXEC:
200 case ACTION_HALT:
201 case ACTION_SOFT_REBOOT:
202 if (arg_when == 0)
203 r = logind_reboot(a);
204 else if (arg_when != USEC_INFINITY)
205 r = logind_schedule_shutdown(a);
206 else /* arg_when == USEC_INFINITY */
207 r = logind_cancel_shutdown();
208 if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
209 /* The latter indicates that the requested operation requires auth,
210 * is not supported or already in progress, in which cases we ignore the error. */
211 return r;
212
213 /* On all other errors, try low-level operation. In order to minimize the difference
214 * between operation with and without logind, we explicitly enable non-blocking mode
215 * for this, as logind's shutdown operations are always non-blocking. */
216 arg_no_block = true;
217 break;
218
219 case ACTION_SUSPEND:
220 case ACTION_HIBERNATE:
221 case ACTION_HYBRID_SLEEP:
222 case ACTION_SUSPEND_THEN_HIBERNATE:
223
224 r = logind_reboot(a);
225 if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
226 return r;
227
228 arg_no_block = true;
229 break;
230
231 case ACTION_SLEEP:
232 return logind_reboot(a);
233
234 case ACTION_EXIT:
235 /* Since exit is so close in behaviour to power-off/reboot, let's also make
236 * it asynchronous, in order to not confuse the user needlessly with unexpected
237 * behaviour. */
238 arg_no_block = true;
239 break;
240
241 default:
242 ;
243 }
244
245 r = verb_start(argc, argv, userdata);
246 }
247
248 if (termination_action && arg_force < 2 &&
249 IN_SET(r, -ENOENT, -ETIMEDOUT))
250 log_notice("It is possible to perform action directly, see discussion of --force --force in man:systemctl(1).");
251
252 return r;
253 }
254
255 int verb_start_system_special(int argc, char *argv[], void *userdata) {
256 /* Like start_special above, but raises an error when running in user mode */
257
258 if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
259 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
260 "Bad action for %s mode.",
261 runtime_scope_cmdline_option_to_string(arg_runtime_scope));
262
263 return verb_start_special(argc, argv, userdata);
264 }