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