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