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