]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/firstboot/firstboot.c
util-lib: split out file attribute calls to chattr-util.[ch]
[thirdparty/systemd.git] / src / firstboot / firstboot.c
CommitLineData
418b9be5
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
418b9be5 22#include <fcntl.h>
418b9be5
LP
23#include <getopt.h>
24#include <shadow.h>
3f6fd1ba 25#include <unistd.h>
418b9be5 26
3f6fd1ba 27#include "ask-password-api.h"
418b9be5 28#include "copy.h"
6bedfcbb 29#include "fd-util.h"
3f6fd1ba
LP
30#include "fileio.h"
31#include "hostname-util.h"
32#include "locale-util.h"
418b9be5 33#include "mkdir.h"
6bedfcbb 34#include "parse-util.h"
418b9be5 35#include "path-util.h"
3df3e884 36#include "random-util.h"
6bedfcbb 37#include "string-util.h"
3f6fd1ba 38#include "strv.h"
288a74cc 39#include "terminal-util.h"
3f6fd1ba 40#include "time-util.h"
e929bee0 41#include "user-util.h"
418b9be5
LP
42
43static char *arg_root = NULL;
44static char *arg_locale = NULL; /* $LANG */
45static char *arg_locale_messages = NULL; /* $LC_MESSAGES */
46static char *arg_timezone = NULL;
47static char *arg_hostname = NULL;
48static sd_id128_t arg_machine_id = {};
49static char *arg_root_password = NULL;
50static bool arg_prompt_locale = false;
51static bool arg_prompt_timezone = false;
52static bool arg_prompt_hostname = false;
53static bool arg_prompt_root_password = false;
54static bool arg_copy_locale = false;
55static bool arg_copy_timezone = false;
56static bool arg_copy_root_password = false;
57
418b9be5
LP
58static bool press_any_key(void) {
59 char k = 0;
60 bool need_nl = true;
61
62 printf("-- Press any key to proceed --");
63 fflush(stdout);
64
94956f8f 65 (void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl);
418b9be5
LP
66
67 if (need_nl)
68 putchar('\n');
69
70 return k != 'q';
71}
72
73static void print_welcome(void) {
74 _cleanup_free_ char *pretty_name = NULL;
75 const char *os_release = NULL;
76 static bool done = false;
77 int r;
78
79 if (done)
80 return;
81
1d13f648 82 os_release = prefix_roota(arg_root, "/etc/os-release");
418b9be5
LP
83 r = parse_env_file(os_release, NEWLINE,
84 "PRETTY_NAME", &pretty_name,
85 NULL);
86 if (r == -ENOENT) {
87
1d13f648 88 os_release = prefix_roota(arg_root, "/usr/lib/os-release");
418b9be5
LP
89 r = parse_env_file(os_release, NEWLINE,
90 "PRETTY_NAME", &pretty_name,
91 NULL);
92 }
93
94 if (r < 0 && r != -ENOENT)
da927ba9 95 log_warning_errno(r, "Failed to read os-release file: %m");
418b9be5
LP
96
97 printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n",
98 isempty(pretty_name) ? "Linux" : pretty_name);
99
100 press_any_key();
101
102 done = true;
103}
104
105static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) {
106 unsigned n, per_column, i, j;
107 unsigned break_lines, break_modulo;
108
109 assert(n_columns > 0);
110
111 n = strv_length(x);
112 per_column = (n + n_columns - 1) / n_columns;
113
114 break_lines = lines();
115 if (break_lines > 2)
116 break_lines--;
117
118 /* The first page gets two extra lines, since we want to show
119 * a title */
120 break_modulo = break_lines;
121 if (break_modulo > 3)
122 break_modulo -= 3;
123
124 for (i = 0; i < per_column; i++) {
125
126 for (j = 0; j < n_columns; j ++) {
127 _cleanup_free_ char *e = NULL;
128
129 if (j * per_column + i >= n)
130 break;
131
132 e = ellipsize(x[j * per_column + i], width, percentage);
133 if (!e)
134 return log_oom();
135
136 printf("%4u) %-*s", j * per_column + i + 1, width, e);
137 }
138
139 putchar('\n');
140
141 /* on the first screen we reserve 2 extra lines for the title */
142 if (i % break_lines == break_modulo) {
143 if (!press_any_key())
144 return 0;
145 }
146 }
147
148 return 0;
149}
150
151static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) {
152 int r;
153
154 assert(text);
155 assert(is_valid);
156 assert(ret);
157
158 for (;;) {
159 _cleanup_free_ char *p = NULL;
160 unsigned u;
161
162 r = ask_string(&p, "%s %s (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET), text);
23bbb0de
MS
163 if (r < 0)
164 return log_error_errno(r, "Failed to query user: %m");
418b9be5
LP
165
166 if (isempty(p)) {
167 log_warning("No data entered, skipping.");
168 return 0;
169 }
170
171 r = safe_atou(p, &u);
172 if (r >= 0) {
173 char *c;
174
175 if (u <= 0 || u > strv_length(l)) {
176 log_error("Specified entry number out of range.");
177 continue;
178 }
179
180 log_info("Selected '%s'.", l[u-1]);
181
182 c = strdup(l[u-1]);
183 if (!c)
184 return log_oom();
185
186 free(*ret);
187 *ret = c;
188 return 0;
189 }
190
191 if (!is_valid(p)) {
192 log_error("Entered data invalid.");
193 continue;
194 }
195
196 free(*ret);
197 *ret = p;
198 p = 0;
199 return 0;
200 }
201}
202
203static int prompt_locale(void) {
204 _cleanup_strv_free_ char **locales = NULL;
205 int r;
206
207 if (arg_locale || arg_locale_messages)
208 return 0;
209
210 if (!arg_prompt_locale)
211 return 0;
212
213 r = get_locales(&locales);
23bbb0de
MS
214 if (r < 0)
215 return log_error_errno(r, "Cannot query locales list: %m");
418b9be5
LP
216
217 print_welcome();
218
219 printf("\nAvailable Locales:\n\n");
220 r = show_menu(locales, 3, 22, 60);
221 if (r < 0)
222 return r;
223
224 putchar('\n');
225
226 r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale);
227 if (r < 0)
228 return r;
229
230 if (isempty(arg_locale))
231 return 0;
232
233 r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages);
234 if (r < 0)
235 return r;
236
237 return 0;
238}
239
240static int process_locale(void) {
241 const char *etc_localeconf;
242 char* locales[3];
243 unsigned i = 0;
244 int r;
245
1d13f648 246 etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
418b9be5
LP
247 if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
248 return 0;
249
250 if (arg_copy_locale && arg_root) {
251
252 mkdir_parents(etc_localeconf, 0755);
f2068bcc 253 r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0);
418b9be5 254 if (r != -ENOENT) {
23bbb0de
MS
255 if (r < 0)
256 return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf);
418b9be5
LP
257
258 log_info("%s copied.", etc_localeconf);
259 return 0;
260 }
261 }
262
263 r = prompt_locale();
264 if (r < 0)
265 return r;
266
267 if (!isempty(arg_locale))
63c372cb 268 locales[i++] = strjoina("LANG=", arg_locale);
418b9be5 269 if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale))
63c372cb 270 locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages);
418b9be5
LP
271
272 if (i == 0)
273 return 0;
274
275 locales[i] = NULL;
276
277 mkdir_parents(etc_localeconf, 0755);
278 r = write_env_file(etc_localeconf, locales);
23bbb0de
MS
279 if (r < 0)
280 return log_error_errno(r, "Failed to write %s: %m", etc_localeconf);
418b9be5
LP
281
282 log_info("%s written.", etc_localeconf);
283 return 0;
284}
285
286static int prompt_timezone(void) {
287 _cleanup_strv_free_ char **zones = NULL;
288 int r;
289
290 if (arg_timezone)
291 return 0;
292
293 if (!arg_prompt_timezone)
294 return 0;
295
296 r = get_timezones(&zones);
23bbb0de
MS
297 if (r < 0)
298 return log_error_errno(r, "Cannot query timezone list: %m");
418b9be5
LP
299
300 print_welcome();
301
302 printf("\nAvailable Time Zones:\n\n");
303 r = show_menu(zones, 3, 22, 30);
304 if (r < 0)
305 return r;
306
307 putchar('\n');
308
309 r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone);
310 if (r < 0)
311 return r;
312
313 return 0;
314}
315
316static int process_timezone(void) {
317 const char *etc_localtime, *e;
318 int r;
319
1d13f648 320 etc_localtime = prefix_roota(arg_root, "/etc/localtime");
418b9be5
LP
321 if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
322 return 0;
323
324 if (arg_copy_timezone && arg_root) {
325 _cleanup_free_ char *p = NULL;
326
327 r = readlink_malloc("/etc/localtime", &p);
328 if (r != -ENOENT) {
23bbb0de
MS
329 if (r < 0)
330 return log_error_errno(r, "Failed to read host timezone: %m");
418b9be5
LP
331
332 mkdir_parents(etc_localtime, 0755);
4a62c710
MS
333 if (symlink(p, etc_localtime) < 0)
334 return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
418b9be5
LP
335
336 log_info("%s copied.", etc_localtime);
337 return 0;
338 }
339 }
340
341 r = prompt_timezone();
342 if (r < 0)
343 return r;
344
345 if (isempty(arg_timezone))
346 return 0;
347
63c372cb 348 e = strjoina("../usr/share/zoneinfo/", arg_timezone);
418b9be5
LP
349
350 mkdir_parents(etc_localtime, 0755);
4a62c710
MS
351 if (symlink(e, etc_localtime) < 0)
352 return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
418b9be5
LP
353
354 log_info("%s written", etc_localtime);
355 return 0;
356}
357
358static int prompt_hostname(void) {
359 int r;
360
361 if (arg_hostname)
362 return 0;
363
364 if (!arg_prompt_hostname)
365 return 0;
366
367 print_welcome();
368 putchar('\n');
369
370 for (;;) {
371 _cleanup_free_ char *h = NULL;
372
373 r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET));
23bbb0de
MS
374 if (r < 0)
375 return log_error_errno(r, "Failed to query hostname: %m");
418b9be5
LP
376
377 if (isempty(h)) {
378 log_warning("No hostname entered, skipping.");
379 break;
380 }
381
34ad6090 382 if (!hostname_is_valid(h, true)) {
418b9be5
LP
383 log_error("Specified hostname invalid.");
384 continue;
385 }
386
34ad6090 387 /* Get rid of the trailing dot that we allow, but don't want to see */
ae691c1d 388 arg_hostname = hostname_cleanup(h);
418b9be5
LP
389 h = NULL;
390 break;
391 }
392
393 return 0;
394}
395
396static int process_hostname(void) {
397 const char *etc_hostname;
398 int r;
399
1d13f648 400 etc_hostname = prefix_roota(arg_root, "/etc/hostname");
418b9be5
LP
401 if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
402 return 0;
403
404 r = prompt_hostname();
405 if (r < 0)
406 return r;
407
408 if (isempty(arg_hostname))
409 return 0;
410
411 mkdir_parents(etc_hostname, 0755);
4c1fc3e4 412 r = write_string_file(etc_hostname, arg_hostname, WRITE_STRING_FILE_CREATE);
23bbb0de
MS
413 if (r < 0)
414 return log_error_errno(r, "Failed to write %s: %m", etc_hostname);
418b9be5
LP
415
416 log_info("%s written.", etc_hostname);
417 return 0;
418}
419
420static int process_machine_id(void) {
421 const char *etc_machine_id;
422 char id[SD_ID128_STRING_MAX];
423 int r;
424
1d13f648 425 etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
418b9be5
LP
426 if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
427 return 0;
428
418b9be5
LP
429 if (sd_id128_equal(arg_machine_id, SD_ID128_NULL))
430 return 0;
431
432 mkdir_parents(etc_machine_id, 0755);
4c1fc3e4 433 r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), WRITE_STRING_FILE_CREATE);
23bbb0de
MS
434 if (r < 0)
435 return log_error_errno(r, "Failed to write machine id: %m");
418b9be5
LP
436
437 log_info("%s written.", etc_machine_id);
438 return 0;
439}
440
441static int prompt_root_password(void) {
442 const char *msg1, *msg2, *etc_shadow;
443 int r;
444
445 if (arg_root_password)
446 return 0;
447
448 if (!arg_prompt_root_password)
449 return 0;
450
1d13f648 451 etc_shadow = prefix_roota(arg_root, "/etc/shadow");
418b9be5
LP
452 if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
453 return 0;
454
455 print_welcome();
456 putchar('\n');
457
63c372cb
LP
458 msg1 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
459 msg2 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: ");
418b9be5
LP
460
461 for (;;) {
ab84f5b9 462 _cleanup_string_free_erase_ char *a = NULL, *b = NULL;
418b9be5 463
e287086b 464 r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a);
23bbb0de
MS
465 if (r < 0)
466 return log_error_errno(r, "Failed to query root password: %m");
418b9be5
LP
467
468 if (isempty(a)) {
469 log_warning("No password entered, skipping.");
470 break;
471 }
472
e287086b 473 r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b);
ab84f5b9 474 if (r < 0)
00843602 475 return log_error_errno(r, "Failed to query root password: %m");
418b9be5
LP
476
477 if (!streq(a, b)) {
478 log_error("Entered passwords did not match, please try again.");
418b9be5
LP
479 continue;
480 }
481
418b9be5
LP
482 arg_root_password = a;
483 a = NULL;
484 break;
485 }
486
487 return 0;
488}
489
490static int write_root_shadow(const char *path, const struct spwd *p) {
491 _cleanup_fclose_ FILE *f = NULL;
492 assert(path);
493 assert(p);
494
3250929b
LP
495 RUN_WITH_UMASK(0777)
496 f = fopen(path, "wex");
418b9be5
LP
497 if (!f)
498 return -errno;
499
500 errno = 0;
501 if (putspent(p, f) != 0)
502 return errno ? -errno : -EIO;
503
504 return fflush_and_check(f);
505}
506
507static int process_root_password(void) {
508
509 static const char table[] =
510 "abcdefghijklmnopqrstuvwxyz"
511 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
512 "0123456789"
513 "./";
514
515 struct spwd item = {
516 .sp_namp = (char*) "root",
ad525df8
IS
517 .sp_min = -1,
518 .sp_max = -1,
519 .sp_warn = -1,
418b9be5
LP
520 .sp_inact = -1,
521 .sp_expire = -1,
522 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
523 };
45035609
LP
524
525 _cleanup_close_ int lock = -1;
418b9be5
LP
526 char salt[3+16+1+1];
527 uint8_t raw[16];
528 unsigned i;
529 char *j;
530
531 const char *etc_shadow;
532 int r;
533
1d13f648 534 etc_shadow = prefix_roota(arg_root, "/etc/shadow");
418b9be5
LP
535 if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
536 return 0;
537
45035609
LP
538 mkdir_parents(etc_shadow, 0755);
539
e929bee0 540 lock = take_etc_passwd_lock(arg_root);
45035609
LP
541 if (lock < 0)
542 return lock;
543
418b9be5
LP
544 if (arg_copy_root_password && arg_root) {
545 struct spwd *p;
546
547 errno = 0;
548 p = getspnam("root");
549 if (p || errno != ENOENT) {
550 if (!p) {
551 if (!errno)
552 errno = EIO;
553
56f64d95 554 log_error_errno(errno, "Failed to find shadow entry for root: %m");
418b9be5
LP
555 return -errno;
556 }
557
558 r = write_root_shadow(etc_shadow, p);
23bbb0de
MS
559 if (r < 0)
560 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
418b9be5
LP
561
562 log_info("%s copied.", etc_shadow);
563 return 0;
564 }
565 }
566
567 r = prompt_root_password();
568 if (r < 0)
569 return r;
570
571 if (!arg_root_password)
572 return 0;
573
574 r = dev_urandom(raw, 16);
23bbb0de
MS
575 if (r < 0)
576 return log_error_errno(r, "Failed to get salt: %m");
418b9be5
LP
577
578 /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
579 assert_cc(sizeof(table) == 64 + 1);
580 j = stpcpy(salt, "$6$");
581 for (i = 0; i < 16; i++)
582 j[i] = table[raw[i] & 63];
583 j[i++] = '$';
584 j[i] = 0;
585
586 errno = 0;
587 item.sp_pwdp = crypt(arg_root_password, salt);
588 if (!item.sp_pwdp) {
589 if (!errno)
590 errno = -EINVAL;
591
56f64d95 592 log_error_errno(errno, "Failed to encrypt password: %m");
418b9be5
LP
593 return -errno;
594 }
595
596 item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
597
598 r = write_root_shadow(etc_shadow, &item);
23bbb0de
MS
599 if (r < 0)
600 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
418b9be5
LP
601
602 log_info("%s written.", etc_shadow);
603 return 0;
604}
605
601185b4 606static void help(void) {
418b9be5
LP
607 printf("%s [OPTIONS...]\n\n"
608 "Configures basic settings of the system.\n\n"
609 " -h --help Show this help\n"
610 " --version Show package version\n"
611 " --root=PATH Operate on an alternate filesystem root\n"
612 " --locale=LOCALE Set primary locale (LANG=)\n"
613 " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n"
614 " --timezone=TIMEZONE Set timezone\n"
615 " --hostname=NAME Set host name\n"
616 " --machine-ID=ID Set machine ID\n"
617 " --root-password=PASSWORD Set root password\n"
618 " --root-password-file=FILE Set root password from file\n"
619 " --prompt-locale Prompt the user for locale settings\n"
620 " --prompt-timezone Prompt the user for timezone\n"
621 " --prompt-hostname Prompt the user for hostname\n"
622 " --prompt-root-password Prompt the user for root password\n"
b57b0625 623 " --prompt Prompt for all of the above\n"
418b9be5
LP
624 " --copy-locale Copy locale from host\n"
625 " --copy-timezone Copy timezone from host\n"
626 " --copy-root-password Copy root password from host\n"
627 " --copy Copy locale, timezone, root password\n"
601185b4
ZJS
628 " --setup-machine-id Generate a new random machine ID\n"
629 , program_invocation_short_name);
418b9be5
LP
630}
631
632static int parse_argv(int argc, char *argv[]) {
633
634 enum {
635 ARG_VERSION = 0x100,
636 ARG_ROOT,
637 ARG_LOCALE,
638 ARG_LOCALE_MESSAGES,
639 ARG_TIMEZONE,
640 ARG_HOSTNAME,
641 ARG_MACHINE_ID,
642 ARG_ROOT_PASSWORD,
643 ARG_ROOT_PASSWORD_FILE,
644 ARG_PROMPT,
645 ARG_PROMPT_LOCALE,
646 ARG_PROMPT_TIMEZONE,
647 ARG_PROMPT_HOSTNAME,
648 ARG_PROMPT_ROOT_PASSWORD,
649 ARG_COPY,
650 ARG_COPY_LOCALE,
651 ARG_COPY_TIMEZONE,
652 ARG_COPY_ROOT_PASSWORD,
653 ARG_SETUP_MACHINE_ID,
654 };
655
656 static const struct option options[] = {
657 { "help", no_argument, NULL, 'h' },
658 { "version", no_argument, NULL, ARG_VERSION },
659 { "root", required_argument, NULL, ARG_ROOT },
660 { "locale", required_argument, NULL, ARG_LOCALE },
661 { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES },
662 { "timezone", required_argument, NULL, ARG_TIMEZONE },
663 { "hostname", required_argument, NULL, ARG_HOSTNAME },
664 { "machine-id", required_argument, NULL, ARG_MACHINE_ID },
665 { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD },
666 { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE },
667 { "prompt", no_argument, NULL, ARG_PROMPT },
668 { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE },
669 { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE },
670 { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME },
671 { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD },
672 { "copy", no_argument, NULL, ARG_COPY },
673 { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE },
674 { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE },
675 { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD },
676 { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID },
677 {}
678 };
679
680 int r, c;
681
682 assert(argc >= 0);
683 assert(argv);
684
601185b4 685 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
418b9be5
LP
686
687 switch (c) {
688
689 case 'h':
601185b4
ZJS
690 help();
691 return 0;
418b9be5
LP
692
693 case ARG_VERSION:
3f6fd1ba 694 return version();
418b9be5
LP
695
696 case ARG_ROOT:
0f03c2a4 697 r = parse_path_argument_and_warn(optarg, true, &arg_root);
0f474365 698 if (r < 0)
0f03c2a4 699 return r;
418b9be5
LP
700 break;
701
702 case ARG_LOCALE:
703 if (!locale_is_valid(optarg)) {
704 log_error("Locale %s is not valid.", optarg);
705 return -EINVAL;
706 }
707
2fc09a9c
DM
708 r = free_and_strdup(&arg_locale, optarg);
709 if (r < 0)
418b9be5
LP
710 return log_oom();
711
712 break;
713
714 case ARG_LOCALE_MESSAGES:
715 if (!locale_is_valid(optarg)) {
716 log_error("Locale %s is not valid.", optarg);
717 return -EINVAL;
718 }
719
2fc09a9c
DM
720 r = free_and_strdup(&arg_locale_messages, optarg);
721 if (r < 0)
418b9be5
LP
722 return log_oom();
723
724 break;
725
726 case ARG_TIMEZONE:
727 if (!timezone_is_valid(optarg)) {
728 log_error("Timezone %s is not valid.", optarg);
729 return -EINVAL;
730 }
731
2fc09a9c
DM
732 r = free_and_strdup(&arg_timezone, optarg);
733 if (r < 0)
418b9be5
LP
734 return log_oom();
735
736 break;
737
738 case ARG_ROOT_PASSWORD:
2fc09a9c
DM
739 r = free_and_strdup(&arg_root_password, optarg);
740 if (r < 0)
418b9be5 741 return log_oom();
418b9be5
LP
742 break;
743
744 case ARG_ROOT_PASSWORD_FILE:
0da16248 745 arg_root_password = mfree(arg_root_password);
418b9be5
LP
746
747 r = read_one_line_file(optarg, &arg_root_password);
23bbb0de
MS
748 if (r < 0)
749 return log_error_errno(r, "Failed to read %s: %m", optarg);
418b9be5
LP
750
751 break;
752
753 case ARG_HOSTNAME:
34ad6090 754 if (!hostname_is_valid(optarg, true)) {
418b9be5
LP
755 log_error("Host name %s is not valid.", optarg);
756 return -EINVAL;
757 }
758
ae691c1d 759 hostname_cleanup(optarg);
2fc09a9c
DM
760 r = free_and_strdup(&arg_hostname, optarg);
761 if (r < 0)
418b9be5
LP
762 return log_oom();
763
764 break;
765
766 case ARG_MACHINE_ID:
767 if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
768 log_error("Failed to parse machine id %s.", optarg);
769 return -EINVAL;
770 }
771
772 break;
773
774 case ARG_PROMPT:
775 arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
776 break;
777
778 case ARG_PROMPT_LOCALE:
779 arg_prompt_locale = true;
780 break;
781
782 case ARG_PROMPT_TIMEZONE:
783 arg_prompt_timezone = true;
784 break;
785
786 case ARG_PROMPT_HOSTNAME:
787 arg_prompt_hostname = true;
788 break;
789
790 case ARG_PROMPT_ROOT_PASSWORD:
791 arg_prompt_root_password = true;
792 break;
793
794 case ARG_COPY:
795 arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true;
e926f647 796 break;
418b9be5
LP
797
798 case ARG_COPY_LOCALE:
799 arg_copy_locale = true;
800 break;
801
802 case ARG_COPY_TIMEZONE:
803 arg_copy_timezone = true;
804 break;
805
806 case ARG_COPY_ROOT_PASSWORD:
807 arg_copy_root_password = true;
808 break;
809
810 case ARG_SETUP_MACHINE_ID:
811
812 r = sd_id128_randomize(&arg_machine_id);
23bbb0de
MS
813 if (r < 0)
814 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
418b9be5
LP
815
816 break;
817
818 case '?':
819 return -EINVAL;
820
821 default:
822 assert_not_reached("Unhandled option");
823 }
418b9be5
LP
824
825 return 1;
826}
827
828int main(int argc, char *argv[]) {
829 int r;
830
831 r = parse_argv(argc, argv);
832 if (r <= 0)
833 goto finish;
834
835 log_set_target(LOG_TARGET_AUTO);
836 log_parse_environment();
837 log_open();
838
839 umask(0022);
840
841 r = process_locale();
842 if (r < 0)
843 goto finish;
844
845 r = process_timezone();
846 if (r < 0)
847 goto finish;
848
849 r = process_hostname();
850 if (r < 0)
851 goto finish;
852
853 r = process_machine_id();
854 if (r < 0)
855 goto finish;
856
857 r = process_root_password();
858 if (r < 0)
859 goto finish;
860
861finish:
862 free(arg_root);
863 free(arg_locale);
864 free(arg_locale_messages);
865 free(arg_timezone);
866 free(arg_hostname);
1602b008 867 string_erase(arg_root_password);
418b9be5
LP
868 free(arg_root_password);
869
870 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
871}