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