]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/firstboot/firstboot.c
machine: remove unused variables
[thirdparty/systemd.git] / src / firstboot / firstboot.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
418b9be5
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
418b9be5 21#include <fcntl.h>
418b9be5
LP
22#include <getopt.h>
23#include <shadow.h>
3f6fd1ba 24#include <unistd.h>
418b9be5 25
b5efdb8a 26#include "alloc-util.h"
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"
f582cbca 37#include "proc-cmdline.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 */
ed457f13 48static char *arg_keymap = NULL;
418b9be5
LP
49static char *arg_locale_messages = NULL; /* $LC_MESSAGES */
50static char *arg_timezone = NULL;
51static char *arg_hostname = NULL;
52static sd_id128_t arg_machine_id = {};
53static char *arg_root_password = NULL;
54static bool arg_prompt_locale = false;
ed457f13 55static bool arg_prompt_keymap = false;
418b9be5
LP
56static bool arg_prompt_timezone = false;
57static bool arg_prompt_hostname = false;
58static bool arg_prompt_root_password = false;
59static bool arg_copy_locale = false;
ed457f13 60static bool arg_copy_keymap = false;
418b9be5
LP
61static bool arg_copy_timezone = false;
62static bool arg_copy_root_password = false;
63
418b9be5
LP
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
1d13f648 88 os_release = prefix_roota(arg_root, "/etc/os-release");
418b9be5
LP
89 r = parse_env_file(os_release, NEWLINE,
90 "PRETTY_NAME", &pretty_name,
91 NULL);
92 if (r == -ENOENT) {
93
1d13f648 94 os_release = prefix_roota(arg_root, "/usr/lib/os-release");
418b9be5
LP
95 r = parse_env_file(os_release, NEWLINE,
96 "PRETTY_NAME", &pretty_name,
97 NULL);
98 }
99
100 if (r < 0 && r != -ENOENT)
da927ba9 101 log_warning_errno(r, "Failed to read os-release file: %m");
418b9be5
LP
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
323b7dc9 168 r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text);
23bbb0de
MS
169 if (r < 0)
170 return log_error_errno(r, "Failed to query user: %m");
418b9be5
LP
171
172 if (isempty(p)) {
173 log_warning("No data entered, skipping.");
174 return 0;
175 }
176
177 r = safe_atou(p, &u);
178 if (r >= 0) {
179 char *c;
180
181 if (u <= 0 || u > strv_length(l)) {
182 log_error("Specified entry number out of range.");
183 continue;
184 }
185
186 log_info("Selected '%s'.", l[u-1]);
187
188 c = strdup(l[u-1]);
189 if (!c)
190 return log_oom();
191
192 free(*ret);
193 *ret = c;
194 return 0;
195 }
196
197 if (!is_valid(p)) {
198 log_error("Entered data invalid.");
199 continue;
200 }
201
202 free(*ret);
203 *ret = p;
204 p = 0;
205 return 0;
206 }
207}
208
209static int prompt_locale(void) {
210 _cleanup_strv_free_ char **locales = NULL;
211 int r;
212
213 if (arg_locale || arg_locale_messages)
214 return 0;
215
216 if (!arg_prompt_locale)
217 return 0;
218
219 r = get_locales(&locales);
23bbb0de
MS
220 if (r < 0)
221 return log_error_errno(r, "Cannot query locales list: %m");
418b9be5
LP
222
223 print_welcome();
224
225 printf("\nAvailable Locales:\n\n");
226 r = show_menu(locales, 3, 22, 60);
227 if (r < 0)
228 return r;
229
230 putchar('\n');
231
232 r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale);
233 if (r < 0)
234 return r;
235
236 if (isempty(arg_locale))
237 return 0;
238
239 r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages);
240 if (r < 0)
241 return r;
242
243 return 0;
244}
245
246static int process_locale(void) {
247 const char *etc_localeconf;
248 char* locales[3];
249 unsigned i = 0;
250 int r;
251
1d13f648 252 etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
b3cd687d 253 if (laccess(etc_localeconf, F_OK) >= 0)
418b9be5
LP
254 return 0;
255
256 if (arg_copy_locale && arg_root) {
257
258 mkdir_parents(etc_localeconf, 0755);
1c876927 259 r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, COPY_REFLINK);
418b9be5 260 if (r != -ENOENT) {
23bbb0de
MS
261 if (r < 0)
262 return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf);
418b9be5
LP
263
264 log_info("%s copied.", etc_localeconf);
265 return 0;
266 }
267 }
268
269 r = prompt_locale();
270 if (r < 0)
271 return r;
272
273 if (!isempty(arg_locale))
63c372cb 274 locales[i++] = strjoina("LANG=", arg_locale);
418b9be5 275 if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale))
63c372cb 276 locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages);
418b9be5
LP
277
278 if (i == 0)
279 return 0;
280
281 locales[i] = NULL;
282
283 mkdir_parents(etc_localeconf, 0755);
284 r = write_env_file(etc_localeconf, locales);
23bbb0de
MS
285 if (r < 0)
286 return log_error_errno(r, "Failed to write %s: %m", etc_localeconf);
418b9be5
LP
287
288 log_info("%s written.", etc_localeconf);
289 return 0;
290}
291
ed457f13
TB
292static int prompt_keymap(void) {
293 _cleanup_strv_free_ char **kmaps = NULL;
294 int r;
295
296 if (arg_keymap)
297 return 0;
298
299 if (!arg_prompt_keymap)
300 return 0;
301
302 r = get_keymaps(&kmaps);
303 if (r == -ENOENT) /* no keymaps installed */
304 return r;
305 if (r < 0)
306 return log_error_errno(r, "Failed to read keymaps: %m");
307
308 print_welcome();
309
310 printf("\nAvailable keymaps:\n\n");
311 r = show_menu(kmaps, 3, 22, 60);
312 if (r < 0)
313 return r;
314
315 putchar('\n');
316
8700b4da
ZJS
317 return prompt_loop("Please enter system keymap name or number",
318 kmaps, keymap_is_valid, &arg_keymap);
ed457f13
TB
319}
320
321static int process_keymap(void) {
322 const char *etc_vconsoleconf;
323 char **keymap;
324 int r;
325
326 etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf");
327 if (laccess(etc_vconsoleconf, F_OK) >= 0)
328 return 0;
329
330 if (arg_copy_keymap && arg_root) {
331
332 mkdir_parents(etc_vconsoleconf, 0755);
333 r = copy_file("/etc/vconsole.conf", etc_vconsoleconf, 0, 0644, 0, COPY_REFLINK);
334 if (r != -ENOENT) {
335 if (r < 0)
336 return log_error_errno(r, "Failed to copy %s: %m", etc_vconsoleconf);
337
338 log_info("%s copied.", etc_vconsoleconf);
339 return 0;
340 }
341 }
342
343 r = prompt_keymap();
344 if (r == -ENOENT)
345 return 0; /* don't fail if no keymaps are installed */
346 if (r < 0)
347 return r;
348
349 if (!isempty(arg_keymap))
350 keymap = STRV_MAKE(strjoina("KEYMAP=", arg_keymap));
351
352 if (!keymap)
353 return 0;
354
355 mkdir_parents(etc_vconsoleconf, 0755);
356 r = write_env_file(etc_vconsoleconf, keymap);
357 if (r < 0)
358 return log_error_errno(r, "Failed to write %s: %m", etc_vconsoleconf);
359
360 log_info("%s written.", etc_vconsoleconf);
361 return 0;
362}
363
418b9be5
LP
364static int prompt_timezone(void) {
365 _cleanup_strv_free_ char **zones = NULL;
366 int r;
367
368 if (arg_timezone)
369 return 0;
370
371 if (!arg_prompt_timezone)
372 return 0;
373
374 r = get_timezones(&zones);
23bbb0de
MS
375 if (r < 0)
376 return log_error_errno(r, "Cannot query timezone list: %m");
418b9be5
LP
377
378 print_welcome();
379
380 printf("\nAvailable Time Zones:\n\n");
381 r = show_menu(zones, 3, 22, 30);
382 if (r < 0)
383 return r;
384
385 putchar('\n');
386
387 r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone);
388 if (r < 0)
389 return r;
390
391 return 0;
392}
393
394static int process_timezone(void) {
395 const char *etc_localtime, *e;
396 int r;
397
1d13f648 398 etc_localtime = prefix_roota(arg_root, "/etc/localtime");
b3cd687d 399 if (laccess(etc_localtime, F_OK) >= 0)
418b9be5
LP
400 return 0;
401
402 if (arg_copy_timezone && arg_root) {
403 _cleanup_free_ char *p = NULL;
404
405 r = readlink_malloc("/etc/localtime", &p);
406 if (r != -ENOENT) {
23bbb0de
MS
407 if (r < 0)
408 return log_error_errno(r, "Failed to read host timezone: %m");
418b9be5
LP
409
410 mkdir_parents(etc_localtime, 0755);
4a62c710
MS
411 if (symlink(p, etc_localtime) < 0)
412 return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
418b9be5
LP
413
414 log_info("%s copied.", etc_localtime);
415 return 0;
416 }
417 }
418
419 r = prompt_timezone();
420 if (r < 0)
421 return r;
422
423 if (isempty(arg_timezone))
424 return 0;
425
63c372cb 426 e = strjoina("../usr/share/zoneinfo/", arg_timezone);
418b9be5
LP
427
428 mkdir_parents(etc_localtime, 0755);
4a62c710
MS
429 if (symlink(e, etc_localtime) < 0)
430 return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
418b9be5
LP
431
432 log_info("%s written", etc_localtime);
433 return 0;
434}
435
436static int prompt_hostname(void) {
437 int r;
438
439 if (arg_hostname)
440 return 0;
441
442 if (!arg_prompt_hostname)
443 return 0;
444
445 print_welcome();
446 putchar('\n');
447
448 for (;;) {
449 _cleanup_free_ char *h = NULL;
450
323b7dc9 451 r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET));
23bbb0de
MS
452 if (r < 0)
453 return log_error_errno(r, "Failed to query hostname: %m");
418b9be5
LP
454
455 if (isempty(h)) {
456 log_warning("No hostname entered, skipping.");
457 break;
458 }
459
34ad6090 460 if (!hostname_is_valid(h, true)) {
418b9be5
LP
461 log_error("Specified hostname invalid.");
462 continue;
463 }
464
34ad6090 465 /* Get rid of the trailing dot that we allow, but don't want to see */
ae691c1d 466 arg_hostname = hostname_cleanup(h);
418b9be5
LP
467 h = NULL;
468 break;
469 }
470
471 return 0;
472}
473
474static int process_hostname(void) {
475 const char *etc_hostname;
476 int r;
477
1d13f648 478 etc_hostname = prefix_roota(arg_root, "/etc/hostname");
b3cd687d 479 if (laccess(etc_hostname, F_OK) >= 0)
418b9be5
LP
480 return 0;
481
482 r = prompt_hostname();
483 if (r < 0)
484 return r;
485
486 if (isempty(arg_hostname))
487 return 0;
488
489 mkdir_parents(etc_hostname, 0755);
0675e94a
AJ
490 r = write_string_file(etc_hostname, arg_hostname,
491 WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC);
23bbb0de
MS
492 if (r < 0)
493 return log_error_errno(r, "Failed to write %s: %m", etc_hostname);
418b9be5
LP
494
495 log_info("%s written.", etc_hostname);
496 return 0;
497}
498
499static int process_machine_id(void) {
500 const char *etc_machine_id;
501 char id[SD_ID128_STRING_MAX];
502 int r;
503
1d13f648 504 etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
b3cd687d 505 if (laccess(etc_machine_id, F_OK) >= 0)
418b9be5
LP
506 return 0;
507
3bbaff3e 508 if (sd_id128_is_null(arg_machine_id))
418b9be5
LP
509 return 0;
510
511 mkdir_parents(etc_machine_id, 0755);
0675e94a
AJ
512 r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id),
513 WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC);
23bbb0de
MS
514 if (r < 0)
515 return log_error_errno(r, "Failed to write machine id: %m");
418b9be5
LP
516
517 log_info("%s written.", etc_machine_id);
518 return 0;
519}
520
521static int prompt_root_password(void) {
522 const char *msg1, *msg2, *etc_shadow;
523 int r;
524
525 if (arg_root_password)
526 return 0;
527
528 if (!arg_prompt_root_password)
529 return 0;
530
1d13f648 531 etc_shadow = prefix_roota(arg_root, "/etc/shadow");
b3cd687d 532 if (laccess(etc_shadow, F_OK) >= 0)
418b9be5
LP
533 return 0;
534
535 print_welcome();
536 putchar('\n');
537
323b7dc9
ZJS
538 msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
539 msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: ");
418b9be5
LP
540
541 for (;;) {
ab84f5b9 542 _cleanup_string_free_erase_ char *a = NULL, *b = NULL;
418b9be5 543
e287086b 544 r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a);
23bbb0de
MS
545 if (r < 0)
546 return log_error_errno(r, "Failed to query root password: %m");
418b9be5
LP
547
548 if (isempty(a)) {
549 log_warning("No password entered, skipping.");
550 break;
551 }
552
e287086b 553 r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b);
ab84f5b9 554 if (r < 0)
00843602 555 return log_error_errno(r, "Failed to query root password: %m");
418b9be5
LP
556
557 if (!streq(a, b)) {
558 log_error("Entered passwords did not match, please try again.");
418b9be5
LP
559 continue;
560 }
561
418b9be5
LP
562 arg_root_password = a;
563 a = NULL;
564 break;
565 }
566
567 return 0;
568}
569
570static int write_root_shadow(const char *path, const struct spwd *p) {
571 _cleanup_fclose_ FILE *f = NULL;
572 assert(path);
573 assert(p);
574
3250929b
LP
575 RUN_WITH_UMASK(0777)
576 f = fopen(path, "wex");
418b9be5
LP
577 if (!f)
578 return -errno;
579
580 errno = 0;
581 if (putspent(p, f) != 0)
f5e5c28f 582 return errno > 0 ? -errno : -EIO;
418b9be5 583
0675e94a 584 return fflush_sync_and_check(f);
418b9be5
LP
585}
586
587static int process_root_password(void) {
588
589 static const char table[] =
590 "abcdefghijklmnopqrstuvwxyz"
591 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
592 "0123456789"
593 "./";
594
595 struct spwd item = {
596 .sp_namp = (char*) "root",
ad525df8
IS
597 .sp_min = -1,
598 .sp_max = -1,
599 .sp_warn = -1,
418b9be5
LP
600 .sp_inact = -1,
601 .sp_expire = -1,
602 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
603 };
45035609
LP
604
605 _cleanup_close_ int lock = -1;
418b9be5
LP
606 char salt[3+16+1+1];
607 uint8_t raw[16];
608 unsigned i;
609 char *j;
610
611 const char *etc_shadow;
612 int r;
613
1d13f648 614 etc_shadow = prefix_roota(arg_root, "/etc/shadow");
b3cd687d 615 if (laccess(etc_shadow, F_OK) >= 0)
418b9be5
LP
616 return 0;
617
45035609
LP
618 mkdir_parents(etc_shadow, 0755);
619
e929bee0 620 lock = take_etc_passwd_lock(arg_root);
45035609 621 if (lock < 0)
db260eed 622 return log_error_errno(lock, "Failed to take a lock: %m");
45035609 623
418b9be5
LP
624 if (arg_copy_root_password && arg_root) {
625 struct spwd *p;
626
627 errno = 0;
628 p = getspnam("root");
629 if (p || errno != ENOENT) {
630 if (!p) {
631 if (!errno)
632 errno = EIO;
633
e1427b13 634 return log_error_errno(errno, "Failed to find shadow entry for root: %m");
418b9be5
LP
635 }
636
637 r = write_root_shadow(etc_shadow, p);
23bbb0de
MS
638 if (r < 0)
639 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
418b9be5
LP
640
641 log_info("%s copied.", etc_shadow);
642 return 0;
643 }
644 }
645
646 r = prompt_root_password();
647 if (r < 0)
648 return r;
649
650 if (!arg_root_password)
651 return 0;
652
f0d09059 653 r = acquire_random_bytes(raw, 16, true);
23bbb0de
MS
654 if (r < 0)
655 return log_error_errno(r, "Failed to get salt: %m");
418b9be5
LP
656
657 /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
658 assert_cc(sizeof(table) == 64 + 1);
659 j = stpcpy(salt, "$6$");
660 for (i = 0; i < 16; i++)
661 j[i] = table[raw[i] & 63];
662 j[i++] = '$';
663 j[i] = 0;
664
665 errno = 0;
666 item.sp_pwdp = crypt(arg_root_password, salt);
667 if (!item.sp_pwdp) {
668 if (!errno)
4546c341 669 errno = EINVAL;
418b9be5 670
e1427b13 671 return log_error_errno(errno, "Failed to encrypt password: %m");
418b9be5
LP
672 }
673
674 item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
675
676 r = write_root_shadow(etc_shadow, &item);
23bbb0de
MS
677 if (r < 0)
678 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
418b9be5
LP
679
680 log_info("%s written.", etc_shadow);
681 return 0;
682}
683
601185b4 684static void help(void) {
418b9be5
LP
685 printf("%s [OPTIONS...]\n\n"
686 "Configures basic settings of the system.\n\n"
687 " -h --help Show this help\n"
688 " --version Show package version\n"
689 " --root=PATH Operate on an alternate filesystem root\n"
690 " --locale=LOCALE Set primary locale (LANG=)\n"
691 " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n"
ed457f13 692 " --keymap=KEYMAP Set keymap\n"
418b9be5
LP
693 " --timezone=TIMEZONE Set timezone\n"
694 " --hostname=NAME Set host name\n"
695 " --machine-ID=ID Set machine ID\n"
696 " --root-password=PASSWORD Set root password\n"
697 " --root-password-file=FILE Set root password from file\n"
698 " --prompt-locale Prompt the user for locale settings\n"
ed457f13 699 " --prompt-keymap Prompt the user for keymap settings\n"
418b9be5
LP
700 " --prompt-timezone Prompt the user for timezone\n"
701 " --prompt-hostname Prompt the user for hostname\n"
702 " --prompt-root-password Prompt the user for root password\n"
b57b0625 703 " --prompt Prompt for all of the above\n"
418b9be5 704 " --copy-locale Copy locale from host\n"
ed457f13 705 " --copy-keymap Copy keymap from host\n"
418b9be5
LP
706 " --copy-timezone Copy timezone from host\n"
707 " --copy-root-password Copy root password from host\n"
ed457f13 708 " --copy Copy locale, keymap, timezone, root password\n"
601185b4
ZJS
709 " --setup-machine-id Generate a new random machine ID\n"
710 , program_invocation_short_name);
418b9be5
LP
711}
712
713static int parse_argv(int argc, char *argv[]) {
714
715 enum {
716 ARG_VERSION = 0x100,
717 ARG_ROOT,
718 ARG_LOCALE,
719 ARG_LOCALE_MESSAGES,
ed457f13 720 ARG_KEYMAP,
418b9be5
LP
721 ARG_TIMEZONE,
722 ARG_HOSTNAME,
723 ARG_MACHINE_ID,
724 ARG_ROOT_PASSWORD,
725 ARG_ROOT_PASSWORD_FILE,
726 ARG_PROMPT,
727 ARG_PROMPT_LOCALE,
ed457f13 728 ARG_PROMPT_KEYMAP,
418b9be5
LP
729 ARG_PROMPT_TIMEZONE,
730 ARG_PROMPT_HOSTNAME,
731 ARG_PROMPT_ROOT_PASSWORD,
732 ARG_COPY,
733 ARG_COPY_LOCALE,
ed457f13 734 ARG_COPY_KEYMAP,
418b9be5
LP
735 ARG_COPY_TIMEZONE,
736 ARG_COPY_ROOT_PASSWORD,
737 ARG_SETUP_MACHINE_ID,
738 };
739
740 static const struct option options[] = {
741 { "help", no_argument, NULL, 'h' },
742 { "version", no_argument, NULL, ARG_VERSION },
743 { "root", required_argument, NULL, ARG_ROOT },
744 { "locale", required_argument, NULL, ARG_LOCALE },
745 { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES },
ed457f13 746 { "keymap", required_argument, NULL, ARG_KEYMAP },
418b9be5
LP
747 { "timezone", required_argument, NULL, ARG_TIMEZONE },
748 { "hostname", required_argument, NULL, ARG_HOSTNAME },
749 { "machine-id", required_argument, NULL, ARG_MACHINE_ID },
750 { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD },
751 { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE },
752 { "prompt", no_argument, NULL, ARG_PROMPT },
753 { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE },
ed457f13 754 { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP },
418b9be5
LP
755 { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE },
756 { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME },
757 { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD },
758 { "copy", no_argument, NULL, ARG_COPY },
759 { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE },
ed457f13 760 { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP },
418b9be5
LP
761 { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE },
762 { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD },
763 { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID },
764 {}
765 };
766
767 int r, c;
768
769 assert(argc >= 0);
770 assert(argv);
771
601185b4 772 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
418b9be5
LP
773
774 switch (c) {
775
776 case 'h':
601185b4
ZJS
777 help();
778 return 0;
418b9be5
LP
779
780 case ARG_VERSION:
3f6fd1ba 781 return version();
418b9be5
LP
782
783 case ARG_ROOT:
0f03c2a4 784 r = parse_path_argument_and_warn(optarg, true, &arg_root);
0f474365 785 if (r < 0)
0f03c2a4 786 return r;
418b9be5
LP
787 break;
788
789 case ARG_LOCALE:
790 if (!locale_is_valid(optarg)) {
791 log_error("Locale %s is not valid.", optarg);
792 return -EINVAL;
793 }
794
2fc09a9c
DM
795 r = free_and_strdup(&arg_locale, optarg);
796 if (r < 0)
418b9be5
LP
797 return log_oom();
798
799 break;
800
801 case ARG_LOCALE_MESSAGES:
802 if (!locale_is_valid(optarg)) {
803 log_error("Locale %s is not valid.", optarg);
804 return -EINVAL;
805 }
806
2fc09a9c
DM
807 r = free_and_strdup(&arg_locale_messages, optarg);
808 if (r < 0)
418b9be5
LP
809 return log_oom();
810
811 break;
812
ed457f13
TB
813 case ARG_KEYMAP:
814 if (!keymap_is_valid(optarg)) {
815 log_error("Keymap %s is not valid.", optarg);
816 return -EINVAL;
817 }
818
819 r = free_and_strdup(&arg_keymap, optarg);
820 if (r < 0)
821 return log_oom();
822
823 break;
824
418b9be5
LP
825 case ARG_TIMEZONE:
826 if (!timezone_is_valid(optarg)) {
827 log_error("Timezone %s is not valid.", optarg);
828 return -EINVAL;
829 }
830
2fc09a9c
DM
831 r = free_and_strdup(&arg_timezone, optarg);
832 if (r < 0)
418b9be5
LP
833 return log_oom();
834
835 break;
836
837 case ARG_ROOT_PASSWORD:
2fc09a9c
DM
838 r = free_and_strdup(&arg_root_password, optarg);
839 if (r < 0)
418b9be5 840 return log_oom();
418b9be5
LP
841 break;
842
843 case ARG_ROOT_PASSWORD_FILE:
0da16248 844 arg_root_password = mfree(arg_root_password);
418b9be5
LP
845
846 r = read_one_line_file(optarg, &arg_root_password);
23bbb0de
MS
847 if (r < 0)
848 return log_error_errno(r, "Failed to read %s: %m", optarg);
418b9be5
LP
849
850 break;
851
852 case ARG_HOSTNAME:
34ad6090 853 if (!hostname_is_valid(optarg, true)) {
418b9be5
LP
854 log_error("Host name %s is not valid.", optarg);
855 return -EINVAL;
856 }
857
ae691c1d 858 hostname_cleanup(optarg);
2fc09a9c
DM
859 r = free_and_strdup(&arg_hostname, optarg);
860 if (r < 0)
418b9be5
LP
861 return log_oom();
862
863 break;
864
865 case ARG_MACHINE_ID:
866 if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
867 log_error("Failed to parse machine id %s.", optarg);
868 return -EINVAL;
869 }
870
871 break;
872
873 case ARG_PROMPT:
ed457f13 874 arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
418b9be5
LP
875 break;
876
877 case ARG_PROMPT_LOCALE:
878 arg_prompt_locale = true;
879 break;
880
ed457f13
TB
881 case ARG_PROMPT_KEYMAP:
882 arg_prompt_keymap = true;
883 break;
884
418b9be5
LP
885 case ARG_PROMPT_TIMEZONE:
886 arg_prompt_timezone = true;
887 break;
888
889 case ARG_PROMPT_HOSTNAME:
890 arg_prompt_hostname = true;
891 break;
892
893 case ARG_PROMPT_ROOT_PASSWORD:
894 arg_prompt_root_password = true;
895 break;
896
897 case ARG_COPY:
ed457f13 898 arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password = true;
e926f647 899 break;
418b9be5
LP
900
901 case ARG_COPY_LOCALE:
902 arg_copy_locale = true;
903 break;
904
ed457f13
TB
905 case ARG_COPY_KEYMAP:
906 arg_copy_keymap = true;
907 break;
908
418b9be5
LP
909 case ARG_COPY_TIMEZONE:
910 arg_copy_timezone = true;
911 break;
912
913 case ARG_COPY_ROOT_PASSWORD:
914 arg_copy_root_password = true;
915 break;
916
917 case ARG_SETUP_MACHINE_ID:
918
919 r = sd_id128_randomize(&arg_machine_id);
23bbb0de
MS
920 if (r < 0)
921 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
418b9be5
LP
922
923 break;
924
925 case '?':
926 return -EINVAL;
927
928 default:
929 assert_not_reached("Unhandled option");
930 }
418b9be5
LP
931
932 return 1;
933}
934
935int main(int argc, char *argv[]) {
1d84ad94 936 bool enabled;
418b9be5
LP
937 int r;
938
939 r = parse_argv(argc, argv);
940 if (r <= 0)
941 goto finish;
942
943 log_set_target(LOG_TARGET_AUTO);
944 log_parse_environment();
945 log_open();
946
947 umask(0022);
948
1d84ad94
LP
949 r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
950 if (r < 0) {
951 log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring.");
952 goto finish;
953 }
954 if (r > 0 && !enabled) {
955 r = 0; /* disabled */
956 goto finish;
f582cbca
LP
957 }
958
418b9be5
LP
959 r = process_locale();
960 if (r < 0)
961 goto finish;
962
ed457f13
TB
963 r = process_keymap();
964 if (r < 0)
965 goto finish;
966
418b9be5
LP
967 r = process_timezone();
968 if (r < 0)
969 goto finish;
970
971 r = process_hostname();
972 if (r < 0)
973 goto finish;
974
975 r = process_machine_id();
976 if (r < 0)
977 goto finish;
978
979 r = process_root_password();
980 if (r < 0)
981 goto finish;
982
983finish:
984 free(arg_root);
985 free(arg_locale);
986 free(arg_locale_messages);
ed457f13 987 free(arg_keymap);
418b9be5
LP
988 free(arg_timezone);
989 free(arg_hostname);
1602b008 990 string_erase(arg_root_password);
418b9be5
LP
991 free(arg_root_password);
992
993 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
994}