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