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

systemd / systemd / 29709461870

19 Jul 2026 07:23PM UTC coverage: 72.889% (-0.09%) from 72.979%
29709461870

push

github

web-flow
Manage dlopen notes at beginning of execution, and downgrade priorities in shared libraries (#43060)

184 of 218 new or added lines in 86 files covered. (84.4%)

1195 existing lines in 53 files now uncovered.

345898 of 474552 relevant lines covered (72.89%)

1349499.83 hits per line

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

49.01
/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 "bus-polkit.h"
15
#include "chase.h"
16
#include "conf-files.h"
17
#include "constants.h"
18
#include "dlopen-note.h"
19
#include "efi-loader.h"
20
#include "efivars.h"
21
#include "env-file.h"
22
#include "escape.h"
23
#include "fd-util.h"
24
#include "find-esp.h"
25
#include "format-table.h"
26
#include "format-util.h"
27
#include "fs-util.h"
28
#include "glyph-util.h"
29
#include "hashmap.h"
30
#include "help-util.h"
31
#include "image-policy.h"
32
#include "json-util.h"
33
#include "locale-setup.h"
34
#include "log.h"
35
#include "loop-util.h"
36
#include "machine-credential.h"
37
#include "main-func.h"
38
#include "mount-util.h"
39
#include "options.h"
40
#include "os-util.h"
41
#include "parse-argument.h"
42
#include "parse-util.h"
43
#include "path-util.h"
44
#include "prompt-util.h"
45
#include "string-table.h"
46
#include "strv.h"
47
#include "terminal-util.h"
48
#include "varlink-io.systemd.SysInstall.h"
49
#include "varlink-util.h"
50

51
static char *arg_node = NULL;
52
static bool arg_welcome = true;
53
static int arg_erase = -1;            /* tri-state */
54
static bool arg_confirm = true;
55
static bool arg_summary = true;
56
static char **arg_definitions = NULL;
57
static char *arg_kernel_image = NULL;
58
static bool arg_reboot = false;
59
static int arg_touch_variables = -1;  /* tri-state */
60
static MachineCredentialContext arg_credentials = {};
61
static bool arg_copy_locale = true;
62
static bool arg_copy_keymap = true;
63
static bool arg_copy_timezone = true;
64
static bool arg_chrome = true;
65
static bool arg_mute_console = false;
66
static bool arg_varlink = false;
67

68
STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
14✔
69
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
14✔
70
STATIC_DESTRUCTOR_REGISTER(arg_kernel_image, freep);
14✔
71
STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
14✔
72

73
typedef enum ProgressPhase {
74
        PROGRESS_ENCRYPT_CREDENTIALS,
75
        PROGRESS_INSTALL_PARTITIONS,
76
        PROGRESS_MOUNT_PARTITIONS,
77
        PROGRESS_INSTALL_KERNEL,
78
        PROGRESS_INSTALL_BOOTLOADER,
79
        PROGRESS_UNMOUNT_PARTITIONS,
80
        _PROGRESS_PHASE_MAX,
81
        _PROGRESS_PHASE_INVALID = -EINVAL,
82
} ProgressPhase;
83

84
static const char *progress_phase_table[_PROGRESS_PHASE_MAX] = {
85
        [PROGRESS_ENCRYPT_CREDENTIALS] = "encrypt-credentials",
86
        [PROGRESS_INSTALL_PARTITIONS]  = "install-partitions",
87
        [PROGRESS_MOUNT_PARTITIONS]    = "mount-partitions",
88
        [PROGRESS_INSTALL_KERNEL]      = "install-kernel",
89
        [PROGRESS_INSTALL_BOOTLOADER]  = "install-bootloader",
90
        [PROGRESS_UNMOUNT_PARTITIONS]  = "unmount-partitions",
91
};
92

93
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(progress_phase, ProgressPhase);
14✔
94

95
static const char *progress_phase_log_table[_PROGRESS_PHASE_MAX] = {
96
        [PROGRESS_ENCRYPT_CREDENTIALS] = "Encrypting credentials...",
97
        [PROGRESS_INSTALL_PARTITIONS]  = "Installing partitions...",
98
        [PROGRESS_MOUNT_PARTITIONS]    = "Mounting partitions...",
99
        [PROGRESS_INSTALL_KERNEL]      = "Installing kernel...",
100
        [PROGRESS_INSTALL_BOOTLOADER]  = "Installing boot loader...",
101
        [PROGRESS_UNMOUNT_PARTITIONS]  = "Unmounting partitions...",
102
};
103

104
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(progress_phase_log, ProgressPhase);
20✔
105

106
typedef enum DeviceFit {
107
        DEVICE_FIT_ENOUGH_FREE_SPACE,
108
        DEVICE_FIT_INSUFFICIENT_FREE_SPACE,
109
        DEVICE_FIT_DISK_TOO_SMALL,
110
        DEVICE_FIT_CONFLICTING_DISK_LABEL_PRESENT,
111
        _DEVICE_FIT_MAX,
112
        _DEVICE_FIT_INVALID = -EINVAL,
113
} DeviceFit;
114

115
static const char *device_fit_table[_DEVICE_FIT_MAX] = {
116
        [DEVICE_FIT_ENOUGH_FREE_SPACE]              = "enough-free-space",
117
        [DEVICE_FIT_INSUFFICIENT_FREE_SPACE]        = "insufficient-free-space",
118
        [DEVICE_FIT_DISK_TOO_SMALL]                 = "disk-too-small",
119
        [DEVICE_FIT_CONFLICTING_DISK_LABEL_PRESENT] = "conflicting-disk-label-present",
120
};
121

122
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(device_fit, DeviceFit);
×
123

124
typedef struct SysInstallContext {
125
        bool copy_locale;
126
        bool copy_keymap;
127
        bool copy_timezone;
128
        MachineCredentialContext credentials;
129
        char **definitions;
130
        bool erase;
131
        char *node;
132
        char *kernel_filename;
133
        int kernel_fd;
134
        bool touch_variables;
135

136
        sd_varlink *repart_link;
137

138
        sd_varlink *link; /* If 'more' is used on the Varlink call, we'll send progress info over this link */
139
} SysInstallContext;
140

141
static void sysinstall_context_done(SysInstallContext *c) {
2✔
142
        assert(c);
2✔
143

144
        strv_free(c->definitions);
2✔
145

146
        free(c->node);
2✔
147

148
        free(c->kernel_filename);
2✔
149
        safe_close(c->kernel_fd);
2✔
150

151
        machine_credential_context_done(&c->credentials);
2✔
152

153
        sd_varlink_flush_close_unref(c->repart_link);
2✔
154

155
        sd_varlink_unref(c->link);
2✔
156
}
2✔
157

158
static int help(void) {
×
159
        int r;
×
160

161
        _cleanup_(table_unrefp) Table *options = NULL;
×
162
        r = option_parser_get_help_table(&options);
×
163
        if (r < 0)
×
164
                return r;
165

166
        help_cmdline("[OPTIONS...] [DEVICE]");
×
167
        help_abstract("Installs the OS to another block device.");
×
168
        help_section("Options:");
×
169

170
        r = table_print_or_warn(options);
×
171
        if (r < 0)
×
172
                return r;
173

174
        help_man_page_reference("systemd-sysinstall", "8");
×
175

176
        return 0;
177
}
178

179
static int parse_argv(int argc, char *argv[]) {
14✔
180
        int r;
14✔
181

182
        assert(argc >= 0);
14✔
183
        assert(argv);
14✔
184

185
        OptionParser opts = { argc, argv };
14✔
186

187
        FOREACH_OPTION_OR_RETURN(c, &opts)
56✔
188
                switch (c) {
14✔
189

190
                OPTION_COMMON_HELP:
×
191
                        return help();
×
192

193
                OPTION_COMMON_VERSION:
×
194
                        return version();
×
195

196
                OPTION_LONG("welcome", "no", "Disable the welcome text"):
1✔
197
                        r = parse_boolean_argument("--welcome=", opts.arg, &arg_welcome);
1✔
198
                        if (r < 0)
1✔
199
                                return r;
200

201
                        break;
202

203
                OPTION_LONG("erase", "BOOL", "Whether to erase the target disk"):
1✔
204
                        r = parse_tristate_argument_with_auto("--erase=", opts.arg, &arg_erase);
1✔
205
                        if (r < 0)
1✔
206
                                return r;
207
                        break;
208

209
                OPTION_LONG("confirm", "no", "Disable query for confirmation"):
1✔
210
                        r = parse_boolean_argument("--confirm=", opts.arg, &arg_confirm);
1✔
211
                        if (r < 0)
1✔
212
                                return r;
213
                        break;
214

215
                OPTION_LONG("summary", "no", "Disable summary before beginning operation"):
1✔
216
                        r = parse_boolean_argument("--summary=", opts.arg, &arg_summary);
1✔
217
                        if (r < 0)
1✔
218
                                return r;
219
                        break;
220

221
                OPTION_LONG("definitions", "DIR", "Find partition definitions in specified directory"): {
1✔
222
                        _cleanup_free_ char *path = NULL;
1✔
223
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &path);
1✔
224
                        if (r < 0)
1✔
225
                                return r;
226
                        if (strv_consume(&arg_definitions, TAKE_PTR(path)) < 0)
1✔
227
                                return log_oom();
×
228
                        break;
1✔
229
                }
230

231
                OPTION_LONG("reboot", "BOOL", "Whether to reboot after installation is complete"):
1✔
232
                        r = parse_boolean_argument("--reboot=", opts.arg, &arg_reboot);
1✔
233
                        if (r < 0)
1✔
234
                                return r;
235
                        break;
236

237
                OPTION_LONG("variables", "BOOL", "Whether to modify EFI variables"):
1✔
238
                        r = parse_tristate_argument_with_auto("--variables=", opts.arg, &arg_touch_variables);
1✔
239
                        if (r < 0)
1✔
240
                                return r;
241
                        break;
242

243
                OPTION_LONG("kernel", "IMAGE", "Explicitly pick kernel image to install"):
1✔
244
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &arg_kernel_image);
1✔
245
                        if (r < 0)
1✔
246
                                return r;
247
                        break;
248

249
                OPTION_LONG("set-credential", "ID:VALUE", "Install a credential with literal value to target system"):
1✔
250
                        r = machine_credential_set(&arg_credentials, opts.arg);
1✔
251
                        if (r < 0)
1✔
252
                                return r;
253
                        break;
254

255
                OPTION_LONG("load-credential", "ID:PATH", "Load a credential to install to new system from file or AF_UNIX stream socket"):
×
256
                        r = machine_credential_load(&arg_credentials, opts.arg);
×
257
                        if (r < 0)
×
258
                                return r;
259

260
                        break;
261

262
                OPTION_LONG("copy-locale", "no", "Don't copy current locale to target system"):
1✔
263
                        r = parse_boolean_argument("--copy-locale=", opts.arg, &arg_copy_locale);
1✔
264
                        if (r < 0)
1✔
265
                                return r;
266
                        break;
267

268
                OPTION_LONG("copy-keymap", "no", "Don't copy current keymap to target system"):
1✔
269
                        r = parse_boolean_argument("--copy-keymap=", opts.arg, &arg_copy_keymap);
1✔
270
                        if (r < 0)
1✔
271
                                return r;
272
                        break;
273

274
                OPTION_LONG("copy-timezone", "no", "Don't copy current timezone to target system"):
1✔
275
                        r = parse_boolean_argument("--copy-timezone=", opts.arg, &arg_copy_timezone);
1✔
276
                        if (r < 0)
1✔
277
                                return r;
278
                        break;
279

280
                OPTION_LONG("chrome", "no", "Whether to show a color bar at top and bottom of terminal"):
1✔
281
                        r = parse_boolean_argument("--chrome=", opts.arg, &arg_chrome);
1✔
282
                        if (r < 0)
1✔
283
                                return r;
284

285
                        break;
286

287
                OPTION_LONG("mute-console", "BOOL", "Whether to disallow kernel/PID 1 writes to the console while running"):
1✔
288
                        r = parse_boolean_argument("--mute-console=", opts.arg, &arg_mute_console);
1✔
289
                        if (r < 0)
1✔
290
                                return r;
291
                        break;
292
                }
293

294
        char **args = option_parser_get_args(&opts);
14✔
295

296
        if (strv_length(args) > 1)
14✔
297
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many arguments.");
×
298
        if (!strv_isempty(args)) {
14✔
299
                arg_node = strdup(args[0]);
1✔
300
                if (!arg_node)
1✔
301
                        return log_oom();
×
302
        }
303

304
        r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
14✔
305
        if (r < 0)
14✔
306
                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
×
307
        if (r > 0)
14✔
308
                arg_varlink = true;
13✔
309

310
        return 1;
311
}
312

313
static int print_welcome(sd_varlink **mute_console_link) {
1✔
314
        _cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL, *fancy_name = NULL;
1✔
315
        const char *pn, *ac;
1✔
316
        int r;
1✔
317

318
        assert(mute_console_link);
1✔
319

320
        if (!*mute_console_link && arg_mute_console)
1✔
321
                (void) mute_console(mute_console_link);
×
322

323
        if (!arg_welcome)
1✔
324
                return 0;
325

326
        r = parse_os_release(
×
327
                        /* root= */ NULL,
328
                        "PRETTY_NAME", &pretty_name,
329
                        "FANCY_NAME",  &fancy_name,
330
                        "NAME",        &os_name,
331
                        "ANSI_COLOR",  &ansi_color);
332
        if (r < 0)
×
333
                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
×
334
                               "Failed to read os-release file, ignoring: %m");
335

336
        pn = os_release_pretty_name(pretty_name, os_name);
×
337
        ac = isempty(ansi_color) ? "0" : ansi_color;
×
338

339
        if (use_fancy_name(unescape_fancy_name(&fancy_name)))
×
340
                printf(ANSI_HIGHLIGHT "Welcome to the " ANSI_NORMAL "%s" ANSI_HIGHLIGHT " Installer!" ANSI_NORMAL "\n", fancy_name);
×
341
        else if (colors_enabled())
×
342
                printf(ANSI_HIGHLIGHT "Welcome to the " ANSI_NORMAL "\x1B[%sm%s" ANSI_HIGHLIGHT " Installer!" ANSI_NORMAL "\n", ac, pn);
×
343
        else
344
                printf("Welcome to the %s Installer!\n", pn);
×
345

346
        putchar('\n');
1✔
347

348
        return 0;
349
}
350

351
static int connect_to_repart(sd_varlink **link) {
3✔
352
        int r;
3✔
353

354
        assert(link);
3✔
355

356
        if (*link) {
3✔
357
                /* Reset the time-out to default here, since we are reusing the connection, but might enqueue
358
                 * a different operation */
359
                r = sd_varlink_set_relative_timeout(*link, 0);
×
360
                if (r < 0)
×
361
                        return r;
3✔
362

363
                return 0;
×
364
        }
365

366
        _cleanup_close_ int fd = -EBADF;
3✔
367
        _cleanup_free_ char *repart = NULL;
3✔
368
        fd = pin_callout_binary("systemd-repart", &repart);
3✔
369
        if (fd < 0)
3✔
370
                return log_error_errno(fd, "Failed to find systemd-repart binary: %m");
×
371

372
        r = sd_varlink_connect_exec(link, repart, /* argv= */ NULL);
3✔
373
        if (r < 0)
3✔
374
                return log_error_errno(r, "Failed to connect to systemd-repart: %m");
×
375

376
        return 1;
377
}
378

379
static int acquire_device_list(
×
380
                sd_varlink **link,
381
                char ***ret_menu,
382
                char ***ret_accepted) {
383
        int r;
×
384

385
        r = connect_to_repart(link);
×
386
        if (r < 0)
×
387
                return r;
×
388

389
        _cleanup_strv_free_ char **menu = NULL, **accepted = NULL;
×
390

391
        sd_json_variant *reply = NULL;
×
392
        const char *error_id = NULL;
×
393
        r = sd_varlink_collectbo(
×
394
                        *link,
395
                        "io.systemd.Repart.ListCandidateDevices",
396
                        &reply,
397
                        &error_id,
398
                        SD_JSON_BUILD_PAIR_BOOLEAN("ignoreRoot", true));
399
        if (r < 0)
×
400
                return log_error_errno(r, "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %m");
×
401
        if (streq_ptr(error_id, "io.systemd.Repart.NoCandidateDevices"))
×
402
                log_debug("No candidate devices found.");
×
403
        else if (error_id) {
×
404
                r = sd_varlink_error_to_errno(error_id, reply); /* If this is a system errno style error, output it with %m */
×
405
                if (r != -EBADR)
×
406
                        return log_error_errno(r, "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %m");
×
407

408
                return log_error_errno(r, "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %s", error_id);
×
409
        } else {
410
                sd_json_variant *i;
×
411
                JSON_VARIANT_ARRAY_FOREACH(i, reply) {
×
412
                        _cleanup_(block_device_done) BlockDevice bd = BLOCK_DEVICE_NULL;
×
413

414
                        static const sd_json_dispatch_field dispatch_table[] = {
×
415
                                { "node",     SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(BlockDevice, node),     SD_JSON_MANDATORY },
416
                                { "symlinks", SD_JSON_VARIANT_ARRAY,  sd_json_dispatch_strv,   offsetof(BlockDevice, symlinks), 0                 },
417
                                {}
418
                        };
419

420
                        r = sd_json_dispatch(i, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &bd);
×
421
                        if (r < 0)
×
422
                                return r;
423

424
                        if (strv_extend(&accepted, bd.node) < 0)
×
425
                                return log_oom();
×
426
                        if (strv_extend_strv(&accepted, bd.symlinks, /* filter_duplicates= */ true) < 0)
×
427
                                return log_oom();
×
428

429
                        /* Prefer the by-id and by-loop-ref because they typically contain the strings most
430
                         * directly understood by the user */
431
                        const char *n = strv_find_prefix(bd.symlinks, "/dev/disk/by-id/");
×
432
                        if (!n)
×
433
                                n = strv_find_prefix(bd.symlinks, "/dev/disk/by-loop-ref/");
×
434
                        if (!n)
×
435
                                n = bd.node;
×
436

437
                        if (strv_extend(&menu, n) < 0)
×
438
                                return log_oom();
×
439
                }
440
        }
441

442
        *ret_menu = TAKE_PTR(menu);
×
443
        *ret_accepted = TAKE_PTR(accepted);
×
444
        return 0;
×
445
}
446

447
static int device_is_valid(const char *node, void *userdata) {
×
448

449
        if (!path_is_valid(node) || !path_is_absolute(node)) {
×
450
                log_error("Not a valid absolute file system path, refusing: %s", node);
×
451
                return false;
×
452
        }
453

454
        struct stat st;
×
455
        if (stat(node, &st) < 0) {
×
456
                log_error_errno(errno, "Failed to check if '%s' is a valid block device node: %m", node);
×
457
                return false;
458
        }
459
        if (!S_ISBLK(st.st_mode)) {
×
460
                log_error("Path '%s' does not refer to a valid block device node, refusing.", node);
×
461
                return false;
462
        }
463

464
        return true;
465
}
466

467
static int refresh_devices(char ***ret_menu, char ***ret_accepted, void *userdata) {
×
468
        sd_varlink **repart_link = ASSERT_PTR(userdata);
×
469

470
        (void) acquire_device_list(repart_link, ret_menu, ret_accepted);
×
471
        return 0;
×
472
}
473

474
static int prompt_block_device(sd_varlink **repart_link, char **ret_node) {
×
475
        int r;
×
476

477
        putchar('\n');
×
478

479
        _cleanup_strv_free_ char **menu = NULL, **accepted = NULL;
×
480
        (void) acquire_device_list(repart_link, &menu, &accepted);
×
481

482
        r = prompt_loop("Please enter target disk device",
×
483
                        GLYPH_COMPUTER_DISK,
484
                        /* prefill= */ NULL,
485
                        menu,
486
                        accepted,
487
                        /* ellipsize_percentage= */ 20,
488
                        /* n_columns= */ 1,
489
                        /* column_width= */ 80,
490
                        device_is_valid,
491
                        refresh_devices,
492
                        /* userdata= */ repart_link,
493
                        PROMPT_SHOW_MENU|PROMPT_SHOW_MENU_NOW|PROMPT_MAY_SKIP|PROMPT_HIDE_SKIP_HINT|PROMPT_HIDE_MENU_HINT,
494
                        ret_node);
495
        if (r < 0)
×
496
                return r;
497
        if (r == 0)
×
498
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation cancelled.");
×
499

500
        return 0;
501
}
502

503
static int sysinstall_context_notify(
20✔
504
                SysInstallContext *context,
505
                ProgressPhase phase,
506
                const char *object,
507
                unsigned percent) {
508

509
        int r;
20✔
510

511
        assert(context);
20✔
512
        assert(phase >= 0);
20✔
513
        assert(phase < _PROGRESS_PHASE_MAX);
20✔
514

515
        log_notice("%s%s%s",
40✔
516
                   emoji_enabled() ? phase == PROGRESS_ENCRYPT_CREDENTIALS ?  glyph(GLYPH_LOCK_AND_KEY) : glyph(GLYPH_COMPUTER_DISK) : "",
517
                   emoji_enabled() ? " " : "",
518
                   progress_phase_log_to_string(phase));
519

520
        if (context->link) {
20✔
521
                r = sd_varlink_notifybo(
14✔
522
                                context->link,
523
                                JSON_BUILD_PAIR_ENUM("phase", progress_phase_to_string(phase)),
524
                                JSON_BUILD_PAIR_STRING_NON_EMPTY("object", object),
525
                                JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("progress", percent, UINT_MAX));
526
                if (r < 0)
14✔
527
                        log_debug_errno(r, "Failed to send varlink notify progress notification, ignoring: %m");
×
528

529
                r = sd_varlink_flush(context->link);
14✔
530
                if (r < 0)
14✔
531
                        log_debug_errno(r, "Failed to flush varlink notify progress notification, ignoring: %m");
×
532
        }
533

534
        return 0;
20✔
535
}
536

537
typedef struct RepartResult {
538
        int ret;
539
        SysInstallContext *context;
540
} RepartResult;
541

542
static int handle_repart_reply(
28✔
543
                sd_varlink *link,
544
                sd_json_variant *reply,
545
                const char *error_id,
546
                sd_varlink_reply_flags_t flags,
547
                void *userdata) {
548

549
        RepartResult *result = userdata;
28✔
550
        int r;
28✔
551

552
        assert(result);
28✔
553

554
        struct {
28✔
555
                uint64_t min_size;
556
                uint64_t current_size;
557
                uint64_t need_free;
558

559
                const char *phase;
560
                const char *object;
561
                unsigned progress;
562
        } p = {
28✔
563
                .min_size = UINT64_MAX,
564
                .current_size = UINT64_MAX,
565
                .need_free = UINT64_MAX,
566
                .progress = UINT_MAX,
567
        };
568

569
        static const sd_json_dispatch_field dispatch_table[] = {
28✔
570
                { "minimalSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       voffsetof(p, min_size),     0 },
571
                { "currentSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       voffsetof(p, current_size), 0 },
572
                { "needFreeBytes",    _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       voffsetof(p, need_free),    0 },
573
                { "phase",            _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, voffsetof(p, phase),        0 },
574
                { "object",           _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, voffsetof(p, object),       0 },
575
                { "progress",         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,         voffsetof(p, progress),     0 },
576
                {}
577
        };
578

579
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
28✔
580
        if (r < 0)
28✔
581
                return result->ret = r;
×
582

583
        if (error_id) {
28✔
584
                const char *sysinstall_error_id = NULL;
×
585

586
                if (streq(error_id, "io.systemd.Repart.InsufficientFreeSpace")) {
×
587
                        sysinstall_error_id = "io.systemd.SysInstall.InsufficientFreeSpace";
×
588
                        result->ret = log_error_errno(SYNTHETIC_ERRNO(ENOSPC), "Not enough free space on disk, cannot install.");
×
589
                } else if (streq(error_id, "io.systemd.Repart.DiskTooSmall")) {
×
590
                        sysinstall_error_id = "io.systemd.SysInstall.DiskTooSmall";
×
591

592
                        result->ret = log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Disk too small for installation, cannot install.");
×
593
                } else if (streq(error_id, "io.systemd.Repart.ConflictingDiskLabelPresent")) {
×
594
                        sysinstall_error_id = "io.systemd.SysInstall.ConflictingDiskLabelPresent";
×
595

596
                        result->ret = log_error_errno(
×
597
                                        SYNTHETIC_ERRNO(EHWPOISON),
598
                                        "A conflicting disk label is already present on the target disk, cannot install unless disk is erased.");
599
                }
600

601
                if (sysinstall_error_id && result->context->link) {
×
602
                        r = sd_varlink_errorbo(
×
603
                                        result->context->link,
604
                                        sysinstall_error_id,
605
                                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("currentSizeBytes", p.current_size, UINT64_MAX),
606
                                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("needFreeBytes", p.need_free, UINT64_MAX),
607
                                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("minimalSizeBytes", p.min_size, UINT64_MAX));
608

609
                        if (r < 0)
×
610
                                return result->ret = r;
×
611

612
                        return result->ret;
×
613
                }
614

615
                r = sd_varlink_error_to_errno(error_id, reply); /* If this is a system errno style error, output it with %m */
×
616
                if (r != -EBADR)
×
617
                        return result->ret = log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %m");
×
618

619
                return result->ret = log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %s", error_id);
×
620
        }
621

622
        if ((p.progress != UINT_MAX || p.object) && result->context->link)
28✔
623
                (void) sysinstall_context_notify(result->context, PROGRESS_INSTALL_PARTITIONS, p.object, p.progress);
8✔
624

625
        return result->ret = 0;
28✔
626
}
627

628
static int sysinstall_context_invoke_repart_run(SysInstallContext *context) {
2✔
629

630
        int r;
2✔
631

632
        assert(context);
2✔
633

634
        r = connect_to_repart(&context->repart_link);
2✔
635
        if (r < 0)
2✔
636
                return r;
2✔
637

638
        /* Seeding the partitions might be very slow, disable timeout */
639
        r = sd_varlink_set_relative_timeout(context->repart_link, UINT64_MAX);
2✔
640
        if (r < 0)
2✔
641
                return log_error_errno(r, "Failed to disable IPC timeout: %m");
×
642

643
        RepartResult result = {
2✔
644
                .context = context,
645
        };
646

647
        sd_varlink_set_userdata(context->repart_link, &result);
2✔
648

649
        r = sd_varlink_bind_reply(context->repart_link, handle_repart_reply);
2✔
650
        if (r < 0)
2✔
651
                return log_error_errno(r, "Failed to bind repart reply callback: %m");
×
652

653
        r = sd_varlink_observebo(
2✔
654
                        context->repart_link,
655
                        "io.systemd.Repart.Run",
656
                        SD_JSON_BUILD_PAIR_STRING("node", context->node),
657
                        SD_JSON_BUILD_PAIR_STRING("empty", context->erase ? "force" : "allow"),
658
                        SD_JSON_BUILD_PAIR_BOOLEAN("dryRun", false),
659
                        SD_JSON_BUILD_PAIR_CONDITION(!!context->definitions, "definitions", SD_JSON_BUILD_STRV(context->definitions)),
660
                        SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsEmpty", true),
661
                        SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsFactoryReset", true));
662
        if (r < 0)
2✔
663
                return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %m");
×
664

665
        for (;;) {
101✔
666
                r = sd_varlink_is_idle(context->repart_link);
101✔
667
                if (r < 0)
101✔
668
                        return log_error_errno(r, "Failed to check if varlink connection is idle: %m");
×
669
                if (r > 0)
101✔
670
                        break;
671

672
                r = sd_varlink_process(context->repart_link);
99✔
673
                if (r < 0)
99✔
674
                        return log_error_errno(r, "Failed to process varlink connection: %m");
×
675
                if (r != 0)
99✔
676
                        continue;
79✔
677

678
                r = sd_varlink_wait(context->repart_link, USEC_INFINITY);
20✔
679
                if (r < 0)
20✔
680
                        return log_error_errno(r, "Failed to wait for varlink connection events: %m");
×
681
        }
682

683
        sd_varlink_set_userdata(context->repart_link, NULL);
2✔
684

685
        r = sd_varlink_bind_reply(context->repart_link, NULL);
2✔
686
        if (r < 0)
2✔
687
                return log_error_errno(r, "Failed to unbind repart reply callback: %m");
×
688

689
        return result.ret;
2✔
690
}
691

692
static int read_repart_metrics(
1✔
693
                sd_json_variant *v,
694
                uint64_t *min_size,
695
                uint64_t *current_size,
696
                uint64_t *need_free,
697
                uint64_t *n_partitions) {
698

699
        int r;
1✔
700

701
        struct {
1✔
702
                uint64_t min_size;
703
                uint64_t current_size;
704
                uint64_t need_free;
705
                uint64_t n_partitions;
706
        } p = {
1✔
707
                .min_size = UINT64_MAX,
708
                .current_size = UINT64_MAX,
709
                .need_free = UINT64_MAX,
710
                .n_partitions = UINT64_MAX,
711
        };
712

713
        static const sd_json_dispatch_field dispatch_table[] = {
1✔
714
                { "minimalSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, min_size),     0 },
715
                { "currentSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, current_size), 0 },
716
                { "needFreeBytes",    _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, need_free),    0 },
717
                { "partitionCount",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, n_partitions), 0 },
718
                {}
719
        };
720

721
        r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
1✔
722
        if (r < 0)
1✔
723
                return r;
1✔
724

725
        if (min_size)
1✔
726
                *min_size = p.min_size;
1✔
727
        if (current_size)
1✔
728
                *current_size = p.current_size;
1✔
729
        if (need_free)
1✔
730
                *need_free = p.need_free;
1✔
731
        if (n_partitions)
1✔
732
                *n_partitions = p.n_partitions;
1✔
733

734
        return 0;
735
}
736

737
static int invoke_repart(
1✔
738
                sd_varlink **link,
739
                const char *node,
740
                bool erase,
741
                bool dry_run,
742
                char **definitions,
743
                uint64_t *min_size,        /* initialized both on success and error */
744
                uint64_t *current_size,    /* ditto */
745
                uint64_t *need_free,       /* ditto */
746
                uint64_t *n_partitions) {  /* ditto; UINT64_MAX if not determined */
747

748
        int r;
1✔
749

750
        assert(link);
1✔
751

752
        /* Note, if dry_run is true, then ENOSPC, E2BIG, EHWPOISON will not be logged about beyond LOG_DEBUG,
753
         * but all other errors will be */
754

755
        r = connect_to_repart(link);
1✔
756
        if (r < 0) {
1✔
757
                read_repart_metrics(/* v= */ NULL, min_size, current_size, need_free, n_partitions);
×
758
                return r;
×
759
        }
760

761
        if (!dry_run) {
1✔
762
                /* Seeding the partitions might be very slow, disable timeout */
UNCOV
763
                r = sd_varlink_set_relative_timeout(*link, UINT64_MAX);
×
UNCOV
764
                if (r < 0)
×
UNCOV
765
                        return log_error_errno(r, "Failed to disable IPC timeout: %m");
×
766
        }
767

768
        sd_json_variant *reply = NULL;
1✔
769
        const char *error_id = NULL;
1✔
770
        r = sd_varlink_callbo(
1✔
771
                        *link,
772
                        "io.systemd.Repart.Run",
773
                        &reply,
774
                        &error_id,
775
                        SD_JSON_BUILD_PAIR_STRING("node", node),
776
                        SD_JSON_BUILD_PAIR_STRING("empty", erase ? "force" : "allow"),
777
                        SD_JSON_BUILD_PAIR_BOOLEAN("dryRun", dry_run),
778
                        SD_JSON_BUILD_PAIR_CONDITION(!!definitions, "definitions", SD_JSON_BUILD_STRV(definitions)),
779
                        SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsEmpty", true),
780
                        SD_JSON_BUILD_PAIR_BOOLEAN("deferPartitionsFactoryReset", true));
781
        if (r < 0) {
1✔
UNCOV
782
                read_repart_metrics(/* v= */ NULL, min_size, current_size, need_free, n_partitions);
×
UNCOV
783
                return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %m");
×
784
        }
785
        if (error_id) {
1✔
786
                if (streq(error_id, "io.systemd.Repart.InsufficientFreeSpace")) {
×
787
                        (void) read_repart_metrics(reply, min_size, current_size, need_free, n_partitions);
×
788
                        return log_full_errno(
×
789
                                        dry_run ? LOG_DEBUG : LOG_ERR,
790
                                        SYNTHETIC_ERRNO(ENOSPC),
791
                                        "Not enough free space on disk, cannot install.");
792
                }
UNCOV
793
                if (streq(error_id, "io.systemd.Repart.DiskTooSmall")) {
×
UNCOV
794
                        (void) read_repart_metrics(reply, min_size, current_size, need_free, n_partitions);
×
795
                        return log_full_errno(
×
796
                                        dry_run ? LOG_DEBUG : LOG_ERR,
797
                                        SYNTHETIC_ERRNO(E2BIG),
798
                                        "Disk too small for installation, cannot install.");
799
                }
800

801
                /* For all other errors reset the metrics */
UNCOV
802
                read_repart_metrics(/* v= */ NULL, min_size, current_size, need_free, n_partitions);
×
803

804
                if (streq(error_id, "io.systemd.Repart.ConflictingDiskLabelPresent"))
×
805
                        return log_full_errno(
×
806
                                        dry_run ? LOG_DEBUG : LOG_ERR,
807
                                        SYNTHETIC_ERRNO(EHWPOISON),
808
                                        "A conflicting disk label is already present on the target disk, cannot install unless disk is erased.");
809

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

UNCOV
814
                return log_error_errno(r, "Failed to issue io.systemd.Repart.Run() varlink call: %s", error_id);
×
815
        }
816

817
        (void) read_repart_metrics(reply, min_size, current_size, need_free, n_partitions);
1✔
818

819
        return 0;
820
}
821

822
static int prompt_erase(
×
823
                bool can_add,
824
                int *ret_erase) {
UNCOV
825
        int r;
×
826

827
        assert(ret_erase);
×
828

UNCOV
829
        putchar('\n');
×
830

UNCOV
831
        char **l = can_add ? STRV_MAKE("keep", "erase") : STRV_MAKE("erase");
×
832

UNCOV
833
        _cleanup_free_ char *reply = NULL;
×
UNCOV
834
        r = prompt_loop(can_add ?
×
835
                        "Please type 'keep' to install the OS in addition to what the disk already contains, or 'erase' to erase all data on the disk" :
836
                        "Please type 'erase' to confirm that all data on the disk shall be erased",
837
                        GLYPH_BROOM,
838
                        /* prefill= */ NULL,
839
                        /* menu= */ l,
840
                        /* accepted= */ l,
841
                        /* ellipsize_percentage= */ 20,
842
                        /* n_columns= */ 2,
843
                        /* column_width= */ 40,
844
                        /* is_valid= */ NULL,
845
                        /* refresh= */ NULL,
846
                        /* userdata= */ NULL,
847
                        PROMPT_SHOW_MENU|PROMPT_MAY_SKIP|PROMPT_HIDE_MENU_HINT|PROMPT_HIDE_SKIP_HINT,
848
                        &reply);
849
        if (r < 0)
×
850
                return r;
UNCOV
851
        if (r == 0)
×
852
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation cancelled.");
×
853

UNCOV
854
        if (streq(reply, "erase"))
×
UNCOV
855
                *ret_erase = true;
×
UNCOV
856
        else if (streq(reply, "keep"))
×
UNCOV
857
                *ret_erase = false;
×
858
        else
UNCOV
859
                assert_not_reached();
×
860

861
        return 0;
862
}
863

864
static int prompt_touch_variables(void) {
1✔
865
        int r;
1✔
866

867
        if (arg_touch_variables >= 0)
1✔
868
                return 0;
1✔
869

UNCOV
870
        putchar('\n');
×
871

UNCOV
872
        char **l = STRV_MAKE("yes", "no");
×
873

UNCOV
874
        _cleanup_free_ char *reply = NULL;
×
UNCOV
875
        r = prompt_loop("Type 'yes' to register OS installation in firmware variables of the local system, 'no' otherwise",
×
876
                        GLYPH_ROCKET,
877
                        /* prefill= */ "yes",
878
                        /* menu= */ l,
879
                        /* accepted= */ l,
880
                        /* ellipsize_percentage= */ 20,
881
                        /* n_columns= */ 2,
882
                        /* column_width= */ 40,
883
                        /* is_valid= */ NULL,
884
                        /* refresh= */ NULL,
885
                        /* userdata= */ NULL,
886
                        PROMPT_SHOW_MENU|PROMPT_MAY_SKIP|PROMPT_HIDE_MENU_HINT|PROMPT_HIDE_SKIP_HINT,
887
                        &reply);
888
        if (r < 0)
×
889
                return r;
890
        if (r == 0)
×
UNCOV
891
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation cancelled.");
×
892

UNCOV
893
        r = parse_boolean(reply);
×
UNCOV
894
        if (r < 0)
×
UNCOV
895
                return log_error_errno(r, "Failed to parse reply: %s", reply);
×
896

UNCOV
897
        arg_touch_variables = r;
×
898

UNCOV
899
        return 0;
×
900
}
901

902
static int prompt_confirm(void) {
1✔
903
        int r;
1✔
904

905
        if (!arg_confirm)
1✔
906
                return 0;
1✔
907

908
        putchar('\n');
×
909

910
        bool yes;
×
911
        r = prompt_loop_yes_no(arg_summary ? "Please type 'yes' to confirm the choices above and begin the installation" :
×
912
                                             "Please type 'yes' to begin the installation",
913
                               /* prefill= */ NULL,
914
                               /* def= */ false,
915
                               &yes);
UNCOV
916
        if (r < 0)
×
917
                return r;
UNCOV
918
        if (!yes)
×
UNCOV
919
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation not confirmed, cancelling.");
×
920

921
        return 0;
922
}
923

924
static int validate_run(sd_varlink **repart_link, const char *node) {
1✔
925
        int r;
1✔
926

927
        assert(repart_link);
1✔
928
        assert(node);
1✔
929

930
        /* First loop: either with explicitly configured --erase= value, or false. A second loop only if not configured explicitly. */
931
        bool try_erase = arg_erase > 0, conflicting_disk_label = false;
1✔
932
        for (;;) {
1✔
933
                uint64_t min_size = UINT64_MAX, current_size = UINT64_MAX, need_free = UINT64_MAX,
1✔
934
                        n_partitions = UINT64_MAX;
1✔
935
                r = invoke_repart(
1✔
936
                                repart_link,
937
                                node,
938
                                try_erase,
939
                                /* dry_run= */ true,
940
                                arg_definitions,
941
                                &min_size,
942
                                &current_size,
943
                                &need_free,
944
                                &n_partitions);
945
                if (r == -ENOSPC) {
1✔
946
                        /* The disk is large enough, but there's not enough unallocated space. Hence proceed, but require erasing */
947
                        if (try_erase || arg_erase >= 0)
×
948
                                return log_error_errno(r, "The selected disk is big enough for the installation but does not have enough free space.");
×
949

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

954
                        try_erase = true;
955
                } else if (r == -E2BIG) {
1✔
956
                        /* Won't fit, whatever we do */
957
                        log_error_errno(r, "The selected disk is not large enough for an OS installation.");
×
958
                        if (current_size != UINT64_MAX)
×
959
                                log_info("The size of the selected disk is %s, but a minimal size of %s is required.",
×
960
                                         FORMAT_BYTES(current_size),
961
                                         FORMAT_BYTES(min_size));
962
                        return r;
963
                } else if (r == -EHWPOISON) {
1✔
UNCOV
964
                        if (try_erase || arg_erase >= 0)
×
UNCOV
965
                                return log_error_errno(r, "The selected disk contains a conflicting disk label, refusing.");
×
966

UNCOV
967
                        log_debug("Disk contains a conflicting disk label, checking if we could install the OS after erasing it.");
×
UNCOV
968
                        try_erase = true;
×
969
                        conflicting_disk_label = true;
×
UNCOV
970
                        continue;
×
971
                } else if (r < 0)
1✔
972
                        /* invoke_repart() already logged about all other errors */
973
                        return r;
974
                else
975
                        /* Nice, we can add the OS to the disk, without erasing anything. */
976
                        log_info("The selected disk has enough free space for an installation of the OS.");
1✔
977

978
                if (conflicting_disk_label)
1✔
UNCOV
979
                        log_warning("A conflicting disk label has been found, and must be erased for installation.");
×
980

981
                if (arg_erase < 0) {
1✔
982
                        if (n_partitions == 0) {
×
983
                                /* If the disk contains no partitions there's nothing worth preserving on
984
                                 * it, hence don't bother asking whether to erase it. */
UNCOV
985
                                log_info("The selected disk contains no partitions, proceeding.");
×
UNCOV
986
                                arg_erase = false;
×
987
                        } else {
988
                                r = prompt_erase(/* can_add= */ !try_erase, &arg_erase);
×
989
                                if (r < 0)
×
UNCOV
990
                                        return r;
×
991
                        }
992
                }
993

994
                return 0;
995
        }
996
}
997

UNCOV
998
static int sysinstall_context_show_summary(SysInstallContext *context) {
×
UNCOV
999
        int r;
×
1000

1001
        printf("\n"
×
1002
               "%sSummary:%s\n", ansi_underline(), ansi_normal());
1003

UNCOV
1004
        _cleanup_(table_unrefp) Table *table = table_new_vertical();
×
UNCOV
1005
        if (!table)
×
UNCOV
1006
                return log_oom();
×
1007

UNCOV
1008
        r = table_add_many(
×
1009
                        table,
1010
                        TABLE_FIELD, "Selected Disk",
1011
                        TABLE_STRING, context->node,
1012
                        TABLE_FIELD, "Erase Disk",
1013
                        TABLE_BOOLEAN, context->erase,
1014
                        TABLE_SET_COLOR, context->erase ? ansi_highlight_red() : NULL,
1015
                        TABLE_FIELD, "Register in Firmware",
1016
                        TABLE_BOOLEAN, context->touch_variables);
1017
        if (r < 0)
×
1018
                return table_log_add_error(r);
×
1019

1020
        static const char * const map[] = {
1021
                "firstboot.keymap",          "Keyboard Map",
1022
                "firstboot.locale",          "Locale",
1023
                "firstboot.locale-messages", "Locale (Messages)",
1024
                "firstboot.timezone",        "Timezone",
1025
                NULL
1026
        };
1027

1028
        STRV_FOREACH_PAIR(id, text, map) {
×
1029
                MachineCredential *c = machine_credential_find(&context->credentials, *id);
×
1030
                if (!c)
×
UNCOV
1031
                        continue;
×
1032

1033
                _cleanup_free_ char *escaped = cescape_length(c->data, c->size);
×
UNCOV
1034
                if (!escaped)
×
UNCOV
1035
                        return log_oom();
×
1036

UNCOV
1037
                r = table_add_many(
×
1038
                                table,
1039
                                TABLE_FIELD, *text,
1040
                                TABLE_STRING, escaped);
UNCOV
1041
                if (r < 0)
×
1042
                        return table_log_add_error(r);
×
1043
        }
1044

UNCOV
1045
        unsigned n_extra_credentials = 0;
×
UNCOV
1046
        FOREACH_ARRAY(cred, context->credentials.credentials, context->credentials.n_credentials) {
×
1047
                bool covered = false;
×
1048

UNCOV
1049
                STRV_FOREACH_PAIR(id, text, map)
×
UNCOV
1050
                        if (streq(*id, cred->id)) {
×
1051
                                covered = true;
1052
                                break;
1053
                        }
1054

UNCOV
1055
                if (!covered)
×
UNCOV
1056
                        n_extra_credentials++;
×
1057
        }
1058

UNCOV
1059
        if (n_extra_credentials > 0) {
×
UNCOV
1060
                r = table_add_many(
×
1061
                                table,
1062
                                TABLE_FIELD, "Extra Credentials",
1063
                                TABLE_UINT, n_extra_credentials);
1064
                if (r < 0)
×
1065
                        return table_log_add_error(r);
×
1066
        }
1067

1068
        r = table_print(table);
×
1069
        if (r < 0)
×
UNCOV
1070
                return r;
×
1071

1072
        return 0;
1073
}
1074

1075
static int find_current_kernel(
×
1076
                char **ret_filename,
1077
                int *ret_fd) {
1078

UNCOV
1079
        int r;
×
1080

1081
        sd_id128_t uuid;
×
1082
        r = efi_stub_get_device_part_uuid(&uuid);
×
1083
        if (r == -ENOENT)
×
1084
                return log_error_errno(r, "Cannot find current kernel, no stub partition UUID passed via EFI variables.");
×
UNCOV
1085
        if (r < 0)
×
UNCOV
1086
                return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
1087

UNCOV
1088
        _cleanup_free_ char *image = NULL;
×
UNCOV
1089
        r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &image);
×
UNCOV
1090
        if (r == -ENOENT)
×
UNCOV
1091
                return log_error_errno(r, "Cannot find current kernel, no stub EFI binary path passed.");
×
UNCOV
1092
        if (r < 0)
×
UNCOV
1093
                return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
1094

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

1098
        _cleanup_free_ char *partition_path = NULL;
×
1099
        _cleanup_close_ int partition_fd = -EBADF;
×
UNCOV
1100
        sd_id128_t partition_uuid;
×
1101
        r = find_esp_and_warn_full(
×
1102
                        /* root= */ NULL,
1103
                        /* path= */ NULL,
1104
                        /* unprivileged_mode= */ false,
1105
                        &partition_path,
1106
                        &partition_fd,
1107
                        /* ret_part= */ NULL,
1108
                        /* ret_pstart= */ NULL,
1109
                        /* ret_psize= */ NULL,
1110
                        &partition_uuid,
1111
                        /* ret_devid= */ NULL);
1112
        if (r < 0 && r != -ENOKEY)
×
1113
                return r;
UNCOV
1114
        if (r < 0 || !sd_id128_equal(uuid, partition_uuid)) {
×
UNCOV
1115
                partition_path = mfree(partition_path);
×
1116
                partition_fd = safe_close(partition_fd);
×
1117

UNCOV
1118
                r = find_xbootldr_and_warn_full(
×
1119
                                /* root= */ NULL,
1120
                                /* path= */ NULL,
1121
                                /* unprivileged_mode= */ false,
1122
                                &partition_path,
1123
                                &partition_fd,
1124
                                &partition_uuid,
1125
                                /* ret_devid= */ NULL);
UNCOV
1126
                if (r < 0 && r != -ENOKEY)
×
1127
                        return r;
1128

1129
                if (r < 0 || !sd_id128_equal(uuid, partition_uuid))
×
1130
                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Unable to find UKI on ESP/XBOOTLDR partitions.");
×
1131
        }
1132

1133
        _cleanup_free_ char *resolved = NULL;
×
1134
        _cleanup_close_ int fd = chase_and_openat(
×
1135
                        /* root_fd= */ partition_fd,
1136
                        /* dir_fd= */ partition_fd,
1137
                        image,
1138
                        CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_REGULAR,
1139
                        O_RDONLY|O_CLOEXEC,
1140
                        &resolved);
UNCOV
1141
        if (fd < 0)
×
UNCOV
1142
                return log_error_errno(fd, "Failed to find EFI binary '%s' on partition '%s': %m", image, partition_path);
×
1143

UNCOV
1144
        _cleanup_free_ char *fn = NULL;
×
UNCOV
1145
        r = path_extract_filename(resolved, &fn);
×
UNCOV
1146
        if (r < 0)
×
UNCOV
1147
                return log_error_errno(r, "Failed to extract UKI file name from '%s': %m", resolved);
×
1148

UNCOV
1149
        if (ret_filename)
×
UNCOV
1150
                *ret_filename = TAKE_PTR(fn);
×
UNCOV
1151
        if (ret_fd)
×
1152
                *ret_fd = TAKE_FD(fd);
×
1153

1154
        return 0;
1155
}
1156

1157
static int connect_to_bootctl(sd_varlink **link) {
4✔
1158
        int r;
4✔
1159

1160
        assert(link);
4✔
1161

1162
        if (*link)
4✔
1163
                return 0;
4✔
1164

1165
        _cleanup_close_ int fd = -EBADF;
4✔
1166
        _cleanup_free_ char *bootctl = NULL;
2✔
1167
        fd = pin_callout_binary("bootctl", &bootctl);
2✔
1168
        if (fd < 0)
2✔
UNCOV
1169
                return log_error_errno(fd, "Failed to find bootctl binary: %m");
×
1170

1171
        r = sd_varlink_connect_exec(link, bootctl, /* argv= */ NULL);
2✔
1172
        if (r < 0)
2✔
UNCOV
1173
                return log_error_errno(r, "Failed to connect to bootctl: %m");
×
1174

1175
        r = sd_varlink_set_allow_fd_passing_output(*link, true);
2✔
1176
        if (r < 0)
2✔
UNCOV
1177
                return log_error_errno(r, "Failed to enable fd passing to bootctl: %m");
×
1178

1179
        return 1;
1180
}
1181

1182
static int invoke_bootctl_install(
2✔
1183
                sd_varlink **link,
1184
                bool variables,
1185
                const char *root_dir,
1186
                int root_fd) {
1187
        int r;
2✔
1188

1189
        assert(link);
2✔
1190
        assert(root_dir);
2✔
1191
        assert(root_fd >= 0);
2✔
1192

1193
        r = connect_to_bootctl(link);
2✔
1194
        if (r < 0)
2✔
1195
                return r;
2✔
1196

1197
        int fd_idx = sd_varlink_push_dup_fd(*link, root_fd);
2✔
1198
        if (fd_idx < 0)
2✔
UNCOV
1199
                return log_error_errno(fd_idx, "Failed to submit root fd onto Varlink connection: %m");
×
1200

1201
        const char *error_id = NULL;
2✔
1202
        r = varlink_callbo_and_log(
2✔
1203
                        *link,
1204
                        "io.systemd.BootControl.Install",
1205
                        /* reply= */ NULL,
1206
                        &error_id,
1207
                        SD_JSON_BUILD_PAIR_STRING("operation", "new"),
1208
                        SD_JSON_BUILD_PAIR_INTEGER("rootFileDescriptor", fd_idx),
1209
                        SD_JSON_BUILD_PAIR_STRING("rootDirectory", root_dir),
1210
                        SD_JSON_BUILD_PAIR_BOOLEAN("touchVariables", variables));
1211
        if (r < 0)
2✔
UNCOV
1212
                return r;
×
1213

1214
        return 0;
1215
}
1216

1217
static int invoke_bootctl_link(
2✔
1218
                sd_varlink **link,
1219
                const char *root_dir,
1220
                int root_fd,
1221
                const char *kernel_filename,
1222
                int kernel_fd,
1223
                char **encrypted_credentials) {
1224
        int r;
2✔
1225

1226
        assert(link);
2✔
1227
        assert(root_dir);
2✔
1228
        assert(root_fd >= 0);
2✔
1229
        assert(kernel_filename);
2✔
1230
        assert(kernel_fd >= 0);
2✔
1231

1232
        r = connect_to_bootctl(link);
2✔
1233
        if (r < 0)
2✔
1234
                return r;
2✔
1235

1236
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
2✔
1237
        STRV_FOREACH_PAIR(name, value, encrypted_credentials) {
4✔
1238
                _cleanup_free_ char *j = strjoin(*name, ".cred");
4✔
1239
                if (!j)
2✔
UNCOV
1240
                        return log_oom();
×
1241

1242
                r = sd_json_variant_append_arraybo(
2✔
1243
                                &array,
1244
                                SD_JSON_BUILD_PAIR_STRING("filename", j),
1245
                                SD_JSON_BUILD_PAIR_BASE64("data", *value, strlen(*value)));
1246
                if (r < 0)
2✔
UNCOV
1247
                        return log_error_errno(r, "Failed to append credential to message: %m");
×
1248
        }
1249

1250
        int root_fd_idx = sd_varlink_push_dup_fd(*link, root_fd);
2✔
1251
        if (root_fd_idx < 0)
2✔
UNCOV
1252
                return log_error_errno(root_fd_idx, "Failed to submit root fd onto Varlink connection: %m");
×
1253

1254
        int kernel_fd_idx = sd_varlink_push_dup_fd(*link, kernel_fd);
2✔
1255
        if (kernel_fd_idx < 0)
2✔
UNCOV
1256
                return log_error_errno(kernel_fd_idx, "Failed to submit kernel fd onto Varlink connection: %m");
×
1257

1258
        const char *error_id = NULL;
2✔
1259
        r = varlink_callbo_and_log(
2✔
1260
                        *link,
1261
                        "io.systemd.BootControl.Link",
1262
                        /* reply= */ NULL,
1263
                        &error_id,
1264
                        SD_JSON_BUILD_PAIR_INTEGER("rootFileDescriptor", root_fd_idx),
1265
                        SD_JSON_BUILD_PAIR_STRING("rootDirectory", root_dir),
1266
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("kernelFilename", kernel_filename),
1267
                        SD_JSON_BUILD_PAIR_INTEGER("kernelFileDescriptor", kernel_fd_idx),
1268
                        SD_JSON_BUILD_PAIR_CONDITION(!!array, "extraFiles", SD_JSON_BUILD_VARIANT(array)));
1269
        if (r < 0)
2✔
1270
                return r;
×
1271

1272
        return 0;
1273
}
1274

1275
static int maybe_reboot(void) {
1✔
1276
        int r;
1✔
1277

1278
        if (!arg_reboot)
1✔
1279
                return 0;
1✔
1280

UNCOV
1281
        log_notice("%s%sSystem will reboot now.",
×
1282
                   emoji_enabled() ? glyph(GLYPH_CIRCLE_ARROW) : "", emoji_enabled() ? " " : "");
1283

UNCOV
1284
        if (!any_key_to_proceed())
×
1285
                return 0;
1286

UNCOV
1287
        log_notice("%s%sInitiating reboot.",
×
1288
                   emoji_enabled() ? glyph(GLYPH_CIRCLE_ARROW) : "", emoji_enabled() ? " " : "");
1289

1290
        _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
1✔
1291
        r = sd_varlink_connect_address(&link, "/run/systemd/io.systemd.Shutdown");
×
1292
        if (r < 0)
×
UNCOV
1293
                return log_error_errno(r, "Failed to connect to systemd-logind: %m");
×
1294

1295
        sd_json_variant *reply = NULL;
×
UNCOV
1296
        const char *error_id = NULL;
×
UNCOV
1297
        r = varlink_callbo_and_log(
×
1298
                        link,
1299
                        "io.systemd.Shutdown.Reboot",
1300
                        &reply,
1301
                        &error_id);
1302
        if (r < 0)
×
1303
                return r;
×
1304

1305
        return 0;
1306
}
1307

1308
static int read_credential_locale(MachineCredentialContext *credentials) {
×
1309
        int r;
×
1310

1311
        if (machine_credential_find(credentials, "firstboot.locale") ||
×
1312
            machine_credential_find(credentials, "firstboot.locale-messages"))
×
1313
                return 0;
1314

1315
        /* For the main locale we check the two env vars, and if neither is there, we use LC_NUMERIC, since
1316
         * it seems to be one of the most fundamental ones, and is not LC_MESSAGES for which we have a
1317
         * separate setting after all */
1318
        const char *l = getenv("LC_ALL") ?: getenv("LANG") ?: setlocale(LC_NUMERIC, NULL);
×
1319
        if (l) {
×
UNCOV
1320
                r = machine_credential_add(credentials, "firstboot.locale", l, /* size= */ SIZE_MAX);
×
1321
                if (r < 0)
×
1322
                        return log_oom();
×
1323
        }
1324

1325
        const char *m = setlocale(LC_MESSAGES, NULL);
×
UNCOV
1326
        if (m && !streq_ptr(m, l)) {
×
UNCOV
1327
                r = machine_credential_add(credentials, "firstboot.locale-messages", m, /* size= */ SIZE_MAX);
×
UNCOV
1328
                if (r < 0)
×
1329
                        return log_oom();
×
1330
        }
1331

1332
        return 0;
1333
}
1334

1335
static int read_credential_keymap(MachineCredentialContext *credentials) {
×
UNCOV
1336
        int r;
×
1337

UNCOV
1338
        if (machine_credential_find(credentials, "firstboot.keymap"))
×
UNCOV
1339
                return 0;
×
1340

1341
        _cleanup_free_ char *keymap = NULL;
×
1342
        r = parse_env_file(
×
1343
                        /* f= */ NULL,
1344
                        etc_vconsole_conf(),
1345
                        "KEYMAP", &keymap);
UNCOV
1346
        if (r < 0 && r != -ENOENT)
×
1347
                return log_error_errno(r, "Failed to parse '%s': %m", etc_vconsole_conf());
×
1348

1349
        if (!isempty(keymap)) {
×
1350
                r = machine_credential_add(credentials, "firstboot.keymap", keymap, /* size= */ SIZE_MAX);
×
UNCOV
1351
                if (r < 0)
×
1352
                        return log_oom();
×
1353
        }
1354

1355
        return 0;
1356
}
1357

UNCOV
1358
static int read_credential_timezone(MachineCredentialContext *credentials) {
×
UNCOV
1359
        int r;
×
1360

UNCOV
1361
        if (machine_credential_find(credentials, "firstboot.timezone"))
×
UNCOV
1362
                return 0;
×
1363

1364
        _cleanup_free_ char *tz = NULL;
×
1365
        r = get_timezone_prefer_env(&tz);
×
UNCOV
1366
        if (r < 0)
×
UNCOV
1367
                log_warning_errno(r, "Failed to read timezone, skipping timezone propagation: %m");
×
1368
        else {
UNCOV
1369
                r = machine_credential_add(credentials, "firstboot.timezone", tz, /* size= */ SIZE_MAX);
×
1370
                if (r < 0)
×
1371
                        return log_oom();
×
1372
        }
1373

1374
        return 0;
1375
}
1376

1377
static int sysinstall_context_read_credentials(SysInstallContext *context) {
2✔
1378
        int r;
2✔
1379

1380
        if (context->copy_locale) {
2✔
UNCOV
1381
                r = read_credential_locale(&context->credentials);
×
UNCOV
1382
                if (r < 0)
×
1383
                        return r;
1384
        }
1385

1386
        if (context->copy_keymap) {
2✔
UNCOV
1387
                r = read_credential_keymap(&context->credentials);
×
UNCOV
1388
                if (r < 0)
×
1389
                        return r;
1390
        }
1391

1392
        if (context->copy_timezone) {
2✔
UNCOV
1393
                r = read_credential_timezone(&context->credentials);
×
UNCOV
1394
                if (r < 0)
×
UNCOV
1395
                        return r;
×
1396
        }
1397

1398
        return 0;
1399
}
1400

1401
static int connect_to_creds(sd_varlink **link) {
2✔
1402
        int r;
2✔
1403

1404
        assert(link);
2✔
1405

1406
        if (*link)
2✔
1407
                return 0;
2✔
1408

1409
        _cleanup_close_ int fd = -EBADF;
2✔
1410
        _cleanup_free_ char *creds = NULL;
2✔
1411
        fd = pin_callout_binary("systemd-creds", &creds);
2✔
1412
        if (fd < 0)
2✔
UNCOV
1413
                return log_error_errno(fd, "Failed to find systemd-creds binary: %m");
×
1414

1415
        r = sd_varlink_connect_exec(link, creds, /* argv= */ NULL);
2✔
1416
        if (r < 0)
2✔
UNCOV
1417
                return log_error_errno(r, "Failed to connect to systemd-creds: %m");
×
1418

1419
        return 1;
1420
}
1421

1422
static int encrypt_one_credential(sd_varlink **link, const MachineCredential *input, char ***encrypted) {
2✔
1423
        int r;
2✔
1424

1425
        assert(link);
2✔
1426
        assert(input);
2✔
1427
        assert(encrypted);
2✔
1428

1429
        log_info("Encrypting credential '%s'...", input->id);
2✔
1430

1431
        r = connect_to_creds(link);
2✔
1432
        if (r < 0)
2✔
1433
                return r;
2✔
1434

1435
        sd_json_variant *reply = NULL;
2✔
1436
        const char *error_id = NULL;
2✔
1437
        r = varlink_callbo_and_log(
2✔
1438
                        *link,
1439
                        "io.systemd.Credentials.Encrypt",
1440
                        &reply,
1441
                        &error_id,
1442
                        SD_JSON_BUILD_PAIR_STRING("name", input->id),
1443
                        SD_JSON_BUILD_PAIR_BASE64("data", input->data, input->size),
1444
                        SD_JSON_BUILD_PAIR_STRING("scope", "system"),
1445
                        /* We pick the 'auto_initrd' key for this, since we want TPM if available, but are fine with NULL if not */
1446
                        SD_JSON_BUILD_PAIR_STRING("withKey", "auto_initrd"));
1447
        if (r < 0)
2✔
1448
                return r;
1449

1450
        static const sd_json_dispatch_field dispatch_table[] = {
2✔
1451
                { "blob", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
1452
                {}
1453
        };
1454

1455
        const char *blob = NULL;
2✔
1456
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &blob);
2✔
1457
        if (r < 0)
2✔
1458
                return r;
1459

1460
        r = strv_extend_many(encrypted, input->id, blob);
2✔
1461
        if (r < 0)
2✔
UNCOV
1462
                return r;
×
1463

1464
        return 0;
1465
}
1466

1467
static int encrypt_credentials(sd_varlink **link, MachineCredentialContext *credentials, char ***encrypted) {
2✔
1468
        int r;
2✔
1469

1470
        assert(link);
2✔
1471
        assert(encrypted);
2✔
1472

1473
        FOREACH_ARRAY(cred, credentials->credentials, credentials->n_credentials) {
4✔
1474
                r = encrypt_one_credential(link, cred, encrypted);
2✔
1475
                if (r < 0)
2✔
1476
                        return r;
1477
        }
1478

1479
        return 0;
1480
}
1481

1482
static const ImagePolicy image_policy = {
1483
        .n_policies = 4,
1484
        .policies = {
1485
                /* We mount / and /usr/ so that we can get access to /etc/machine-id and /etc/kernel/ */
1486
                { PARTITION_ROOT,     PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1487
                { PARTITION_USR,      PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1488
                { PARTITION_ESP,      PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1489
                { PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1490
        },
1491
        .default_flags = PARTITION_POLICY_IGNORE,
1492
};
1493

1494
static int settle_definitions(char **definitions, char ***ret_definitions) {
2✔
1495

1496
        _cleanup_strv_free_ char **d = NULL;
2✔
1497
        int r;
2✔
1498

1499
        assert(ret_definitions);
2✔
1500

1501
        if (definitions) {
2✔
1502
                d = strv_copy(definitions);
2✔
1503
                if (!d)
2✔
1504
                        return log_oom();
×
1505

1506
                *ret_definitions = TAKE_PTR(d);
2✔
1507

1508
                return 0;
2✔
1509
        }
1510

1511
        /* If /usr/lib/repart.sysinstall.d/ is populated, use it, otherwise use the regular definition
1512
         * files */
1513

UNCOV
1514
        _cleanup_strv_free_ char **files = NULL;
×
UNCOV
1515
        r = conf_files_list_strv(
×
1516
                        &files,
1517
                        ".conf",
1518
                        /* root= */ NULL,
1519
                        CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN|CONF_FILES_DONT_PREFIX_ROOT,
UNCOV
1520
                        (const char**) CONF_PATHS_STRV("repart.sysinstall.d"));
×
UNCOV
1521
        if (r < 0)
×
UNCOV
1522
                return log_error_errno(r, "Failed to enumerate *.conf files: %m");
×
1523

UNCOV
1524
        if (!strv_isempty(files)) {
×
UNCOV
1525
                d = strv_copy(CONF_PATHS_STRV("repart.sysinstall.d"));
×
UNCOV
1526
                if (!d)
×
UNCOV
1527
                        return log_oom();
×
1528

UNCOV
1529
                *ret_definitions = TAKE_PTR(d);
×
1530
        }
1531

1532
        return 0;
1533
}
1534

1535
static int sysinstall_context_settle_definitions(SysInstallContext *context,
2✔
1536
                                                 char **definitions) {
1537

1538
        return settle_definitions(definitions, &context->definitions);
2✔
1539
}
1540

1541
static int sysinstall_context_settle_kernel_image(SysInstallContext *context,
2✔
1542
                                                  const char *kernel_image) {
1543

1544
        _cleanup_free_ char *kernel_filename = NULL;
2✔
1545
        _cleanup_close_ int kernel_fd = -EBADF;
2✔
1546
        int r;
2✔
1547

1548
        assert(context->kernel_fd < 0);
2✔
1549
        assert(!context->kernel_filename);
2✔
1550

1551
        if (kernel_image) {
2✔
1552
                r = path_extract_filename(kernel_image, &kernel_filename);
2✔
1553
                if (r < 0)
2✔
UNCOV
1554
                        return log_error_errno(r, "Failed to extract filename from kernel path '%s': %m", kernel_image);
×
1555
                if (r == O_DIRECTORY)
2✔
UNCOV
1556
                        return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Kernel path '%s' refers to directory, must be regular file, refusing.", kernel_image);
×
1557

1558
                kernel_fd = xopenat_full(XAT_FDROOT, kernel_image, O_RDONLY|O_CLOEXEC, XO_REGULAR, MODE_INVALID);
2✔
1559
                if (kernel_fd < 0)
2✔
UNCOV
1560
                        return log_error_errno(kernel_fd, "Failed to open kernel image '%s': %m", kernel_image);
×
1561

1562
        } else {
UNCOV
1563
                r = find_current_kernel(&kernel_filename, &kernel_fd);
×
UNCOV
1564
                if (r < 0)
×
1565
                        return r;
1566
        }
1567

1568
        context->kernel_filename = TAKE_PTR(kernel_filename);
2✔
1569
        context->kernel_fd = TAKE_FD(kernel_fd);
2✔
1570

1571
        return 0;
2✔
1572
}
1573

1574
static int sysinstall_context_run(SysInstallContext *context) {
2✔
1575

1576
        int r;
2✔
1577

1578
        assert(context);
2✔
1579

1580
        (void) sysinstall_context_notify(context, PROGRESS_ENCRYPT_CREDENTIALS, NULL, UINT_MAX);
2✔
1581

1582
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *creds_link = NULL;
2✔
1583
        _cleanup_strv_free_ char **encrypted_credentials = NULL;
2✔
1584
        r = encrypt_credentials(&creds_link, &context->credentials, &encrypted_credentials);
2✔
1585
        if (r < 0)
2✔
1586
                return r;
1587

1588
        (void) sysinstall_context_notify(context, PROGRESS_INSTALL_PARTITIONS, NULL, UINT_MAX);
2✔
1589

1590
        /* Do the main part of the installation */
1591

1592
        r = sysinstall_context_invoke_repart_run(context);
2✔
1593
        if (r < 0)
2✔
1594
                return r;
1595

1596
        (void) sysinstall_context_notify(context, PROGRESS_MOUNT_PARTITIONS, NULL, UINT_MAX);
2✔
1597

1598
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2✔
UNCOV
1599
        _cleanup_(umount_and_freep) char *root_dir = NULL;
×
1600
        _cleanup_close_ int root_fd = -EBADF;
2✔
1601
        r = mount_image_privately_interactively(
4✔
1602
                        context->node,
2✔
1603
                        &image_policy,
1604
                        DISSECT_IMAGE_REQUIRE_ROOT |
1605
                        DISSECT_IMAGE_RELAX_VAR_CHECK |
1606
                        DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
1607
                        DISSECT_IMAGE_DISCARD_ANY |
1608
                        DISSECT_IMAGE_GPT_ONLY |
1609
                        DISSECT_IMAGE_FSCK |
1610
                        DISSECT_IMAGE_USR_NO_ROOT |
1611
                        DISSECT_IMAGE_ADD_PARTITION_DEVICES |
1612
                        DISSECT_IMAGE_PIN_PARTITION_DEVICES,
1613
                        &root_dir,
1614
                        &root_fd,
1615
                        &loop_device);
1616
        if (r < 0)
2✔
UNCOV
1617
                return log_error_errno(r, "Failed to mount new image: %m");
×
1618

1619
        (void) sysinstall_context_notify(context, PROGRESS_INSTALL_KERNEL, NULL, UINT_MAX);
2✔
1620

1621
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *bootctl_link = NULL;
2✔
1622
        r = invoke_bootctl_link(&bootctl_link, root_dir, root_fd, context->kernel_filename, context->kernel_fd, encrypted_credentials);
2✔
1623
        if (r < 0)
2✔
1624
                return r;
1625

1626
        (void) sysinstall_context_notify(context, PROGRESS_INSTALL_BOOTLOADER, NULL, UINT_MAX);
2✔
1627

1628
        r = invoke_bootctl_install(&bootctl_link, context->touch_variables, root_dir, root_fd);
2✔
1629
        if (r < 0)
2✔
1630
                return r;
1631

1632
        (void) sysinstall_context_notify(context, PROGRESS_UNMOUNT_PARTITIONS, NULL, UINT_MAX);
2✔
1633

1634
        root_fd = safe_close(root_fd);
2✔
1635
        r = umount_recursive(root_dir, /* flags= */ 0);
2✔
1636
        if (r < 0)
2✔
1637
                log_warning_errno(r, "Failed to unmount target disk, proceeding anyway: %m");
×
1638
        loop_device = loop_device_unref(loop_device);
2✔
1639
        sync();
2✔
1640

1641
        return 0;
2✔
1642
}
1643

1644
typedef struct ListCandidateDevicesContext {
1645
        char **definitions;
1646
        bool subscribe;
1647

1648
        sd_varlink *repart_link; /* A repart connection to get candidate devices */
1649
        sd_varlink *dry_run_repart_link; /* A second repart connection to perform a dry run on each node */
1650

1651
        sd_varlink *link;
1652
} ListCandidateDevicesContext;
1653

1654
static ListCandidateDevicesContext* list_candidate_devices_context_new(void) {
×
1655
        ListCandidateDevicesContext *context = new(ListCandidateDevicesContext, 1);
×
1656

UNCOV
1657
        if (!context)
×
1658
                return NULL;
1659

UNCOV
1660
        *context = (ListCandidateDevicesContext) {};
×
1661

UNCOV
1662
        return context;
×
1663
}
1664

UNCOV
1665
static ListCandidateDevicesContext* list_candidate_devices_context_free(ListCandidateDevicesContext *context) {
×
1666
        if (!context)
×
1667
                return NULL;
1668

1669
        strv_free(context->definitions);
×
1670

1671
        context->repart_link = sd_varlink_flush_close_unref(context->repart_link);
×
UNCOV
1672
        context->dry_run_repart_link = sd_varlink_flush_close_unref(context->dry_run_repart_link);
×
1673
        context->link = sd_varlink_unref(context->link);
×
1674

UNCOV
1675
        return mfree(context);
×
1676
}
1677

UNCOV
1678
DEFINE_TRIVIAL_CLEANUP_FUNC(ListCandidateDevicesContext*, list_candidate_devices_context_free);
×
1679

UNCOV
1680
static int list_candidate_devices_context_settle_definitions(ListCandidateDevicesContext *context,
×
1681
                                                             char **definitions) {
1682

UNCOV
1683
        return settle_definitions(definitions, &context->definitions);
×
1684
}
1685

UNCOV
1686
static void vl_on_disconnect(sd_varlink_server *server, sd_varlink *link, void *userdata) {
×
1687
        assert(server);
×
1688
        assert(link);
×
1689

1690
        list_candidate_devices_context_free(sd_varlink_set_userdata(link, NULL));
×
1691
}
×
1692

1693
typedef struct DevicesResponse {
1694
        const char *node;
1695
        char **symlinks;
1696
        uint64_t diskseq;
1697
        uint64_t size;
1698
        const char *model;
1699
        const char *vendor;
1700
        const char *subsystem;
1701
        const char *action;
1702
} DevicesResponse;
1703

1704
static void devices_response_done(DevicesResponse *p) {
×
1705
        assert(p);
×
1706

UNCOV
1707
        p->symlinks = strv_free(p->symlinks);
×
1708
}
×
1709

UNCOV
1710
static int fetch_candidate_devices_reply(
×
1711
                sd_varlink *repart_link,
1712
                sd_json_variant *reply,
1713
                const char *error_id,
1714
                sd_varlink_reply_flags_t flags,
1715
                void *userdata) {
1716

UNCOV
1717
        int r;
×
UNCOV
1718
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
×
UNCOV
1719
        ListCandidateDevicesContext *context = ASSERT_PTR(userdata);
×
1720

UNCOV
1721
        if (error_id) {
×
UNCOV
1722
                if (streq(error_id, "io.systemd.Repart.NoCandidateDevices"))
×
1723
                        return sd_varlink_error(context->link, "io.systemd.SysInstall.NoCandidateDevices", NULL);
×
1724

UNCOV
1725
                return sd_varlink_error(context->link, error_id, NULL);
×
1726
        }
1727

1728
        static const sd_json_dispatch_field dispatch_table[] = {
×
1729
                { "node",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, node),      0 },
1730
                { "symlinks",  SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,         offsetof(DevicesResponse, symlinks),  0 },
1731
                { "diskseq",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(DevicesResponse, diskseq),   0 },
1732
                { "sizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(DevicesResponse, size),      0 },
1733
                { "model",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, model),     0 },
1734
                { "vendor",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, vendor),    0 },
1735
                { "subsystem", SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, subsystem), 0 },
1736
                { "action",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, action),    0 },
1737
                {}
1738
        };
1739

UNCOV
1740
        _cleanup_(devices_response_done) DevicesResponse p = {
×
1741
                .diskseq = UINT64_MAX,
1742
                .size = UINT64_MAX,
1743
        };
UNCOV
1744
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
×
UNCOV
1745
        if (r < 0)
×
1746
                return r;
1747

1748
        if (context->subscribe) {
×
1749
                /* The action needs to be ready, remove or add else we don't support the action */
UNCOV
1750
                if (streq(p.action, "ready"))
×
UNCOV
1751
                        return sd_varlink_notifybo(context->link, SD_JSON_BUILD_PAIR("action", JSON_BUILD_CONST_STRING("ready")));
×
1752

UNCOV
1753
                if (streq(p.action, "remove"))
×
UNCOV
1754
                        return sd_varlink_notifybo(context->link,
×
1755
                                        SD_JSON_BUILD_PAIR("action", JSON_BUILD_CONST_STRING("remove")),
1756
                                        SD_JSON_BUILD_PAIR_STRING("node", p.node));
1757

1758
                if (!streq(p.action, "add")) {
×
1759
                        log_debug("Skip unsupported action '%s' while fetching candidate devices.", p.action);
×
1760
                        return 0;
1761
                }
1762
        }
1763

1764
        uint64_t min_size = UINT64_MAX, current_size = UINT64_MAX, need_free = UINT64_MAX;
×
UNCOV
1765
        r = invoke_repart(
×
1766
                        &context->dry_run_repart_link,
1767
                        p.node,
1768
                        /* erase= */ false,
1769
                        /* dry_run= */ true,
1770
                        context->definitions,
1771
                        &min_size,
1772
                        &current_size,
1773
                        &need_free,
1774
                        /* n_partitions= */ NULL);
1775

UNCOV
1776
        DeviceFit fit;
×
UNCOV
1777
        if (r < 0) {
×
UNCOV
1778
                if (r == -ENOSPC)
×
1779
                        fit = DEVICE_FIT_INSUFFICIENT_FREE_SPACE;
UNCOV
1780
                else if (r == -E2BIG)
×
1781
                        fit = DEVICE_FIT_DISK_TOO_SMALL;
UNCOV
1782
                else if (r == -EHWPOISON)
×
1783
                        fit = DEVICE_FIT_CONFLICTING_DISK_LABEL_PRESENT;
1784
                else
1785
                        return r;
1786
        } else
1787
                fit = DEVICE_FIT_ENOUGH_FREE_SPACE;
1788

UNCOV
1789
        r = sd_json_buildo(&v,
×
1790
                        SD_JSON_BUILD_PAIR_STRING("node", p.node),
1791
                        JSON_BUILD_PAIR_STRV_NON_EMPTY("symlinks", p.symlinks),
1792
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("diskseq", p.diskseq, UINT64_MAX),
1793
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("sizeBytes", p.size, UINT64_MAX),
1794
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("model", p.model),
1795
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("vendor", p.vendor),
1796
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("subsystem", p.subsystem),
1797
                        JSON_BUILD_PAIR_ENUM("fit", device_fit_to_string(fit)),
1798
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("currentSizeBytes", current_size, UINT64_MAX),
1799
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("needFreeBytes", need_free, UINT64_MAX),
1800
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("minimalSizeBytes", min_size, UINT64_MAX),
1801
                        SD_JSON_BUILD_PAIR_CONDITION(context->subscribe, "action", JSON_BUILD_CONST_STRING("add")));
1802
        if (r < 0)
×
1803
                return r;
1804

UNCOV
1805
        if (FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES))
×
UNCOV
1806
                return sd_varlink_notify(context->link, v);
×
1807

UNCOV
1808
        return sd_varlink_reply(context->link, v);
×
1809
}
1810

1811
typedef struct ListCandidateDevicesParameters {
1812
        char **definitions;
1813
        bool subscribe;
1814
} ListCandidateDevicesParameters;
1815

UNCOV
1816
static void list_candidate_devices_parameters_done(ListCandidateDevicesParameters *p) {
×
1817
        assert(p);
×
1818

1819
        p->definitions = strv_free(p->definitions);
×
UNCOV
1820
}
×
1821

UNCOV
1822
static int vl_method_list_candidate_devices(
×
1823
                sd_varlink *link,
1824
                sd_json_variant *parameters,
1825
                sd_varlink_method_flags_t flags,
1826
                void *userdata) {
UNCOV
1827
        int r;
×
1828

UNCOV
1829
        assert(link);
×
1830

UNCOV
1831
        sd_varlink_server *varlink_server = sd_varlink_get_server(link);
×
UNCOV
1832
        sd_event *event = sd_varlink_server_get_event(varlink_server);
×
UNCOV
1833
        Hashmap **polkit_registry = ASSERT_PTR(sd_varlink_server_get_userdata(varlink_server));
×
1834

1835
        assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE));
×
1836

UNCOV
1837
        r = varlink_verify_polkit_async(
×
1838
                        link,
1839
                        /* bus= */ NULL,
1840
                        "io.systemd.sysinstall.ListCandidateDevices",
1841
                        /* details= */ NULL,
1842
                        polkit_registry);
1843
        if (r <= 0)
×
1844
                return r;
×
1845

UNCOV
1846
        static const sd_json_dispatch_field dispatch_table[] = {
×
1847
                { "definitions", SD_JSON_VARIANT_ARRAY,   json_dispatch_strv_path,  offsetof(ListCandidateDevicesParameters, definitions), SD_JSON_STRICT },
1848
                { "subscribe",   SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(ListCandidateDevicesParameters, subscribe),   0              },
1849
                {}
1850
        };
1851

UNCOV
1852
        _cleanup_(list_candidate_devices_parameters_done) ListCandidateDevicesParameters p = {};
×
UNCOV
1853
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
×
1854
        if (r != 0)
×
1855
                return r;
1856

UNCOV
1857
        _cleanup_(list_candidate_devices_context_freep) ListCandidateDevicesContext* context = list_candidate_devices_context_new();
×
UNCOV
1858
        if (!context)
×
UNCOV
1859
                return log_oom();
×
1860

1861
        context->subscribe = p.subscribe;
×
UNCOV
1862
        r = list_candidate_devices_context_settle_definitions(context, p.definitions);
×
UNCOV
1863
        if (r < 0)
×
1864
                return r;
1865

UNCOV
1866
        context->link = sd_varlink_ref(link);
×
1867

UNCOV
1868
        r = connect_to_repart(&context->repart_link);
×
UNCOV
1869
        if (r < 0)
×
1870
                return r;
1871

UNCOV
1872
        r = sd_varlink_attach_event(context->repart_link, event, SD_EVENT_PRIORITY_NORMAL);
×
UNCOV
1873
        if (r < 0)
×
UNCOV
1874
                return log_error_errno(
×
1875
                                r,
1876
                                "Failed to attach io.systemd.Repart.ListCandidateDevices() varlink connection to event loop: %m");
1877

UNCOV
1878
        r = sd_varlink_bind_reply(context->repart_link, fetch_candidate_devices_reply);
×
UNCOV
1879
        if (r < 0)
×
1880
                return r;
1881

1882
        r = sd_varlink_observebo(
×
1883
                        context->repart_link,
1884
                        "io.systemd.Repart.ListCandidateDevices",
1885
                        SD_JSON_BUILD_PAIR_BOOLEAN("subscribe", context->subscribe),
1886
                        SD_JSON_BUILD_PAIR_BOOLEAN("ignoreRoot", true));
1887

UNCOV
1888
        if (r < 0)
×
UNCOV
1889
                return log_error_errno(
×
1890
                                r,
1891
                                "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %m");
1892

UNCOV
1893
        r = sd_varlink_server_bind_disconnect(varlink_server, vl_on_disconnect);
×
UNCOV
1894
        if (r < 0)
×
1895
                return r;
1896

1897
        /* Disable connection timeout so that the connection to repart doesn't close before the link is
1898
         * disconnected */
UNCOV
1899
        r = sd_varlink_set_relative_timeout(context->repart_link, UINT64_MAX);
×
UNCOV
1900
        if (r < 0)
×
1901
                return r;
1902

1903
        /* The context is freed in vl_on_disconnect() */
UNCOV
1904
        sd_varlink_set_userdata(context->repart_link, context);
×
UNCOV
1905
        sd_varlink_set_userdata(link, TAKE_PTR(context));
×
1906

1907
        return 0;
1908
}
1909

1910
typedef struct RunParameters {
1911
        char *node;
1912
        char **definitions;
1913
        bool erase;
1914
        bool variables;
1915
        char *kernel_image;
1916
        bool copy_locale;
1917
        bool copy_keymap;
1918
        bool copy_timezone;
1919
        sd_json_variant *credentials;
1920
} RunParameters;
1921

1922
static void run_parameters_done(RunParameters *p) {
1✔
1923
        assert(p);
1✔
1924

1925
        p->node = mfree(p->node);
1✔
1926
        p->definitions = strv_free(p->definitions);
1✔
1927
        p->kernel_image = mfree(p->kernel_image);
1✔
1928
        p->credentials = sd_json_variant_unref(p->credentials);
1✔
1929
}
1✔
1930

1931
typedef struct CredentialParameters {
1932
        const char *id;
1933
        struct iovec value;
1934
} CredentialParameters;
1935

1936
static void credential_parameters_done(CredentialParameters *p) {
1✔
1937
        assert(p);
1✔
1938

1939
        iovec_done_erase(&p->value);
1✔
1940
}
1✔
1941

1942
static int credentials_from_json_array(MachineCredentialContext *credentials, sd_json_variant *v) {
1✔
1943

1944
        int r;
1✔
1945
        sd_json_variant *credential;
1✔
1946

1947
        assert(credentials);
1✔
1948

1949
        static const sd_json_dispatch_field dispatch_table[] = {
1✔
1950
                { "id",    SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CredentialParameters, id),    SD_JSON_MANDATORY },
1951
                { "value", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec,  offsetof(CredentialParameters, value), SD_JSON_MANDATORY },
1952
                {}
1953
        };
1954

1955
        JSON_VARIANT_ARRAY_FOREACH(credential, v) {
2✔
1956
                _cleanup_(credential_parameters_done) CredentialParameters p = {};
1✔
1957

1958
                r = sd_json_dispatch(credential, dispatch_table, /* flags= */ 0, &p);
1✔
1959
                if (r < 0)
1✔
1960
                        return r;
1961

1962
                r = machine_credential_add(credentials, p.id, p.value.iov_base, p.value.iov_len);
1✔
1963
                if (r < 0)
1✔
1964
                        return r;
1965
        }
1966

1967
        return 0;
1✔
1968
}
1969

1970
static int vl_method_run(
1✔
1971
                sd_varlink *link,
1972
                sd_json_variant *parameters,
1973
                sd_varlink_method_flags_t flags,
1974
                void *userdata) {
1975

1976
        static const sd_json_dispatch_field dispatch_table[] = {
1✔
1977
                { "node",            SD_JSON_VARIANT_STRING,  json_dispatch_path,       offsetof(RunParameters, node),          SD_JSON_MANDATORY | SD_JSON_STRICT },
1978
                { "definitions",     SD_JSON_VARIANT_ARRAY,   json_dispatch_strv_path,  offsetof(RunParameters, definitions),   SD_JSON_NULLABLE | SD_JSON_STRICT  },
1979
                { "erase",           SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, erase),         SD_JSON_MANDATORY                  },
1980
                { "variables",       SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, variables),     SD_JSON_NULLABLE                   },
1981
                { "kernelImagePath", SD_JSON_VARIANT_STRING,  json_dispatch_path,       offsetof(RunParameters, kernel_image),  SD_JSON_NULLABLE | SD_JSON_STRICT  },
1982
                { "copyLocale",      SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, copy_locale),   SD_JSON_NULLABLE                   },
1983
                { "copyKeymap",      SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, copy_keymap),   SD_JSON_NULLABLE                   },
1984
                { "copyTimezone",    SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, copy_timezone), SD_JSON_NULLABLE                   },
1985
                { "credentials",     SD_JSON_VARIANT_ARRAY,   sd_json_dispatch_variant, offsetof(RunParameters, credentials),   SD_JSON_NULLABLE                   },
1986
                {}
1987
        };
1988

1989
        int r;
1✔
1990

1991
        assert(link);
1✔
1992

1993
        sd_varlink_server *varlink_server = sd_varlink_get_server(link);
1✔
1994
        Hashmap **polkit_registry = ASSERT_PTR(sd_varlink_server_get_userdata(varlink_server));
1✔
1995

1996
        r = varlink_verify_polkit_async(
1✔
1997
                        link,
1998
                        /* bus= */ NULL,
1999
                        "io.systemd.sysinstall.Run",
2000
                        /* details= */ NULL,
2001
                        polkit_registry);
2002
        if (r <= 0)
1✔
2003
                return r;
1✔
2004

2005
        _cleanup_(run_parameters_done) RunParameters p = {};
1✔
2006
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
1✔
2007
        if (r != 0)
1✔
2008
                return r;
2009

2010
        _cleanup_(sysinstall_context_done) SysInstallContext context = (SysInstallContext) {
1✔
2011
                .copy_locale = p.copy_locale,
1✔
2012
                .copy_keymap = p.copy_keymap,
1✔
2013
                .copy_timezone = p.copy_timezone,
1✔
2014
                .erase = p.erase,
1✔
2015
                .touch_variables = p.variables,
1✔
2016
                .node = TAKE_PTR(p.node),
1✔
2017
                .kernel_fd = -EBADF,
2018
        };
2019

2020
        r = sysinstall_context_settle_definitions(&context, p.definitions);
1✔
2021
        if (r < 0)
1✔
2022
                return r;
2023

2024
        if (FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
1✔
2025
                context.link = sd_varlink_ref(link);
1✔
2026

2027
        r = credentials_from_json_array(&context.credentials, p.credentials);
1✔
2028
        if (r < 0)
1✔
2029
                return r;
2030

2031
        r = sysinstall_context_read_credentials(&context);
1✔
2032
        if (r < 0)
1✔
2033
                return r;
2034

2035
        r = sysinstall_context_settle_kernel_image(&context, p.kernel_image);
1✔
2036
        if (r < 0)
1✔
2037
                return r;
2038

2039
        r = sysinstall_context_run(&context);
1✔
2040
        if (r < 0)
1✔
2041
                return r;
2042

2043
        return sd_varlink_reply(link, NULL);
1✔
2044
}
2045

2046
static int vl_server(void) {
13✔
2047
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
13✔
2048
        _cleanup_hashmap_free_ Hashmap *polkit_registry = NULL;
13✔
2049
        int r;
13✔
2050

2051
        /* Invocation as Varlink service */
2052

2053
        r = varlink_server_new(
13✔
2054
                        &varlink_server,
2055
                        0,
2056
                        /* userdata= */ &polkit_registry);
2057
        if (r < 0)
13✔
UNCOV
2058
                return log_error_errno(r, "Failed to allocate Varlink server: %m");
×
2059

2060
        r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_SysInstall);
13✔
2061
        if (r < 0)
13✔
UNCOV
2062
                return log_error_errno(r, "Failed to add Varlink interface: %m");
×
2063

2064
        r = sd_varlink_server_bind_method_many(
13✔
2065
                        varlink_server,
2066
                        "io.systemd.SysInstall.ListCandidateDevices", vl_method_list_candidate_devices,
2067
                        "io.systemd.SysInstall.Run",                  vl_method_run);
2068
        if (r < 0)
13✔
UNCOV
2069
                return log_error_errno(r, "Failed to bind Varlink methods: %m");
×
2070

2071
        r = sd_varlink_server_loop_auto(varlink_server);
13✔
2072
        if (r < 0)
13✔
UNCOV
2073
                return log_error_errno(r, "Failed to run Varlink event loop: %m");
×
2074

2075
        return 0;
2076
}
2077

2078
static void end_marker(void) {
1✔
2079

2080
        if (!arg_welcome)
1✔
2081
                return;
2082

UNCOV
2083
        printf("\n%sExiting first boot settings tool.%s\n\n", ansi_grey(), ansi_normal());
×
UNCOV
2084
        fflush(stdout);
×
2085
}
2086

2087
static int run(int argc, char *argv[]) {
14✔
2088
        int r;
14✔
2089

2090
        LIBBLKID_NOTE(recommended);
14✔
2091
        LIBCRYPTO_NOTE(suggested);
14✔
2092
        LIBCRYPTSETUP_NOTE(suggested);
14✔
2093
        LIBMOUNT_NOTE(recommended);
14✔
2094

2095
        setlocale(LC_ALL, "");
14✔
2096

2097
        r = parse_argv(argc, argv);
14✔
2098
        if (r <= 0)
14✔
2099
                return r;
14✔
2100

2101
        log_setup();
14✔
2102

2103
        if (arg_varlink)
14✔
2104
                return vl_server();
13✔
2105

UNCOV
2106
        _cleanup_(sysinstall_context_done) SysInstallContext context = (SysInstallContext) {
×
2107
                .copy_locale = arg_copy_locale,
2108
                .copy_keymap = arg_copy_keymap,
2109
                .copy_timezone = arg_copy_timezone,
2110
                .credentials = TAKE_STRUCT(arg_credentials),
1✔
2111
                .kernel_fd = -EBADF,
2112
        };
2113

2114
        r = sysinstall_context_settle_definitions(&context, arg_definitions);
1✔
2115
        if (r < 0)
1✔
2116
                return r;
2117

2118
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
1✔
2119
        if (arg_welcome) {
1✔
UNCOV
2120
                if (arg_mute_console)
×
UNCOV
2121
                        (void) mute_console(&mute_console_link);
×
2122

UNCOV
2123
                (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
×
2124

2125
                if (arg_chrome)
×
2126
                        chrome_show("Operating System Installer", /* bottom= */ NULL);
×
2127
        }
2128

2129
        DEFER_VOID_CALL(end_marker);
1✔
UNCOV
2130
        DEFER_VOID_CALL(chrome_hide);
×
2131

2132
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *repart_link = NULL;
1✔
2133
        if (arg_node) {
1✔
2134
                r = print_welcome(&mute_console_link);
1✔
2135
                if (r < 0)
1✔
2136
                        return r;
2137

2138
                r = validate_run(&repart_link, arg_node);
1✔
2139
                if (r < 0)
1✔
2140
                        return r;
2141
        } else {
2142
                /* Determine the minimum disk size */
UNCOV
2143
                uint64_t min_size = UINT64_MAX;
×
2144
                r = invoke_repart(
×
2145
                                &repart_link,
2146
                                /* node= */ NULL,
2147
                                /* erase= */ true,
2148
                                /* dry_run= */ true,
2149
                                arg_definitions,
2150
                                &min_size,
2151
                                /* current_size= */ NULL,
2152
                                /* need_free= */ NULL,
2153
                                /* n_partitions= */ NULL);
UNCOV
2154
                if (r < 0)
×
UNCOV
2155
                        return r;
×
2156

2157
                r = print_welcome(&mute_console_link);
×
UNCOV
2158
                if (r < 0)
×
2159
                        return r;
2160

UNCOV
2161
                log_info("Required minimal installation disk size is %s.", FORMAT_BYTES(min_size));
×
2162

UNCOV
2163
                for (;;) {
×
UNCOV
2164
                        _cleanup_free_ char *node = NULL;
×
UNCOV
2165
                        r = prompt_block_device(&repart_link, &node);
×
UNCOV
2166
                        if (r < 0)
×
2167
                                return r;
2168

UNCOV
2169
                        r = validate_run(&repart_link, node);
×
UNCOV
2170
                        if (IN_SET(r, -ENOSPC, -E2BIG, -EHWPOISON)) /* Device is no fit, pick other */
×
UNCOV
2171
                                continue;
×
UNCOV
2172
                        if (r < 0)
×
2173
                                return r;
2174

UNCOV
2175
                        arg_node = TAKE_PTR(node);
×
UNCOV
2176
                        break;
×
2177
                }
2178
        }
2179

2180
        r = prompt_touch_variables();
1✔
2181
        if (r < 0)
1✔
2182
                return r;
2183

2184
        r = sysinstall_context_read_credentials(&context);
1✔
2185
        if (r < 0)
1✔
2186
                return r;
2187

2188
        r = sysinstall_context_settle_kernel_image(&context, arg_kernel_image);
1✔
2189
        if (r < 0)
1✔
2190
                return r;
2191

2192
        /* Verify we have everything we need */
2193
        assert(arg_node);
1✔
2194
        assert(arg_erase >= 0);
1✔
2195
        assert(arg_touch_variables >= 0);
1✔
2196

2197
        context.node = TAKE_PTR(arg_node);
1✔
2198
        context.touch_variables = arg_touch_variables;
1✔
2199
        context.erase = arg_erase;
1✔
2200

2201
        if (arg_summary) {
1✔
UNCOV
2202
                r = sysinstall_context_show_summary(&context);
×
2203
                if (r < 0)
×
2204
                        return r;
2205
        }
2206

2207
        r = prompt_confirm();
1✔
2208
        if (r < 0)
1✔
2209
                return r;
2210

2211
        putchar('\n');
1✔
2212

2213
        r = sysinstall_context_run(&context);
1✔
2214
        if (r < 0)
1✔
2215
                return r;
2216

2217
        log_notice("%s%sInstallation succeeded.",
2✔
2218
                   emoji_enabled() ? glyph(GLYPH_SPARKLES) : "", emoji_enabled() ? " " : "");
2219

2220
        r = maybe_reboot();
1✔
2221
        if (r < 0)
1✔
UNCOV
2222
                return r;
×
2223

2224
        return 0;
2225
}
2226

2227
DEFINE_MAIN_FUNCTION(run);
14✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc