]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/firstboot/firstboot.c
tree-wide: whenever we deal with passwords, erase them from memory after use
[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_free_ 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 string_erase(a);
472 return log_error_errno(r, "Failed to query root password: %m");
473 }
474
475 if (!streq(a, b)) {
476 log_error("Entered passwords did not match, please try again.");
477 string_erase(a);
478 string_erase(b);
479 continue;
480 }
481
482 string_erase(b);
483 arg_root_password = a;
484 a = NULL;
485 break;
486 }
487
488 return 0;
489 }
490
491 static int write_root_shadow(const char *path, const struct spwd *p) {
492 _cleanup_fclose_ FILE *f = NULL;
493 assert(path);
494 assert(p);
495
496 RUN_WITH_UMASK(0777)
497 f = fopen(path, "wex");
498 if (!f)
499 return -errno;
500
501 errno = 0;
502 if (putspent(p, f) != 0)
503 return errno ? -errno : -EIO;
504
505 return fflush_and_check(f);
506 }
507
508 static int process_root_password(void) {
509
510 static const char table[] =
511 "abcdefghijklmnopqrstuvwxyz"
512 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
513 "0123456789"
514 "./";
515
516 struct spwd item = {
517 .sp_namp = (char*) "root",
518 .sp_min = -1,
519 .sp_max = -1,
520 .sp_warn = -1,
521 .sp_inact = -1,
522 .sp_expire = -1,
523 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
524 };
525
526 _cleanup_close_ int lock = -1;
527 char salt[3+16+1+1];
528 uint8_t raw[16];
529 unsigned i;
530 char *j;
531
532 const char *etc_shadow;
533 int r;
534
535 etc_shadow = prefix_roota(arg_root, "/etc/shadow");
536 if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
537 return 0;
538
539 mkdir_parents(etc_shadow, 0755);
540
541 lock = take_password_lock(arg_root);
542 if (lock < 0)
543 return lock;
544
545 if (arg_copy_root_password && arg_root) {
546 struct spwd *p;
547
548 errno = 0;
549 p = getspnam("root");
550 if (p || errno != ENOENT) {
551 if (!p) {
552 if (!errno)
553 errno = EIO;
554
555 log_error_errno(errno, "Failed to find shadow entry for root: %m");
556 return -errno;
557 }
558
559 r = write_root_shadow(etc_shadow, p);
560 if (r < 0)
561 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
562
563 log_info("%s copied.", etc_shadow);
564 return 0;
565 }
566 }
567
568 r = prompt_root_password();
569 if (r < 0)
570 return r;
571
572 if (!arg_root_password)
573 return 0;
574
575 r = dev_urandom(raw, 16);
576 if (r < 0)
577 return log_error_errno(r, "Failed to get salt: %m");
578
579 /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
580 assert_cc(sizeof(table) == 64 + 1);
581 j = stpcpy(salt, "$6$");
582 for (i = 0; i < 16; i++)
583 j[i] = table[raw[i] & 63];
584 j[i++] = '$';
585 j[i] = 0;
586
587 errno = 0;
588 item.sp_pwdp = crypt(arg_root_password, salt);
589 if (!item.sp_pwdp) {
590 if (!errno)
591 errno = -EINVAL;
592
593 log_error_errno(errno, "Failed to encrypt password: %m");
594 return -errno;
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 free(arg_root);
699 arg_root = path_make_absolute_cwd(optarg);
700 if (!arg_root)
701 return log_oom();
702
703 path_kill_slashes(arg_root);
704
705 if (path_equal(arg_root, "/"))
706 arg_root = mfree(arg_root);
707
708 break;
709
710 case ARG_LOCALE:
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, optarg);
717 if (r < 0)
718 return log_oom();
719
720 break;
721
722 case ARG_LOCALE_MESSAGES:
723 if (!locale_is_valid(optarg)) {
724 log_error("Locale %s is not valid.", optarg);
725 return -EINVAL;
726 }
727
728 r = free_and_strdup(&arg_locale_messages, optarg);
729 if (r < 0)
730 return log_oom();
731
732 break;
733
734 case ARG_TIMEZONE:
735 if (!timezone_is_valid(optarg)) {
736 log_error("Timezone %s is not valid.", optarg);
737 return -EINVAL;
738 }
739
740 r = free_and_strdup(&arg_timezone, optarg);
741 if (r < 0)
742 return log_oom();
743
744 break;
745
746 case ARG_ROOT_PASSWORD:
747 r = free_and_strdup(&arg_root_password, optarg);
748 if (r < 0)
749 return log_oom();
750 break;
751
752 case ARG_ROOT_PASSWORD_FILE:
753 arg_root_password = mfree(arg_root_password);
754
755 r = read_one_line_file(optarg, &arg_root_password);
756 if (r < 0)
757 return log_error_errno(r, "Failed to read %s: %m", optarg);
758
759 break;
760
761 case ARG_HOSTNAME:
762 if (!hostname_is_valid(optarg, true)) {
763 log_error("Host name %s is not valid.", optarg);
764 return -EINVAL;
765 }
766
767 hostname_cleanup(optarg);
768 r = free_and_strdup(&arg_hostname, optarg);
769 if (r < 0)
770 return log_oom();
771
772 break;
773
774 case ARG_MACHINE_ID:
775 if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
776 log_error("Failed to parse machine id %s.", optarg);
777 return -EINVAL;
778 }
779
780 break;
781
782 case ARG_PROMPT:
783 arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
784 break;
785
786 case ARG_PROMPT_LOCALE:
787 arg_prompt_locale = true;
788 break;
789
790 case ARG_PROMPT_TIMEZONE:
791 arg_prompt_timezone = true;
792 break;
793
794 case ARG_PROMPT_HOSTNAME:
795 arg_prompt_hostname = true;
796 break;
797
798 case ARG_PROMPT_ROOT_PASSWORD:
799 arg_prompt_root_password = true;
800 break;
801
802 case ARG_COPY:
803 arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true;
804 break;
805
806 case ARG_COPY_LOCALE:
807 arg_copy_locale = true;
808 break;
809
810 case ARG_COPY_TIMEZONE:
811 arg_copy_timezone = true;
812 break;
813
814 case ARG_COPY_ROOT_PASSWORD:
815 arg_copy_root_password = true;
816 break;
817
818 case ARG_SETUP_MACHINE_ID:
819
820 r = sd_id128_randomize(&arg_machine_id);
821 if (r < 0)
822 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
823
824 break;
825
826 case '?':
827 return -EINVAL;
828
829 default:
830 assert_not_reached("Unhandled option");
831 }
832
833 return 1;
834 }
835
836 int main(int argc, char *argv[]) {
837 int r;
838
839 r = parse_argv(argc, argv);
840 if (r <= 0)
841 goto finish;
842
843 log_set_target(LOG_TARGET_AUTO);
844 log_parse_environment();
845 log_open();
846
847 umask(0022);
848
849 r = process_locale();
850 if (r < 0)
851 goto finish;
852
853 r = process_timezone();
854 if (r < 0)
855 goto finish;
856
857 r = process_hostname();
858 if (r < 0)
859 goto finish;
860
861 r = process_machine_id();
862 if (r < 0)
863 goto finish;
864
865 r = process_root_password();
866 if (r < 0)
867 goto finish;
868
869 finish:
870 free(arg_root);
871 free(arg_locale);
872 free(arg_locale_messages);
873 free(arg_timezone);
874 free(arg_hostname);
875 string_erase(arg_root_password);
876 free(arg_root_password);
877
878 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
879 }