]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/firstboot/firstboot.c
firstboot: move --image= logic into common code
[thirdparty/systemd.git] / src / firstboot / firstboot.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
418b9be5 2
418b9be5 3#include <fcntl.h>
418b9be5 4#include <getopt.h>
3ff9fa59 5#include <linux/loop.h>
3f6fd1ba 6#include <unistd.h>
418b9be5 7
dccca82b
LP
8#include "sd-id128.h"
9
b5efdb8a 10#include "alloc-util.h"
3f6fd1ba 11#include "ask-password-api.h"
418b9be5 12#include "copy.h"
3ff9fa59 13#include "dissect-image.h"
686d13b9 14#include "env-file.h"
6bedfcbb 15#include "fd-util.h"
3f6fd1ba 16#include "fileio.h"
f4f15635 17#include "fs-util.h"
3f6fd1ba 18#include "hostname-util.h"
f05e1d0d 19#include "kbd-util.h"
42f3b2f9 20#include "libcrypt-util.h"
3f6fd1ba 21#include "locale-util.h"
45f39418 22#include "main-func.h"
9ae4ef49 23#include "memory-util.h"
418b9be5 24#include "mkdir.h"
3ff9fa59
LP
25#include "mount-util.h"
26#include "namespace-util.h"
d58ad743 27#include "os-util.h"
6bedfcbb 28#include "parse-util.h"
418b9be5 29#include "path-util.h"
294bf0c3 30#include "pretty-print.h"
f582cbca 31#include "proc-cmdline.h"
3df3e884 32#include "random-util.h"
6bedfcbb 33#include "string-util.h"
3f6fd1ba 34#include "strv.h"
288a74cc 35#include "terminal-util.h"
3f6fd1ba 36#include "time-util.h"
b4909a3f 37#include "tmpfile-util-label.h"
3ff9fa59 38#include "tmpfile-util.h"
affb60b1 39#include "umask-util.h"
e929bee0 40#include "user-util.h"
418b9be5
LP
41
42static char *arg_root = NULL;
3ff9fa59 43static char *arg_image = NULL;
418b9be5 44static char *arg_locale = NULL; /* $LANG */
ed457f13 45static char *arg_keymap = NULL;
418b9be5
LP
46static char *arg_locale_messages = NULL; /* $LC_MESSAGES */
47static char *arg_timezone = NULL;
48static char *arg_hostname = NULL;
49static sd_id128_t arg_machine_id = {};
50static char *arg_root_password = NULL;
28900a1b 51static char *arg_root_shell = NULL;
a5925354 52static char *arg_kernel_cmdline = NULL;
418b9be5 53static bool arg_prompt_locale = false;
ed457f13 54static bool arg_prompt_keymap = false;
418b9be5
LP
55static bool arg_prompt_timezone = false;
56static bool arg_prompt_hostname = false;
57static bool arg_prompt_root_password = false;
28900a1b 58static bool arg_prompt_root_shell = false;
418b9be5 59static bool arg_copy_locale = false;
ed457f13 60static bool arg_copy_keymap = false;
418b9be5
LP
61static bool arg_copy_timezone = false;
62static bool arg_copy_root_password = false;
28900a1b 63static bool arg_copy_root_shell = false;
b4909a3f 64static bool arg_force = false;
4926ceaf 65static bool arg_delete_root_password = false;
676339a1 66static bool arg_root_password_is_hashed = false;
a1225020 67static bool arg_welcome = true;
418b9be5 68
45f39418 69STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
3ff9fa59 70STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
45f39418
YW
71STATIC_DESTRUCTOR_REGISTER(arg_locale, freep);
72STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep);
73STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
74STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep);
75STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep);
9ae4ef49 76STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep);
45f39418 77
418b9be5
LP
78static bool press_any_key(void) {
79 char k = 0;
80 bool need_nl = true;
81
82 printf("-- Press any key to proceed --");
83 fflush(stdout);
84
94956f8f 85 (void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl);
418b9be5
LP
86
87 if (need_nl)
88 putchar('\n');
89
90 return k != 'q';
91}
92
93static void print_welcome(void) {
4aeadec7 94 _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
418b9be5 95 static bool done = false;
4aeadec7 96 const char *pn;
418b9be5
LP
97 int r;
98
a1225020
LP
99 if (!arg_welcome)
100 return;
101
418b9be5
LP
102 if (done)
103 return;
104
4aeadec7
LP
105 r = parse_os_release(
106 arg_root,
107 "PRETTY_NAME", &pretty_name,
108 "ANSI_COLOR", &ansi_color,
109 NULL);
d58ad743
LP
110 if (r < 0)
111 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
112 "Failed to read os-release file, ignoring: %m");
418b9be5 113
4aeadec7
LP
114 pn = isempty(pretty_name) ? "Linux" : pretty_name;
115
116 if (colors_enabled())
117 printf("\nWelcome to your new installation of \x1B[%sm%s\x1B[0m!\n", ansi_color, pn);
118 else
119 printf("\nWelcome to your new installation of %s!\n", pn);
120
121 printf("\nPlease configure your system!\n\n");
418b9be5
LP
122
123 press_any_key();
124
125 done = true;
126}
127
128static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) {
418b9be5 129 unsigned break_lines, break_modulo;
da6053d0 130 size_t n, per_column, i, j;
418b9be5
LP
131
132 assert(n_columns > 0);
133
134 n = strv_length(x);
be6b0c21 135 per_column = DIV_ROUND_UP(n, n_columns);
418b9be5
LP
136
137 break_lines = lines();
138 if (break_lines > 2)
139 break_lines--;
140
141 /* The first page gets two extra lines, since we want to show
142 * a title */
143 break_modulo = break_lines;
144 if (break_modulo > 3)
145 break_modulo -= 3;
146
147 for (i = 0; i < per_column; i++) {
148
149 for (j = 0; j < n_columns; j ++) {
150 _cleanup_free_ char *e = NULL;
151
152 if (j * per_column + i >= n)
153 break;
154
155 e = ellipsize(x[j * per_column + i], width, percentage);
156 if (!e)
157 return log_oom();
158
da6053d0 159 printf("%4zu) %-*s", j * per_column + i + 1, width, e);
418b9be5
LP
160 }
161
162 putchar('\n');
163
164 /* on the first screen we reserve 2 extra lines for the title */
165 if (i % break_lines == break_modulo) {
166 if (!press_any_key())
167 return 0;
168 }
169 }
170
171 return 0;
172}
173
ecada8f2 174static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*is_valid)(const char *name), char **ret) {
418b9be5
LP
175 int r;
176
177 assert(text);
178 assert(is_valid);
179 assert(ret);
180
181 for (;;) {
182 _cleanup_free_ char *p = NULL;
183 unsigned u;
184
ecada8f2
ZJS
185 r = ask_string(&p, "%s %s (empty to skip, \"list\" to list options): ",
186 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), text);
23bbb0de
MS
187 if (r < 0)
188 return log_error_errno(r, "Failed to query user: %m");
418b9be5
LP
189
190 if (isempty(p)) {
191 log_warning("No data entered, skipping.");
192 return 0;
193 }
194
ecada8f2
ZJS
195 if (streq(p, "list")) {
196 r = show_menu(l, 3, 22, percentage);
197 if (r < 0)
198 return r;
199
200 putchar('\n');
201 continue;
202 };
203
418b9be5
LP
204 r = safe_atou(p, &u);
205 if (r >= 0) {
418b9be5
LP
206 if (u <= 0 || u > strv_length(l)) {
207 log_error("Specified entry number out of range.");
208 continue;
209 }
210
211 log_info("Selected '%s'.", l[u-1]);
bfbf5f74 212 if (free_and_strdup(ret, l[u-1]) < 0)
418b9be5
LP
213 return log_oom();
214
418b9be5
LP
215 return 0;
216 }
217
218 if (!is_valid(p)) {
219 log_error("Entered data invalid.");
220 continue;
221 }
222
bfbf5f74 223 return free_and_replace(*ret, p);
418b9be5
LP
224 }
225}
226
a00a78b8
LP
227static bool locale_is_ok(const char *name) {
228
229 if (arg_root)
230 return locale_is_valid(name);
231
232 return locale_is_installed(name) > 0;
233}
234
418b9be5
LP
235static int prompt_locale(void) {
236 _cleanup_strv_free_ char **locales = NULL;
237 int r;
238
239 if (arg_locale || arg_locale_messages)
240 return 0;
241
242 if (!arg_prompt_locale)
243 return 0;
244
245 r = get_locales(&locales);
23bbb0de
MS
246 if (r < 0)
247 return log_error_errno(r, "Cannot query locales list: %m");
418b9be5 248
bdd7cd26
LP
249 if (strv_isempty(locales))
250 log_debug("No locales found, skipping locale selection.");
251 else if (strv_length(locales) == 1) {
418b9be5 252
bdd7cd26
LP
253 if (streq(locales[0], SYSTEMD_DEFAULT_LOCALE))
254 log_debug("Only installed locale is default locale anyway, not setting locale explicitly.");
255 else {
256 log_debug("Only a single locale available (%s), selecting it as default.", locales[0]);
418b9be5 257
bdd7cd26
LP
258 arg_locale = strdup(locales[0]);
259 if (!arg_locale)
260 return log_oom();
418b9be5 261
bdd7cd26
LP
262 /* Not setting arg_locale_message here, since it defaults to LANG anyway */
263 }
264 } else {
265 print_welcome();
418b9be5 266
ecada8f2 267 r = prompt_loop("Please enter system locale name or number",
a00a78b8 268 locales, 60, locale_is_ok, &arg_locale);
bdd7cd26
LP
269 if (r < 0)
270 return r;
271
272 if (isempty(arg_locale))
273 return 0;
274
ecada8f2 275 r = prompt_loop("Please enter system message locale name or number",
a00a78b8 276 locales, 60, locale_is_ok, &arg_locale_messages);
bdd7cd26
LP
277 if (r < 0)
278 return r;
279
280 /* Suppress the messages setting if it's the same as the main locale anyway */
281 if (streq_ptr(arg_locale, arg_locale_messages))
282 arg_locale_messages = mfree(arg_locale_messages);
283 }
418b9be5
LP
284
285 return 0;
286}
287
288static int process_locale(void) {
289 const char *etc_localeconf;
290 char* locales[3];
291 unsigned i = 0;
292 int r;
293
1d13f648 294 etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
b4909a3f 295 if (laccess(etc_localeconf, F_OK) >= 0 && !arg_force)
418b9be5
LP
296 return 0;
297
298 if (arg_copy_locale && arg_root) {
299
22e596d6 300 (void) mkdir_parents(etc_localeconf, 0755);
8a016c74 301 r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, 0, COPY_REFLINK);
418b9be5 302 if (r != -ENOENT) {
23bbb0de
MS
303 if (r < 0)
304 return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf);
418b9be5
LP
305
306 log_info("%s copied.", etc_localeconf);
307 return 0;
308 }
309 }
310
311 r = prompt_locale();
312 if (r < 0)
313 return r;
314
315 if (!isempty(arg_locale))
63c372cb 316 locales[i++] = strjoina("LANG=", arg_locale);
418b9be5 317 if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale))
63c372cb 318 locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages);
418b9be5
LP
319
320 if (i == 0)
321 return 0;
322
323 locales[i] = NULL;
324
22e596d6 325 (void) mkdir_parents(etc_localeconf, 0755);
418b9be5 326 r = write_env_file(etc_localeconf, locales);
23bbb0de
MS
327 if (r < 0)
328 return log_error_errno(r, "Failed to write %s: %m", etc_localeconf);
418b9be5
LP
329
330 log_info("%s written.", etc_localeconf);
331 return 0;
332}
333
ed457f13
TB
334static int prompt_keymap(void) {
335 _cleanup_strv_free_ char **kmaps = NULL;
336 int r;
337
338 if (arg_keymap)
339 return 0;
340
341 if (!arg_prompt_keymap)
342 return 0;
343
344 r = get_keymaps(&kmaps);
345 if (r == -ENOENT) /* no keymaps installed */
346 return r;
347 if (r < 0)
348 return log_error_errno(r, "Failed to read keymaps: %m");
349
350 print_welcome();
351
8700b4da 352 return prompt_loop("Please enter system keymap name or number",
ecada8f2 353 kmaps, 60, keymap_is_valid, &arg_keymap);
ed457f13
TB
354}
355
356static int process_keymap(void) {
357 const char *etc_vconsoleconf;
358 char **keymap;
359 int r;
360
361 etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf");
b4909a3f 362 if (laccess(etc_vconsoleconf, F_OK) >= 0 && !arg_force)
ed457f13
TB
363 return 0;
364
365 if (arg_copy_keymap && arg_root) {
366
22e596d6 367 (void) mkdir_parents(etc_vconsoleconf, 0755);
8a016c74 368 r = copy_file("/etc/vconsole.conf", etc_vconsoleconf, 0, 0644, 0, 0, COPY_REFLINK);
ed457f13
TB
369 if (r != -ENOENT) {
370 if (r < 0)
371 return log_error_errno(r, "Failed to copy %s: %m", etc_vconsoleconf);
372
373 log_info("%s copied.", etc_vconsoleconf);
374 return 0;
375 }
376 }
377
378 r = prompt_keymap();
379 if (r == -ENOENT)
380 return 0; /* don't fail if no keymaps are installed */
381 if (r < 0)
382 return r;
383
a7353b4d 384 if (isempty(arg_keymap))
ed457f13
TB
385 return 0;
386
a7353b4d
YW
387 keymap = STRV_MAKE(strjoina("KEYMAP=", arg_keymap));
388
389 r = mkdir_parents(etc_vconsoleconf, 0755);
390 if (r < 0)
391 return log_error_errno(r, "Failed to create the parent directory of %s: %m", etc_vconsoleconf);
392
ed457f13
TB
393 r = write_env_file(etc_vconsoleconf, keymap);
394 if (r < 0)
395 return log_error_errno(r, "Failed to write %s: %m", etc_vconsoleconf);
396
397 log_info("%s written.", etc_vconsoleconf);
398 return 0;
399}
400
089fb865
MG
401static bool timezone_is_valid_log_error(const char *name) {
402 return timezone_is_valid(name, LOG_ERR);
403}
404
418b9be5
LP
405static int prompt_timezone(void) {
406 _cleanup_strv_free_ char **zones = NULL;
407 int r;
408
409 if (arg_timezone)
410 return 0;
411
412 if (!arg_prompt_timezone)
413 return 0;
414
415 r = get_timezones(&zones);
23bbb0de
MS
416 if (r < 0)
417 return log_error_errno(r, "Cannot query timezone list: %m");
418b9be5
LP
418
419 print_welcome();
420
ecada8f2
ZJS
421 r = prompt_loop("Please enter timezone name or number",
422 zones, 30, timezone_is_valid_log_error, &arg_timezone);
418b9be5
LP
423 if (r < 0)
424 return r;
425
426 return 0;
427}
428
429static int process_timezone(void) {
430 const char *etc_localtime, *e;
431 int r;
432
1d13f648 433 etc_localtime = prefix_roota(arg_root, "/etc/localtime");
b4909a3f 434 if (laccess(etc_localtime, F_OK) >= 0 && !arg_force)
418b9be5
LP
435 return 0;
436
437 if (arg_copy_timezone && arg_root) {
438 _cleanup_free_ char *p = NULL;
439
440 r = readlink_malloc("/etc/localtime", &p);
441 if (r != -ENOENT) {
23bbb0de
MS
442 if (r < 0)
443 return log_error_errno(r, "Failed to read host timezone: %m");
418b9be5 444
22e596d6 445 (void) mkdir_parents(etc_localtime, 0755);
4a62c710
MS
446 if (symlink(p, etc_localtime) < 0)
447 return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
418b9be5
LP
448
449 log_info("%s copied.", etc_localtime);
450 return 0;
451 }
452 }
453
454 r = prompt_timezone();
455 if (r < 0)
456 return r;
457
458 if (isempty(arg_timezone))
459 return 0;
460
63c372cb 461 e = strjoina("../usr/share/zoneinfo/", arg_timezone);
418b9be5 462
22e596d6 463 (void) mkdir_parents(etc_localtime, 0755);
4a62c710
MS
464 if (symlink(e, etc_localtime) < 0)
465 return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
418b9be5
LP
466
467 log_info("%s written", etc_localtime);
468 return 0;
469}
470
471static int prompt_hostname(void) {
472 int r;
473
474 if (arg_hostname)
475 return 0;
476
477 if (!arg_prompt_hostname)
478 return 0;
479
480 print_welcome();
481 putchar('\n');
482
483 for (;;) {
484 _cleanup_free_ char *h = NULL;
485
9a6f746f 486 r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
23bbb0de
MS
487 if (r < 0)
488 return log_error_errno(r, "Failed to query hostname: %m");
418b9be5
LP
489
490 if (isempty(h)) {
491 log_warning("No hostname entered, skipping.");
492 break;
493 }
494
34ad6090 495 if (!hostname_is_valid(h, true)) {
418b9be5
LP
496 log_error("Specified hostname invalid.");
497 continue;
498 }
499
34ad6090 500 /* Get rid of the trailing dot that we allow, but don't want to see */
ae691c1d 501 arg_hostname = hostname_cleanup(h);
418b9be5
LP
502 h = NULL;
503 break;
504 }
505
506 return 0;
507}
508
509static int process_hostname(void) {
510 const char *etc_hostname;
511 int r;
512
1d13f648 513 etc_hostname = prefix_roota(arg_root, "/etc/hostname");
b4909a3f 514 if (laccess(etc_hostname, F_OK) >= 0 && !arg_force)
418b9be5
LP
515 return 0;
516
517 r = prompt_hostname();
518 if (r < 0)
519 return r;
520
521 if (isempty(arg_hostname))
522 return 0;
523
0675e94a 524 r = write_string_file(etc_hostname, arg_hostname,
b4909a3f
DDM
525 WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
526 (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
23bbb0de
MS
527 if (r < 0)
528 return log_error_errno(r, "Failed to write %s: %m", etc_hostname);
418b9be5
LP
529
530 log_info("%s written.", etc_hostname);
531 return 0;
532}
533
534static int process_machine_id(void) {
535 const char *etc_machine_id;
536 char id[SD_ID128_STRING_MAX];
537 int r;
538
1d13f648 539 etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
b4909a3f 540 if (laccess(etc_machine_id, F_OK) >= 0 && !arg_force)
418b9be5
LP
541 return 0;
542
3bbaff3e 543 if (sd_id128_is_null(arg_machine_id))
418b9be5
LP
544 return 0;
545
0675e94a 546 r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id),
b4909a3f
DDM
547 WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
548 (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
23bbb0de
MS
549 if (r < 0)
550 return log_error_errno(r, "Failed to write machine id: %m");
418b9be5
LP
551
552 log_info("%s written.", etc_machine_id);
553 return 0;
554}
555
556static int prompt_root_password(void) {
1fbc95d3 557 const char *msg1, *msg2;
418b9be5
LP
558 int r;
559
560 if (arg_root_password)
561 return 0;
562
563 if (!arg_prompt_root_password)
564 return 0;
565
418b9be5
LP
566 print_welcome();
567 putchar('\n');
568
3619634c
LP
569 msg1 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip):");
570 msg2 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter new root password again:");
418b9be5
LP
571
572 for (;;) {
c7b7d74e 573 _cleanup_strv_free_erase_ char **a = NULL, **b = NULL;
418b9be5 574
daa55720 575 r = ask_password_tty(-1, msg1, NULL, 0, 0, NULL, &a);
23bbb0de
MS
576 if (r < 0)
577 return log_error_errno(r, "Failed to query root password: %m");
39e96f84
ZJS
578 if (strv_length(a) != 1)
579 return log_error_errno(SYNTHETIC_ERRNO(EIO),
580 "Received multiple passwords, where we expected one.");
418b9be5 581
c7b7d74e 582 if (isempty(*a)) {
418b9be5
LP
583 log_warning("No password entered, skipping.");
584 break;
585 }
586
daa55720 587 r = ask_password_tty(-1, msg2, NULL, 0, 0, NULL, &b);
ab84f5b9 588 if (r < 0)
00843602 589 return log_error_errno(r, "Failed to query root password: %m");
39e96f84
ZJS
590 if (strv_length(b) != 1)
591 return log_error_errno(SYNTHETIC_ERRNO(EIO),
592 "Received multiple passwords, where we expected one.");
418b9be5 593
c7b7d74e 594 if (!streq(*a, *b)) {
418b9be5 595 log_error("Entered passwords did not match, please try again.");
418b9be5
LP
596 continue;
597 }
598
c7b7d74e 599 arg_root_password = TAKE_PTR(*a);
418b9be5
LP
600 break;
601 }
602
603 return 0;
604}
605
31363bd5
DDM
606static int find_shell(const char *path, const char *root) {
607 int r;
608
609 assert(path);
610
611 if (!valid_shell(path))
612 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a valid shell", path);
613
614 r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, NULL, NULL);
615 if (r < 0) {
616 const char *p;
617 p = prefix_roota(root, path);
618 return log_error_errno(r, "Failed to resolve shell %s: %m", p);
619 }
620
621 return 0;
622}
623
28900a1b
DDM
624static int prompt_root_shell(void) {
625 int r;
626
627 if (arg_root_shell || !arg_prompt_root_shell)
628 return 0;
629
630 print_welcome();
631 putchar('\n');
632
633 for (;;) {
634 _cleanup_free_ char *s = NULL;
635
636 r = ask_string(&s, "%s Please enter root shell for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
637 if (r < 0)
638 return log_error_errno(r, "Failed to query root shell: %m");
639
640 if (isempty(s)) {
641 log_warning("No shell entered, skipping.");
642 break;
643 }
644
31363bd5
DDM
645 r = find_shell(s, arg_root);
646 if (r < 0)
28900a1b 647 continue;
28900a1b
DDM
648
649 arg_root_shell = TAKE_PTR(s);
650 break;
651 }
652
653 return 0;
654}
655
656static int write_root_passwd(const char *passwd_path, const char *password, const char *shell) {
4926ceaf
DDM
657 _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
658 _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
659 int r;
660
c4a53ebf
DDM
661 assert(password);
662
4926ceaf
DDM
663 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
664 if (r < 0)
665 return r;
666
667 original = fopen(passwd_path, "re");
668 if (original) {
669 struct passwd *i;
670
671 r = sync_rights(fileno(original), fileno(passwd));
672 if (r < 0)
673 return r;
674
675 while ((r = fgetpwent_sane(original, &i)) > 0) {
676
28900a1b 677 if (streq(i->pw_name, "root")) {
4926ceaf 678 i->pw_passwd = (char *) password;
28900a1b
DDM
679 if (shell)
680 i->pw_shell = (char *) shell;
681 }
4926ceaf
DDM
682
683 r = putpwent_sane(i, passwd);
684 if (r < 0)
685 return r;
686 }
687 if (r < 0)
688 return r;
689
690 } else {
691 struct passwd root = {
692 .pw_name = (char *) "root",
693 .pw_passwd = (char *) password,
694 .pw_uid = 0,
695 .pw_gid = 0,
696 .pw_gecos = (char *) "Super User",
697 .pw_dir = (char *) "/root",
28900a1b 698 .pw_shell = (char *) (shell ?: "/bin/sh"),
4926ceaf
DDM
699 };
700
701 if (errno != ENOENT)
702 return -errno;
703
b226422c 704 r = fchmod(fileno(passwd), 0644);
4926ceaf
DDM
705 if (r < 0)
706 return -errno;
707
708 r = putpwent_sane(&root, passwd);
709 if (r < 0)
710 return r;
711 }
712
713 r = fflush_sync_and_check(passwd);
714 if (r < 0)
715 return r;
716
717 r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
718 if (r < 0)
719 return r;
720
721 return 0;
722}
723
b4909a3f
DDM
724static int write_root_shadow(const char *shadow_path, const char *hashed_password) {
725 _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
726 _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
100d5f6e
FB
727 int r;
728
c4a53ebf
DDM
729 assert(hashed_password);
730
b4909a3f
DDM
731 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
732 if (r < 0)
733 return r;
734
735 original = fopen(shadow_path, "re");
736 if (original) {
737 struct spwd *i;
738
739 r = sync_rights(fileno(original), fileno(shadow));
740 if (r < 0)
741 return r;
742
743 while ((r = fgetspent_sane(original, &i)) > 0) {
744
745 if (streq(i->sp_namp, "root")) {
746 i->sp_pwdp = (char *) hashed_password;
747 i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
748 }
749
750 r = putspent_sane(i, shadow);
751 if (r < 0)
752 return r;
753 }
754 if (r < 0)
755 return r;
756
757 } else {
758 struct spwd root = {
759 .sp_namp = (char*) "root",
760 .sp_pwdp = (char *) hashed_password,
761 .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY),
762 .sp_min = -1,
763 .sp_max = -1,
764 .sp_warn = -1,
765 .sp_inact = -1,
766 .sp_expire = -1,
767 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
768 };
769
770 if (errno != ENOENT)
771 return -errno;
418b9be5 772
b4909a3f
DDM
773 r = fchmod(fileno(shadow), 0000);
774 if (r < 0)
775 return -errno;
418b9be5 776
b4909a3f
DDM
777 r = putspent_sane(&root, shadow);
778 if (r < 0)
779 return r;
780 }
781
782 r = fflush_sync_and_check(shadow);
100d5f6e
FB
783 if (r < 0)
784 return r;
418b9be5 785
b4909a3f
DDM
786 r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
787 if (r < 0)
788 return r;
789
790 return 0;
418b9be5
LP
791}
792
28900a1b 793static int process_root_args(void) {
45035609 794 _cleanup_close_ int lock = -1;
38620433 795 struct crypt_data cd = {};
c4a53ebf
DDM
796 const char *password, *hashed_password;
797 const char *etc_passwd, *etc_shadow;
418b9be5
LP
798 int r;
799
c4a53ebf 800 etc_passwd = prefix_roota(arg_root, "/etc/passwd");
1d13f648 801 etc_shadow = prefix_roota(arg_root, "/etc/shadow");
c4a53ebf
DDM
802
803 /* We only mess with passwd and shadow if both do not exist or --force is specified. These files are
804 * tightly coupled and hence we make sure we have permission from the user to create/modify both
805 * files. */
806 if ((laccess(etc_passwd, F_OK) >= 0 || laccess(etc_shadow, F_OK) >= 0) && !arg_force)
418b9be5
LP
807 return 0;
808
c4a53ebf 809 (void) mkdir_parents(etc_passwd, 0755);
45035609 810
e929bee0 811 lock = take_etc_passwd_lock(arg_root);
45035609 812 if (lock < 0)
c4a53ebf 813 return log_error_errno(lock, "Failed to take a lock on %s: %m", etc_passwd);
4926ceaf 814
28900a1b
DDM
815 if (arg_copy_root_shell && arg_root) {
816 struct passwd *p;
817
818 errno = 0;
819 p = getpwnam("root");
820 if (!p)
821 return log_error_errno(errno_or_else(EIO), "Failed to find passwd entry for root: %m");
822
823 r = free_and_strdup(&arg_root_shell, p->pw_shell);
824 if (r < 0)
825 return log_oom();
826 }
827
828 r = prompt_root_shell();
829 if (r < 0)
830 return r;
831
418b9be5
LP
832 if (arg_copy_root_password && arg_root) {
833 struct spwd *p;
834
835 errno = 0;
836 p = getspnam("root");
c4a53ebf
DDM
837 if (!p)
838 return log_error_errno(errno_or_else(EIO), "Failed to find shadow entry for root: %m");
418b9be5 839
c4a53ebf
DDM
840 r = free_and_strdup(&arg_root_password, p->sp_pwdp);
841 if (r < 0)
842 return log_oom();
418b9be5 843
c4a53ebf 844 arg_root_password_is_hashed = true;
418b9be5
LP
845 }
846
847 r = prompt_root_password();
848 if (r < 0)
849 return r;
850
c4a53ebf
DDM
851 if (arg_root_password && arg_root_password_is_hashed) {
852 password = "x";
676339a1 853 hashed_password = arg_root_password;
c4a53ebf 854 } else if (arg_root_password) {
676339a1
DDM
855 _cleanup_free_ char *salt = NULL;
856 /* hashed_password points inside cd after crypt_r returns so cd has function scope. */
857
c4a53ebf
DDM
858 password = "x";
859
676339a1
DDM
860 r = make_salt(&salt);
861 if (r < 0)
862 return log_error_errno(r, "Failed to get salt: %m");
418b9be5 863
676339a1
DDM
864 errno = 0;
865 hashed_password = crypt_r(arg_root_password, salt, &cd);
866 if (!hashed_password)
867 return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno,
868 "Failed to encrypt password: %m");
c4a53ebf
DDM
869 } else if (arg_delete_root_password)
870 password = hashed_password = "";
871 else
872 password = hashed_password = "!";
873
28900a1b 874 r = write_root_passwd(etc_passwd, password, arg_root_shell);
c4a53ebf
DDM
875 if (r < 0)
876 return log_error_errno(r, "Failed to write %s: %m", etc_passwd);
877
878 log_info("%s written", etc_passwd);
418b9be5 879
b4909a3f 880 r = write_root_shadow(etc_shadow, hashed_password);
23bbb0de
MS
881 if (r < 0)
882 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
418b9be5
LP
883
884 log_info("%s written.", etc_shadow);
885 return 0;
886}
887
a5925354
DDM
888static int process_kernel_cmdline(void) {
889 const char *etc_kernel_cmdline;
890 int r;
891
892 etc_kernel_cmdline = prefix_roota(arg_root, "/etc/kernel/cmdline");
893 if (laccess(etc_kernel_cmdline, F_OK) >= 0 && !arg_force)
894 return 0;
895
896 if (!arg_kernel_cmdline)
897 return 0;
898
899 r = write_string_file(etc_kernel_cmdline, arg_kernel_cmdline,
900 WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
901 (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
902 if (r < 0)
903 return log_error_errno(r, "Failed to write %s: %m", etc_kernel_cmdline);
904
905 log_info("%s written.", etc_kernel_cmdline);
906 return 0;
907}
908
37ec0fdd
LP
909static int help(void) {
910 _cleanup_free_ char *link = NULL;
911 int r;
912
913 r = terminal_urlify_man("systemd-firstboot", "1", &link);
914 if (r < 0)
915 return log_oom();
916
418b9be5
LP
917 printf("%s [OPTIONS...]\n\n"
918 "Configures basic settings of the system.\n\n"
676339a1
DDM
919 " -h --help Show this help\n"
920 " --version Show package version\n"
921 " --root=PATH Operate on an alternate filesystem root\n"
3ff9fa59 922 " --image=PATH Operate on an alternate filesystem image\n"
676339a1
DDM
923 " --locale=LOCALE Set primary locale (LANG=)\n"
924 " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n"
925 " --keymap=KEYMAP Set keymap\n"
926 " --timezone=TIMEZONE Set timezone\n"
927 " --hostname=NAME Set hostname\n"
928 " --machine-ID=ID Set machine ID\n"
929 " --root-password=PASSWORD Set root password from plaintext password\n"
930 " --root-password-file=FILE Set root password from file\n"
931 " --root-password-hashed=HASHED_PASSWORD Set root password from hashed password\n"
f649325b 932 " --root-shell=SHELL Set root shell\n"
676339a1
DDM
933 " --prompt-locale Prompt the user for locale settings\n"
934 " --prompt-keymap Prompt the user for keymap settings\n"
935 " --prompt-timezone Prompt the user for timezone\n"
936 " --prompt-hostname Prompt the user for hostname\n"
937 " --prompt-root-password Prompt the user for root password\n"
f649325b 938 " --prompt-root-shell Prompt the user for root shell\n"
676339a1
DDM
939 " --prompt Prompt for all of the above\n"
940 " --copy-locale Copy locale from host\n"
941 " --copy-keymap Copy keymap from host\n"
942 " --copy-timezone Copy timezone from host\n"
943 " --copy-root-password Copy root password from host\n"
f649325b 944 " --copy-root-shell Copy root shell from host\n"
676339a1
DDM
945 " --copy Copy locale, keymap, timezone, root password\n"
946 " --setup-machine-id Generate a new random machine ID\n"
947 " --force Overwrite existing files\n"
948 " --delete-root-password Delete root password\n"
a1225020 949 " --welcome=no Disable the welcome text\n"
37ec0fdd
LP
950 "\nSee the %s for details.\n"
951 , program_invocation_short_name
952 , link
953 );
954
955 return 0;
418b9be5
LP
956}
957
958static int parse_argv(int argc, char *argv[]) {
959
960 enum {
961 ARG_VERSION = 0x100,
962 ARG_ROOT,
3ff9fa59 963 ARG_IMAGE,
418b9be5
LP
964 ARG_LOCALE,
965 ARG_LOCALE_MESSAGES,
ed457f13 966 ARG_KEYMAP,
418b9be5
LP
967 ARG_TIMEZONE,
968 ARG_HOSTNAME,
969 ARG_MACHINE_ID,
970 ARG_ROOT_PASSWORD,
971 ARG_ROOT_PASSWORD_FILE,
676339a1 972 ARG_ROOT_PASSWORD_HASHED,
28900a1b 973 ARG_ROOT_SHELL,
a5925354 974 ARG_KERNEL_COMMAND_LINE,
418b9be5
LP
975 ARG_PROMPT,
976 ARG_PROMPT_LOCALE,
ed457f13 977 ARG_PROMPT_KEYMAP,
418b9be5
LP
978 ARG_PROMPT_TIMEZONE,
979 ARG_PROMPT_HOSTNAME,
980 ARG_PROMPT_ROOT_PASSWORD,
28900a1b 981 ARG_PROMPT_ROOT_SHELL,
418b9be5
LP
982 ARG_COPY,
983 ARG_COPY_LOCALE,
ed457f13 984 ARG_COPY_KEYMAP,
418b9be5
LP
985 ARG_COPY_TIMEZONE,
986 ARG_COPY_ROOT_PASSWORD,
28900a1b 987 ARG_COPY_ROOT_SHELL,
418b9be5 988 ARG_SETUP_MACHINE_ID,
b4909a3f 989 ARG_FORCE,
4926ceaf 990 ARG_DELETE_ROOT_PASSWORD,
a1225020 991 ARG_WELCOME,
418b9be5
LP
992 };
993
994 static const struct option options[] = {
676339a1
DDM
995 { "help", no_argument, NULL, 'h' },
996 { "version", no_argument, NULL, ARG_VERSION },
997 { "root", required_argument, NULL, ARG_ROOT },
3ff9fa59 998 { "image", required_argument, NULL, ARG_IMAGE },
676339a1
DDM
999 { "locale", required_argument, NULL, ARG_LOCALE },
1000 { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES },
1001 { "keymap", required_argument, NULL, ARG_KEYMAP },
1002 { "timezone", required_argument, NULL, ARG_TIMEZONE },
1003 { "hostname", required_argument, NULL, ARG_HOSTNAME },
1004 { "machine-id", required_argument, NULL, ARG_MACHINE_ID },
1005 { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD },
1006 { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE },
1007 { "root-password-hashed", required_argument, NULL, ARG_ROOT_PASSWORD_HASHED },
28900a1b 1008 { "root-shell", required_argument, NULL, ARG_ROOT_SHELL },
a5925354 1009 { "kernel-command-line", required_argument, NULL, ARG_KERNEL_COMMAND_LINE },
676339a1
DDM
1010 { "prompt", no_argument, NULL, ARG_PROMPT },
1011 { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE },
1012 { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP },
1013 { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE },
1014 { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME },
1015 { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD },
28900a1b 1016 { "prompt-root-shell", no_argument, NULL, ARG_PROMPT_ROOT_SHELL },
676339a1
DDM
1017 { "copy", no_argument, NULL, ARG_COPY },
1018 { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE },
1019 { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP },
1020 { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE },
1021 { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD },
28900a1b 1022 { "copy-root-shell", no_argument, NULL, ARG_COPY_ROOT_SHELL },
676339a1
DDM
1023 { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID },
1024 { "force", no_argument, NULL, ARG_FORCE },
1025 { "delete-root-password", no_argument, NULL, ARG_DELETE_ROOT_PASSWORD },
a1225020 1026 { "welcome", required_argument, NULL, ARG_WELCOME },
418b9be5
LP
1027 {}
1028 };
1029
1030 int r, c;
1031
1032 assert(argc >= 0);
1033 assert(argv);
1034
601185b4 1035 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
418b9be5
LP
1036
1037 switch (c) {
1038
1039 case 'h':
37ec0fdd 1040 return help();
418b9be5
LP
1041
1042 case ARG_VERSION:
3f6fd1ba 1043 return version();
418b9be5
LP
1044
1045 case ARG_ROOT:
0f03c2a4 1046 r = parse_path_argument_and_warn(optarg, true, &arg_root);
0f474365 1047 if (r < 0)
0f03c2a4 1048 return r;
418b9be5
LP
1049 break;
1050
3ff9fa59
LP
1051 case ARG_IMAGE:
1052 r = parse_path_argument_and_warn(optarg, false, &arg_image);
1053 if (r < 0)
1054 return r;
1055 break;
1056
418b9be5 1057 case ARG_LOCALE:
2fc09a9c
DM
1058 r = free_and_strdup(&arg_locale, optarg);
1059 if (r < 0)
418b9be5
LP
1060 return log_oom();
1061
1062 break;
1063
1064 case ARG_LOCALE_MESSAGES:
2fc09a9c
DM
1065 r = free_and_strdup(&arg_locale_messages, optarg);
1066 if (r < 0)
418b9be5
LP
1067 return log_oom();
1068
1069 break;
1070
ed457f13 1071 case ARG_KEYMAP:
baaa35ad
ZJS
1072 if (!keymap_is_valid(optarg))
1073 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1074 "Keymap %s is not valid.", optarg);
ed457f13
TB
1075
1076 r = free_and_strdup(&arg_keymap, optarg);
1077 if (r < 0)
1078 return log_oom();
1079
1080 break;
1081
418b9be5 1082 case ARG_TIMEZONE:
baaa35ad
ZJS
1083 if (!timezone_is_valid(optarg, LOG_ERR))
1084 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1085 "Timezone %s is not valid.", optarg);
418b9be5 1086
2fc09a9c
DM
1087 r = free_and_strdup(&arg_timezone, optarg);
1088 if (r < 0)
418b9be5
LP
1089 return log_oom();
1090
1091 break;
1092
1093 case ARG_ROOT_PASSWORD:
2fc09a9c
DM
1094 r = free_and_strdup(&arg_root_password, optarg);
1095 if (r < 0)
418b9be5 1096 return log_oom();
676339a1
DDM
1097
1098 arg_root_password_is_hashed = false;
418b9be5
LP
1099 break;
1100
1101 case ARG_ROOT_PASSWORD_FILE:
0da16248 1102 arg_root_password = mfree(arg_root_password);
418b9be5
LP
1103
1104 r = read_one_line_file(optarg, &arg_root_password);
23bbb0de
MS
1105 if (r < 0)
1106 return log_error_errno(r, "Failed to read %s: %m", optarg);
418b9be5 1107
676339a1
DDM
1108 arg_root_password_is_hashed = false;
1109 break;
1110
1111 case ARG_ROOT_PASSWORD_HASHED:
1112 r = free_and_strdup(&arg_root_password, optarg);
1113 if (r < 0)
1114 return log_oom();
1115
1116 arg_root_password_is_hashed = true;
418b9be5
LP
1117 break;
1118
28900a1b 1119 case ARG_ROOT_SHELL:
31363bd5
DDM
1120 r = find_shell(optarg, arg_root);
1121 if (r < 0)
1122 return r;
28900a1b
DDM
1123
1124 r = free_and_strdup(&arg_root_shell, optarg);
1125 if (r < 0)
1126 return log_oom();
1127
1128 break;
1129
418b9be5 1130 case ARG_HOSTNAME:
baaa35ad
ZJS
1131 if (!hostname_is_valid(optarg, true))
1132 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1133 "Host name %s is not valid.", optarg);
418b9be5 1134
ae691c1d 1135 hostname_cleanup(optarg);
2fc09a9c
DM
1136 r = free_and_strdup(&arg_hostname, optarg);
1137 if (r < 0)
418b9be5
LP
1138 return log_oom();
1139
1140 break;
1141
1142 case ARG_MACHINE_ID:
baaa35ad
ZJS
1143 if (sd_id128_from_string(optarg, &arg_machine_id) < 0)
1144 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1145 "Failed to parse machine id %s.", optarg);
418b9be5
LP
1146
1147 break;
1148
a5925354
DDM
1149 case ARG_KERNEL_COMMAND_LINE:
1150 r = free_and_strdup(&arg_kernel_cmdline, optarg);
1151 if (r < 0)
1152 return log_oom();
1153
1154 break;
1155
418b9be5 1156 case ARG_PROMPT:
28900a1b
DDM
1157 arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname =
1158 arg_prompt_root_password = arg_prompt_root_shell = true;
418b9be5
LP
1159 break;
1160
1161 case ARG_PROMPT_LOCALE:
1162 arg_prompt_locale = true;
1163 break;
1164
ed457f13
TB
1165 case ARG_PROMPT_KEYMAP:
1166 arg_prompt_keymap = true;
1167 break;
1168
418b9be5
LP
1169 case ARG_PROMPT_TIMEZONE:
1170 arg_prompt_timezone = true;
1171 break;
1172
1173 case ARG_PROMPT_HOSTNAME:
1174 arg_prompt_hostname = true;
1175 break;
1176
1177 case ARG_PROMPT_ROOT_PASSWORD:
1178 arg_prompt_root_password = true;
1179 break;
1180
28900a1b
DDM
1181 case ARG_PROMPT_ROOT_SHELL:
1182 arg_prompt_root_shell = true;
1183 break;
1184
418b9be5 1185 case ARG_COPY:
28900a1b
DDM
1186 arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password =
1187 arg_copy_root_shell = true;
e926f647 1188 break;
418b9be5
LP
1189
1190 case ARG_COPY_LOCALE:
1191 arg_copy_locale = true;
1192 break;
1193
ed457f13
TB
1194 case ARG_COPY_KEYMAP:
1195 arg_copy_keymap = true;
1196 break;
1197
418b9be5
LP
1198 case ARG_COPY_TIMEZONE:
1199 arg_copy_timezone = true;
1200 break;
1201
1202 case ARG_COPY_ROOT_PASSWORD:
1203 arg_copy_root_password = true;
1204 break;
1205
28900a1b
DDM
1206 case ARG_COPY_ROOT_SHELL:
1207 arg_copy_root_shell = true;
1208 break;
1209
418b9be5 1210 case ARG_SETUP_MACHINE_ID:
418b9be5 1211 r = sd_id128_randomize(&arg_machine_id);
23bbb0de
MS
1212 if (r < 0)
1213 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
418b9be5
LP
1214
1215 break;
1216
b4909a3f
DDM
1217 case ARG_FORCE:
1218 arg_force = true;
1219 break;
1220
4926ceaf
DDM
1221 case ARG_DELETE_ROOT_PASSWORD:
1222 arg_delete_root_password = true;
1223 break;
1224
a1225020
LP
1225 case ARG_WELCOME:
1226 r = parse_boolean(optarg);
1227 if (r < 0)
1228 return log_error_errno(r, "Failed to parse --welcome= argument: %s", optarg);
1229
1230 arg_welcome = r;
1231 break;
1232
418b9be5
LP
1233 case '?':
1234 return -EINVAL;
1235
1236 default:
1237 assert_not_reached("Unhandled option");
1238 }
418b9be5 1239
a00a78b8
LP
1240 /* We check if the specified locale strings are valid down here, so that we can take --root= into
1241 * account when looking for the locale files. */
1242
1243 if (arg_locale && !locale_is_ok(arg_locale))
1244 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
1245 if (arg_locale_messages && !locale_is_ok(arg_locale_messages))
1246 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages);
1247
4926ceaf
DDM
1248 if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password))
1249 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1250 "--delete-root-password cannot be combined with other root password options");
1251
3ff9fa59
LP
1252 if (arg_image && arg_root)
1253 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
1254
418b9be5
LP
1255 return 1;
1256}
1257
45f39418 1258static int run(int argc, char *argv[]) {
3ff9fa59
LP
1259 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
1260 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
1261 _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
418b9be5
LP
1262 int r;
1263
1264 r = parse_argv(argc, argv);
1265 if (r <= 0)
45f39418 1266 return r;
418b9be5 1267
6bf3c61c 1268 log_setup_service();
418b9be5
LP
1269
1270 umask(0022);
1271
3ff9fa59
LP
1272 if (!arg_root && !arg_image) {
1273 bool enabled;
1274
1275 /* If we are called without --root=/--image= let's honour the systemd.firstboot kernel
1276 * command line option, because we are called to provision the host with basic settings (as
1277 * opposed to some other file system tree/image) */
1278
1279 r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
1280 if (r < 0)
1281 return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
1282 if (r > 0 && !enabled)
1283 return 0; /* disabled */
1284 }
1285
6aa05ebd
LP
1286 if (arg_image) {
1287 assert(!arg_root);
1288
1289 r = mount_image_privately_interactively(
1290 arg_image,
1291 DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
1292 &unlink_dir,
1293 &loop_device,
1294 &decrypted_image);
1295 if (r < 0)
1296 return r;
1297
1298 arg_root = strdup(unlink_dir);
1299 if (!arg_root)
1300 return log_oom();
1301 }
f582cbca 1302
418b9be5
LP
1303 r = process_locale();
1304 if (r < 0)
45f39418 1305 return r;
418b9be5 1306
ed457f13
TB
1307 r = process_keymap();
1308 if (r < 0)
45f39418 1309 return r;
ed457f13 1310
418b9be5
LP
1311 r = process_timezone();
1312 if (r < 0)
45f39418 1313 return r;
418b9be5
LP
1314
1315 r = process_hostname();
1316 if (r < 0)
45f39418 1317 return r;
418b9be5
LP
1318
1319 r = process_machine_id();
1320 if (r < 0)
45f39418 1321 return r;
418b9be5 1322
28900a1b 1323 r = process_root_args();
418b9be5 1324 if (r < 0)
45f39418
YW
1325 return r;
1326
a5925354
DDM
1327 r = process_kernel_cmdline();
1328 if (r < 0)
1329 return r;
1330
45f39418 1331 return 0;
418b9be5 1332}
45f39418
YW
1333
1334DEFINE_MAIN_FUNCTION(run);