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