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

systemd / systemd / 28630269468

02 Jul 2026 10:23PM UTC coverage: 72.618% (-0.06%) from 72.68%
28630269468

push

github

yuwata
test: ignore fails when the formatted timezone differs from the current one

When formatting a timestamp the C API takes into account historical data
from tzdata, so it returns a date strings with a historically-correct
timezone abbreviation. However, tzname[] doesn't do this and it returns
the most recent abbreviation for the given zone.

For example, according to tzdata America/Cancun switched from EST/EDT to
CST/CDT on 1998-08-02:

Zone America/Cancun     -5:47:04 -      LMT     1922 Jan  1  6:00u
                        -6:00   -       CST     1981 Dec 26  2:00
                        -5:00   -       EST     1983 Jan  4  0:00
                        -6:00   Mexico  C%sT    1997 Oct 26  2:00
                        -5:00   Mexico  E%sT    1998 Aug  2  2:00
                        -6:00   Mexico  C%sT    2015 Feb  1  2:00
                        -5:00   -       EST

So, formatting a timestamp from this time will yield a string with the
EDT timezone:

$ TZ=America/Cancun date -d "@902035565"
Sun Aug  2 01:26:05 EDT 1998

But using tzname[] (or strptime %z) shows the most recent data, where
America/Cancun uses EST (and doesn't use DST anymore, hence
tzname[1]=CDT that glibc remembers from the previous zone epoch):

$ TZ=America/Cancun ./tz
{EST, CDT}

This means that when we parse the formatted timestamp back we don't use
the historical timezone data, so we might end up with a different
offset:

TZ=America/Cancun, tzname[0]=EST, tzname[1]=CDT
@902035565603993 → Sun 1998-08-02 01:26:05 EDT → @902039165000000 → Sun 1998-08-02 01:26:05 CDT
src/test/test-time-util.c:452: Assertion failed: Expected "ignore" to be true
Aborted                    (core dumped) build-local/test-time-util

Instead of adding exceptions for every single timezone that switched
between different offsets in the past, let's address this a bit more
generally and skip the check if the parsed timezone doesn't match any of
the current timezones - this still keeps the check that the time... (continued)

1 of 5 new or added lines in 1 file covered. (20.0%)

9391 existing lines in 86 files now uncovered.

341680 of 470518 relevant lines covered (72.62%)

1343034.26 hits per line

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

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

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

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

110
static bool welcome_done = false;
111

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

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

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

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

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

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

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

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

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

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

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

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

166
        welcome_done = true;
6✔
167
}
168

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

173
        assert(dir_fd >= 0);
1,386✔
174
        assert(filename);
1,386✔
175

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

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

189
        if (!passwd)
649✔
190
                return false;
191

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

313
                print_welcome(rfd, mute_console_link);
1✔
314

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

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

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

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

357
        return 0;
358
}
359

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

367
        assert(rfd >= 0);
154✔
368

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

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

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

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

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

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

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

405
        if (i == 0)
18✔
406
                return 0;
407

408
        locales[i] = NULL;
7✔
409

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

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

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

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

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

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

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

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

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

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

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

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

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

482
        print_welcome(rfd, mute_console_link);
1✔
483

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

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

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

509
        assert(rfd >= 0);
154✔
510

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

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

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

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

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

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

544
        if (isempty(arg_keymap))
159✔
545
                return 0;
546

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

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

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

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

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

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

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

576
        assert(rfd >= 0);
135✔
577

578
        if (arg_timezone)
135✔
579
                return 0;
580

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

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

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

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

605
        print_welcome(rfd, mute_console_link);
1✔
606

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

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

629
        assert(rfd >= 0);
154✔
630

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

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

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

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

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

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

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

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

668
        if (isempty(arg_timezone))
271✔
669
                return 0;
670

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

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

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

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

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

691
        assert(rfd >= 0);
140✔
692

693
        if (arg_hostname)
140✔
694
                return 0;
140✔
695

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

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

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

717
        print_welcome(rfd, mute_console_link);
1✔
718

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

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

738
        return 0;
739
}
740

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

746
        assert(rfd >= 0);
154✔
747

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

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

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

762
        if (isempty(arg_hostname))
163✔
763
                return 0;
764

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

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

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

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

795
        assert(rfd >= 0);
154✔
796

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

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

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

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

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

823
static int process_machine_tags(int rfd) {
154✔
824
        int r;
154✔
825

826
        assert(rfd >= 0);
154✔
827

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

908
        print_welcome(rfd, mute_console_link);
1✔
909

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

913
        suggest_passwords();
1✔
914

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

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

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

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

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

UNCOV
946
                req.message = msg2;
×
947

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

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

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

964
        return 0;
1✔
965
}
966

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

970
        assert(path);
7✔
971

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

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

979
        return 0;
980
}
981

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

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

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

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

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

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

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

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

1012
        print_welcome(rfd, mute_console_link);
2✔
1013

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1098
        return 0;
1099
}
1100

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

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

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

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

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

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

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

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

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

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

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

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

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

1171
        return 0;
1172
}
1173

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

1181
        assert(rfd >= 0);
154✔
1182

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

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

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

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

1200
                k += r;
308✔
1201
        }
1202

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

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

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

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

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

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

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

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

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

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

1244
                arg_root_password_is_hashed = true;
2✔
1245
        }
1246

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

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

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

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

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

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

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

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

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

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

1298
        assert(rfd >= 0);
154✔
1299

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

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

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

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

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

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

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

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

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

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

1346
static int process_reset(int rfd) {
154✔
1347
        int r;
154✔
1348

1349
        assert(rfd >= 0);
154✔
1350

1351
        if (!arg_reset)
154✔
1352
                return 0;
1353

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

1366
        return 0;
1✔
1367
}
1368

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

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

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

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

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

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

1393
        OptionParser opts = { argc, argv };
159✔
1394
        int r;
159✔
1395

1396
        FOREACH_OPTION_OR_RETURN(c, &opts)
1,160✔
1397
                switch (c) {
687✔
1398

UNCOV
1399
                OPTION_COMMON_HELP:
×
UNCOV
1400
                        return help();
×
1401

UNCOV
1402
                OPTION_COMMON_VERSION:
×
UNCOV
1403
                        return version();
×
1404

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1633
        return 1;
1634
}
1635

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

UNCOV
1639
        assert(bus);
×
1640

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

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

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

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

UNCOV
1662
        assert(bus);
×
1663

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

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

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

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

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

1689
static void end_marker(void) {
155✔
1690

1691
        if (!welcome_done)
155✔
1692
                return;
1693

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

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

1705
        r = parse_argv(argc, argv);
159✔
1706
        if (r <= 0)
159✔
1707
                return r;
1708

1709
        log_setup();
155✔
1710

1711
        umask(0022);
155✔
1712

1713
        bool offline = arg_root || arg_image;
155✔
1714

1715
        if (!offline) {
113✔
1716
                /* If we are called without --root=/--image= let's honour the systemd.firstboot kernel
1717
                 * command line option, because we are called to provision the host with basic settings (as
1718
                 * opposed to some other file system tree/image) */
1719

1720
                FirstBootMode mode;
113✔
1721
                _cleanup_free_ char *bad = NULL;
113✔
1722
                r = firstboot_mode_from_cmdline(&mode, &bad);
113✔
1723
                if (r < 0)
113✔
1724
                        return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument%s: %m",
×
1725
                                               bad ? strjoina(" (invalid value '", bad, "')") : "");
1726
                if (mode == FIRSTBOOT_NO) {
113✔
1727
                        log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
113✔
1728
                        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;
113✔
UNCOV
1729
                } else if (mode == FIRSTBOOT_HEADLESS) {
×
1730
                        log_debug("Found systemd.firstboot=headless kernel command line argument, skipping interactive prompts but keeping non-interactive auto-configuration.");
×
UNCOV
1731
                        arg_headless = true;
×
1732
                }
1733
        }
1734

1735
        r = mac_init();
155✔
1736
        if (r < 0)
155✔
1737
                return r;
1738

1739
        if (arg_image) {
155✔
UNCOV
1740
                assert(!arg_root);
×
1741

UNCOV
1742
                r = mount_image_privately_interactively(
×
1743
                                arg_image,
1744
                                arg_image_policy,
1745
                                DISSECT_IMAGE_GENERIC_ROOT |
1746
                                DISSECT_IMAGE_REQUIRE_ROOT |
1747
                                DISSECT_IMAGE_VALIDATE_OS |
1748
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
1749
                                DISSECT_IMAGE_FSCK |
1750
                                DISSECT_IMAGE_GROWFS |
1751
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
1752
                                &mounted_dir,
1753
                                &rfd,
1754
                                &loop_device);
UNCOV
1755
                if (r < 0)
×
1756
                        return r;
1757

UNCOV
1758
                arg_root = strdup(mounted_dir);
×
UNCOV
1759
                if (!arg_root)
×
UNCOV
1760
                        return log_oom();
×
1761
        } else {
1762
                rfd = open(empty_to_root(arg_root), O_DIRECTORY|O_CLOEXEC);
155✔
1763
                if (rfd < 0)
155✔
UNCOV
1764
                        return log_error_errno(errno, "Failed to open %s: %m", empty_to_root(arg_root));
×
1765
        }
1766

1767
        LOG_SET_PREFIX(arg_image ?: arg_root);
314✔
1768
        DEFER_VOID_CALL(end_marker);
155✔
UNCOV
1769
        DEFER_VOID_CALL(chrome_hide);
×
1770

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

1774
        if (arg_keymap && !keymap_is_ok(arg_keymap, FD_TO_PTR(rfd)))
155✔
UNCOV
1775
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap %s is not installed.", arg_keymap);
×
1776
        if (arg_locale && !locale_is_ok(arg_locale, FD_TO_PTR(rfd)))
155✔
UNCOV
1777
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
×
1778
        if (arg_locale_messages && !locale_is_ok(arg_locale_messages, FD_TO_PTR(rfd)))
155✔
UNCOV
1779
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages);
×
1780

1781
        if (arg_root_shell) {
155✔
1782
                r = find_shell(rfd, arg_root_shell);
5✔
1783
                if (r < 0)
5✔
1784
                        return r;
1785
        }
1786

1787
        r = process_reset(rfd);
154✔
1788
        if (r < 0)
154✔
1789
                return r;
1790

1791
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *mute_console_link = NULL;
155✔
1792
        r = process_locale(rfd, &mute_console_link);
154✔
1793
        if (r < 0)
154✔
1794
                return r;
1795
        if (r > 0 && !offline)
154✔
UNCOV
1796
                (void) reload_system_manager(&bus);
×
1797

1798
        r = process_keymap(rfd, &mute_console_link);
154✔
1799
        if (r < 0)
154✔
1800
                return r;
1801
        if (r > 0 && !offline)
154✔
UNCOV
1802
                (void) reload_vconsole(&bus);
×
1803

1804
        r = process_timezone(rfd, &mute_console_link);
154✔
1805
        if (r < 0)
154✔
1806
                return r;
1807

1808
        r = process_hostname(rfd, &mute_console_link);
154✔
1809
        if (r < 0)
154✔
1810
                return r;
1811

1812
        r = process_root_account(rfd, &mute_console_link);
154✔
1813
        if (r < 0)
154✔
1814
                return r;
1815

1816
        r = process_kernel_cmdline(rfd);
154✔
1817
        if (r < 0)
154✔
1818
                return r;
1819

1820
        r = process_machine_id(rfd);
154✔
1821
        if (r < 0)
154✔
1822
                return r;
1823

1824
        r = process_machine_tags(rfd);
154✔
1825
        if (r < 0)
154✔
UNCOV
1826
                return r;
×
1827

1828
        return 0;
1829
}
1830

1831
DEFINE_MAIN_FUNCTION(run);
159✔
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