]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/firstboot/firstboot.c
util-lib: move more locale-related calls to locale-util.[ch]
[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
418b9be5 22#include <fcntl.h>
418b9be5
LP
23#include <getopt.h>
24#include <shadow.h>
3f6fd1ba 25#include <unistd.h>
418b9be5 26
3f6fd1ba 27#include "ask-password-api.h"
418b9be5 28#include "copy.h"
6bedfcbb 29#include "fd-util.h"
3f6fd1ba 30#include "fileio.h"
f4f15635 31#include "fs-util.h"
3f6fd1ba
LP
32#include "hostname-util.h"
33#include "locale-util.h"
418b9be5 34#include "mkdir.h"
6bedfcbb 35#include "parse-util.h"
418b9be5 36#include "path-util.h"
3df3e884 37#include "random-util.h"
6bedfcbb 38#include "string-util.h"
3f6fd1ba 39#include "strv.h"
288a74cc 40#include "terminal-util.h"
3f6fd1ba 41#include "time-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
163 r = ask_string(&p, "%s %s (empty to skip): ", draw_special_char(DRAW_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");
418b9be5
LP
248 if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
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");
418b9be5
LP
322 if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
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
374 r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_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");
418b9be5
LP
402 if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
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");
418b9be5
LP
427 if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
428 return 0;
429
418b9be5
LP
430 if (sd_id128_equal(arg_machine_id, SD_ID128_NULL))
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");
418b9be5
LP
453 if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
454 return 0;
455
456 print_welcome();
457 putchar('\n');
458
63c372cb
LP
459 msg1 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
460 msg2 = strjoina(draw_special_char(DRAW_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)
503 return errno ? -errno : -EIO;
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");
418b9be5
LP
536 if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
537 return 0;
538
45035609
LP
539 mkdir_parents(etc_shadow, 0755);
540
e929bee0 541 lock = take_etc_passwd_lock(arg_root);
45035609
LP
542 if (lock < 0)
543 return lock;
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
56f64d95 555 log_error_errno(errno, "Failed to find shadow entry for root: %m");
418b9be5
LP
556 return -errno;
557 }
558
559 r = write_root_shadow(etc_shadow, p);
23bbb0de
MS
560 if (r < 0)
561 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
418b9be5
LP
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);
23bbb0de
MS
576 if (r < 0)
577 return log_error_errno(r, "Failed to get salt: %m");
418b9be5
LP
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
56f64d95 593 log_error_errno(errno, "Failed to encrypt password: %m");
418b9be5
LP
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);
23bbb0de
MS
600 if (r < 0)
601 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
418b9be5
LP
602
603 log_info("%s written.", etc_shadow);
604 return 0;
605}
606
601185b4 607static void help(void) {
418b9be5
LP
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"
b57b0625 624 " --prompt Prompt for all of the above\n"
418b9be5
LP
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"
601185b4
ZJS
629 " --setup-machine-id Generate a new random machine ID\n"
630 , program_invocation_short_name);
418b9be5
LP
631}
632
633static 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
601185b4 686 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
418b9be5
LP
687
688 switch (c) {
689
690 case 'h':
601185b4
ZJS
691 help();
692 return 0;
418b9be5
LP
693
694 case ARG_VERSION:
3f6fd1ba 695 return version();
418b9be5
LP
696
697 case ARG_ROOT:
0f03c2a4 698 r = parse_path_argument_and_warn(optarg, true, &arg_root);
0f474365 699 if (r < 0)
0f03c2a4 700 return r;
418b9be5
LP
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
2fc09a9c
DM
709 r = free_and_strdup(&arg_locale, optarg);
710 if (r < 0)
418b9be5
LP
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
2fc09a9c
DM
721 r = free_and_strdup(&arg_locale_messages, optarg);
722 if (r < 0)
418b9be5
LP
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
2fc09a9c
DM
733 r = free_and_strdup(&arg_timezone, optarg);
734 if (r < 0)
418b9be5
LP
735 return log_oom();
736
737 break;
738
739 case ARG_ROOT_PASSWORD:
2fc09a9c
DM
740 r = free_and_strdup(&arg_root_password, optarg);
741 if (r < 0)
418b9be5 742 return log_oom();
418b9be5
LP
743 break;
744
745 case ARG_ROOT_PASSWORD_FILE:
0da16248 746 arg_root_password = mfree(arg_root_password);
418b9be5
LP
747
748 r = read_one_line_file(optarg, &arg_root_password);
23bbb0de
MS
749 if (r < 0)
750 return log_error_errno(r, "Failed to read %s: %m", optarg);
418b9be5
LP
751
752 break;
753
754 case ARG_HOSTNAME:
34ad6090 755 if (!hostname_is_valid(optarg, true)) {
418b9be5
LP
756 log_error("Host name %s is not valid.", optarg);
757 return -EINVAL;
758 }
759
ae691c1d 760 hostname_cleanup(optarg);
2fc09a9c
DM
761 r = free_and_strdup(&arg_hostname, optarg);
762 if (r < 0)
418b9be5
LP
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;
e926f647 797 break;
418b9be5
LP
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);
23bbb0de
MS
814 if (r < 0)
815 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
418b9be5
LP
816
817 break;
818
819 case '?':
820 return -EINVAL;
821
822 default:
823 assert_not_reached("Unhandled option");
824 }
418b9be5
LP
825
826 return 1;
827}
828
829int 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
862finish:
863 free(arg_root);
864 free(arg_locale);
865 free(arg_locale_messages);
866 free(arg_timezone);
867 free(arg_hostname);
1602b008 868 string_erase(arg_root_password);
418b9be5
LP
869 free(arg_root_password);
870
871 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
872}