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