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

systemd / systemd / 29132483780

10 Jul 2026 08:43PM UTC coverage: 72.912% (+0.2%) from 72.702%
29132483780

push

github

bluca
man: run forgotten 'update-man-rules'

344600 of 472622 relevant lines covered (72.91%)

1365091.83 hits per line

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

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

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

7
#include "sd-varlink.h"
8

9
#include "alloc-util.h"
10
#include "ansi-color.h"
11
#include "blockdev-list.h"
12
#include "build.h"
13
#include "build-path.h"
14
#include "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 (;;) {
99✔
666
                r = sd_varlink_is_idle(context->repart_link);
99✔
667
                if (r < 0)
99✔
668
                        return log_error_errno(r, "Failed to check if varlink connection is idle: %m");
×
669
                if (r > 0)
99✔
670
                        break;
671

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

678
                r = sd_varlink_wait(context->repart_link, USEC_INFINITY);
19✔
679
                if (r < 0)
19✔
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_space_metrics(
1✔
693
                sd_json_variant *v,
694
                uint64_t *min_size,
695
                uint64_t *current_size,
696
                uint64_t *need_free) {
697

698
        int r;
1✔
699

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

710
        static const sd_json_dispatch_field dispatch_table[] = {
1✔
711
                { "minimalSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, min_size),     0 },
712
                { "currentSizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, current_size), 0 },
713
                { "needFreeBytes",    _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, need_free),    0 },
714
                {}
715
        };
716

717
        r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
1✔
718
        if (r < 0)
1✔
719
                return r;
1✔
720

721
        if (min_size)
1✔
722
                *min_size = p.min_size;
1✔
723
        if (current_size)
1✔
724
                *current_size = p.current_size;
1✔
725
        if (need_free)
1✔
726
                *need_free = p.need_free;
1✔
727

728
        return 0;
729
}
730

731
static int invoke_repart(
1✔
732
                sd_varlink **link,
733
                const char *node,
734
                bool erase,
735
                bool dry_run,
736
                char **definitions,
737
                uint64_t *min_size,        /* initialized both on success and error */
738
                uint64_t *current_size,    /* ditto */
739
                uint64_t *need_free) {     /* ditto */
740

741
        int r;
1✔
742

743
        assert(link);
1✔
744

745
        /* Note, if dry_run is true, then ENOSPC, E2BIG, EHWPOISON will not be logged about beyond LOG_DEBUG,
746
         * but all other errors will be */
747

748
        r = connect_to_repart(link);
1✔
749
        if (r < 0) {
1✔
750
                read_space_metrics(/* v= */ NULL, min_size, current_size, need_free);
×
751
                return r;
×
752
        }
753

754
        if (!dry_run) {
1✔
755
                /* Seeding the partitions might be very slow, disable timeout */
756
                r = sd_varlink_set_relative_timeout(*link, UINT64_MAX);
×
757
                if (r < 0)
×
758
                        return log_error_errno(r, "Failed to disable IPC timeout: %m");
×
759
        }
760

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

794
                /* For all other errors reset the metrics */
795
                read_space_metrics(/* v= */ NULL, min_size, current_size, need_free);
×
796

797
                if (streq(error_id, "io.systemd.Repart.ConflictingDiskLabelPresent"))
×
798
                        return log_full_errno(
×
799
                                        dry_run ? LOG_DEBUG : LOG_ERR,
800
                                        SYNTHETIC_ERRNO(EHWPOISON),
801
                                        "A conflicting disk label is already present on the target disk, cannot install unless disk is erased.");
802

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

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

810
        (void) read_space_metrics(reply, min_size, current_size, need_free);
1✔
811

812
        return 0;
813
}
814

815
static int prompt_erase(
×
816
                bool can_add,
817
                int *ret_erase) {
818
        int r;
×
819

820
        assert(ret_erase);
×
821

822
        putchar('\n');
×
823

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

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

847
        if (streq(reply, "erase"))
×
848
                *ret_erase = true;
×
849
        else if (streq(reply, "keep"))
×
850
                *ret_erase = false;
×
851
        else
852
                assert_not_reached();
×
853

854
        return 0;
855
}
856

857
static int prompt_touch_variables(void) {
1✔
858
        int r;
1✔
859

860
        if (arg_touch_variables >= 0)
1✔
861
                return 0;
1✔
862

863
        putchar('\n');
×
864

865
        char **l = STRV_MAKE("yes", "no");
×
866

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

886
        r = parse_boolean(reply);
×
887
        if (r < 0)
×
888
                return log_error_errno(r, "Failed to parse reply: %s", reply);
×
889

890
        arg_touch_variables = r;
×
891

892
        return 0;
×
893
}
894

895
static int prompt_confirm(void) {
1✔
896
        int r;
1✔
897

898
        if (!arg_confirm)
1✔
899
                return 0;
1✔
900

901
        putchar('\n');
×
902

903
        bool yes;
×
904
        r = prompt_loop_yes_no(arg_summary ? "Please type 'yes' to confirm the choices above and begin the installation" :
×
905
                                             "Please type 'yes' to begin the installation",
906
                               /* def= */ false,
907
                               &yes);
908
        if (r < 0)
×
909
                return r;
910
        if (!yes)
×
911
                return log_error_errno(SYNTHETIC_ERRNO(ECANCELED), "Installation not confirmed, cancelling.");
×
912

913
        return 0;
914
}
915

916
static int validate_run(sd_varlink **repart_link, const char *node) {
1✔
917
        int r;
1✔
918

919
        assert(repart_link);
1✔
920
        assert(node);
1✔
921

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

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

944
                        try_erase = true;
945
                } else if (r == -E2BIG) {
1✔
946
                        /* Won't fit, whatever we do */
947
                        log_error_errno(r, "The selected disk is not large enough for an OS installation.");
×
948
                        if (current_size != UINT64_MAX)
×
949
                                log_info("The size of the selected disk is %s, but a minimal size of %s is required.",
×
950
                                         FORMAT_BYTES(current_size),
951
                                         FORMAT_BYTES(min_size));
952
                        return r;
953
                } else if (r == -EHWPOISON) {
1✔
954
                        if (try_erase || arg_erase >= 0)
×
955
                                return log_error_errno(r, "The selected disk contains a conflicting disk label, refusing.");
×
956

957
                        log_debug("Disk contains a conflicting disk label, checking if we could install the OS after erasing it.");
×
958
                        try_erase = true;
×
959
                        conflicting_disk_label = true;
×
960
                        continue;
×
961
                } else if (r < 0)
1✔
962
                        /* invoke_repart() already logged about all other errors */
963
                        return r;
964
                else
965
                        /* Nice, we can add the OS to the disk, without erasing anything. */
966
                        log_info("The selected disk has enough free space for an installation of the OS.");
1✔
967

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

971
                if (arg_erase < 0) {
1✔
972
                        r = prompt_erase(/* can_add= */ !try_erase, &arg_erase);
×
973
                        if (r < 0)
×
974
                                return r;
×
975
                }
976

977
                return 0;
978
        }
979
}
980

981
static int sysinstall_context_show_summary(SysInstallContext *context) {
×
982
        int r;
×
983

984
        printf("\n"
×
985
               "%sSummary:%s\n", ansi_underline(), ansi_normal());
986

987
        _cleanup_(table_unrefp) Table *table = table_new_vertical();
×
988
        if (!table)
×
989
                return log_oom();
×
990

991
        r = table_add_many(
×
992
                        table,
993
                        TABLE_FIELD, "Selected Disk",
994
                        TABLE_STRING, context->node,
995
                        TABLE_FIELD, "Erase Disk",
996
                        TABLE_BOOLEAN, context->erase,
997
                        TABLE_SET_COLOR, context->erase ? ansi_highlight_red() : NULL,
998
                        TABLE_FIELD, "Register in Firmware",
999
                        TABLE_BOOLEAN, context->touch_variables);
1000
        if (r < 0)
×
1001
                return table_log_add_error(r);
×
1002

1003
        static const char * const map[] = {
1004
                "firstboot.keymap",          "Keyboard Map",
1005
                "firstboot.locale",          "Locale",
1006
                "firstboot.locale-messages", "Locale (Messages)",
1007
                "firstboot.timezone",        "Timezone",
1008
                NULL
1009
        };
1010

1011
        STRV_FOREACH_PAIR(id, text, map) {
×
1012
                MachineCredential *c = machine_credential_find(&context->credentials, *id);
×
1013
                if (!c)
×
1014
                        continue;
×
1015

1016
                _cleanup_free_ char *escaped = cescape_length(c->data, c->size);
×
1017
                if (!escaped)
×
1018
                        return log_oom();
×
1019

1020
                r = table_add_many(
×
1021
                                table,
1022
                                TABLE_FIELD, *text,
1023
                                TABLE_STRING, escaped);
1024
                if (r < 0)
×
1025
                        return table_log_add_error(r);
×
1026
        }
1027

1028
        unsigned n_extra_credentials = 0;
×
1029
        FOREACH_ARRAY(cred, context->credentials.credentials, context->credentials.n_credentials) {
×
1030
                bool covered = false;
×
1031

1032
                STRV_FOREACH_PAIR(id, text, map)
×
1033
                        if (streq(*id, cred->id)) {
×
1034
                                covered = true;
1035
                                break;
1036
                        }
1037

1038
                if (!covered)
×
1039
                        n_extra_credentials++;
×
1040
        }
1041

1042
        if (n_extra_credentials > 0) {
×
1043
                r = table_add_many(
×
1044
                                table,
1045
                                TABLE_FIELD, "Extra Credentials",
1046
                                TABLE_UINT, n_extra_credentials);
1047
                if (r < 0)
×
1048
                        return table_log_add_error(r);
×
1049
        }
1050

1051
        r = table_print(table);
×
1052
        if (r < 0)
×
1053
                return r;
×
1054

1055
        return 0;
1056
}
1057

1058
static int find_current_kernel(
×
1059
                char **ret_filename,
1060
                int *ret_fd) {
1061

1062
        int r;
×
1063

1064
        sd_id128_t uuid;
×
1065
        r = efi_stub_get_device_part_uuid(&uuid);
×
1066
        if (r == -ENOENT)
×
1067
                return log_error_errno(r, "Cannot find current kernel, no stub partition UUID passed via EFI variables.");
×
1068
        if (r < 0)
×
1069
                return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
1070

1071
        _cleanup_free_ char *image = NULL;
×
1072
        r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &image);
×
1073
        if (r == -ENOENT)
×
1074
                return log_error_errno(r, "Cannot find current kernel, no stub EFI binary path passed.");
×
1075
        if (r < 0)
×
1076
                return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
1077

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

1081
        _cleanup_free_ char *partition_path = NULL;
×
1082
        _cleanup_close_ int partition_fd = -EBADF;
×
1083
        sd_id128_t partition_uuid;
×
1084
        r = find_esp_and_warn_full(
×
1085
                        /* root= */ NULL,
1086
                        /* path= */ NULL,
1087
                        /* unprivileged_mode= */ false,
1088
                        &partition_path,
1089
                        &partition_fd,
1090
                        /* ret_part= */ NULL,
1091
                        /* ret_pstart= */ NULL,
1092
                        /* ret_psize= */ NULL,
1093
                        &partition_uuid,
1094
                        /* ret_devid= */ NULL);
1095
        if (r < 0 && r != -ENOKEY)
×
1096
                return r;
1097
        if (r < 0 || !sd_id128_equal(uuid, partition_uuid)) {
×
1098
                partition_path = mfree(partition_path);
×
1099
                partition_fd = safe_close(partition_fd);
×
1100

1101
                r = find_xbootldr_and_warn_full(
×
1102
                                /* root= */ NULL,
1103
                                /* path= */ NULL,
1104
                                /* unprivileged_mode= */ false,
1105
                                &partition_path,
1106
                                &partition_fd,
1107
                                &partition_uuid,
1108
                                /* ret_devid= */ NULL);
1109
                if (r < 0 && r != -ENOKEY)
×
1110
                        return r;
1111

1112
                if (r < 0 || !sd_id128_equal(uuid, partition_uuid))
×
1113
                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Unable to find UKI on ESP/XBOOTLDR partitions.");
×
1114
        }
1115

1116
        _cleanup_free_ char *resolved = NULL;
×
1117
        _cleanup_close_ int fd = chase_and_openat(
×
1118
                        /* root_fd= */ partition_fd,
1119
                        /* dir_fd= */ partition_fd,
1120
                        image,
1121
                        CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_REGULAR,
1122
                        O_RDONLY|O_CLOEXEC,
1123
                        &resolved);
1124
        if (fd < 0)
×
1125
                return log_error_errno(fd, "Failed to find EFI binary '%s' on partition '%s': %m", image, partition_path);
×
1126

1127
        _cleanup_free_ char *fn = NULL;
×
1128
        r = path_extract_filename(resolved, &fn);
×
1129
        if (r < 0)
×
1130
                return log_error_errno(r, "Failed to extract UKI file name from '%s': %m", resolved);
×
1131

1132
        if (ret_filename)
×
1133
                *ret_filename = TAKE_PTR(fn);
×
1134
        if (ret_fd)
×
1135
                *ret_fd = TAKE_FD(fd);
×
1136

1137
        return 0;
1138
}
1139

1140
static int connect_to_bootctl(sd_varlink **link) {
4✔
1141
        int r;
4✔
1142

1143
        assert(link);
4✔
1144

1145
        if (*link)
4✔
1146
                return 0;
4✔
1147

1148
        _cleanup_close_ int fd = -EBADF;
4✔
1149
        _cleanup_free_ char *bootctl = NULL;
2✔
1150
        fd = pin_callout_binary("bootctl", &bootctl);
2✔
1151
        if (fd < 0)
2✔
1152
                return log_error_errno(fd, "Failed to find bootctl binary: %m");
×
1153

1154
        r = sd_varlink_connect_exec(link, bootctl, /* argv= */ NULL);
2✔
1155
        if (r < 0)
2✔
1156
                return log_error_errno(r, "Failed to connect to bootctl: %m");
×
1157

1158
        r = sd_varlink_set_allow_fd_passing_output(*link, true);
2✔
1159
        if (r < 0)
2✔
1160
                return log_error_errno(r, "Failed to enable fd passing to bootctl: %m");
×
1161

1162
        return 1;
1163
}
1164

1165
static int invoke_bootctl_install(
2✔
1166
                sd_varlink **link,
1167
                bool variables,
1168
                const char *root_dir,
1169
                int root_fd) {
1170
        int r;
2✔
1171

1172
        assert(link);
2✔
1173
        assert(root_dir);
2✔
1174
        assert(root_fd >= 0);
2✔
1175

1176
        r = connect_to_bootctl(link);
2✔
1177
        if (r < 0)
2✔
1178
                return r;
2✔
1179

1180
        int fd_idx = sd_varlink_push_dup_fd(*link, root_fd);
2✔
1181
        if (fd_idx < 0)
2✔
1182
                return log_error_errno(fd_idx, "Failed to submit root fd onto Varlink connection: %m");
×
1183

1184
        const char *error_id = NULL;
2✔
1185
        r = varlink_callbo_and_log(
2✔
1186
                        *link,
1187
                        "io.systemd.BootControl.Install",
1188
                        /* reply= */ NULL,
1189
                        &error_id,
1190
                        SD_JSON_BUILD_PAIR_STRING("operation", "new"),
1191
                        SD_JSON_BUILD_PAIR_INTEGER("rootFileDescriptor", fd_idx),
1192
                        SD_JSON_BUILD_PAIR_STRING("rootDirectory", root_dir),
1193
                        SD_JSON_BUILD_PAIR_BOOLEAN("touchVariables", variables));
1194
        if (r < 0)
2✔
1195
                return r;
×
1196

1197
        return 0;
1198
}
1199

1200
static int invoke_bootctl_link(
2✔
1201
                sd_varlink **link,
1202
                const char *root_dir,
1203
                int root_fd,
1204
                const char *kernel_filename,
1205
                int kernel_fd,
1206
                char **encrypted_credentials) {
1207
        int r;
2✔
1208

1209
        assert(link);
2✔
1210
        assert(root_dir);
2✔
1211
        assert(root_fd >= 0);
2✔
1212
        assert(kernel_filename);
2✔
1213
        assert(kernel_fd >= 0);
2✔
1214

1215
        r = connect_to_bootctl(link);
2✔
1216
        if (r < 0)
2✔
1217
                return r;
2✔
1218

1219
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
2✔
1220
        STRV_FOREACH_PAIR(name, value, encrypted_credentials) {
4✔
1221
                _cleanup_free_ char *j = strjoin(*name, ".cred");
4✔
1222
                if (!j)
2✔
1223
                        return log_oom();
×
1224

1225
                r = sd_json_variant_append_arraybo(
2✔
1226
                                &array,
1227
                                SD_JSON_BUILD_PAIR_STRING("filename", j),
1228
                                SD_JSON_BUILD_PAIR_BASE64("data", *value, strlen(*value)));
1229
                if (r < 0)
2✔
1230
                        return log_error_errno(r, "Failed to append credential to message: %m");
×
1231
        }
1232

1233
        int root_fd_idx = sd_varlink_push_dup_fd(*link, root_fd);
2✔
1234
        if (root_fd_idx < 0)
2✔
1235
                return log_error_errno(root_fd_idx, "Failed to submit root fd onto Varlink connection: %m");
×
1236

1237
        int kernel_fd_idx = sd_varlink_push_dup_fd(*link, kernel_fd);
2✔
1238
        if (kernel_fd_idx < 0)
2✔
1239
                return log_error_errno(kernel_fd_idx, "Failed to submit kernel fd onto Varlink connection: %m");
×
1240

1241
        const char *error_id = NULL;
2✔
1242
        r = varlink_callbo_and_log(
2✔
1243
                        *link,
1244
                        "io.systemd.BootControl.Link",
1245
                        /* reply= */ NULL,
1246
                        &error_id,
1247
                        SD_JSON_BUILD_PAIR_INTEGER("rootFileDescriptor", root_fd_idx),
1248
                        SD_JSON_BUILD_PAIR_STRING("rootDirectory", root_dir),
1249
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("kernelFilename", kernel_filename),
1250
                        SD_JSON_BUILD_PAIR_INTEGER("kernelFileDescriptor", kernel_fd_idx),
1251
                        SD_JSON_BUILD_PAIR_CONDITION(!!array, "extraFiles", SD_JSON_BUILD_VARIANT(array)));
1252
        if (r < 0)
2✔
1253
                return r;
×
1254

1255
        return 0;
1256
}
1257

1258
static int maybe_reboot(void) {
1✔
1259
        int r;
1✔
1260

1261
        if (!arg_reboot)
1✔
1262
                return 0;
1✔
1263

1264
        log_notice("%s%sSystem will reboot now.",
×
1265
                   emoji_enabled() ? glyph(GLYPH_CIRCLE_ARROW) : "", emoji_enabled() ? " " : "");
1266

1267
        if (!any_key_to_proceed())
×
1268
                return 0;
1269

1270
        log_notice("%s%sInitiating reboot.",
×
1271
                   emoji_enabled() ? glyph(GLYPH_CIRCLE_ARROW) : "", emoji_enabled() ? " " : "");
1272

1273
        _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
1✔
1274
        r = sd_varlink_connect_address(&link, "/run/systemd/io.systemd.Shutdown");
×
1275
        if (r < 0)
×
1276
                return log_error_errno(r, "Failed to connect to systemd-logind: %m");
×
1277

1278
        sd_json_variant *reply = NULL;
×
1279
        const char *error_id = NULL;
×
1280
        r = varlink_callbo_and_log(
×
1281
                        link,
1282
                        "io.systemd.Shutdown.Reboot",
1283
                        &reply,
1284
                        &error_id);
1285
        if (r < 0)
×
1286
                return r;
×
1287

1288
        return 0;
1289
}
1290

1291
static int read_credential_locale(MachineCredentialContext *credentials) {
×
1292
        int r;
×
1293

1294
        if (machine_credential_find(credentials, "firstboot.locale") ||
×
1295
            machine_credential_find(credentials, "firstboot.locale-messages"))
×
1296
                return 0;
1297

1298
        /* For the main locale we check the two env vars, and if neither is there, we use LC_NUMERIC, since
1299
         * it seems to be one of the most fundamental ones, and is not LC_MESSAGES for which we have a
1300
         * separate setting after all */
1301
        const char *l = getenv("LC_ALL") ?: getenv("LANG") ?: setlocale(LC_NUMERIC, NULL);
×
1302
        if (l) {
×
1303
                r = machine_credential_add(credentials, "firstboot.locale", l, /* size= */ SIZE_MAX);
×
1304
                if (r < 0)
×
1305
                        return log_oom();
×
1306
        }
1307

1308
        const char *m = setlocale(LC_MESSAGES, NULL);
×
1309
        if (m && !streq_ptr(m, l)) {
×
1310
                r = machine_credential_add(credentials, "firstboot.locale-messages", m, /* size= */ SIZE_MAX);
×
1311
                if (r < 0)
×
1312
                        return log_oom();
×
1313
        }
1314

1315
        return 0;
1316
}
1317

1318
static int read_credential_keymap(MachineCredentialContext *credentials) {
×
1319
        int r;
×
1320

1321
        if (machine_credential_find(credentials, "firstboot.keymap"))
×
1322
                return 0;
×
1323

1324
        _cleanup_free_ char *keymap = NULL;
×
1325
        r = parse_env_file(
×
1326
                        /* f= */ NULL,
1327
                        etc_vconsole_conf(),
1328
                        "KEYMAP", &keymap);
1329
        if (r < 0 && r != -ENOENT)
×
1330
                return log_error_errno(r, "Failed to parse '%s': %m", etc_vconsole_conf());
×
1331

1332
        if (!isempty(keymap)) {
×
1333
                r = machine_credential_add(credentials, "firstboot.keymap", keymap, /* size= */ SIZE_MAX);
×
1334
                if (r < 0)
×
1335
                        return log_oom();
×
1336
        }
1337

1338
        return 0;
1339
}
1340

1341
static int read_credential_timezone(MachineCredentialContext *credentials) {
×
1342
        int r;
×
1343

1344
        if (machine_credential_find(credentials, "firstboot.timezone"))
×
1345
                return 0;
×
1346

1347
        _cleanup_free_ char *tz = NULL;
×
1348
        r = get_timezone_prefer_env(&tz);
×
1349
        if (r < 0)
×
1350
                log_warning_errno(r, "Failed to read timezone, skipping timezone propagation: %m");
×
1351
        else {
1352
                r = machine_credential_add(credentials, "firstboot.timezone", tz, /* size= */ SIZE_MAX);
×
1353
                if (r < 0)
×
1354
                        return log_oom();
×
1355
        }
1356

1357
        return 0;
1358
}
1359

1360
static int sysinstall_context_read_credentials(SysInstallContext *context) {
2✔
1361
        int r;
2✔
1362

1363
        if (context->copy_locale) {
2✔
1364
                r = read_credential_locale(&context->credentials);
×
1365
                if (r < 0)
×
1366
                        return r;
1367
        }
1368

1369
        if (context->copy_keymap) {
2✔
1370
                r = read_credential_keymap(&context->credentials);
×
1371
                if (r < 0)
×
1372
                        return r;
1373
        }
1374

1375
        if (context->copy_timezone) {
2✔
1376
                r = read_credential_timezone(&context->credentials);
×
1377
                if (r < 0)
×
1378
                        return r;
×
1379
        }
1380

1381
        return 0;
1382
}
1383

1384
static int connect_to_creds(sd_varlink **link) {
2✔
1385
        int r;
2✔
1386

1387
        assert(link);
2✔
1388

1389
        if (*link)
2✔
1390
                return 0;
2✔
1391

1392
        _cleanup_close_ int fd = -EBADF;
2✔
1393
        _cleanup_free_ char *creds = NULL;
2✔
1394
        fd = pin_callout_binary("systemd-creds", &creds);
2✔
1395
        if (fd < 0)
2✔
1396
                return log_error_errno(fd, "Failed to find systemd-creds binary: %m");
×
1397

1398
        r = sd_varlink_connect_exec(link, creds, /* argv= */ NULL);
2✔
1399
        if (r < 0)
2✔
1400
                return log_error_errno(r, "Failed to connect to systemd-creds: %m");
×
1401

1402
        return 1;
1403
}
1404

1405
static int encrypt_one_credential(sd_varlink **link, const MachineCredential *input, char ***encrypted) {
2✔
1406
        int r;
2✔
1407

1408
        assert(link);
2✔
1409
        assert(input);
2✔
1410
        assert(encrypted);
2✔
1411

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

1414
        r = connect_to_creds(link);
2✔
1415
        if (r < 0)
2✔
1416
                return r;
2✔
1417

1418
        sd_json_variant *reply = NULL;
2✔
1419
        const char *error_id = NULL;
2✔
1420
        r = varlink_callbo_and_log(
2✔
1421
                        *link,
1422
                        "io.systemd.Credentials.Encrypt",
1423
                        &reply,
1424
                        &error_id,
1425
                        SD_JSON_BUILD_PAIR_STRING("name", input->id),
1426
                        SD_JSON_BUILD_PAIR_BASE64("data", input->data, input->size),
1427
                        SD_JSON_BUILD_PAIR_STRING("scope", "system"),
1428
                        /* We pick the 'auto_initrd' key for this, since we want TPM if available, but are fine with NULL if not */
1429
                        SD_JSON_BUILD_PAIR_STRING("withKey", "auto_initrd"));
1430
        if (r < 0)
2✔
1431
                return r;
1432

1433
        static const sd_json_dispatch_field dispatch_table[] = {
2✔
1434
                { "blob", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
1435
                {}
1436
        };
1437

1438
        const char *blob = NULL;
2✔
1439
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &blob);
2✔
1440
        if (r < 0)
2✔
1441
                return r;
1442

1443
        r = strv_extend_many(encrypted, input->id, blob);
2✔
1444
        if (r < 0)
2✔
1445
                return r;
×
1446

1447
        return 0;
1448
}
1449

1450
static int encrypt_credentials(sd_varlink **link, MachineCredentialContext *credentials, char ***encrypted) {
2✔
1451
        int r;
2✔
1452

1453
        assert(link);
2✔
1454
        assert(encrypted);
2✔
1455

1456
        FOREACH_ARRAY(cred, credentials->credentials, credentials->n_credentials) {
4✔
1457
                r = encrypt_one_credential(link, cred, encrypted);
2✔
1458
                if (r < 0)
2✔
1459
                        return r;
1460
        }
1461

1462
        return 0;
1463
}
1464

1465
static const ImagePolicy image_policy = {
1466
        .n_policies = 4,
1467
        .policies = {
1468
                /* We mount / and /usr/ so that we can get access to /etc/machine-id and /etc/kernel/ */
1469
                { PARTITION_ROOT,     PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1470
                { PARTITION_USR,      PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1471
                { PARTITION_ESP,      PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1472
                { PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
1473
        },
1474
        .default_flags = PARTITION_POLICY_IGNORE,
1475
};
1476

1477
static int settle_definitions(char **definitions, char ***ret_definitions) {
2✔
1478

1479
        _cleanup_strv_free_ char **d = NULL;
2✔
1480
        int r;
2✔
1481

1482
        assert(ret_definitions);
2✔
1483

1484
        if (definitions) {
2✔
1485
                d = strv_copy(definitions);
2✔
1486
                if (!d)
2✔
1487
                        return log_oom();
×
1488

1489
                *ret_definitions = TAKE_PTR(d);
2✔
1490

1491
                return 0;
2✔
1492
        }
1493

1494
        /* If /usr/lib/repart.sysinstall.d/ is populated, use it, otherwise use the regular definition
1495
         * files */
1496

1497
        _cleanup_strv_free_ char **files = NULL;
×
1498
        r = conf_files_list_strv(
×
1499
                        &files,
1500
                        ".conf",
1501
                        /* root= */ NULL,
1502
                        CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN|CONF_FILES_DONT_PREFIX_ROOT,
1503
                        (const char**) CONF_PATHS_STRV("repart.sysinstall.d"));
×
1504
        if (r < 0)
×
1505
                return log_error_errno(r, "Failed to enumerate *.conf files: %m");
×
1506

1507
        if (!strv_isempty(files)) {
×
1508
                d = strv_copy(CONF_PATHS_STRV("repart.sysinstall.d"));
×
1509
                if (!d)
×
1510
                        return log_oom();
×
1511

1512
                *ret_definitions = TAKE_PTR(d);
×
1513
        }
1514

1515
        return 0;
1516
}
1517

1518
static int sysinstall_context_settle_definitions(SysInstallContext *context,
2✔
1519
                                                 char **definitions) {
1520

1521
        return settle_definitions(definitions, &context->definitions);
2✔
1522
}
1523

1524
static int sysinstall_context_settle_kernel_image(SysInstallContext *context,
2✔
1525
                                                  const char *kernel_image) {
1526

1527
        _cleanup_free_ char *kernel_filename = NULL;
2✔
1528
        _cleanup_close_ int kernel_fd = -EBADF;
2✔
1529
        int r;
2✔
1530

1531
        assert(context->kernel_fd < 0);
2✔
1532
        assert(!context->kernel_filename);
2✔
1533

1534
        if (kernel_image) {
2✔
1535
                r = path_extract_filename(kernel_image, &kernel_filename);
2✔
1536
                if (r < 0)
2✔
1537
                        return log_error_errno(r, "Failed to extract filename from kernel path '%s': %m", kernel_image);
×
1538
                if (r == O_DIRECTORY)
2✔
1539
                        return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Kernel path '%s' refers to directory, must be regular file, refusing.", kernel_image);
×
1540

1541
                kernel_fd = xopenat_full(XAT_FDROOT, kernel_image, O_RDONLY|O_CLOEXEC, XO_REGULAR, MODE_INVALID);
2✔
1542
                if (kernel_fd < 0)
2✔
1543
                        return log_error_errno(kernel_fd, "Failed to open kernel image '%s': %m", kernel_image);
×
1544

1545
        } else {
1546
                r = find_current_kernel(&kernel_filename, &kernel_fd);
×
1547
                if (r < 0)
×
1548
                        return r;
1549
        }
1550

1551
        context->kernel_filename = TAKE_PTR(kernel_filename);
2✔
1552
        context->kernel_fd = TAKE_FD(kernel_fd);
2✔
1553

1554
        return 0;
2✔
1555
}
1556

1557
static int sysinstall_context_run(SysInstallContext *context) {
2✔
1558

1559
        int r;
2✔
1560

1561
        assert(context);
2✔
1562

1563
        (void) sysinstall_context_notify(context, PROGRESS_ENCRYPT_CREDENTIALS, NULL, UINT_MAX);
2✔
1564

1565
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *creds_link = NULL;
2✔
1566
        _cleanup_strv_free_ char **encrypted_credentials = NULL;
2✔
1567
        r = encrypt_credentials(&creds_link, &context->credentials, &encrypted_credentials);
2✔
1568
        if (r < 0)
2✔
1569
                return r;
1570

1571
        (void) sysinstall_context_notify(context, PROGRESS_INSTALL_PARTITIONS, NULL, UINT_MAX);
2✔
1572

1573
        /* Do the main part of the installation */
1574

1575
        r = sysinstall_context_invoke_repart_run(context);
2✔
1576
        if (r < 0)
2✔
1577
                return r;
1578

1579
        (void) sysinstall_context_notify(context, PROGRESS_MOUNT_PARTITIONS, NULL, UINT_MAX);
2✔
1580

1581
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2✔
1582
        _cleanup_(umount_and_freep) char *root_dir = NULL;
×
1583
        _cleanup_close_ int root_fd = -EBADF;
2✔
1584
        r = mount_image_privately_interactively(
4✔
1585
                        context->node,
2✔
1586
                        &image_policy,
1587
                        DISSECT_IMAGE_REQUIRE_ROOT |
1588
                        DISSECT_IMAGE_RELAX_VAR_CHECK |
1589
                        DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
1590
                        DISSECT_IMAGE_DISCARD_ANY |
1591
                        DISSECT_IMAGE_GPT_ONLY |
1592
                        DISSECT_IMAGE_FSCK |
1593
                        DISSECT_IMAGE_USR_NO_ROOT |
1594
                        DISSECT_IMAGE_ADD_PARTITION_DEVICES |
1595
                        DISSECT_IMAGE_PIN_PARTITION_DEVICES,
1596
                        &root_dir,
1597
                        &root_fd,
1598
                        &loop_device);
1599
        if (r < 0)
2✔
1600
                return log_error_errno(r, "Failed to mount new image: %m");
×
1601

1602
        (void) sysinstall_context_notify(context, PROGRESS_INSTALL_KERNEL, NULL, UINT_MAX);
2✔
1603

1604
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *bootctl_link = NULL;
2✔
1605
        r = invoke_bootctl_link(&bootctl_link, root_dir, root_fd, context->kernel_filename, context->kernel_fd, encrypted_credentials);
2✔
1606
        if (r < 0)
2✔
1607
                return r;
1608

1609
        (void) sysinstall_context_notify(context, PROGRESS_INSTALL_BOOTLOADER, NULL, UINT_MAX);
2✔
1610

1611
        r = invoke_bootctl_install(&bootctl_link, context->touch_variables, root_dir, root_fd);
2✔
1612
        if (r < 0)
2✔
1613
                return r;
1614

1615
        (void) sysinstall_context_notify(context, PROGRESS_UNMOUNT_PARTITIONS, NULL, UINT_MAX);
2✔
1616

1617
        root_fd = safe_close(root_fd);
2✔
1618
        r = umount_recursive(root_dir, /* flags= */ 0);
2✔
1619
        if (r < 0)
2✔
1620
                log_warning_errno(r, "Failed to unmount target disk, proceeding anyway: %m");
×
1621
        loop_device = loop_device_unref(loop_device);
2✔
1622
        sync();
2✔
1623

1624
        return 0;
2✔
1625
}
1626

1627
typedef struct ListCandidateDevicesContext {
1628
        char **definitions;
1629
        bool subscribe;
1630

1631
        sd_varlink *repart_link; /* A repart connection to get candidate devices */
1632
        sd_varlink *dry_run_repart_link; /* A second repart connection to perform a dry run on each node */
1633

1634
        sd_varlink *link;
1635
} ListCandidateDevicesContext;
1636

1637
static ListCandidateDevicesContext* list_candidate_devices_context_new(void) {
×
1638
        ListCandidateDevicesContext *context = new(ListCandidateDevicesContext, 1);
×
1639

1640
        if (!context)
×
1641
                return NULL;
1642

1643
        *context = (ListCandidateDevicesContext) {};
×
1644

1645
        return context;
×
1646
}
1647

1648
static ListCandidateDevicesContext* list_candidate_devices_context_free(ListCandidateDevicesContext *context) {
×
1649
        if (!context)
×
1650
                return NULL;
1651

1652
        strv_free(context->definitions);
×
1653

1654
        context->repart_link = sd_varlink_flush_close_unref(context->repart_link);
×
1655
        context->dry_run_repart_link = sd_varlink_flush_close_unref(context->dry_run_repart_link);
×
1656
        context->link = sd_varlink_unref(context->link);
×
1657

1658
        return mfree(context);
×
1659
}
1660

1661
DEFINE_TRIVIAL_CLEANUP_FUNC(ListCandidateDevicesContext*, list_candidate_devices_context_free);
×
1662

1663
static int list_candidate_devices_context_settle_definitions(ListCandidateDevicesContext *context,
×
1664
                                                             char **definitions) {
1665

1666
        return settle_definitions(definitions, &context->definitions);
×
1667
}
1668

1669
static void vl_on_disconnect(sd_varlink_server *server, sd_varlink *link, void *userdata) {
×
1670
        assert(server);
×
1671
        assert(link);
×
1672

1673
        list_candidate_devices_context_free(sd_varlink_set_userdata(link, NULL));
×
1674
}
×
1675

1676
typedef struct DevicesResponse {
1677
        const char *node;
1678
        char **symlinks;
1679
        uint64_t diskseq;
1680
        uint64_t size;
1681
        const char *model;
1682
        const char *vendor;
1683
        const char *subsystem;
1684
        const char *action;
1685
} DevicesResponse;
1686

1687
static void devices_response_done(DevicesResponse *p) {
×
1688
        assert(p);
×
1689

1690
        p->symlinks = strv_free(p->symlinks);
×
1691
}
×
1692

1693
static int fetch_candidate_devices_reply(
×
1694
                sd_varlink *repart_link,
1695
                sd_json_variant *reply,
1696
                const char *error_id,
1697
                sd_varlink_reply_flags_t flags,
1698
                void *userdata) {
1699

1700
        int r;
×
1701
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
×
1702
        ListCandidateDevicesContext *context = ASSERT_PTR(userdata);
×
1703

1704
        if (error_id) {
×
1705
                if (streq(error_id, "io.systemd.Repart.NoCandidateDevices"))
×
1706
                        return sd_varlink_error(context->link, "io.systemd.SysInstall.NoCandidateDevices", NULL);
×
1707

1708
                return sd_varlink_error(context->link, error_id, NULL);
×
1709
        }
1710

1711
        static const sd_json_dispatch_field dispatch_table[] = {
×
1712
                { "node",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, node),      0 },
1713
                { "symlinks",  SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,         offsetof(DevicesResponse, symlinks),  0 },
1714
                { "diskseq",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(DevicesResponse, diskseq),   0 },
1715
                { "sizeBytes", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(DevicesResponse, size),      0 },
1716
                { "model",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, model),     0 },
1717
                { "vendor",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, vendor),    0 },
1718
                { "subsystem", SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, subsystem), 0 },
1719
                { "action",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string, offsetof(DevicesResponse, action),    0 },
1720
                {}
1721
        };
1722

1723
        _cleanup_(devices_response_done) DevicesResponse p = {
×
1724
                .diskseq = UINT64_MAX,
1725
                .size = UINT64_MAX,
1726
        };
1727
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
×
1728
        if (r < 0)
×
1729
                return r;
1730

1731
        if (context->subscribe) {
×
1732
                /* The action needs to be ready, remove or add else we don't support the action */
1733
                if (streq(p.action, "ready"))
×
1734
                        return sd_varlink_notifybo(context->link, SD_JSON_BUILD_PAIR("action", JSON_BUILD_CONST_STRING("ready")));
×
1735

1736
                if (streq(p.action, "remove"))
×
1737
                        return sd_varlink_notifybo(context->link,
×
1738
                                        SD_JSON_BUILD_PAIR("action", JSON_BUILD_CONST_STRING("remove")),
1739
                                        SD_JSON_BUILD_PAIR_STRING("node", p.node));
1740

1741
                if (!streq(p.action, "add")) {
×
1742
                        log_debug("Skip unsupported action '%s' while fetching candidate devices.", p.action);
×
1743
                        return 0;
1744
                }
1745
        }
1746

1747
        uint64_t min_size = UINT64_MAX, current_size = UINT64_MAX, need_free = UINT64_MAX;
×
1748
        r = invoke_repart(
×
1749
                        &context->dry_run_repart_link,
1750
                        p.node,
1751
                        /* erase= */ false,
1752
                        /* dry_run= */ true,
1753
                        context->definitions,
1754
                        &min_size,
1755
                        &current_size,
1756
                        &need_free);
1757

1758
        DeviceFit fit;
×
1759
        if (r < 0) {
×
1760
                if (r == -ENOSPC)
×
1761
                        fit = DEVICE_FIT_INSUFFICIENT_FREE_SPACE;
1762
                else if (r == -E2BIG)
×
1763
                        fit = DEVICE_FIT_DISK_TOO_SMALL;
1764
                else if (r == -EHWPOISON)
×
1765
                        fit = DEVICE_FIT_CONFLICTING_DISK_LABEL_PRESENT;
1766
                else
1767
                        return r;
1768
        } else
1769
                fit = DEVICE_FIT_ENOUGH_FREE_SPACE;
1770

1771
        r = sd_json_buildo(&v,
×
1772
                        SD_JSON_BUILD_PAIR_STRING("node", p.node),
1773
                        JSON_BUILD_PAIR_STRV_NON_EMPTY("symlinks", p.symlinks),
1774
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("diskseq", p.diskseq, UINT64_MAX),
1775
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("sizeBytes", p.size, UINT64_MAX),
1776
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("model", p.model),
1777
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("vendor", p.vendor),
1778
                        JSON_BUILD_PAIR_STRING_NON_EMPTY("subsystem", p.subsystem),
1779
                        JSON_BUILD_PAIR_ENUM("fit", device_fit_to_string(fit)),
1780
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("currentSizeBytes", current_size, UINT64_MAX),
1781
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("needFreeBytes", need_free, UINT64_MAX),
1782
                        JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("minimalSizeBytes", min_size, UINT64_MAX),
1783
                        SD_JSON_BUILD_PAIR_CONDITION(context->subscribe, "action", JSON_BUILD_CONST_STRING("add")));
1784
        if (r < 0)
×
1785
                return r;
1786

1787
        if (FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES))
×
1788
                return sd_varlink_notify(context->link, v);
×
1789

1790
        return sd_varlink_reply(context->link, v);
×
1791
}
1792

1793
typedef struct ListCandidateDevicesParameters {
1794
        char **definitions;
1795
        bool subscribe;
1796
} ListCandidateDevicesParameters;
1797

1798
static void list_candidate_devices_parameters_done(ListCandidateDevicesParameters *p) {
×
1799
        assert(p);
×
1800

1801
        p->definitions = strv_free(p->definitions);
×
1802
}
×
1803

1804
static int vl_method_list_candidate_devices(
×
1805
                sd_varlink *link,
1806
                sd_json_variant *parameters,
1807
                sd_varlink_method_flags_t flags,
1808
                void *userdata) {
1809
        int r;
×
1810

1811
        assert(link);
×
1812

1813
        sd_varlink_server *varlink_server = sd_varlink_get_server(link);
×
1814
        sd_event *event = sd_varlink_server_get_event(varlink_server);
×
1815
        Hashmap **polkit_registry = ASSERT_PTR(sd_varlink_server_get_userdata(varlink_server));
×
1816

1817
        assert(FLAGS_SET(flags, SD_VARLINK_METHOD_MORE));
×
1818

1819
        r = varlink_verify_polkit_async(
×
1820
                        link,
1821
                        /* bus= */ NULL,
1822
                        "io.systemd.sysinstall.ListCandidateDevices",
1823
                        /* details= */ NULL,
1824
                        polkit_registry);
1825
        if (r <= 0)
×
1826
                return r;
×
1827

1828
        static const sd_json_dispatch_field dispatch_table[] = {
×
1829
                { "definitions", SD_JSON_VARIANT_ARRAY,   json_dispatch_strv_path,  offsetof(ListCandidateDevicesParameters, definitions), SD_JSON_STRICT },
1830
                { "subscribe",   SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(ListCandidateDevicesParameters, subscribe),   0              },
1831
                {}
1832
        };
1833

1834
        _cleanup_(list_candidate_devices_parameters_done) ListCandidateDevicesParameters p = {};
×
1835
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
×
1836
        if (r != 0)
×
1837
                return r;
1838

1839
        _cleanup_(list_candidate_devices_context_freep) ListCandidateDevicesContext* context = list_candidate_devices_context_new();
×
1840
        if (!context)
×
1841
                return log_oom();
×
1842

1843
        context->subscribe = p.subscribe;
×
1844
        r = list_candidate_devices_context_settle_definitions(context, p.definitions);
×
1845
        if (r < 0)
×
1846
                return r;
1847

1848
        context->link = sd_varlink_ref(link);
×
1849

1850
        r = connect_to_repart(&context->repart_link);
×
1851
        if (r < 0)
×
1852
                return r;
1853

1854
        r = sd_varlink_attach_event(context->repart_link, event, SD_EVENT_PRIORITY_NORMAL);
×
1855
        if (r < 0)
×
1856
                return log_error_errno(
×
1857
                                r,
1858
                                "Failed to attach io.systemd.Repart.ListCandidateDevices() varlink connection to event loop: %m");
1859

1860
        r = sd_varlink_bind_reply(context->repart_link, fetch_candidate_devices_reply);
×
1861
        if (r < 0)
×
1862
                return r;
1863

1864
        r = sd_varlink_observebo(
×
1865
                        context->repart_link,
1866
                        "io.systemd.Repart.ListCandidateDevices",
1867
                        SD_JSON_BUILD_PAIR_BOOLEAN("subscribe", context->subscribe),
1868
                        SD_JSON_BUILD_PAIR_BOOLEAN("ignoreRoot", true));
1869

1870
        if (r < 0)
×
1871
                return log_error_errno(
×
1872
                                r,
1873
                                "Failed to issue io.systemd.Repart.ListCandidateDevices() varlink call: %m");
1874

1875
        r = sd_varlink_server_bind_disconnect(varlink_server, vl_on_disconnect);
×
1876
        if (r < 0)
×
1877
                return r;
1878

1879
        /* Disable connection timeout so that the connection to repart doesn't close before the link is
1880
         * disconnected */
1881
        r = sd_varlink_set_relative_timeout(context->repart_link, UINT64_MAX);
×
1882
        if (r < 0)
×
1883
                return r;
1884

1885
        /* The context is freed in vl_on_disconnect() */
1886
        sd_varlink_set_userdata(context->repart_link, context);
×
1887
        sd_varlink_set_userdata(link, TAKE_PTR(context));
×
1888

1889
        return 0;
1890
}
1891

1892
typedef struct RunParameters {
1893
        char *node;
1894
        char **definitions;
1895
        bool erase;
1896
        bool variables;
1897
        char *kernel_image;
1898
        bool copy_locale;
1899
        bool copy_keymap;
1900
        bool copy_timezone;
1901
        sd_json_variant *credentials;
1902
} RunParameters;
1903

1904
static void run_parameters_done(RunParameters *p) {
1✔
1905
        assert(p);
1✔
1906

1907
        p->node = mfree(p->node);
1✔
1908
        p->definitions = strv_free(p->definitions);
1✔
1909
        p->kernel_image = mfree(p->kernel_image);
1✔
1910
        p->credentials = sd_json_variant_unref(p->credentials);
1✔
1911
}
1✔
1912

1913
typedef struct CredentialParameters {
1914
        const char *id;
1915
        struct iovec value;
1916
} CredentialParameters;
1917

1918
static void credential_parameters_done(CredentialParameters *p) {
1✔
1919
        assert(p);
1✔
1920

1921
        iovec_done_erase(&p->value);
1✔
1922
}
1✔
1923

1924
static int credentials_from_json_array(MachineCredentialContext *credentials, sd_json_variant *v) {
1✔
1925

1926
        int r;
1✔
1927
        sd_json_variant *credential;
1✔
1928

1929
        assert(credentials);
1✔
1930

1931
        static const sd_json_dispatch_field dispatch_table[] = {
1✔
1932
                { "id",    SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CredentialParameters, id),    SD_JSON_MANDATORY },
1933
                { "value", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec,  offsetof(CredentialParameters, value), SD_JSON_MANDATORY },
1934
                {}
1935
        };
1936

1937
        JSON_VARIANT_ARRAY_FOREACH(credential, v) {
2✔
1938
                _cleanup_(credential_parameters_done) CredentialParameters p = {};
1✔
1939

1940
                r = sd_json_dispatch(credential, dispatch_table, /* flags= */ 0, &p);
1✔
1941
                if (r < 0)
1✔
1942
                        return r;
1943

1944
                r = machine_credential_add(credentials, p.id, p.value.iov_base, p.value.iov_len);
1✔
1945
                if (r < 0)
1✔
1946
                        return r;
1947
        }
1948

1949
        return 0;
1✔
1950
}
1951

1952
static int vl_method_run(
1✔
1953
                sd_varlink *link,
1954
                sd_json_variant *parameters,
1955
                sd_varlink_method_flags_t flags,
1956
                void *userdata) {
1957

1958
        static const sd_json_dispatch_field dispatch_table[] = {
1✔
1959
                { "node",            SD_JSON_VARIANT_STRING,  json_dispatch_path,       offsetof(RunParameters, node),          SD_JSON_MANDATORY | SD_JSON_STRICT },
1960
                { "definitions",     SD_JSON_VARIANT_ARRAY,   json_dispatch_strv_path,  offsetof(RunParameters, definitions),   SD_JSON_NULLABLE | SD_JSON_STRICT  },
1961
                { "erase",           SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, erase),         SD_JSON_MANDATORY                  },
1962
                { "variables",       SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, variables),     SD_JSON_NULLABLE                   },
1963
                { "kernelImagePath", SD_JSON_VARIANT_STRING,  json_dispatch_path,       offsetof(RunParameters, kernel_image),  SD_JSON_NULLABLE | SD_JSON_STRICT  },
1964
                { "copyLocale",      SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, copy_locale),   SD_JSON_NULLABLE                   },
1965
                { "copyKeymap",      SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, copy_keymap),   SD_JSON_NULLABLE                   },
1966
                { "copyTimezone",    SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, copy_timezone), SD_JSON_NULLABLE                   },
1967
                { "credentials",     SD_JSON_VARIANT_ARRAY,   sd_json_dispatch_variant, offsetof(RunParameters, credentials),   SD_JSON_NULLABLE                   },
1968
                {}
1969
        };
1970

1971
        int r;
1✔
1972

1973
        assert(link);
1✔
1974

1975
        sd_varlink_server *varlink_server = sd_varlink_get_server(link);
1✔
1976
        Hashmap **polkit_registry = ASSERT_PTR(sd_varlink_server_get_userdata(varlink_server));
1✔
1977

1978
        r = varlink_verify_polkit_async(
1✔
1979
                        link,
1980
                        /* bus= */ NULL,
1981
                        "io.systemd.sysinstall.Run",
1982
                        /* details= */ NULL,
1983
                        polkit_registry);
1984
        if (r <= 0)
1✔
1985
                return r;
1✔
1986

1987
        _cleanup_(run_parameters_done) RunParameters p = {};
1✔
1988
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
1✔
1989
        if (r != 0)
1✔
1990
                return r;
1991

1992
        _cleanup_(sysinstall_context_done) SysInstallContext context = (SysInstallContext) {
1✔
1993
                .copy_locale = p.copy_locale,
1✔
1994
                .copy_keymap = p.copy_keymap,
1✔
1995
                .copy_timezone = p.copy_timezone,
1✔
1996
                .erase = p.erase,
1✔
1997
                .touch_variables = p.variables,
1✔
1998
                .node = TAKE_PTR(p.node),
1✔
1999
                .kernel_fd = -EBADF,
2000
        };
2001

2002
        r = sysinstall_context_settle_definitions(&context, p.definitions);
1✔
2003
        if (r < 0)
1✔
2004
                return r;
2005

2006
        if (FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
1✔
2007
                context.link = sd_varlink_ref(link);
1✔
2008

2009
        r = credentials_from_json_array(&context.credentials, p.credentials);
1✔
2010
        if (r < 0)
1✔
2011
                return r;
2012

2013
        r = sysinstall_context_read_credentials(&context);
1✔
2014
        if (r < 0)
1✔
2015
                return r;
2016

2017
        r = sysinstall_context_settle_kernel_image(&context, p.kernel_image);
1✔
2018
        if (r < 0)
1✔
2019
                return r;
2020

2021
        r = sysinstall_context_run(&context);
1✔
2022
        if (r < 0)
1✔
2023
                return r;
2024

2025
        return sd_varlink_reply(link, NULL);
1✔
2026
}
2027

2028
static int vl_server(void) {
13✔
2029
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
13✔
2030
        _cleanup_hashmap_free_ Hashmap *polkit_registry = NULL;
13✔
2031
        int r;
13✔
2032

2033
        /* Invocation as Varlink service */
2034

2035
        r = varlink_server_new(
13✔
2036
                        &varlink_server,
2037
                        0,
2038
                        /* userdata= */ &polkit_registry);
2039
        if (r < 0)
13✔
2040
                return log_error_errno(r, "Failed to allocate Varlink server: %m");
×
2041

2042
        r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_SysInstall);
13✔
2043
        if (r < 0)
13✔
2044
                return log_error_errno(r, "Failed to add Varlink interface: %m");
×
2045

2046
        r = sd_varlink_server_bind_method_many(
13✔
2047
                        varlink_server,
2048
                        "io.systemd.SysInstall.ListCandidateDevices", vl_method_list_candidate_devices,
2049
                        "io.systemd.SysInstall.Run",                  vl_method_run);
2050
        if (r < 0)
13✔
2051
                return log_error_errno(r, "Failed to bind Varlink methods: %m");
×
2052

2053
        r = sd_varlink_server_loop_auto(varlink_server);
13✔
2054
        if (r < 0)
13✔
2055
                return log_error_errno(r, "Failed to run Varlink event loop: %m");
×
2056

2057
        return 0;
2058
}
2059

2060
static void end_marker(void) {
1✔
2061

2062
        if (!arg_welcome)
1✔
2063
                return;
2064

2065
        printf("\n%sExiting first boot settings tool.%s\n\n", ansi_grey(), ansi_normal());
×
2066
        fflush(stdout);
×
2067
}
2068

2069
static int run(int argc, char *argv[]) {
14✔
2070
        int r;
14✔
2071

2072
        LIBBLKID_NOTE(recommended);
14✔
2073
        LIBCRYPTO_NOTE(suggested);
14✔
2074
        LIBCRYPTSETUP_NOTE(suggested);
14✔
2075
        LIBMOUNT_NOTE(recommended);
14✔
2076

2077
        setlocale(LC_ALL, "");
14✔
2078

2079
        r = parse_argv(argc, argv);
14✔
2080
        if (r <= 0)
14✔
2081
                return r;
14✔
2082

2083
        log_setup();
14✔
2084

2085
        if (arg_varlink)
14✔
2086
                return vl_server();
13✔
2087

2088
        _cleanup_(sysinstall_context_done) SysInstallContext context = (SysInstallContext) {
×
2089
                .copy_locale = arg_copy_locale,
2090
                .copy_keymap = arg_copy_keymap,
2091
                .copy_timezone = arg_copy_timezone,
2092
                .credentials = TAKE_STRUCT(arg_credentials),
1✔
2093
                .kernel_fd = -EBADF,
2094
        };
2095

2096
        r = sysinstall_context_settle_definitions(&context, arg_definitions);
1✔
2097
        if (r < 0)
1✔
2098
                return r;
2099

2100
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
1✔
2101
        if (arg_welcome) {
1✔
2102
                if (arg_mute_console)
×
2103
                        (void) mute_console(&mute_console_link);
×
2104

2105
                (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
×
2106

2107
                if (arg_chrome)
×
2108
                        chrome_show("Operating System Installer", /* bottom= */ NULL);
×
2109
        }
2110

2111
        DEFER_VOID_CALL(end_marker);
1✔
2112
        DEFER_VOID_CALL(chrome_hide);
×
2113

2114
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *repart_link = NULL;
1✔
2115
        if (arg_node) {
1✔
2116
                r = print_welcome(&mute_console_link);
1✔
2117
                if (r < 0)
1✔
2118
                        return r;
2119

2120
                r = validate_run(&repart_link, arg_node);
1✔
2121
                if (r < 0)
1✔
2122
                        return r;
2123
        } else {
2124
                /* Determine the minimum disk size */
2125
                uint64_t min_size = UINT64_MAX;
×
2126
                r = invoke_repart(
×
2127
                                &repart_link,
2128
                                /* node= */ NULL,
2129
                                /* erase= */ true,
2130
                                /* dry_run= */ true,
2131
                                arg_definitions,
2132
                                &min_size,
2133
                                /* current_size= */ NULL,
2134
                                /* need_free= */ NULL);
2135
                if (r < 0)
×
2136
                        return r;
×
2137

2138
                r = print_welcome(&mute_console_link);
×
2139
                if (r < 0)
×
2140
                        return r;
2141

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

2144
                for (;;) {
×
2145
                        _cleanup_free_ char *node = NULL;
×
2146
                        r = prompt_block_device(&repart_link, &node);
×
2147
                        if (r < 0)
×
2148
                                return r;
2149

2150
                        r = validate_run(&repart_link, node);
×
2151
                        if (IN_SET(r, -ENOSPC, -E2BIG, -EHWPOISON)) /* Device is no fit, pick other */
×
2152
                                continue;
×
2153
                        if (r < 0)
×
2154
                                return r;
2155

2156
                        arg_node = TAKE_PTR(node);
×
2157
                        break;
×
2158
                }
2159
        }
2160

2161
        r = prompt_touch_variables();
1✔
2162
        if (r < 0)
1✔
2163
                return r;
2164

2165
        r = sysinstall_context_read_credentials(&context);
1✔
2166
        if (r < 0)
1✔
2167
                return r;
2168

2169
        r = sysinstall_context_settle_kernel_image(&context, arg_kernel_image);
1✔
2170
        if (r < 0)
1✔
2171
                return r;
2172

2173
        /* Verify we have everything we need */
2174
        assert(arg_node);
1✔
2175
        assert(arg_erase >= 0);
1✔
2176
        assert(arg_touch_variables >= 0);
1✔
2177

2178
        context.node = TAKE_PTR(arg_node);
1✔
2179
        context.touch_variables = arg_touch_variables;
1✔
2180
        context.erase = arg_erase;
1✔
2181

2182
        if (arg_summary) {
1✔
2183
                r = sysinstall_context_show_summary(&context);
×
2184
                if (r < 0)
×
2185
                        return r;
2186
        }
2187

2188
        r = prompt_confirm();
1✔
2189
        if (r < 0)
1✔
2190
                return r;
2191

2192
        putchar('\n');
1✔
2193

2194
        r = sysinstall_context_run(&context);
1✔
2195
        if (r < 0)
1✔
2196
                return r;
2197

2198
        log_notice("%s%sInstallation succeeded.",
2✔
2199
                   emoji_enabled() ? glyph(GLYPH_SPARKLES) : "", emoji_enabled() ? " " : "");
2200

2201
        r = maybe_reboot();
1✔
2202
        if (r < 0)
1✔
2203
                return r;
×
2204

2205
        return 0;
2206
}
2207

2208
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