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