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