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