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

systemd / systemd / 29968961921

22 Jul 2026 03:11PM UTC coverage: 73.067% (+0.2%) from 72.837%
29968961921

push

github

keszybz
homed: verify privileged worker changes

Workers may return a newer embedded identity while processing
an owner update. Accept self-modifiable changes, but require
privileged changes to carry a trusted signature.

Follow-up for 70a5db582

16 of 36 new or added lines in 1 file covered. (44.44%)

1697 existing lines in 49 files now uncovered.

346625 of 474391 relevant lines covered (73.07%)

1335649.2 hits per line

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

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

3
#include <fcntl.h>
4
#include <unistd.h>
5

6
#include "sd-bus.h"
7
#include "sd-id128.h"
8
#include "sd-varlink.h"
9

10
#include "alloc-util.h"
11
#include "ansi-color.h"
12
#include "ask-password-api.h"
13
#include "build.h"
14
#include "bus-error.h"
15
#include "bus-locator.h"
16
#include "bus-unit-util.h"
17
#include "bus-util.h"
18
#include "bus-wait-for-jobs.h"
19
#include "chase.h"
20
#include "copy.h"
21
#include "creds-util.h"
22
#include "dissect-image.h"
23
#include "dlopen-note.h"
24
#include "env-file.h"
25
#include "errno-util.h"
26
#include "fd-util.h"
27
#include "fileio.h"
28
#include "firstboot-util.h"
29
#include "format-table.h"
30
#include "fs-util.h"
31
#include "glyph-util.h"
32
#include "help-util.h"
33
#include "hostname-setup.h"
34
#include "hostname-util.h"
35
#include "image-policy.h"
36
#include "kbd-util.h"
37
#include "label-util.h"
38
#include "libcrypt-util.h"
39
#include "locale-setup.h"
40
#include "locale-util.h"
41
#include "lock-util.h"
42
#include "loop-util.h"
43
#include "main-func.h"
44
#include "memory-util.h"
45
#include "mount-util.h"
46
#include "options.h"
47
#include "os-util.h"
48
#include "parse-argument.h"
49
#include "password-quality-util.h"
50
#include "path-util.h"
51
#include "plymouth-util.h"
52
#include "prompt-util.h"
53
#include "runtime-scope.h"
54
#include "smack-util.h"
55
#include "stat-util.h"
56
#include "string-util.h"
57
#include "strv.h"
58
#include "terminal-util.h"
59
#include "time-util.h"
60
#include "tmpfile-util.h"
61
#include "user-util.h"
62
#include "vconsole-util.h"
63

64
static char *arg_root = NULL;
65
static char *arg_image = NULL;
66
static char *arg_locale = NULL;  /* $LANG */
67
static char *arg_locale_messages = NULL; /* $LC_MESSAGES */
68
static char *arg_keymap = NULL;
69
static char *arg_timezone = NULL;
70
static char *arg_hostname = NULL;
71
static sd_id128_t arg_machine_id = {};
72
static char **arg_machine_tags = NULL;
73
static char *arg_root_password = NULL;
74
static char *arg_root_shell = NULL;
75
static char *arg_kernel_cmdline = NULL;
76
static bool arg_prompt_locale = false;
77
static bool arg_prompt_keymap = false;
78
static bool arg_prompt_keymap_auto = false;
79
static bool arg_prompt_timezone = false;
80
static bool arg_prompt_hostname = false;
81
static bool arg_prompt_root_password = false;
82
static bool arg_prompt_root_shell = false;
83
static bool arg_headless = false;
84
static bool arg_copy_locale = false;
85
static bool arg_copy_keymap = false;
86
static bool arg_copy_timezone = false;
87
static bool arg_copy_root_password = false;
88
static bool arg_copy_root_shell = false;
89
static bool arg_force = false;
90
static bool arg_delete_root_password = false;
91
static bool arg_root_password_is_hashed = false;
92
static bool arg_welcome = true;
93
static bool arg_reset = false;
94
static ImagePolicy *arg_image_policy = NULL;
95
static bool arg_chrome = true;
96
static bool arg_mute_console = false;
97

98
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
161✔
99
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
161✔
100
STATIC_DESTRUCTOR_REGISTER(arg_locale, freep);
161✔
101
STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep);
161✔
102
STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
161✔
103
STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep);
161✔
104
STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep);
161✔
105
STATIC_DESTRUCTOR_REGISTER(arg_machine_tags, strv_freep);
161✔
106
STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep);
161✔
107
STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep);
161✔
108
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
161✔
109
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
161✔
110

111
static bool welcome_done = false;
112

113
static void print_welcome(int rfd, sd_varlink **mute_console_link) {
7✔
114
        _cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL, *fancy_name = NULL;
7✔
115
        const char *pn, *ac;
7✔
116
        int r;
7✔
117

118
        assert(rfd >= 0);
7✔
119
        assert(mute_console_link);
7✔
120

121
        /* Needs to be called before mute_console or it will garble the screen */
122
        if (arg_welcome)
7✔
123
                (void) plymouth_hide_splash();
6✔
124

125
        if (!*mute_console_link && arg_mute_console)
7✔
126
                (void) mute_console(mute_console_link);
×
127

128
        if (!arg_welcome)
7✔
129
                return;
130

131
        if (welcome_done) {
6✔
132
                putchar('\n'); /* Add some breathing room between multiple prompts */
1✔
133
                return;
134
        }
135

136
        (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
6✔
137

138
        if (arg_chrome)
6✔
139
                chrome_show("Initial Setup", /* bottom= */ NULL);
6✔
140

141
        r = parse_os_release_at(rfd,
6✔
142
                                "PRETTY_NAME", &pretty_name,
143
                                "FANCY_NAME", &fancy_name,
144
                                "NAME", &os_name,
145
                                "ANSI_COLOR", &ansi_color);
146
        if (r < 0)
6✔
147
                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
6✔
148
                               "Failed to read os-release file, ignoring: %m");
149

150
        pn = os_release_pretty_name(pretty_name, os_name);
6✔
151
        ac = isempty(ansi_color) ? "0" : ansi_color;
6✔
152

153
        if (use_fancy_name(unescape_fancy_name(&fancy_name)))
6✔
154
                printf(ANSI_HIGHLIGHT "Welcome to " ANSI_NORMAL "%s" ANSI_HIGHLIGHT "!" ANSI_NORMAL "\n", fancy_name);
×
155
        else if (colors_enabled())
6✔
156
                printf(ANSI_HIGHLIGHT "Welcome to " ANSI_NORMAL "\x1B[%sm%s" ANSI_HIGHLIGHT "!" ANSI_NORMAL "\n", ac, pn);
×
157
        else
158
                printf("Welcome to %s!\n", pn);
6✔
159

160
        putchar('\n');
6✔
161
        if (emoji_enabled()) {
6✔
162
                fputs(glyph(GLYPH_SPARKLES), stdout);
×
163
                putchar(' ');
×
164
        }
165
        printf("Please configure the system!\n\n");
6✔
166

167
        welcome_done = true;
6✔
168
}
169

170
static int should_configure(int dir_fd, const char *filename) {
1,401✔
171
        _cleanup_fclose_ FILE *passwd = NULL, *shadow = NULL;
1,401✔
172
        int r;
1,401✔
173

174
        assert(dir_fd >= 0);
1,401✔
175
        assert(filename);
1,401✔
176

177
        if (streq(filename, "passwd") && !arg_force)
1,401✔
178
                /* We may need to do additional checks, so open the file. */
179
                r = xfopenat(dir_fd, filename, "re", O_NOFOLLOW, &passwd);
152✔
180
        else
181
                r = RET_NERRNO(faccessat(dir_fd, filename, F_OK, AT_SYMLINK_NOFOLLOW));
1,249✔
182

183
        if (r == -ENOENT)
844✔
184
                return true; /* missing */
185
        if (r < 0)
677✔
186
                return log_error_errno(r, "Failed to access %s: %m", filename);
×
187
        if (arg_force)
677✔
188
                return true; /* exists, but if --force was given we should still configure the file. */
189

190
        if (!passwd)
654✔
191
                return false;
192

193
        /* In case of /etc/passwd, do an additional check for the root password field.
194
         * We first check that passwd redirects to shadow, and then we check shadow.
195
         */
196
        struct passwd *i;
197
        while ((r = fgetpwent_sane(passwd, &i)) > 0) {
120✔
198
                if (!streq(i->pw_name, "root"))
119✔
199
                        continue;
×
200

201
                if (streq_ptr(i->pw_passwd, PASSWORD_SEE_SHADOW))
119✔
202
                        break;
203
                log_debug("passwd: root account with non-shadow password found, treating root as configured");
×
204
                return false;
205
        }
206
        if (r < 0)
120✔
207
                return log_error_errno(r, "Failed to read %s: %m", filename);
×
208
        if (r == 0) {
120✔
209
                log_debug("No root account found in %s, assuming root is not configured.", filename);
1✔
210
                return true;
211
        }
212

213
        r = xfopenat(dir_fd, "shadow", "re", O_NOFOLLOW, &shadow);
119✔
214
        if (r == -ENOENT) {
119✔
215
                log_debug("No shadow file found, assuming root is not configured.");
×
216
                return true; /* missing */
217
        }
218
        if (r < 0)
119✔
219
                return log_error_errno(r, "Failed to access shadow: %m");
×
220

221
        struct spwd *j;
222
        while ((r = fgetspent_sane(shadow, &j)) > 0) {
119✔
223
                if (!streq(j->sp_namp, "root"))
119✔
224
                        continue;
×
225

226
                bool unprovisioned = streq_ptr(j->sp_pwdp, PASSWORD_UNPROVISIONED);
119✔
227
                log_debug("Root account found, %s.",
232✔
228
                          unprovisioned ? "with unprovisioned password, treating root as not configured" :
229
                                          "treating root as configured");
230
                return unprovisioned;
119✔
231
        }
232
        if (r < 0)
×
233
                return log_error_errno(r, "Failed to read shadow: %m");
×
234
        assert(r == 0);
×
235
        log_debug("No root account found in shadow, assuming root is not configured.");
×
236
        return true;
237
}
238

239
static int locale_is_ok(const char *name, void *userdata) {
11✔
240
        int rfd = ASSERT_FD(PTR_TO_FD(userdata)), r;
11✔
241

242
        r = dir_fd_is_root(rfd);
11✔
243
        if (r < 0)
11✔
244
                log_debug_errno(r, "Unable to determine if operating on host root directory, assuming we are: %m");
×
245

246
        return r != 0 ? locale_is_installed(name) > 0 : locale_is_valid(name);
11✔
247
}
248

249
static bool headless_skips_prompt_for(const char *what) {
7✔
250
        assert(what);
7✔
251

252
        if (!arg_headless)
7✔
253
                return false;
254

255
        log_debug("Running headless, not prompting for %s.", what);
×
256
        return true;
257
}
258

259
static int prompt_locale(int rfd, sd_varlink **mute_console_link) {
19✔
260
        _cleanup_strv_free_ char **locales = NULL;
19✔
261
        bool acquired_from_creds = false;
19✔
262
        int r;
19✔
263

264
        assert(rfd >= 0);
19✔
265

266
        if (arg_locale || arg_locale_messages)
19✔
267
                return 0;
268

269
        r = read_credential("firstboot.locale", (void**) &arg_locale, NULL);
14✔
270
        if (r < 0)
14✔
271
                log_debug_errno(r, "Failed to read credential firstboot.locale, ignoring: %m");
13✔
272
        else
273
                acquired_from_creds = true;
274

275
        r = read_credential("firstboot.locale-messages", (void**) &arg_locale_messages, NULL);
14✔
276
        if (r < 0)
14✔
277
                log_debug_errno(r, "Failed to read credential firstboot.locale-messages, ignoring: %m");
13✔
278
        else
279
                acquired_from_creds = true;
280

281
        if (acquired_from_creds) {
13✔
282
                log_debug("Acquired locale from credentials.");
1✔
283
                return 0;
284
        }
285

286
        if (!arg_prompt_locale) {
13✔
287
                log_debug("Prompting for locale was not requested.");
12✔
288
                return 0;
289
        }
290

291
        r = get_locales(&locales);
1✔
292
        if (r < 0)
1✔
293
                return log_error_errno(r, "Cannot query locales list: %m");
×
294

295
        if (strv_isempty(locales))
1✔
296
                log_debug("No locales found, skipping locale selection.");
×
297
        else if (strv_length(locales) == 1) {
1✔
298

299
                if (streq(locales[0], SYSTEMD_DEFAULT_LOCALE))
×
300
                        log_debug("Only installed locale is default locale anyway, not setting locale explicitly.");
×
301
                else {
302
                        log_debug("Only a single locale available (%s), selecting it as default.", locales[0]);
×
303

304
                        arg_locale = strdup(locales[0]);
×
305
                        if (!arg_locale)
×
306
                                return log_oom();
×
307

308
                        /* Not setting arg_locale_message here, since it defaults to LANG anyway */
309
                }
310
        } else {
311
                if (headless_skips_prompt_for("locale"))
1✔
312
                        return 0;
×
313

314
                print_welcome(rfd, mute_console_link);
1✔
315

316
                _cleanup_free_ char *prefill = NULL;
1✔
317
                (void) locale_lang_from_efi(&prefill, LOCALE_REQUIRE_INSTALLED|LOCALE_SUPPRESS_EN_US);
1✔
318

319
                r = prompt_loop("Please enter the new system locale name or number",
2✔
320
                                GLYPH_WORLD,
321
                                prefill,
322
                                locales,
323
                                /* accepted= */ NULL,
324
                                /* ellipsize_percentage= */ 60,
325
                                /* n_columns= */ 3,
326
                                /* column_width= */ 20,
327
                                locale_is_ok,
328
                                /* refresh= */ NULL,
329
                                FD_TO_PTR(rfd),
1✔
330
                                PROMPT_MAY_SKIP|PROMPT_SHOW_MENU,
331
                                &arg_locale);
332
                if (r < 0)
1✔
333
                        return r;
334
                if (isempty(arg_locale))
1✔
335
                        return 0;
336

337
                r = prompt_loop("Please enter the new system message locale name or number",
1✔
338
                                GLYPH_WORLD,
339
                                /* prefill= */ NULL,
340
                                locales,
341
                                /* accepted= */ NULL,
342
                                /* ellipsize_percentage= */ 60,
343
                                /* n_columns= */ 3,
344
                                /* column_width= */ 20,
345
                                locale_is_ok,
346
                                /* refresh= */ NULL,
347
                                FD_TO_PTR(rfd),
348
                                PROMPT_MAY_SKIP|PROMPT_SHOW_MENU,
349
                                &arg_locale_messages);
350
                if (r < 0)
1✔
351
                        return r;
352

353
                /* Suppress the messages setting if it's the same as the main locale anyway */
354
                if (streq_ptr(arg_locale, arg_locale_messages))
1✔
355
                        arg_locale_messages = mfree(arg_locale_messages);
×
356
        }
357

358
        return 0;
359
}
360

361
static int process_locale(int rfd, sd_varlink **mute_console_link) {
156✔
362
        _cleanup_close_ int pfd = -EBADF;
156✔
363
        _cleanup_free_ char *f = NULL;
156✔
364
        char* locales[3];
156✔
365
        unsigned i = 0;
156✔
366
        int r;
156✔
367

368
        assert(rfd >= 0);
156✔
369

370
        pfd = chase_and_open_parent_at(rfd, rfd, etc_locale_conf(),
156✔
371
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
372
                                       &f);
373
        if (pfd < 0)
156✔
374
                return log_error_errno(pfd, "Failed to chase /etc/locale.conf: %m");
×
375

376
        r = should_configure(pfd, f);
156✔
377
        if (r == 0)
156✔
378
                log_debug("Found /etc/locale.conf, assuming locale information has been configured.");
134✔
379
        if (r <= 0)
156✔
380
                return r;
381

382
        r = dir_fd_is_root(rfd);
22✔
383
        if (r < 0)
22✔
384
                return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
×
385

386
        if (arg_copy_locale && r == 0) {
22✔
387
                r = copy_file_atomic_at(AT_FDCWD, etc_locale_conf(), pfd, f, 0644, /* copy_flags= */ 0);
3✔
388
                if (r != -ENOENT) {
3✔
389
                        if (r < 0)
3✔
390
                                return log_error_errno(r, "Failed to copy host's /etc/locale.conf: %m");
×
391

392
                        log_info("Copied host's /etc/locale.conf.");
3✔
393
                        return 0;
394
                }
395
        }
396

397
        r = prompt_locale(rfd, mute_console_link);
19✔
398
        if (r < 0)
19✔
399
                return r;
400

401
        if (!isempty(arg_locale))
19✔
402
                locales[i++] = strjoina("LANG=", arg_locale);
30✔
403
        if (!isempty(arg_locale_messages) && !streq_ptr(arg_locale_messages, arg_locale))
24✔
404
                locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages);
25✔
405

406
        if (i == 0)
19✔
407
                return 0;
408

409
        locales[i] = NULL;
7✔
410

411
        r = write_env_file(
7✔
412
                        pfd,
413
                        f,
414
                        /* headers= */ NULL,
415
                        locales,
416
                        WRITE_ENV_FILE_LABEL);
417
        if (r < 0)
7✔
418
                return log_error_errno(r, "Failed to write /etc/locale.conf: %m");
×
419

420
        log_info("/etc/locale.conf written.");
7✔
421
        return 1;
422
}
423

424
static int keymap_is_ok(const char* name, void *userdata) {
5✔
425
        int rfd = ASSERT_FD(PTR_TO_FD(userdata)), r;
5✔
426

427
        r = dir_fd_is_root(rfd);
5✔
428
        if (r < 0)
5✔
429
                log_debug_errno(r, "Unable to determine if operating on host root directory, assuming we are: %m");
×
430

431
        return r != 0 ? keymap_exists(name) > 0 : keymap_is_valid(name);
5✔
432
}
433

434
static int prompt_keymap(int rfd, sd_varlink **mute_console_link) {
27✔
435
        _cleanup_strv_free_ char **kmaps = NULL;
27✔
436
        int r;
27✔
437

438
        assert(rfd >= 0);
27✔
439

440
        if (arg_keymap)
27✔
441
                return 0;
442

443
        _cleanup_free_ char *km = NULL;
24✔
444
        r = read_credential("firstboot.keymap", (void**) &km, NULL);
24✔
445
        if (r < 0)
24✔
446
                log_debug_errno(r, "Failed to read credential firstboot.keymap, ignoring: %m");
23✔
447
        else if (!keymap_is_valid(km))
1✔
448
                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap '%s' supplied via credential is not valid, ignoring.", km);
×
449
        else {
450
                log_debug("Acquired keymap from credential.");
1✔
451
                arg_keymap = TAKE_PTR(km);
1✔
452
                return 0;
1✔
453
        }
454

455
        bool b;
23✔
456
        if (arg_prompt_keymap_auto) {
23✔
457
                _cleanup_free_ char *ttyname = NULL;
1✔
458

459
                r = getttyname_harder(STDOUT_FILENO, &ttyname);
1✔
460
                if (r < 0) {
1✔
461
                        log_debug_errno(r, "Cannot determine TTY we are connected, ignoring: %m");
1✔
462
                        b = false; /* if we can't resolve this, it's probably not a VT */
463
                } else {
464
                        b = tty_is_vc_resolve(ttyname);
×
465
                        log_debug("Detected connection to local console: %s", yes_no(b));
×
466
                }
467
        } else
468
                b = arg_prompt_keymap;
22✔
469
        if (!b) {
23✔
470
                log_debug("Prompting for keymap was not requested.");
22✔
471
                return 0;
472
        }
473

474
        if (headless_skips_prompt_for("keymap"))
1✔
475
                return 0;
476

477
        r = get_keymaps(&kmaps);
1✔
478
        if (r == -ENOENT) /* no keymaps installed */
1✔
479
                return log_debug_errno(r, "No keymaps are installed.");
×
480
        if (r < 0)
1✔
481
                return log_error_errno(r, "Failed to read keymaps: %m");
×
482

483
        print_welcome(rfd, mute_console_link);
1✔
484

485
        _cleanup_free_ char *prefill = NULL;
1✔
486
        (void) vconsole_keymap_from_efi(&prefill);
1✔
487

488
        return prompt_loop(
1✔
489
                        "Please enter the new keymap name or number",
490
                        GLYPH_KEYBOARD,
491
                        prefill,
492
                        kmaps,
493
                        /* accepted= */ NULL,
494
                        /* ellipsize_percentage= */ 60,
495
                        /* n_columns= */ 3,
496
                        /* column_width= */ 20,
497
                        keymap_is_ok,
498
                        /* refresh= */ NULL,
499
                        FD_TO_PTR(rfd),
1✔
500
                        PROMPT_MAY_SKIP|PROMPT_SHOW_MENU,
501
                        &arg_keymap);
502
}
503

504
static int process_keymap(int rfd, sd_varlink **mute_console_link) {
156✔
505
        _cleanup_close_ int pfd = -EBADF;
156✔
506
        _cleanup_free_ char *f = NULL;
156✔
507
        _cleanup_strv_free_ char **keymap = NULL;
×
508
        int r;
156✔
509

510
        assert(rfd >= 0);
156✔
511

512
        pfd = chase_and_open_parent_at(rfd, rfd, etc_vconsole_conf(),
156✔
513
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
514
                                       &f);
515
        if (pfd < 0)
156✔
516
                return log_error_errno(pfd, "Failed to chase /etc/vconsole.conf: %m");
×
517

518
        r = should_configure(pfd, f);
156✔
519
        if (r == 0)
156✔
520
                log_debug("Found /etc/vconsole.conf, assuming console has been configured.");
126✔
521
        if (r <= 0)
156✔
522
                return r;
523

524
        r = dir_fd_is_root(rfd);
30✔
525
        if (r < 0)
30✔
526
                return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
×
527

528
        if (arg_copy_keymap && r == 0) {
30✔
529
                r = copy_file_atomic_at(AT_FDCWD, etc_vconsole_conf(), pfd, f, 0644, /* copy_flags= */ 0);
3✔
530
                if (r != -ENOENT) {
3✔
531
                        if (r < 0)
3✔
532
                                return log_error_errno(r, "Failed to copy host's /etc/vconsole.conf: %m");
×
533

534
                        log_info("Copied host's /etc/vconsole.conf.");
3✔
535
                        return 0;
536
                }
537
        }
538

539
        r = prompt_keymap(rfd, mute_console_link);
27✔
540
        if (r == -ENOENT)
27✔
541
                return 0; /* don't fail if no keymaps are installed */
542
        if (r < 0)
27✔
543
                return r;
544

545
        if (isempty(arg_keymap))
161✔
546
                return 0;
547

548
        VCContext vc = {
5✔
549
                .keymap = arg_keymap,
550
        };
551
        _cleanup_(x11_context_clear) X11Context xc = {};
5✔
552

553
        r = vconsole_convert_to_x11(&vc, /* verify= */ NULL, &xc);
5✔
554
        if (r < 0)
5✔
555
                return log_error_errno(r, "Failed to convert keymap data: %m");
×
556

557
        r = vconsole_serialize(&vc, &xc, &keymap);
5✔
558
        if (r < 0)
5✔
559
                return log_error_errno(r, "Failed to serialize keymap data: %m");
×
560

561
        r = write_vconsole_conf(pfd, f, keymap);
5✔
562
        if (r < 0)
5✔
563
                return log_error_errno(r, "Failed to write /etc/vconsole.conf: %m");
×
564

565
        log_info("/etc/vconsole.conf written.");
5✔
566
        return 1;
567
}
568

569
static int timezone_is_ok(const char *name, void *userdata) {
1✔
570
        return timezone_is_valid(name, LOG_DEBUG);
1✔
571
}
572

573
static int prompt_timezone(int rfd, sd_varlink **mute_console_link) {
137✔
574
        _cleanup_strv_free_ char **zones = NULL;
137✔
575
        int r;
137✔
576

577
        assert(rfd >= 0);
137✔
578

579
        if (arg_timezone)
137✔
580
                return 0;
581

582
        _cleanup_free_ char *tz = NULL;
134✔
583
        r = read_credential("firstboot.timezone", (void**) &tz, NULL);
134✔
584
        if (r < 0)
134✔
585
                log_debug_errno(r, "Failed to read credential firstboot.timezone, ignoring: %m");
20✔
586
        else if (!timezone_is_valid(tz, LOG_DEBUG))
114✔
587
                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' supplied via credential is not valid, ignoring.", tz);
×
588
        else {
589
                log_debug("Acquired timezone from credential.");
114✔
590
                arg_timezone = TAKE_PTR(tz);
114✔
591
                return 0;
114✔
592
        }
593

594
        if (!arg_prompt_timezone) {
20✔
595
                log_debug("Prompting for timezone was not requested.");
19✔
596
                return 0;
597
        }
598

599
        if (headless_skips_prompt_for("timezone"))
1✔
600
                return 0;
601

602
        r = get_timezones(&zones);
1✔
603
        if (r < 0)
1✔
604
                return log_error_errno(r, "Cannot query timezone list: %m");
×
605

606
        print_welcome(rfd, mute_console_link);
1✔
607

608
        return prompt_loop(
1✔
609
                        "Please enter the new timezone name or number",
610
                        GLYPH_CLOCK,
611
                        /* prefill= */ NULL,
612
                        zones,
613
                        /* accepted= */ NULL,
614
                        /* ellipsize_percentage= */ 30,
615
                        /* n_columns= */ 3,
616
                        /* column_width= */ 20,
617
                        timezone_is_ok,
618
                        /* refresh= */ NULL,
619
                        FD_TO_PTR(rfd),
1✔
620
                        PROMPT_MAY_SKIP|PROMPT_SHOW_MENU,
621
                        &arg_timezone);
622
}
623

624
static int process_timezone(int rfd, sd_varlink **mute_console_link) {
156✔
625
        _cleanup_close_ int pfd = -EBADF;
156✔
626
        _cleanup_free_ char *f = NULL, *relpath = NULL;
156✔
627
        const char *e;
156✔
628
        int r;
156✔
629

630
        assert(rfd >= 0);
156✔
631

632
        pfd = chase_and_open_parent_at(rfd, rfd, etc_localtime(),
156✔
633
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
634
                                       &f);
635
        if (pfd < 0)
156✔
636
                return log_error_errno(pfd, "Failed to chase /etc/localtime: %m");
×
637

638
        r = should_configure(pfd, f);
156✔
639
        if (r == 0)
156✔
640
                log_debug("Found /etc/localtime, assuming timezone has been configured.");
16✔
641
        if (r <= 0)
156✔
642
                return r;
643

644
        r = dir_fd_is_root(rfd);
140✔
645
        if (r < 0)
140✔
646
                return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
×
647

648
        if (arg_copy_timezone && r == 0) {
140✔
649
                _cleanup_free_ char *s = NULL;
3✔
650

651
                r = readlink_malloc(etc_localtime(), &s);
3✔
652
                if (r != -ENOENT) {
3✔
653
                        if (r < 0)
3✔
654
                                return log_error_errno(r, "Failed to read host's /etc/localtime: %m");
×
655

656
                        r = symlinkat_atomic_full(s, pfd, f, SYMLINK_LABEL);
3✔
657
                        if (r < 0)
3✔
658
                                return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
×
659

660
                        log_info("Copied host's /etc/localtime.");
3✔
661
                        return 0;
662
                }
663
        }
664

665
        r = prompt_timezone(rfd, mute_console_link);
137✔
666
        if (r < 0)
137✔
667
                return r;
668

669
        if (isempty(arg_timezone))
274✔
670
                return 0;
671

672
        e = strjoina("/usr/share/zoneinfo/", arg_timezone);
590✔
673
        r = path_make_relative_parent(etc_localtime(), e, &relpath);
118✔
674
        if (r < 0)
118✔
675
                return r;
676

677
        r = symlinkat_atomic_full(relpath, pfd, f, SYMLINK_LABEL);
118✔
678
        if (r < 0)
118✔
679
                return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
×
680

681
        log_info("/etc/localtime written");
118✔
682
        return 0;
683
}
684

685
static int hostname_is_ok(const char *name, void *userdata) {
1✔
686
        return hostname_is_valid(name, VALID_HOSTNAME_TRAILING_DOT);
1✔
687
}
688

689
static int prompt_hostname(int rfd, sd_varlink **mute_console_link) {
142✔
690
        int r;
142✔
691

692
        assert(rfd >= 0);
142✔
693

694
        if (arg_hostname)
142✔
695
                return 0;
142✔
696

697
        _cleanup_free_ char *hn = NULL;
135✔
698
        r = read_credential("firstboot.hostname", (void**) &hn, NULL);
135✔
699
        if (r < 0)
135✔
700
                log_debug_errno(r, "Failed to read credential firstboot.hostname, ignoring: %m");
134✔
701
        else if (!hostname_is_valid(hn, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK|VALID_HOSTNAME_WORD_TOKEN))
1✔
702
                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Hostname '%s' supplied via credential is not valid, ignoring.", hn);
×
703
        else {
704
                log_debug("Acquired hostname from credentials.");
1✔
705
                arg_hostname = TAKE_PTR(hn);
1✔
706
                hostname_cleanup(arg_hostname);
1✔
707
                return 0;
708
        }
709

710
        if (!arg_prompt_hostname) {
134✔
711
                log_debug("Prompting for hostname was not requested.");
133✔
712
                return 0;
713
        }
714

715
        if (headless_skips_prompt_for("hostname"))
1✔
716
                return 0;
717

718
        print_welcome(rfd, mute_console_link);
1✔
719

720
        r = prompt_loop("Please enter the new hostname",
2✔
721
                        GLYPH_LABEL,
722
                        /* prefill= */ NULL,
723
                        /* menu= */ NULL,
724
                        /* accepted= */ NULL,
725
                        /* ellipsize_percentage= */ 100,
726
                        /* n_columns= */ 3,
727
                        /* column_width= */ 20,
728
                        hostname_is_ok,
729
                        /* refresh= */ NULL,
730
                        FD_TO_PTR(rfd),
1✔
731
                        PROMPT_MAY_SKIP,
732
                        &arg_hostname);
733
        if (r < 0)
1✔
734
                return r;
735

736
        if (arg_hostname)
1✔
737
                hostname_cleanup(arg_hostname);
1✔
738

739
        return 0;
740
}
741

742
static int process_hostname(int rfd, sd_varlink **mute_console_link) {
156✔
743
        _cleanup_close_ int pfd = -EBADF;
156✔
744
        _cleanup_free_ char *f = NULL;
156✔
745
        int r;
156✔
746

747
        assert(rfd >= 0);
156✔
748

749
        pfd = chase_and_open_parent_at(rfd, rfd, etc_hostname(), CHASE_MKDIR_0755|CHASE_WARN, &f);
156✔
750
        if (pfd < 0)
156✔
751
                return log_error_errno(pfd, "Failed to chase /etc/hostname: %m");
×
752

753
        r = should_configure(pfd, f);
156✔
754
        if (r == 0)
156✔
755
                log_debug("Found /etc/hostname, assuming hostname has been configured.");
14✔
756
        if (r <= 0)
156✔
757
                return r;
758

759
        r = prompt_hostname(rfd, mute_console_link);
142✔
760
        if (r < 0)
142✔
761
                return r;
762

763
        if (isempty(arg_hostname))
165✔
764
                return 0;
765

766
        /* On running systems we have a machine ID, so resolve any '?'/'$' wildcards now and persist them.
767
         * This "freezes" the name, so later word list updates do not change it. When operating on an offline
768
         * image (--root=/--image=) the target's machine ID is not known yet, so write the template verbatim
769
         * and let it be resolved on each first boot. */
770
        const char *hostname = arg_hostname;
9✔
771
        _cleanup_free_ char *resolved = NULL;
9✔
772
        if (!arg_root) {
9✔
773
                r = hostname_substitute_wildcards(arg_hostname, &resolved);
1✔
774
                if (r < 0)
1✔
775
                        log_warning_errno(r, "Failed to resolve wildcards in hostname '%s', writing it verbatim: %m", arg_hostname);
×
776
                else if (!hostname_is_valid(resolved, VALID_HOSTNAME_TRAILING_DOT))
1✔
777
                        log_warning("Resolved hostname '%s' is invalid, writing template '%s' verbatim instead.", resolved, arg_hostname);
×
778
                else
779
                        hostname = resolved;
780
        }
781

782
        r = write_string_file_at(pfd, f, hostname,
9✔
783
                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
784
        if (r < 0)
9✔
785
                return log_error_errno(r, "Failed to write /etc/hostname: %m");
×
786

787
        log_info("/etc/hostname written.");
9✔
788
        return 0;
789
}
790

791
static int process_machine_id(int rfd) {
155✔
792
        _cleanup_close_ int pfd = -EBADF;
155✔
793
        _cleanup_free_ char *f = NULL;
155✔
794
        int r;
155✔
795

796
        assert(rfd >= 0);
155✔
797

798
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/machine-id",
155✔
799
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
800
                                       &f);
801
        if (pfd < 0)
155✔
802
                return log_error_errno(pfd, "Failed to chase /etc/machine-id: %m");
×
803

804
        r = should_configure(pfd, f);
155✔
805
        if (r == 0)
155✔
806
                log_debug("Found /etc/machine-id, assuming machine-id has been configured.");
123✔
807
        if (r <= 0)
155✔
808
                return r;
809

810
        if (sd_id128_is_null(arg_machine_id)) {
32✔
811
                log_debug("Initialization of machine-id was not requested, skipping.");
29✔
812
                return 0;
813
        }
814

815
        r = write_string_file_at(pfd, "machine-id", SD_ID128_TO_STRING(arg_machine_id),
3✔
816
                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
817
        if (r < 0)
3✔
818
                return log_error_errno(r, "Failed to write /etc/machine-id: %m");
×
819

820
        log_info("/etc/machine-id written.");
3✔
821
        return 0;
822
}
823

824
static int process_machine_tags(int rfd) {
155✔
825
        int r;
155✔
826

827
        assert(rfd >= 0);
155✔
828

829
        _cleanup_free_ char *f = NULL;
155✔
830
        _cleanup_close_ int pfd = chase_and_open_parent_at(
310✔
831
                        /* root_fd= */ rfd,
832
                        /* dir_fd= */ rfd,
833
                        "/etc/machine-info",
834
                        CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
835
                        &f);
836
        if (pfd < 0)
155✔
837
                return log_error_errno(pfd, "Failed to chase /etc/machine-info parent: %m");
×
838

839
        r = should_configure(pfd, f);
155✔
840
        if (r == 0)
155✔
841
                log_debug("Found /etc/machine-info, assuming machine tags have been configured.");
×
842
        if (r <= 0)
155✔
843
                return r;
844

845
        if (!arg_machine_tags) {
155✔
846
                _cleanup_free_ char *tags = NULL;
154✔
847
                r = read_credential("firstboot.machine-tags", (void**) &tags, /* ret_size= */ NULL);
154✔
848
                if (r < 0)
154✔
849
                        log_debug_errno(r, "Failed to read credential firstboot.machine-tags, ignoring: %m");
154✔
850
                else {
851
                        _cleanup_strv_free_ char **l = NULL;
×
852
                        r = machine_tags_from_string(tags, /* graceful= */ false, &l);
2✔
853
                        if (r < 0)
2✔
854
                                log_warning_errno(r, "Failed to parse machine tags '%s', ignoring credential: %m", tags);
2✔
855
                        else {
856
                                strv_free_and_replace(arg_machine_tags, l);
1✔
857
                                log_debug("Acquired machine tags list from credentials.");
1✔
858
                        }
859
                }
860
        }
861

862
        /* NB: We do not prompt for machine tags, at least not for now */
863

864
        if (!arg_machine_tags) {
155✔
865
                log_debug("Initialization of machine tags was not requested, skipping.");
153✔
866
                return 0;
867
        }
868

869
        _cleanup_free_ char *j = strv_join(arg_machine_tags, ":");
4✔
870
        if (!j)
2✔
871
                return log_oom();
×
872

873
        _cleanup_free_ char *c = strjoin("TAGS=\"", j, "\"\n");
4✔
874
        if (!c)
2✔
875
                return log_oom();
×
876

877
        r = write_string_file_at(
2✔
878
                        pfd,
879
                        "machine-info",
880
                        c,
881
                        WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
882
        if (r < 0)
2✔
883
                return log_error_errno(r, "Failed to write /etc/machine-info: %m");
×
884

885
        log_info("/etc/machine-info written.");
2✔
886
        return 0;
887
}
888

889
static int prompt_root_password(int rfd, sd_varlink **mute_console_link) {
37✔
890
        const char *msg1, *msg2;
37✔
891
        int r;
37✔
892

893
        assert(rfd >= 0);
37✔
894

895
        if (arg_root_password)
37✔
896
                return 0;
897

898
        if (get_credential_user_password("root", &arg_root_password, &arg_root_password_is_hashed) >= 0)
28✔
899
                return 0;
900

901
        if (!arg_prompt_root_password) {
27✔
902
                log_debug("Prompting for root password was not requested.");
26✔
903
                return 0;
904
        }
905

906
        if (headless_skips_prompt_for("root password"))
1✔
907
                return 0;
908

909
        print_welcome(rfd, mute_console_link);
1✔
910

911
        msg1 = "Please enter the new root password (empty to skip):";
1✔
912
        msg2 = "Please enter the new root password again:";
1✔
913

914
        suggest_passwords();
1✔
915

916
        for (;;) {
×
917
                _cleanup_strv_free_erase_ char **a = NULL, **b = NULL;
1✔
918
                _cleanup_free_ char *error = NULL;
1✔
919

920
                AskPasswordRequest req = {
1✔
921
                        .tty_fd = -EBADF,
922
                        .message = msg1,
923
                        .until = USEC_INFINITY,
924
                        .hup_fd = -EBADF,
925
                };
926

927
                r = ask_password_tty(&req, /* flags= */ 0, &a);
1✔
928
                if (r < 0)
1✔
929
                        return log_error_errno(r, "Failed to query root password: %m");
×
930
                if (strv_length(a) != 1)
1✔
931
                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
932
                                               "Received multiple passwords, where we expected one.");
933

934
                if (isempty(*a)) {
1✔
935
                        log_info("No password entered, skipping.");
1✔
936
                        break;
937
                }
938

939
                r = check_password_quality(*a, /* old= */ NULL, "root", &error);
×
940
                if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
×
941
                        log_warning("Password quality check is not supported, proceeding anyway.");
×
942
                else if (r < 0)
×
943
                        return log_error_errno(r, "Failed to check password quality: %m");
×
944
                else if (r == 0)
×
945
                        log_warning("Password is weak, accepting anyway: %s", error);
×
946

947
                req.message = msg2;
×
948

949
                r = ask_password_tty(&req, /* flags= */ 0, &b);
×
950
                if (r < 0)
×
951
                        return log_error_errno(r, "Failed to query root password: %m");
×
952
                if (strv_length(b) != 1)
×
953
                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
954
                                               "Received multiple passwords, where we expected one.");
955

956
                if (!streq(*a, *b)) {
×
957
                        log_error("Entered passwords did not match, please try again.");
×
958
                        continue;
×
959
                }
960

961
                arg_root_password = TAKE_PTR(*a);
×
962
                break;
×
963
        }
964

965
        return 0;
1✔
966
}
967

968
static int find_shell(int rfd, const char *path) {
9✔
969
        int r;
9✔
970

971
        assert(path);
9✔
972

973
        if (!valid_shell(path))
9✔
974
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a valid shell", path);
×
975

976
        r = chaseat(rfd, rfd, path, /* flags= */ 0, /* ret_path= */ NULL, /* ret_fd= */ NULL);
9✔
977
        if (r < 0)
9✔
978
                return log_error_errno(r, "Failed to resolve shell %s: %m", path);
2✔
979

980
        return 0;
981
}
982

983
static int shell_is_ok(const char *path, void *userdata) {
2✔
984
        int rfd = ASSERT_FD(PTR_TO_FD(userdata));
2✔
985

986
        return find_shell(rfd, path) >= 0;
2✔
987
}
988

989
static int prompt_root_shell(int rfd, sd_varlink **mute_console_link) {
38✔
990
        int r;
38✔
991

992
        assert(rfd >= 0);
38✔
993

994
        if (arg_root_shell)
38✔
995
                return 0;
996

997
        r = read_credential("passwd.shell.root", (void**) &arg_root_shell, NULL);
33✔
998
        if (r < 0)
33✔
999
                log_debug_errno(r, "Failed to read credential passwd.shell.root, ignoring: %m");
31✔
1000
        else {
1001
                r = find_shell(rfd, arg_root_shell);
2✔
1002
                if (r < 0)
2✔
1003
                        return r;
1004

1005
                log_debug("Acquired root shell from credential.");
1✔
1006
                return 0;
1007
        }
1008

1009
        if (!arg_prompt_root_shell) {
31✔
1010
                log_debug("Prompting for root shell was not requested.");
29✔
1011
                return 0;
1012
        }
1013

1014
        if (headless_skips_prompt_for("root shell"))
2✔
1015
                return 0;
1016

1017
        print_welcome(rfd, mute_console_link);
2✔
1018

1019
        return prompt_loop(
2✔
1020
                        "Please enter the new root shell",
1021
                        GLYPH_SHELL,
1022
                        /* prefill= */ NULL,
1023
                        /* menu= */ NULL,
1024
                        /* accepted= */ NULL,
1025
                        /* ellipsize_percentage= */ 0,
1026
                        /* n_columns= */ 3,
1027
                        /* column_width= */ 20,
1028
                        shell_is_ok,
1029
                        /* refresh= */ NULL,
1030
                        FD_TO_PTR(rfd),
2✔
1031
                        PROMPT_MAY_SKIP,
1032
                        &arg_root_shell);
1033
}
1034

1035
static int write_root_passwd(int rfd, int etc_fd, const char *password, const char *shell) {
15✔
1036
        _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
15✔
1037
        _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
15✔
1038
        int r;
15✔
1039
        bool found = false;
15✔
1040

1041
        r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp);
15✔
1042
        if (r < 0)
15✔
1043
                return r;
1044

1045
        r = xfopenat(etc_fd, "passwd", "re", O_NOFOLLOW, &original);
15✔
1046
        if (r < 0 && r != -ENOENT)
15✔
1047
                return r;
1048

1049
        if (original) {
15✔
1050
                struct passwd *i;
4✔
1051

1052
                r = copy_rights(fileno(original), fileno(passwd));
4✔
1053
                if (r < 0)
4✔
UNCOV
1054
                        return r;
×
1055

1056
                while ((r = fgetpwent_sane(original, &i)) > 0) {
7✔
1057

1058
                        if (streq(i->pw_name, "root")) {
3✔
1059
                                if (password)
3✔
1060
                                        i->pw_passwd = (char *) password;
2✔
1061
                                if (shell)
3✔
1062
                                        i->pw_shell = (char *) shell;
2✔
1063
                                found = true;
1064
                        }
1065

1066
                        r = putpwent_sane(i, passwd);
3✔
1067
                        if (r < 0)
3✔
1068
                                return r;
1069
                }
1070
                if (r < 0)
4✔
1071
                        return r;
1072

1073
        } else {
1074
                r = fchmod(fileno(passwd), 0644);
11✔
1075
                if (r < 0)
11✔
UNCOV
1076
                        return -errno;
×
1077
        }
1078

1079
        if (!found) {
4✔
1080
                struct passwd root = {
12✔
1081
                        .pw_name = (char *) "root",
1082
                        .pw_passwd = (char *) (password ?: PASSWORD_SEE_SHADOW),
12✔
1083
                        .pw_uid = 0,
1084
                        .pw_gid = 0,
1085
                        .pw_gecos = (char *) "Super User",
1086
                        .pw_dir = (char *) "/root",
1087
                        .pw_shell = (char *) (shell ?: default_root_shell_at(rfd)),
6✔
1088
                };
1089

1090
                r = putpwent_sane(&root, passwd);
12✔
1091
                if (r < 0)
12✔
UNCOV
1092
                        return r;
×
1093
        }
1094

1095
        r = fflush_sync_and_check(passwd);
15✔
1096
        if (r < 0)
15✔
1097
                return r;
1098

1099
        r = renameat_and_apply_smack_floor_label(etc_fd, passwd_tmp, etc_fd, "passwd");
15✔
1100
        if (r < 0)
15✔
UNCOV
1101
                return r;
×
1102

1103
        return 0;
1104
}
1105

1106
static int write_root_shadow(int etc_fd, const char *hashed_password) {
15✔
1107
        _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
15✔
1108
        _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
15✔
1109
        int r;
15✔
1110
        bool found = false;
15✔
1111

1112
        r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp);
15✔
1113
        if (r < 0)
15✔
1114
                return r;
1115

1116
        r = xfopenat(etc_fd, "shadow", "re", O_NOFOLLOW, &original);
15✔
1117
        if (r < 0 && r != -ENOENT)
15✔
1118
                return r;
1119

1120
        if (original) {
15✔
1121
                struct spwd *i;
4✔
1122

1123
                r = copy_rights(fileno(original), fileno(shadow));
4✔
1124
                if (r < 0)
4✔
UNCOV
1125
                        return r;
×
1126

1127
                while ((r = fgetspent_sane(original, &i)) > 0) {
7✔
1128

1129
                        if (streq(i->sp_namp, "root")) {
3✔
1130
                                if (hashed_password) {
3✔
1131
                                        i->sp_pwdp = (char *) hashed_password;
2✔
1132
                                        i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
2✔
1133
                                }
1134
                                found = true;
1135
                        }
1136

1137
                        r = putspent_sane(i, shadow);
3✔
1138
                        if (r < 0)
3✔
1139
                                return r;
1140
                }
1141
                if (r < 0)
4✔
1142
                        return r;
1143

1144
        } else {
1145
                r = fchmod(fileno(shadow), 0000);
11✔
1146
                if (r < 0)
11✔
UNCOV
1147
                        return -errno;
×
1148
        }
1149

1150
        if (!found) {
4✔
1151
                struct spwd root = {
36✔
1152
                        .sp_namp = (char*) "root",
1153
                        .sp_pwdp = (char *) (hashed_password ?: PASSWORD_LOCKED_AND_INVALID),
12✔
1154
                        .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY),
12✔
1155
                        .sp_min = -1,
1156
                        .sp_max = -1,
1157
                        .sp_warn = -1,
1158
                        .sp_inact = -1,
1159
                        .sp_expire = -1,
1160
                        .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
1161
                };
1162

1163
                r = putspent_sane(&root, shadow);
12✔
1164
                if (r < 0)
12✔
UNCOV
1165
                        return r;
×
1166
        }
1167

1168
        r = fflush_sync_and_check(shadow);
15✔
1169
        if (r < 0)
15✔
1170
                return r;
1171

1172
        r = renameat_and_apply_smack_floor_label(etc_fd, shadow_tmp, etc_fd, "shadow");
15✔
1173
        if (r < 0)
15✔
UNCOV
1174
                return r;
×
1175

1176
        return 0;
1177
}
1178

1179
static int process_root_account(int rfd, sd_varlink **mute_console_link) {
156✔
1180
        _cleanup_close_ int pfd = -EBADF;
156✔
UNCOV
1181
        _cleanup_(release_lock_file) LockFile lock = LOCK_FILE_INIT;
×
1182
        _cleanup_(erase_and_freep) char *_hashed_password = NULL;
156✔
1183
        const char *password, *hashed_password;
156✔
1184
        int k = 0, r;
156✔
1185

1186
        assert(rfd >= 0);
156✔
1187

1188
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/passwd",
156✔
1189
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
1190
                                       NULL);
1191
        if (pfd < 0)
156✔
UNCOV
1192
                return log_error_errno(pfd, "Failed to chase /etc/passwd: %m");
×
1193

1194
        /* Ensure that passwd and shadow are in the same directory and are not symlinks. */
1195

1196
        FOREACH_STRING(s, "passwd", "shadow") {
468✔
1197
                r = verify_regular_at(pfd, s, /* follow= */ false);
312✔
1198
                if (r < 0 && r != -ENOENT)
312✔
UNCOV
1199
                        return log_error_errno(r, "Verification of /etc/%s being regular file failed: %m", s);
×
1200

1201
                r = should_configure(pfd, s);
312✔
1202
                if (r < 0)
312✔
1203
                        return r;
1204

1205
                k += r;
312✔
1206
        }
1207

1208
        if (k == 0) {
156✔
1209
                log_debug("Found /etc/passwd and /etc/shadow, assuming root account has been initialized.");
118✔
1210
                return 0;
1211
        }
1212

1213
        r = make_lock_file_at(pfd, ETC_PASSWD_LOCK_FILENAME, LOCK_EX, &lock);
38✔
1214
        if (r < 0)
38✔
1215
                return log_error_errno(r, "Failed to take a lock on /etc/passwd: %m");
×
1216

1217
        k = dir_fd_is_root(rfd);
38✔
1218
        if (k < 0)
38✔
UNCOV
1219
                return log_error_errno(k, "Failed to check if directory file descriptor is root: %m");
×
1220

1221
        if (arg_copy_root_shell && k == 0) {
38✔
1222
                _cleanup_free_ struct passwd *p = NULL;
2✔
1223

1224
                r = getpwnam_malloc("root", &p);
2✔
1225
                if (r < 0)
2✔
1226
                        return log_error_errno(r, "Failed to find passwd entry for root: %m");
×
1227

1228
                r = free_and_strdup(&arg_root_shell, p->pw_shell);
2✔
1229
                if (r < 0)
2✔
UNCOV
1230
                        return log_oom();
×
1231
        }
1232

1233
        r = prompt_root_shell(rfd, mute_console_link);
38✔
1234
        if (r < 0)
38✔
1235
                return r;
1236

1237
        if (arg_copy_root_password && k == 0) {
37✔
1238
                struct spwd *p;
2✔
1239

1240
                errno = 0;
2✔
1241
                p = getspnam("root");
2✔
1242
                if (!p)
2✔
1243
                        return log_error_errno(errno_or_else(EIO), "Failed to find shadow entry for root: %m");
×
1244

1245
                r = free_and_strdup(&arg_root_password, p->sp_pwdp);
2✔
1246
                if (r < 0)
2✔
UNCOV
1247
                        return log_oom();
×
1248

1249
                arg_root_password_is_hashed = true;
2✔
1250
        }
1251

1252
        r = prompt_root_password(rfd, mute_console_link);
37✔
1253
        if (r < 0)
37✔
1254
                return r;
1255

1256
        if (arg_root_password && arg_root_password_is_hashed) {
37✔
1257
                password = PASSWORD_SEE_SHADOW;
1258
                hashed_password = arg_root_password;
1259
        } else if (arg_root_password) {
31✔
1260
                r = hash_password(arg_root_password, &_hashed_password);
4✔
1261
                if (r < 0)
4✔
UNCOV
1262
                        return log_error_errno(r, "Failed to hash password: %m");
×
1263

1264
                password = PASSWORD_SEE_SHADOW;
4✔
1265
                hashed_password = _hashed_password;
4✔
1266

1267
        } else if (arg_delete_root_password) {
27✔
1268
                password = PASSWORD_SEE_SHADOW;
1269
                hashed_password = PASSWORD_NONE;
1270
        } else if (!arg_root_password && arg_prompt_root_password) {
26✔
1271
                /* If the user was prompted, but no password was supplied, lock the account. */
1272
                password = PASSWORD_SEE_SHADOW;
1273
                hashed_password = PASSWORD_LOCKED_AND_INVALID;
1274
        } else
1275
                /* Leave the password as is. */
1276
                password = hashed_password = NULL;
25✔
1277

1278
        /* Don't create/modify passwd and shadow if there's nothing to do. */
1279
        if (!(password || hashed_password || arg_root_shell)) {
37✔
1280
                log_debug("Initialization of root account was not requested, skipping.");
22✔
1281
                return 0;
1282
        }
1283

1284
        r = write_root_passwd(rfd, pfd, password, arg_root_shell);
15✔
1285
        if (r < 0)
15✔
UNCOV
1286
                return log_error_errno(r, "Failed to write /etc/passwd: %m");
×
1287

1288
        log_info("/etc/passwd written.");
15✔
1289

1290
        r = write_root_shadow(pfd, hashed_password);
15✔
1291
        if (r < 0)
15✔
UNCOV
1292
                return log_error_errno(r, "Failed to write /etc/shadow: %m");
×
1293

1294
        log_info("/etc/shadow written.");
15✔
1295
        return 0;
1296
}
1297

1298
static int process_kernel_cmdline(int rfd) {
155✔
1299
        _cleanup_close_ int pfd = -EBADF;
155✔
1300
        _cleanup_free_ char *f = NULL;
155✔
1301
        int r;
155✔
1302

1303
        assert(rfd >= 0);
155✔
1304

1305
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/kernel/cmdline",
155✔
1306
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
1307
                                       &f);
1308
        if (pfd < 0)
155✔
UNCOV
1309
                return log_error_errno(pfd, "Failed to chase /etc/kernel/cmdline: %m");
×
1310

1311
        r = should_configure(pfd, f);
155✔
1312
        if (r == 0)
155✔
1313
                log_debug("Found /etc/kernel/cmdline, assuming kernel command line has been configured.");
1✔
1314
        if (r <= 0)
155✔
1315
                return r;
1316

1317
        if (!arg_kernel_cmdline) {
154✔
1318
                log_debug("Creation of /etc/kernel/cmdline was not requested, skipping.");
152✔
1319
                return 0;
1320
        }
1321

1322
        r = write_string_file_at(pfd, "cmdline", arg_kernel_cmdline,
2✔
1323
                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
1324
        if (r < 0)
2✔
UNCOV
1325
                return log_error_errno(r, "Failed to write /etc/kernel/cmdline: %m");
×
1326

1327
        log_info("/etc/kernel/cmdline written.");
2✔
1328
        return 0;
1329
}
1330

1331
static int reset_one(int rfd, const char *path) {
6✔
1332
        _cleanup_close_ int pfd = -EBADF;
6✔
1333
        _cleanup_free_ char *f = NULL;
6✔
1334

1335
        assert(rfd >= 0);
6✔
1336
        assert(path);
6✔
1337

1338
        pfd = chase_and_open_parent_at(rfd, rfd, path, CHASE_WARN|CHASE_NOFOLLOW, &f);
6✔
1339
        if (pfd == -ENOENT)
6✔
1340
                return 0;
1341
        if (pfd < 0)
6✔
UNCOV
1342
                return log_error_errno(pfd, "Failed to resolve %s: %m", path);
×
1343

1344
        if (unlinkat(pfd, f, 0) < 0)
6✔
UNCOV
1345
                return errno == ENOENT ? 0 : log_error_errno(errno, "Failed to remove %s: %m", path);
×
1346

1347
        log_info("Removed %s", path);
6✔
1348
        return 0;
1349
}
1350

1351
static int process_reset(int rfd) {
156✔
1352
        int r;
156✔
1353

1354
        assert(rfd >= 0);
156✔
1355

1356
        if (!arg_reset)
156✔
1357
                return 0;
1358

1359
        FOREACH_STRING(p,
7✔
1360
                       etc_locale_conf(),
1361
                       etc_vconsole_conf(),
1362
                       etc_hostname(),
1363
                       "/etc/machine-id",
1364
                       "/etc/kernel/cmdline",
1365
                       etc_localtime()) {
1366
                r = reset_one(rfd, p);
6✔
1367
                if (r < 0)
6✔
UNCOV
1368
                        return r;
×
1369
        }
1370

1371
        return 0;
1✔
1372
}
1373

1374
static int help(void) {
×
1375
        _cleanup_(table_unrefp) Table *options = NULL;
×
UNCOV
1376
        int r;
×
1377

1378
        r = option_parser_get_help_table(&options);
×
1379
        if (r < 0)
×
1380
                return r;
1381

1382
        help_cmdline("[OPTIONS...]");
×
1383
        help_abstract("Configures basic settings of the system.");
×
UNCOV
1384
        help_section("Options");
×
1385

1386
        r = table_print_or_warn(options);
×
UNCOV
1387
        if (r < 0)
×
1388
                return r;
1389

UNCOV
1390
        help_man_page_reference("systemd-firstboot", "1");
×
1391
        return 0;
1392
}
1393

1394
static int parse_argv(int argc, char *argv[]) {
161✔
1395
        assert(argc >= 0);
161✔
1396
        assert(argv);
161✔
1397

1398
        OptionParser opts = { argc, argv };
161✔
1399
        int r;
161✔
1400

1401
        FOREACH_OPTION_OR_RETURN(c, &opts)
1,172✔
1402
                switch (c) {
693✔
1403

1404
                OPTION_COMMON_HELP:
×
UNCOV
1405
                        return help();
×
1406

UNCOV
1407
                OPTION_COMMON_VERSION:
×
UNCOV
1408
                        return version();
×
1409

1410
                OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
47✔
1411
                        r = parse_path_argument(opts.arg, true, &arg_root);
47✔
1412
                        if (r < 0)
47✔
1413
                                return r;
1414
                        break;
1415

UNCOV
1416
                OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
×
UNCOV
1417
                        r = parse_path_argument(opts.arg, false, &arg_image);
×
1418
                        if (r < 0)
×
1419
                                return r;
1420
                        break;
1421

UNCOV
1422
                OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
×
UNCOV
1423
                        r = parse_image_policy_argument(opts.arg, &arg_image_policy);
×
UNCOV
1424
                        if (r < 0)
×
1425
                                return r;
1426
                        break;
1427

1428
                OPTION_LONG("locale", "LOCALE", "Set primary locale (LANG=)"):
5✔
1429
                        r = free_and_strdup_warn(&arg_locale, opts.arg);
5✔
1430
                        if (r < 0)
5✔
1431
                                return r;
1432
                        break;
1433

1434
                OPTION_LONG("locale-messages", "LOCALE", "Set message locale (LC_MESSAGES=)"):
4✔
1435
                        r = free_and_strdup_warn(&arg_locale_messages, opts.arg);
4✔
1436
                        if (r < 0)
4✔
1437
                                return r;
1438
                        break;
1439

1440
                OPTION_LONG("keymap", "KEYMAP", "Set keymap"):
4✔
1441
                        if (!keymap_is_valid(opts.arg))
4✔
UNCOV
1442
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1443
                                                       "Keymap %s is not valid.", opts.arg);
1444

1445
                        r = free_and_strdup_warn(&arg_keymap, opts.arg);
4✔
1446
                        if (r < 0)
4✔
1447
                                return r;
1448
                        break;
1449

1450
                OPTION_LONG("timezone", "TIMEZONE", "Set timezone"):
5✔
1451
                        if (!timezone_is_valid(opts.arg, LOG_ERR))
5✔
1452
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
1453
                                                       "Timezone %s is not valid.", opts.arg);
1454

1455
                        r = free_and_strdup_warn(&arg_timezone, opts.arg);
4✔
1456
                        if (r < 0)
4✔
1457
                                return r;
1458
                        break;
1459

1460
                OPTION_LONG("hostname", "NAME", "Set hostname"):
9✔
1461
                        if (!hostname_is_valid(opts.arg, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK|VALID_HOSTNAME_WORD_TOKEN))
9✔
1462
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
1463
                                                       "Host name %s is not valid.", opts.arg);
1464

1465
                        r = free_and_strdup_warn(&arg_hostname, opts.arg);
8✔
1466
                        if (r < 0)
8✔
1467
                                return r;
1468

1469
                        hostname_cleanup(arg_hostname);
8✔
1470
                        break;
1471

1472
                OPTION_LONG("setup-machine-id", NULL, "Set a random machine ID"):
1✔
1473
                        r = sd_id128_randomize(&arg_machine_id);
1✔
1474
                        if (r < 0)
1✔
UNCOV
1475
                                return log_error_errno(r, "Failed to generate randomized machine ID: %m");
×
1476
                        break;
1477

1478
                OPTION_LONG("machine-id", "ID", "Set specified machine ID"):
4✔
1479
                        r = sd_id128_from_string(opts.arg, &arg_machine_id);
4✔
1480
                        if (r < 0)
4✔
1481
                                return log_error_errno(r, "Failed to parse machine id %s.", opts.arg);
1✔
1482
                        break;
1483

1484
                OPTION_LONG("machine-tags", "TAG[:…]", "Set machine tags"): {
2✔
1485
                        _cleanup_strv_free_ char **tags = NULL;
1✔
1486
                        r = machine_tags_from_string(opts.arg, /* graceful= */ false, &tags);
2✔
1487
                        if (r < 0)
2✔
1488
                                return log_error_errno(r, "Failed to parse machine tags '%s': %m", opts.arg);
1✔
1489

1490
                        strv_free_and_replace(arg_machine_tags, tags);
1✔
1491
                        break;
1✔
1492
                }
1493

1494
                OPTION_LONG("root-password", "PASSWORD", "Set root password from plaintext password"):
4✔
1495
                        r = free_and_strdup_warn(&arg_root_password, opts.arg);
4✔
1496
                        if (r < 0)
4✔
1497
                                return r;
1498

1499
                        arg_root_password_is_hashed = false;
4✔
1500
                        break;
4✔
1501

1502
                OPTION_LONG("root-password-file", "FILE", "Set root password from file"):
1✔
1503
                        arg_root_password = mfree(arg_root_password);
1✔
1504

1505
                        r = read_one_line_file(opts.arg, &arg_root_password);
1✔
1506
                        if (r < 0)
1✔
UNCOV
1507
                                return log_error_errno(r, "Failed to read %s: %m", opts.arg);
×
1508

1509
                        arg_root_password_is_hashed = false;
1✔
1510
                        break;
1✔
1511

1512
                OPTION_LONG("root-password-hashed", "HASH", "Set root password from hashed password"):
4✔
1513
                        r = free_and_strdup_warn(&arg_root_password, opts.arg);
4✔
1514
                        if (r < 0)
4✔
1515
                                return r;
1516

1517
                        arg_root_password_is_hashed = true;
4✔
1518
                        break;
4✔
1519

1520
                OPTION_LONG("root-shell", "SHELL", "Set root shell"):
5✔
1521
                        r = free_and_strdup_warn(&arg_root_shell, opts.arg);
5✔
1522
                        if (r < 0)
5✔
1523
                                return r;
1524
                        break;
1525

1526
                OPTION_LONG("kernel-command-line", "CMDLINE", "Set kernel command line"):
3✔
1527
                        r = free_and_strdup_warn(&arg_kernel_cmdline, opts.arg);
3✔
1528
                        if (r < 0)
3✔
1529
                                return r;
1530
                        break;
1531

1532
                OPTION_LONG("prompt-locale", NULL, "Prompt the user for locale settings"):
115✔
1533
                        arg_prompt_locale = true;
115✔
1534
                        break;
115✔
1535

1536
                OPTION_LONG("prompt-keymap", NULL, "Prompt the user for keymap settings"):
2✔
1537
                        arg_prompt_keymap = true;
2✔
1538
                        arg_prompt_keymap_auto = false;
2✔
1539
                        break;
2✔
1540

1541
                OPTION_LONG("prompt-keymap-auto", NULL,
114✔
1542
                            "Prompt the user for keymap settings if invoked on local console"):
1543
                        arg_prompt_keymap_auto = true;
114✔
1544
                        break;
114✔
1545

1546
                OPTION_LONG("prompt-timezone", NULL, "Prompt the user for timezone"):
115✔
1547
                        arg_prompt_timezone = true;
115✔
1548
                        break;
115✔
1549

1550
                OPTION_LONG("prompt-hostname", NULL, "Prompt the user for hostname"):
1✔
1551
                        arg_prompt_hostname = true;
1✔
1552
                        break;
1✔
1553

1554
                OPTION_LONG("prompt-root-password", NULL, "Prompt the user for root password"):
115✔
1555
                        arg_prompt_root_password = true;
115✔
1556
                        break;
115✔
1557

1558
                OPTION_LONG("prompt-root-shell", NULL, "Prompt the user for root shell"):
4✔
1559
                        arg_prompt_root_shell = true;
4✔
1560
                        break;
4✔
1561

1562
                OPTION_LONG("prompt", NULL, "Prompt for all of the above"):
×
UNCOV
1563
                        arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname =
×
UNCOV
1564
                                arg_prompt_root_password = arg_prompt_root_shell = true;
×
UNCOV
1565
                        arg_prompt_keymap_auto = false;
×
UNCOV
1566
                        break;
×
1567

1568
                OPTION_LONG("copy-locale", NULL, "Copy locale from host"):
2✔
1569
                        arg_copy_locale = true;
2✔
1570
                        break;
2✔
1571

1572
                OPTION_LONG("copy-keymap", NULL, "Copy keymap from host"):
2✔
1573
                        arg_copy_keymap = true;
2✔
1574
                        break;
2✔
1575

1576
                OPTION_LONG("copy-timezone", NULL, "Copy timezone from host"):
2✔
1577
                        arg_copy_timezone = true;
2✔
1578
                        break;
2✔
1579

1580
                OPTION_LONG("copy-root-password", NULL, "Copy root password from host"):
1✔
1581
                        arg_copy_root_password = true;
1✔
1582
                        break;
1✔
1583

1584
                OPTION_LONG("copy-root-shell", NULL, "Copy root shell from host"):
1✔
1585
                        arg_copy_root_shell = true;
1✔
1586
                        break;
1✔
1587

1588
                OPTION_LONG("copy", NULL, "Copy all of the above"):
1✔
1589
                        arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password =
1✔
1590
                                arg_copy_root_shell = true;
1✔
1591
                        break;
1✔
1592

1593
                OPTION_LONG("force", NULL, "Overwrite existing files"):
4✔
1594
                        arg_force = true;
4✔
1595
                        break;
4✔
1596

1597
                OPTION_LONG("delete-root-password", NULL, "Delete root password"):
1✔
1598
                        arg_delete_root_password = true;
1✔
1599
                        break;
1✔
1600

1601
                OPTION_LONG("welcome", "BOOL", "Whether to show the welcome text"):
1✔
1602
                        r = parse_boolean_argument("--welcome=", opts.arg, &arg_welcome);
1✔
1603
                        if (r < 0)
1✔
1604
                                return r;
1605
                        break;
1606

UNCOV
1607
                OPTION_LONG("chrome", "BOOL",
×
1608
                            "Whether to show a color bar at top and bottom of terminal"):
UNCOV
1609
                        r = parse_boolean_argument("--chrome=", opts.arg, &arg_chrome);
×
UNCOV
1610
                        if (r < 0)
×
1611
                                return r;
1612
                        break;
1613

1614
                OPTION_LONG("mute-console", "BOOL",
113✔
1615
                            "Whether to disallow kernel/PID 1 writes to the console while running"):
1616
                        r = parse_boolean_argument("--mute-console=", opts.arg, &arg_mute_console);
113✔
1617
                        if (r < 0)
113✔
1618
                                return r;
1619
                        break;
1620

1621
                OPTION_LONG("reset", NULL, "Remove existing files"):
1✔
1622
                        arg_reset = true;
1✔
1623
                        break;
1✔
1624
                }
1625

1626
        if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password))
157✔
1627
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1628
                                       "--delete-root-password cannot be combined with other root password options.");
1629

1630
        if (arg_image && arg_root)
157✔
1631
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1632
                                       "--root= and --image= cannot be used together.");
1633

1634
        if (!sd_id128_is_null(arg_machine_id) && !(arg_image || arg_root) && !arg_force)
310✔
UNCOV
1635
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1636
                                       "--machine-id=/--setup-machine-id only works with --root= or --image=.");
1637

1638
        return 1;
1639
}
1640

UNCOV
1641
static int reload_system_manager(sd_bus **bus) {
×
1642
        int r;
×
1643

1644
        assert(bus);
×
1645

UNCOV
1646
        if (!*bus) {
×
UNCOV
1647
                r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, RUNTIME_SCOPE_SYSTEM, bus);
×
1648
                if (r < 0)
×
1649
                        return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL, RUNTIME_SCOPE_SYSTEM);
×
1650
        }
1651

1652
        r = bus_service_manager_reload(*bus);
×
UNCOV
1653
        if (r < 0)
×
1654
                return r;
1655

1656
        log_info("Requested manager reload to apply locale configuration.");
×
1657
        return 0;
1658
}
1659

1660
static int reload_vconsole(sd_bus **bus) {
×
1661
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
UNCOV
1662
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
×
1663
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
UNCOV
1664
        const char *object;
×
1665
        int r;
×
1666

1667
        assert(bus);
×
1668

UNCOV
1669
        if (!*bus) {
×
UNCOV
1670
                r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, RUNTIME_SCOPE_SYSTEM, bus);
×
1671
                if (r < 0)
×
1672
                        return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL, RUNTIME_SCOPE_SYSTEM);
×
1673
        }
1674

1675
        r = bus_wait_for_jobs_new(*bus, &w);
×
UNCOV
1676
        if (r < 0)
×
1677
                return log_error_errno(r, "Could not watch jobs: %m");
×
1678

UNCOV
1679
        r = bus_call_method(*bus, bus_systemd_mgr, "RestartUnit", &error, &reply,
×
1680
                            "ss", "systemd-vconsole-setup.service", "replace");
1681
        if (r < 0)
×
1682
                return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
×
1683

1684
        r = sd_bus_message_read(reply, "o", &object);
×
1685
        if (r < 0)
×
1686
                return bus_log_parse_error(r);
×
1687

UNCOV
1688
        r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
×
UNCOV
1689
        if (r < 0)
×
UNCOV
1690
                return log_error_errno(r, "Failed to wait for systemd-vconsole-setup.service/restart: %m");
×
1691
        return 0;
1692
}
1693

1694
static void end_marker(void) {
157✔
1695

1696
        if (!welcome_done)
157✔
1697
                return;
1698

1699
        printf("\n%sExiting first boot settings tool.%s\n\n", ansi_grey(), ansi_normal());
12✔
1700
        fflush(stdout);
6✔
1701
}
1702

1703
static int run(int argc, char *argv[]) {
161✔
1704
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
161✔
1705
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
161✔
UNCOV
1706
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
×
1707
        _cleanup_close_ int rfd = -EBADF;
161✔
1708
        int r;
161✔
1709

1710
        LIBBLKID_NOTE(recommended);
161✔
1711
        LIBCRYPT_NOTE(recommended);
161✔
1712
        LIBCRYPTO_NOTE(suggested);
161✔
1713
        LIBCRYPTSETUP_NOTE(suggested);
161✔
1714
        LIBMOUNT_NOTE(recommended);
161✔
1715
        LIBSELINUX_NOTE(recommended);
161✔
1716
        PASSWORD_NOTE(suggested);
161✔
1717

1718
        r = parse_argv(argc, argv);
161✔
1719
        if (r <= 0)
161✔
1720
                return r;
1721

1722
        log_setup();
157✔
1723

1724
        umask(0022);
157✔
1725

1726
        bool offline = arg_root || arg_image;
157✔
1727

1728
        if (!offline) {
114✔
1729
                /* If we are called without --root=/--image= let's honour the systemd.firstboot kernel
1730
                 * command line option, because we are called to provision the host with basic settings (as
1731
                 * opposed to some other file system tree/image) */
1732

1733
                FirstBootMode mode;
114✔
1734
                _cleanup_free_ char *bad = NULL;
114✔
1735
                r = firstboot_mode_from_cmdline(&mode, &bad);
114✔
1736
                if (r < 0)
114✔
UNCOV
1737
                        return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument%s: %m",
×
1738
                                               bad ? strjoina(" (invalid value '", bad, "')") : "");
1739
                if (mode == FIRSTBOOT_NO) {
114✔
1740
                        log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
114✔
1741
                        arg_prompt_locale = arg_prompt_keymap = arg_prompt_keymap_auto = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false;
114✔
UNCOV
1742
                } else if (mode == FIRSTBOOT_HEADLESS) {
×
UNCOV
1743
                        log_debug("Found systemd.firstboot=headless kernel command line argument, skipping interactive prompts but keeping non-interactive auto-configuration.");
×
UNCOV
1744
                        arg_headless = true;
×
1745
                }
1746
        }
1747

1748
        r = mac_init();
157✔
1749
        if (r < 0)
157✔
1750
                return r;
1751

1752
        if (arg_image) {
157✔
UNCOV
1753
                assert(!arg_root);
×
1754

UNCOV
1755
                r = mount_image_privately_interactively(
×
1756
                                arg_image,
1757
                                arg_image_policy,
1758
                                DISSECT_IMAGE_GENERIC_ROOT |
1759
                                DISSECT_IMAGE_REQUIRE_ROOT |
1760
                                DISSECT_IMAGE_VALIDATE_OS |
1761
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
1762
                                DISSECT_IMAGE_FSCK |
1763
                                DISSECT_IMAGE_GROWFS |
1764
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
1765
                                &mounted_dir,
1766
                                &rfd,
1767
                                &loop_device);
1768
                if (r < 0)
×
1769
                        return r;
1770

UNCOV
1771
                arg_root = strdup(mounted_dir);
×
UNCOV
1772
                if (!arg_root)
×
1773
                        return log_oom();
×
1774
        } else {
1775
                rfd = open(empty_to_root(arg_root), O_DIRECTORY|O_CLOEXEC);
157✔
1776
                if (rfd < 0)
157✔
UNCOV
1777
                        return log_error_errno(errno, "Failed to open %s: %m", empty_to_root(arg_root));
×
1778
        }
1779

1780
        LOG_SET_PREFIX(arg_image ?: arg_root);
318✔
1781
        DEFER_VOID_CALL(end_marker);
157✔
UNCOV
1782
        DEFER_VOID_CALL(chrome_hide);
×
1783

1784
        /* We check these conditions here instead of in parse_argv() so that we can take the root directory
1785
         * into account. */
1786

1787
        if (arg_keymap && !keymap_is_ok(arg_keymap, FD_TO_PTR(rfd)))
157✔
1788
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap %s is not installed.", arg_keymap);
×
1789
        if (arg_locale && !locale_is_ok(arg_locale, FD_TO_PTR(rfd)))
157✔
UNCOV
1790
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
×
1791
        if (arg_locale_messages && !locale_is_ok(arg_locale_messages, FD_TO_PTR(rfd)))
157✔
UNCOV
1792
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages);
×
1793

1794
        if (arg_root_shell) {
157✔
1795
                r = find_shell(rfd, arg_root_shell);
5✔
1796
                if (r < 0)
5✔
1797
                        return r;
1798
        }
1799

1800
        r = process_reset(rfd);
156✔
1801
        if (r < 0)
156✔
1802
                return r;
1803

1804
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
157✔
1805
        r = process_locale(rfd, &mute_console_link);
156✔
1806
        if (r < 0)
156✔
1807
                return r;
1808
        if (r > 0 && !offline)
156✔
UNCOV
1809
                (void) reload_system_manager(&bus);
×
1810

1811
        r = process_keymap(rfd, &mute_console_link);
156✔
1812
        if (r < 0)
156✔
1813
                return r;
1814
        if (r > 0 && !offline)
156✔
UNCOV
1815
                (void) reload_vconsole(&bus);
×
1816

1817
        r = process_timezone(rfd, &mute_console_link);
156✔
1818
        if (r < 0)
156✔
1819
                return r;
1820

1821
        r = process_hostname(rfd, &mute_console_link);
156✔
1822
        if (r < 0)
156✔
1823
                return r;
1824

1825
        r = process_root_account(rfd, &mute_console_link);
156✔
1826
        if (r < 0)
156✔
1827
                return r;
1828

1829
        r = process_kernel_cmdline(rfd);
155✔
1830
        if (r < 0)
155✔
1831
                return r;
1832

1833
        r = process_machine_id(rfd);
155✔
1834
        if (r < 0)
155✔
1835
                return r;
1836

1837
        r = process_machine_tags(rfd);
155✔
1838
        if (r < 0)
155✔
UNCOV
1839
                return r;
×
1840

1841
        return 0;
1842
}
1843

1844
DEFINE_MAIN_FUNCTION(run);
161✔
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