]>
Commit | Line | Data |
---|---|---|
42551ea7 YW |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
3 | #include <getopt.h> | |
8857aa74 | 4 | #include <stdlib.h> |
9d9e99a8 | 5 | #include <sys/utsname.h> |
8857aa74 | 6 | #include <unistd.h> |
42551ea7 | 7 | |
76d62b63 | 8 | #include "argv-util.h" |
42551ea7 | 9 | #include "boot-entry.h" |
658e6cc4 | 10 | #include "build.h" |
42551ea7 YW |
11 | #include "chase.h" |
12 | #include "conf-files.h" | |
658e6cc4 | 13 | #include "dirent-util.h" |
8857aa74 | 14 | #include "dissect-image.h" |
42551ea7 YW |
15 | #include "env-file.h" |
16 | #include "env-util.h" | |
17 | #include "exec-util.h" | |
6553db60 | 18 | #include "extract-word.h" |
42551ea7 YW |
19 | #include "fd-util.h" |
20 | #include "fileio.h" | |
21 | #include "find-esp.h" | |
bdd36c00 | 22 | #include "format-table.h" |
9d9e99a8 | 23 | #include "fs-util.h" |
42551ea7 | 24 | #include "id128-util.h" |
04a8c2ce | 25 | #include "image-policy.h" |
3f5196ff | 26 | #include "kernel-config.h" |
42551ea7 | 27 | #include "kernel-image.h" |
8857aa74 | 28 | #include "loop-util.h" |
42551ea7 | 29 | #include "main-func.h" |
04a8c2ce | 30 | #include "mount-util.h" |
4cff5e92 | 31 | #include "parse-argument.h" |
42551ea7 YW |
32 | #include "path-util.h" |
33 | #include "pretty-print.h" | |
658e6cc4 | 34 | #include "recurse-dir.h" |
42551ea7 YW |
35 | #include "rm-rf.h" |
36 | #include "stat-util.h" | |
37 | #include "string-table.h" | |
38 | #include "string-util.h" | |
39 | #include "strv.h" | |
40 | #include "tmpfile-util.h" | |
41 | #include "verbs.h" | |
42 | ||
43 | static bool arg_verbose = false; | |
4cff5e92 YW |
44 | static char *arg_esp_path = NULL; |
45 | static char *arg_xbootldr_path = NULL; | |
b79621aa | 46 | static int arg_make_entry_directory = -1; /* tristate */ |
bdd36c00 | 47 | static PagerFlags arg_pager_flags = 0; |
309a747f | 48 | static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; |
04a8c2ce DDM |
49 | static char *arg_root = NULL; |
50 | static char *arg_image = NULL; | |
51 | static ImagePolicy *arg_image_policy = NULL; | |
658e6cc4 | 52 | static bool arg_legend = true; |
4cff5e92 YW |
53 | |
54 | STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); | |
55 | STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); | |
04a8c2ce | 56 | STATIC_DESTRUCTOR_REGISTER(arg_root, freep); |
d1b1cf8f | 57 | STATIC_DESTRUCTOR_REGISTER(arg_image, freep); |
04a8c2ce | 58 | STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); |
42551ea7 YW |
59 | |
60 | typedef enum Action { | |
61 | ACTION_ADD, | |
62 | ACTION_REMOVE, | |
63 | ACTION_INSPECT, | |
64 | _ACTION_MAX, | |
65 | _ACTION_INVALID = -EINVAL, | |
66 | } Action; | |
67 | ||
68 | typedef enum Layout { | |
69 | LAYOUT_AUTO, | |
70 | LAYOUT_UKI, | |
71 | LAYOUT_BLS, | |
72 | LAYOUT_OTHER, | |
73 | _LAYOUT_MAX, | |
74 | _LAYOUT_INVALID = -EINVAL, | |
75 | } Layout; | |
76 | ||
77 | static const char * const layout_table[_LAYOUT_MAX] = { | |
78 | [LAYOUT_AUTO] = "auto", | |
79 | [LAYOUT_UKI] = "uki", | |
80 | [LAYOUT_BLS] = "bls", | |
81 | [LAYOUT_OTHER] = "other", | |
82 | }; | |
83 | ||
84 | DEFINE_PRIVATE_STRING_TABLE_LOOKUP(layout, Layout); | |
85 | ||
86 | typedef struct Context { | |
87 | int rfd; | |
88 | Action action; | |
89 | sd_id128_t machine_id; | |
90 | bool machine_id_is_random; | |
91 | KernelImageType kernel_image_type; | |
92 | Layout layout; | |
93 | char *layout_other; | |
94 | char *conf_root; | |
95 | char *boot_root; | |
96 | BootEntryTokenType entry_token_type; | |
97 | char *entry_token; | |
98 | char *entry_dir; | |
99 | char *version; | |
100 | char *kernel; | |
101 | char **initrds; | |
102 | char *initrd_generator; | |
103 | char *uki_generator; | |
104 | char *staging_area; | |
105 | char **plugins; | |
106 | char **argv; | |
107 | char **envp; | |
108 | } Context; | |
109 | ||
ec1399f5 LP |
110 | #define CONTEXT_NULL (Context) { .rfd = -EBADF } |
111 | ||
42551ea7 YW |
112 | static void context_done(Context *c) { |
113 | assert(c); | |
114 | ||
115 | free(c->layout_other); | |
116 | free(c->conf_root); | |
117 | free(c->boot_root); | |
118 | free(c->entry_token); | |
119 | free(c->entry_dir); | |
120 | free(c->version); | |
121 | free(c->kernel); | |
122 | strv_free(c->initrds); | |
123 | free(c->initrd_generator); | |
124 | free(c->uki_generator); | |
125 | if (c->action == ACTION_INSPECT) | |
126 | free(c->staging_area); | |
127 | else | |
128 | rm_rf_physical_and_free(c->staging_area); | |
129 | strv_free(c->plugins); | |
130 | strv_free(c->argv); | |
131 | strv_free(c->envp); | |
132 | ||
133 | safe_close(c->rfd); | |
134 | } | |
135 | ||
ec1399f5 LP |
136 | static int context_copy(const Context *source, Context *ret) { |
137 | int r; | |
138 | ||
139 | assert(source); | |
140 | assert(ret); | |
27d420f4 | 141 | assert(source->rfd >= 0 || source->rfd == AT_FDCWD); |
ec1399f5 LP |
142 | |
143 | _cleanup_(context_done) Context copy = (Context) { | |
27d420f4 | 144 | .rfd = AT_FDCWD, |
ec1399f5 LP |
145 | .action = source->action, |
146 | .machine_id = source->machine_id, | |
147 | .machine_id_is_random = source->machine_id_is_random, | |
148 | .kernel_image_type = source->kernel_image_type, | |
149 | .layout = source->layout, | |
150 | .entry_token_type = source->entry_token_type, | |
151 | }; | |
152 | ||
27d420f4 LN |
153 | if (source->rfd >= 0) { |
154 | copy.rfd = fd_reopen(source->rfd, O_CLOEXEC|O_DIRECTORY|O_PATH); | |
155 | if (copy.rfd < 0) | |
156 | return copy.rfd; | |
157 | } | |
ec1399f5 | 158 | |
6a705f12 | 159 | r = strdup_to(©.layout_other, source->layout_other); |
ec1399f5 LP |
160 | if (r < 0) |
161 | return r; | |
6a705f12 | 162 | r = strdup_to(©.conf_root, source->conf_root); |
ec1399f5 LP |
163 | if (r < 0) |
164 | return r; | |
6a705f12 | 165 | r = strdup_to(©.boot_root, source->boot_root); |
ec1399f5 LP |
166 | if (r < 0) |
167 | return r; | |
6a705f12 | 168 | r = strdup_to(©.entry_token, source->entry_token); |
ec1399f5 LP |
169 | if (r < 0) |
170 | return r; | |
6a705f12 | 171 | r = strdup_to(©.entry_dir, source->entry_dir); |
ec1399f5 LP |
172 | if (r < 0) |
173 | return r; | |
6a705f12 | 174 | r = strdup_to(©.version, source->version); |
ec1399f5 LP |
175 | if (r < 0) |
176 | return r; | |
6a705f12 | 177 | r = strdup_to(©.kernel, source->kernel); |
ec1399f5 LP |
178 | if (r < 0) |
179 | return r; | |
27d420f4 LN |
180 | r = strv_copy_unless_empty(source->initrds, ©.initrds); |
181 | if (r < 0) | |
182 | return r; | |
6a705f12 | 183 | r = strdup_to(©.initrd_generator, source->initrd_generator); |
ec1399f5 LP |
184 | if (r < 0) |
185 | return r; | |
6a705f12 | 186 | r = strdup_to(©.uki_generator, source->uki_generator); |
ec1399f5 LP |
187 | if (r < 0) |
188 | return r; | |
6a705f12 | 189 | r = strdup_to(©.staging_area, source->staging_area); |
ec1399f5 LP |
190 | if (r < 0) |
191 | return r; | |
27d420f4 LN |
192 | r = strv_copy_unless_empty(source->plugins, ©.plugins); |
193 | if (r < 0) | |
194 | return r; | |
195 | r = strv_copy_unless_empty(source->argv, ©.argv); | |
196 | if (r < 0) | |
197 | return r; | |
198 | r = strv_copy_unless_empty(source->envp, ©.envp); | |
199 | if (r < 0) | |
200 | return r; | |
ec1399f5 LP |
201 | |
202 | *ret = copy; | |
203 | copy = CONTEXT_NULL; | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
42551ea7 | 208 | static int context_open_root(Context *c) { |
54bc114f YW |
209 | int r; |
210 | ||
42551ea7 YW |
211 | assert(c); |
212 | assert(c->rfd < 0); | |
213 | ||
54bc114f YW |
214 | if (isempty(arg_root)) |
215 | return 0; | |
216 | ||
217 | r = path_is_root(arg_root); | |
218 | if (r < 0) | |
219 | return log_error_errno(r, "Failed to determine if '%s' is the root directory: %m", arg_root); | |
220 | if (r > 0) | |
221 | return 0; | |
222 | ||
04a8c2ce | 223 | c->rfd = open(empty_to_root(arg_root), O_CLOEXEC | O_DIRECTORY | O_PATH); |
42551ea7 | 224 | if (c->rfd < 0) |
04a8c2ce | 225 | return log_error_errno(errno, "Failed to open root directory '%s': %m", empty_to_root(arg_root)); |
42551ea7 YW |
226 | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static const char* context_get_layout(const Context *c) { | |
231 | assert(c); | |
232 | assert(c->layout >= 0); | |
233 | ||
234 | return c->layout_other ?: layout_to_string(c->layout); | |
235 | } | |
236 | ||
237 | static int context_set_layout(Context *c, const char *s, const char *source) { | |
238 | Layout t; | |
239 | ||
240 | assert(c); | |
241 | assert(source); | |
242 | ||
243 | if (c->layout >= 0 || !s) | |
244 | return 0; | |
245 | ||
246 | assert(!c->layout_other); | |
247 | ||
248 | t = layout_from_string(s); | |
249 | if (t >= 0) | |
250 | c->layout = t; | |
251 | else if (isempty(s)) | |
252 | c->layout = LAYOUT_AUTO; | |
253 | else { | |
254 | c->layout_other = strdup(s); | |
255 | if (!c->layout_other) | |
256 | return log_oom(); | |
257 | ||
258 | c->layout = LAYOUT_OTHER; | |
259 | } | |
260 | ||
261 | log_debug("layout=%s set via %s", context_get_layout(c), source); | |
262 | return 1; | |
263 | } | |
264 | ||
265 | static int context_set_machine_id(Context *c, const char *s, const char *source) { | |
266 | int r; | |
267 | ||
268 | assert(c); | |
269 | assert(source); | |
270 | ||
271 | if (!sd_id128_is_null(c->machine_id) || !s) | |
272 | return 0; | |
273 | ||
274 | r = sd_id128_from_string(s, &c->machine_id); | |
275 | if (r < 0) | |
276 | return log_warning_errno(r, "Failed to parse machine ID specified via %s, ignoring.", source); | |
277 | ||
278 | if (sd_id128_is_null(c->machine_id)) | |
279 | return 0; | |
280 | ||
281 | log_debug("MACHINE_ID=%s set via %s.", SD_ID128_TO_STRING(c->machine_id), source); | |
282 | return 1; | |
283 | } | |
284 | ||
285 | static int context_set_string(const char *s, const char *source, const char *name, char **dest) { | |
286 | char *p; | |
287 | ||
288 | assert(source); | |
289 | assert(name); | |
290 | assert(dest); | |
291 | ||
292 | if (*dest || !s) | |
293 | return 0; | |
294 | ||
295 | p = strdup(s); | |
296 | if (!p) | |
297 | return log_oom(); | |
298 | ||
299 | log_debug("%s (%s) set via %s.", name, p, source); | |
300 | ||
301 | *dest = p; | |
302 | return 1; | |
303 | } | |
304 | ||
305 | static int context_set_initrd_generator(Context *c, const char *s, const char *source) { | |
306 | assert(c); | |
307 | return context_set_string(s, source, "INITRD_GENERATOR", &c->initrd_generator); | |
308 | } | |
309 | ||
310 | static int context_set_uki_generator(Context *c, const char *s, const char *source) { | |
311 | assert(c); | |
312 | return context_set_string(s, source, "UKI_GENERATOR", &c->uki_generator); | |
313 | } | |
314 | ||
315 | static int context_set_version(Context *c, const char *s) { | |
316 | assert(c); | |
317 | ||
dbab0056 | 318 | if (s && !filename_is_valid(s)) |
42551ea7 YW |
319 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version specified: %s", s); |
320 | ||
321 | return context_set_string(s, "command line", "kernel version", &c->version); | |
322 | } | |
323 | ||
54bc114f | 324 | static int context_set_path(Context *c, const char *s, const char *source, const char *name, char **dest) { |
42551ea7 YW |
325 | char *p; |
326 | int r; | |
327 | ||
328 | assert(c); | |
329 | assert(source); | |
330 | assert(name); | |
331 | assert(dest); | |
332 | ||
333 | if (*dest || !s) | |
334 | return 0; | |
335 | ||
54bc114f YW |
336 | if (c->rfd >= 0) { |
337 | r = chaseat(c->rfd, s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL); | |
338 | if (r < 0) | |
339 | return log_warning_errno(r, "Failed to chase path %s for %s specified via %s, ignoring: %m", | |
340 | s, name, source); | |
341 | } else { | |
342 | r = path_make_absolute_cwd(s, &p); | |
343 | if (r < 0) | |
344 | return log_warning_errno(r, "Failed to make path '%s' for %s specified via %s absolute, ignoring: %m", | |
345 | s, name, source); | |
346 | } | |
42551ea7 YW |
347 | |
348 | log_debug("%s (%s) set via %s.", name, p, source); | |
349 | ||
350 | *dest = p; | |
351 | return 1; | |
352 | } | |
353 | ||
354 | static int context_set_boot_root(Context *c, const char *s, const char *source) { | |
355 | assert(c); | |
54bc114f | 356 | return context_set_path(c, s, source, "BOOT_ROOT", &c->boot_root); |
42551ea7 YW |
357 | } |
358 | ||
359 | static int context_set_conf_root(Context *c, const char *s, const char *source) { | |
360 | assert(c); | |
54bc114f | 361 | return context_set_path(c, s, source, "CONF_ROOT", &c->conf_root); |
42551ea7 YW |
362 | } |
363 | ||
364 | static int context_set_kernel(Context *c, const char *s) { | |
365 | assert(c); | |
54bc114f | 366 | return context_set_path(c, s, "command line", "kernel image file", &c->kernel); |
42551ea7 YW |
367 | } |
368 | ||
54bc114f | 369 | static int context_set_path_strv(Context *c, char* const* strv, const char *source, const char *name, char ***dest) { |
42551ea7 YW |
370 | _cleanup_strv_free_ char **w = NULL; |
371 | int r; | |
372 | ||
373 | assert(c); | |
374 | assert(source); | |
375 | assert(name); | |
376 | assert(dest); | |
377 | ||
378 | if (*dest) | |
379 | return 0; | |
380 | ||
381 | STRV_FOREACH(s, strv) { | |
382 | char *p; | |
383 | ||
54bc114f YW |
384 | if (c->rfd >= 0) { |
385 | r = chaseat(c->rfd, *s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL); | |
386 | if (r < 0) | |
387 | return log_warning_errno(r, "Failed to chase path %s for %s specified via %s: %m", | |
388 | *s, name, source); | |
389 | } else { | |
390 | r = path_make_absolute_cwd(*s, &p); | |
391 | if (r < 0) | |
392 | return log_warning_errno(r, "Failed to make path '%s' for %s specified via %s absolute, ignoring: %m", | |
393 | *s, name, source); | |
394 | } | |
42551ea7 YW |
395 | r = strv_consume(&w, p); |
396 | if (r < 0) | |
397 | return log_oom(); | |
398 | } | |
399 | ||
400 | if (strv_isempty(w)) | |
401 | return 0; | |
402 | ||
403 | log_debug("%s set via %s", name, source); | |
404 | ||
405 | *dest = TAKE_PTR(w); | |
406 | return 1; | |
407 | } | |
408 | ||
409 | static int context_set_plugins(Context *c, const char *s, const char *source) { | |
410 | _cleanup_strv_free_ char **v = NULL; | |
6e1816ef | 411 | int r; |
42551ea7 YW |
412 | |
413 | assert(c); | |
414 | ||
415 | if (c->plugins || !s) | |
416 | return 0; | |
417 | ||
6e1816ef YW |
418 | r = strv_split_full(&v, s, NULL, EXTRACT_UNQUOTE); |
419 | if (r < 0) | |
420 | return log_error_errno(r, "Failed to parse plugin paths from %s: %m", source); | |
42551ea7 | 421 | |
54bc114f | 422 | return context_set_path_strv(c, v, source, "plugins", &c->plugins); |
42551ea7 YW |
423 | } |
424 | ||
425 | static int context_set_initrds(Context *c, char* const* strv) { | |
426 | assert(c); | |
54bc114f | 427 | return context_set_path_strv(c, strv, "command line", "initrds", &c->initrds); |
42551ea7 YW |
428 | } |
429 | ||
430 | static int context_load_environment(Context *c) { | |
431 | assert(c); | |
432 | ||
433 | (void) context_set_machine_id(c, getenv("MACHINE_ID"), "environment"); | |
434 | (void) context_set_boot_root(c, getenv("BOOT_ROOT"), "environment"); | |
435 | (void) context_set_conf_root(c, getenv("KERNEL_INSTALL_CONF_ROOT"), "environment"); | |
436 | (void) context_set_plugins(c, getenv("KERNEL_INSTALL_PLUGINS"), "environment"); | |
437 | return 0; | |
438 | } | |
439 | ||
db26d802 ZJS |
440 | static int context_load_install_conf(Context *c) { |
441 | _cleanup_free_ char *machine_id = NULL, *boot_root = NULL, *layout = NULL, | |
442 | *initrd_generator = NULL, *uki_generator = NULL; | |
42551ea7 YW |
443 | int r; |
444 | ||
445 | assert(c); | |
42551ea7 | 446 | |
3f5196ff ZJS |
447 | r = load_kernel_install_conf(arg_root, |
448 | c->conf_root, | |
449 | &machine_id, | |
450 | &boot_root, | |
451 | &layout, | |
452 | &initrd_generator, | |
453 | &uki_generator); | |
454 | if (r <= 0) | |
455 | return r; | |
42551ea7 | 456 | |
db26d802 ZJS |
457 | (void) context_set_machine_id(c, machine_id, "config"); |
458 | (void) context_set_boot_root(c, boot_root, "config"); | |
459 | (void) context_set_layout(c, layout, "config"); | |
460 | (void) context_set_initrd_generator(c, initrd_generator, "config"); | |
461 | (void) context_set_uki_generator(c, uki_generator, "config"); | |
42551ea7 | 462 | |
db26d802 | 463 | log_debug("Loaded config."); |
42551ea7 YW |
464 | return 0; |
465 | } | |
466 | ||
467 | static int context_load_machine_info(Context *c) { | |
a0613123 | 468 | _cleanup_fclose_ FILE *f = NULL; |
1fd2af1a | 469 | _cleanup_free_ char *machine_id = NULL, *layout = NULL; |
42551ea7 YW |
470 | static const char *path = "/etc/machine-info"; |
471 | int r; | |
472 | ||
473 | assert(c); | |
474 | ||
475 | /* If the user configured an explicit machine ID in /etc/machine-info to use for our purpose, we'll | |
476 | * use that instead (for compatibility). */ | |
477 | ||
1fd2af1a | 478 | if (!sd_id128_is_null(c->machine_id) && c->layout >= 0) |
42551ea7 YW |
479 | return 0; |
480 | ||
a5ebaf79 YW |
481 | /* For testing. To make not read host's /etc/machine-info. */ |
482 | r = getenv_bool("KERNEL_INSTALL_READ_MACHINE_INFO"); | |
483 | if (r < 0 && r != -ENXIO) | |
484 | log_warning_errno(r, "Failed to read $KERNEL_INSTALL_READ_MACHINE_INFO, assuming yes: %m"); | |
485 | if (r == 0) { | |
5ea4afcf | 486 | log_debug("Skipping reading of /etc/machine-info."); |
a5ebaf79 YW |
487 | return 0; |
488 | } | |
489 | ||
a0613123 | 490 | r = chase_and_fopenat_unlocked(c->rfd, path, CHASE_AT_RESOLVE_IN_ROOT, "re", NULL, &f); |
42551ea7 YW |
491 | if (r == -ENOENT) |
492 | return 0; | |
493 | if (r < 0) | |
494 | return log_error_errno(r, "Failed to chase %s: %m", path); | |
495 | ||
496 | log_debug("Loading %s…", path); | |
497 | ||
a0613123 DDM |
498 | r = parse_env_file(f, path, |
499 | "KERNEL_INSTALL_MACHINE_ID", &machine_id, | |
500 | "KERNEL_INSTALL_LAYOUT", &layout); | |
42551ea7 YW |
501 | if (r < 0) |
502 | return log_error_errno(r, "Failed to parse '%s': %m", path); | |
503 | ||
504 | (void) context_set_machine_id(c, machine_id, path); | |
1fd2af1a | 505 | (void) context_set_layout(c, layout, path); |
42551ea7 YW |
506 | return 0; |
507 | } | |
508 | ||
509 | static int context_load_machine_id(Context *c) { | |
510 | int r; | |
511 | ||
512 | assert(c); | |
513 | ||
514 | r = id128_get_machine_at(c->rfd, &c->machine_id); | |
d66ad6ff ZJS |
515 | if (ERRNO_IS_NEG_MACHINE_ID_UNSET(r)) |
516 | return 0; | |
517 | if (r < 0) | |
42551ea7 | 518 | return log_error_errno(r, "Failed to load machine ID from /etc/machine-id: %m"); |
42551ea7 YW |
519 | |
520 | log_debug("MACHINE_ID=%s set via /etc/machine-id.", SD_ID128_TO_STRING(c->machine_id)); | |
521 | return 1; /* loaded */ | |
522 | } | |
523 | ||
524 | static int context_ensure_machine_id(Context *c) { | |
525 | int r; | |
526 | ||
527 | assert(c); | |
528 | ||
529 | if (!sd_id128_is_null(c->machine_id)) | |
530 | return 0; | |
531 | ||
532 | /* If /etc/machine-id is initialized we'll use it. */ | |
533 | r = context_load_machine_id(c); | |
534 | if (r != 0) | |
535 | return r; | |
536 | ||
537 | /* Otherwise we'll use a freshly generated one. */ | |
538 | r = sd_id128_randomize(&c->machine_id); | |
539 | if (r < 0) | |
540 | return log_error_errno(r, "Failed to generate random ID: %m"); | |
541 | ||
542 | c->machine_id_is_random = true; | |
543 | log_debug("New machine ID '%s' generated.", SD_ID128_TO_STRING(c->machine_id)); | |
544 | return 0; | |
545 | } | |
546 | ||
547 | static int context_acquire_xbootldr(Context *c) { | |
548 | int r; | |
549 | ||
550 | assert(c); | |
551 | assert(!c->boot_root); | |
552 | ||
553 | r = find_xbootldr_and_warn_at( | |
554 | /* rfd = */ c->rfd, | |
4cff5e92 | 555 | /* path = */ arg_xbootldr_path, |
42551ea7 YW |
556 | /* unprivileged_mode= */ -1, |
557 | /* ret_path = */ &c->boot_root, | |
558 | /* ret_uuid = */ NULL, | |
559 | /* ret_devid = */ NULL); | |
560 | if (r == -ENOKEY) { | |
561 | log_debug_errno(r, "Couldn't find an XBOOTLDR partition."); | |
562 | return 0; | |
563 | } | |
564 | if (r == -EACCES && geteuid() != 0) | |
565 | return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m"); | |
566 | if (r < 0) | |
567 | return r; | |
568 | ||
569 | log_debug("Using XBOOTLDR partition at %s as $BOOT_ROOT.", c->boot_root); | |
570 | return 1; /* found */ | |
571 | } | |
572 | ||
573 | static int context_acquire_esp(Context *c) { | |
574 | int r; | |
575 | ||
576 | assert(c); | |
577 | assert(!c->boot_root); | |
578 | ||
579 | r = find_esp_and_warn_at( | |
580 | /* rfd = */ c->rfd, | |
4cff5e92 | 581 | /* path = */ arg_esp_path, |
42551ea7 YW |
582 | /* unprivileged_mode= */ -1, |
583 | /* ret_path = */ &c->boot_root, | |
584 | /* ret_part = */ NULL, | |
585 | /* ret_pstart = */ NULL, | |
586 | /* ret_psize = */ NULL, | |
587 | /* ret_uuid = */ NULL, | |
588 | /* ret_devid = */ NULL); | |
589 | if (r == -ENOKEY) { | |
590 | log_debug_errno(r, "Couldn't find EFI system partition, ignoring."); | |
591 | return 0; | |
592 | } | |
593 | if (r == -EACCES && geteuid() != 0) | |
594 | return log_error_errno(r, "Failed to determine EFI system partition: %m"); | |
595 | if (r < 0) | |
596 | return r; | |
597 | ||
598 | log_debug("Using EFI System Partition at %s as $BOOT_ROOT.", c->boot_root); | |
599 | return 1; /* found */ | |
600 | } | |
601 | ||
602 | static int context_ensure_boot_root(Context *c) { | |
603 | int r; | |
604 | ||
605 | assert(c); | |
606 | ||
607 | /* If BOOT_ROOT is specified via environment or install.conf, then use it. */ | |
608 | if (c->boot_root) | |
609 | return 0; | |
610 | ||
611 | /* Otherwise, use XBOOTLDR partition, if mounted. */ | |
612 | r = context_acquire_xbootldr(c); | |
613 | if (r != 0) | |
614 | return r; | |
615 | ||
616 | /* Otherwise, use EFI system partition, if mounted. */ | |
617 | r = context_acquire_esp(c); | |
618 | if (r != 0) | |
619 | return r; | |
620 | ||
621 | /* If all else fails, use /boot. */ | |
54bc114f YW |
622 | if (c->rfd >= 0) { |
623 | r = chaseat(c->rfd, "/boot", CHASE_AT_RESOLVE_IN_ROOT, &c->boot_root, /* ret_fd = */ NULL); | |
624 | if (r < 0) | |
37252381 | 625 | return log_error_errno(r, "Failed to chase '/boot/': %m"); |
54bc114f YW |
626 | } else { |
627 | c->boot_root = strdup("/boot"); | |
628 | if (!c->boot_root) | |
629 | return log_oom(); | |
630 | } | |
42551ea7 YW |
631 | |
632 | log_debug("KERNEL_INSTALL_BOOT_ROOT autodetection yielded no candidates, using \"%s\".", c->boot_root); | |
633 | return 0; | |
634 | } | |
635 | ||
636 | static int context_ensure_entry_token(Context *c) { | |
637 | int r; | |
638 | ||
639 | assert(c); | |
640 | ||
641 | /* Now that we determined the machine ID to use, let's determine the "token" for the boot loader | |
642 | * entry to generate. We use that for naming the directory below $BOOT where we want to place the | |
643 | * kernel/initrd and related resources, as well for naming the .conf boot loader spec entry. | |
644 | * Typically this is just the machine ID, but it can be anything else, too, if we are told so. */ | |
645 | ||
646 | r = boot_entry_token_ensure_at( | |
647 | c->rfd, | |
648 | c->conf_root, | |
649 | c->machine_id, | |
650 | c->machine_id_is_random, | |
651 | &c->entry_token_type, | |
652 | &c->entry_token); | |
653 | if (r < 0) | |
654 | return r; | |
655 | ||
656 | log_debug("Using entry token: %s", c->entry_token); | |
657 | return 0; | |
658 | } | |
659 | ||
660 | static int context_load_plugins(Context *c) { | |
661 | int r; | |
662 | ||
663 | assert(c); | |
664 | ||
665 | if (c->plugins) | |
666 | return 0; | |
667 | ||
668 | r = conf_files_list_strv_at( | |
669 | &c->plugins, | |
670 | ".install", | |
671 | c->rfd, | |
672 | CONF_FILES_EXECUTABLE | CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED, | |
673 | STRV_MAKE_CONST("/etc/kernel/install.d", "/usr/lib/kernel/install.d")); | |
674 | if (r < 0) | |
675 | return log_error_errno(r, "Failed to find plugins: %m"); | |
676 | ||
677 | return 0; | |
678 | } | |
679 | ||
680 | static int context_init(Context *c) { | |
681 | int r; | |
682 | ||
683 | assert(c); | |
684 | ||
685 | r = context_open_root(c); | |
686 | if (r < 0) | |
687 | return r; | |
688 | ||
689 | r = context_load_environment(c); | |
690 | if (r < 0) | |
691 | return r; | |
692 | ||
42551ea7 YW |
693 | r = context_load_install_conf(c); |
694 | if (r < 0) | |
695 | return r; | |
696 | ||
697 | r = context_load_machine_info(c); | |
698 | if (r < 0) | |
699 | return r; | |
700 | ||
701 | r = context_ensure_machine_id(c); | |
702 | if (r < 0) | |
703 | return r; | |
704 | ||
705 | r = context_ensure_boot_root(c); | |
706 | if (r < 0) | |
707 | return r; | |
708 | ||
709 | r = context_ensure_entry_token(c); | |
710 | if (r < 0) | |
711 | return r; | |
712 | ||
713 | r = context_load_plugins(c); | |
714 | if (r < 0) | |
715 | return r; | |
716 | ||
717 | return 0; | |
718 | } | |
719 | ||
720 | static int context_inspect_kernel(Context *c) { | |
721 | assert(c); | |
722 | ||
723 | if (!c->kernel) | |
724 | return 0; | |
725 | ||
726 | return inspect_kernel(c->rfd, c->kernel, &c->kernel_image_type, NULL, NULL, NULL); | |
727 | } | |
728 | ||
729 | static int context_ensure_layout(Context *c) { | |
730 | int r; | |
731 | ||
732 | assert(c); | |
733 | assert(c->boot_root); | |
734 | assert(c->entry_token); | |
735 | ||
736 | if (c->layout >= 0 && c->layout != LAYOUT_AUTO) | |
737 | return 0; | |
738 | ||
739 | /* No layout configured by the administrator. Let's try to figure it out automatically from metadata | |
740 | * already contained in $BOOT_ROOT. */ | |
741 | ||
742 | if (c->kernel_image_type == KERNEL_IMAGE_TYPE_UKI) { | |
743 | c->layout = LAYOUT_UKI; | |
744 | log_debug("Kernel image type is %s, using layout=%s.", | |
745 | kernel_image_type_to_string(c->kernel_image_type), layout_to_string(c->layout)); | |
746 | return 0; | |
747 | } | |
748 | ||
749 | _cleanup_free_ char *srel_path = path_join(c->boot_root, "loader/entries.srel"); | |
750 | if (!srel_path) | |
751 | return log_oom(); | |
752 | ||
753 | _cleanup_free_ char *srel = NULL; | |
754 | r = read_one_line_file_at(c->rfd, srel_path, &srel); | |
755 | if (r >= 0) { | |
756 | if (streq(srel, "type1")) | |
757 | /* The loader/entries.srel file clearly indicates that the installed boot loader | |
758 | * implements the proper standard upstream boot loader spec for Type #1 entries. | |
759 | * Let's default to that, then. */ | |
760 | c->layout = LAYOUT_BLS; | |
761 | else | |
762 | /* The loader/entries.srel file indicates some other spec is implemented and owns the | |
763 | * /loader/entries/ directory. Since we have no idea what that means, let's stay away | |
764 | * from it by default. */ | |
765 | c->layout = LAYOUT_OTHER; | |
766 | ||
767 | log_debug("%s with '%s' found, using layout=%s.", srel_path, srel, layout_to_string(c->layout)); | |
768 | return 0; | |
769 | } else if (r != -ENOENT) | |
770 | return log_error_errno(r, "Failed to read %s: %m", srel_path); | |
771 | ||
772 | _cleanup_free_ char *entry_token_path = path_join(c->boot_root, c->entry_token); | |
773 | if (!entry_token_path) | |
774 | return log_oom(); | |
775 | ||
2560dcbf | 776 | r = is_dir_at(c->rfd, entry_token_path, /* follow = */ false); |
42551ea7 YW |
777 | if (r < 0 && r != -ENOENT) |
778 | return log_error_errno(r, "Failed to check if '%s' is a directory: %m", entry_token_path); | |
779 | if (r > 0) { | |
780 | /* If the metadata in $BOOT_ROOT doesn't tell us anything, then check if the entry token | |
781 | * directory already exists. If so, let's assume it's the standard boot loader spec, too. */ | |
782 | c->layout = LAYOUT_BLS; | |
783 | log_debug("%s exists, using layout=%s.", entry_token_path, layout_to_string(c->layout)); | |
784 | return 0; | |
785 | } | |
786 | ||
787 | /* There's no metadata in $BOOT_ROOT, and apparently no entry token directory installed? Then we | |
788 | * really don't know anything. */ | |
789 | c->layout = LAYOUT_OTHER; | |
4092f722 ZJS |
790 | log_debug("Entry-token directory %s not found, using layout=%s.", |
791 | entry_token_path, | |
792 | layout_to_string(c->layout)); | |
42551ea7 YW |
793 | return 0; |
794 | } | |
795 | ||
796 | static int context_set_up_staging_area(Context *c) { | |
797 | static const char *template = "/tmp/kernel-install.staging.XXXXXX"; | |
798 | int r; | |
799 | ||
800 | assert(c); | |
801 | ||
802 | if (c->staging_area) | |
803 | return 0; | |
804 | ||
805 | if (c->action == ACTION_INSPECT) { | |
806 | /* This is only used for display. The directory will not be created. */ | |
807 | c->staging_area = strdup(template); | |
808 | if (!c->staging_area) | |
809 | return log_oom(); | |
810 | } else { | |
811 | r = mkdtemp_malloc(template, &c->staging_area); | |
812 | if (r < 0) | |
813 | return log_error_errno(r, "Failed to create staging area: %m"); | |
814 | } | |
815 | ||
816 | return 0; | |
817 | } | |
818 | ||
819 | static int context_build_entry_dir(Context *c) { | |
820 | assert(c); | |
821 | assert(c->boot_root); | |
822 | assert(c->entry_token); | |
823 | assert(c->version || c->action == ACTION_INSPECT); | |
824 | ||
825 | if (c->entry_dir) | |
826 | return 0; | |
827 | ||
828 | c->entry_dir = path_join(c->boot_root, c->entry_token, c->version ?: "KERNEL_VERSION"); | |
829 | if (!c->entry_dir) | |
830 | return log_oom(); | |
831 | ||
832 | log_debug("Using ENTRY_DIR=%s", c->entry_dir); | |
833 | return 0; | |
834 | } | |
835 | ||
836 | static bool context_should_make_entry_dir(Context *c) { | |
837 | assert(c); | |
838 | ||
839 | /* Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN to signal to | |
840 | * 00-entry-directory to create $ENTRY_DIR to serve as the indication to use or to not use the BLS */ | |
841 | ||
b79621aa YW |
842 | if (arg_make_entry_directory < 0) |
843 | return c->layout == LAYOUT_BLS; | |
844 | ||
845 | return arg_make_entry_directory; | |
42551ea7 YW |
846 | } |
847 | ||
848 | static int context_make_entry_dir(Context *c) { | |
849 | _cleanup_close_ int fd = -EBADF; | |
850 | ||
851 | assert(c); | |
852 | assert(c->entry_dir); | |
853 | ||
854 | if (c->action != ACTION_ADD) | |
855 | return 0; | |
856 | ||
857 | if (!context_should_make_entry_dir(c)) | |
858 | return 0; | |
859 | ||
860 | log_debug("mkdir -p %s", c->entry_dir); | |
861 | fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT | CHASE_MKDIR_0755, | |
862 | O_CLOEXEC | O_CREAT | O_DIRECTORY | O_PATH, NULL); | |
863 | if (fd < 0) | |
864 | return log_error_errno(fd, "Failed to make directory '%s': %m", c->entry_dir); | |
865 | ||
866 | return 0; | |
867 | } | |
868 | ||
869 | static int context_remove_entry_dir(Context *c) { | |
870 | _cleanup_free_ char *p = NULL; | |
871 | _cleanup_close_ int fd = -EBADF; | |
872 | struct stat st; | |
873 | int r; | |
874 | ||
875 | assert(c); | |
876 | assert(c->entry_dir); | |
877 | ||
878 | if (c->action != ACTION_REMOVE) | |
879 | return 0; | |
880 | ||
881 | if (!context_should_make_entry_dir(c)) | |
882 | return 0; | |
883 | ||
884 | log_debug("rm -rf %s", c->entry_dir); | |
885 | fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT, O_CLOEXEC | O_DIRECTORY, &p); | |
886 | if (fd < 0) { | |
887 | if (IN_SET(fd, -ENOTDIR, -ENOENT)) | |
888 | return 0; | |
889 | return log_debug_errno(fd, "Failed to chase and open %s, ignoring: %m", c->entry_dir); | |
890 | } | |
891 | ||
892 | if (fstat(fd, &st) < 0) | |
893 | return log_debug_errno(errno, "Failed to stat %s: %m", p); | |
894 | ||
895 | r = rm_rf_children(TAKE_FD(fd), REMOVE_PHYSICAL|REMOVE_MISSING_OK|REMOVE_CHMOD, &st); | |
896 | if (r < 0) | |
897 | log_debug_errno(r, "Failed to remove children of %s, ignoring: %m", p); | |
898 | ||
899 | if (unlinkat(c->rfd, p, AT_REMOVEDIR) < 0) | |
900 | log_debug_errno(errno, "Failed to remove %s, ignoring: %m", p); | |
901 | ||
902 | return 0; | |
903 | } | |
904 | ||
905 | static int context_build_arguments(Context *c) { | |
906 | _cleanup_strv_free_ char **a = NULL; | |
907 | const char *verb; | |
908 | int r; | |
909 | ||
910 | assert(c); | |
911 | assert(c->entry_dir); | |
912 | ||
913 | if (c->argv) | |
914 | return 0; | |
915 | ||
916 | switch (c->action) { | |
917 | case ACTION_ADD: | |
918 | assert(c->version); | |
919 | assert(c->kernel); | |
920 | verb = "add"; | |
921 | break; | |
922 | ||
923 | case ACTION_REMOVE: | |
924 | assert(c->version); | |
925 | assert(!c->kernel); | |
926 | assert(!c->initrds); | |
927 | verb = "remove"; | |
928 | break; | |
929 | ||
930 | case ACTION_INSPECT: | |
42551ea7 YW |
931 | verb = "add|remove"; |
932 | break; | |
933 | ||
934 | default: | |
935 | assert_not_reached(); | |
936 | } | |
937 | ||
938 | a = strv_new("dummy-arg", /* to make strv_free() works for this variable. */ | |
939 | verb, | |
940 | c->version ?: "KERNEL_VERSION", | |
941 | c->entry_dir); | |
942 | if (!a) | |
943 | return log_oom(); | |
944 | ||
945 | if (c->action == ACTION_ADD) { | |
946 | r = strv_extend(&a, c->kernel); | |
947 | if (r < 0) | |
948 | return log_oom(); | |
949 | ||
950 | r = strv_extend_strv(&a, c->initrds, /* filter_duplicates = */ false); | |
951 | if (r < 0) | |
952 | return log_oom(); | |
953 | ||
954 | } else if (c->action == ACTION_INSPECT) { | |
69f3c619 LP |
955 | r = strv_extend_many( |
956 | &a, | |
957 | c->kernel ?: "[KERNEL_IMAGE]", | |
958 | "[INITRD...]"); | |
42551ea7 YW |
959 | if (r < 0) |
960 | return log_oom(); | |
961 | } | |
962 | ||
963 | c->argv = TAKE_PTR(a); | |
964 | return 0; | |
965 | } | |
966 | ||
967 | static int context_build_environment(Context *c) { | |
968 | _cleanup_strv_free_ char **e = NULL; | |
969 | int r; | |
970 | ||
971 | assert(c); | |
972 | ||
973 | if (c->envp) | |
974 | return 0; | |
975 | ||
976 | r = strv_env_assign_many(&e, | |
977 | "LC_COLLATE", SYSTEMD_DEFAULT_LOCALE, | |
978 | "KERNEL_INSTALL_VERBOSE", one_zero(arg_verbose), | |
979 | "KERNEL_INSTALL_IMAGE_TYPE", kernel_image_type_to_string(c->kernel_image_type), | |
980 | "KERNEL_INSTALL_MACHINE_ID", SD_ID128_TO_STRING(c->machine_id), | |
981 | "KERNEL_INSTALL_ENTRY_TOKEN", c->entry_token, | |
982 | "KERNEL_INSTALL_BOOT_ROOT", c->boot_root, | |
983 | "KERNEL_INSTALL_LAYOUT", context_get_layout(c), | |
984 | "KERNEL_INSTALL_INITRD_GENERATOR", strempty(c->initrd_generator), | |
985 | "KERNEL_INSTALL_UKI_GENERATOR", strempty(c->uki_generator), | |
986 | "KERNEL_INSTALL_STAGING_AREA", c->staging_area); | |
987 | if (r < 0) | |
988 | return log_error_errno(r, "Failed to build environment variables for plugins: %m"); | |
989 | ||
990 | c->envp = TAKE_PTR(e); | |
991 | return 0; | |
992 | } | |
993 | ||
994 | static int context_prepare_execution(Context *c) { | |
995 | int r; | |
996 | ||
997 | assert(c); | |
998 | ||
999 | r = context_inspect_kernel(c); | |
1000 | if (r < 0) | |
1001 | return r; | |
1002 | ||
1003 | r = context_ensure_layout(c); | |
1004 | if (r < 0) | |
1005 | return r; | |
1006 | ||
1007 | r = context_set_up_staging_area(c); | |
1008 | if (r < 0) | |
1009 | return r; | |
1010 | ||
1011 | r = context_build_entry_dir(c); | |
1012 | if (r < 0) | |
1013 | return r; | |
1014 | ||
1015 | r = context_build_arguments(c); | |
1016 | if (r < 0) | |
1017 | return r; | |
1018 | ||
1019 | r = context_build_environment(c); | |
1020 | if (r < 0) | |
1021 | return r; | |
1022 | ||
1023 | return 0; | |
1024 | } | |
1025 | ||
1026 | static int context_execute(Context *c) { | |
e42931b0 | 1027 | int r, ret; |
42551ea7 YW |
1028 | |
1029 | assert(c); | |
1030 | ||
1031 | r = context_make_entry_dir(c); | |
1032 | if (r < 0) | |
1033 | return r; | |
1034 | ||
1035 | if (DEBUG_LOGGING) { | |
1036 | _cleanup_free_ char *x = strv_join_full(c->plugins, "", "\n ", /* escape_separator = */ false); | |
1037 | log_debug("Using plugins: %s", strna(x)); | |
1038 | ||
1039 | _cleanup_free_ char *y = strv_join_full(c->envp, "", "\n ", /* escape_separator = */ false); | |
1040 | log_debug("Plugin environment: %s", strna(y)); | |
1041 | ||
1042 | _cleanup_free_ char *z = strv_join(strv_skip(c->argv, 1), " "); | |
1043 | log_debug("Plugin arguments: %s", strna(z)); | |
1044 | } | |
1045 | ||
e42931b0 | 1046 | ret = execute_strv( |
42551ea7 YW |
1047 | /* name = */ NULL, |
1048 | c->plugins, | |
1049 | /* root = */ NULL, | |
1050 | USEC_INFINITY, | |
1051 | /* callbacks = */ NULL, | |
1052 | /* callback_args = */ NULL, | |
1053 | c->argv, | |
1054 | c->envp, | |
1055 | EXEC_DIR_SKIP_REMAINING); | |
42551ea7 YW |
1056 | |
1057 | r = context_remove_entry_dir(c); | |
1058 | if (r < 0) | |
1059 | return r; | |
1060 | ||
e42931b0 YW |
1061 | /* This returns 0 on success, positive exit code on plugin failure, negative errno on other failures. */ |
1062 | return ret; | |
42551ea7 YW |
1063 | } |
1064 | ||
b4afa94c | 1065 | static bool bypass(void) { |
daa2547e | 1066 | return should_bypass("KERNEL_INSTALL"); |
b4afa94c DDM |
1067 | } |
1068 | ||
ec1399f5 LP |
1069 | static int do_add( |
1070 | Context *c, | |
1071 | const char *version, | |
1072 | const char *kernel, | |
1073 | char **initrds) { | |
1074 | ||
1075 | int r; | |
1076 | ||
1077 | assert(c); | |
1078 | assert(version); | |
1079 | assert(kernel); | |
1080 | ||
1081 | r = context_set_version(c, version); | |
1082 | if (r < 0) | |
1083 | return r; | |
1084 | ||
1085 | r = context_set_kernel(c, kernel); | |
1086 | if (r < 0) | |
1087 | return r; | |
1088 | ||
1089 | r = context_set_initrds(c, initrds); | |
1090 | if (r < 0) | |
1091 | return r; | |
1092 | ||
1093 | r = context_prepare_execution(c); | |
1094 | if (r < 0) | |
1095 | return r; | |
1096 | ||
1097 | return context_execute(c); | |
1098 | } | |
1099 | ||
758d1476 LP |
1100 | static int kernel_from_version(const char *version, char **ret_kernel) { |
1101 | _cleanup_free_ char *vmlinuz = NULL; | |
1102 | int r; | |
1103 | ||
1104 | assert(version); | |
1105 | ||
1106 | vmlinuz = path_join("/usr/lib/modules/", version, "/vmlinuz"); | |
1107 | if (!vmlinuz) | |
1108 | return log_oom(); | |
1109 | ||
3f8999a7 | 1110 | r = access_nofollow(vmlinuz, F_OK); |
7c1dd9e2 MY |
1111 | if (r == -ENOENT) |
1112 | return log_error_errno(r, "Kernel image not installed to '%s', requiring manual kernel image path specification.", vmlinuz); | |
1113 | if (r < 0) | |
c62345ff | 1114 | return log_error_errno(r, "Failed to determine if kernel image is installed to '%s': %m", vmlinuz); |
758d1476 LP |
1115 | |
1116 | *ret_kernel = TAKE_PTR(vmlinuz); | |
1117 | return 0; | |
1118 | } | |
1119 | ||
42551ea7 YW |
1120 | static int verb_add(int argc, char *argv[], void *userdata) { |
1121 | Context *c = ASSERT_PTR(userdata); | |
9d9e99a8 LP |
1122 | _cleanup_free_ char *vmlinuz = NULL; |
1123 | const char *version, *kernel; | |
1124 | char **initrds; | |
1125 | struct utsname un; | |
42551ea7 YW |
1126 | int r; |
1127 | ||
42551ea7 YW |
1128 | assert(argv); |
1129 | ||
04a8c2ce | 1130 | if (arg_root) |
398760c8 | 1131 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'add' does not support --root= or --image=."); |
04a8c2ce | 1132 | |
b4afa94c DDM |
1133 | if (bypass()) |
1134 | return 0; | |
1135 | ||
42551ea7 YW |
1136 | c->action = ACTION_ADD; |
1137 | ||
9d9e99a8 LP |
1138 | /* We use the same order of arguments that "inspect" introduced, i.e. if only on argument is |
1139 | * specified we take it as the kernel path, not the version, i.e. it's the first argument that is | |
1140 | * optional, not the 2nd. */ | |
1141 | version = argc > 2 ? empty_or_dash_to_null(argv[1]) : NULL; | |
1142 | kernel = argc > 2 ? empty_or_dash_to_null(argv[2]) : | |
1143 | (argc > 1 ? empty_or_dash_to_null(argv[1]) : NULL); | |
1144 | initrds = strv_skip(argv, 3); | |
1145 | ||
1146 | if (!version) { | |
1147 | assert_se(uname(&un) >= 0); | |
1148 | version = un.release; | |
1149 | } | |
1150 | ||
1151 | if (!kernel) { | |
758d1476 LP |
1152 | r = kernel_from_version(version, &vmlinuz); |
1153 | if (r < 0) | |
1154 | return r; | |
9d9e99a8 LP |
1155 | |
1156 | kernel = vmlinuz; | |
1157 | } | |
1158 | ||
ec1399f5 LP |
1159 | return do_add(c, version, kernel, initrds); |
1160 | } | |
42551ea7 | 1161 | |
ec1399f5 LP |
1162 | static int verb_add_all(int argc, char *argv[], void *userdata) { |
1163 | Context *c = ASSERT_PTR(userdata); | |
1164 | _cleanup_close_ int fd = -EBADF; | |
1165 | size_t n = 0; | |
1166 | int ret = 0, r; | |
42551ea7 | 1167 | |
ec1399f5 | 1168 | assert(argv); |
42551ea7 | 1169 | |
398760c8 AAF |
1170 | if (arg_root) |
1171 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'add-all' does not support --root= or --image=."); | |
1172 | ||
ec1399f5 LP |
1173 | if (bypass()) |
1174 | return 0; | |
1175 | ||
1176 | c->action = ACTION_ADD; | |
1177 | ||
50cc0ee6 | 1178 | fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL); |
ec1399f5 | 1179 | if (fd < 0) |
50cc0ee6 | 1180 | return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root)); |
ec1399f5 LP |
1181 | |
1182 | _cleanup_free_ DirectoryEntries *de = NULL; | |
1183 | r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); | |
42551ea7 | 1184 | if (r < 0) |
ec1399f5 | 1185 | return log_error_errno(r, "Failed to numerate /usr/lib/modules/ contents: %m"); |
42551ea7 | 1186 | |
ec1399f5 | 1187 | FOREACH_ARRAY(d, de->entries, de->n_entries) { |
ec1399f5 LP |
1188 | r = dirent_ensure_type(fd, *d); |
1189 | if (r < 0) { | |
1190 | if (r != -ENOENT) /* don't log if just gone by now */ | |
50cc0ee6 | 1191 | log_debug_errno(r, "Failed to check if '%s/usr/lib/modules/%s' is a directory, ignoring: %m", strempty(arg_root), (*d)->d_name); |
ec1399f5 LP |
1192 | continue; |
1193 | } | |
1194 | ||
1195 | if ((*d)->d_type != DT_DIR) | |
1196 | continue; | |
1197 | ||
1198 | _cleanup_free_ char *fn = path_join((*d)->d_name, "vmlinuz"); | |
1199 | if (!fn) | |
1200 | return log_oom(); | |
1201 | ||
1202 | if (faccessat(fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0) { | |
1203 | if (errno != ENOENT) | |
50cc0ee6 | 1204 | log_debug_errno(errno, "Failed to check if '%s/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", strempty(arg_root), (*d)->d_name); |
ec1399f5 LP |
1205 | |
1206 | log_notice("Not adding version '%s', because kernel image not found.", (*d)->d_name); | |
1207 | continue; | |
1208 | } | |
1209 | ||
1210 | _cleanup_(context_done) Context copy = CONTEXT_NULL; | |
1211 | ||
1212 | r = context_copy(c, ©); | |
1213 | if (r < 0) | |
1214 | return log_error_errno(r, "Failed to copy execution context: %m"); | |
1215 | ||
50cc0ee6 DDM |
1216 | /* do_add() will look up the path in the correct root directory so we don't need to prefix it |
1217 | * with arg_root here. */ | |
ec1399f5 LP |
1218 | _cleanup_free_ char *full = path_join("/usr/lib/modules/", fn); |
1219 | if (!full) | |
1220 | return log_oom(); | |
1221 | ||
1222 | r = do_add(©, | |
1223 | /* version= */ (*d)->d_name, | |
1224 | /* kernel= */ full, | |
1225 | /* initrds= */ NULL); | |
e42931b0 | 1226 | if (r == 0) |
ec1399f5 | 1227 | n++; |
e42931b0 YW |
1228 | else if (ret == 0) |
1229 | ret = r; | |
ec1399f5 LP |
1230 | } |
1231 | ||
1232 | if (n > 0) | |
ec9ff6ea | 1233 | log_debug("Installed %zu kernel(s).", n); |
ec1399f5 LP |
1234 | else if (ret == 0) |
1235 | ret = log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No kernels to install found."); | |
1236 | ||
1237 | return ret; | |
42551ea7 YW |
1238 | } |
1239 | ||
1240 | static int run_as_installkernel(int argc, char *argv[], Context *c) { | |
1241 | /* kernel's install.sh invokes us as | |
1242 | * /sbin/installkernel <version> <vmlinuz> <map> <installation-dir> | |
1243 | * We ignore the last two arguments. */ | |
1244 | if (optind + 2 > argc) | |
1245 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'installkernel' command requires at least two arguments."); | |
1246 | ||
1247 | return verb_add(3, STRV_MAKE("add", argv[optind], argv[optind+1]), c); | |
1248 | } | |
1249 | ||
1250 | static int verb_remove(int argc, char *argv[], void *userdata) { | |
1251 | Context *c = ASSERT_PTR(userdata); | |
1252 | int r; | |
1253 | ||
149aedb8 | 1254 | assert(argc >= 2); |
42551ea7 YW |
1255 | assert(argv); |
1256 | ||
04a8c2ce | 1257 | if (arg_root) |
398760c8 | 1258 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'remove' does not support --root= or --image=."); |
04a8c2ce | 1259 | |
149aedb8 YW |
1260 | if (argc > 2) |
1261 | log_debug("Too many arguments specified. 'kernel-install remove' takes only kernel version. " | |
1262 | "Ignoring residual arguments."); | |
1263 | ||
b4afa94c DDM |
1264 | if (bypass()) |
1265 | return 0; | |
1266 | ||
42551ea7 YW |
1267 | c->action = ACTION_REMOVE; |
1268 | ||
9d9e99a8 LP |
1269 | /* Note, we do not automatically derive the kernel version to remove from uname() here (unlike we do |
1270 | * it for the "add" verb), since we don't want to make it too easy to uninstall your running | |
1271 | * kernel, as a safety precaution */ | |
1272 | ||
42551ea7 YW |
1273 | r = context_set_version(c, argv[1]); |
1274 | if (r < 0) | |
1275 | return r; | |
1276 | ||
1277 | r = context_prepare_execution(c); | |
1278 | if (r < 0) | |
1279 | return r; | |
1280 | ||
1281 | return context_execute(c); | |
1282 | } | |
1283 | ||
1284 | static int verb_inspect(int argc, char *argv[], void *userdata) { | |
1285 | Context *c = ASSERT_PTR(userdata); | |
bdd36c00 | 1286 | _cleanup_(table_unrefp) Table *t = NULL; |
758d1476 LP |
1287 | _cleanup_free_ char *vmlinuz = NULL; |
1288 | const char *version, *kernel; | |
1289 | char **initrds; | |
1290 | struct utsname un; | |
42551ea7 YW |
1291 | int r; |
1292 | ||
1293 | c->action = ACTION_INSPECT; | |
1294 | ||
758d1476 LP |
1295 | /* When only a single parameter is specified 'inspect' it's the kernel image path, and not the kernel |
1296 | * version. i.e. it's the first argument that is optional, not the 2nd. That's a bit unfortunate, but | |
1297 | * we keep the behaviour for compatibility. If users want to specify only the version (and have the | |
1298 | * kernel image path derived automatically), then they may specify an empty string or "dash" as | |
1299 | * kernel image path. */ | |
1300 | version = argc > 2 ? empty_or_dash_to_null(argv[1]) : NULL; | |
1301 | kernel = argc > 2 ? empty_or_dash_to_null(argv[2]) : | |
1302 | (argc > 1 ? empty_or_dash_to_null(argv[1]) : NULL); | |
1303 | initrds = strv_skip(argv, 3); | |
bdd36c00 | 1304 | |
dbab0056 | 1305 | if (!version && !arg_root) { |
758d1476 LP |
1306 | assert_se(uname(&un) >= 0); |
1307 | version = un.release; | |
1308 | } | |
bdd36c00 | 1309 | |
dbab0056 | 1310 | if (!kernel && version) { |
758d1476 | 1311 | r = kernel_from_version(version, &vmlinuz); |
bdd36c00 DDM |
1312 | if (r < 0) |
1313 | return r; | |
758d1476 LP |
1314 | |
1315 | kernel = vmlinuz; | |
f7665b7f YW |
1316 | } |
1317 | ||
758d1476 LP |
1318 | r = context_set_version(c, version); |
1319 | if (r < 0) | |
1320 | return r; | |
1321 | ||
1322 | r = context_set_kernel(c, kernel); | |
1323 | if (r < 0) | |
1324 | return r; | |
1325 | ||
1326 | r = context_set_initrds(c, initrds); | |
1327 | if (r < 0) | |
1328 | return r; | |
1329 | ||
42551ea7 YW |
1330 | r = context_prepare_execution(c); |
1331 | if (r < 0) | |
1332 | return r; | |
1333 | ||
bdd36c00 DDM |
1334 | t = table_new_vertical(); |
1335 | if (!t) | |
1336 | return log_oom(); | |
1337 | ||
1338 | r = table_add_many(t, | |
1339 | TABLE_FIELD, "Machine ID", | |
1340 | TABLE_ID128, c->machine_id, | |
1341 | TABLE_FIELD, "Kernel Image Type", | |
1342 | TABLE_STRING, kernel_image_type_to_string(c->kernel_image_type), | |
1343 | TABLE_FIELD, "Layout", | |
1344 | TABLE_STRING, context_get_layout(c), | |
1345 | TABLE_FIELD, "Boot Root", | |
1346 | TABLE_STRING, c->boot_root, | |
1347 | TABLE_FIELD, "Entry Token Type", | |
1348 | TABLE_STRING, boot_entry_token_type_to_string(c->entry_token_type), | |
1349 | TABLE_FIELD, "Entry Token", | |
1350 | TABLE_STRING, c->entry_token, | |
1351 | TABLE_FIELD, "Entry Directory", | |
1352 | TABLE_STRING, c->entry_dir, | |
1353 | TABLE_FIELD, "Kernel Version", | |
1354 | TABLE_STRING, c->version, | |
1355 | TABLE_FIELD, "Kernel", | |
1356 | TABLE_STRING, c->kernel, | |
1357 | TABLE_FIELD, "Initrds", | |
1358 | TABLE_STRV, c->initrds, | |
1359 | TABLE_FIELD, "Initrd Generator", | |
1360 | TABLE_STRING, c->initrd_generator, | |
1361 | TABLE_FIELD, "UKI Generator", | |
1362 | TABLE_STRING, c->uki_generator, | |
1363 | TABLE_FIELD, "Plugins", | |
1364 | TABLE_STRV, c->plugins, | |
1365 | TABLE_FIELD, "Plugin Environment", | |
1366 | TABLE_STRV, c->envp); | |
1367 | if (r < 0) | |
1368 | return table_log_add_error(r); | |
1369 | ||
23441a3d | 1370 | if (!sd_json_format_enabled(arg_json_format_flags)) { |
bdd36c00 DDM |
1371 | r = table_add_many(t, |
1372 | TABLE_FIELD, "Plugin Arguments", | |
1373 | TABLE_STRV, strv_skip(c->argv, 1)); | |
1374 | if (r < 0) | |
1375 | return table_log_add_error(r); | |
1376 | } | |
42551ea7 | 1377 | |
bdd36c00 | 1378 | table_set_ersatz_string(t, TABLE_ERSATZ_UNSET); |
42551ea7 | 1379 | |
bdd36c00 DDM |
1380 | for (size_t row = 1; row < table_get_rows(t); row++) { |
1381 | _cleanup_free_ char *name = NULL; | |
42551ea7 | 1382 | |
bdd36c00 DDM |
1383 | name = strdup(table_get_at(t, row, 0)); |
1384 | if (!name) | |
1385 | return log_oom(); | |
42551ea7 | 1386 | |
bdd36c00 DDM |
1387 | r = table_set_json_field_name(t, row - 1, delete_chars(name, " ")); |
1388 | if (r < 0) | |
1389 | return log_error_errno(r, "Failed to set JSON field name: %m"); | |
1390 | } | |
1391 | ||
1392 | return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, /* show_header= */ false); | |
42551ea7 YW |
1393 | } |
1394 | ||
658e6cc4 | 1395 | static int verb_list(int argc, char *argv[], void *userdata) { |
50cc0ee6 | 1396 | Context *c = ASSERT_PTR(userdata); |
658e6cc4 LP |
1397 | _cleanup_close_ int fd = -EBADF; |
1398 | int r; | |
1399 | ||
50cc0ee6 | 1400 | fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL); |
658e6cc4 | 1401 | if (fd < 0) |
50cc0ee6 | 1402 | return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root)); |
658e6cc4 LP |
1403 | |
1404 | _cleanup_free_ DirectoryEntries *de = NULL; | |
1405 | r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); | |
1406 | if (r < 0) | |
1407 | return log_error_errno(r, "Failed to numerate /usr/lib/modules/ contents: %m"); | |
1408 | ||
1409 | _cleanup_(table_unrefp) Table *table = NULL; | |
1410 | table = table_new("version", "has kernel", "path"); | |
1411 | if (!table) | |
1412 | return log_oom(); | |
1413 | ||
1414 | table_set_ersatz_string(table, TABLE_ERSATZ_DASH); | |
1415 | table_set_align_percent(table, table_get_cell(table, 0, 1), 100); | |
1416 | ||
1417 | FOREACH_ARRAY(d, de->entries, de->n_entries) { | |
658e6cc4 LP |
1418 | _cleanup_free_ char *j = path_join("/usr/lib/modules/", (*d)->d_name); |
1419 | if (!j) | |
1420 | return log_oom(); | |
1421 | ||
1422 | r = dirent_ensure_type(fd, *d); | |
1423 | if (r < 0) { | |
1424 | if (r != -ENOENT) /* don't log if just gone by now */ | |
50cc0ee6 | 1425 | log_debug_errno(r, "Failed to check if '%s/%s' is a directory, ignoring: %m", strempty(arg_root), j); |
658e6cc4 LP |
1426 | continue; |
1427 | } | |
1428 | ||
1429 | if ((*d)->d_type != DT_DIR) | |
1430 | continue; | |
1431 | ||
1432 | _cleanup_free_ char *fn = path_join((*d)->d_name, "vmlinuz"); | |
1433 | if (!fn) | |
1434 | return log_oom(); | |
1435 | ||
1436 | bool exists; | |
1437 | if (faccessat(fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0) { | |
1438 | if (errno != ENOENT) | |
50cc0ee6 | 1439 | log_debug_errno(errno, "Failed to check if '%s/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", strempty(arg_root), (*d)->d_name); |
658e6cc4 LP |
1440 | |
1441 | exists = false; | |
1442 | } else | |
1443 | exists = true; | |
1444 | ||
1445 | r = table_add_many(table, | |
1446 | TABLE_STRING, (*d)->d_name, | |
1447 | TABLE_BOOLEAN_CHECKMARK, exists, | |
1448 | TABLE_SET_COLOR, ansi_highlight_green_red(exists), | |
1449 | TABLE_PATH, j); | |
1450 | if (r < 0) | |
1451 | return table_log_add_error(r); | |
1452 | } | |
1453 | ||
1454 | return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); | |
1455 | } | |
1456 | ||
42551ea7 YW |
1457 | static int help(void) { |
1458 | _cleanup_free_ char *link = NULL; | |
1459 | int r; | |
1460 | ||
1461 | r = terminal_urlify_man("kernel-install", "8", &link); | |
1462 | if (r < 0) | |
1463 | return log_oom(); | |
1464 | ||
1465 | printf("%1$s [OPTIONS...] COMMAND ...\n\n" | |
111f9889 | 1466 | "%5$sAdd and remove kernel and initrd images to and from the boot partition.%6$s\n" |
658e6cc4 | 1467 | "\n%3$sUsage:%4$s\n" |
9d9e99a8 | 1468 | " kernel-install [OPTIONS...] add [[[KERNEL-VERSION] KERNEL-IMAGE] [INITRD ...]]\n" |
ec1399f5 | 1469 | " kernel-install [OPTIONS...] add-all\n" |
eb25844f | 1470 | " kernel-install [OPTIONS...] remove KERNEL-VERSION\n" |
758d1476 LP |
1471 | " kernel-install [OPTIONS...] inspect [[[KERNEL-VERSION] KERNEL-IMAGE]\n" |
1472 | " [INITRD ...]]\n" | |
658e6cc4 LP |
1473 | " kernel-install [OPTIONS...] list\n" |
1474 | "\n%3$sOptions:%4$s\n" | |
04a8c2ce DDM |
1475 | " -h --help Show this help\n" |
1476 | " --version Show package version\n" | |
1477 | " -v --verbose Increase verbosity\n" | |
1478 | " --esp-path=PATH Path to the EFI System Partition (ESP)\n" | |
1479 | " --boot-path=PATH Path to the $BOOT partition\n" | |
b79621aa | 1480 | " --make-entry-directory=yes|no|auto\n" |
04a8c2ce | 1481 | " Create $BOOT/ENTRY-TOKEN/ directory\n" |
1fd90ed3 | 1482 | " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n" |
04a8c2ce DDM |
1483 | " Entry token to use for this installation\n" |
1484 | " --no-pager Do not pipe inspect output into a pager\n" | |
1485 | " --json=pretty|short|off Generate JSON output\n" | |
658e6cc4 | 1486 | " --no-legend Do not show the headers and footers\n" |
04a8c2ce DDM |
1487 | " --root=PATH Operate on an alternate filesystem root\n" |
1488 | " --image=PATH Operate on disk image as filesystem root\n" | |
1489 | " --image-policy=POLICY Specify disk image dissection policy\n" | |
eb25844f ZJS |
1490 | "\n" |
1491 | "This program may also be invoked as 'installkernel':\n" | |
1492 | " installkernel [OPTIONS...] VERSION VMLINUZ [MAP] [INSTALLATION-DIR]\n" | |
1493 | "(The optional arguments are passed by kernel build system, but ignored.)\n" | |
1494 | "\n" | |
658e6cc4 | 1495 | "See the %2$s for details.\n", |
42551ea7 | 1496 | program_invocation_short_name, |
658e6cc4 LP |
1497 | link, |
1498 | ansi_underline(), | |
42551ea7 | 1499 | ansi_normal(), |
658e6cc4 LP |
1500 | ansi_highlight(), |
1501 | ansi_normal()); | |
42551ea7 YW |
1502 | |
1503 | return 0; | |
1504 | } | |
1505 | ||
1fd90ed3 | 1506 | static int parse_argv(int argc, char *argv[], Context *c) { |
42551ea7 YW |
1507 | enum { |
1508 | ARG_VERSION = 0x100, | |
658e6cc4 | 1509 | ARG_NO_LEGEND, |
4cff5e92 YW |
1510 | ARG_ESP_PATH, |
1511 | ARG_BOOT_PATH, | |
b79621aa | 1512 | ARG_MAKE_ENTRY_DIRECTORY, |
1fd90ed3 | 1513 | ARG_ENTRY_TOKEN, |
bdd36c00 DDM |
1514 | ARG_NO_PAGER, |
1515 | ARG_JSON, | |
04a8c2ce DDM |
1516 | ARG_ROOT, |
1517 | ARG_IMAGE, | |
1518 | ARG_IMAGE_POLICY, | |
42551ea7 YW |
1519 | }; |
1520 | static const struct option options[] = { | |
1521 | { "help", no_argument, NULL, 'h' }, | |
1522 | { "version", no_argument, NULL, ARG_VERSION }, | |
1523 | { "verbose", no_argument, NULL, 'v' }, | |
4cff5e92 YW |
1524 | { "esp-path", required_argument, NULL, ARG_ESP_PATH }, |
1525 | { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, | |
b79621aa | 1526 | { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, |
1fd90ed3 | 1527 | { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, |
bdd36c00 DDM |
1528 | { "no-pager", no_argument, NULL, ARG_NO_PAGER }, |
1529 | { "json", required_argument, NULL, ARG_JSON }, | |
04a8c2ce DDM |
1530 | { "root", required_argument, NULL, ARG_ROOT }, |
1531 | { "image", required_argument, NULL, ARG_IMAGE }, | |
1532 | { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, | |
658e6cc4 | 1533 | { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, |
42551ea7 YW |
1534 | {} |
1535 | }; | |
4cff5e92 | 1536 | int t, r; |
42551ea7 YW |
1537 | |
1538 | assert(argc >= 0); | |
1539 | assert(argv); | |
1fd90ed3 | 1540 | assert(c); |
42551ea7 YW |
1541 | |
1542 | while ((t = getopt_long(argc, argv, "hv", options, NULL)) >= 0) | |
1543 | switch (t) { | |
1544 | case 'h': | |
1545 | return help(); | |
1546 | ||
1547 | case ARG_VERSION: | |
1548 | return version(); | |
1549 | ||
658e6cc4 LP |
1550 | case ARG_NO_LEGEND: |
1551 | arg_legend = false; | |
1552 | break; | |
1553 | ||
42551ea7 YW |
1554 | case 'v': |
1555 | log_set_max_level(LOG_DEBUG); | |
1556 | arg_verbose = true; | |
1557 | break; | |
1558 | ||
4cff5e92 YW |
1559 | case ARG_ESP_PATH: |
1560 | r = parse_path_argument(optarg, /* suppress_root = */ false, &arg_esp_path); | |
1561 | if (r < 0) | |
1562 | return log_oom(); | |
1563 | break; | |
1564 | ||
1565 | case ARG_BOOT_PATH: | |
1566 | r = parse_path_argument(optarg, /* suppress_root = */ false, &arg_xbootldr_path); | |
1567 | if (r < 0) | |
1568 | return log_oom(); | |
1569 | break; | |
1570 | ||
b79621aa YW |
1571 | case ARG_MAKE_ENTRY_DIRECTORY: |
1572 | if (streq(optarg, "auto")) | |
1573 | arg_make_entry_directory = -1; | |
1574 | else { | |
1575 | r = parse_boolean_argument("--make-entry-directory=", optarg, NULL); | |
1576 | if (r < 0) | |
1577 | return r; | |
1578 | ||
1579 | arg_make_entry_directory = r; | |
1580 | } | |
1581 | break; | |
1582 | ||
1fd90ed3 YW |
1583 | case ARG_ENTRY_TOKEN: |
1584 | r = parse_boot_entry_token_type(optarg, &c->entry_token_type, &c->entry_token); | |
1585 | if (r < 0) | |
1586 | return r; | |
1587 | break; | |
1588 | ||
bdd36c00 DDM |
1589 | case ARG_NO_PAGER: |
1590 | arg_pager_flags |= PAGER_DISABLE; | |
1591 | break; | |
1592 | ||
1593 | case ARG_JSON: | |
1594 | r = parse_json_argument(optarg, &arg_json_format_flags); | |
1595 | if (r < 0) | |
1596 | return r; | |
1597 | break; | |
1598 | ||
04a8c2ce DDM |
1599 | case ARG_ROOT: |
1600 | r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root); | |
1601 | if (r < 0) | |
1602 | return r; | |
1603 | break; | |
1604 | ||
1605 | case ARG_IMAGE: | |
1606 | r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image); | |
1607 | if (r < 0) | |
1608 | return r; | |
1609 | break; | |
1610 | ||
1611 | case ARG_IMAGE_POLICY: | |
1612 | r = parse_image_policy_argument(optarg, &arg_image_policy); | |
1613 | if (r < 0) | |
1614 | return r; | |
1615 | break; | |
1616 | ||
42551ea7 YW |
1617 | case '?': |
1618 | return -EINVAL; | |
1619 | ||
1620 | default: | |
1621 | assert_not_reached(); | |
1622 | } | |
1623 | ||
04a8c2ce DDM |
1624 | if (arg_image && arg_root) |
1625 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported."); | |
1626 | ||
42551ea7 YW |
1627 | return 1; |
1628 | } | |
1629 | ||
1630 | static int run(int argc, char* argv[]) { | |
1631 | static const Verb verbs[] = { | |
9d9e99a8 | 1632 | { "add", 1, VERB_ANY, 0, verb_add }, |
ec1399f5 | 1633 | { "add-all", 1, 1, 0, verb_add_all }, |
149aedb8 | 1634 | { "remove", 2, VERB_ANY, 0, verb_remove }, |
bdd36c00 | 1635 | { "inspect", 1, VERB_ANY, VERB_DEFAULT, verb_inspect }, |
658e6cc4 | 1636 | { "list", 1, 1, 0, verb_list }, |
42551ea7 YW |
1637 | {} |
1638 | }; | |
1639 | _cleanup_(context_done) Context c = { | |
54bc114f | 1640 | .rfd = AT_FDCWD, |
42551ea7 YW |
1641 | .action = _ACTION_INVALID, |
1642 | .kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN, | |
1643 | .layout = _LAYOUT_INVALID, | |
1644 | .entry_token_type = BOOT_ENTRY_TOKEN_AUTO, | |
1645 | }; | |
04a8c2ce DDM |
1646 | _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; |
1647 | _cleanup_(umount_and_freep) char *mounted_dir = NULL; | |
42551ea7 YW |
1648 | int r; |
1649 | ||
1650 | log_setup(); | |
1651 | ||
1fd90ed3 | 1652 | r = parse_argv(argc, argv, &c); |
42551ea7 YW |
1653 | if (r <= 0) |
1654 | return r; | |
1655 | ||
04a8c2ce DDM |
1656 | if (arg_image) { |
1657 | assert(!arg_root); | |
1658 | ||
1659 | r = mount_image_privately_interactively( | |
1660 | arg_image, | |
1661 | arg_image_policy, | |
1662 | DISSECT_IMAGE_GENERIC_ROOT | | |
1663 | DISSECT_IMAGE_REQUIRE_ROOT | | |
1664 | DISSECT_IMAGE_RELAX_VAR_CHECK | | |
f4a63ce2 LP |
1665 | DISSECT_IMAGE_VALIDATE_OS | |
1666 | DISSECT_IMAGE_ALLOW_USERSPACE_VERITY, | |
04a8c2ce DDM |
1667 | &mounted_dir, |
1668 | /* ret_dir_fd= */ NULL, | |
1669 | &loop_device); | |
1670 | if (r < 0) | |
1671 | return r; | |
1672 | ||
1673 | arg_root = strdup(mounted_dir); | |
1674 | if (!arg_root) | |
1675 | return log_oom(); | |
1676 | } | |
1677 | ||
42551ea7 YW |
1678 | r = context_init(&c); |
1679 | if (r < 0) | |
1680 | return r; | |
1681 | ||
1682 | if (invoked_as(argv, "installkernel")) | |
1683 | return run_as_installkernel(argc, argv, &c); | |
1684 | ||
1685 | return dispatch_verb(argc, argv, verbs, &c); | |
1686 | } | |
1687 | ||
e42931b0 | 1688 | DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); |