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