]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/firstboot/firstboot.c
path-util: unify how we process paths specified on the command line
[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 r = parse_path_argument_and_warn(optarg, true, &arg_root);
694 if (r < 0)
695 return r;
696 break;
697
698 case ARG_LOCALE:
699 if (!locale_is_valid(optarg)) {
700 log_error("Locale %s is not valid.", optarg);
701 return -EINVAL;
702 }
703
704 r = free_and_strdup(&arg_locale, optarg);
705 if (r < 0)
706 return log_oom();
707
708 break;
709
710 case ARG_LOCALE_MESSAGES:
711 if (!locale_is_valid(optarg)) {
712 log_error("Locale %s is not valid.", optarg);
713 return -EINVAL;
714 }
715
716 r = free_and_strdup(&arg_locale_messages, optarg);
717 if (r < 0)
718 return log_oom();
719
720 break;
721
722 case ARG_TIMEZONE:
723 if (!timezone_is_valid(optarg)) {
724 log_error("Timezone %s is not valid.", optarg);
725 return -EINVAL;
726 }
727
728 r = free_and_strdup(&arg_timezone, optarg);
729 if (r < 0)
730 return log_oom();
731
732 break;
733
734 case ARG_ROOT_PASSWORD:
735 r = free_and_strdup(&arg_root_password, optarg);
736 if (r < 0)
737 return log_oom();
738 break;
739
740 case ARG_ROOT_PASSWORD_FILE:
741 arg_root_password = mfree(arg_root_password);
742
743 r = read_one_line_file(optarg, &arg_root_password);
744 if (r < 0)
745 return log_error_errno(r, "Failed to read %s: %m", optarg);
746
747 break;
748
749 case ARG_HOSTNAME:
750 if (!hostname_is_valid(optarg, true)) {
751 log_error("Host name %s is not valid.", optarg);
752 return -EINVAL;
753 }
754
755 hostname_cleanup(optarg);
756 r = free_and_strdup(&arg_hostname, optarg);
757 if (r < 0)
758 return log_oom();
759
760 break;
761
762 case ARG_MACHINE_ID:
763 if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
764 log_error("Failed to parse machine id %s.", optarg);
765 return -EINVAL;
766 }
767
768 break;
769
770 case ARG_PROMPT:
771 arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
772 break;
773
774 case ARG_PROMPT_LOCALE:
775 arg_prompt_locale = true;
776 break;
777
778 case ARG_PROMPT_TIMEZONE:
779 arg_prompt_timezone = true;
780 break;
781
782 case ARG_PROMPT_HOSTNAME:
783 arg_prompt_hostname = true;
784 break;
785
786 case ARG_PROMPT_ROOT_PASSWORD:
787 arg_prompt_root_password = true;
788 break;
789
790 case ARG_COPY:
791 arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true;
792 break;
793
794 case ARG_COPY_LOCALE:
795 arg_copy_locale = true;
796 break;
797
798 case ARG_COPY_TIMEZONE:
799 arg_copy_timezone = true;
800 break;
801
802 case ARG_COPY_ROOT_PASSWORD:
803 arg_copy_root_password = true;
804 break;
805
806 case ARG_SETUP_MACHINE_ID:
807
808 r = sd_id128_randomize(&arg_machine_id);
809 if (r < 0)
810 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
811
812 break;
813
814 case '?':
815 return -EINVAL;
816
817 default:
818 assert_not_reached("Unhandled option");
819 }
820
821 return 1;
822 }
823
824 int main(int argc, char *argv[]) {
825 int r;
826
827 r = parse_argv(argc, argv);
828 if (r <= 0)
829 goto finish;
830
831 log_set_target(LOG_TARGET_AUTO);
832 log_parse_environment();
833 log_open();
834
835 umask(0022);
836
837 r = process_locale();
838 if (r < 0)
839 goto finish;
840
841 r = process_timezone();
842 if (r < 0)
843 goto finish;
844
845 r = process_hostname();
846 if (r < 0)
847 goto finish;
848
849 r = process_machine_id();
850 if (r < 0)
851 goto finish;
852
853 r = process_root_password();
854 if (r < 0)
855 goto finish;
856
857 finish:
858 free(arg_root);
859 free(arg_locale);
860 free(arg_locale_messages);
861 free(arg_timezone);
862 free(arg_hostname);
863 string_erase(arg_root_password);
864 free(arg_root_password);
865
866 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
867 }