• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

0 of 1 new or added line in 1 file covered. (0.0%)

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/kernel-install/kernel-install.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <getopt.h>
4
#include <stdbool.h>
5
#include <sys/utsname.h>
6

7
#include "argv-util.h"
8
#include "boot-entry.h"
9
#include "build.h"
10
#include "chase.h"
11
#include "conf-files.h"
12
#include "dirent-util.h"
13
#include "env-file.h"
14
#include "env-util.h"
15
#include "exec-util.h"
16
#include "extract-word.h"
17
#include "fd-util.h"
18
#include "fileio.h"
19
#include "find-esp.h"
20
#include "format-table.h"
21
#include "fs-util.h"
22
#include "id128-util.h"
23
#include "image-policy.h"
24
#include "kernel-config.h"
25
#include "kernel-image.h"
26
#include "main-func.h"
27
#include "mkdir.h"
28
#include "mount-util.h"
29
#include "parse-argument.h"
30
#include "path-util.h"
31
#include "pretty-print.h"
32
#include "recurse-dir.h"
33
#include "rm-rf.h"
34
#include "stat-util.h"
35
#include "string-table.h"
36
#include "string-util.h"
37
#include "strv.h"
38
#include "tmpfile-util.h"
39
#include "verbs.h"
40

41
static bool arg_verbose = false;
42
static char *arg_esp_path = NULL;
43
static char *arg_xbootldr_path = NULL;
44
static int arg_make_entry_directory = -1; /* tristate */
45
static PagerFlags arg_pager_flags = 0;
46
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
47
static char *arg_root = NULL;
48
static char *arg_image = NULL;
49
static ImagePolicy *arg_image_policy = NULL;
50
static bool arg_legend = true;
51

52
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
×
53
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
×
54
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
×
55
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
×
UNCOV
56
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
×
57

58
typedef enum Action {
59
        ACTION_ADD,
60
        ACTION_REMOVE,
61
        ACTION_INSPECT,
62
        _ACTION_MAX,
63
        _ACTION_INVALID = -EINVAL,
64
} Action;
65

66
typedef enum Layout {
67
        LAYOUT_AUTO,
68
        LAYOUT_UKI,
69
        LAYOUT_BLS,
70
        LAYOUT_OTHER,
71
        _LAYOUT_MAX,
72
        _LAYOUT_INVALID = -EINVAL,
73
} Layout;
74

75
static const char * const layout_table[_LAYOUT_MAX] = {
76
        [LAYOUT_AUTO]  = "auto",
77
        [LAYOUT_UKI]   = "uki",
78
        [LAYOUT_BLS]   = "bls",
79
        [LAYOUT_OTHER] = "other",
80
};
81

UNCOV
82
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(layout, Layout);
×
83

84
typedef struct Context {
85
        int rfd;
86
        Action action;
87
        sd_id128_t machine_id;
88
        bool machine_id_is_random;
89
        KernelImageType kernel_image_type;
90
        Layout layout;
91
        char *layout_other;
92
        char *conf_root;
93
        char *boot_root;
94
        BootEntryTokenType entry_token_type;
95
        char *entry_token;
96
        char *entry_dir;
97
        char *version;
98
        char *kernel;
99
        char **initrds;
100
        char *initrd_generator;
101
        char *uki_generator;
102
        char *staging_area;
103
        char **plugins;
104
        char **argv;
105
        char **envp;
106
} Context;
107

108
#define CONTEXT_NULL (Context) { .rfd = -EBADF }
109

110
static void context_done(Context *c) {
×
UNCOV
111
        assert(c);
×
112

113
        free(c->layout_other);
×
114
        free(c->conf_root);
×
115
        free(c->boot_root);
×
116
        free(c->entry_token);
×
117
        free(c->entry_dir);
×
118
        free(c->version);
×
119
        free(c->kernel);
×
120
        strv_free(c->initrds);
×
121
        free(c->initrd_generator);
×
122
        free(c->uki_generator);
×
123
        if (c->action == ACTION_INSPECT)
×
UNCOV
124
                free(c->staging_area);
×
125
        else
126
                rm_rf_physical_and_free(c->staging_area);
×
127
        strv_free(c->plugins);
×
128
        strv_free(c->argv);
×
UNCOV
129
        strv_free(c->envp);
×
130

131
        safe_close(c->rfd);
×
UNCOV
132
}
×
133

134
static int context_copy(const Context *source, Context *ret) {
×
UNCOV
135
        int r;
×
136

137
        assert(source);
×
138
        assert(ret);
×
UNCOV
139
        assert(source->rfd >= 0 || source->rfd == AT_FDCWD);
×
140

UNCOV
141
        _cleanup_(context_done) Context copy = (Context) {
×
142
                .rfd = AT_FDCWD,
UNCOV
143
                .action = source->action,
×
144
                .machine_id = source->machine_id,
145
                .machine_id_is_random = source->machine_id_is_random,
×
146
                .kernel_image_type = source->kernel_image_type,
×
147
                .layout = source->layout,
×
UNCOV
148
                .entry_token_type = source->entry_token_type,
×
149
        };
150

151
        if (source->rfd >= 0) {
×
152
                copy.rfd = fd_reopen(source->rfd, O_CLOEXEC|O_DIRECTORY|O_PATH);
×
UNCOV
153
                if (copy.rfd < 0)
×
154
                        return copy.rfd;
155
        }
156

157
        r = strdup_to(&copy.layout_other, source->layout_other);
×
UNCOV
158
        if (r < 0)
×
159
                return r;
160
        r = strdup_to(&copy.conf_root, source->conf_root);
×
UNCOV
161
        if (r < 0)
×
162
                return r;
163
        r = strdup_to(&copy.boot_root, source->boot_root);
×
UNCOV
164
        if (r < 0)
×
165
                return r;
166
        r = strdup_to(&copy.entry_token, source->entry_token);
×
UNCOV
167
        if (r < 0)
×
168
                return r;
169
        r = strdup_to(&copy.entry_dir, source->entry_dir);
×
UNCOV
170
        if (r < 0)
×
171
                return r;
172
        r = strdup_to(&copy.version, source->version);
×
UNCOV
173
        if (r < 0)
×
174
                return r;
175
        r = strdup_to(&copy.kernel, source->kernel);
×
UNCOV
176
        if (r < 0)
×
177
                return r;
178
        r = strv_copy_unless_empty(source->initrds, &copy.initrds);
×
UNCOV
179
        if (r < 0)
×
180
                return r;
181
        r = strdup_to(&copy.initrd_generator, source->initrd_generator);
×
UNCOV
182
        if (r < 0)
×
183
                return r;
184
        r = strdup_to(&copy.uki_generator, source->uki_generator);
×
UNCOV
185
        if (r < 0)
×
186
                return r;
187
        r = strdup_to(&copy.staging_area, source->staging_area);
×
UNCOV
188
        if (r < 0)
×
189
                return r;
190
        r = strv_copy_unless_empty(source->plugins, &copy.plugins);
×
UNCOV
191
        if (r < 0)
×
192
                return r;
193
        r = strv_copy_unless_empty(source->argv, &copy.argv);
×
UNCOV
194
        if (r < 0)
×
195
                return r;
196
        r = strv_copy_unless_empty(source->envp, &copy.envp);
×
UNCOV
197
        if (r < 0)
×
198
                return r;
199

200
        *ret = copy;
×
UNCOV
201
        copy = CONTEXT_NULL;
×
202

UNCOV
203
        return 0;
×
204
}
205

206
static int context_open_root(Context *c) {
×
UNCOV
207
        int r;
×
208

209
        assert(c);
×
UNCOV
210
        assert(c->rfd < 0);
×
211

UNCOV
212
        if (isempty(arg_root))
×
213
                return 0;
214

215
        r = path_is_root(arg_root);
×
216
        if (r < 0)
×
217
                return log_error_errno(r, "Failed to determine if '%s' is the root directory: %m", arg_root);
×
UNCOV
218
        if (r > 0)
×
219
                return 0;
220

221
        c->rfd = open(empty_to_root(arg_root), O_CLOEXEC | O_DIRECTORY | O_PATH);
×
222
        if (c->rfd < 0)
×
UNCOV
223
                return log_error_errno(errno, "Failed to open root directory '%s': %m", empty_to_root(arg_root));
×
224

225
        return 0;
226
}
227

228
static const char* context_get_layout(const Context *c) {
×
229
        assert(c);
×
UNCOV
230
        assert(c->layout >= 0);
×
231

UNCOV
232
        return c->layout_other ?: layout_to_string(c->layout);
×
233
}
234

235
static int context_set_layout(Context *c, const char *s, const char *source) {
×
UNCOV
236
        Layout t;
×
237

238
        assert(c);
×
UNCOV
239
        assert(source);
×
240

UNCOV
241
        if (c->layout >= 0 || !s)
×
242
                return 0;
243

UNCOV
244
        assert(!c->layout_other);
×
245

246
        t = layout_from_string(s);
×
247
        if (t >= 0)
×
248
                c->layout = t;
×
249
        else if (isempty(s))
×
UNCOV
250
                c->layout = LAYOUT_AUTO;
×
251
        else {
252
                c->layout_other = strdup(s);
×
253
                if (!c->layout_other)
×
UNCOV
254
                        return log_oom();
×
255

UNCOV
256
                c->layout = LAYOUT_OTHER;
×
257
        }
258

UNCOV
259
        log_debug("layout=%s set via %s", context_get_layout(c), source);
×
260
        return 1;
261
}
262

263
static int context_set_machine_id(Context *c, const char *s, const char *source) {
×
UNCOV
264
        int r;
×
265

266
        assert(c);
×
UNCOV
267
        assert(source);
×
268

269
        if (!sd_id128_is_null(c->machine_id) || !s)
×
UNCOV
270
                return 0;
×
271

272
        r = sd_id128_from_string(s, &c->machine_id);
×
273
        if (r < 0)
×
UNCOV
274
                return log_warning_errno(r, "Failed to parse machine ID specified via %s, ignoring.", source);
×
275

276
        if (sd_id128_is_null(c->machine_id))
×
UNCOV
277
                return 0;
×
278

279
        log_debug("MACHINE_ID=%s set via %s.", SD_ID128_TO_STRING(c->machine_id), source);
×
UNCOV
280
        return 1;
×
281
}
282

283
static int context_set_string(const char *s, const char *source, const char *name, char **dest) {
×
UNCOV
284
        char *p;
×
285

286
        assert(source);
×
287
        assert(name);
×
UNCOV
288
        assert(dest);
×
289

UNCOV
290
        if (*dest || !s)
×
291
                return 0;
292

293
        p = strdup(s);
×
294
        if (!p)
×
UNCOV
295
                return log_oom();
×
296

UNCOV
297
        log_debug("%s (%s) set via %s.", name, p, source);
×
298

299
        *dest = p;
×
UNCOV
300
        return 1;
×
301
}
302

303
static int context_set_initrd_generator(Context *c, const char *s, const char *source) {
×
304
        assert(c);
×
UNCOV
305
        return context_set_string(s, source, "INITRD_GENERATOR", &c->initrd_generator);
×
306
}
307

308
static int context_set_uki_generator(Context *c, const char *s, const char *source) {
×
309
        assert(c);
×
UNCOV
310
        return context_set_string(s, source, "UKI_GENERATOR", &c->uki_generator);
×
311
}
312

313
static int context_set_version(Context *c, const char *s) {
×
UNCOV
314
        assert(c);
×
315

316
        if (s && !filename_is_valid(s))
×
UNCOV
317
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version specified: %s", s);
×
318

UNCOV
319
        return context_set_string(s, "command line", "kernel version", &c->version);
×
320
}
321

322
static int context_set_path(Context *c, const char *s, const char *source, const char *name, char **dest) {
×
323
        char *p;
×
UNCOV
324
        int r;
×
325

326
        assert(c);
×
327
        assert(source);
×
328
        assert(name);
×
UNCOV
329
        assert(dest);
×
330

331
        if (*dest || !s)
×
UNCOV
332
                return 0;
×
333

334
        if (c->rfd >= 0) {
×
335
                r = chaseat(c->rfd, s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL);
×
336
                if (r < 0)
×
UNCOV
337
                        return log_warning_errno(r, "Failed to chase path %s for %s specified via %s, ignoring: %m",
×
338
                                                 s, name, source);
339
        } else {
340
                r = path_make_absolute_cwd(s, &p);
×
341
                if (r < 0)
×
UNCOV
342
                        return log_warning_errno(r, "Failed to make path '%s' for %s specified via %s absolute, ignoring: %m",
×
343
                                                 s, name, source);
344
        }
345

UNCOV
346
        log_debug("%s (%s) set via %s.", name, p, source);
×
347

348
        *dest = p;
×
UNCOV
349
        return 1;
×
350
}
351

352
static int context_set_boot_root(Context *c, const char *s, const char *source) {
×
353
        assert(c);
×
UNCOV
354
        return context_set_path(c, s, source, "BOOT_ROOT", &c->boot_root);
×
355
}
356

357
static int context_set_conf_root(Context *c, const char *s, const char *source) {
×
358
        assert(c);
×
UNCOV
359
        return context_set_path(c, s, source, "CONF_ROOT", &c->conf_root);
×
360
}
361

362
static int context_set_kernel(Context *c, const char *s) {
×
363
        assert(c);
×
UNCOV
364
        return context_set_path(c, s, "command line", "kernel image file", &c->kernel);
×
365
}
366

367
static int context_set_path_strv(Context *c, char* const* strv, const char *source, const char *name, char ***dest) {
×
368
        _cleanup_strv_free_ char **w = NULL;
×
UNCOV
369
        int r;
×
370

371
        assert(c);
×
372
        assert(source);
×
373
        assert(name);
×
UNCOV
374
        assert(dest);
×
375

UNCOV
376
        if (*dest)
×
377
                return 0;
378

379
        STRV_FOREACH(s, strv) {
×
UNCOV
380
                char *p;
×
381

382
                if (c->rfd >= 0) {
×
383
                        r = chaseat(c->rfd, *s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL);
×
384
                        if (r < 0)
×
UNCOV
385
                                return log_warning_errno(r, "Failed to chase path %s for %s specified via %s: %m",
×
386
                                                         *s, name, source);
387
                } else {
388
                        r = path_make_absolute_cwd(*s, &p);
×
389
                        if (r < 0)
×
UNCOV
390
                                return log_warning_errno(r, "Failed to make path '%s' for %s specified via %s absolute, ignoring: %m",
×
391
                                                         *s, name, source);
392
                }
393
                r = strv_consume(&w, p);
×
394
                if (r < 0)
×
UNCOV
395
                        return log_oom();
×
396
        }
397

UNCOV
398
        if (strv_isempty(w))
×
399
                return 0;
400

UNCOV
401
        log_debug("%s set via %s", name, source);
×
402

403
        *dest = TAKE_PTR(w);
×
UNCOV
404
        return 1;
×
405
}
406

407
static int context_set_plugins(Context *c, const char *s, const char *source) {
×
408
        _cleanup_strv_free_ char **v = NULL;
×
UNCOV
409
        int r;
×
410

UNCOV
411
        assert(c);
×
412

UNCOV
413
        if (c->plugins || !s)
×
414
                return 0;
415

416
        r = strv_split_full(&v, s, NULL, EXTRACT_UNQUOTE);
×
417
        if (r < 0)
×
UNCOV
418
                return log_error_errno(r, "Failed to parse plugin paths from %s: %m", source);
×
419

UNCOV
420
        return context_set_path_strv(c, v, source, "plugins", &c->plugins);
×
421
}
422

423
static int context_set_initrds(Context *c, char* const* strv) {
×
424
        assert(c);
×
UNCOV
425
        return context_set_path_strv(c, strv, "command line", "initrds", &c->initrds);
×
426
}
427

428
static int context_load_environment(Context *c) {
×
UNCOV
429
        assert(c);
×
430

431
        (void) context_set_machine_id(c, getenv("MACHINE_ID"), "environment");
×
432
        (void) context_set_boot_root(c, getenv("BOOT_ROOT"), "environment");
×
433
        (void) context_set_conf_root(c, getenv("KERNEL_INSTALL_CONF_ROOT"), "environment");
×
434
        (void) context_set_plugins(c, getenv("KERNEL_INSTALL_PLUGINS"), "environment");
×
UNCOV
435
        return 0;
×
436
}
437

438
static int context_load_install_conf(Context *c) {
×
439
        _cleanup_free_ char *machine_id = NULL, *boot_root = NULL, *layout = NULL,
×
440
                            *initrd_generator = NULL, *uki_generator = NULL;
×
UNCOV
441
        int r;
×
442

UNCOV
443
        assert(c);
×
444

445
        r = load_kernel_install_conf(arg_root,
×
UNCOV
446
                                     c->conf_root,
×
447
                                     &machine_id,
448
                                     &boot_root,
449
                                     &layout,
450
                                     &initrd_generator,
451
                                     &uki_generator);
UNCOV
452
        if (r <= 0)
×
453
                return r;
454

455
        (void) context_set_machine_id(c, machine_id, "config");
×
456
        (void) context_set_boot_root(c, boot_root, "config");
×
457
        (void) context_set_layout(c, layout, "config");
×
458
        (void) context_set_initrd_generator(c, initrd_generator, "config");
×
UNCOV
459
        (void) context_set_uki_generator(c, uki_generator, "config");
×
460

UNCOV
461
        log_debug("Loaded config.");
×
462
        return 0;
463
}
464

465
static int context_load_machine_info(Context *c) {
×
466
        _cleanup_fclose_ FILE *f = NULL;
×
467
        _cleanup_free_ char *machine_id = NULL, *layout = NULL;
×
468
        static const char *path = "/etc/machine-info";
×
UNCOV
469
        int r;
×
470

UNCOV
471
        assert(c);
×
472

473
        /* If the user configured an explicit machine ID in /etc/machine-info to use for our purpose, we'll
474
         * use that instead (for compatibility). */
475

UNCOV
476
        if (!sd_id128_is_null(c->machine_id) && c->layout >= 0)
×
477
                return 0;
478

479
        /* For testing. To make not read host's /etc/machine-info. */
480
        r = getenv_bool("KERNEL_INSTALL_READ_MACHINE_INFO");
×
481
        if (r < 0 && r != -ENXIO)
×
482
                log_warning_errno(r, "Failed to read $KERNEL_INSTALL_READ_MACHINE_INFO, assuming yes: %m");
×
483
        if (r == 0) {
×
484
                log_debug("Skipping reading of /etc/machine-info.");
×
UNCOV
485
                return 0;
×
486
        }
487

488
        r = chase_and_fopenat_unlocked(c->rfd, path, CHASE_AT_RESOLVE_IN_ROOT, "re", NULL, &f);
×
UNCOV
489
        if (r == -ENOENT)
×
490
                return 0;
491
        if (r < 0)
×
UNCOV
492
                return log_error_errno(r, "Failed to chase %s: %m", path);
×
493

UNCOV
494
        log_debug("Loading %s…", path);
×
495

UNCOV
496
        r = parse_env_file(f, path,
×
497
                           "KERNEL_INSTALL_MACHINE_ID", &machine_id,
498
                           "KERNEL_INSTALL_LAYOUT", &layout);
499
        if (r < 0)
×
UNCOV
500
                return log_error_errno(r, "Failed to parse '%s': %m", path);
×
501

502
        (void) context_set_machine_id(c, machine_id, path);
×
UNCOV
503
        (void) context_set_layout(c, layout, path);
×
504
        return 0;
505
}
506

507
static int context_load_machine_id(Context *c) {
×
UNCOV
508
        int r;
×
509

UNCOV
510
        assert(c);
×
511

512
        r = id128_get_machine_at(c->rfd, &c->machine_id);
×
UNCOV
513
        if (ERRNO_IS_NEG_MACHINE_ID_UNSET(r))
×
514
                return 0;
515
        if (r < 0)
×
UNCOV
516
                return log_error_errno(r, "Failed to load machine ID from /etc/machine-id: %m");
×
517

518
        log_debug("MACHINE_ID=%s set via /etc/machine-id.", SD_ID128_TO_STRING(c->machine_id));
×
UNCOV
519
        return 1; /* loaded */
×
520
}
521

522
static int context_ensure_machine_id(Context *c) {
×
UNCOV
523
        int r;
×
524

UNCOV
525
        assert(c);
×
526

527
        if (!sd_id128_is_null(c->machine_id))
×
UNCOV
528
                return 0;
×
529

530
        /* If /etc/machine-id is initialized we'll use it. */
531
        r = context_load_machine_id(c);
×
UNCOV
532
        if (r != 0)
×
533
                return r;
534

535
        /* Otherwise we'll use a freshly generated one. */
536
        r = sd_id128_randomize(&c->machine_id);
×
537
        if (r < 0)
×
UNCOV
538
                return log_error_errno(r, "Failed to generate random ID: %m");
×
539

540
        c->machine_id_is_random = true;
×
541
        log_debug("New machine ID '%s' generated.", SD_ID128_TO_STRING(c->machine_id));
×
UNCOV
542
        return 0;
×
543
}
544

545
static int context_acquire_xbootldr(Context *c) {
×
UNCOV
546
        int r;
×
547

548
        assert(c);
×
UNCOV
549
        assert(!c->boot_root);
×
550

UNCOV
551
        r = find_xbootldr_and_warn_at(
×
552
                        /* rfd = */ c->rfd,
553
                        /* path = */ arg_xbootldr_path,
554
                        /* unprivileged_mode= */ -1,
555
                        /* ret_path = */ &c->boot_root,
556
                        /* ret_uuid = */ NULL,
557
                        /* ret_devid = */ NULL);
558
        if (r == -ENOKEY) {
×
559
                log_debug_errno(r, "Couldn't find an XBOOTLDR partition.");
×
UNCOV
560
                return 0;
×
561
        }
562
        if (r == -EACCES && geteuid() != 0)
×
563
                return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
×
UNCOV
564
        if (r < 0)
×
565
                return r;
566

UNCOV
567
        log_debug("Using XBOOTLDR partition at %s as $BOOT_ROOT.", c->boot_root);
×
568
        return 1; /* found */
569
}
570

571
static int context_acquire_esp(Context *c) {
×
UNCOV
572
        int r;
×
573

574
        assert(c);
×
UNCOV
575
        assert(!c->boot_root);
×
576

UNCOV
577
        r = find_esp_and_warn_at(
×
578
                        /* rfd = */ c->rfd,
579
                        /* path = */ arg_esp_path,
580
                        /* unprivileged_mode= */ -1,
581
                        /* ret_path = */ &c->boot_root,
582
                        /* ret_part = */ NULL,
583
                        /* ret_pstart = */ NULL,
584
                        /* ret_psize = */ NULL,
585
                        /* ret_uuid = */ NULL,
586
                        /* ret_devid = */ NULL);
587
        if (r == -ENOKEY) {
×
588
                log_debug_errno(r, "Couldn't find EFI system partition, ignoring.");
×
UNCOV
589
                return 0;
×
590
        }
591
        if (r == -EACCES && geteuid() != 0)
×
592
                return log_error_errno(r, "Failed to determine EFI system partition: %m");
×
UNCOV
593
        if (r < 0)
×
594
                return r;
595

UNCOV
596
        log_debug("Using EFI System Partition at %s as $BOOT_ROOT.", c->boot_root);
×
597
        return 1; /* found */
598
}
599

600
static int context_ensure_boot_root(Context *c) {
×
UNCOV
601
        int r;
×
602

UNCOV
603
        assert(c);
×
604

605
        /* If BOOT_ROOT is specified via environment or install.conf, then use it. */
UNCOV
606
        if (c->boot_root)
×
607
                return 0;
608

609
        /* Otherwise, use XBOOTLDR partition, if mounted. */
610
        r = context_acquire_xbootldr(c);
×
UNCOV
611
        if (r != 0)
×
612
                return r;
613

614
        /* Otherwise, use EFI system partition, if mounted. */
615
        r = context_acquire_esp(c);
×
UNCOV
616
        if (r != 0)
×
617
                return r;
618

619
        /* If all else fails, use /boot. */
620
        if (c->rfd >= 0) {
×
621
                r = chaseat(c->rfd, "/boot", CHASE_AT_RESOLVE_IN_ROOT, &c->boot_root, /* ret_fd = */ NULL);
×
622
                if (r < 0)
×
UNCOV
623
                        return log_error_errno(r, "Failed to chase '/boot/': %m");
×
624
        } else {
625
                c->boot_root = strdup("/boot");
×
626
                if (!c->boot_root)
×
UNCOV
627
                        return log_oom();
×
628
        }
629

UNCOV
630
        log_debug("KERNEL_INSTALL_BOOT_ROOT autodetection yielded no candidates, using \"%s\".", c->boot_root);
×
631
        return 0;
632
}
633

634
static int context_ensure_entry_token(Context *c) {
×
UNCOV
635
        int r;
×
636

UNCOV
637
        assert(c);
×
638

639
        /* Now that we determined the machine ID to use, let's determine the "token" for the boot loader
640
         * entry to generate. We use that for naming the directory below $BOOT where we want to place the
641
         * kernel/initrd and related resources, as well for naming the .conf boot loader spec entry.
642
         * Typically this is just the machine ID, but it can be anything else, too, if we are told so. */
643

UNCOV
644
        r = boot_entry_token_ensure_at(
×
645
                        c->rfd,
UNCOV
646
                        c->conf_root,
×
647
                        c->machine_id,
UNCOV
648
                        c->machine_id_is_random,
×
649
                        &c->entry_token_type,
650
                        &c->entry_token);
UNCOV
651
        if (r < 0)
×
652
                return r;
653

UNCOV
654
        log_debug("Using entry token: %s", c->entry_token);
×
655
        return 0;
656
}
657

658
static int context_load_plugins(Context *c) {
×
UNCOV
659
        int r;
×
660

UNCOV
661
        assert(c);
×
662

663
        if (c->plugins)
×
UNCOV
664
                return 0;
×
665

UNCOV
666
        r = conf_files_list_strv_at(
×
667
                        &c->plugins,
668
                        ".install",
669
                        c->rfd,
670
                        CONF_FILES_EXECUTABLE | CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED,
671
                        STRV_MAKE_CONST("/etc/kernel/install.d", "/usr/lib/kernel/install.d"));
×
672
        if (r < 0)
×
UNCOV
673
                return log_error_errno(r, "Failed to find plugins: %m");
×
674

675
        return 0;
676
}
677

678
static int context_init(Context *c) {
×
UNCOV
679
        int r;
×
680

UNCOV
681
        assert(c);
×
682

683
        r = context_open_root(c);
×
UNCOV
684
        if (r < 0)
×
685
                return r;
686

687
        r = context_load_environment(c);
×
UNCOV
688
        if (r < 0)
×
689
                return r;
690

691
        r = context_load_install_conf(c);
×
UNCOV
692
        if (r < 0)
×
693
                return r;
694

695
        r = context_load_machine_info(c);
×
UNCOV
696
        if (r < 0)
×
697
                return r;
698

699
        r = context_ensure_machine_id(c);
×
UNCOV
700
        if (r < 0)
×
701
                return r;
702

703
        r = context_ensure_boot_root(c);
×
UNCOV
704
        if (r < 0)
×
705
                return r;
706

707
        r = context_ensure_entry_token(c);
×
UNCOV
708
        if (r < 0)
×
709
                return r;
710

711
        r = context_load_plugins(c);
×
712
        if (r < 0)
×
UNCOV
713
                return r;
×
714

715
        return 0;
716
}
717

718
static int context_inspect_kernel(Context *c) {
×
UNCOV
719
        assert(c);
×
720

UNCOV
721
        if (!c->kernel)
×
722
                return 0;
723

UNCOV
724
        return inspect_kernel(c->rfd, c->kernel, &c->kernel_image_type, NULL, NULL, NULL);
×
725
}
726

727
static int context_ensure_layout(Context *c) {
×
UNCOV
728
        int r;
×
729

730
        assert(c);
×
731
        assert(c->boot_root);
×
UNCOV
732
        assert(c->entry_token);
×
733

734
        if (c->layout >= 0 && c->layout != LAYOUT_AUTO)
×
UNCOV
735
                return 0;
×
736

737
        /* No layout configured by the administrator. Let's try to figure it out automatically from metadata
738
         * already contained in $BOOT_ROOT. */
739

740
        if (c->kernel_image_type == KERNEL_IMAGE_TYPE_UKI) {
×
741
                c->layout = LAYOUT_UKI;
×
UNCOV
742
                log_debug("Kernel image type is %s, using layout=%s.",
×
743
                          kernel_image_type_to_string(c->kernel_image_type), layout_to_string(c->layout));
UNCOV
744
                return 0;
×
745
        }
746

747
        _cleanup_free_ char *srel_path = path_join(c->boot_root, "loader/entries.srel");
×
748
        if (!srel_path)
×
UNCOV
749
                return log_oom();
×
750

751
        _cleanup_free_ char *srel = NULL;
×
752
        r = read_one_line_file_at(c->rfd, srel_path, &srel);
×
753
        if (r >= 0) {
×
UNCOV
754
                if (streq(srel, "type1"))
×
755
                        /* The loader/entries.srel file clearly indicates that the installed boot loader
756
                         * implements the proper standard upstream boot loader spec for Type #1 entries.
757
                         * Let's default to that, then. */
UNCOV
758
                        c->layout = LAYOUT_BLS;
×
759
                else
760
                        /* The loader/entries.srel file indicates some other spec is implemented and owns the
761
                         * /loader/entries/ directory. Since we have no idea what that means, let's stay away
762
                         * from it by default. */
UNCOV
763
                        c->layout = LAYOUT_OTHER;
×
764

765
                log_debug("%s with '%s' found, using layout=%s.", srel_path, srel, layout_to_string(c->layout));
×
766
                return 0;
×
767
        } else if (r != -ENOENT)
×
UNCOV
768
                return log_error_errno(r, "Failed to read %s: %m", srel_path);
×
769

770
        _cleanup_free_ char *entry_token_path = path_join(c->boot_root, c->entry_token);
×
771
        if (!entry_token_path)
×
UNCOV
772
                return log_oom();
×
773

774
        r = is_dir_at(c->rfd, entry_token_path, /* follow = */ false);
×
775
        if (r < 0 && r != -ENOENT)
×
776
                return log_error_errno(r, "Failed to check if '%s' is a directory: %m", entry_token_path);
×
UNCOV
777
        if (r > 0) {
×
778
                /* If the metadata in $BOOT_ROOT doesn't tell us anything, then check if the entry token
779
                 * directory already exists. If so, let's assume it's the standard boot loader spec, too. */
780
                c->layout = LAYOUT_BLS;
×
781
                log_debug("%s exists, using layout=%s.", entry_token_path, layout_to_string(c->layout));
×
UNCOV
782
                return 0;
×
783
        }
784

785
        /* There's no metadata in $BOOT_ROOT, and apparently no entry token directory installed? Then we
786
         * really don't know anything. */
787
        c->layout = LAYOUT_OTHER;
×
UNCOV
788
        log_debug("Entry-token directory not found, using layout=%s.", layout_to_string(c->layout));
×
789
        return 0;
790
}
791

792
static int context_set_up_staging_area(Context *c) {
×
793
        static const char *template = "/tmp/kernel-install.staging.XXXXXX";
×
UNCOV
794
        int r;
×
795

UNCOV
796
        assert(c);
×
797

UNCOV
798
        if (c->staging_area)
×
799
                return 0;
800

UNCOV
801
        if (c->action == ACTION_INSPECT) {
×
802
                /* This is only used for display. The directory will not be created. */
803
                c->staging_area = strdup(template);
×
804
                if (!c->staging_area)
×
UNCOV
805
                        return log_oom();
×
806
        } else {
807
                r = mkdtemp_malloc(template, &c->staging_area);
×
808
                if (r < 0)
×
UNCOV
809
                        return log_error_errno(r, "Failed to create staging area: %m");
×
810
        }
811

812
        return 0;
813
}
814

815
static int context_build_entry_dir(Context *c) {
×
816
        assert(c);
×
817
        assert(c->boot_root);
×
818
        assert(c->entry_token);
×
UNCOV
819
        assert(c->version || c->action == ACTION_INSPECT);
×
820

UNCOV
821
        if (c->entry_dir)
×
822
                return 0;
823

824
        c->entry_dir = path_join(c->boot_root, c->entry_token, c->version ?: "KERNEL_VERSION");
×
825
        if (!c->entry_dir)
×
UNCOV
826
                return log_oom();
×
827

UNCOV
828
        log_debug("Using ENTRY_DIR=%s", c->entry_dir);
×
829
        return 0;
830
}
831

832
static bool context_should_make_entry_dir(Context *c) {
×
UNCOV
833
        assert(c);
×
834

835
        /* Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN to signal to
836
         * 00-entry-directory to create $ENTRY_DIR to serve as the indication to use or to not use the BLS */
837

838
        if (arg_make_entry_directory < 0)
×
UNCOV
839
                return c->layout == LAYOUT_BLS;
×
840

UNCOV
841
        return arg_make_entry_directory;
×
842
}
843

844
static int context_make_entry_dir(Context *c) {
×
UNCOV
845
        _cleanup_close_ int fd = -EBADF;
×
846

847
        assert(c);
×
UNCOV
848
        assert(c->entry_dir);
×
849

UNCOV
850
        if (c->action != ACTION_ADD)
×
851
                return 0;
852

UNCOV
853
        if (!context_should_make_entry_dir(c))
×
854
                return 0;
855

856
        log_debug("mkdir -p %s", c->entry_dir);
×
UNCOV
857
        fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT | CHASE_MKDIR_0755,
×
858
                              O_CLOEXEC | O_CREAT | O_DIRECTORY | O_PATH, NULL);
859
        if (fd < 0)
×
UNCOV
860
                return log_error_errno(fd, "Failed to make directory '%s': %m", c->entry_dir);
×
861

862
        return 0;
863
}
864

865
static int context_remove_entry_dir(Context *c) {
×
866
        _cleanup_free_ char *p = NULL;
×
867
        _cleanup_close_ int fd = -EBADF;
×
868
        struct stat st;
×
UNCOV
869
        int r;
×
870

871
        assert(c);
×
UNCOV
872
        assert(c->entry_dir);
×
873

UNCOV
874
        if (c->action != ACTION_REMOVE)
×
875
                return 0;
876

UNCOV
877
        if (!context_should_make_entry_dir(c))
×
878
                return 0;
879

880
        log_debug("rm -rf %s", c->entry_dir);
×
881
        fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT, O_CLOEXEC | O_DIRECTORY, &p);
×
882
        if (fd < 0) {
×
UNCOV
883
                if (IN_SET(fd, -ENOTDIR, -ENOENT))
×
884
                        return 0;
UNCOV
885
                return log_debug_errno(fd, "Failed to chase and open %s, ignoring: %m", c->entry_dir);
×
886
        }
887

888
        if (fstat(fd, &st) < 0)
×
UNCOV
889
                return log_debug_errno(errno, "Failed to stat %s: %m", p);
×
890

891
        r = rm_rf_children(TAKE_FD(fd), REMOVE_PHYSICAL|REMOVE_MISSING_OK|REMOVE_CHMOD, &st);
×
892
        if (r < 0)
×
UNCOV
893
                log_debug_errno(r, "Failed to remove children of %s, ignoring: %m", p);
×
894

895
        if (unlinkat(c->rfd, p, AT_REMOVEDIR) < 0)
×
UNCOV
896
                log_debug_errno(errno, "Failed to remove %s, ignoring: %m", p);
×
897

898
        return 0;
899
}
900

901
static int context_build_arguments(Context *c) {
×
902
        _cleanup_strv_free_ char **a = NULL;
×
903
        const char *verb;
×
UNCOV
904
        int r;
×
905

906
        assert(c);
×
UNCOV
907
        assert(c->entry_dir);
×
908

UNCOV
909
        if (c->argv)
×
910
                return 0;
911

912
        switch (c->action) {
×
913
        case ACTION_ADD:
×
914
                assert(c->version);
×
UNCOV
915
                assert(c->kernel);
×
916
                verb = "add";
917
                break;
918

919
        case ACTION_REMOVE:
×
920
                assert(c->version);
×
921
                assert(!c->kernel);
×
UNCOV
922
                assert(!c->initrds);
×
923
                verb = "remove";
924
                break;
925

926
        case ACTION_INSPECT:
927
                verb = "add|remove";
928
                break;
929

930
        default:
×
UNCOV
931
                assert_not_reached();
×
932
        }
933

UNCOV
934
        a = strv_new("dummy-arg", /* to make strv_free() works for this variable. */
×
935
                     verb,
936
                     c->version ?: "KERNEL_VERSION",
937
                     c->entry_dir);
938
        if (!a)
×
UNCOV
939
                return log_oom();
×
940

941
        if (c->action == ACTION_ADD) {
×
942
                r = strv_extend(&a, c->kernel);
×
943
                if (r < 0)
×
UNCOV
944
                        return log_oom();
×
945

946
                r = strv_extend_strv(&a, c->initrds, /* filter_duplicates = */ false);
×
947
                if (r < 0)
×
UNCOV
948
                        return log_oom();
×
949

950
        } else if (c->action == ACTION_INSPECT) {
×
UNCOV
951
                r = strv_extend_many(
×
952
                                &a,
953
                                c->kernel ?: "[KERNEL_IMAGE]",
954
                                "[INITRD...]");
955
                if (r < 0)
×
UNCOV
956
                        return log_oom();
×
957
        }
958

959
        c->argv = TAKE_PTR(a);
×
UNCOV
960
        return 0;
×
961
}
962

963
static int context_build_environment(Context *c) {
×
964
        _cleanup_strv_free_ char **e = NULL;
×
UNCOV
965
        int r;
×
966

UNCOV
967
        assert(c);
×
968

UNCOV
969
        if (c->envp)
×
970
                return 0;
971

UNCOV
972
        r = strv_env_assign_many(&e,
×
973
                                 "LC_COLLATE",                      SYSTEMD_DEFAULT_LOCALE,
974
                                 "KERNEL_INSTALL_VERBOSE",          one_zero(arg_verbose),
975
                                 "KERNEL_INSTALL_IMAGE_TYPE",       kernel_image_type_to_string(c->kernel_image_type),
976
                                 "KERNEL_INSTALL_MACHINE_ID",       SD_ID128_TO_STRING(c->machine_id),
977
                                 "KERNEL_INSTALL_ENTRY_TOKEN",      c->entry_token,
978
                                 "KERNEL_INSTALL_BOOT_ROOT",        c->boot_root,
979
                                 "KERNEL_INSTALL_LAYOUT",           context_get_layout(c),
980
                                 "KERNEL_INSTALL_INITRD_GENERATOR", strempty(c->initrd_generator),
981
                                 "KERNEL_INSTALL_UKI_GENERATOR",    strempty(c->uki_generator),
982
                                 "KERNEL_INSTALL_STAGING_AREA",     c->staging_area);
983
        if (r < 0)
×
UNCOV
984
                return log_error_errno(r, "Failed to build environment variables for plugins: %m");
×
985

986
        c->envp = TAKE_PTR(e);
×
UNCOV
987
        return 0;
×
988
}
989

990
static int context_prepare_execution(Context *c) {
×
UNCOV
991
        int r;
×
992

UNCOV
993
        assert(c);
×
994

995
        r = context_inspect_kernel(c);
×
UNCOV
996
        if (r < 0)
×
997
                return r;
998

999
        r = context_ensure_layout(c);
×
UNCOV
1000
        if (r < 0)
×
1001
                return r;
1002

1003
        r = context_set_up_staging_area(c);
×
UNCOV
1004
        if (r < 0)
×
1005
                return r;
1006

1007
        r = context_build_entry_dir(c);
×
UNCOV
1008
        if (r < 0)
×
1009
                return r;
1010

1011
        r = context_build_arguments(c);
×
UNCOV
1012
        if (r < 0)
×
1013
                return r;
1014

1015
        r = context_build_environment(c);
×
1016
        if (r < 0)
×
UNCOV
1017
                return r;
×
1018

1019
        return 0;
1020
}
1021

1022
static int context_execute(Context *c) {
×
UNCOV
1023
        int r, ret;
×
1024

UNCOV
1025
        assert(c);
×
1026

1027
        r = context_make_entry_dir(c);
×
UNCOV
1028
        if (r < 0)
×
1029
                return r;
1030

1031
        if (DEBUG_LOGGING) {
×
1032
                _cleanup_free_ char *x = strv_join_full(c->plugins, "", "\n  ", /* escape_separator = */ false);
×
UNCOV
1033
                log_debug("Using plugins: %s", strna(x));
×
1034

1035
                _cleanup_free_ char *y = strv_join_full(c->envp, "", "\n  ", /* escape_separator = */ false);
×
UNCOV
1036
                log_debug("Plugin environment: %s", strna(y));
×
1037

1038
                _cleanup_free_ char *z = strv_join(strv_skip(c->argv, 1), " ");
×
UNCOV
1039
                log_debug("Plugin arguments: %s", strna(z));
×
1040
        }
1041

UNCOV
1042
        ret = execute_strv(
×
1043
                        /* name = */ NULL,
UNCOV
1044
                        c->plugins,
×
1045
                        /* root = */ NULL,
1046
                        USEC_INFINITY,
1047
                        /* callbacks = */ NULL,
1048
                        /* callback_args = */ NULL,
1049
                        c->argv,
1050
                        c->envp,
1051
                        EXEC_DIR_SKIP_REMAINING);
1052

1053
        r = context_remove_entry_dir(c);
×
1054
        if (r < 0)
×
UNCOV
1055
                return r;
×
1056

1057
        /* This returns 0 on success, positive exit code on plugin failure, negative errno on other failures. */
1058
        return ret;
1059
}
1060

1061
static bool bypass(void) {
×
UNCOV
1062
        return should_bypass("KERNEL_INSTALL");
×
1063
}
1064

UNCOV
1065
static int do_add(
×
1066
                Context *c,
1067
                const char *version,
1068
                const char *kernel,
1069
                char **initrds) {
1070

UNCOV
1071
        int r;
×
1072

1073
        assert(c);
×
1074
        assert(version);
×
UNCOV
1075
        assert(kernel);
×
1076

1077
        r = context_set_version(c, version);
×
UNCOV
1078
        if (r < 0)
×
1079
                return r;
1080

1081
        r = context_set_kernel(c, kernel);
×
UNCOV
1082
        if (r < 0)
×
1083
                return r;
1084

1085
        r = context_set_initrds(c, initrds);
×
UNCOV
1086
        if (r < 0)
×
1087
                return r;
1088

1089
        r = context_prepare_execution(c);
×
UNCOV
1090
        if (r < 0)
×
1091
                return r;
1092

UNCOV
1093
        return context_execute(c);
×
1094
}
1095

1096
static int kernel_from_version(const char *version, char **ret_kernel) {
×
1097
        _cleanup_free_ char *vmlinuz = NULL;
×
UNCOV
1098
        int r;
×
1099

UNCOV
1100
        assert(version);
×
1101

1102
        vmlinuz = path_join("/usr/lib/modules/", version, "/vmlinuz");
×
1103
        if (!vmlinuz)
×
UNCOV
1104
                return log_oom();
×
1105

1106
        r = access_nofollow(vmlinuz, F_OK);
×
1107
        if (r == -ENOENT)
×
1108
                return log_error_errno(r, "Kernel image not installed to '%s', requiring manual kernel image path specification.", vmlinuz);
×
1109
        if (r < 0)
×
UNCOV
1110
                return log_error_errno(r, "Failed to determine if kernel image is installed to '%s': %m", vmlinuz);
×
1111

1112
        *ret_kernel = TAKE_PTR(vmlinuz);
×
UNCOV
1113
        return 0;
×
1114
}
1115

1116
static int verb_add(int argc, char *argv[], void *userdata) {
×
1117
        Context *c = ASSERT_PTR(userdata);
×
1118
        _cleanup_free_ char *vmlinuz = NULL;
×
1119
        const char *version, *kernel;
×
1120
        char **initrds;
×
1121
        struct utsname un;
×
UNCOV
1122
        int r;
×
1123

UNCOV
1124
        assert(argv);
×
1125

1126
        if (arg_root)
×
UNCOV
1127
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'add' does not support --root= or --image=.");
×
1128

UNCOV
1129
        if (bypass())
×
1130
                return 0;
1131

UNCOV
1132
        c->action = ACTION_ADD;
×
1133

1134
        /* We use the same order of arguments that "inspect" introduced, i.e. if only on argument is
1135
         * specified we take it as the kernel path, not the version, i.e. it's the first argument that is
1136
         * optional, not the 2nd. */
1137
        version = argc > 2 ? empty_or_dash_to_null(argv[1]) : NULL;
×
1138
        kernel = argc > 2 ? empty_or_dash_to_null(argv[2]) :
×
1139
                (argc > 1 ? empty_or_dash_to_null(argv[1]) : NULL);
×
UNCOV
1140
        initrds = strv_skip(argv, 3);
×
1141

1142
        if (!version) {
×
UNCOV
1143
                assert_se(uname(&un) >= 0);
×
1144
                version = un.release;
1145
        }
1146

1147
        if (!kernel) {
×
1148
                r = kernel_from_version(version, &vmlinuz);
×
UNCOV
1149
                if (r < 0)
×
1150
                        return r;
1151

UNCOV
1152
                kernel = vmlinuz;
×
1153
        }
1154

UNCOV
1155
        return do_add(c, version, kernel, initrds);
×
1156
}
1157

1158
static int verb_add_all(int argc, char *argv[], void *userdata) {
×
1159
        Context *c = ASSERT_PTR(userdata);
×
1160
        _cleanup_close_ int fd = -EBADF;
×
1161
        size_t n = 0;
×
UNCOV
1162
        int ret = 0, r;
×
1163

UNCOV
1164
        assert(argv);
×
1165

1166
        if (arg_root)
×
UNCOV
1167
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'add-all' does not support --root= or --image=.");
×
1168

UNCOV
1169
        if (bypass())
×
1170
                return 0;
1171

UNCOV
1172
        c->action = ACTION_ADD;
×
1173

1174
        fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL);
×
1175
        if (fd < 0)
×
UNCOV
1176
                return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root));
×
1177

1178
        _cleanup_free_ DirectoryEntries *de = NULL;
×
1179
        r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
×
1180
        if (r < 0)
×
UNCOV
1181
                return log_error_errno(r, "Failed to numerate /usr/lib/modules/ contents: %m");
×
1182

1183
        FOREACH_ARRAY(d, de->entries, de->n_entries) {
×
1184
                r = dirent_ensure_type(fd, *d);
×
1185
                if (r < 0) {
×
1186
                        if (r != -ENOENT) /* don't log if just gone by now */
×
1187
                                log_debug_errno(r, "Failed to check if '%s/usr/lib/modules/%s' is a directory, ignoring: %m", strempty(arg_root), (*d)->d_name);
×
UNCOV
1188
                        continue;
×
1189
                }
1190

1191
                if ((*d)->d_type != DT_DIR)
×
UNCOV
1192
                        continue;
×
1193

1194
                _cleanup_free_ char *fn = path_join((*d)->d_name, "vmlinuz");
×
1195
                if (!fn)
×
UNCOV
1196
                        return log_oom();
×
1197

1198
                if (faccessat(fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0) {
×
1199
                        if (errno != ENOENT)
×
UNCOV
1200
                                log_debug_errno(errno, "Failed to check if '%s/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", strempty(arg_root), (*d)->d_name);
×
1201

1202
                        log_notice("Not adding version '%s', because kernel image not found.", (*d)->d_name);
×
UNCOV
1203
                        continue;
×
1204
                }
1205

UNCOV
1206
                _cleanup_(context_done) Context copy = CONTEXT_NULL;
×
1207

1208
                r = context_copy(c, &copy);
×
1209
                if (r < 0)
×
UNCOV
1210
                        return log_error_errno(r, "Failed to copy execution context: %m");
×
1211

1212
                /* do_add() will look up the path in the correct root directory so we don't need to prefix it
1213
                 * with arg_root here. */
1214
                _cleanup_free_ char *full = path_join("/usr/lib/modules/", fn);
×
1215
                if (!full)
×
UNCOV
1216
                        return log_oom();
×
1217

1218
                r = do_add(&copy,
×
UNCOV
1219
                           /* version= */ (*d)->d_name,
×
1220
                           /* kernel= */ full,
1221
                           /* initrds= */ NULL);
1222
                if (r == 0)
×
1223
                        n++;
×
1224
                else if (ret == 0)
×
UNCOV
1225
                        ret = r;
×
1226
        }
1227

1228
        if (n > 0)
×
1229
                log_debug("Installed %zu kernel(s).", n);
×
1230
        else if (ret == 0)
×
UNCOV
1231
                ret = log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No kernels to install found.");
×
1232

1233
        return ret;
1234
}
1235

UNCOV
1236
static int run_as_installkernel(int argc, char *argv[], Context *c) {
×
1237
        /* kernel's install.sh invokes us as
1238
         *   /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
1239
         * We ignore the last two arguments. */
1240
        if (optind + 2 > argc)
×
UNCOV
1241
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'installkernel' command requires at least two arguments.");
×
1242

UNCOV
1243
        return verb_add(3, STRV_MAKE("add", argv[optind], argv[optind+1]), c);
×
1244
}
1245

1246
static int verb_remove(int argc, char *argv[], void *userdata) {
×
1247
        Context *c = ASSERT_PTR(userdata);
×
UNCOV
1248
        int r;
×
1249

1250
        assert(argc >= 2);
×
UNCOV
1251
        assert(argv);
×
1252

1253
        if (arg_root)
×
UNCOV
1254
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "'remove' does not support --root= or --image=.");
×
1255

1256
        if (argc > 2)
×
UNCOV
1257
                log_debug("Too many arguments specified. 'kernel-install remove' takes only kernel version. "
×
1258
                          "Ignoring residual arguments.");
1259

UNCOV
1260
        if (bypass())
×
1261
                return 0;
1262

UNCOV
1263
        c->action = ACTION_REMOVE;
×
1264

1265
        /* Note, we do not automatically derive the kernel version to remove from uname() here (unlike we do
1266
         * it for the "add" verb), since we don't want to make it too easy to uninstall your running
1267
         * kernel, as a safety precaution */
1268

1269
        r = context_set_version(c, argv[1]);
×
UNCOV
1270
        if (r < 0)
×
1271
                return r;
1272

1273
        r = context_prepare_execution(c);
×
UNCOV
1274
        if (r < 0)
×
1275
                return r;
1276

UNCOV
1277
        return context_execute(c);
×
1278
}
1279

1280
static int verb_inspect(int argc, char *argv[], void *userdata) {
×
1281
        Context *c = ASSERT_PTR(userdata);
×
1282
        _cleanup_(table_unrefp) Table *t = NULL;
×
1283
        _cleanup_free_ char *vmlinuz = NULL;
×
1284
        const char *version, *kernel;
×
1285
        char **initrds;
×
1286
        struct utsname un;
×
UNCOV
1287
        int r;
×
1288

UNCOV
1289
        c->action = ACTION_INSPECT;
×
1290

1291
        /* When only a single parameter is specified 'inspect' it's the kernel image path, and not the kernel
1292
         * version. i.e. it's the first argument that is optional, not the 2nd. That's a bit unfortunate, but
1293
         * we keep the behaviour for compatibility. If users want to specify only the version (and have the
1294
         * kernel image path derived automatically), then they may specify an empty string or "dash" as
1295
         * kernel image path. */
1296
        version = argc > 2 ? empty_or_dash_to_null(argv[1]) : NULL;
×
1297
        kernel = argc > 2 ? empty_or_dash_to_null(argv[2]) :
×
1298
                (argc > 1 ? empty_or_dash_to_null(argv[1]) : NULL);
×
UNCOV
1299
        initrds = strv_skip(argv, 3);
×
1300

1301
        if (!version && !arg_root) {
×
UNCOV
1302
                assert_se(uname(&un) >= 0);
×
1303
                version = un.release;
1304
        }
1305

1306
        if (!kernel && version) {
×
1307
                r = kernel_from_version(version, &vmlinuz);
×
UNCOV
1308
                if (r < 0)
×
1309
                        return r;
1310

UNCOV
1311
                kernel = vmlinuz;
×
1312
        }
1313

1314
        r = context_set_version(c, version);
×
UNCOV
1315
        if (r < 0)
×
1316
                return r;
1317

1318
        r = context_set_kernel(c, kernel);
×
UNCOV
1319
        if (r < 0)
×
1320
                return r;
1321

1322
        r = context_set_initrds(c, initrds);
×
UNCOV
1323
        if (r < 0)
×
1324
                return r;
1325

1326
        r = context_prepare_execution(c);
×
UNCOV
1327
        if (r < 0)
×
1328
                return r;
1329

1330
        t = table_new_vertical();
×
1331
        if (!t)
×
UNCOV
1332
                return log_oom();
×
1333

UNCOV
1334
        r = table_add_many(t,
×
1335
                           TABLE_FIELD, "Machine ID",
1336
                           TABLE_ID128, c->machine_id,
1337
                           TABLE_FIELD, "Kernel Image Type",
1338
                           TABLE_STRING, kernel_image_type_to_string(c->kernel_image_type),
1339
                           TABLE_FIELD, "Layout",
1340
                           TABLE_STRING, context_get_layout(c),
1341
                           TABLE_FIELD, "Boot Root",
1342
                           TABLE_STRING, c->boot_root,
1343
                           TABLE_FIELD, "Entry Token Type",
1344
                           TABLE_STRING, boot_entry_token_type_to_string(c->entry_token_type),
1345
                           TABLE_FIELD, "Entry Token",
1346
                           TABLE_STRING, c->entry_token,
1347
                           TABLE_FIELD, "Entry Directory",
1348
                           TABLE_STRING, c->entry_dir,
1349
                           TABLE_FIELD, "Kernel Version",
1350
                           TABLE_STRING, c->version,
1351
                           TABLE_FIELD, "Kernel",
1352
                           TABLE_STRING, c->kernel,
1353
                           TABLE_FIELD, "Initrds",
1354
                           TABLE_STRV, c->initrds,
1355
                           TABLE_FIELD, "Initrd Generator",
1356
                           TABLE_STRING, c->initrd_generator,
1357
                           TABLE_FIELD, "UKI Generator",
1358
                           TABLE_STRING, c->uki_generator,
1359
                           TABLE_FIELD, "Plugins",
1360
                           TABLE_STRV, c->plugins,
1361
                           TABLE_FIELD, "Plugin Environment",
1362
                           TABLE_STRV, c->envp);
1363
        if (r < 0)
×
UNCOV
1364
                return table_log_add_error(r);
×
1365

1366
        if (!sd_json_format_enabled(arg_json_format_flags)) {
×
UNCOV
1367
                r = table_add_many(t,
×
1368
                                   TABLE_FIELD, "Plugin Arguments",
1369
                                   TABLE_STRV, strv_skip(c->argv, 1));
1370
                if (r < 0)
×
UNCOV
1371
                        return table_log_add_error(r);
×
1372
        }
1373

UNCOV
1374
        table_set_ersatz_string(t, TABLE_ERSATZ_UNSET);
×
1375

1376
        for (size_t row = 1; row < table_get_rows(t); row++) {
×
UNCOV
1377
                _cleanup_free_ char *name = NULL;
×
1378

1379
                name = strdup(table_get_at(t, row, 0));
×
1380
                if (!name)
×
UNCOV
1381
                        return log_oom();
×
1382

1383
                r = table_set_json_field_name(t, row - 1, delete_chars(name, " "));
×
1384
                if (r < 0)
×
UNCOV
1385
                        return log_error_errno(r, "Failed to set JSON field name: %m");
×
1386
        }
1387

UNCOV
1388
        return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, /* show_header= */ false);
×
1389
}
1390

1391
static int verb_list(int argc, char *argv[], void *userdata) {
×
1392
        Context *c = ASSERT_PTR(userdata);
×
1393
        _cleanup_close_ int fd = -EBADF;
×
UNCOV
1394
        int r;
×
1395

1396
        fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL);
×
1397
        if (fd < 0)
×
UNCOV
1398
                return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root));
×
1399

1400
        _cleanup_free_ DirectoryEntries *de = NULL;
×
1401
        r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
×
1402
        if (r < 0)
×
UNCOV
1403
                return log_error_errno(r, "Failed to numerate /usr/lib/modules/ contents: %m");
×
1404

1405
        _cleanup_(table_unrefp) Table *table = NULL;
×
1406
        table = table_new("version", "has kernel", "path");
×
1407
        if (!table)
×
UNCOV
1408
                return log_oom();
×
1409

1410
        table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
×
UNCOV
1411
        table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
×
1412

1413
        FOREACH_ARRAY(d, de->entries, de->n_entries) {
×
1414
                _cleanup_free_ char *j = path_join("/usr/lib/modules/", (*d)->d_name);
×
1415
                if (!j)
×
UNCOV
1416
                        return log_oom();
×
1417

1418
                r = dirent_ensure_type(fd, *d);
×
1419
                if (r < 0) {
×
1420
                        if (r != -ENOENT) /* don't log if just gone by now */
×
1421
                                log_debug_errno(r, "Failed to check if '%s/%s' is a directory, ignoring: %m", strempty(arg_root), j);
×
UNCOV
1422
                        continue;
×
1423
                }
1424

1425
                if ((*d)->d_type != DT_DIR)
×
UNCOV
1426
                        continue;
×
1427

1428
                _cleanup_free_ char *fn = path_join((*d)->d_name, "vmlinuz");
×
1429
                if (!fn)
×
UNCOV
1430
                        return log_oom();
×
1431

1432
                bool exists;
×
1433
                if (faccessat(fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0) {
×
1434
                        if (errno != ENOENT)
×
UNCOV
1435
                                log_debug_errno(errno, "Failed to check if '%s/usr/lib/modules/%s/vmlinuz' exists, ignoring: %m", strempty(arg_root), (*d)->d_name);
×
1436

1437
                        exists = false;
1438
                } else
1439
                        exists = true;
1440

UNCOV
1441
                r = table_add_many(table,
×
1442
                                   TABLE_STRING, (*d)->d_name,
1443
                                   TABLE_BOOLEAN_CHECKMARK, exists,
1444
                                   TABLE_SET_COLOR, ansi_highlight_green_red(exists),
1445
                                   TABLE_PATH, j);
1446
                if (r < 0)
×
UNCOV
1447
                        return table_log_add_error(r);
×
1448
        }
1449

UNCOV
1450
        return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
×
1451
}
1452

1453
static int help(void) {
×
1454
        _cleanup_free_ char *link = NULL;
×
UNCOV
1455
        int r;
×
1456

1457
        r = terminal_urlify_man("kernel-install", "8", &link);
×
1458
        if (r < 0)
×
UNCOV
1459
                return log_oom();
×
1460

UNCOV
1461
        printf("%1$s [OPTIONS...] COMMAND ...\n\n"
×
1462
               "%5$sAdd and remove kernel and initrd images to and from the boot partition.%6$s\n"
1463
               "\n%3$sUsage:%4$s\n"
1464
               "  kernel-install [OPTIONS...] add [[[KERNEL-VERSION] KERNEL-IMAGE] [INITRD ...]]\n"
1465
               "  kernel-install [OPTIONS...] add-all\n"
1466
               "  kernel-install [OPTIONS...] remove KERNEL-VERSION\n"
1467
               "  kernel-install [OPTIONS...] inspect [[[KERNEL-VERSION] KERNEL-IMAGE]\n"
1468
               "                                      [INITRD ...]]\n"
1469
               "  kernel-install [OPTIONS...] list\n"
1470
               "\n%3$sOptions:%4$s\n"
1471
               "  -h --help                    Show this help\n"
1472
               "     --version                 Show package version\n"
1473
               "  -v --verbose                 Increase verbosity\n"
1474
               "     --esp-path=PATH           Path to the EFI System Partition (ESP)\n"
1475
               "     --boot-path=PATH          Path to the $BOOT partition\n"
1476
               "     --make-entry-directory=yes|no|auto\n"
1477
               "                               Create $BOOT/ENTRY-TOKEN/ directory\n"
1478
               "     --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
1479
               "                               Entry token to use for this installation\n"
1480
               "     --no-pager                Do not pipe inspect output into a pager\n"
1481
               "     --json=pretty|short|off   Generate JSON output\n"
1482
               "     --no-legend               Do not show the headers and footers\n"
1483
               "     --root=PATH               Operate on an alternate filesystem root\n"
1484
               "     --image=PATH              Operate on disk image as filesystem root\n"
1485
               "     --image-policy=POLICY     Specify disk image dissection policy\n"
1486
               "\n"
1487
               "This program may also be invoked as 'installkernel':\n"
1488
               "  installkernel  [OPTIONS...] VERSION VMLINUZ [MAP] [INSTALLATION-DIR]\n"
1489
               "(The optional arguments are passed by kernel build system, but ignored.)\n"
1490
               "\n"
1491
               "See the %2$s for details.\n",
1492
               program_invocation_short_name,
1493
               link,
1494
               ansi_underline(),
1495
               ansi_normal(),
1496
               ansi_highlight(),
1497
               ansi_normal());
1498

1499
        return 0;
1500
}
1501

1502
static int parse_argv(int argc, char *argv[], Context *c) {
×
UNCOV
1503
        enum {
×
1504
                ARG_VERSION = 0x100,
1505
                ARG_NO_LEGEND,
1506
                ARG_ESP_PATH,
1507
                ARG_BOOT_PATH,
1508
                ARG_MAKE_ENTRY_DIRECTORY,
1509
                ARG_ENTRY_TOKEN,
1510
                ARG_NO_PAGER,
1511
                ARG_JSON,
1512
                ARG_ROOT,
1513
                ARG_IMAGE,
1514
                ARG_IMAGE_POLICY,
1515
        };
UNCOV
1516
        static const struct option options[] = {
×
1517
                { "help",                 no_argument,       NULL, 'h'                      },
1518
                { "version",              no_argument,       NULL, ARG_VERSION              },
1519
                { "verbose",              no_argument,       NULL, 'v'                      },
1520
                { "esp-path",             required_argument, NULL, ARG_ESP_PATH             },
1521
                { "boot-path",            required_argument, NULL, ARG_BOOT_PATH            },
1522
                { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY },
1523
                { "entry-token",          required_argument, NULL, ARG_ENTRY_TOKEN          },
1524
                { "no-pager",             no_argument,       NULL, ARG_NO_PAGER             },
1525
                { "json",                 required_argument, NULL, ARG_JSON                 },
1526
                { "root",                 required_argument, NULL, ARG_ROOT                 },
1527
                { "image",                required_argument, NULL, ARG_IMAGE                },
1528
                { "image-policy",         required_argument, NULL, ARG_IMAGE_POLICY         },
1529
                { "no-legend",            no_argument,       NULL, ARG_NO_LEGEND            },
1530
                {}
1531
        };
UNCOV
1532
        int t, r;
×
1533

1534
        assert(argc >= 0);
×
1535
        assert(argv);
×
UNCOV
1536
        assert(c);
×
1537

1538
        while ((t = getopt_long(argc, argv, "hv", options, NULL)) >= 0)
×
1539
                switch (t) {
×
1540
                case 'h':
×
UNCOV
1541
                        return help();
×
1542

1543
                case ARG_VERSION:
×
UNCOV
1544
                        return version();
×
1545

1546
                case ARG_NO_LEGEND:
×
1547
                        arg_legend = false;
×
UNCOV
1548
                        break;
×
1549

1550
                case 'v':
×
1551
                        log_set_max_level(LOG_DEBUG);
×
1552
                        arg_verbose = true;
×
UNCOV
1553
                        break;
×
1554

1555
                case ARG_ESP_PATH:
×
1556
                        r = parse_path_argument(optarg, /* suppress_root = */ false, &arg_esp_path);
×
1557
                        if (r < 0)
×
UNCOV
1558
                                return log_oom();
×
1559
                        break;
1560

1561
                case ARG_BOOT_PATH:
×
1562
                        r = parse_path_argument(optarg, /* suppress_root = */ false, &arg_xbootldr_path);
×
1563
                        if (r < 0)
×
UNCOV
1564
                                return log_oom();
×
1565
                        break;
1566

1567
                case ARG_MAKE_ENTRY_DIRECTORY:
×
1568
                        if (streq(optarg, "auto"))
×
UNCOV
1569
                                arg_make_entry_directory = -1;
×
1570
                        else {
1571
                                r = parse_boolean_argument("--make-entry-directory=", optarg, NULL);
×
UNCOV
1572
                                if (r < 0)
×
1573
                                        return r;
1574

UNCOV
1575
                                arg_make_entry_directory = r;
×
1576
                        }
1577
                        break;
1578

1579
                case ARG_ENTRY_TOKEN:
×
1580
                        r = parse_boot_entry_token_type(optarg, &c->entry_token_type, &c->entry_token);
×
UNCOV
1581
                        if (r < 0)
×
1582
                                return r;
1583
                        break;
1584

1585
                case ARG_NO_PAGER:
×
1586
                        arg_pager_flags |= PAGER_DISABLE;
×
UNCOV
1587
                        break;
×
1588

1589
                case ARG_JSON:
×
1590
                        r = parse_json_argument(optarg, &arg_json_format_flags);
×
UNCOV
1591
                        if (r < 0)
×
1592
                                return r;
1593
                        break;
1594

1595
                case ARG_ROOT:
×
1596
                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
×
UNCOV
1597
                        if (r < 0)
×
1598
                                return r;
1599
                        break;
1600

1601
                case ARG_IMAGE:
×
1602
                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
×
UNCOV
1603
                        if (r < 0)
×
1604
                                return r;
1605
                        break;
1606

1607
                case ARG_IMAGE_POLICY:
×
1608
                        r = parse_image_policy_argument(optarg, &arg_image_policy);
×
UNCOV
1609
                        if (r < 0)
×
1610
                                return r;
1611
                        break;
1612

1613
                case '?':
1614
                        return -EINVAL;
1615

1616
                default:
×
UNCOV
1617
                        assert_not_reached();
×
1618
                }
1619

1620
        if (arg_image && arg_root)
×
UNCOV
1621
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
×
1622

1623
        return 1;
1624
}
1625

1626
static int run(int argc, char* argv[]) {
×
UNCOV
1627
        static const Verb verbs[] = {
×
1628
                { "add",         1,        VERB_ANY, 0,            verb_add            },
1629
                { "add-all",     1,        1,        0,            verb_add_all        },
1630
                { "remove",      2,        VERB_ANY, 0,            verb_remove         },
1631
                { "inspect",     1,        VERB_ANY, VERB_DEFAULT, verb_inspect        },
1632
                { "list",        1,        1,        0,            verb_list           },
1633
                {}
1634
        };
UNCOV
1635
        _cleanup_(context_done) Context c = {
×
1636
                .rfd = AT_FDCWD,
1637
                .action = _ACTION_INVALID,
1638
                .kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN,
1639
                .layout = _LAYOUT_INVALID,
1640
                .entry_token_type = BOOT_ENTRY_TOKEN_AUTO,
1641
        };
1642
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
×
1643
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
×
UNCOV
1644
        int r;
×
1645

UNCOV
1646
        log_setup();
×
1647

1648
        r = parse_argv(argc, argv, &c);
×
UNCOV
1649
        if (r <= 0)
×
1650
                return r;
1651

1652
        if (arg_image) {
×
UNCOV
1653
                assert(!arg_root);
×
1654

UNCOV
1655
                r = mount_image_privately_interactively(
×
1656
                                arg_image,
1657
                                arg_image_policy,
1658
                                DISSECT_IMAGE_GENERIC_ROOT |
1659
                                DISSECT_IMAGE_REQUIRE_ROOT |
1660
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
1661
                                DISSECT_IMAGE_VALIDATE_OS |
1662
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
1663
                                &mounted_dir,
1664
                                /* ret_dir_fd= */ NULL,
1665
                                &loop_device);
UNCOV
1666
                if (r < 0)
×
1667
                        return r;
1668

1669
                arg_root = strdup(mounted_dir);
×
1670
                if (!arg_root)
×
UNCOV
1671
                        return log_oom();
×
1672
        }
1673

1674
        r = context_init(&c);
×
UNCOV
1675
        if (r < 0)
×
1676
                return r;
1677

1678
        if (invoked_as(argv, "installkernel"))
×
UNCOV
1679
                return run_as_installkernel(argc, argv, &c);
×
1680

UNCOV
1681
        return dispatch_verb(argc, argv, verbs, &c);
×
1682
}
1683

UNCOV
1684
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc