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