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