• 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

78.78
/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);
160✔
99
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
160✔
100
STATIC_DESTRUCTOR_REGISTER(arg_locale, freep);
160✔
101
STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep);
160✔
102
STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
160✔
103
STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep);
160✔
104
STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep);
160✔
105
STATIC_DESTRUCTOR_REGISTER(arg_machine_tags, strv_freep);
160✔
106
STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep);
160✔
107
STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep);
160✔
108
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
160✔
109
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
160✔
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,395✔
171
        _cleanup_fclose_ FILE *passwd = NULL, *shadow = NULL;
1,395✔
172
        int r;
1,395✔
173

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

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

183
        if (r == -ENOENT)
838✔
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) {
18✔
260
        _cleanup_strv_free_ char **locales = NULL;
18✔
261
        bool acquired_from_creds = false;
18✔
262
        int r;
18✔
263

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

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

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

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

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

286
        if (!arg_prompt_locale) {
12✔
287
                log_debug("Prompting for locale was not requested.");
11✔
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) {
155✔
362
        _cleanup_close_ int pfd = -EBADF;
155✔
363
        _cleanup_free_ char *f = NULL;
155✔
364
        char* locales[3];
155✔
365
        unsigned i = 0;
155✔
366
        int r;
155✔
367

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

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

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

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

386
        if (arg_copy_locale && r == 0) {
21✔
387
                r = copy_file_atomic_at(AT_FDCWD, etc_locale_conf(), pfd, f, 0644, COPY_REFLINK);
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);
18✔
398
        if (r < 0)
18✔
399
                return r;
400

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

406
        if (i == 0)
18✔
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) {
26✔
435
        _cleanup_strv_free_ char **kmaps = NULL;
26✔
436
        int r;
26✔
437

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

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

443
        _cleanup_free_ char *km = NULL;
23✔
444
        r = read_credential("firstboot.keymap", (void**) &km, NULL);
23✔
445
        if (r < 0)
23✔
446
                log_debug_errno(r, "Failed to read credential firstboot.keymap, ignoring: %m");
22✔
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;
22✔
456
        if (arg_prompt_keymap_auto) {
22✔
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;
21✔
469
        if (!b) {
22✔
470
                log_debug("Prompting for keymap was not requested.");
21✔
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) {
155✔
505
        _cleanup_close_ int pfd = -EBADF;
155✔
506
        _cleanup_free_ char *f = NULL;
155✔
507
        _cleanup_strv_free_ char **keymap = NULL;
×
508
        int r;
155✔
509

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

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

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

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

528
        if (arg_copy_keymap && r == 0) {
29✔
529
                r = copy_file_atomic_at(AT_FDCWD, etc_vconsole_conf(), pfd, f, 0644, COPY_REFLINK);
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);
26✔
540
        if (r == -ENOENT)
26✔
541
                return 0; /* don't fail if no keymaps are installed */
542
        if (r < 0)
26✔
543
                return r;
544

545
        if (isempty(arg_keymap))
160✔
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) {
136✔
574
        _cleanup_strv_free_ char **zones = NULL;
136✔
575
        int r;
136✔
576

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

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

582
        _cleanup_free_ char *tz = NULL;
133✔
583
        r = read_credential("firstboot.timezone", (void**) &tz, NULL);
133✔
584
        if (r < 0)
133✔
585
                log_debug_errno(r, "Failed to read credential firstboot.timezone, ignoring: %m");
19✔
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) {
19✔
595
                log_debug("Prompting for timezone was not requested.");
18✔
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) {
155✔
625
        _cleanup_close_ int pfd = -EBADF;
155✔
626
        _cleanup_free_ char *f = NULL, *relpath = NULL;
155✔
627
        const char *e;
155✔
628
        int r;
155✔
629

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

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

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

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

648
        if (arg_copy_timezone && r == 0) {
139✔
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);
136✔
666
        if (r < 0)
136✔
667
                return r;
668

669
        if (isempty(arg_timezone))
273✔
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) {
141✔
690
        int r;
141✔
691

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

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

697
        _cleanup_free_ char *hn = NULL;
134✔
698
        r = read_credential("firstboot.hostname", (void**) &hn, NULL);
134✔
699
        if (r < 0)
134✔
700
                log_debug_errno(r, "Failed to read credential firstboot.hostname, ignoring: %m");
133✔
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) {
133✔
711
                log_debug("Prompting for hostname was not requested.");
132✔
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) {
155✔
743
        _cleanup_close_ int pfd = -EBADF;
155✔
744
        _cleanup_free_ char *f = NULL;
155✔
745
        int r;
155✔
746

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

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

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

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

763
        if (isempty(arg_hostname))
164✔
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) {
7✔
969
        int r;
7✔
970

971
        assert(path);
7✔
972

973
        if (!valid_shell(path))
7✔
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);
7✔
977
        if (r < 0)
7✔
978
                return log_error_errno(r, "Failed to resolve shell %s: %m", path);
1✔
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) {
37✔
990
        int r;
37✔
991

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

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

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

1005
        if (!arg_prompt_root_shell) {
31✔
1006
                log_debug("Prompting for root shell was not requested.");
29✔
1007
                return 0;
1008
        }
1009

1010
        if (headless_skips_prompt_for("root shell"))
2✔
1011
                return 0;
1012

1013
        print_welcome(rfd, mute_console_link);
2✔
1014

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

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

1037
        r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp);
15✔
1038
        if (r < 0)
15✔
1039
                return r;
1040

1041
        r = xfopenat(etc_fd, "passwd", "re", O_NOFOLLOW, &original);
15✔
1042
        if (r < 0 && r != -ENOENT)
15✔
1043
                return r;
1044

1045
        if (original) {
15✔
1046
                struct passwd *i;
4✔
1047

1048
                r = copy_rights(fileno(original), fileno(passwd));
4✔
1049
                if (r < 0)
4✔
1050
                        return r;
×
1051

1052
                while ((r = fgetpwent_sane(original, &i)) > 0) {
7✔
1053

1054
                        if (streq(i->pw_name, "root")) {
3✔
1055
                                if (password)
3✔
1056
                                        i->pw_passwd = (char *) password;
2✔
1057
                                if (shell)
3✔
1058
                                        i->pw_shell = (char *) shell;
2✔
1059
                                found = true;
1060
                        }
1061

1062
                        r = putpwent_sane(i, passwd);
3✔
1063
                        if (r < 0)
3✔
1064
                                return r;
1065
                }
1066
                if (r < 0)
4✔
1067
                        return r;
1068

1069
        } else {
1070
                r = fchmod(fileno(passwd), 0644);
11✔
1071
                if (r < 0)
11✔
1072
                        return -errno;
×
1073
        }
1074

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

1086
                r = putpwent_sane(&root, passwd);
12✔
1087
                if (r < 0)
12✔
1088
                        return r;
×
1089
        }
1090

1091
        r = fflush_sync_and_check(passwd);
15✔
1092
        if (r < 0)
15✔
1093
                return r;
1094

1095
        r = renameat_and_apply_smack_floor_label(etc_fd, passwd_tmp, etc_fd, "passwd");
15✔
1096
        if (r < 0)
15✔
1097
                return r;
×
1098

1099
        return 0;
1100
}
1101

1102
static int write_root_shadow(int etc_fd, const char *hashed_password) {
15✔
1103
        _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
15✔
1104
        _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
15✔
1105
        int r;
15✔
1106
        bool found = false;
15✔
1107

1108
        r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp);
15✔
1109
        if (r < 0)
15✔
1110
                return r;
1111

1112
        r = xfopenat(etc_fd, "shadow", "re", O_NOFOLLOW, &original);
15✔
1113
        if (r < 0 && r != -ENOENT)
15✔
1114
                return r;
1115

1116
        if (original) {
15✔
1117
                struct spwd *i;
4✔
1118

1119
                r = copy_rights(fileno(original), fileno(shadow));
4✔
1120
                if (r < 0)
4✔
1121
                        return r;
×
1122

1123
                while ((r = fgetspent_sane(original, &i)) > 0) {
7✔
1124

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

1133
                        r = putspent_sane(i, shadow);
3✔
1134
                        if (r < 0)
3✔
1135
                                return r;
1136
                }
1137
                if (r < 0)
4✔
1138
                        return r;
1139

1140
        } else {
1141
                r = fchmod(fileno(shadow), 0000);
11✔
1142
                if (r < 0)
11✔
1143
                        return -errno;
×
1144
        }
1145

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

1159
                r = putspent_sane(&root, shadow);
12✔
1160
                if (r < 0)
12✔
1161
                        return r;
×
1162
        }
1163

1164
        r = fflush_sync_and_check(shadow);
15✔
1165
        if (r < 0)
15✔
1166
                return r;
1167

1168
        r = renameat_and_apply_smack_floor_label(etc_fd, shadow_tmp, etc_fd, "shadow");
15✔
1169
        if (r < 0)
15✔
1170
                return r;
×
1171

1172
        return 0;
1173
}
1174

1175
static int process_root_account(int rfd, sd_varlink **mute_console_link) {
155✔
1176
        _cleanup_close_ int pfd = -EBADF;
155✔
1177
        _cleanup_(release_lock_file) LockFile lock = LOCK_FILE_INIT;
×
1178
        _cleanup_(erase_and_freep) char *_hashed_password = NULL;
155✔
1179
        const char *password, *hashed_password;
155✔
1180
        int k = 0, r;
155✔
1181

1182
        assert(rfd >= 0);
155✔
1183

1184
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/passwd",
155✔
1185
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
1186
                                       NULL);
1187
        if (pfd < 0)
155✔
1188
                return log_error_errno(pfd, "Failed to chase /etc/passwd: %m");
×
1189

1190
        /* Ensure that passwd and shadow are in the same directory and are not symlinks. */
1191

1192
        FOREACH_STRING(s, "passwd", "shadow") {
465✔
1193
                r = verify_regular_at(pfd, s, /* follow= */ false);
310✔
1194
                if (r < 0 && r != -ENOENT)
310✔
1195
                        return log_error_errno(r, "Verification of /etc/%s being regular file failed: %m", s);
×
1196

1197
                r = should_configure(pfd, s);
310✔
1198
                if (r < 0)
310✔
1199
                        return r;
1200

1201
                k += r;
310✔
1202
        }
1203

1204
        if (k == 0) {
155✔
1205
                log_debug("Found /etc/passwd and /etc/shadow, assuming root account has been initialized.");
118✔
1206
                return 0;
1207
        }
1208

1209
        r = make_lock_file_at(pfd, ETC_PASSWD_LOCK_FILENAME, LOCK_EX, &lock);
37✔
1210
        if (r < 0)
37✔
1211
                return log_error_errno(r, "Failed to take a lock on /etc/passwd: %m");
×
1212

1213
        k = dir_fd_is_root(rfd);
37✔
1214
        if (k < 0)
37✔
1215
                return log_error_errno(k, "Failed to check if directory file descriptor is root: %m");
×
1216

1217
        if (arg_copy_root_shell && k == 0) {
37✔
1218
                _cleanup_free_ struct passwd *p = NULL;
2✔
1219

1220
                r = getpwnam_malloc("root", &p);
2✔
1221
                if (r < 0)
2✔
1222
                        return log_error_errno(r, "Failed to find passwd entry for root: %m");
×
1223

1224
                r = free_and_strdup(&arg_root_shell, p->pw_shell);
2✔
1225
                if (r < 0)
2✔
1226
                        return log_oom();
×
1227
        }
1228

1229
        r = prompt_root_shell(rfd, mute_console_link);
37✔
1230
        if (r < 0)
37✔
1231
                return r;
1232

1233
        if (arg_copy_root_password && k == 0) {
37✔
1234
                struct spwd *p;
2✔
1235

1236
                errno = 0;
2✔
1237
                p = getspnam("root");
2✔
1238
                if (!p)
2✔
1239
                        return log_error_errno(errno_or_else(EIO), "Failed to find shadow entry for root: %m");
×
1240

1241
                r = free_and_strdup(&arg_root_password, p->sp_pwdp);
2✔
1242
                if (r < 0)
2✔
1243
                        return log_oom();
×
1244

1245
                arg_root_password_is_hashed = true;
2✔
1246
        }
1247

1248
        r = prompt_root_password(rfd, mute_console_link);
37✔
1249
        if (r < 0)
37✔
1250
                return r;
1251

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

1260
                password = PASSWORD_SEE_SHADOW;
4✔
1261
                hashed_password = _hashed_password;
4✔
1262

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

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

1280
        r = write_root_passwd(rfd, pfd, password, arg_root_shell);
15✔
1281
        if (r < 0)
15✔
1282
                return log_error_errno(r, "Failed to write /etc/passwd: %m");
×
1283

1284
        log_info("/etc/passwd written.");
15✔
1285

1286
        r = write_root_shadow(pfd, hashed_password);
15✔
1287
        if (r < 0)
15✔
1288
                return log_error_errno(r, "Failed to write /etc/shadow: %m");
×
1289

1290
        log_info("/etc/shadow written.");
15✔
1291
        return 0;
1292
}
1293

1294
static int process_kernel_cmdline(int rfd) {
155✔
1295
        _cleanup_close_ int pfd = -EBADF;
155✔
1296
        _cleanup_free_ char *f = NULL;
155✔
1297
        int r;
155✔
1298

1299
        assert(rfd >= 0);
155✔
1300

1301
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/kernel/cmdline",
155✔
1302
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
1303
                                       &f);
1304
        if (pfd < 0)
155✔
1305
                return log_error_errno(pfd, "Failed to chase /etc/kernel/cmdline: %m");
×
1306

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

1313
        if (!arg_kernel_cmdline) {
154✔
1314
                log_debug("Creation of /etc/kernel/cmdline was not requested, skipping.");
152✔
1315
                return 0;
1316
        }
1317

1318
        r = write_string_file_at(pfd, "cmdline", arg_kernel_cmdline,
2✔
1319
                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
1320
        if (r < 0)
2✔
1321
                return log_error_errno(r, "Failed to write /etc/kernel/cmdline: %m");
×
1322

1323
        log_info("/etc/kernel/cmdline written.");
2✔
1324
        return 0;
1325
}
1326

1327
static int reset_one(int rfd, const char *path) {
6✔
1328
        _cleanup_close_ int pfd = -EBADF;
6✔
1329
        _cleanup_free_ char *f = NULL;
6✔
1330

1331
        assert(rfd >= 0);
6✔
1332
        assert(path);
6✔
1333

1334
        pfd = chase_and_open_parent_at(rfd, rfd, path, CHASE_WARN|CHASE_NOFOLLOW, &f);
6✔
1335
        if (pfd == -ENOENT)
6✔
1336
                return 0;
1337
        if (pfd < 0)
6✔
1338
                return log_error_errno(pfd, "Failed to resolve %s: %m", path);
×
1339

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

1343
        log_info("Removed %s", path);
6✔
1344
        return 0;
1345
}
1346

1347
static int process_reset(int rfd) {
155✔
1348
        int r;
155✔
1349

1350
        assert(rfd >= 0);
155✔
1351

1352
        if (!arg_reset)
155✔
1353
                return 0;
1354

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

1367
        return 0;
1✔
1368
}
1369

1370
static int help(void) {
×
1371
        _cleanup_(table_unrefp) Table *options = NULL;
×
1372
        int r;
×
1373

1374
        r = option_parser_get_help_table(&options);
×
1375
        if (r < 0)
×
1376
                return r;
1377

1378
        help_cmdline("[OPTIONS...]");
×
1379
        help_abstract("Configures basic settings of the system.");
×
1380
        help_section("Options");
×
1381

1382
        r = table_print_or_warn(options);
×
1383
        if (r < 0)
×
1384
                return r;
1385

1386
        help_man_page_reference("systemd-firstboot", "1");
×
1387
        return 0;
1388
}
1389

1390
static int parse_argv(int argc, char *argv[]) {
160✔
1391
        assert(argc >= 0);
160✔
1392
        assert(argv);
160✔
1393

1394
        OptionParser opts = { argc, argv };
160✔
1395
        int r;
160✔
1396

1397
        FOREACH_OPTION_OR_RETURN(c, &opts)
1,168✔
1398
                switch (c) {
692✔
1399

1400
                OPTION_COMMON_HELP:
×
1401
                        return help();
×
1402

1403
                OPTION_COMMON_VERSION:
×
1404
                        return version();
×
1405

1406
                OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
46✔
1407
                        r = parse_path_argument(opts.arg, true, &arg_root);
46✔
1408
                        if (r < 0)
46✔
1409
                                return r;
1410
                        break;
1411

1412
                OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
×
1413
                        r = parse_path_argument(opts.arg, false, &arg_image);
×
1414
                        if (r < 0)
×
1415
                                return r;
1416
                        break;
1417

1418
                OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
×
1419
                        r = parse_image_policy_argument(opts.arg, &arg_image_policy);
×
1420
                        if (r < 0)
×
1421
                                return r;
1422
                        break;
1423

1424
                OPTION_LONG("locale", "LOCALE", "Set primary locale (LANG=)"):
5✔
1425
                        r = free_and_strdup_warn(&arg_locale, opts.arg);
5✔
1426
                        if (r < 0)
5✔
1427
                                return r;
1428
                        break;
1429

1430
                OPTION_LONG("locale-messages", "LOCALE", "Set message locale (LC_MESSAGES=)"):
4✔
1431
                        r = free_and_strdup_warn(&arg_locale_messages, opts.arg);
4✔
1432
                        if (r < 0)
4✔
1433
                                return r;
1434
                        break;
1435

1436
                OPTION_LONG("keymap", "KEYMAP", "Set keymap"):
4✔
1437
                        if (!keymap_is_valid(opts.arg))
4✔
1438
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1439
                                                       "Keymap %s is not valid.", opts.arg);
1440

1441
                        r = free_and_strdup_warn(&arg_keymap, opts.arg);
4✔
1442
                        if (r < 0)
4✔
1443
                                return r;
1444
                        break;
1445

1446
                OPTION_LONG("timezone", "TIMEZONE", "Set timezone"):
5✔
1447
                        if (!timezone_is_valid(opts.arg, LOG_ERR))
5✔
1448
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
1449
                                                       "Timezone %s is not valid.", opts.arg);
1450

1451
                        r = free_and_strdup_warn(&arg_timezone, opts.arg);
4✔
1452
                        if (r < 0)
4✔
1453
                                return r;
1454
                        break;
1455

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

1461
                        r = free_and_strdup_warn(&arg_hostname, opts.arg);
8✔
1462
                        if (r < 0)
8✔
1463
                                return r;
1464

1465
                        hostname_cleanup(arg_hostname);
8✔
1466
                        break;
1467

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

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

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

1486
                        strv_free_and_replace(arg_machine_tags, tags);
1✔
1487
                        break;
1✔
1488
                }
1489

1490
                OPTION_LONG("root-password", "PASSWORD", "Set root password from plaintext password"):
4✔
1491
                        r = free_and_strdup_warn(&arg_root_password, opts.arg);
4✔
1492
                        if (r < 0)
4✔
1493
                                return r;
1494

1495
                        arg_root_password_is_hashed = false;
4✔
1496
                        break;
4✔
1497

1498
                OPTION_LONG("root-password-file", "FILE", "Set root password from file"):
1✔
1499
                        arg_root_password = mfree(arg_root_password);
1✔
1500

1501
                        r = read_one_line_file(opts.arg, &arg_root_password);
1✔
1502
                        if (r < 0)
1✔
1503
                                return log_error_errno(r, "Failed to read %s: %m", opts.arg);
×
1504

1505
                        arg_root_password_is_hashed = false;
1✔
1506
                        break;
1✔
1507

1508
                OPTION_LONG("root-password-hashed", "HASH", "Set root password from hashed password"):
4✔
1509
                        r = free_and_strdup_warn(&arg_root_password, opts.arg);
4✔
1510
                        if (r < 0)
4✔
1511
                                return r;
1512

1513
                        arg_root_password_is_hashed = true;
4✔
1514
                        break;
4✔
1515

1516
                OPTION_LONG("root-shell", "SHELL", "Set root shell"):
5✔
1517
                        r = free_and_strdup_warn(&arg_root_shell, opts.arg);
5✔
1518
                        if (r < 0)
5✔
1519
                                return r;
1520
                        break;
1521

1522
                OPTION_LONG("kernel-command-line", "CMDLINE", "Set kernel command line"):
3✔
1523
                        r = free_and_strdup_warn(&arg_kernel_cmdline, opts.arg);
3✔
1524
                        if (r < 0)
3✔
1525
                                return r;
1526
                        break;
1527

1528
                OPTION_LONG("prompt-locale", NULL, "Prompt the user for locale settings"):
115✔
1529
                        arg_prompt_locale = true;
115✔
1530
                        break;
115✔
1531

1532
                OPTION_LONG("prompt-keymap", NULL, "Prompt the user for keymap settings"):
2✔
1533
                        arg_prompt_keymap = true;
2✔
1534
                        arg_prompt_keymap_auto = false;
2✔
1535
                        break;
2✔
1536

1537
                OPTION_LONG("prompt-keymap-auto", NULL,
114✔
1538
                            "Prompt the user for keymap settings if invoked on local console"):
1539
                        arg_prompt_keymap_auto = true;
114✔
1540
                        break;
114✔
1541

1542
                OPTION_LONG("prompt-timezone", NULL, "Prompt the user for timezone"):
115✔
1543
                        arg_prompt_timezone = true;
115✔
1544
                        break;
115✔
1545

1546
                OPTION_LONG("prompt-hostname", NULL, "Prompt the user for hostname"):
1✔
1547
                        arg_prompt_hostname = true;
1✔
1548
                        break;
1✔
1549

1550
                OPTION_LONG("prompt-root-password", NULL, "Prompt the user for root password"):
115✔
1551
                        arg_prompt_root_password = true;
115✔
1552
                        break;
115✔
1553

1554
                OPTION_LONG("prompt-root-shell", NULL, "Prompt the user for root shell"):
4✔
1555
                        arg_prompt_root_shell = true;
4✔
1556
                        break;
4✔
1557

1558
                OPTION_LONG("prompt", NULL, "Prompt for all of the above"):
×
1559
                        arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname =
×
1560
                                arg_prompt_root_password = arg_prompt_root_shell = true;
×
1561
                        arg_prompt_keymap_auto = false;
×
1562
                        break;
×
1563

1564
                OPTION_LONG("copy-locale", NULL, "Copy locale from host"):
2✔
1565
                        arg_copy_locale = true;
2✔
1566
                        break;
2✔
1567

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

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

1576
                OPTION_LONG("copy-root-password", NULL, "Copy root password from host"):
1✔
1577
                        arg_copy_root_password = true;
1✔
1578
                        break;
1✔
1579

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

1584
                OPTION_LONG("copy", NULL, "Copy all of the above"):
1✔
1585
                        arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password =
1✔
1586
                                arg_copy_root_shell = true;
1✔
1587
                        break;
1✔
1588

1589
                OPTION_LONG("force", NULL, "Overwrite existing files"):
4✔
1590
                        arg_force = true;
4✔
1591
                        break;
4✔
1592

1593
                OPTION_LONG("delete-root-password", NULL, "Delete root password"):
1✔
1594
                        arg_delete_root_password = true;
1✔
1595
                        break;
1✔
1596

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

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

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

1617
                OPTION_LONG("reset", NULL, "Remove existing files"):
1✔
1618
                        arg_reset = true;
1✔
1619
                        break;
1✔
1620
                }
1621

1622
        if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password))
156✔
1623
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1624
                                       "--delete-root-password cannot be combined with other root password options.");
1625

1626
        if (arg_image && arg_root)
156✔
1627
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1628
                                       "--root= and --image= cannot be used together.");
1629

1630
        if (!sd_id128_is_null(arg_machine_id) && !(arg_image || arg_root) && !arg_force)
308✔
1631
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1632
                                       "--machine-id=/--setup-machine-id only works with --root= or --image=.");
1633

1634
        return 1;
1635
}
1636

1637
static int reload_system_manager(sd_bus **bus) {
×
1638
        int r;
×
1639

1640
        assert(bus);
×
1641

1642
        if (!*bus) {
×
1643
                r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, RUNTIME_SCOPE_SYSTEM, bus);
×
1644
                if (r < 0)
×
1645
                        return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL, RUNTIME_SCOPE_SYSTEM);
×
1646
        }
1647

1648
        r = bus_service_manager_reload(*bus);
×
1649
        if (r < 0)
×
1650
                return r;
1651

1652
        log_info("Requested manager reload to apply locale configuration.");
×
1653
        return 0;
1654
}
1655

1656
static int reload_vconsole(sd_bus **bus) {
×
1657
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1658
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
×
1659
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
1660
        const char *object;
×
1661
        int r;
×
1662

1663
        assert(bus);
×
1664

1665
        if (!*bus) {
×
1666
                r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, RUNTIME_SCOPE_SYSTEM, bus);
×
1667
                if (r < 0)
×
1668
                        return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL, RUNTIME_SCOPE_SYSTEM);
×
1669
        }
1670

1671
        r = bus_wait_for_jobs_new(*bus, &w);
×
1672
        if (r < 0)
×
1673
                return log_error_errno(r, "Could not watch jobs: %m");
×
1674

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

1680
        r = sd_bus_message_read(reply, "o", &object);
×
1681
        if (r < 0)
×
1682
                return bus_log_parse_error(r);
×
1683

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

1690
static void end_marker(void) {
156✔
1691

1692
        if (!welcome_done)
156✔
1693
                return;
1694

1695
        printf("\n%sExiting first boot settings tool.%s\n\n", ansi_grey(), ansi_normal());
12✔
1696
        fflush(stdout);
6✔
1697
}
1698

1699
static int run(int argc, char *argv[]) {
160✔
1700
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
160✔
1701
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
160✔
1702
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
×
1703
        _cleanup_close_ int rfd = -EBADF;
160✔
1704
        int r;
160✔
1705

1706
        LIBBLKID_NOTE(recommended);
160✔
1707
        LIBCRYPT_NOTE(recommended);
160✔
1708
        LIBCRYPTO_NOTE(suggested);
160✔
1709
        LIBCRYPTSETUP_NOTE(suggested);
160✔
1710
        LIBMOUNT_NOTE(recommended);
160✔
1711
        LIBSELINUX_NOTE(recommended);
160✔
1712
        PASSWORD_NOTE(suggested);
160✔
1713

1714
        r = parse_argv(argc, argv);
160✔
1715
        if (r <= 0)
160✔
1716
                return r;
1717

1718
        log_setup();
156✔
1719

1720
        umask(0022);
156✔
1721

1722
        bool offline = arg_root || arg_image;
156✔
1723

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

1729
                FirstBootMode mode;
114✔
1730
                _cleanup_free_ char *bad = NULL;
114✔
1731
                r = firstboot_mode_from_cmdline(&mode, &bad);
114✔
1732
                if (r < 0)
114✔
1733
                        return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument%s: %m",
×
1734
                                               bad ? strjoina(" (invalid value '", bad, "')") : "");
1735
                if (mode == FIRSTBOOT_NO) {
114✔
1736
                        log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
114✔
1737
                        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✔
1738
                } else if (mode == FIRSTBOOT_HEADLESS) {
×
1739
                        log_debug("Found systemd.firstboot=headless kernel command line argument, skipping interactive prompts but keeping non-interactive auto-configuration.");
×
1740
                        arg_headless = true;
×
1741
                }
1742
        }
1743

1744
        r = mac_init();
156✔
1745
        if (r < 0)
156✔
1746
                return r;
1747

1748
        if (arg_image) {
156✔
1749
                assert(!arg_root);
×
1750

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

1767
                arg_root = strdup(mounted_dir);
×
1768
                if (!arg_root)
×
1769
                        return log_oom();
×
1770
        } else {
1771
                rfd = open(empty_to_root(arg_root), O_DIRECTORY|O_CLOEXEC);
156✔
1772
                if (rfd < 0)
156✔
1773
                        return log_error_errno(errno, "Failed to open %s: %m", empty_to_root(arg_root));
×
1774
        }
1775

1776
        LOG_SET_PREFIX(arg_image ?: arg_root);
316✔
1777
        DEFER_VOID_CALL(end_marker);
156✔
1778
        DEFER_VOID_CALL(chrome_hide);
×
1779

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

1783
        if (arg_keymap && !keymap_is_ok(arg_keymap, FD_TO_PTR(rfd)))
156✔
1784
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap %s is not installed.", arg_keymap);
×
1785
        if (arg_locale && !locale_is_ok(arg_locale, FD_TO_PTR(rfd)))
156✔
1786
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
×
1787
        if (arg_locale_messages && !locale_is_ok(arg_locale_messages, FD_TO_PTR(rfd)))
156✔
1788
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages);
×
1789

1790
        if (arg_root_shell) {
156✔
1791
                r = find_shell(rfd, arg_root_shell);
5✔
1792
                if (r < 0)
5✔
1793
                        return r;
1794
        }
1795

1796
        r = process_reset(rfd);
155✔
1797
        if (r < 0)
155✔
1798
                return r;
1799

1800
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
156✔
1801
        r = process_locale(rfd, &mute_console_link);
155✔
1802
        if (r < 0)
155✔
1803
                return r;
1804
        if (r > 0 && !offline)
155✔
1805
                (void) reload_system_manager(&bus);
×
1806

1807
        r = process_keymap(rfd, &mute_console_link);
155✔
1808
        if (r < 0)
155✔
1809
                return r;
1810
        if (r > 0 && !offline)
155✔
1811
                (void) reload_vconsole(&bus);
×
1812

1813
        r = process_timezone(rfd, &mute_console_link);
155✔
1814
        if (r < 0)
155✔
1815
                return r;
1816

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

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

1825
        r = process_kernel_cmdline(rfd);
155✔
1826
        if (r < 0)
155✔
1827
                return r;
1828

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

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

1837
        return 0;
1838
}
1839

1840
DEFINE_MAIN_FUNCTION(run);
160✔
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