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