1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
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.
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.
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/>.
26 #include "alloc-util.h"
27 #include "ask-password-api.h"
32 #include "hostname-util.h"
33 #include "locale-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "proc-cmdline.h"
38 #include "random-util.h"
39 #include "string-util.h"
41 #include "terminal-util.h"
42 #include "time-util.h"
43 #include "umask-util.h"
44 #include "user-util.h"
46 static char *arg_root
= NULL
;
47 static char *arg_locale
= NULL
; /* $LANG */
48 static char *arg_keymap
= NULL
;
49 static char *arg_locale_messages
= NULL
; /* $LC_MESSAGES */
50 static char *arg_timezone
= NULL
;
51 static char *arg_hostname
= NULL
;
52 static sd_id128_t arg_machine_id
= {};
53 static char *arg_root_password
= NULL
;
54 static bool arg_prompt_locale
= false;
55 static bool arg_prompt_keymap
= false;
56 static bool arg_prompt_timezone
= false;
57 static bool arg_prompt_hostname
= false;
58 static bool arg_prompt_root_password
= false;
59 static bool arg_copy_locale
= false;
60 static bool arg_copy_keymap
= false;
61 static bool arg_copy_timezone
= false;
62 static bool arg_copy_root_password
= false;
64 static bool press_any_key(void) {
68 printf("-- Press any key to proceed --");
71 (void) read_one_char(stdin
, &k
, USEC_INFINITY
, &need_nl
);
79 static void print_welcome(void) {
80 _cleanup_free_
char *pretty_name
= NULL
;
81 const char *os_release
= NULL
;
82 static bool done
= false;
88 os_release
= prefix_roota(arg_root
, "/etc/os-release");
89 r
= parse_env_file(os_release
, NEWLINE
,
90 "PRETTY_NAME", &pretty_name
,
94 os_release
= prefix_roota(arg_root
, "/usr/lib/os-release");
95 r
= parse_env_file(os_release
, NEWLINE
,
96 "PRETTY_NAME", &pretty_name
,
100 if (r
< 0 && r
!= -ENOENT
)
101 log_warning_errno(r
, "Failed to read os-release file: %m");
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
);
111 static 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
;
115 assert(n_columns
> 0);
118 per_column
= (n
+ n_columns
- 1) / n_columns
;
120 break_lines
= lines();
124 /* The first page gets two extra lines, since we want to show
126 break_modulo
= break_lines
;
127 if (break_modulo
> 3)
130 for (i
= 0; i
< per_column
; i
++) {
132 for (j
= 0; j
< n_columns
; j
++) {
133 _cleanup_free_
char *e
= NULL
;
135 if (j
* per_column
+ i
>= n
)
138 e
= ellipsize(x
[j
* per_column
+ i
], width
, percentage
);
142 printf("%4u) %-*s", j
* per_column
+ i
+ 1, width
, e
);
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())
157 static int prompt_loop(const char *text
, char **l
, bool (*is_valid
)(const char *name
), char **ret
) {
165 _cleanup_free_
char *p
= NULL
;
168 r
= ask_string(&p
, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET
), text
);
170 return log_error_errno(r
, "Failed to query user: %m");
173 log_warning("No data entered, skipping.");
177 r
= safe_atou(p
, &u
);
181 if (u
<= 0 || u
> strv_length(l
)) {
182 log_error("Specified entry number out of range.");
186 log_info("Selected '%s'.", l
[u
-1]);
198 log_error("Entered data invalid.");
209 static int prompt_locale(void) {
210 _cleanup_strv_free_
char **locales
= NULL
;
213 if (arg_locale
|| arg_locale_messages
)
216 if (!arg_prompt_locale
)
219 r
= get_locales(&locales
);
221 return log_error_errno(r
, "Cannot query locales list: %m");
225 printf("\nAvailable Locales:\n\n");
226 r
= show_menu(locales
, 3, 22, 60);
232 r
= prompt_loop("Please enter system locale name or number", locales
, locale_is_valid
, &arg_locale
);
236 if (isempty(arg_locale
))
239 r
= prompt_loop("Please enter system message locale name or number", locales
, locale_is_valid
, &arg_locale_messages
);
246 static int process_locale(void) {
247 const char *etc_localeconf
;
252 etc_localeconf
= prefix_roota(arg_root
, "/etc/locale.conf");
253 if (laccess(etc_localeconf
, F_OK
) >= 0)
256 if (arg_copy_locale
&& arg_root
) {
258 mkdir_parents(etc_localeconf
, 0755);
259 r
= copy_file("/etc/locale.conf", etc_localeconf
, 0, 0644, 0, COPY_REFLINK
);
262 return log_error_errno(r
, "Failed to copy %s: %m", etc_localeconf
);
264 log_info("%s copied.", etc_localeconf
);
273 if (!isempty(arg_locale
))
274 locales
[i
++] = strjoina("LANG=", arg_locale
);
275 if (!isempty(arg_locale_messages
) && !streq(arg_locale_messages
, arg_locale
))
276 locales
[i
++] = strjoina("LC_MESSAGES=", arg_locale_messages
);
283 mkdir_parents(etc_localeconf
, 0755);
284 r
= write_env_file(etc_localeconf
, locales
);
286 return log_error_errno(r
, "Failed to write %s: %m", etc_localeconf
);
288 log_info("%s written.", etc_localeconf
);
292 static int prompt_keymap(void) {
293 _cleanup_strv_free_
char **kmaps
= NULL
;
299 if (!arg_prompt_keymap
)
302 r
= get_keymaps(&kmaps
);
303 if (r
== -ENOENT
) /* no keymaps installed */
306 return log_error_errno(r
, "Failed to read keymaps: %m");
310 printf("\nAvailable keymaps:\n\n");
311 r
= show_menu(kmaps
, 3, 22, 60);
317 return prompt_loop("Please enter system keymap name or number",
318 kmaps
, keymap_is_valid
, &arg_keymap
);
321 static int process_keymap(void) {
322 const char *etc_vconsoleconf
;
326 etc_vconsoleconf
= prefix_roota(arg_root
, "/etc/vconsole.conf");
327 if (laccess(etc_vconsoleconf
, F_OK
) >= 0)
330 if (arg_copy_keymap
&& arg_root
) {
332 mkdir_parents(etc_vconsoleconf
, 0755);
333 r
= copy_file("/etc/vconsole.conf", etc_vconsoleconf
, 0, 0644, 0, COPY_REFLINK
);
336 return log_error_errno(r
, "Failed to copy %s: %m", etc_vconsoleconf
);
338 log_info("%s copied.", etc_vconsoleconf
);
345 return 0; /* don't fail if no keymaps are installed */
349 if (isempty(arg_keymap
))
352 keymap
= STRV_MAKE(strjoina("KEYMAP=", arg_keymap
));
354 r
= mkdir_parents(etc_vconsoleconf
, 0755);
356 return log_error_errno(r
, "Failed to create the parent directory of %s: %m", etc_vconsoleconf
);
358 r
= write_env_file(etc_vconsoleconf
, keymap
);
360 return log_error_errno(r
, "Failed to write %s: %m", etc_vconsoleconf
);
362 log_info("%s written.", etc_vconsoleconf
);
366 static int prompt_timezone(void) {
367 _cleanup_strv_free_
char **zones
= NULL
;
373 if (!arg_prompt_timezone
)
376 r
= get_timezones(&zones
);
378 return log_error_errno(r
, "Cannot query timezone list: %m");
382 printf("\nAvailable Time Zones:\n\n");
383 r
= show_menu(zones
, 3, 22, 30);
389 r
= prompt_loop("Please enter timezone name or number", zones
, timezone_is_valid
, &arg_timezone
);
396 static int process_timezone(void) {
397 const char *etc_localtime
, *e
;
400 etc_localtime
= prefix_roota(arg_root
, "/etc/localtime");
401 if (laccess(etc_localtime
, F_OK
) >= 0)
404 if (arg_copy_timezone
&& arg_root
) {
405 _cleanup_free_
char *p
= NULL
;
407 r
= readlink_malloc("/etc/localtime", &p
);
410 return log_error_errno(r
, "Failed to read host timezone: %m");
412 mkdir_parents(etc_localtime
, 0755);
413 if (symlink(p
, etc_localtime
) < 0)
414 return log_error_errno(errno
, "Failed to create %s symlink: %m", etc_localtime
);
416 log_info("%s copied.", etc_localtime
);
421 r
= prompt_timezone();
425 if (isempty(arg_timezone
))
428 e
= strjoina("../usr/share/zoneinfo/", arg_timezone
);
430 mkdir_parents(etc_localtime
, 0755);
431 if (symlink(e
, etc_localtime
) < 0)
432 return log_error_errno(errno
, "Failed to create %s symlink: %m", etc_localtime
);
434 log_info("%s written", etc_localtime
);
438 static int prompt_hostname(void) {
444 if (!arg_prompt_hostname
)
451 _cleanup_free_
char *h
= NULL
;
453 r
= ask_string(&h
, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET
));
455 return log_error_errno(r
, "Failed to query hostname: %m");
458 log_warning("No hostname entered, skipping.");
462 if (!hostname_is_valid(h
, true)) {
463 log_error("Specified hostname invalid.");
467 /* Get rid of the trailing dot that we allow, but don't want to see */
468 arg_hostname
= hostname_cleanup(h
);
476 static int process_hostname(void) {
477 const char *etc_hostname
;
480 etc_hostname
= prefix_roota(arg_root
, "/etc/hostname");
481 if (laccess(etc_hostname
, F_OK
) >= 0)
484 r
= prompt_hostname();
488 if (isempty(arg_hostname
))
491 mkdir_parents(etc_hostname
, 0755);
492 r
= write_string_file(etc_hostname
, arg_hostname
,
493 WRITE_STRING_FILE_CREATE
| WRITE_STRING_FILE_SYNC
);
495 return log_error_errno(r
, "Failed to write %s: %m", etc_hostname
);
497 log_info("%s written.", etc_hostname
);
501 static int process_machine_id(void) {
502 const char *etc_machine_id
;
503 char id
[SD_ID128_STRING_MAX
];
506 etc_machine_id
= prefix_roota(arg_root
, "/etc/machine-id");
507 if (laccess(etc_machine_id
, F_OK
) >= 0)
510 if (sd_id128_is_null(arg_machine_id
))
513 mkdir_parents(etc_machine_id
, 0755);
514 r
= write_string_file(etc_machine_id
, sd_id128_to_string(arg_machine_id
, id
),
515 WRITE_STRING_FILE_CREATE
| WRITE_STRING_FILE_SYNC
);
517 return log_error_errno(r
, "Failed to write machine id: %m");
519 log_info("%s written.", etc_machine_id
);
523 static int prompt_root_password(void) {
524 const char *msg1
, *msg2
, *etc_shadow
;
527 if (arg_root_password
)
530 if (!arg_prompt_root_password
)
533 etc_shadow
= prefix_roota(arg_root
, "/etc/shadow");
534 if (laccess(etc_shadow
, F_OK
) >= 0)
540 msg1
= strjoina(special_glyph(TRIANGULAR_BULLET
), " Please enter a new root password (empty to skip): ");
541 msg2
= strjoina(special_glyph(TRIANGULAR_BULLET
), " Please enter new root password again: ");
544 _cleanup_string_free_erase_
char *a
= NULL
, *b
= NULL
;
546 r
= ask_password_tty(msg1
, NULL
, 0, 0, NULL
, &a
);
548 return log_error_errno(r
, "Failed to query root password: %m");
551 log_warning("No password entered, skipping.");
555 r
= ask_password_tty(msg2
, NULL
, 0, 0, NULL
, &b
);
557 return log_error_errno(r
, "Failed to query root password: %m");
560 log_error("Entered passwords did not match, please try again.");
564 arg_root_password
= a
;
572 static int write_root_shadow(const char *path
, const struct spwd
*p
) {
573 _cleanup_fclose_
FILE *f
= NULL
;
578 f
= fopen(path
, "wex");
583 if (putspent(p
, f
) != 0)
584 return errno
> 0 ? -errno
: -EIO
;
586 return fflush_sync_and_check(f
);
589 static int process_root_password(void) {
591 static const char table
[] =
592 "abcdefghijklmnopqrstuvwxyz"
593 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
598 .sp_namp
= (char*) "root",
604 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
607 _cleanup_close_
int lock
= -1;
613 const char *etc_shadow
;
616 etc_shadow
= prefix_roota(arg_root
, "/etc/shadow");
617 if (laccess(etc_shadow
, F_OK
) >= 0)
620 mkdir_parents(etc_shadow
, 0755);
622 lock
= take_etc_passwd_lock(arg_root
);
624 return log_error_errno(lock
, "Failed to take a lock: %m");
626 if (arg_copy_root_password
&& arg_root
) {
630 p
= getspnam("root");
631 if (p
|| errno
!= ENOENT
) {
636 return log_error_errno(errno
, "Failed to find shadow entry for root: %m");
639 r
= write_root_shadow(etc_shadow
, p
);
641 return log_error_errno(r
, "Failed to write %s: %m", etc_shadow
);
643 log_info("%s copied.", etc_shadow
);
648 r
= prompt_root_password();
652 if (!arg_root_password
)
655 r
= acquire_random_bytes(raw
, 16, true);
657 return log_error_errno(r
, "Failed to get salt: %m");
659 /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
660 assert_cc(sizeof(table
) == 64 + 1);
661 j
= stpcpy(salt
, "$6$");
662 for (i
= 0; i
< 16; i
++)
663 j
[i
] = table
[raw
[i
] & 63];
668 item
.sp_pwdp
= crypt(arg_root_password
, salt
);
673 return log_error_errno(errno
, "Failed to encrypt password: %m");
676 item
.sp_lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
678 r
= write_root_shadow(etc_shadow
, &item
);
680 return log_error_errno(r
, "Failed to write %s: %m", etc_shadow
);
682 log_info("%s written.", etc_shadow
);
686 static void help(void) {
687 printf("%s [OPTIONS...]\n\n"
688 "Configures basic settings of the system.\n\n"
689 " -h --help Show this help\n"
690 " --version Show package version\n"
691 " --root=PATH Operate on an alternate filesystem root\n"
692 " --locale=LOCALE Set primary locale (LANG=)\n"
693 " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n"
694 " --keymap=KEYMAP Set keymap\n"
695 " --timezone=TIMEZONE Set timezone\n"
696 " --hostname=NAME Set host name\n"
697 " --machine-ID=ID Set machine ID\n"
698 " --root-password=PASSWORD Set root password\n"
699 " --root-password-file=FILE Set root password from file\n"
700 " --prompt-locale Prompt the user for locale settings\n"
701 " --prompt-keymap Prompt the user for keymap settings\n"
702 " --prompt-timezone Prompt the user for timezone\n"
703 " --prompt-hostname Prompt the user for hostname\n"
704 " --prompt-root-password Prompt the user for root password\n"
705 " --prompt Prompt for all of the above\n"
706 " --copy-locale Copy locale from host\n"
707 " --copy-keymap Copy keymap from host\n"
708 " --copy-timezone Copy timezone from host\n"
709 " --copy-root-password Copy root password from host\n"
710 " --copy Copy locale, keymap, timezone, root password\n"
711 " --setup-machine-id Generate a new random machine ID\n"
712 , program_invocation_short_name
);
715 static int parse_argv(int argc
, char *argv
[]) {
727 ARG_ROOT_PASSWORD_FILE
,
733 ARG_PROMPT_ROOT_PASSWORD
,
738 ARG_COPY_ROOT_PASSWORD
,
739 ARG_SETUP_MACHINE_ID
,
742 static const struct option options
[] = {
743 { "help", no_argument
, NULL
, 'h' },
744 { "version", no_argument
, NULL
, ARG_VERSION
},
745 { "root", required_argument
, NULL
, ARG_ROOT
},
746 { "locale", required_argument
, NULL
, ARG_LOCALE
},
747 { "locale-messages", required_argument
, NULL
, ARG_LOCALE_MESSAGES
},
748 { "keymap", required_argument
, NULL
, ARG_KEYMAP
},
749 { "timezone", required_argument
, NULL
, ARG_TIMEZONE
},
750 { "hostname", required_argument
, NULL
, ARG_HOSTNAME
},
751 { "machine-id", required_argument
, NULL
, ARG_MACHINE_ID
},
752 { "root-password", required_argument
, NULL
, ARG_ROOT_PASSWORD
},
753 { "root-password-file", required_argument
, NULL
, ARG_ROOT_PASSWORD_FILE
},
754 { "prompt", no_argument
, NULL
, ARG_PROMPT
},
755 { "prompt-locale", no_argument
, NULL
, ARG_PROMPT_LOCALE
},
756 { "prompt-keymap", no_argument
, NULL
, ARG_PROMPT_KEYMAP
},
757 { "prompt-timezone", no_argument
, NULL
, ARG_PROMPT_TIMEZONE
},
758 { "prompt-hostname", no_argument
, NULL
, ARG_PROMPT_HOSTNAME
},
759 { "prompt-root-password", no_argument
, NULL
, ARG_PROMPT_ROOT_PASSWORD
},
760 { "copy", no_argument
, NULL
, ARG_COPY
},
761 { "copy-locale", no_argument
, NULL
, ARG_COPY_LOCALE
},
762 { "copy-keymap", no_argument
, NULL
, ARG_COPY_KEYMAP
},
763 { "copy-timezone", no_argument
, NULL
, ARG_COPY_TIMEZONE
},
764 { "copy-root-password", no_argument
, NULL
, ARG_COPY_ROOT_PASSWORD
},
765 { "setup-machine-id", no_argument
, NULL
, ARG_SETUP_MACHINE_ID
},
774 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
786 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
792 if (!locale_is_valid(optarg
)) {
793 log_error("Locale %s is not valid.", optarg
);
797 r
= free_and_strdup(&arg_locale
, optarg
);
803 case ARG_LOCALE_MESSAGES
:
804 if (!locale_is_valid(optarg
)) {
805 log_error("Locale %s is not valid.", optarg
);
809 r
= free_and_strdup(&arg_locale_messages
, optarg
);
816 if (!keymap_is_valid(optarg
)) {
817 log_error("Keymap %s is not valid.", optarg
);
821 r
= free_and_strdup(&arg_keymap
, optarg
);
828 if (!timezone_is_valid(optarg
)) {
829 log_error("Timezone %s is not valid.", optarg
);
833 r
= free_and_strdup(&arg_timezone
, optarg
);
839 case ARG_ROOT_PASSWORD
:
840 r
= free_and_strdup(&arg_root_password
, optarg
);
845 case ARG_ROOT_PASSWORD_FILE
:
846 arg_root_password
= mfree(arg_root_password
);
848 r
= read_one_line_file(optarg
, &arg_root_password
);
850 return log_error_errno(r
, "Failed to read %s: %m", optarg
);
855 if (!hostname_is_valid(optarg
, true)) {
856 log_error("Host name %s is not valid.", optarg
);
860 hostname_cleanup(optarg
);
861 r
= free_and_strdup(&arg_hostname
, optarg
);
868 if (sd_id128_from_string(optarg
, &arg_machine_id
) < 0) {
869 log_error("Failed to parse machine id %s.", optarg
);
876 arg_prompt_locale
= arg_prompt_keymap
= arg_prompt_timezone
= arg_prompt_hostname
= arg_prompt_root_password
= true;
879 case ARG_PROMPT_LOCALE
:
880 arg_prompt_locale
= true;
883 case ARG_PROMPT_KEYMAP
:
884 arg_prompt_keymap
= true;
887 case ARG_PROMPT_TIMEZONE
:
888 arg_prompt_timezone
= true;
891 case ARG_PROMPT_HOSTNAME
:
892 arg_prompt_hostname
= true;
895 case ARG_PROMPT_ROOT_PASSWORD
:
896 arg_prompt_root_password
= true;
900 arg_copy_locale
= arg_copy_keymap
= arg_copy_timezone
= arg_copy_root_password
= true;
903 case ARG_COPY_LOCALE
:
904 arg_copy_locale
= true;
907 case ARG_COPY_KEYMAP
:
908 arg_copy_keymap
= true;
911 case ARG_COPY_TIMEZONE
:
912 arg_copy_timezone
= true;
915 case ARG_COPY_ROOT_PASSWORD
:
916 arg_copy_root_password
= true;
919 case ARG_SETUP_MACHINE_ID
:
921 r
= sd_id128_randomize(&arg_machine_id
);
923 return log_error_errno(r
, "Failed to generate randomized machine ID: %m");
931 assert_not_reached("Unhandled option");
937 int main(int argc
, char *argv
[]) {
941 r
= parse_argv(argc
, argv
);
945 log_set_target(LOG_TARGET_AUTO
);
946 log_parse_environment();
951 r
= proc_cmdline_get_bool("systemd.firstboot", &enabled
);
953 log_error_errno(r
, "Failed to parse systemd.firstboot= kernel command line argument, ignoring.");
956 if (r
> 0 && !enabled
) {
957 r
= 0; /* disabled */
961 r
= process_locale();
965 r
= process_keymap();
969 r
= process_timezone();
973 r
= process_hostname();
977 r
= process_machine_id();
981 r
= process_root_password();
988 free(arg_locale_messages
);
992 string_erase(arg_root_password
);
993 free(arg_root_password
);
995 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;