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