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