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