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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

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

78.3
/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 "env-file.h"
24
#include "errno-util.h"
25
#include "fd-util.h"
26
#include "fileio.h"
27
#include "format-table.h"
28
#include "fs-util.h"
29
#include "glyph-util.h"
30
#include "help-util.h"
31
#include "hostname-util.h"
32
#include "image-policy.h"
33
#include "kbd-util.h"
34
#include "label-util.h"
35
#include "libcrypt-util.h"
36
#include "locale-setup.h"
37
#include "locale-util.h"
38
#include "lock-util.h"
39
#include "loop-util.h"
40
#include "main-func.h"
41
#include "memory-util.h"
42
#include "mount-util.h"
43
#include "options.h"
44
#include "os-util.h"
45
#include "parse-argument.h"
46
#include "password-quality-util.h"
47
#include "path-util.h"
48
#include "plymouth-util.h"
49
#include "proc-cmdline.h"
50
#include "prompt-util.h"
51
#include "runtime-scope.h"
52
#include "smack-util.h"
53
#include "stat-util.h"
54
#include "string-util.h"
55
#include "strv.h"
56
#include "terminal-util.h"
57
#include "time-util.h"
58
#include "tmpfile-util.h"
59
#include "user-util.h"
60
#include "vconsole-util.h"
61

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

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

108
static bool welcome_done = false;
109

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

115
        assert(rfd >= 0);
7✔
116
        assert(mute_console_link);
7✔
117

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

122
        if (!*mute_console_link && arg_mute_console)
7✔
UNCOV
123
                (void) mute_console(mute_console_link);
×
124

125
        if (!arg_welcome)
7✔
126
                return;
127

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

133
        (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* flags= */ 0);
6✔
134

135
        if (arg_chrome)
6✔
136
                chrome_show("Initial Setup", /* bottom= */ NULL);
6✔
137

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

147
        pn = os_release_pretty_name(pretty_name, os_name);
6✔
148
        ac = isempty(ansi_color) ? "0" : ansi_color;
6✔
149

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

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

164
        welcome_done = true;
6✔
165
}
166

167
static int should_configure(int dir_fd, const char *filename) {
1,323✔
168
        _cleanup_fclose_ FILE *passwd = NULL, *shadow = NULL;
1,323✔
169
        int r;
1,323✔
170

171
        assert(dir_fd >= 0);
1,323✔
172
        assert(filename);
1,323✔
173

174
        if (streq(filename, "passwd") && !arg_force)
1,323✔
175
                /* We may need to do additional checks, so open the file. */
176
                r = xfopenat(dir_fd, filename, "re", O_NOFOLLOW, &passwd);
144✔
177
        else
178
                r = RET_NERRNO(faccessat(dir_fd, filename, F_OK, AT_SYMLINK_NOFOLLOW));
1,179✔
179

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

187
        if (!passwd)
634✔
188
                return false;
189

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

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

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

218
        struct spwd *j;
219
        while ((r = fgetspent_sane(shadow, &j)) > 0) {
115✔
220
                if (!streq(j->sp_namp, "root"))
115✔
UNCOV
221
                        continue;
×
222

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

236
static int locale_is_ok(const char *name, void *userdata) {
11✔
237
        int rfd = ASSERT_FD(PTR_TO_FD(userdata)), r;
11✔
238

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

243
        return r != 0 ? locale_is_installed(name) > 0 : locale_is_valid(name);
11✔
244
}
245

246
static int prompt_locale(int rfd, sd_varlink **mute_console_link) {
14✔
247
        _cleanup_strv_free_ char **locales = NULL;
14✔
248
        bool acquired_from_creds = false;
14✔
249
        int r;
14✔
250

251
        assert(rfd >= 0);
14✔
252

253
        if (arg_locale || arg_locale_messages)
14✔
254
                return 0;
255

256
        r = read_credential("firstboot.locale", (void**) &arg_locale, NULL);
9✔
257
        if (r < 0)
9✔
258
                log_debug_errno(r, "Failed to read credential firstboot.locale, ignoring: %m");
8✔
259
        else
260
                acquired_from_creds = true;
261

262
        r = read_credential("firstboot.locale-messages", (void**) &arg_locale_messages, NULL);
9✔
263
        if (r < 0)
9✔
264
                log_debug_errno(r, "Failed to read credential firstboot.locale-messages, ignoring: %m");
8✔
265
        else
266
                acquired_from_creds = true;
267

268
        if (acquired_from_creds) {
8✔
269
                log_debug("Acquired locale from credentials.");
1✔
270
                return 0;
271
        }
272

273
        if (!arg_prompt_locale) {
8✔
274
                log_debug("Prompting for locale was not requested.");
7✔
275
                return 0;
276
        }
277

278
        r = get_locales(&locales);
1✔
279
        if (r < 0)
1✔
UNCOV
280
                return log_error_errno(r, "Cannot query locales list: %m");
×
281

282
        if (strv_isempty(locales))
1✔
283
                log_debug("No locales found, skipping locale selection.");
×
284
        else if (strv_length(locales) == 1) {
1✔
285

UNCOV
286
                if (streq(locales[0], SYSTEMD_DEFAULT_LOCALE))
×
287
                        log_debug("Only installed locale is default locale anyway, not setting locale explicitly.");
×
288
                else {
289
                        log_debug("Only a single locale available (%s), selecting it as default.", locales[0]);
×
290

UNCOV
291
                        arg_locale = strdup(locales[0]);
×
UNCOV
292
                        if (!arg_locale)
×
UNCOV
293
                                return log_oom();
×
294

295
                        /* Not setting arg_locale_message here, since it defaults to LANG anyway */
296
                }
297
        } else {
298
                print_welcome(rfd, mute_console_link);
1✔
299

300
                _cleanup_free_ char *prefill = NULL;
1✔
301
                (void) locale_lang_from_efi(&prefill, LOCALE_REQUIRE_INSTALLED|LOCALE_SUPPRESS_EN_US);
1✔
302

303
                r = prompt_loop("Please enter the new system locale name or number",
2✔
304
                                GLYPH_WORLD,
305
                                prefill,
306
                                locales,
307
                                /* accepted= */ NULL,
308
                                /* ellipsize_percentage= */ 60,
309
                                /* n_columns= */ 3,
310
                                /* column_width= */ 20,
311
                                locale_is_ok,
312
                                /* refresh= */ NULL,
313
                                FD_TO_PTR(rfd),
1✔
314
                                PROMPT_MAY_SKIP|PROMPT_SHOW_MENU,
315
                                &arg_locale);
316
                if (r < 0)
1✔
317
                        return r;
318
                if (isempty(arg_locale))
1✔
319
                        return 0;
320

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

337
                /* Suppress the messages setting if it's the same as the main locale anyway */
338
                if (streq_ptr(arg_locale, arg_locale_messages))
1✔
UNCOV
339
                        arg_locale_messages = mfree(arg_locale_messages);
×
340
        }
341

342
        return 0;
343
}
344

345
static int process_locale(int rfd, sd_varlink **mute_console_link) {
147✔
346
        _cleanup_close_ int pfd = -EBADF;
147✔
347
        _cleanup_free_ char *f = NULL;
147✔
348
        char* locales[3];
147✔
349
        unsigned i = 0;
147✔
350
        int r;
147✔
351

352
        assert(rfd >= 0);
147✔
353

354
        pfd = chase_and_open_parent_at(rfd, rfd, etc_locale_conf(),
147✔
355
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
356
                                       &f);
357
        if (pfd < 0)
147✔
UNCOV
358
                return log_error_errno(pfd, "Failed to chase /etc/locale.conf: %m");
×
359

360
        r = should_configure(pfd, f);
147✔
361
        if (r == 0)
147✔
362
                log_debug("Found /etc/locale.conf, assuming locale information has been configured.");
130✔
363
        if (r <= 0)
147✔
364
                return r;
365

366
        r = dir_fd_is_root(rfd);
17✔
367
        if (r < 0)
17✔
UNCOV
368
                return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
×
369

370
        if (arg_copy_locale && r == 0) {
17✔
371
                r = copy_file_atomic_at(AT_FDCWD, etc_locale_conf(), pfd, f, 0644, COPY_REFLINK);
3✔
372
                if (r != -ENOENT) {
3✔
373
                        if (r < 0)
3✔
UNCOV
374
                                return log_error_errno(r, "Failed to copy host's /etc/locale.conf: %m");
×
375

376
                        log_info("Copied host's /etc/locale.conf.");
3✔
377
                        return 0;
378
                }
379
        }
380

381
        r = prompt_locale(rfd, mute_console_link);
14✔
382
        if (r < 0)
14✔
383
                return r;
384

385
        if (!isempty(arg_locale))
14✔
386
                locales[i++] = strjoina("LANG=", arg_locale);
30✔
387
        if (!isempty(arg_locale_messages) && !streq_ptr(arg_locale_messages, arg_locale))
19✔
388
                locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages);
25✔
389

390
        if (i == 0)
14✔
391
                return 0;
392

393
        locales[i] = NULL;
7✔
394

395
        r = write_env_file(
7✔
396
                        pfd,
397
                        f,
398
                        /* headers= */ NULL,
399
                        locales,
400
                        WRITE_ENV_FILE_LABEL);
401
        if (r < 0)
7✔
UNCOV
402
                return log_error_errno(r, "Failed to write /etc/locale.conf: %m");
×
403

404
        log_info("/etc/locale.conf written.");
7✔
405
        return 1;
406
}
407

408
static int keymap_is_ok(const char* name, void *userdata) {
5✔
409
        int rfd = ASSERT_FD(PTR_TO_FD(userdata)), r;
5✔
410

411
        r = dir_fd_is_root(rfd);
5✔
412
        if (r < 0)
5✔
UNCOV
413
                log_debug_errno(r, "Unable to determine if operating on host root directory, assuming we are: %m");
×
414

415
        return r != 0 ? keymap_exists(name) > 0 : keymap_is_valid(name);
5✔
416
}
417

418
static int prompt_keymap(int rfd, sd_varlink **mute_console_link) {
22✔
419
        _cleanup_strv_free_ char **kmaps = NULL;
22✔
420
        int r;
22✔
421

422
        assert(rfd >= 0);
22✔
423

424
        if (arg_keymap)
22✔
425
                return 0;
426

427
        _cleanup_free_ char *km = NULL;
19✔
428
        r = read_credential("firstboot.keymap", (void**) &km, NULL);
19✔
429
        if (r < 0)
19✔
430
                log_debug_errno(r, "Failed to read credential firstboot.keymap, ignoring: %m");
18✔
431
        else if (!keymap_is_valid(km))
1✔
UNCOV
432
                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap '%s' supplied via credential is not valid, ignoring.", km);
×
433
        else {
434
                log_debug("Acquired keymap from credential.");
1✔
435
                arg_keymap = TAKE_PTR(km);
1✔
436
                return 0;
1✔
437
        }
438

439
        bool b;
18✔
440
        if (arg_prompt_keymap_auto) {
18✔
441
                _cleanup_free_ char *ttyname = NULL;
1✔
442

443
                r = getttyname_harder(STDOUT_FILENO, &ttyname);
1✔
444
                if (r < 0) {
1✔
445
                        log_debug_errno(r, "Cannot determine TTY we are connected, ignoring: %m");
1✔
446
                        b = false; /* if we can't resolve this, it's probably not a VT */
447
                } else {
UNCOV
448
                        b = tty_is_vc_resolve(ttyname);
×
UNCOV
449
                        log_debug("Detected connection to local console: %s", yes_no(b));
×
450
                }
451
        } else
452
                b = arg_prompt_keymap;
17✔
453
        if (!b) {
18✔
454
                log_debug("Prompting for keymap was not requested.");
17✔
455
                return 0;
456
        }
457

458
        r = get_keymaps(&kmaps);
1✔
459
        if (r == -ENOENT) /* no keymaps installed */
1✔
UNCOV
460
                return log_debug_errno(r, "No keymaps are installed.");
×
461
        if (r < 0)
1✔
UNCOV
462
                return log_error_errno(r, "Failed to read keymaps: %m");
×
463

464
        print_welcome(rfd, mute_console_link);
1✔
465

466
        _cleanup_free_ char *prefill = NULL;
1✔
467
        (void) vconsole_keymap_from_efi(&prefill);
1✔
468

469
        return prompt_loop(
1✔
470
                        "Please enter the new keymap name or number",
471
                        GLYPH_KEYBOARD,
472
                        prefill,
473
                        kmaps,
474
                        /* accepted= */ NULL,
475
                        /* ellipsize_percentage= */ 60,
476
                        /* n_columns= */ 3,
477
                        /* column_width= */ 20,
478
                        keymap_is_ok,
479
                        /* refresh= */ NULL,
480
                        FD_TO_PTR(rfd),
1✔
481
                        PROMPT_MAY_SKIP|PROMPT_SHOW_MENU,
482
                        &arg_keymap);
483
}
484

485
static int process_keymap(int rfd, sd_varlink **mute_console_link) {
147✔
486
        _cleanup_close_ int pfd = -EBADF;
147✔
487
        _cleanup_free_ char *f = NULL;
147✔
UNCOV
488
        _cleanup_strv_free_ char **keymap = NULL;
×
489
        int r;
147✔
490

491
        assert(rfd >= 0);
147✔
492

493
        pfd = chase_and_open_parent_at(rfd, rfd, etc_vconsole_conf(),
147✔
494
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
495
                                       &f);
496
        if (pfd < 0)
147✔
UNCOV
497
                return log_error_errno(pfd, "Failed to chase /etc/vconsole.conf: %m");
×
498

499
        r = should_configure(pfd, f);
147✔
500
        if (r == 0)
147✔
501
                log_debug("Found /etc/vconsole.conf, assuming console has been configured.");
122✔
502
        if (r <= 0)
147✔
503
                return r;
504

505
        r = dir_fd_is_root(rfd);
25✔
506
        if (r < 0)
25✔
UNCOV
507
                return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
×
508

509
        if (arg_copy_keymap && r == 0) {
25✔
510
                r = copy_file_atomic_at(AT_FDCWD, etc_vconsole_conf(), pfd, f, 0644, COPY_REFLINK);
3✔
511
                if (r != -ENOENT) {
3✔
512
                        if (r < 0)
3✔
UNCOV
513
                                return log_error_errno(r, "Failed to copy host's /etc/vconsole.conf: %m");
×
514

515
                        log_info("Copied host's /etc/vconsole.conf.");
3✔
516
                        return 0;
517
                }
518
        }
519

520
        r = prompt_keymap(rfd, mute_console_link);
22✔
521
        if (r == -ENOENT)
22✔
522
                return 0; /* don't fail if no keymaps are installed */
523
        if (r < 0)
22✔
524
                return r;
525

526
        if (isempty(arg_keymap))
152✔
527
                return 0;
528

529
        VCContext vc = {
5✔
530
                .keymap = arg_keymap,
531
        };
532
        _cleanup_(x11_context_clear) X11Context xc = {};
5✔
533

534
        r = vconsole_convert_to_x11(&vc, /* verify= */ NULL, &xc);
5✔
535
        if (r < 0)
5✔
UNCOV
536
                return log_error_errno(r, "Failed to convert keymap data: %m");
×
537

538
        r = vconsole_serialize(&vc, &xc, &keymap);
5✔
539
        if (r < 0)
5✔
UNCOV
540
                return log_error_errno(r, "Failed to serialize keymap data: %m");
×
541

542
        r = write_vconsole_conf(pfd, f, keymap);
5✔
543
        if (r < 0)
5✔
UNCOV
544
                return log_error_errno(r, "Failed to write /etc/vconsole.conf: %m");
×
545

546
        log_info("/etc/vconsole.conf written.");
5✔
547
        return 1;
548
}
549

550
static int timezone_is_ok(const char *name, void *userdata) {
1✔
551
        return timezone_is_valid(name, LOG_DEBUG);
1✔
552
}
553

554
static int prompt_timezone(int rfd, sd_varlink **mute_console_link) {
128✔
555
        _cleanup_strv_free_ char **zones = NULL;
128✔
556
        int r;
128✔
557

558
        assert(rfd >= 0);
128✔
559

560
        if (arg_timezone)
128✔
561
                return 0;
562

563
        _cleanup_free_ char *tz = NULL;
125✔
564
        r = read_credential("firstboot.timezone", (void**) &tz, NULL);
125✔
565
        if (r < 0)
125✔
566
                log_debug_errno(r, "Failed to read credential firstboot.timezone, ignoring: %m");
15✔
567
        else if (!timezone_is_valid(tz, LOG_DEBUG))
110✔
UNCOV
568
                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' supplied via credential is not valid, ignoring.", tz);
×
569
        else {
570
                log_debug("Acquired timezone from credential.");
110✔
571
                arg_timezone = TAKE_PTR(tz);
110✔
572
                return 0;
110✔
573
        }
574

575
        if (!arg_prompt_timezone) {
15✔
576
                log_debug("Prompting for timezone was not requested.");
14✔
577
                return 0;
578
        }
579

580
        r = get_timezones(&zones);
1✔
581
        if (r < 0)
1✔
UNCOV
582
                return log_error_errno(r, "Cannot query timezone list: %m");
×
583

584
        print_welcome(rfd, mute_console_link);
1✔
585

586
        return prompt_loop(
1✔
587
                        "Please enter the new timezone name or number",
588
                        GLYPH_CLOCK,
589
                        /* prefill= */ NULL,
590
                        zones,
591
                        /* accepted= */ NULL,
592
                        /* ellipsize_percentage= */ 30,
593
                        /* n_columns= */ 3,
594
                        /* column_width= */ 20,
595
                        timezone_is_ok,
596
                        /* refresh= */ NULL,
597
                        FD_TO_PTR(rfd),
1✔
598
                        PROMPT_MAY_SKIP|PROMPT_SHOW_MENU,
599
                        &arg_timezone);
600
}
601

602
static int process_timezone(int rfd, sd_varlink **mute_console_link) {
147✔
603
        _cleanup_close_ int pfd = -EBADF;
147✔
604
        _cleanup_free_ char *f = NULL, *relpath = NULL;
147✔
605
        const char *e;
147✔
606
        int r;
147✔
607

608
        assert(rfd >= 0);
147✔
609

610
        pfd = chase_and_open_parent_at(rfd, rfd, etc_localtime(),
147✔
611
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
612
                                       &f);
613
        if (pfd < 0)
147✔
UNCOV
614
                return log_error_errno(pfd, "Failed to chase /etc/localtime: %m");
×
615

616
        r = should_configure(pfd, f);
147✔
617
        if (r == 0)
147✔
618
                log_debug("Found /etc/localtime, assuming timezone has been configured.");
16✔
619
        if (r <= 0)
147✔
620
                return r;
621

622
        r = dir_fd_is_root(rfd);
131✔
623
        if (r < 0)
131✔
UNCOV
624
                return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
×
625

626
        if (arg_copy_timezone && r == 0) {
131✔
627
                _cleanup_free_ char *s = NULL;
3✔
628

629
                r = readlink_malloc(etc_localtime(), &s);
3✔
630
                if (r != -ENOENT) {
3✔
631
                        if (r < 0)
3✔
UNCOV
632
                                return log_error_errno(r, "Failed to read host's /etc/localtime: %m");
×
633

634
                        r = symlinkat_atomic_full(s, pfd, f, SYMLINK_LABEL);
3✔
635
                        if (r < 0)
3✔
UNCOV
636
                                return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
×
637

638
                        log_info("Copied host's /etc/localtime.");
3✔
639
                        return 0;
640
                }
641
        }
642

643
        r = prompt_timezone(rfd, mute_console_link);
128✔
644
        if (r < 0)
128✔
645
                return r;
646

647
        if (isempty(arg_timezone))
261✔
648
                return 0;
649

650
        e = strjoina("/usr/share/zoneinfo/", arg_timezone);
570✔
651
        r = path_make_relative_parent(etc_localtime(), e, &relpath);
114✔
652
        if (r < 0)
114✔
653
                return r;
654

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

659
        log_info("/etc/localtime written");
114✔
660
        return 0;
661
}
662

663
static int hostname_is_ok(const char *name, void *userdata) {
1✔
664
        return hostname_is_valid(name, VALID_HOSTNAME_TRAILING_DOT);
1✔
665
}
666

667
static int prompt_hostname(int rfd, sd_varlink **mute_console_link) {
133✔
668
        int r;
133✔
669

670
        assert(rfd >= 0);
133✔
671

672
        if (arg_hostname)
133✔
673
                return 0;
133✔
674

675
        _cleanup_free_ char *hn = NULL;
129✔
676
        r = read_credential("firstboot.hostname", (void**) &hn, NULL);
129✔
677
        if (r < 0)
129✔
678
                log_debug_errno(r, "Failed to read credential firstboot.hostname, ignoring: %m");
129✔
UNCOV
679
        else if (!hostname_is_valid(hn, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK))
×
UNCOV
680
                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Hostname '%s' supplied via credential is not valid, ignoring.", hn);
×
681
        else {
UNCOV
682
                log_debug("Acquired hostname from credentials.");
×
UNCOV
683
                arg_hostname = TAKE_PTR(hn);
×
UNCOV
684
                hostname_cleanup(arg_hostname);
×
685
                return 0;
686
        }
687

688
        if (!arg_prompt_hostname) {
129✔
689
                log_debug("Prompting for hostname was not requested.");
128✔
690
                return 0;
691
        }
692

693
        print_welcome(rfd, mute_console_link);
1✔
694

695
        r = prompt_loop("Please enter the new hostname",
2✔
696
                        GLYPH_LABEL,
697
                        /* prefill= */ NULL,
698
                        /* menu= */ NULL,
699
                        /* accepted= */ NULL,
700
                        /* ellipsize_percentage= */ 100,
701
                        /* n_columns= */ 3,
702
                        /* column_width= */ 20,
703
                        hostname_is_ok,
704
                        /* refresh= */ NULL,
705
                        FD_TO_PTR(rfd),
1✔
706
                        PROMPT_MAY_SKIP,
707
                        &arg_hostname);
708
        if (r < 0)
1✔
709
                return r;
710

711
        if (arg_hostname)
1✔
712
                hostname_cleanup(arg_hostname);
1✔
713

714
        return 0;
715
}
716

717
static int process_hostname(int rfd, sd_varlink **mute_console_link) {
147✔
718
        _cleanup_close_ int pfd = -EBADF;
147✔
719
        _cleanup_free_ char *f = NULL;
147✔
720
        int r;
147✔
721

722
        assert(rfd >= 0);
147✔
723

724
        pfd = chase_and_open_parent_at(rfd, rfd, etc_hostname(), CHASE_MKDIR_0755|CHASE_WARN, &f);
147✔
725
        if (pfd < 0)
147✔
UNCOV
726
                return log_error_errno(pfd, "Failed to chase /etc/hostname: %m");
×
727

728
        r = should_configure(pfd, f);
147✔
729
        if (r == 0)
147✔
730
                log_debug("Found /etc/hostname, assuming hostname has been configured.");
14✔
731
        if (r <= 0)
147✔
732
                return r;
733

734
        r = prompt_hostname(rfd, mute_console_link);
133✔
735
        if (r < 0)
133✔
736
                return r;
737

738
        if (isempty(arg_hostname))
152✔
739
                return 0;
740

741
        r = write_string_file_at(pfd, f, arg_hostname,
5✔
742
                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
743
        if (r < 0)
5✔
UNCOV
744
                return log_error_errno(r, "Failed to write /etc/hostname: %m");
×
745

746
        log_info("/etc/hostname written.");
5✔
747
        return 0;
748
}
749

750
static int process_machine_id(int rfd) {
147✔
751
        _cleanup_close_ int pfd = -EBADF;
147✔
752
        _cleanup_free_ char *f = NULL;
147✔
753
        int r;
147✔
754

755
        assert(rfd >= 0);
147✔
756

757
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/machine-id",
147✔
758
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
759
                                       &f);
760
        if (pfd < 0)
147✔
UNCOV
761
                return log_error_errno(pfd, "Failed to chase /etc/machine-id: %m");
×
762

763
        r = should_configure(pfd, f);
147✔
764
        if (r == 0)
147✔
765
                log_debug("Found /etc/machine-id, assuming machine-id has been configured.");
119✔
766
        if (r <= 0)
147✔
767
                return r;
768

769
        if (sd_id128_is_null(arg_machine_id)) {
28✔
770
                log_debug("Initialization of machine-id was not requested, skipping.");
25✔
771
                return 0;
772
        }
773

774
        r = write_string_file_at(pfd, "machine-id", SD_ID128_TO_STRING(arg_machine_id),
3✔
775
                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
776
        if (r < 0)
3✔
UNCOV
777
                return log_error_errno(r, "Failed to write /etc/machine-id: %m");
×
778

779
        log_info("/etc/machine-id written.");
3✔
780
        return 0;
781
}
782

783
static int process_machine_tags(int rfd) {
147✔
784
        int r;
147✔
785

786
        assert(rfd >= 0);
147✔
787

788
        _cleanup_free_ char *f = NULL;
147✔
789
        _cleanup_close_ int pfd = chase_and_open_parent_at(
294✔
790
                        /* root_fd= */ rfd,
791
                        /* dir_fd= */ rfd,
792
                        "/etc/machine-info",
793
                        CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
794
                        &f);
795
        if (pfd < 0)
147✔
UNCOV
796
                return log_error_errno(pfd, "Failed to chase /etc/machine-info parent: %m");
×
797

798
        r = should_configure(pfd, f);
147✔
799
        if (r == 0)
147✔
UNCOV
800
                log_debug("Found /etc/machine-info, assuming machine tags have been configured.");
×
801
        if (r <= 0)
147✔
802
                return r;
803

804
        if (!arg_machine_tags) {
147✔
805
                _cleanup_free_ char *tags = NULL;
146✔
806
                r = read_credential("firstboot.machine-tags", (void**) &tags, /* ret_size= */ NULL);
146✔
807
                if (r < 0)
146✔
808
                        log_debug_errno(r, "Failed to read credential firstboot.machine-tags, ignoring: %m");
146✔
809
                else {
UNCOV
810
                        _cleanup_strv_free_ char **l = NULL;
×
811
                        r = machine_tags_from_string(tags, /* graceful= */ false, &l);
2✔
812
                        if (r < 0)
2✔
813
                                log_warning_errno(r, "Failed to parse machine tags '%s', ignoring credential: %m", tags);
2✔
814
                        else {
815
                                strv_free_and_replace(arg_machine_tags, l);
1✔
816
                                log_debug("Acquired machine tags list from credentials.");
1✔
817
                        }
818
                }
819
        }
820

821
        /* NB: We do not prompt for machine tags, at least not for now */
822

823
        if (!arg_machine_tags) {
147✔
824
                log_debug("Initialization of machine tags was not requested, skipping.");
145✔
825
                return 0;
826
        }
827

828
        _cleanup_free_ char *j = strv_join(arg_machine_tags, ":");
4✔
829
        if (!j)
2✔
UNCOV
830
                return log_oom();
×
831

832
        _cleanup_free_ char *c = strjoin("TAGS=\"", j, "\"\n");
4✔
833
        if (!c)
2✔
834
                return log_oom();
×
835

836
        r = write_string_file_at(
2✔
837
                        pfd,
838
                        "machine-info",
839
                        c,
840
                        WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
841
        if (r < 0)
2✔
UNCOV
842
                return log_error_errno(r, "Failed to write /etc/machine-info: %m");
×
843

844
        log_info("/etc/machine-info written.");
2✔
845
        return 0;
846
}
847

848
static int prompt_root_password(int rfd, sd_varlink **mute_console_link) {
33✔
849
        const char *msg1, *msg2;
33✔
850
        int r;
33✔
851

852
        assert(rfd >= 0);
33✔
853

854
        if (arg_root_password)
33✔
855
                return 0;
856

857
        if (get_credential_user_password("root", &arg_root_password, &arg_root_password_is_hashed) >= 0)
24✔
858
                return 0;
859

860
        if (!arg_prompt_root_password) {
23✔
861
                log_debug("Prompting for root password was not requested.");
22✔
862
                return 0;
863
        }
864

865
        print_welcome(rfd, mute_console_link);
1✔
866

867
        msg1 = "Please enter the new root password (empty to skip):";
1✔
868
        msg2 = "Please enter the new root password again:";
1✔
869

870
        suggest_passwords();
1✔
871

UNCOV
872
        for (;;) {
×
873
                _cleanup_strv_free_erase_ char **a = NULL, **b = NULL;
1✔
874
                _cleanup_free_ char *error = NULL;
1✔
875

876
                AskPasswordRequest req = {
1✔
877
                        .tty_fd = -EBADF,
878
                        .message = msg1,
879
                        .until = USEC_INFINITY,
880
                        .hup_fd = -EBADF,
881
                };
882

883
                r = ask_password_tty(&req, /* flags= */ 0, &a);
1✔
884
                if (r < 0)
1✔
UNCOV
885
                        return log_error_errno(r, "Failed to query root password: %m");
×
886
                if (strv_length(a) != 1)
1✔
UNCOV
887
                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
888
                                               "Received multiple passwords, where we expected one.");
889

890
                if (isempty(*a)) {
1✔
891
                        log_info("No password entered, skipping.");
1✔
892
                        break;
893
                }
894

UNCOV
895
                r = check_password_quality(*a, /* old= */ NULL, "root", &error);
×
UNCOV
896
                if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
×
UNCOV
897
                        log_warning("Password quality check is not supported, proceeding anyway.");
×
UNCOV
898
                else if (r < 0)
×
UNCOV
899
                        return log_error_errno(r, "Failed to check password quality: %m");
×
UNCOV
900
                else if (r == 0)
×
UNCOV
901
                        log_warning("Password is weak, accepting anyway: %s", error);
×
902

UNCOV
903
                req.message = msg2;
×
904

UNCOV
905
                r = ask_password_tty(&req, /* flags= */ 0, &b);
×
UNCOV
906
                if (r < 0)
×
UNCOV
907
                        return log_error_errno(r, "Failed to query root password: %m");
×
UNCOV
908
                if (strv_length(b) != 1)
×
UNCOV
909
                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
910
                                               "Received multiple passwords, where we expected one.");
911

UNCOV
912
                if (!streq(*a, *b)) {
×
UNCOV
913
                        log_error("Entered passwords did not match, please try again.");
×
UNCOV
914
                        continue;
×
915
                }
916

UNCOV
917
                arg_root_password = TAKE_PTR(*a);
×
UNCOV
918
                break;
×
919
        }
920

921
        return 0;
1✔
922
}
923

924
static int find_shell(int rfd, const char *path) {
7✔
925
        int r;
7✔
926

927
        assert(path);
7✔
928

929
        if (!valid_shell(path))
7✔
UNCOV
930
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a valid shell", path);
×
931

932
        r = chaseat(rfd, rfd, path, /* flags= */ 0, /* ret_path= */ NULL, /* ret_fd= */ NULL);
7✔
933
        if (r < 0)
7✔
934
                return log_error_errno(r, "Failed to resolve shell %s: %m", path);
1✔
935

936
        return 0;
937
}
938

939
static int shell_is_ok(const char *path, void *userdata) {
2✔
940
        int rfd = ASSERT_FD(PTR_TO_FD(userdata));
2✔
941

942
        return find_shell(rfd, path) >= 0;
2✔
943
}
944

945
static int prompt_root_shell(int rfd, sd_varlink **mute_console_link) {
33✔
946
        int r;
33✔
947

948
        assert(rfd >= 0);
33✔
949

950
        if (arg_root_shell)
33✔
951
                return 0;
952

953
        r = read_credential("passwd.shell.root", (void**) &arg_root_shell, NULL);
28✔
954
        if (r < 0)
28✔
955
                log_debug_errno(r, "Failed to read credential passwd.shell.root, ignoring: %m");
27✔
956
        else {
957
                log_debug("Acquired root shell from credential.");
1✔
958
                return 0;
959
        }
960

961
        if (!arg_prompt_root_shell) {
27✔
962
                log_debug("Prompting for root shell was not requested.");
25✔
963
                return 0;
964
        }
965

966
        print_welcome(rfd, mute_console_link);
2✔
967

968
        return prompt_loop(
2✔
969
                        "Please enter the new root shell",
970
                        GLYPH_SHELL,
971
                        /* prefill= */ NULL,
972
                        /* menu= */ NULL,
973
                        /* accepted= */ NULL,
974
                        /* ellipsize_percentage= */ 0,
975
                        /* n_columns= */ 3,
976
                        /* column_width= */ 20,
977
                        shell_is_ok,
978
                        /* refresh= */ NULL,
979
                        FD_TO_PTR(rfd),
2✔
980
                        PROMPT_MAY_SKIP,
981
                        &arg_root_shell);
982
}
983

984
static int write_root_passwd(int rfd, int etc_fd, const char *password, const char *shell) {
15✔
985
        _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
15✔
986
        _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
15✔
987
        int r;
15✔
988
        bool found = false;
15✔
989

990
        r = fopen_temporary_at_label(etc_fd, "passwd", "passwd", &passwd, &passwd_tmp);
15✔
991
        if (r < 0)
15✔
992
                return r;
993

994
        r = xfopenat(etc_fd, "passwd", "re", O_NOFOLLOW, &original);
15✔
995
        if (r < 0 && r != -ENOENT)
15✔
996
                return r;
997

998
        if (original) {
15✔
999
                struct passwd *i;
4✔
1000

1001
                r = copy_rights(fileno(original), fileno(passwd));
4✔
1002
                if (r < 0)
4✔
UNCOV
1003
                        return r;
×
1004

1005
                while ((r = fgetpwent_sane(original, &i)) > 0) {
7✔
1006

1007
                        if (streq(i->pw_name, "root")) {
3✔
1008
                                if (password)
3✔
1009
                                        i->pw_passwd = (char *) password;
2✔
1010
                                if (shell)
3✔
1011
                                        i->pw_shell = (char *) shell;
2✔
1012
                                found = true;
1013
                        }
1014

1015
                        r = putpwent_sane(i, passwd);
3✔
1016
                        if (r < 0)
3✔
1017
                                return r;
1018
                }
1019
                if (r < 0)
4✔
1020
                        return r;
1021

1022
        } else {
1023
                r = fchmod(fileno(passwd), 0644);
11✔
1024
                if (r < 0)
11✔
UNCOV
1025
                        return -errno;
×
1026
        }
1027

1028
        if (!found) {
4✔
1029
                struct passwd root = {
12✔
1030
                        .pw_name = (char *) "root",
1031
                        .pw_passwd = (char *) (password ?: PASSWORD_SEE_SHADOW),
12✔
1032
                        .pw_uid = 0,
1033
                        .pw_gid = 0,
1034
                        .pw_gecos = (char *) "Super User",
1035
                        .pw_dir = (char *) "/root",
1036
                        .pw_shell = (char *) (shell ?: default_root_shell_at(rfd)),
6✔
1037
                };
1038

1039
                r = putpwent_sane(&root, passwd);
12✔
1040
                if (r < 0)
12✔
UNCOV
1041
                        return r;
×
1042
        }
1043

1044
        r = fflush_sync_and_check(passwd);
15✔
1045
        if (r < 0)
15✔
1046
                return r;
1047

1048
        r = renameat_and_apply_smack_floor_label(etc_fd, passwd_tmp, etc_fd, "passwd");
15✔
1049
        if (r < 0)
15✔
UNCOV
1050
                return r;
×
1051

1052
        return 0;
1053
}
1054

1055
static int write_root_shadow(int etc_fd, const char *hashed_password) {
15✔
1056
        _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
15✔
1057
        _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
15✔
1058
        int r;
15✔
1059
        bool found = false;
15✔
1060

1061
        r = fopen_temporary_at_label(etc_fd, "shadow", "shadow", &shadow, &shadow_tmp);
15✔
1062
        if (r < 0)
15✔
1063
                return r;
1064

1065
        r = xfopenat(etc_fd, "shadow", "re", O_NOFOLLOW, &original);
15✔
1066
        if (r < 0 && r != -ENOENT)
15✔
1067
                return r;
1068

1069
        if (original) {
15✔
1070
                struct spwd *i;
4✔
1071

1072
                r = copy_rights(fileno(original), fileno(shadow));
4✔
1073
                if (r < 0)
4✔
UNCOV
1074
                        return r;
×
1075

1076
                while ((r = fgetspent_sane(original, &i)) > 0) {
7✔
1077

1078
                        if (streq(i->sp_namp, "root")) {
3✔
1079
                                if (hashed_password) {
3✔
1080
                                        i->sp_pwdp = (char *) hashed_password;
2✔
1081
                                        i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
2✔
1082
                                }
1083
                                found = true;
1084
                        }
1085

1086
                        r = putspent_sane(i, shadow);
3✔
1087
                        if (r < 0)
3✔
1088
                                return r;
1089
                }
1090
                if (r < 0)
4✔
1091
                        return r;
1092

1093
        } else {
1094
                r = fchmod(fileno(shadow), 0000);
11✔
1095
                if (r < 0)
11✔
UNCOV
1096
                        return -errno;
×
1097
        }
1098

1099
        if (!found) {
4✔
1100
                struct spwd root = {
36✔
1101
                        .sp_namp = (char*) "root",
1102
                        .sp_pwdp = (char *) (hashed_password ?: PASSWORD_LOCKED_AND_INVALID),
12✔
1103
                        .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY),
12✔
1104
                        .sp_min = -1,
1105
                        .sp_max = -1,
1106
                        .sp_warn = -1,
1107
                        .sp_inact = -1,
1108
                        .sp_expire = -1,
1109
                        .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
1110
                };
1111

1112
                r = putspent_sane(&root, shadow);
12✔
1113
                if (r < 0)
12✔
UNCOV
1114
                        return r;
×
1115
        }
1116

1117
        r = fflush_sync_and_check(shadow);
15✔
1118
        if (r < 0)
15✔
1119
                return r;
1120

1121
        r = renameat_and_apply_smack_floor_label(etc_fd, shadow_tmp, etc_fd, "shadow");
15✔
1122
        if (r < 0)
15✔
UNCOV
1123
                return r;
×
1124

1125
        return 0;
1126
}
1127

1128
static int process_root_account(int rfd, sd_varlink **mute_console_link) {
147✔
1129
        _cleanup_close_ int pfd = -EBADF;
147✔
1130
        _cleanup_(release_lock_file) LockFile lock = LOCK_FILE_INIT;
×
1131
        _cleanup_(erase_and_freep) char *_hashed_password = NULL;
147✔
1132
        const char *password, *hashed_password;
147✔
1133
        int k = 0, r;
147✔
1134

1135
        assert(rfd >= 0);
147✔
1136

1137
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/passwd",
147✔
1138
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
1139
                                       NULL);
1140
        if (pfd < 0)
147✔
UNCOV
1141
                return log_error_errno(pfd, "Failed to chase /etc/passwd: %m");
×
1142

1143
        /* Ensure that passwd and shadow are in the same directory and are not symlinks. */
1144

1145
        FOREACH_STRING(s, "passwd", "shadow") {
441✔
1146
                r = verify_regular_at(pfd, s, /* follow= */ false);
294✔
1147
                if (r < 0 && r != -ENOENT)
294✔
UNCOV
1148
                        return log_error_errno(r, "Verification of /etc/%s being regular file failed: %m", s);
×
1149

1150
                r = should_configure(pfd, s);
294✔
1151
                if (r < 0)
294✔
1152
                        return r;
1153

1154
                k += r;
294✔
1155
        }
1156

1157
        if (k == 0) {
147✔
1158
                log_debug("Found /etc/passwd and /etc/shadow, assuming root account has been initialized.");
114✔
1159
                return 0;
1160
        }
1161

1162
        r = make_lock_file_at(pfd, ETC_PASSWD_LOCK_FILENAME, LOCK_EX, &lock);
33✔
1163
        if (r < 0)
33✔
UNCOV
1164
                return log_error_errno(r, "Failed to take a lock on /etc/passwd: %m");
×
1165

1166
        k = dir_fd_is_root(rfd);
33✔
1167
        if (k < 0)
33✔
UNCOV
1168
                return log_error_errno(k, "Failed to check if directory file descriptor is root: %m");
×
1169

1170
        if (arg_copy_root_shell && k == 0) {
33✔
1171
                _cleanup_free_ struct passwd *p = NULL;
2✔
1172

1173
                r = getpwnam_malloc("root", &p);
2✔
1174
                if (r < 0)
2✔
UNCOV
1175
                        return log_error_errno(r, "Failed to find passwd entry for root: %m");
×
1176

1177
                r = free_and_strdup(&arg_root_shell, p->pw_shell);
2✔
1178
                if (r < 0)
2✔
UNCOV
1179
                        return log_oom();
×
1180
        }
1181

1182
        r = prompt_root_shell(rfd, mute_console_link);
33✔
1183
        if (r < 0)
33✔
1184
                return r;
1185

1186
        if (arg_copy_root_password && k == 0) {
33✔
1187
                struct spwd *p;
2✔
1188

1189
                errno = 0;
2✔
1190
                p = getspnam("root");
2✔
1191
                if (!p)
2✔
UNCOV
1192
                        return log_error_errno(errno_or_else(EIO), "Failed to find shadow entry for root: %m");
×
1193

1194
                r = free_and_strdup(&arg_root_password, p->sp_pwdp);
2✔
1195
                if (r < 0)
2✔
UNCOV
1196
                        return log_oom();
×
1197

1198
                arg_root_password_is_hashed = true;
2✔
1199
        }
1200

1201
        r = prompt_root_password(rfd, mute_console_link);
33✔
1202
        if (r < 0)
33✔
1203
                return r;
1204

1205
        if (arg_root_password && arg_root_password_is_hashed) {
33✔
1206
                password = PASSWORD_SEE_SHADOW;
1207
                hashed_password = arg_root_password;
1208
        } else if (arg_root_password) {
27✔
1209
                r = hash_password(arg_root_password, &_hashed_password);
4✔
1210
                if (r < 0)
4✔
UNCOV
1211
                        return log_error_errno(r, "Failed to hash password: %m");
×
1212

1213
                password = PASSWORD_SEE_SHADOW;
4✔
1214
                hashed_password = _hashed_password;
4✔
1215

1216
        } else if (arg_delete_root_password) {
23✔
1217
                password = PASSWORD_SEE_SHADOW;
1218
                hashed_password = PASSWORD_NONE;
1219
        } else if (!arg_root_password && arg_prompt_root_password) {
22✔
1220
                /* If the user was prompted, but no password was supplied, lock the account. */
1221
                password = PASSWORD_SEE_SHADOW;
1222
                hashed_password = PASSWORD_LOCKED_AND_INVALID;
1223
        } else
1224
                /* Leave the password as is. */
1225
                password = hashed_password = NULL;
21✔
1226

1227
        /* Don't create/modify passwd and shadow if there's nothing to do. */
1228
        if (!(password || hashed_password || arg_root_shell)) {
33✔
1229
                log_debug("Initialization of root account was not requested, skipping.");
18✔
1230
                return 0;
1231
        }
1232

1233
        r = write_root_passwd(rfd, pfd, password, arg_root_shell);
15✔
1234
        if (r < 0)
15✔
UNCOV
1235
                return log_error_errno(r, "Failed to write /etc/passwd: %m");
×
1236

1237
        log_info("/etc/passwd written.");
15✔
1238

1239
        r = write_root_shadow(pfd, hashed_password);
15✔
1240
        if (r < 0)
15✔
UNCOV
1241
                return log_error_errno(r, "Failed to write /etc/shadow: %m");
×
1242

1243
        log_info("/etc/shadow written.");
15✔
1244
        return 0;
1245
}
1246

1247
static int process_kernel_cmdline(int rfd) {
147✔
1248
        _cleanup_close_ int pfd = -EBADF;
147✔
1249
        _cleanup_free_ char *f = NULL;
147✔
1250
        int r;
147✔
1251

1252
        assert(rfd >= 0);
147✔
1253

1254
        pfd = chase_and_open_parent_at(rfd, rfd, "/etc/kernel/cmdline",
147✔
1255
                                       CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
1256
                                       &f);
1257
        if (pfd < 0)
147✔
UNCOV
1258
                return log_error_errno(pfd, "Failed to chase /etc/kernel/cmdline: %m");
×
1259

1260
        r = should_configure(pfd, f);
147✔
1261
        if (r == 0)
147✔
1262
                log_debug("Found /etc/kernel/cmdline, assuming kernel command line has been configured.");
1✔
1263
        if (r <= 0)
147✔
1264
                return r;
1265

1266
        if (!arg_kernel_cmdline) {
146✔
1267
                log_debug("Creation of /etc/kernel/cmdline was not requested, skipping.");
144✔
1268
                return 0;
1269
        }
1270

1271
        r = write_string_file_at(pfd, "cmdline", arg_kernel_cmdline,
2✔
1272
                                 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
1273
        if (r < 0)
2✔
UNCOV
1274
                return log_error_errno(r, "Failed to write /etc/kernel/cmdline: %m");
×
1275

1276
        log_info("/etc/kernel/cmdline written.");
2✔
1277
        return 0;
1278
}
1279

1280
static int reset_one(int rfd, const char *path) {
6✔
1281
        _cleanup_close_ int pfd = -EBADF;
6✔
1282
        _cleanup_free_ char *f = NULL;
6✔
1283

1284
        assert(rfd >= 0);
6✔
1285
        assert(path);
6✔
1286

1287
        pfd = chase_and_open_parent_at(rfd, rfd, path, CHASE_WARN|CHASE_NOFOLLOW, &f);
6✔
1288
        if (pfd == -ENOENT)
6✔
1289
                return 0;
1290
        if (pfd < 0)
6✔
1291
                return log_error_errno(pfd, "Failed to resolve %s: %m", path);
×
1292

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

1296
        log_info("Removed %s", path);
6✔
1297
        return 0;
1298
}
1299

1300
static int process_reset(int rfd) {
147✔
1301
        int r;
147✔
1302

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

1305
        if (!arg_reset)
147✔
1306
                return 0;
1307

1308
        FOREACH_STRING(p,
7✔
1309
                       etc_locale_conf(),
1310
                       etc_vconsole_conf(),
1311
                       etc_hostname(),
1312
                       "/etc/machine-id",
1313
                       "/etc/kernel/cmdline",
1314
                       etc_localtime()) {
1315
                r = reset_one(rfd, p);
6✔
1316
                if (r < 0)
6✔
1317
                        return r;
×
1318
        }
1319

1320
        return 0;
1✔
1321
}
1322

UNCOV
1323
static int help(void) {
×
UNCOV
1324
        _cleanup_(table_unrefp) Table *options = NULL;
×
UNCOV
1325
        int r;
×
1326

UNCOV
1327
        r = option_parser_get_help_table(&options);
×
UNCOV
1328
        if (r < 0)
×
1329
                return r;
1330

UNCOV
1331
        help_cmdline("[OPTIONS...]");
×
UNCOV
1332
        help_abstract("Configures basic settings of the system.");
×
UNCOV
1333
        help_section("Options");
×
1334

UNCOV
1335
        r = table_print_or_warn(options);
×
UNCOV
1336
        if (r < 0)
×
1337
                return r;
1338

UNCOV
1339
        help_man_page_reference("systemd-firstboot", "1");
×
1340
        return 0;
1341
}
1342

1343
static int parse_argv(int argc, char *argv[]) {
151✔
1344
        assert(argc >= 0);
151✔
1345
        assert(argv);
151✔
1346

1347
        OptionParser opts = { argc, argv };
151✔
1348
        int r;
151✔
1349

1350
        FOREACH_OPTION_OR_RETURN(c, &opts)
1,113✔
1351
                switch (c) {
663✔
1352

UNCOV
1353
                OPTION_COMMON_HELP:
×
UNCOV
1354
                        return help();
×
1355

UNCOV
1356
                OPTION_COMMON_VERSION:
×
UNCOV
1357
                        return version();
×
1358

1359
                OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
42✔
1360
                        r = parse_path_argument(opts.arg, true, &arg_root);
42✔
1361
                        if (r < 0)
42✔
1362
                                return r;
1363
                        break;
1364

UNCOV
1365
                OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
×
UNCOV
1366
                        r = parse_path_argument(opts.arg, false, &arg_image);
×
UNCOV
1367
                        if (r < 0)
×
1368
                                return r;
1369
                        break;
1370

UNCOV
1371
                OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
×
1372
                        r = parse_image_policy_argument(opts.arg, &arg_image_policy);
×
UNCOV
1373
                        if (r < 0)
×
1374
                                return r;
1375
                        break;
1376

1377
                OPTION_LONG("locale", "LOCALE", "Set primary locale (LANG=)"):
5✔
1378
                        r = free_and_strdup_warn(&arg_locale, opts.arg);
5✔
1379
                        if (r < 0)
5✔
1380
                                return r;
1381
                        break;
1382

1383
                OPTION_LONG("locale-messages", "LOCALE", "Set message locale (LC_MESSAGES=)"):
4✔
1384
                        r = free_and_strdup_warn(&arg_locale_messages, opts.arg);
4✔
1385
                        if (r < 0)
4✔
1386
                                return r;
1387
                        break;
1388

1389
                OPTION_LONG("keymap", "KEYMAP", "Set keymap"):
4✔
1390
                        if (!keymap_is_valid(opts.arg))
4✔
UNCOV
1391
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1392
                                                       "Keymap %s is not valid.", opts.arg);
1393

1394
                        r = free_and_strdup_warn(&arg_keymap, opts.arg);
4✔
1395
                        if (r < 0)
4✔
1396
                                return r;
1397
                        break;
1398

1399
                OPTION_LONG("timezone", "TIMEZONE", "Set timezone"):
5✔
1400
                        if (!timezone_is_valid(opts.arg, LOG_ERR))
5✔
1401
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
1402
                                                       "Timezone %s is not valid.", opts.arg);
1403

1404
                        r = free_and_strdup_warn(&arg_timezone, opts.arg);
4✔
1405
                        if (r < 0)
4✔
1406
                                return r;
1407
                        break;
1408

1409
                OPTION_LONG("hostname", "NAME", "Set hostname"):
5✔
1410
                        if (!hostname_is_valid(opts.arg, VALID_HOSTNAME_TRAILING_DOT|VALID_HOSTNAME_QUESTION_MARK))
5✔
UNCOV
1411
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1412
                                                       "Host name %s is not valid.", opts.arg);
1413

1414
                        r = free_and_strdup_warn(&arg_hostname, opts.arg);
5✔
1415
                        if (r < 0)
5✔
1416
                                return r;
1417

1418
                        hostname_cleanup(arg_hostname);
5✔
1419
                        break;
1420

1421
                OPTION_LONG("setup-machine-id", NULL, "Set a random machine ID"):
1✔
1422
                        r = sd_id128_randomize(&arg_machine_id);
1✔
1423
                        if (r < 0)
1✔
UNCOV
1424
                                return log_error_errno(r, "Failed to generate randomized machine ID: %m");
×
1425
                        break;
1426

1427
                OPTION_LONG("machine-id", "ID", "Set specified machine ID"):
4✔
1428
                        r = sd_id128_from_string(opts.arg, &arg_machine_id);
4✔
1429
                        if (r < 0)
4✔
1430
                                return log_error_errno(r, "Failed to parse machine id %s.", opts.arg);
1✔
1431
                        break;
1432

1433
                OPTION_LONG("machine-tags", "TAG[:…]", "Set machine tags"): {
2✔
1434
                        _cleanup_strv_free_ char **tags = NULL;
1✔
1435
                        r = machine_tags_from_string(opts.arg, /* graceful= */ false, &tags);
2✔
1436
                        if (r < 0)
2✔
1437
                                return log_error_errno(r, "Failed to parse machine tags '%s': %m", opts.arg);
1✔
1438

1439
                        strv_free_and_replace(arg_machine_tags, tags);
1✔
1440
                        break;
1✔
1441
                }
1442

1443
                OPTION_LONG("root-password", "PASSWORD", "Set root password from plaintext password"):
4✔
1444
                        r = free_and_strdup_warn(&arg_root_password, opts.arg);
4✔
1445
                        if (r < 0)
4✔
1446
                                return r;
1447

1448
                        arg_root_password_is_hashed = false;
4✔
1449
                        break;
4✔
1450

1451
                OPTION_LONG("root-password-file", "FILE", "Set root password from file"):
1✔
1452
                        arg_root_password = mfree(arg_root_password);
1✔
1453

1454
                        r = read_one_line_file(opts.arg, &arg_root_password);
1✔
1455
                        if (r < 0)
1✔
UNCOV
1456
                                return log_error_errno(r, "Failed to read %s: %m", opts.arg);
×
1457

1458
                        arg_root_password_is_hashed = false;
1✔
1459
                        break;
1✔
1460

1461
                OPTION_LONG("root-password-hashed", "HASH", "Set root password from hashed password"):
4✔
1462
                        r = free_and_strdup_warn(&arg_root_password, opts.arg);
4✔
1463
                        if (r < 0)
4✔
1464
                                return r;
1465

1466
                        arg_root_password_is_hashed = true;
4✔
1467
                        break;
4✔
1468

1469
                OPTION_LONG("root-shell", "SHELL", "Set root shell"):
5✔
1470
                        r = free_and_strdup_warn(&arg_root_shell, opts.arg);
5✔
1471
                        if (r < 0)
5✔
1472
                                return r;
1473
                        break;
1474

1475
                OPTION_LONG("kernel-command-line", "CMDLINE", "Set kernel command line"):
3✔
1476
                        r = free_and_strdup_warn(&arg_kernel_cmdline, opts.arg);
3✔
1477
                        if (r < 0)
3✔
1478
                                return r;
1479
                        break;
1480

1481
                OPTION_LONG("prompt-locale", NULL, "Prompt the user for locale settings"):
111✔
1482
                        arg_prompt_locale = true;
111✔
1483
                        break;
111✔
1484

1485
                OPTION_LONG("prompt-keymap", NULL, "Prompt the user for keymap settings"):
2✔
1486
                        arg_prompt_keymap = true;
2✔
1487
                        arg_prompt_keymap_auto = false;
2✔
1488
                        break;
2✔
1489

1490
                OPTION_LONG("prompt-keymap-auto", NULL,
110✔
1491
                            "Prompt the user for keymap settings if invoked on local console"):
1492
                        arg_prompt_keymap_auto = true;
110✔
1493
                        break;
110✔
1494

1495
                OPTION_LONG("prompt-timezone", NULL, "Prompt the user for timezone"):
111✔
1496
                        arg_prompt_timezone = true;
111✔
1497
                        break;
111✔
1498

1499
                OPTION_LONG("prompt-hostname", NULL, "Prompt the user for hostname"):
1✔
1500
                        arg_prompt_hostname = true;
1✔
1501
                        break;
1✔
1502

1503
                OPTION_LONG("prompt-root-password", NULL, "Prompt the user for root password"):
111✔
1504
                        arg_prompt_root_password = true;
111✔
1505
                        break;
111✔
1506

1507
                OPTION_LONG("prompt-root-shell", NULL, "Prompt the user for root shell"):
4✔
1508
                        arg_prompt_root_shell = true;
4✔
1509
                        break;
4✔
1510

1511
                OPTION_LONG("prompt", NULL, "Prompt for all of the above"):
×
1512
                        arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname =
×
1513
                                arg_prompt_root_password = arg_prompt_root_shell = true;
×
1514
                        arg_prompt_keymap_auto = false;
×
UNCOV
1515
                        break;
×
1516

1517
                OPTION_LONG("copy-locale", NULL, "Copy locale from host"):
2✔
1518
                        arg_copy_locale = true;
2✔
1519
                        break;
2✔
1520

1521
                OPTION_LONG("copy-keymap", NULL, "Copy keymap from host"):
2✔
1522
                        arg_copy_keymap = true;
2✔
1523
                        break;
2✔
1524

1525
                OPTION_LONG("copy-timezone", NULL, "Copy timezone from host"):
2✔
1526
                        arg_copy_timezone = true;
2✔
1527
                        break;
2✔
1528

1529
                OPTION_LONG("copy-root-password", NULL, "Copy root password from host"):
1✔
1530
                        arg_copy_root_password = true;
1✔
1531
                        break;
1✔
1532

1533
                OPTION_LONG("copy-root-shell", NULL, "Copy root shell from host"):
1✔
1534
                        arg_copy_root_shell = true;
1✔
1535
                        break;
1✔
1536

1537
                OPTION_LONG("copy", NULL, "Copy all of the above"):
1✔
1538
                        arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password =
1✔
1539
                                arg_copy_root_shell = true;
1✔
1540
                        break;
1✔
1541

1542
                OPTION_LONG("force", NULL, "Overwrite existing files"):
3✔
1543
                        arg_force = true;
3✔
1544
                        break;
3✔
1545

1546
                OPTION_LONG("delete-root-password", NULL, "Delete root password"):
1✔
1547
                        arg_delete_root_password = true;
1✔
1548
                        break;
1✔
1549

1550
                OPTION_LONG("welcome", "BOOL", "Whether to show the welcome text"):
1✔
1551
                        r = parse_boolean_argument("--welcome=", opts.arg, &arg_welcome);
1✔
1552
                        if (r < 0)
1✔
1553
                                return r;
1554
                        break;
1555

UNCOV
1556
                OPTION_LONG("chrome", "BOOL",
×
1557
                            "Whether to show a color bar at top and bottom of terminal"):
UNCOV
1558
                        r = parse_boolean_argument("--chrome=", opts.arg, &arg_chrome);
×
UNCOV
1559
                        if (r < 0)
×
1560
                                return r;
1561
                        break;
1562

1563
                OPTION_LONG("mute-console", "BOOL",
109✔
1564
                            "Whether to disallow kernel/PID 1 writes to the console while running"):
1565
                        r = parse_boolean_argument("--mute-console=", opts.arg, &arg_mute_console);
109✔
1566
                        if (r < 0)
109✔
1567
                                return r;
1568
                        break;
1569

1570
                OPTION_LONG("reset", NULL, "Remove existing files"):
1✔
1571
                        arg_reset = true;
1✔
1572
                        break;
1✔
1573
                }
1574

1575
        if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password))
148✔
UNCOV
1576
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1577
                                       "--delete-root-password cannot be combined with other root password options.");
1578

1579
        if (arg_image && arg_root)
148✔
UNCOV
1580
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1581
                                       "--root= and --image= cannot be used together.");
1582

1583
        if (!sd_id128_is_null(arg_machine_id) && !(arg_image || arg_root) && !arg_force)
292✔
1584
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1585
                                       "--machine-id=/--setup-machine-id only works with --root= or --image=.");
1586

1587
        return 1;
1588
}
1589

UNCOV
1590
static int reload_system_manager(sd_bus **bus) {
×
UNCOV
1591
        int r;
×
1592

UNCOV
1593
        assert(bus);
×
1594

UNCOV
1595
        if (!*bus) {
×
1596
                r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, RUNTIME_SCOPE_SYSTEM, bus);
×
UNCOV
1597
                if (r < 0)
×
1598
                        return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL, RUNTIME_SCOPE_SYSTEM);
×
1599
        }
1600

UNCOV
1601
        r = bus_service_manager_reload(*bus);
×
UNCOV
1602
        if (r < 0)
×
1603
                return r;
1604

UNCOV
1605
        log_info("Requested manager reload to apply locale configuration.");
×
1606
        return 0;
1607
}
1608

UNCOV
1609
static int reload_vconsole(sd_bus **bus) {
×
UNCOV
1610
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
1611
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
×
UNCOV
1612
        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
×
UNCOV
1613
        const char *object;
×
1614
        int r;
×
1615

1616
        assert(bus);
×
1617

UNCOV
1618
        if (!*bus) {
×
UNCOV
1619
                r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, RUNTIME_SCOPE_SYSTEM, bus);
×
1620
                if (r < 0)
×
UNCOV
1621
                        return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL, RUNTIME_SCOPE_SYSTEM);
×
1622
        }
1623

1624
        r = bus_wait_for_jobs_new(*bus, &w);
×
UNCOV
1625
        if (r < 0)
×
UNCOV
1626
                return log_error_errno(r, "Could not watch jobs: %m");
×
1627

UNCOV
1628
        r = bus_call_method(*bus, bus_systemd_mgr, "RestartUnit", &error, &reply,
×
1629
                            "ss", "systemd-vconsole-setup.service", "replace");
1630
        if (r < 0)
×
UNCOV
1631
                return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
×
1632

UNCOV
1633
        r = sd_bus_message_read(reply, "o", &object);
×
1634
        if (r < 0)
×
UNCOV
1635
                return bus_log_parse_error(r);
×
1636

UNCOV
1637
        r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
×
UNCOV
1638
        if (r < 0)
×
UNCOV
1639
                return log_error_errno(r, "Failed to wait for systemd-vconsole-setup.service/restart: %m");
×
1640
        return 0;
1641
}
1642

1643
static void end_marker(void) {
148✔
1644

1645
        if (!welcome_done)
148✔
1646
                return;
1647

1648
        printf("\n%sExiting first boot settings tool.%s\n\n", ansi_grey(), ansi_normal());
12✔
1649
        fflush(stdout);
6✔
1650
}
1651

1652
static int run(int argc, char *argv[]) {
151✔
1653
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
151✔
1654
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
151✔
UNCOV
1655
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
×
1656
        _cleanup_close_ int rfd = -EBADF;
151✔
1657
        int r;
151✔
1658

1659
        r = parse_argv(argc, argv);
151✔
1660
        if (r <= 0)
151✔
1661
                return r;
1662

1663
        log_setup();
148✔
1664

1665
        umask(0022);
148✔
1666

1667
        bool offline = arg_root || arg_image;
148✔
1668

1669
        if (!offline) {
109✔
1670
                /* If we are called without --root=/--image= let's honour the systemd.firstboot kernel
1671
                 * command line option, because we are called to provision the host with basic settings (as
1672
                 * opposed to some other file system tree/image) */
1673

1674
                bool enabled;
109✔
1675
                r = proc_cmdline_get_bool("systemd.firstboot", /* flags= */ 0, &enabled);
109✔
1676
                if (r < 0)
109✔
1677
                        return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
×
1678
                if (r > 0 && !enabled) {
109✔
1679
                        log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
109✔
1680
                        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;
109✔
1681
                }
1682
        }
1683

1684
        r = mac_init();
148✔
1685
        if (r < 0)
148✔
1686
                return r;
1687

1688
        if (arg_image) {
148✔
UNCOV
1689
                assert(!arg_root);
×
1690

UNCOV
1691
                r = mount_image_privately_interactively(
×
1692
                                arg_image,
1693
                                arg_image_policy,
1694
                                DISSECT_IMAGE_GENERIC_ROOT |
1695
                                DISSECT_IMAGE_REQUIRE_ROOT |
1696
                                DISSECT_IMAGE_VALIDATE_OS |
1697
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
1698
                                DISSECT_IMAGE_FSCK |
1699
                                DISSECT_IMAGE_GROWFS |
1700
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
1701
                                &mounted_dir,
1702
                                &rfd,
1703
                                &loop_device);
UNCOV
1704
                if (r < 0)
×
1705
                        return r;
1706

UNCOV
1707
                arg_root = strdup(mounted_dir);
×
UNCOV
1708
                if (!arg_root)
×
UNCOV
1709
                        return log_oom();
×
1710
        } else {
1711
                rfd = open(empty_to_root(arg_root), O_DIRECTORY|O_CLOEXEC);
148✔
1712
                if (rfd < 0)
148✔
UNCOV
1713
                        return log_error_errno(errno, "Failed to open %s: %m", empty_to_root(arg_root));
×
1714
        }
1715

1716
        LOG_SET_PREFIX(arg_image ?: arg_root);
299✔
1717
        DEFER_VOID_CALL(end_marker);
148✔
UNCOV
1718
        DEFER_VOID_CALL(chrome_hide);
×
1719

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

1723
        if (arg_keymap && !keymap_is_ok(arg_keymap, FD_TO_PTR(rfd)))
148✔
UNCOV
1724
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap %s is not installed.", arg_keymap);
×
1725
        if (arg_locale && !locale_is_ok(arg_locale, FD_TO_PTR(rfd)))
148✔
UNCOV
1726
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
×
1727
        if (arg_locale_messages && !locale_is_ok(arg_locale_messages, FD_TO_PTR(rfd)))
148✔
UNCOV
1728
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages);
×
1729

1730
        if (arg_root_shell) {
148✔
1731
                r = find_shell(rfd, arg_root_shell);
5✔
1732
                if (r < 0)
5✔
1733
                        return r;
1734
        }
1735

1736
        r = process_reset(rfd);
147✔
1737
        if (r < 0)
147✔
1738
                return r;
1739

1740
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
148✔
1741
        r = process_locale(rfd, &mute_console_link);
147✔
1742
        if (r < 0)
147✔
1743
                return r;
1744
        if (r > 0 && !offline)
147✔
UNCOV
1745
                (void) reload_system_manager(&bus);
×
1746

1747
        r = process_keymap(rfd, &mute_console_link);
147✔
1748
        if (r < 0)
147✔
1749
                return r;
1750
        if (r > 0 && !offline)
147✔
UNCOV
1751
                (void) reload_vconsole(&bus);
×
1752

1753
        r = process_timezone(rfd, &mute_console_link);
147✔
1754
        if (r < 0)
147✔
1755
                return r;
1756

1757
        r = process_hostname(rfd, &mute_console_link);
147✔
1758
        if (r < 0)
147✔
1759
                return r;
1760

1761
        r = process_root_account(rfd, &mute_console_link);
147✔
1762
        if (r < 0)
147✔
1763
                return r;
1764

1765
        r = process_kernel_cmdline(rfd);
147✔
1766
        if (r < 0)
147✔
1767
                return r;
1768

1769
        r = process_machine_id(rfd);
147✔
1770
        if (r < 0)
147✔
1771
                return r;
1772

1773
        r = process_machine_tags(rfd);
147✔
1774
        if (r < 0)
147✔
UNCOV
1775
                return r;
×
1776

1777
        return 0;
1778
}
1779

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

© 2026 Coveralls, Inc