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