]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
7b4d7cc0 | 2 | |
3f6fd1ba | 3 | #include <getopt.h> |
7b4d7cc0 | 4 | |
c56be2c2 | 5 | #include "blockdev-util.h" |
85f87535 | 6 | #include "bootctl.h" |
baafb202 | 7 | #include "bootctl-install.h" |
85f87535 | 8 | #include "bootctl-random-seed.h" |
34ea7e02 | 9 | #include "bootctl-reboot-to-firmware.h" |
6d8ca257 | 10 | #include "bootctl-set-efivar.h" |
da36788f LP |
11 | #include "bootctl-status.h" |
12 | #include "bootctl-systemd-efi-options.h" | |
53c368d7 | 13 | #include "bootctl-uki.h" |
d6b4d1c7 | 14 | #include "build.h" |
c56be2c2 | 15 | #include "devnum-util.h" |
80a2381d | 16 | #include "dissect-image.h" |
9dae4c8a | 17 | #include "escape.h" |
e94830c0 | 18 | #include "find-esp.h" |
608f8ec9 | 19 | #include "main-func.h" |
80a2381d | 20 | #include "mount-util.h" |
57db6f18 | 21 | #include "pager.h" |
6a3fff75 | 22 | #include "parse-argument.h" |
294bf0c3 | 23 | #include "pretty-print.h" |
d88c96ff | 24 | #include "utf8.h" |
79ec3995 LP |
25 | #include "varlink.h" |
26 | #include "varlink-io.systemd.BootControl.h" | |
2f2c539c LP |
27 | #include "verbs.h" |
28 | #include "virt.h" | |
7b4d7cc0 | 29 | |
d9bdb29b RH |
30 | /* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description |
31 | * stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this | |
32 | * string, but we limit the length to something reasonable to prevent from the firmware | |
33 | * having to deal with a potentially too long string. */ | |
34 | #define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255) | |
35 | ||
85f87535 LP |
36 | char *arg_esp_path = NULL; |
37 | char *arg_xbootldr_path = NULL; | |
38 | bool arg_print_esp_path = false; | |
39 | bool arg_print_dollar_boot_path = false; | |
c56be2c2 | 40 | unsigned arg_print_root_device = 0; |
85f87535 LP |
41 | bool arg_touch_variables = true; |
42 | PagerFlags arg_pager_flags = 0; | |
43 | bool arg_graceful = false; | |
44 | bool arg_quiet = false; | |
45 | int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */ | |
46 | sd_id128_t arg_machine_id = SD_ID128_NULL; | |
47 | char *arg_install_layout = NULL; | |
9e43296f | 48 | BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO; |
85f87535 LP |
49 | char *arg_entry_token = NULL; |
50 | JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; | |
51 | bool arg_arch_all = false; | |
52 | char *arg_root = NULL; | |
53 | char *arg_image = NULL; | |
baafb202 | 54 | InstallSource arg_install_source = ARG_INSTALL_SOURCE_AUTO; |
85f87535 | 55 | char *arg_efi_boot_option_description = NULL; |
8702496b | 56 | bool arg_dry_run = false; |
84be0c71 | 57 | ImagePolicy *arg_image_policy = NULL; |
79ec3995 | 58 | bool arg_varlink = false; |
25579a43 | 59 | |
fbf45d22 LP |
60 | STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); |
61 | STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); | |
47fb161e | 62 | STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep); |
f337f903 | 63 | STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep); |
80a2381d LB |
64 | STATIC_DESTRUCTOR_REGISTER(arg_root, freep); |
65 | STATIC_DESTRUCTOR_REGISTER(arg_image, freep); | |
d9bdb29b | 66 | STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep); |
84be0c71 | 67 | STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); |
fbf45d22 | 68 | |
da36788f | 69 | int acquire_esp( |
0b2aa206 | 70 | int unprivileged_mode, |
e5a8b4b5 | 71 | bool graceful, |
5caa3167 LP |
72 | uint32_t *ret_part, |
73 | uint64_t *ret_pstart, | |
74 | uint64_t *ret_psize, | |
f63b5ad9 LP |
75 | sd_id128_t *ret_uuid, |
76 | dev_t *ret_devid) { | |
5caa3167 LP |
77 | |
78 | char *np; | |
2f2c539c LP |
79 | int r; |
80 | ||
fbf45d22 LP |
81 | /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on |
82 | * its own, except for ENOKEY (which is good, we want to show our own message in that case, | |
83 | * suggesting use of --esp-path=) and EACCESS (only when we request unprivileged mode; in this case | |
84 | * we simply eat up the error here, so that --list and --status work too, without noise about | |
85 | * this). */ | |
5caa3167 | 86 | |
80a2381d | 87 | r = find_esp_and_warn(arg_root, arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); |
e5a8b4b5 LP |
88 | if (r == -ENOKEY) { |
89 | if (graceful) | |
14e6e444 ZJS |
90 | return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_INFO, r, |
91 | "Couldn't find EFI system partition, skipping."); | |
e5a8b4b5 | 92 | |
af918182 | 93 | return log_error_errno(r, |
5caa3167 | 94 | "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n" |
fbf45d22 | 95 | "Alternatively, use --esp-path= to specify path to mount point."); |
e5a8b4b5 | 96 | } |
5caa3167 LP |
97 | if (r < 0) |
98 | return r; | |
99 | ||
fbf45d22 LP |
100 | free_and_replace(arg_esp_path, np); |
101 | log_debug("Using EFI System Partition at %s.", arg_esp_path); | |
102 | ||
957b2423 | 103 | return 0; |
fbf45d22 | 104 | } |
0974a682 | 105 | |
da36788f | 106 | int acquire_xbootldr( |
0b2aa206 | 107 | int unprivileged_mode, |
f63b5ad9 LP |
108 | sd_id128_t *ret_uuid, |
109 | dev_t *ret_devid) { | |
110 | ||
fbf45d22 LP |
111 | char *np; |
112 | int r; | |
5caa3167 | 113 | |
80a2381d | 114 | r = find_xbootldr_and_warn(arg_root, arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid); |
fbf45d22 LP |
115 | if (r == -ENOKEY) { |
116 | log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT."); | |
f63b5ad9 LP |
117 | arg_xbootldr_path = mfree(arg_xbootldr_path); |
118 | ||
fbf45d22 LP |
119 | if (ret_uuid) |
120 | *ret_uuid = SD_ID128_NULL; | |
f63b5ad9 LP |
121 | if (ret_devid) |
122 | *ret_devid = 0; | |
fbf45d22 LP |
123 | return 0; |
124 | } | |
125 | if (r < 0) | |
126 | return r; | |
127 | ||
128 | free_and_replace(arg_xbootldr_path, np); | |
129 | log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path); | |
130 | ||
131 | return 1; | |
7b4d7cc0 KS |
132 | } |
133 | ||
2f2c539c | 134 | static int help(int argc, char *argv[], void *userdata) { |
37ec0fdd LP |
135 | _cleanup_free_ char *link = NULL; |
136 | int r; | |
137 | ||
eb98074b ZJS |
138 | pager_open(arg_pager_flags); |
139 | ||
37ec0fdd LP |
140 | r = terminal_urlify_man("bootctl", "1", &link); |
141 | if (r < 0) | |
142 | return log_oom(); | |
2f2c539c | 143 | |
e684d2d5 | 144 | printf("%1$s [OPTIONS...] COMMAND ...\n" |
64a7fcc5 LP |
145 | "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n" |
146 | "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n" | |
5bbf72bb | 147 | " status Show status of installed boot loader and EFI variables\n" |
002914e6 | 148 | " reboot-to-firmware [BOOL]\n" |
5bbf72bb | 149 | " Query or set reboot-to-firmware EFI flag\n" |
64a7fcc5 | 150 | "\n%3$sBoot Loader Specification Commands:%4$s\n" |
5bbf72bb | 151 | " list List boot loader entries\n" |
eab70618 LP |
152 | " unlink ID Remove boot loader entry\n" |
153 | " cleanup Remove files in ESP not referenced in any boot entry\n" | |
154 | "\n%3$sBoot Loader Interface Commands:%4$s\n" | |
5bbf72bb LP |
155 | " set-default ID Set default boot loader entry\n" |
156 | " set-oneshot ID Set default boot loader entry, for next boot only\n" | |
157 | " set-timeout SECONDS Set the menu timeout\n" | |
39ddc32a | 158 | " set-timeout-oneshot SECONDS\n" |
5bbf72bb | 159 | " Set the menu timeout for the next boot only\n" |
64a7fcc5 | 160 | "\n%3$ssystemd-boot Commands:%4$s\n" |
5bbf72bb LP |
161 | " install Install systemd-boot to the ESP and EFI variables\n" |
162 | " update Update systemd-boot in the ESP and EFI variables\n" | |
163 | " remove Remove systemd-boot from the ESP and EFI variables\n" | |
164 | " is-installed Test whether systemd-boot is installed in the ESP\n" | |
2d085515 LP |
165 | " random-seed Initialize or refresh random seed in ESP and EFI\n" |
166 | " variables\n" | |
1e7d6cc0 | 167 | "\n%3$sKernel Image Commands:%4$s\n" |
5bbf72bb LP |
168 | " kernel-identify Identify kernel image type\n" |
169 | " kernel-inspect Prints details about the kernel image\n" | |
64a7fcc5 | 170 | "\n%3$sOptions:%4$s\n" |
e1fac8a6 ZJS |
171 | " -h --help Show this help\n" |
172 | " --version Print version\n" | |
173 | " --esp-path=PATH Path to the EFI System Partition (ESP)\n" | |
174 | " --boot-path=PATH Path to the $BOOT partition\n" | |
80a2381d LB |
175 | " --root=PATH Operate on an alternate filesystem root\n" |
176 | " --image=PATH Operate on disk image as filesystem root\n" | |
84be0c71 LP |
177 | " --image-policy=POLICY\n" |
178 | " Specify disk image dissection policy\n" | |
02d06ba1 LB |
179 | " --install-source=auto|image|host\n" |
180 | " Where to pick files when using --root=/--image=\n" | |
c56be2c2 LP |
181 | " -p --print-esp-path Print path to the EFI System Partition mount point\n" |
182 | " -x --print-boot-path Print path to the $BOOT partition mount point\n" | |
183 | " -R --print-root-device\n" | |
d5163f9c LP |
184 | " Print path to the block device node backing the\n" |
185 | " root file system (returns e.g. /dev/nvme0n1p5)\n" | |
186 | " -RR Print path to the whole disk block device node\n" | |
187 | " backing the root FS (returns e.g. /dev/nvme0n1)\n" | |
e1fac8a6 ZJS |
188 | " --no-variables Don't touch EFI variables\n" |
189 | " --no-pager Do not pipe output into a pager\n" | |
351de38e LP |
190 | " --graceful Don't fail when the ESP cannot be found or EFI\n" |
191 | " variables cannot be written\n" | |
14e6e444 | 192 | " -q --quiet Suppress output\n" |
f337f903 LP |
193 | " --make-entry-directory=yes|no|auto\n" |
194 | " Create $BOOT/ENTRY-TOKEN/ directory\n" | |
195 | " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n" | |
196 | " Entry token to use for this installation\n" | |
0d1506d4 LP |
197 | " --json=pretty|short|off\n" |
198 | " Generate JSON output\n" | |
6e916539 JJ |
199 | " --all-architectures\n" |
200 | " Install all supported EFI architectures\n" | |
d9bdb29b RH |
201 | " --efi-boot-option-description=DESCRIPTION\n" |
202 | " Description of the entry in the boot option list\n" | |
8702496b | 203 | " --dry-run Dry run (unlink and cleanup)\n" |
bc556335 DDM |
204 | "\nSee the %2$s for details.\n", |
205 | program_invocation_short_name, | |
206 | link, | |
207 | ansi_underline(), | |
208 | ansi_normal(), | |
209 | ansi_highlight(), | |
210 | ansi_normal()); | |
0974a682 KS |
211 | |
212 | return 0; | |
213 | } | |
214 | ||
0974a682 KS |
215 | static int parse_argv(int argc, char *argv[]) { |
216 | enum { | |
fbf45d22 LP |
217 | ARG_ESP_PATH = 0x100, |
218 | ARG_BOOT_PATH, | |
80a2381d LB |
219 | ARG_ROOT, |
220 | ARG_IMAGE, | |
06e78680 | 221 | ARG_IMAGE_POLICY, |
02d06ba1 | 222 | ARG_INSTALL_SOURCE, |
0974a682 KS |
223 | ARG_VERSION, |
224 | ARG_NO_VARIABLES, | |
57db6f18 | 225 | ARG_NO_PAGER, |
351de38e | 226 | ARG_GRACEFUL, |
f337f903 LP |
227 | ARG_MAKE_ENTRY_DIRECTORY, |
228 | ARG_ENTRY_TOKEN, | |
0d1506d4 | 229 | ARG_JSON, |
6e916539 | 230 | ARG_ARCH_ALL, |
d9bdb29b | 231 | ARG_EFI_BOOT_OPTION_DESCRIPTION, |
8702496b | 232 | ARG_DRY_RUN, |
7b4d7cc0 KS |
233 | }; |
234 | ||
0974a682 | 235 | static const struct option options[] = { |
d9bdb29b RH |
236 | { "help", no_argument, NULL, 'h' }, |
237 | { "version", no_argument, NULL, ARG_VERSION }, | |
238 | { "esp-path", required_argument, NULL, ARG_ESP_PATH }, | |
239 | { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */ | |
240 | { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, | |
241 | { "root", required_argument, NULL, ARG_ROOT }, | |
242 | { "image", required_argument, NULL, ARG_IMAGE }, | |
06e78680 | 243 | { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, |
d9bdb29b RH |
244 | { "install-source", required_argument, NULL, ARG_INSTALL_SOURCE }, |
245 | { "print-esp-path", no_argument, NULL, 'p' }, | |
246 | { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ | |
247 | { "print-boot-path", no_argument, NULL, 'x' }, | |
c56be2c2 | 248 | { "print-root-device", no_argument, NULL, 'R' }, |
d9bdb29b RH |
249 | { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, |
250 | { "no-pager", no_argument, NULL, ARG_NO_PAGER }, | |
251 | { "graceful", no_argument, NULL, ARG_GRACEFUL }, | |
252 | { "quiet", no_argument, NULL, 'q' }, | |
253 | { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, | |
254 | { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */ | |
255 | { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, | |
256 | { "json", required_argument, NULL, ARG_JSON }, | |
257 | { "all-architectures", no_argument, NULL, ARG_ARCH_ALL }, | |
258 | { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION }, | |
8702496b | 259 | { "dry-run", no_argument, NULL, ARG_DRY_RUN }, |
a36b411e | 260 | {} |
0974a682 KS |
261 | }; |
262 | ||
2f2c539c | 263 | int c, r; |
7b4d7cc0 KS |
264 | |
265 | assert(argc >= 0); | |
266 | assert(argv); | |
267 | ||
4697ee9f | 268 | while ((c = getopt_long(argc, argv, "hpxRq", options, NULL)) >= 0) |
0974a682 | 269 | switch (c) { |
7b4d7cc0 | 270 | |
0974a682 | 271 | case 'h': |
2f2c539c | 272 | help(0, NULL, NULL); |
7b4d7cc0 | 273 | return 0; |
7b4d7cc0 | 274 | |
0974a682 | 275 | case ARG_VERSION: |
3f6fd1ba | 276 | return version(); |
7b4d7cc0 | 277 | |
fbf45d22 LP |
278 | case ARG_ESP_PATH: |
279 | r = free_and_strdup(&arg_esp_path, optarg); | |
280 | if (r < 0) | |
281 | return log_oom(); | |
282 | break; | |
283 | ||
284 | case ARG_BOOT_PATH: | |
285 | r = free_and_strdup(&arg_xbootldr_path, optarg); | |
2f2c539c LP |
286 | if (r < 0) |
287 | return log_oom(); | |
0974a682 KS |
288 | break; |
289 | ||
80a2381d LB |
290 | case ARG_ROOT: |
291 | r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root); | |
292 | if (r < 0) | |
293 | return r; | |
294 | break; | |
295 | ||
296 | case ARG_IMAGE: | |
297 | r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image); | |
298 | if (r < 0) | |
299 | return r; | |
300 | break; | |
301 | ||
06e78680 YW |
302 | case ARG_IMAGE_POLICY: |
303 | r = parse_image_policy_argument(optarg, &arg_image_policy); | |
304 | if (r < 0) | |
305 | return r; | |
306 | break; | |
307 | ||
02d06ba1 LB |
308 | case ARG_INSTALL_SOURCE: |
309 | if (streq(optarg, "auto")) | |
310 | arg_install_source = ARG_INSTALL_SOURCE_AUTO; | |
311 | else if (streq(optarg, "image")) | |
312 | arg_install_source = ARG_INSTALL_SOURCE_IMAGE; | |
313 | else if (streq(optarg, "host")) | |
314 | arg_install_source = ARG_INSTALL_SOURCE_HOST; | |
315 | else | |
316 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
317 | "Unexpected parameter for --install-source=: %s", optarg); | |
318 | ||
319 | break; | |
320 | ||
30b50477 | 321 | case 'p': |
fbf45d22 LP |
322 | arg_print_esp_path = true; |
323 | break; | |
324 | ||
fba4e945 | 325 | case 'x': |
fbf45d22 | 326 | arg_print_dollar_boot_path = true; |
30b50477 ZJS |
327 | break; |
328 | ||
c56be2c2 | 329 | case 'R': |
b3a9d980 | 330 | arg_print_root_device++; |
c56be2c2 LP |
331 | break; |
332 | ||
0974a682 KS |
333 | case ARG_NO_VARIABLES: |
334 | arg_touch_variables = false; | |
335 | break; | |
336 | ||
57db6f18 | 337 | case ARG_NO_PAGER: |
0221d68a | 338 | arg_pager_flags |= PAGER_DISABLE; |
57db6f18 LP |
339 | break; |
340 | ||
351de38e LP |
341 | case ARG_GRACEFUL: |
342 | arg_graceful = true; | |
343 | break; | |
344 | ||
14e6e444 ZJS |
345 | case 'q': |
346 | arg_quiet = true; | |
347 | break; | |
348 | ||
9e43296f YW |
349 | case ARG_ENTRY_TOKEN: |
350 | r = parse_boot_entry_token_type(optarg, &arg_entry_token_type, &arg_entry_token); | |
351 | if (r < 0) | |
352 | return r; | |
f337f903 | 353 | break; |
f337f903 LP |
354 | |
355 | case ARG_MAKE_ENTRY_DIRECTORY: | |
1ff493d5 | 356 | if (streq(optarg, "auto")) /* retained for backwards compatibility */ |
f337f903 | 357 | arg_make_entry_directory = -1; /* yes if machine-id is permanent */ |
6a3fff75 | 358 | else { |
51214cf4 | 359 | r = parse_boolean_argument("--make-entry-directory=", optarg, NULL); |
6a3fff75 | 360 | if (r < 0) |
361 | return r; | |
f337f903 | 362 | |
51214cf4 | 363 | arg_make_entry_directory = r; |
6a3fff75 | 364 | } |
365 | break; | |
366 | ||
0d1506d4 LP |
367 | case ARG_JSON: |
368 | r = parse_json_argument(optarg, &arg_json_format_flags); | |
369 | if (r <= 0) | |
370 | return r; | |
6e916539 | 371 | break; |
0d1506d4 | 372 | |
6e916539 JJ |
373 | case ARG_ARCH_ALL: |
374 | arg_arch_all = true; | |
0d1506d4 LP |
375 | break; |
376 | ||
d9bdb29b RH |
377 | case ARG_EFI_BOOT_OPTION_DESCRIPTION: |
378 | if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) { | |
379 | _cleanup_free_ char *escaped = NULL; | |
380 | ||
381 | escaped = cescape(optarg); | |
382 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
383 | "Invalid --efi-boot-option-description=: %s", strna(escaped)); | |
384 | } | |
385 | if (strlen(optarg) > EFI_BOOT_OPTION_DESCRIPTION_MAX) | |
386 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
51214cf4 ZJS |
387 | "--efi-boot-option-description= too long: %zu > %zu", |
388 | strlen(optarg), EFI_BOOT_OPTION_DESCRIPTION_MAX); | |
d9bdb29b RH |
389 | r = free_and_strdup_warn(&arg_efi_boot_option_description, optarg); |
390 | if (r < 0) | |
391 | return r; | |
392 | break; | |
393 | ||
8702496b LN |
394 | case ARG_DRY_RUN: |
395 | arg_dry_run = true; | |
396 | break; | |
397 | ||
0974a682 KS |
398 | case '?': |
399 | return -EINVAL; | |
400 | ||
401 | default: | |
04499a70 | 402 | assert_not_reached(); |
7b4d7cc0 | 403 | } |
7b4d7cc0 | 404 | |
c56be2c2 LP |
405 | if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) > 1) |
406 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
407 | "--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R cannot be combined."); | |
408 | ||
80a2381d | 409 | if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list", |
8702496b | 410 | "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup")) |
80a2381d LB |
411 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), |
412 | "Options --root= and --image= are not supported with verb %s.", | |
413 | argv[optind]); | |
414 | ||
415 | if (arg_root && arg_image) | |
416 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported."); | |
417 | ||
02d06ba1 LB |
418 | if (arg_install_source != ARG_INSTALL_SOURCE_AUTO && !arg_root && !arg_image) |
419 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=."); | |
420 | ||
8702496b LN |
421 | if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup")) |
422 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry is only supported with --unlink or --cleanup"); | |
423 | ||
79ec3995 LP |
424 | r = varlink_invocation(VARLINK_ALLOW_ACCEPT); |
425 | if (r < 0) | |
426 | return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m"); | |
427 | if (r > 0) { | |
428 | arg_varlink = true; | |
429 | arg_pager_flags |= PAGER_DISABLE; | |
430 | } | |
431 | ||
0974a682 KS |
432 | return 1; |
433 | } | |
7b4d7cc0 | 434 | |
2f2c539c | 435 | static int bootctl_main(int argc, char *argv[]) { |
2f2c539c | 436 | static const Verb verbs[] = { |
2536752d ZJS |
437 | { "help", VERB_ANY, VERB_ANY, 0, help }, |
438 | { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status }, | |
439 | { "install", VERB_ANY, 1, 0, verb_install }, | |
440 | { "update", VERB_ANY, 1, 0, verb_install }, | |
441 | { "remove", VERB_ANY, 1, 0, verb_remove }, | |
442 | { "is-installed", VERB_ANY, 1, 0, verb_is_installed }, | |
53c368d7 | 443 | { "kernel-identify", 2, 2, 0, verb_kernel_identify }, |
a0525598 | 444 | { "kernel-inspect", 2, 2, 0, verb_kernel_inspect }, |
2536752d | 445 | { "list", VERB_ANY, 1, 0, verb_list }, |
8702496b LN |
446 | { "unlink", 2, 2, 0, verb_unlink }, |
447 | { "cleanup", VERB_ANY, 1, 0, verb_list }, | |
39ddc32a JJ |
448 | { "set-default", 2, 2, 0, verb_set_efivar }, |
449 | { "set-oneshot", 2, 2, 0, verb_set_efivar }, | |
450 | { "set-timeout", 2, 2, 0, verb_set_efivar }, | |
451 | { "set-timeout-oneshot", 2, 2, 0, verb_set_efivar }, | |
2536752d ZJS |
452 | { "random-seed", VERB_ANY, 1, 0, verb_random_seed }, |
453 | { "systemd-efi-options", VERB_ANY, 2, 0, verb_systemd_efi_options }, | |
002914e6 | 454 | { "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware }, |
2f2c539c LP |
455 | {} |
456 | }; | |
457 | ||
458 | return dispatch_verb(argc, argv, verbs, NULL); | |
459 | } | |
460 | ||
608f8ec9 | 461 | static int run(int argc, char *argv[]) { |
80a2381d | 462 | _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; |
a4b3e942 | 463 | _cleanup_(umount_and_freep) char *mounted_dir = NULL; |
601185b4 | 464 | int r; |
7b4d7cc0 | 465 | |
87dc8b08 | 466 | log_setup(); |
7b4d7cc0 | 467 | |
341890de | 468 | /* If we run in a container, automatically turn off EFI file system access */ |
2f2c539c LP |
469 | if (detect_container() > 0) |
470 | arg_touch_variables = false; | |
471 | ||
7b4d7cc0 | 472 | r = parse_argv(argc, argv); |
601185b4 | 473 | if (r <= 0) |
608f8ec9 | 474 | return r; |
57db6f18 | 475 | |
79ec3995 LP |
476 | if (arg_varlink) { |
477 | _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL; | |
478 | ||
479 | /* Invocation as Varlink service */ | |
480 | ||
481 | r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY); | |
482 | if (r < 0) | |
483 | return log_error_errno(r, "Failed to allocate Varlink server: %m"); | |
484 | ||
485 | r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_BootControl); | |
486 | if (r < 0) | |
487 | return log_error_errno(r, "Failed to add Varlink interface: %m"); | |
488 | ||
489 | r = varlink_server_bind_method_many( | |
490 | varlink_server, | |
491 | "io.systemd.BootControl.ListBootEntries", vl_method_list_boot_entries, | |
492 | "io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware, | |
493 | "io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware); | |
494 | if (r < 0) | |
495 | return log_error_errno(r, "Failed to bind Varlink methods: %m"); | |
496 | ||
497 | r = varlink_server_loop_auto(varlink_server); | |
498 | if (r < 0) | |
499 | return log_error_errno(r, "Failed to run Varlink event loop: %m"); | |
500 | ||
501 | return EXIT_SUCCESS; | |
502 | } | |
503 | ||
c56be2c2 LP |
504 | if (arg_print_root_device > 0) { |
505 | _cleanup_free_ char *path = NULL; | |
506 | dev_t devno; | |
507 | ||
508 | r = blockdev_get_root(LOG_ERR, &devno); | |
509 | if (r < 0) | |
510 | return r; | |
511 | if (r == 0) { | |
512 | log_error("Root file system not backed by a (single) whole block device."); | |
513 | return 80; /* some recognizable error code */ | |
514 | } | |
515 | ||
516 | if (arg_print_root_device > 1) { | |
517 | r = block_get_whole_disk(devno, &devno); | |
518 | if (r < 0) | |
519 | log_debug_errno(r, "Unable to find whole block device for root block device, ignoring: %m"); | |
520 | } | |
521 | ||
522 | r = device_path_make_canonical(S_IFBLK, devno, &path); | |
523 | if (r < 0) | |
93d82cfc MY |
524 | return log_error_errno(r, |
525 | "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m", | |
526 | DEVNUM_FORMAT_VAL(devno)); | |
c56be2c2 LP |
527 | |
528 | puts(path); | |
03221e74 | 529 | return 0; |
c56be2c2 LP |
530 | } |
531 | ||
80a2381d LB |
532 | /* Open up and mount the image */ |
533 | if (arg_image) { | |
534 | assert(!arg_root); | |
535 | ||
536 | r = mount_image_privately_interactively( | |
537 | arg_image, | |
84be0c71 | 538 | arg_image_policy, |
80a2381d | 539 | DISSECT_IMAGE_GENERIC_ROOT | |
f4a63ce2 LP |
540 | DISSECT_IMAGE_RELAX_VAR_CHECK | |
541 | DISSECT_IMAGE_ALLOW_USERSPACE_VERITY, | |
a4b3e942 | 542 | &mounted_dir, |
a133d2c3 | 543 | /* ret_dir_fd= */ NULL, |
e330f97a | 544 | &loop_device); |
80a2381d LB |
545 | if (r < 0) |
546 | return r; | |
547 | ||
a4b3e942 | 548 | arg_root = strdup(mounted_dir); |
80a2381d LB |
549 | if (!arg_root) |
550 | return log_oom(); | |
551 | } | |
552 | ||
608f8ec9 | 553 | return bootctl_main(argc, argv); |
7b4d7cc0 | 554 | } |
608f8ec9 | 555 | |
002914e6 | 556 | DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); |