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

systemd / systemd / 25705508282

11 May 2026 11:07PM UTC coverage: 72.65% (+0.1%) from 72.511%
25705508282

push

github

bluca
firstboot,sysinstall,hostnamed: always show FANCY_NAME=

This makes sure that whenever we want to show the OS name we can show
the fancy name. Thus this moves the escaping/validation of the fancy
name out of hostnamed into generic code, and then makes use of it in
sysinstall,firstboot,prompt-util.

17 of 36 new or added lines in 6 files covered. (47.22%)

2673 existing lines in 72 files now uncovered.

327104 of 450245 relevant lines covered (72.65%)

1200575.51 hits per line

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

49.0
/src/sysinstall/sysinstall.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <locale.h>
4
#include <sys/stat.h>
5
#include <unistd.h>
6

7
#include "sd-varlink.h"
8

9
#include "alloc-util.h"
10
#include "ansi-color.h"
11
#include "blockdev-list.h"
12
#include "build.h"
13
#include "build-path.h"
14
#include "chase.h"
15
#include "conf-files.h"
16
#include "constants.h"
17
#include "efi-loader.h"
18
#include "efivars.h"
19
#include "env-file.h"
20
#include "escape.h"
21
#include "fd-util.h"
22
#include "find-esp.h"
23
#include "format-table.h"
24
#include "format-util.h"
25
#include "fs-util.h"
26
#include "glyph-util.h"
27
#include "help-util.h"
28
#include "image-policy.h"
29
#include "json-util.h"
30
#include "locale-setup.h"
31
#include "log.h"
32
#include "loop-util.h"
33
#include "machine-credential.h"
34
#include "main-func.h"
35
#include "mount-util.h"
36
#include "options.h"
37
#include "os-util.h"
38
#include "parse-argument.h"
39
#include "parse-util.h"
40
#include "path-util.h"
41
#include "prompt-util.h"
42
#include "strv.h"
43
#include "terminal-util.h"
44
#include "varlink-util.h"
45

46
static char *arg_node = NULL;
47
static bool arg_welcome = true;
48
static int arg_erase = -1;            /* tri-state */
49
static bool arg_confirm = true;
50
static bool arg_summary = true;
51
static char **arg_definitions = NULL;
52
static char *arg_kernel_image = NULL;
53
static bool arg_reboot = false;
54
static int arg_touch_variables = -1;  /* tri-state */
55
static MachineCredentialContext arg_credentials = {};
56
static bool arg_copy_locale = true;
57
static bool arg_copy_keymap = true;
58
static bool arg_copy_timezone = true;
59
static bool arg_chrome = true;
60
static bool arg_mute_console = false;
61

62
STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
1✔
63
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
1✔
64
STATIC_DESTRUCTOR_REGISTER(arg_kernel_image, freep);
1✔
65
STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
1✔
66

67
static int help(void) {
×
68
        int r;
×
69

70
        _cleanup_(table_unrefp) Table *options = NULL;
×
71
        r = option_parser_get_help_table(&options);
×
72
        if (r < 0)
×
73
                return r;
74

75
        help_cmdline("[OPTIONS...] [DEVICE]");
×
76
        help_abstract("Installs the OS to another block device.");
×
77
        help_section("Options:");
×
78

79
        r = table_print_or_warn(options);
×
80
        if (r < 0)
×
81
                return r;
82

83
        help_man_page_reference("systemd-sysinstall", "8");
×
84

85
        return 0;
86
}
87

88
static int parse_argv(int argc, char *argv[]) {
1✔
89
        int r;
1✔
90

91
        assert(argc >= 0);
1✔
92
        assert(argv);
1✔
93

94
        OptionParser opts = { argc, argv };
1✔
95

96
        FOREACH_OPTION_OR_RETURN(c, &opts)
16✔
97
                switch (c) {
14✔
98

99
                OPTION_COMMON_HELP:
×
100
                        return help();
×
101

102
                OPTION_COMMON_VERSION:
×
103
                        return version();
×
104

105
                OPTION_LONG("welcome", "no", "Disable the welcome text"):
1✔
106
                        r = parse_boolean_argument("--welcome=", opts.arg, &arg_welcome);
1✔
107
                        if (r < 0)
1✔
108
                                return r;
109

110
                        break;
111

112
                OPTION_LONG("erase", "BOOL", "Whether to erase the target disk"):
1✔
113
                        r = parse_tristate_argument_with_auto("--erase=", opts.arg, &arg_erase);
1✔
114
                        if (r < 0)
1✔
115
                                return r;
116
                        break;
117

118
                OPTION_LONG("confirm", "no", "Disable query for confirmation"):
1✔
119
                        r = parse_boolean_argument("--confirm=", opts.arg, &arg_confirm);
1✔
120
                        if (r < 0)
1✔
121
                                return r;
122
                        break;
123

124
                OPTION_LONG("summary", "no", "Disable summary before beginning operation"):
1✔
125
                        r = parse_boolean_argument("--summary=", opts.arg, &arg_summary);
1✔
126
                        if (r < 0)
1✔
127
                                return r;
128
                        break;
129

130
                OPTION_LONG("definitions", "DIR", "Find partition definitions in specified directory"): {
1✔
131
                        _cleanup_free_ char *path = NULL;
1✔
132
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &path);
1✔
133
                        if (r < 0)
1✔
134
                                return r;
135
                        if (strv_consume(&arg_definitions, TAKE_PTR(path)) < 0)
1✔
136
                                return log_oom();
×
137
                        break;
1✔
138
                }
139

140
                OPTION_LONG("reboot", "BOOL", "Whether to reboot after installation is complete"):
1✔
141
                        r = parse_boolean_argument("--reboot=", opts.arg, &arg_reboot);
1✔
142
                        if (r < 0)
1✔
143
                                return r;
144
                        break;
145

146
                OPTION_LONG("variables", "BOOL", "Whether to modify EFI variables"):
1✔
147
                        r = parse_tristate_argument_with_auto("--variables=", opts.arg, &arg_touch_variables);
1✔
148
                        if (r < 0)
1✔
149
                                return r;
150
                        break;
151

152
                OPTION_LONG("kernel", "IMAGE", "Explicitly pick kernel image to install"):
1✔
153
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &arg_kernel_image);
1✔
154
                        if (r < 0)
1✔
155
                                return r;
156
                        break;
157

158
                OPTION_LONG("set-credential", "ID:VALUE", "Install a credential with literal value to target system"):
1✔
159
                        r = machine_credential_set(&arg_credentials, opts.arg);
1✔
160
                        if (r < 0)
1✔
161
                                return r;
162
                        break;
163

164
                OPTION_LONG("load-credential", "ID:PATH", "Load a credential to install to new system from file or AF_UNIX stream socket"):
×
165
                        r = machine_credential_load(&arg_credentials, opts.arg);
×
166
                        if (r < 0)
×
167
                                return r;
168

169
                        break;
170

171
                OPTION_LONG("copy-locale", "no", "Don't copy current locale to target system"):
1✔
172
                        r = parse_boolean_argument("--copy-locale=", opts.arg, &arg_copy_locale);
1✔
173
                        if (r < 0)
1✔
174
                                return r;
175
                        break;
176

177
                OPTION_LONG("copy-keymap", "no", "Don't copy current keymap to target system"):
1✔
178
                        r = parse_boolean_argument("--copy-keymap=", opts.arg, &arg_copy_keymap);
1✔
179
                        if (r < 0)
1✔
180
                                return r;
181
                        break;
182

183
                OPTION_LONG("copy-timezone", "no", "Don't copy current timezone to target system"):
1✔
184
                        r = parse_boolean_argument("--copy-timezone=", opts.arg, &arg_copy_timezone);
1✔
185
                        if (r < 0)
1✔
186
                                return r;
187
                        break;
188

189
                OPTION_LONG("chrome", "no", "Whether to show a color bar at top and bottom of terminal"):
1✔
190
                        r = parse_boolean_argument("--chrome=", opts.arg, &arg_chrome);
1✔
191
                        if (r < 0)
1✔
192
                                return r;
193

194
                        break;
195

196
                OPTION_LONG("mute-console", "BOOL", "Whether to disallow kernel/PID 1 writes to the console while running"):
1✔
197
                        r = parse_boolean_argument("--mute-console=", opts.arg, &arg_mute_console);
1✔
198
                        if (r < 0)
1✔
199
                                return r;
200
                        break;
201
                }
202

203
        char **args = option_parser_get_args(&opts);
1✔
204

205
        if (strv_length(args) > 1)
1✔
206
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many arguments.");
×
207
        if (!strv_isempty(args)) {
2✔
208
                arg_node = strdup(args[0]);
1✔
209
                if (!arg_node)
1✔
210
                        return log_oom();
×
211
        }
212

213
        return 1;
214
}
215

216
static int print_welcome(sd_varlink **mute_console_link) {
1✔
217
        _cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL, *fancy_name = NULL;
1✔
218
        const char *pn, *ac;
1✔
219
        int r;
1✔
220

221
        assert(mute_console_link);
1✔
222

223
        if (!*mute_console_link && arg_mute_console)
1✔
224
                (void) mute_console(mute_console_link);
×
225

226
        if (!arg_welcome)
1✔
227
                return 0;
228

229
        r = parse_os_release(
×
230
                        /* root= */ NULL,
231
                        "PRETTY_NAME", &pretty_name,
232
                        "FANCY_NAME",  &fancy_name,
233
                        "NAME",        &os_name,
234
                        "ANSI_COLOR",  &ansi_color);
235
        if (r < 0)
×
236
                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
×
237
                               "Failed to read os-release file, ignoring: %m");
238

239
        pn = os_release_pretty_name(pretty_name, os_name);
×
240
        ac = isempty(ansi_color) ? "0" : ansi_color;
×
241

NEW
242
        if (use_fancy_name(unescape_fancy_name(&fancy_name)))
×
NEW
243
                printf(ANSI_HIGHLIGHT "Welcome to the " ANSI_NORMAL "%s" ANSI_HIGHLIGHT " Installer!" ANSI_NORMAL "\n", fancy_name);
×
NEW
244
        else if (colors_enabled())
×
UNCOV
245
                printf(ANSI_HIGHLIGHT "Welcome to the " ANSI_NORMAL "\x1B[%sm%s" ANSI_HIGHLIGHT " Installer!" ANSI_NORMAL "\n", ac, pn);
×
246
        else
247
                printf("Welcome to the %s Installer!\n", pn);
×
248

249
        putchar('\n');
1✔
250

251
        return 0;
252
}
253

254
static int connect_to_repart(sd_varlink **link) {
2✔
255
        int r;
2✔
256

257
        assert(link);
2✔
258

259
        if (*link) {
2✔
260
                /* Reset the time-out to default here, since we are reusing the connection, but might enqueue
261
                 * a different operation */
262
                r = sd_varlink_set_relative_timeout(*link, 0);
1✔
263
                if (r < 0)
1✔
264
                        return r;
2✔
265

266
                return 0;
1✔
267
        }
268

269
        _cleanup_close_ int fd = -EBADF;
2✔
270
        _cleanup_free_ char *repart = NULL;
1✔
271
        fd = pin_callout_binary("systemd-repart", &repart);
1✔
272
        if (fd < 0)
1✔
273
                return log_error_errno(fd, "Failed to find systemd-repart binary: %m");
×
274

275
        r = sd_varlink_connect_exec(link, repart, /* argv= */ NULL);
1✔
276
        if (r < 0)
1✔
277
                return log_error_errno(r, "Failed to connect to systemd-repart: %m");
×
278

279
        return 1;
280
}
281

282
static int acquire_device_list(
×
283
                sd_varlink **link,
284
                char ***ret_menu,
285
                char ***ret_accepted) {
286
        int r;
×
287

288
        r = connect_to_repart(link);
×
289
        if (r < 0)
×
290
                return r;
×
291

292
        _cleanup_strv_free_ char **menu = NULL, **accepted = NULL;
×
293

294
        sd_json_variant *reply = NULL;
×
295
        const char *error_id = NULL;
×
296
        r = sd_varlink_collectbo(
×
297
                        *link,
298
                        "io.systemd.Repart.ListCandidateDevices",
299
                        &reply,
300
                        &error_id,
301
                        SD_JSON_BUILD_PAIR_BOOLEAN("ignoreRoot", true));
302
        if (r < 0)
×
303
                return log_error_errno(r, "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %m");
×
304
        if (streq_ptr(error_id, "io.systemd.Repart.NoCandidateDevices"))
×
305
                log_debug("No candidate devices found.");
×
306
        else if (error_id) {
×
307
                r = sd_varlink_error_to_errno(error_id, reply); /* If this is a system errno style error, output it with %m */
×
308
                if (r != -EBADR)
×
309
                        return log_error_errno(r, "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %m");
×
310

311
                return log_error_errno(r, "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %s", error_id);
×
312
        } else {
313
                sd_json_variant *i;
×
314
                JSON_VARIANT_ARRAY_FOREACH(i, reply) {
×
315
                        _cleanup_(block_device_done) BlockDevice bd = BLOCK_DEVICE_NULL;
×
316

317
                        static const sd_json_dispatch_field dispatch_table[] = {
×
318
                                { "node",     SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(BlockDevice, node),     SD_JSON_MANDATORY },
319
                                { "symlinks", SD_JSON_VARIANT_ARRAY,  sd_json_dispatch_strv,   offsetof(BlockDevice, symlinks), 0                 },
320
                                {}
321
                        };
322

323
                        r = sd_json_dispatch(i, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &bd);
×
324
                        if (r < 0)
×
325
                                return r;
326

327
                        if (strv_extend(&accepted, bd.node) < 0)
×
328
                                return log_oom();
×
329
                        if (strv_extend_strv(&accepted, bd.symlinks, /* filter_duplicates= */ true) < 0)
×
330
                                return log_oom();
×
331

332
                        /* Prefer the by-id and by-loop-ref because they typically contain the strings most
333
                         * directly understood by the user */
334
                        const char *n = strv_find_prefix(bd.symlinks, "/dev/disk/by-id/");
×
335
                        if (!n)
×
336
                                n = strv_find_prefix(bd.symlinks, "/dev/disk/by-loop-ref/");
×
337
                        if (!n)
×
338
                                n = bd.node;
×
339

340
                        if (strv_extend(&menu, n) < 0)
×
341
                                return log_oom();
×
342
                }
343
        }
344

345
        *ret_menu = TAKE_PTR(menu);
×
346
        *ret_accepted = TAKE_PTR(accepted);
×
347
        return 0;
×
348
}
349

350
static int device_is_valid(const char *node, void *userdata) {
×
351

352
        if (!path_is_valid(node) || !path_is_absolute(node)) {
×
353
                log_error("Not a valid absolute file system path, refusing: %s", node);
×
354
                return false;
×
355
        }
356

357
        struct stat st;
×
358
        if (stat(node, &st) < 0) {
×
359
                log_error_errno(errno, "Failed to check if '%s' is a valid block device node: %m", node);
×
360
                return false;
361
        }
362
        if (!S_ISBLK(st.st_mode)) {
×
363
                log_error("Path '%s' does not refer to a valid block device node, refusing.", node);
×
364
                return false;
365
        }
366

367
        return true;
368
}
369

370
static int refresh_devices(char ***ret_menu, char ***ret_accepted, void *userdata) {
×
371
        sd_varlink **repart_link = ASSERT_PTR(userdata);
×
372

373
        (void) acquire_device_list(repart_link, ret_menu, ret_accepted);
×
374
        return 0;
×
375
}
376

377
static int prompt_block_device(sd_varlink **repart_link, char **ret_node) {
×
378
        int r;
×
379

380
        putchar('\n');
×
381

382
        _cleanup_strv_free_ char **menu = NULL, **accepted = NULL;
×
383
        (void) acquire_device_list(repart_link, &menu, &accepted);
×
384

385
        r = prompt_loop("Please enter target disk device",
×
386
                        GLYPH_COMPUTER_DISK,
387
                        menu,
388
                        accepted,
389
                        /* ellipsize_percentage= */ 20,
390
                        /* n_columns= */ 1,
391
                        /* column_width= */ 80,
392
                        device_is_valid,
393
                        refresh_devices,
394
                        /* userdata= */ repart_link,
395
                        PROMPT_SHOW_MENU|PROMPT_SHOW_MENU_NOW|PROMPT_MAY_SKIP|PROMPT_HIDE_SKIP_HINT|PROMPT_HIDE_MENU_HINT,
396
                        ret_node);
397
        if (r < 0)
×
398
                return r;
399
        if (r == 0)
×
400
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation cancelled.");
×
401

402
        return 0;
403
}
404

405
static int read_space_metrics(
2✔
406
                sd_json_variant *v,
407
                uint64_t *min_size,
408
                uint64_t *current_size,
409
                uint64_t *need_free) {
410

411
        int r;
2✔
412

413
        struct {
2✔
414
                uint64_t min_size;
415
                uint64_t current_size;
416
                uint64_t need_free;
417
        } p = {
2✔
418
                .min_size = UINT64_MAX,
419
                .current_size = UINT64_MAX,
420
                .need_free = UINT64_MAX,
421
        };
422

423
        static const sd_json_dispatch_field dispatch_table[] = {
2✔
424
                { "minimalSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, min_size),     0 },
425
                { "currentSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, current_size), 0 },
426
                { "needFreeBytes",    _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, need_free),    0 },
427
                {}
428
        };
429

430
        r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
2✔
431
        if (r < 0)
2✔
432
                return r;
2✔
433

434
        if (min_size)
2✔
435
                *min_size = p.min_size;
1✔
436
        if (current_size)
2✔
437
                *current_size = p.current_size;
1✔
438
        if (need_free)
2✔
439
                *need_free = p.need_free;
1✔
440

441
        return 0;
442
}
443

444
static int invoke_repart(
2✔
445
                sd_varlink **link,
446
                const char *node,
447
                bool erase,
448
                bool dry_run,
449
                uint64_t *min_size,        /* initialized both on success and error */
450
                uint64_t *current_size,    /* ditto */
451
                uint64_t *need_free) {     /* ditto */
452

453
        int r;
2✔
454

455
        assert(link);
2✔
456

457
        /* Note, if dry_run is true, then ENOSPC, E2BIG, EHWPOISON will not be logged about beyond LOG_DEBUG,
458
         * but all other errors will be */
459

460
        r = connect_to_repart(link);
2✔
461
        if (r < 0) {
2✔
462
                read_space_metrics(/* v= */ NULL, min_size, current_size, need_free);
×
463
                return r;
×
464
        }
465

466
        if (!dry_run) {
2✔
467
                /* Seeding the partitions might be very slow, disable timeout */
468
                r = sd_varlink_set_relative_timeout(*link, UINT64_MAX);
1✔
469
                if (r < 0)
1✔
470
                        return log_error_errno(r, "Failed to disable IPC timeout: %m");
×
471
        }
472

473
        sd_json_variant *reply = NULL;
2✔
474
        const char *error_id = NULL;
2✔
475
        r = sd_varlink_callbo(
2✔
476
                        *link,
477
                        "io.systemd.Repart.Run",
478
                        &reply,
479
                        &error_id,
480
                        SD_JSON_BUILD_PAIR_STRING("node", node),
481
                        SD_JSON_BUILD_PAIR_STRING("empty", erase ? "force" : "allow"),
482
                        SD_JSON_BUILD_PAIR_BOOLEAN("dryRun", dry_run),
483
                        SD_JSON_BUILD_PAIR_CONDITION(!!arg_definitions, "definitions", SD_JSON_BUILD_STRV(arg_definitions)),
484
                        SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsEmpty", true),
485
                        SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsFactoryReset", true));
486
        if (r < 0) {
2✔
487
                read_space_metrics(/* v= */ NULL, min_size, current_size, need_free);
×
488
                return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %m");
×
489
        }
490
        if (error_id) {
2✔
491
                if (streq(error_id, "io.systemd.Repart.InsufficientFreeSpace")) {
×
492
                        (void) read_space_metrics(reply, min_size, current_size, need_free);
×
493
                        return log_full_errno(
×
494
                                        dry_run ? LOG_DEBUG : LOG_ERR,
495
                                        SYNTHETIC_ERRNO(ENOSPC),
496
                                        "Not enough free space on disk, cannot install.");
497
                }
498
                if (streq(error_id, "io.systemd.Repart.DiskTooSmall")) {
×
499
                        (void) read_space_metrics(reply, min_size, current_size, need_free);
×
500
                        return log_full_errno(
×
501
                                        dry_run ? LOG_DEBUG : LOG_ERR,
502
                                        SYNTHETIC_ERRNO(E2BIG),
503
                                        "Disk too small for installation, cannot install.");
504
                }
505

506
                /* For all other errors reset the metrics */
507
                read_space_metrics(/* v= */ NULL, min_size, current_size, need_free);
×
508

509
                if (streq(error_id, "io.systemd.Repart.ConflictingDiskLabelPresent"))
×
510
                        return log_full_errno(
×
511
                                        dry_run ? LOG_DEBUG : LOG_ERR,
512
                                        SYNTHETIC_ERRNO(EHWPOISON),
513
                                        "A conflicting disk label is already present on the target disk, cannot install unless disk is erased.");
514

515
                r = sd_varlink_error_to_errno(error_id, reply); /* If this is a system errno style error, output it with %m */
×
516
                if (r != -EBADR)
×
517
                        return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %m");
×
518

519
                return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %s", error_id);
×
520
        }
521

522
        (void) read_space_metrics(reply, min_size, current_size, need_free);
2✔
523

524
        return 0;
525
}
526

527
static int prompt_erase(
×
528
                bool can_add,
529
                int *ret_erase) {
530
        int r;
×
531

532
        assert(ret_erase);
×
533

534
        putchar('\n');
×
535

536
        char **l = can_add ? STRV_MAKE("keep", "erase") : STRV_MAKE("erase");
×
537

538
        _cleanup_free_ char *reply = NULL;
×
539
        r = prompt_loop(can_add ?
×
540
                        "Please type 'keep' to install the OS in addition to what the disk already contains, or 'erase' to erase all data on the disk" :
541
                        "Please type 'erase' to confirm that all data on the disk shall be erased",
542
                        GLYPH_BROOM,
543
                        /* menu= */ l,
544
                        /* accepted= */ l,
545
                        /* ellipsize_percentage= */ 20,
546
                        /* n_columns= */ 2,
547
                        /* column_width= */ 40,
548
                        /* is_valid= */ NULL,
549
                        /* refresh= */ NULL,
550
                        /* userdata= */ NULL,
551
                        PROMPT_SHOW_MENU|PROMPT_MAY_SKIP|PROMPT_HIDE_MENU_HINT|PROMPT_HIDE_SKIP_HINT,
552
                        &reply);
553
        if (r < 0)
×
554
                return r;
555
        if (r == 0)
×
556
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation cancelled.");
×
557

558
        if (streq(reply, "erase"))
×
559
                *ret_erase = true;
×
560
        else if (streq(reply, "keep"))
×
561
                *ret_erase = false;
×
562
        else
563
                assert_not_reached();
×
564

565
        return 0;
566
}
567

568
static int prompt_touch_variables(void) {
1✔
569
        int r;
1✔
570

571
        if (arg_touch_variables >= 0)
1✔
572
                return 0;
1✔
573

574
        putchar('\n');
×
575

576
        char **l = STRV_MAKE("yes", "no");
×
577

578
        _cleanup_free_ char *reply = NULL;
×
579
        r = prompt_loop("Type 'yes' to register OS installation in firmware variables of the local system, 'no' otherwise",
×
580
                        GLYPH_ROCKET,
581
                        /* menu= */ l,
582
                        /* accepted= */ l,
583
                        /* ellipsize_percentage= */ 20,
584
                        /* n_columns= */ 2,
585
                        /* column_width= */ 40,
586
                        /* is_valid= */ NULL,
587
                        /* refresh= */ NULL,
588
                        /* userdata= */ NULL,
589
                        PROMPT_SHOW_MENU|PROMPT_MAY_SKIP|PROMPT_HIDE_MENU_HINT|PROMPT_HIDE_SKIP_HINT,
590
                        &reply);
591
        if (r < 0)
×
592
                return r;
593
        if (r == 0)
×
594
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation cancelled.");
×
595

596
        r = parse_boolean(reply);
×
597
        if (r < 0)
×
598
                return log_error_errno(r, "Failed to parse reply: %s", reply);
×
599

600
        arg_touch_variables = r;
×
601

602
        return 0;
×
603
}
604

605
static int prompt_confirm(void) {
1✔
606
        int r;
1✔
607

608
        if (!arg_confirm)
1✔
609
                return 0;
1✔
610

611
        putchar('\n');
×
612

613
        char **l = STRV_MAKE("yes", "no");
×
614

615
        _cleanup_free_ char *reply = NULL;
×
616
        r = prompt_loop(arg_summary ? "Please type 'yes' to confirm the choices above and begin the installation" :
×
617
                                      "Please type 'yes' to begin the installation",
618
                        GLYPH_WARNING_SIGN,
619
                        /* menu= */ l,
620
                        /* accepted= */ l,
621
                        /* ellipsize_percentage= */ 20,
622
                        /* n_columns= */ 2,
623
                        /* column_width= */ 40,
624
                        /* is_valid= */ NULL,
625
                        /* refresh= */ NULL,
626
                        /* userdata= */ NULL,
627
                        PROMPT_SHOW_MENU|PROMPT_MAY_SKIP|PROMPT_HIDE_MENU_HINT|PROMPT_HIDE_SKIP_HINT,
628
                        &reply);
629
        if (r < 0)
×
630
                return r;
631
        if (r == 0)
×
632
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation cancelled.");
×
633

634
        if (!streq(reply, "yes"))
×
635
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation not confirmed, cancelling.");
×
636

637
        return 0;
638
}
639

640
static int validate_run(sd_varlink **repart_link, const char *node) {
1✔
641
        int r;
1✔
642

643
        assert(repart_link);
1✔
644
        assert(node);
1✔
645

646
        /* First loop: either with explicitly configured --erase= value, or false. A second loop only if not configured explicitly. */
647
        bool try_erase = arg_erase > 0, conflicting_disk_label = false;
1✔
648
        for (;;) {
1✔
649
                uint64_t min_size = UINT64_MAX, current_size = UINT64_MAX, need_free = UINT64_MAX;
1✔
650
                r = invoke_repart(
1✔
651
                                repart_link,
652
                                node,
653
                                try_erase,
654
                                /* dry_run= */ true,
655
                                &min_size,
656
                                &current_size,
657
                                &need_free);
658
                if (r == -ENOSPC) {
1✔
659
                        /* The disk is large enough, but there's not enough unallocated space. Hence proceed, but require erasing */
660
                        if (try_erase || arg_erase >= 0)
×
661
                                return log_error_errno(r, "The selected disk is big enough for the installation but does not have enough free space.");
×
662

663
                        log_notice("The selected disk is big enough for the installation but does not have enough free space. Installation will require erasing.");
×
664
                        if (need_free != UINT64_MAX)
×
665
                                log_info("Required free space is %s.", FORMAT_BYTES(need_free));
×
666

667
                        try_erase = true;
668
                } else if (r == -E2BIG) {
1✔
669
                        /* Won't fit, whatever we do */
670
                        log_error_errno(r, "The selected disk is not large enough for an OS installation.");
×
671
                        if (current_size != UINT64_MAX)
×
672
                                log_info("The size of the selected disk is %s, but a minimal size of %s is required.",
×
673
                                         FORMAT_BYTES(current_size),
674
                                         FORMAT_BYTES(min_size));
675
                        return r;
676
                } else if (r == -EHWPOISON) {
1✔
677
                        if (try_erase || arg_erase >= 0)
×
678
                                return log_error_errno(r, "The selected disk contains a conflicting disk label, refusing.");
×
679

680
                        log_debug("Disk contains a conflicting disk label, checking if we could install the OS after erasing it.");
×
681
                        try_erase = true;
×
682
                        conflicting_disk_label = true;
×
683
                        continue;
×
684
                } else if (r < 0)
1✔
685
                        /* invoke_repart() already logged about all other errors */
686
                        return r;
687
                else
688
                        /* Nice, we can add the OS to the disk, without erasing anything. */
689
                        log_info("The selected disk has enough free space for an installation of the OS.");
1✔
690

691
                if (conflicting_disk_label)
1✔
692
                        log_warning("A conflicting disk label has been found, and must be erased for installation.");
×
693

694
                if (arg_erase < 0) {
1✔
695
                        r = prompt_erase(/* can_add= */ !try_erase, &arg_erase);
×
696
                        if (r < 0)
×
697
                                return r;
×
698
                }
699

700
                return 0;
701
        }
702
}
703

704
static int show_summary(void) {
1✔
705
        int r;
1✔
706

707
        if (!arg_summary)
1✔
708
                return 0;
1✔
709

710
        printf("\n"
×
711
               "%sSummary:%s\n", ansi_underline(), ansi_normal());
712

713
        _cleanup_(table_unrefp) Table *table = table_new_vertical();
×
714
        if (!table)
×
715
                return log_oom();
×
716

717
        r = table_add_many(
×
718
                        table,
719
                        TABLE_FIELD, "Selected Disk",
720
                        TABLE_STRING, arg_node,
721
                        TABLE_FIELD, "Erase Disk",
722
                        TABLE_BOOLEAN, arg_erase,
723
                        TABLE_SET_COLOR, arg_erase ? ansi_highlight_red() : NULL,
724
                        TABLE_FIELD, "Register in Firmware",
725
                        TABLE_BOOLEAN, arg_touch_variables);
726
        if (r < 0)
×
727
                return table_log_add_error(r);
×
728

729
        static const char * const map[] = {
730
                "firstboot.keymap",          "Keyboard Map",
731
                "firstboot.locale",          "Locale",
732
                "firstboot.locale-messages", "Locale (Messages)",
733
                "firstboot.timezone",        "Timezone",
734
                NULL
735
        };
736

737
        STRV_FOREACH_PAIR(id, text, map) {
×
738
                MachineCredential *c = machine_credential_find(&arg_credentials, *id);
×
739
                if (!c)
×
740
                        continue;
×
741

742
                _cleanup_free_ char *escaped = cescape_length(c->data, c->size);
×
743
                if (!escaped)
×
744
                        return log_oom();
×
745

746
                r = table_add_many(
×
747
                                table,
748
                                TABLE_FIELD, *text,
749
                                TABLE_STRING, escaped);
750
                if (r < 0)
×
751
                        return table_log_add_error(r);
×
752
        }
753

754
        unsigned n_extra_credentials = 0;
×
755
        FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
×
756
                bool covered = false;
×
757

758
                STRV_FOREACH_PAIR(id, text, map)
×
759
                        if (streq(*id, cred->id)) {
×
760
                                covered = true;
761
                                break;
762
                        }
763

764
                if (!covered)
×
765
                        n_extra_credentials++;
×
766
        }
767

768
        if (n_extra_credentials > 0) {
×
769
                r = table_add_many(
×
770
                                table,
771
                                TABLE_FIELD, "Extra Credentials",
772
                                TABLE_UINT, n_extra_credentials);
773
                if (r < 0)
×
774
                        return table_log_add_error(r);
×
775
        }
776

777
        r = table_print(table);
×
778
        if (r < 0)
×
779
                return r;
×
780

781
        return 0;
782
}
783

784
static int find_current_kernel(
×
785
                char **ret_filename,
786
                int *ret_fd) {
787

788
        int r;
×
789

790
        sd_id128_t uuid;
×
791
        r = efi_stub_get_device_part_uuid(&uuid);
×
792
        if (r == -ENOENT)
×
793
                return log_error_errno(r, "Cannot find current kernel, no stub partition UUID passed via EFI variables.");
×
794
        if (r < 0)
×
795
                return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
796

797
        _cleanup_free_ char *image = NULL;
×
798
        r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &image);
×
799
        if (r == -ENOENT)
×
800
                return log_error_errno(r, "Cannot find current kernel, no stub EFI binary path passed.");
×
801
        if (r < 0)
×
802
                return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
803

804
        /* Note: we search for the *host* ESP here (i.e. the one the current EFI paths relate to), not the
805
         * one of the target image */
806

807
        _cleanup_free_ char *partition_path = NULL;
×
808
        _cleanup_close_ int partition_fd = -EBADF;
×
809
        sd_id128_t partition_uuid;
×
810
        r = find_esp_and_warn_full(
×
811
                        /* root= */ NULL,
812
                        /* path= */ NULL,
813
                        /* unprivileged_mode= */ false,
814
                        &partition_path,
815
                        &partition_fd,
816
                        /* ret_part= */ NULL,
817
                        /* ret_pstart= */ NULL,
818
                        /* ret_psize= */ NULL,
819
                        &partition_uuid,
820
                        /* ret_devid= */ NULL);
821
        if (r < 0 && r != -ENOKEY)
×
822
                return r;
823
        if (r < 0 || !sd_id128_equal(uuid, partition_uuid)) {
×
824
                partition_path = mfree(partition_path);
×
825
                partition_fd = safe_close(partition_fd);
×
826

827
                r = find_xbootldr_and_warn_full(
×
828
                                /* root= */ NULL,
829
                                /* path= */ NULL,
830
                                /* unprivileged_mode= */ false,
831
                                &partition_path,
832
                                &partition_fd,
833
                                &partition_uuid,
834
                                /* ret_devid= */ NULL);
835
                if (r < 0 && r != -ENOKEY)
×
836
                        return r;
837

838
                if (r < 0 || !sd_id128_equal(uuid, partition_uuid))
×
839
                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Unable to find UKI on ESP/XBOOTLDR partitions.");
×
840
        }
841

842
        _cleanup_free_ char *resolved = NULL;
×
843
        _cleanup_close_ int fd = chase_and_openat(
×
844
                        /* root_fd= */ partition_fd,
845
                        /* dir_fd= */ partition_fd,
846
                        image,
847
                        CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_REGULAR,
848
                        O_RDONLY|O_CLOEXEC,
849
                        &resolved);
850
        if (fd < 0)
×
851
                return log_error_errno(fd, "Failed to find EFI binary '%s' on partition '%s': %m", image, partition_path);
×
852

853
        _cleanup_free_ char *fn = NULL;
×
854
        r = path_extract_filename(resolved, &fn);
×
855
        if (r < 0)
×
856
                return log_error_errno(r, "Failed to extract UKI file name from '%s': %m", resolved);
×
857

858
        if (ret_filename)
×
859
                *ret_filename = TAKE_PTR(fn);
×
860
        if (ret_fd)
×
861
                *ret_fd = TAKE_FD(fd);
×
862

863
        return 0;
864
}
865

866
static int connect_to_bootctl(sd_varlink **link) {
2✔
867
        int r;
2✔
868

869
        assert(link);
2✔
870

871
        if (*link)
2✔
872
                return 0;
2✔
873

874
        _cleanup_close_ int fd = -EBADF;
2✔
875
        _cleanup_free_ char *bootctl = NULL;
1✔
876
        fd = pin_callout_binary("bootctl", &bootctl);
1✔
877
        if (fd < 0)
1✔
878
                return log_error_errno(fd, "Failed to find bootctl binary: %m");
×
879

880
        r = sd_varlink_connect_exec(link, bootctl, /* argv= */ NULL);
1✔
881
        if (r < 0)
1✔
882
                return log_error_errno(r, "Failed to connect to bootctl: %m");
×
883

884
        r = sd_varlink_set_allow_fd_passing_output(*link, true);
1✔
885
        if (r < 0)
1✔
886
                return log_error_errno(r, "Failed to enable fd passing to bootctl: %m");
×
887

888
        return 1;
889
}
890

891
static int invoke_bootctl_install(
1✔
892
                sd_varlink **link,
893
                const char *root_dir,
894
                int root_fd) {
895
        int r;
1✔
896

897
        assert(link);
1✔
898
        assert(root_dir);
1✔
899
        assert(root_fd >= 0);
1✔
900

901
        r = connect_to_bootctl(link);
1✔
902
        if (r < 0)
1✔
903
                return r;
1✔
904

905
        int fd_idx = sd_varlink_push_dup_fd(*link, root_fd);
1✔
906
        if (fd_idx < 0)
1✔
907
                return log_error_errno(fd_idx, "Failed to submit root fd onto Varlink connection: %m");
×
908

909
        const char *error_id = NULL;
1✔
910
        r = varlink_callbo_and_log(
1✔
911
                        *link,
912
                        "io.systemd.BootControl.Install",
913
                        /* reply= */ NULL,
914
                        &error_id,
915
                        SD_JSON_BUILD_PAIR_STRING("operation", "new"),
916
                        SD_JSON_BUILD_PAIR_INTEGER("rootFileDescriptor", fd_idx),
917
                        SD_JSON_BUILD_PAIR_STRING("rootDirectory", root_dir),
918
                        SD_JSON_BUILD_PAIR_BOOLEAN("touchVariables", arg_touch_variables));
919
        if (r < 0)
1✔
920
                return r;
×
921

922
        return 0;
923
}
924

925
static int invoke_bootctl_link(
1✔
926
                sd_varlink **link,
927
                const char *root_dir,
928
                int root_fd,
929
                char **encrypted_credentials) {
930
        int r;
1✔
931

932
        assert(link);
1✔
933
        assert(root_dir);
1✔
934
        assert(root_fd >= 0);
1✔
935

936
        r = connect_to_bootctl(link);
1✔
937
        if (r < 0)
1✔
938
                return r;
1✔
939

940
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
1✔
941
        STRV_FOREACH_PAIR(name, value, encrypted_credentials) {
2✔
942
                _cleanup_free_ char *j = strjoin(*name, ".cred");
2✔
943
                if (!j)
1✔
944
                        return log_oom();
×
945

946
                r = sd_json_variant_append_arraybo(
1✔
947
                                &array,
948
                                SD_JSON_BUILD_PAIR_STRING("filename", j),
949
                                SD_JSON_BUILD_PAIR_BASE64("data", *value, strlen(*value)));
950
                if (r < 0)
1✔
951
                        return log_error_errno(r, "Failed to append credential to message: %m");
×
952
        }
953

954
        int root_fd_idx = sd_varlink_push_dup_fd(*link, root_fd);
1✔
955
        if (root_fd_idx < 0)
1✔
956
                return log_error_errno(root_fd_idx, "Failed to submit root fd onto Varlink connection: %m");
×
957

958
        _cleanup_free_ char *kernel_filename = NULL;
1✔
959
        _cleanup_close_ int kernel_fd = -EBADF;
1✔
960
        if (arg_kernel_image) {
1✔
961
                r = path_extract_filename(arg_kernel_image, &kernel_filename);
1✔
962
                if (r < 0)
1✔
963
                        return log_error_errno(r, "Failed to extract filename from kernel path '%s': %m", arg_kernel_image);
×
964
                if (r == O_DIRECTORY)
1✔
965
                        return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Kernel path '%s' refers to directory, must be regular file, refusing.", arg_kernel_image);
×
966

967
                kernel_fd = xopenat_full(XAT_FDROOT, arg_kernel_image, O_RDONLY|O_CLOEXEC, XO_REGULAR, MODE_INVALID);
1✔
968
                if (kernel_fd < 0)
1✔
969
                        return log_error_errno(kernel_fd, "Failed to open kernel image '%s': %m", arg_kernel_image);
×
970

971
        } else {
972
                r = find_current_kernel(&kernel_filename, &kernel_fd);
×
973
                if (r < 0)
×
974
                        return r;
975
        }
976

977
        int kernel_fd_idx = sd_varlink_push_dup_fd(*link, kernel_fd);
1✔
978
        if (kernel_fd_idx < 0)
1✔
979
                return log_error_errno(kernel_fd_idx, "Failed to submit kernel fd onto Varlink connection: %m");
×
980

981
        const char *error_id = NULL;
1✔
982
        r = varlink_callbo_and_log(
1✔
983
                        *link,
984
                        "io.systemd.BootControl.Link",
985
                        /* reply= */ NULL,
986
                        &error_id,
987
                        SD_JSON_BUILD_PAIR_INTEGER("rootFileDescriptor", root_fd_idx),
988
                        SD_JSON_BUILD_PAIR_STRING("rootDirectory", root_dir),
989
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("kernelFilename", kernel_filename),
990
                        SD_JSON_BUILD_PAIR_INTEGER("kernelFileDescriptor", kernel_fd_idx),
991
                        SD_JSON_BUILD_PAIR_CONDITION(!!array, "extraFiles", SD_JSON_BUILD_VARIANT(array)));
992
        if (r < 0)
1✔
993
                return r;
×
994

995
        return 0;
996
}
997

998
static int maybe_reboot(void) {
1✔
999
        int r;
1✔
1000

1001
        if (!arg_reboot)
1✔
1002
                return 0;
1✔
1003

1004
        log_notice("%s%sSystem will reboot now.",
×
1005
                   emoji_enabled() ? glyph(GLYPH_CIRCLE_ARROW) : "", emoji_enabled() ? " " : "");
1006

1007
        if (!any_key_to_proceed())
×
1008
                return 0;
1009

1010
        log_notice("%s%sInitiating reboot.",
×
1011
                   emoji_enabled() ? glyph(GLYPH_CIRCLE_ARROW) : "", emoji_enabled() ? " " : "");
1012

1013
        _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
1✔
1014
        r = sd_varlink_connect_address(&link, "/run/systemd/io.systemd.Shutdown");
×
1015
        if (r < 0)
×
1016
                return log_error_errno(r, "Failed to connect to systemd-logind: %m");
×
1017

1018
        sd_json_variant *reply = NULL;
×
1019
        const char *error_id = NULL;
×
1020
        r = varlink_callbo_and_log(
×
1021
                        link,
1022
                        "io.systemd.Shutdown.Reboot",
1023
                        &reply,
1024
                        &error_id);
1025
        if (r < 0)
×
1026
                return r;
×
1027

1028
        return 0;
1029
}
1030

1031
static int read_credential_locale(void) {
1✔
1032
        int r;
1✔
1033

1034
        if (!arg_copy_locale)
1✔
1035
                return 0;
1036

1037
        if (machine_credential_find(&arg_credentials, "firstboot.locale") ||
×
1038
            machine_credential_find(&arg_credentials, "firstboot.locale-messages"))
×
1039
                return 0;
1040

1041
        /* For the main locale we check the two env vars, and if neither is there, we use LC_NUMERIC, since
1042
         * it seems to be one of the most fundamental ones, and is not LC_MESSAGES for which we have a
1043
         * separate setting after all */
1044
        const char *l = getenv("LC_ALL") ?: getenv("LANG") ?: setlocale(LC_NUMERIC, NULL);
×
1045
        if (l) {
×
1046
                r = machine_credential_add(&arg_credentials, "firstboot.locale", l, /* size= */ SIZE_MAX);
×
1047
                if (r < 0)
×
1048
                        return log_oom();
×
1049
        }
1050

1051
        const char *m = setlocale(LC_MESSAGES, NULL);
×
1052
        if (m && !streq_ptr(m, l)) {
×
1053
                r = machine_credential_add(&arg_credentials, "firstboot.locale-messages", m, /* size= */ SIZE_MAX);
×
1054
                if (r < 0)
×
1055
                        return log_oom();
×
1056
        }
1057

1058
        return 0;
1059
}
1060

1061
static int read_credential_keymap(void) {
1✔
1062
        int r;
1✔
1063

1064
        if (!arg_copy_keymap)
1✔
1065
                return 0;
1✔
1066

1067
        if (machine_credential_find(&arg_credentials, "firstboot.keymap"))
×
1068
                return 0;
1069

1070
        _cleanup_free_ char *keymap = NULL;
×
1071
        r = parse_env_file(
×
1072
                        /* f= */ NULL,
1073
                        etc_vconsole_conf(),
1074
                        "KEYMAP", &keymap);
1075
        if (r < 0 && r != -ENOENT)
×
1076
                return log_error_errno(r, "Failed to parse '%s': %m", etc_vconsole_conf());
×
1077

1078
        if (!isempty(keymap)) {
×
1079
                r = machine_credential_add(&arg_credentials, "firstboot.keymap", keymap, /* size= */ SIZE_MAX);
×
1080
                if (r < 0)
×
1081
                        return log_oom();
×
1082
        }
1083

1084
        return 0;
1085
}
1086

1087
static int read_credential_timezone(void) {
1✔
1088
        int r;
1✔
1089

1090
        if (!arg_copy_timezone)
1✔
1091
                return 0;
1✔
1092

1093
        if (machine_credential_find(&arg_credentials, "firstboot.timezone"))
×
1094
                return 0;
1095

1096
        _cleanup_free_ char *tz = NULL;
×
1097
        r = get_timezone_prefer_env(&tz);
×
1098
        if (r < 0)
×
1099
                log_warning_errno(r, "Failed to read timezone, skipping timezone propagation: %m");
×
1100
        else {
1101
                r = machine_credential_add(&arg_credentials, "firstboot.timezone", tz, /* size= */ SIZE_MAX);
×
1102
                if (r < 0)
×
1103
                        return log_oom();
×
1104
        }
1105

1106
        return 0;
1107
}
1108

1109
static int read_credentials(void) {
1✔
1110
        int r;
1✔
1111

1112
        r = read_credential_locale();
1✔
1113
        if (r < 0)
1✔
1114
                return r;
1115

1116
        r = read_credential_keymap();
1✔
1117
        if (r < 0)
1✔
1118
                return r;
1119

1120
        r = read_credential_timezone();
1✔
1121
        if (r < 0)
1✔
1122
                return r;
×
1123

1124
        return 0;
1125
}
1126

1127
static int connect_to_creds(sd_varlink **link) {
1✔
1128
        int r;
1✔
1129

1130
        assert(link);
1✔
1131

1132
        if (*link)
1✔
1133
                return 0;
1✔
1134

1135
        _cleanup_close_ int fd = -EBADF;
1✔
1136
        _cleanup_free_ char *creds = NULL;
1✔
1137
        fd = pin_callout_binary("systemd-creds", &creds);
1✔
1138
        if (fd < 0)
1✔
1139
                return log_error_errno(fd, "Failed to find systemd-creds binary: %m");
×
1140

1141
        r = sd_varlink_connect_exec(link, creds, /* argv= */ NULL);
1✔
1142
        if (r < 0)
1✔
1143
                return log_error_errno(r, "Failed to connect to systemd-creds: %m");
×
1144

1145
        return 1;
1146
}
1147

1148
static int encrypt_one_credential(sd_varlink **link, const MachineCredential *input, char ***encrypted) {
1✔
1149
        int r;
1✔
1150

1151
        assert(link);
1✔
1152
        assert(input);
1✔
1153
        assert(encrypted);
1✔
1154

1155
        log_info("Encrypting credential '%s'...", input->id);
1✔
1156

1157
        r = connect_to_creds(link);
1✔
1158
        if (r < 0)
1✔
1159
                return r;
1✔
1160

1161
        sd_json_variant *reply = NULL;
1✔
1162
        const char *error_id = NULL;
1✔
1163
        r = varlink_callbo_and_log(
1✔
1164
                        *link,
1165
                        "io.systemd.Credentials.Encrypt",
1166
                        &reply,
1167
                        &error_id,
1168
                        SD_JSON_BUILD_PAIR_STRING("name", input->id),
1169
                        SD_JSON_BUILD_PAIR_BASE64("data", input->data, input->size),
1170
                        SD_JSON_BUILD_PAIR_STRING("scope", "system"),
1171
                        /* We pick the 'auto_initrd' key for this, since we want TPM if available, but are fine with NULL if not */
1172
                        SD_JSON_BUILD_PAIR_STRING("withKey", "auto_initrd"));
1173
        if (r < 0)
1✔
1174
                return r;
1175

1176
        static const sd_json_dispatch_field dispatch_table[] = {
1✔
1177
                { "blob", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
1178
                {}
1179
        };
1180

1181
        const char *blob = NULL;
1✔
1182
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &blob);
1✔
1183
        if (r < 0)
1✔
1184
                return r;
1185

1186
        r = strv_extend_many(encrypted, input->id, blob);
1✔
1187
        if (r < 0)
1✔
1188
                return r;
×
1189

1190
        return 0;
1191
}
1192

1193
static int encrypt_credentials(sd_varlink **link, char ***encrypted) {
1✔
1194
        int r;
1✔
1195

1196
        assert(link);
1✔
1197
        assert(encrypted);
1✔
1198

1199
        FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
2✔
1200
                r = encrypt_one_credential(link, cred, encrypted);
1✔
1201
                if (r < 0)
1✔
1202
                        return r;
1203
        }
1204

1205
        return 0;
1206
}
1207

1208
static const ImagePolicy image_policy = {
1209
        .n_policies = 4,
1210
        .policies = {
1211
                /* We mount / and /usr/ so that we can get access to /etc/machine-id and /etc/kernel/ */
1212
                { PARTITION_ROOT,     PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1213
                { PARTITION_USR,      PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1214
                { PARTITION_ESP,      PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1215
                { PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1216
        },
1217
        .default_flags = PARTITION_POLICY_IGNORE,
1218
};
1219

1220
static int settle_definitions(void) {
1✔
1221
        int r;
1✔
1222

1223
        if (arg_definitions)
1✔
1224
                return 0;
1✔
1225

1226
        /* If /usr/lib/repart.sysinstall.d/ is populated, use it, otherwise use the regular definition
1227
         * files */
1228

1229
        _cleanup_strv_free_ char **files = NULL;
×
1230
        r = conf_files_list_strv(
×
1231
                        &files,
1232
                        ".conf",
1233
                        /* root= */ NULL,
1234
                        CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN|CONF_FILES_DONT_PREFIX_ROOT,
1235
                        (const char**) CONF_PATHS_STRV("repart.sysinstall.d"));
×
1236
        if (r < 0)
×
1237
                return log_error_errno(r, "Failed to enumerate *.conf files: %m");
×
1238

1239
        if (!strv_isempty(files)) {
×
1240
                arg_definitions = strv_copy(CONF_PATHS_STRV("repart.sysinstall.d"));
×
1241
                if (!arg_definitions)
×
1242
                        return log_oom();
×
1243
        }
1244

1245
        return 0;
1246
}
1247

1248
static int run(int argc, char *argv[]) {
1✔
1249
        int r;
1✔
1250

1251
        setlocale(LC_ALL, "");
1✔
1252

1253
        r = parse_argv(argc, argv);
1✔
1254
        if (r <= 0)
1✔
1255
                return r;
1✔
1256

1257
        log_setup();
1✔
1258

1259
        r = settle_definitions();
1✔
1260
        if (r < 0)
1✔
1261
                return r;
1262

1263
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
1✔
1264
        if (arg_welcome) {
1✔
1265
                if (arg_mute_console)
×
1266
                        (void) mute_console(&mute_console_link);
×
1267

1268
                (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
×
1269

1270
                if (arg_chrome)
×
1271
                        chrome_show("Operating System Installer", /* bottom= */ NULL);
×
1272
        }
1273

1274
        DEFER_VOID_CALL(chrome_hide);
×
1275

1276
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *repart_link = NULL;
1✔
1277
        if (arg_node) {
1✔
1278
                r = print_welcome(&mute_console_link);
1✔
1279
                if (r < 0)
1✔
1280
                        return r;
1281

1282
                r = validate_run(&repart_link, arg_node);
1✔
1283
                if (r < 0)
1✔
1284
                        return r;
1285
        } else {
1286
                /* Determine the minimum disk size */
1287
                uint64_t min_size = UINT64_MAX;
×
1288
                r = invoke_repart(
×
1289
                                &repart_link,
1290
                                /* node= */ NULL,
1291
                                /* erase= */ true,
1292
                                /* dry_run= */ true,
1293
                                &min_size,
1294
                                /* current_size= */ NULL,
1295
                                /* need_free= */ NULL);
1296
                if (r < 0)
×
1297
                        return r;
×
1298

1299
                r = print_welcome(&mute_console_link);
×
1300
                if (r < 0)
×
1301
                        return r;
1302

1303
                log_info("Required minimal installation disk size is %s.", FORMAT_BYTES(min_size));
×
1304

1305
                for (;;) {
×
1306
                        _cleanup_free_ char *node = NULL;
×
1307
                        r = prompt_block_device(&repart_link, &node);
×
1308
                        if (r < 0)
×
1309
                                return r;
1310

1311
                        r = validate_run(&repart_link, node);
×
1312
                        if (IN_SET(r, -ENOSPC, -E2BIG, -EHWPOISON)) /* Device is no fit, pick other */
×
1313
                                continue;
×
1314
                        if (r < 0)
×
1315
                                return r;
1316

1317
                        arg_node = TAKE_PTR(node);
×
1318
                        break;
×
1319
                }
1320
        }
1321

1322
        r = prompt_touch_variables();
1✔
1323
        if (r < 0)
1✔
1324
                return r;
1325

1326
        r = read_credentials();
1✔
1327
        if (r < 0)
1✔
1328
                return r;
1329

1330
        /* Verify we have everything we need */
1331
        assert(arg_node);
1✔
1332
        assert(arg_erase >= 0);
1✔
1333
        assert(arg_touch_variables >= 0);
1✔
1334

1335
        r = show_summary();
1✔
1336
        if (r < 0)
1✔
1337
                return r;
1338

1339
        r = prompt_confirm();
1✔
1340
        if (r < 0)
1✔
1341
                return r;
1342

1343
        putchar('\n');
1✔
1344

1345
        log_notice("%s%sEncrypting credentials...",
2✔
1346
                   emoji_enabled() ? glyph(GLYPH_LOCK_AND_KEY) : "", emoji_enabled() ? " " : "");
1347

1348
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *creds_link = NULL;
1✔
1349
        _cleanup_strv_free_ char **encrypted_credentials = NULL;
1✔
1350
        r = encrypt_credentials(&creds_link, &encrypted_credentials);
1✔
1351
        if (r < 0)
1✔
1352
                return r;
1353

1354
        log_notice("%s%sInstalling partitions...",
2✔
1355
                   emoji_enabled() ? glyph(GLYPH_COMPUTER_DISK) : "", emoji_enabled() ? " " : "");
1356

1357
        /* Do the main part of the installation */
1358
        r = invoke_repart(
1✔
1359
                        &repart_link,
1360
                        arg_node,
1361
                        arg_erase,
1362
                        /* dry_run= */ false,
1363
                        /* min_size= */ NULL,
1364
                        /* current_size= */ NULL,
1365
                        /* need_free= */ NULL);
1366
        if (r < 0)
1✔
1367
                return r;
1368

1369
        log_notice("%s%sMounting partitions...",
2✔
1370
                   emoji_enabled() ? glyph(GLYPH_COMPUTER_DISK) : "", emoji_enabled() ? " " : "");
1371

1372
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
1✔
1373
        _cleanup_(umount_and_freep) char *root_dir = NULL;
×
1374
        _cleanup_close_ int root_fd = -EBADF;
1✔
1375
        r = mount_image_privately_interactively(
1✔
1376
                        arg_node,
1377
                        &image_policy,
1378
                        DISSECT_IMAGE_REQUIRE_ROOT |
1379
                        DISSECT_IMAGE_RELAX_VAR_CHECK |
1380
                        DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
1381
                        DISSECT_IMAGE_DISCARD_ANY |
1382
                        DISSECT_IMAGE_GPT_ONLY |
1383
                        DISSECT_IMAGE_FSCK |
1384
                        DISSECT_IMAGE_USR_NO_ROOT |
1385
                        DISSECT_IMAGE_ADD_PARTITION_DEVICES |
1386
                        DISSECT_IMAGE_PIN_PARTITION_DEVICES,
1387
                        &root_dir,
1388
                        &root_fd,
1389
                        &loop_device);
1390
        if (r < 0)
1✔
1391
                return log_error_errno(r, "Failed to mount new image: %m");
×
1392

1393
        log_notice("%s%sInstalling kernel...",
2✔
1394
                   emoji_enabled() ? glyph(GLYPH_COMPUTER_DISK) : "", emoji_enabled() ? " " : "");
1395

1396
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *bootctl_link = NULL;
1✔
1397
        r = invoke_bootctl_link(&bootctl_link, root_dir, root_fd, encrypted_credentials);
1✔
1398
        if (r < 0)
1✔
1399
                return r;
1400

1401
        log_notice("%s%sInstalling boot loader...",
2✔
1402
                   emoji_enabled() ? glyph(GLYPH_COMPUTER_DISK) : "", emoji_enabled() ? " " : "");
1403

1404
        r = invoke_bootctl_install(&bootctl_link, root_dir, root_fd);
1✔
1405
        if (r < 0)
1✔
1406
                return r;
1407

1408
        log_notice("%s%sUnmounting partitions...",
2✔
1409
                   emoji_enabled() ? glyph(GLYPH_COMPUTER_DISK) : "", emoji_enabled() ? " " : "");
1410

1411
        root_fd = safe_close(root_fd);
1✔
1412
        r = umount_recursive(root_dir, /* flags= */ 0);
1✔
1413
        if (r < 0)
1✔
1414
                log_warning_errno(r, "Failed to unmount target disk, proceeding anyway: %m");
×
1415
        loop_device = loop_device_unref(loop_device);
1✔
1416
        sync();
1✔
1417

1418
        log_notice("%s%sInstallation succeeded.",
2✔
1419
                   emoji_enabled() ? glyph(GLYPH_SPARKLES) : "", emoji_enabled() ? " " : "");
1420

1421
        r = maybe_reboot();
1✔
1422
        if (r < 0)
1✔
1423
                return r;
×
1424

1425
        return 0;
1426
}
1427

1428
DEFINE_MAIN_FUNCTION(run);
1✔
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