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