diff --git a/man/machine-id.xml b/man/machine-id.xml
index 9bd49582fc..ec1ab64dec 100644
--- a/man/machine-id.xml
+++ b/man/machine-id.xml
@@ -119,20 +119,26 @@
First Boot Semantics
- /etc/machine-id is used to decide whether a boot is the first one. The rules
+ /etc/machine-id is used to decide whether a boot is the first one. The rules
are as follows:
- If /etc/machine-id does not exist, this is a first boot. During
- early boot, systemd will write uninitialized\n to this file and overmount
- a temporary file which contains the actual machine ID. Later (after first-boot-complete.target
- has been reached), the real machine ID will be written to disk.
+ The kernel command argument systemd.condition-first-boot= may be
+ used to override the autodetection logic, see
+ kernel-command-line7.
+
+
+ Otherwise, if /etc/machine-id does not exist, this is a first
+ boot. During early boot, systemd will write uninitialized\n to
+ this file and overmount a temporary file which contains the actual machine ID. Later (after
+ first-boot-complete.target has been reached), the real machine ID will be written
+ to disk.
If /etc/machine-id contains the string uninitialized,
- a boot is also considered the first boot. The same mechanism as above applies.
+ a boot is also considered the first boot. The same mechanism as above applies.
If /etc/machine-id exists and is empty, a boot is
- not considered the first boot. systemd will still bind-mount a file
+ not considered the first boot. systemd will still bind-mount a file
containing the actual machine-id over it and later try to commit it to disk (if /etc/ is
writable).
@@ -140,8 +146,9 @@
not a first boot.
- If by any of the above rules, a first boot is detected, units with ConditionFirstBoot=yes
- will be run.
+ If according to the above rules a first boot is detected, units with
+ ConditionFirstBoot=yes will be run and systemd will perform
+ additional initialization steps, in particular presetting units.
diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml
index 9e6db28536..ab730d2cc2 100644
--- a/man/systemd.preset.xml
+++ b/man/systemd.preset.xml
@@ -47,12 +47,16 @@
units, but rather centralize them in a distribution or spin default policy, which can be amended by
administrator policy, see below.
- If no preset files exist, systemctl
- preset will enable all units that are installed by
- default. If this is not desired and all units shall rather be
- disabled, it is necessary to ship a preset file with a single,
- catchall "disable *" line. (See example 1,
- below.)
+ If no preset files exist, preset operations will enable all units that are installed by default. If
+ this is not desired and all units shall rather be disabled, it is necessary to ship a preset file with a
+ single, catchall "disable *" line. (See example 1, below.)
+
+ When the machine is booted for the first time,
+ systemd1 will
+ enable/disable all units according to preset policy, similarly to systemctl
+ preset-all. Also see "First Boot Semantics" in
+ machine-id5.
+
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index d502b6da18..ebb84e9db8 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1455,15 +1455,18 @@
ConditionFirstBoot=
Takes a boolean argument. This condition may be used to conditionalize units on
- whether the system is booting up for the first time. This roughly means that /etc/
- is unpopulated (for details, see "First Boot Semantics" in
+ whether the system is booting up for the first time. This roughly means that /etc/
+ was unpopulated when the system started booting (for details, see "First Boot Semantics" in
machine-id5).
- This may be used to populate /etc/ on the first boot after factory reset, or
- when a new system instance boots up for the first time.
+ First boot is considered finished (this condition will evaluate as false) after the manager
+ has finished the startup phase.
+
+ This condition may be used to populate /etc/ on the first boot after
+ factory reset, or when a new system instance boots up for the first time.
For robustness, units with ConditionFirstBoot=yes should order themselves
before first-boot-complete.target and pull in this passive target with
- Wants=. This ensures that in a case of an aborted first boot, these units will
+ Wants=. This ensures that in a case of an aborted first boot, these units will
be re-run during the next system startup.
If the systemd.condition-first-boot= option is specified on the kernel
diff --git a/man/systemd.xml b/man/systemd.xml
index f4213e2ee8..1a68301d50 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -188,8 +188,13 @@
systemd.special7
for details about these target units.
- systemd only keeps a minimal set of units loaded into memory. Specifically, the only units that are kept
- loaded into memory are those for which at least one of the following conditions is true:
+ On first boot, systemd will enable or disable units according to preset policy.
+ See systemd.preset5
+ and "First Boot Semantics" in
+ machine-id5.
+
+ systemd only keeps a minimal set of units loaded into memory. Specifically, the only units that are
+ kept loaded into memory are those for which at least one of the following conditions is true:
It is in an active, activating, deactivating or failed state (i.e. in any unit state except for inactive)
diff --git a/src/core/main.c b/src/core/main.c
index 27ed8a89ba..14a4f81452 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -2034,6 +2034,8 @@ static int invoke_main_loop(
}
static void log_execution_mode(bool *ret_first_boot) {
+ bool first_boot = false;
+
assert(ret_first_boot);
if (arg_system) {
@@ -2050,29 +2052,40 @@ static void log_execution_mode(bool *ret_first_boot) {
log_info("Detected architecture %s.", architecture_to_string(uname_architecture()));
- if (in_initrd()) {
- *ret_first_boot = false;
+ if (in_initrd())
log_info("Running in initrd.");
- } else {
+ else {
int r;
_cleanup_free_ char *id_text = NULL;
- /* Let's check whether we are in first boot. We use /etc/machine-id as flag file
- * for this: If it is missing or contains the value "uninitialized", this is the
- * first boot. In any other case, it is not. This allows container managers and
- * installers to provision a couple of files already. If the container manager
- * wants to provision the machine ID itself it should pass $container_uuid to PID 1. */
+ /* Let's check whether we are in first boot. First, check if an override was
+ * specified on the kernel commandline. If yes, we honour that. */
- r = read_one_line_file("/etc/machine-id", &id_text);
- if (r < 0 || streq(id_text, "uninitialized")) {
- if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Unexpected error while reading /etc/machine-id, ignoring: %m");
+ r = proc_cmdline_get_bool("systemd.condition-first-boot", &first_boot);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse systemd.condition-first-boot= kernel commandline argument, ignoring: %m");
- *ret_first_boot = true;
- log_info("Detected first boot.");
- } else {
- *ret_first_boot = false;
- log_debug("Detected initialized system, this is not the first boot.");
+ if (r > 0)
+ log_full(first_boot ? LOG_INFO : LOG_DEBUG,
+ "Kernel commandline argument says we are %s first boot.",
+ first_boot ? "in" : "not in");
+ else {
+ /* Second, perform autodetection. We use /etc/machine-id as flag file for
+ * this: If it is missing or contains the value "uninitialized", this is the
+ * first boot. In other cases, it is not. This allows container managers and
+ * installers to provision a couple of files in /etc but still permit the
+ * first-boot initialization to occur. If the container manager wants to
+ * provision the machine ID it should pass $container_uuid to PID 1. */
+
+ r = read_one_line_file("/etc/machine-id", &id_text);
+ if (r < 0 || streq(id_text, "uninitialized")) {
+ if (r < 0 && r != -ENOENT)
+ log_warning_errno(r, "Unexpected error while reading /etc/machine-id, ignoring: %m");
+
+ first_boot = true;
+ log_info("Detected first boot.");
+ } else
+ log_debug("Detected initialized system, this is not the first boot.");
}
}
@@ -2092,9 +2105,9 @@ static void log_execution_mode(bool *ret_first_boot) {
arg_action == ACTION_TEST ? " test" : "",
getuid(), strna(t), systemd_features);
}
-
- *ret_first_boot = false;
}
+
+ *ret_first_boot = first_boot;
}
static int initialize_runtime(
@@ -2134,7 +2147,7 @@ static int initialize_runtime(
(void) os_release_status();
(void) hostname_setup(true);
/* Force transient machine-id on first boot. */
- machine_id_setup(NULL, first_boot, arg_machine_id, NULL);
+ machine_id_setup(NULL, /* force_transient= */ first_boot, arg_machine_id, NULL);
(void) loopback_setup();
bump_unix_max_dgram_qlen();
bump_file_max_and_nr_open();
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 74fd722e80..065ee896cd 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -259,8 +259,10 @@ static int prompt_locale(void) {
return 0;
}
- if (!arg_prompt_locale)
+ if (!arg_prompt_locale) {
+ log_debug("Prompting for locale was not requested.");
return 0;
+ }
r = get_locales(&locales);
if (r < 0)
@@ -312,8 +314,11 @@ static int process_locale(void) {
int r;
etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
- if (laccess(etc_localeconf, F_OK) >= 0 && !arg_force)
+ if (laccess(etc_localeconf, F_OK) >= 0 && !arg_force) {
+ log_debug("Found %s, assuming locale information has been configured.",
+ etc_localeconf);
return 0;
+ }
if (arg_copy_locale && arg_root) {
@@ -366,12 +371,14 @@ static int prompt_keymap(void) {
return 0;
}
- if (!arg_prompt_keymap)
+ if (!arg_prompt_keymap) {
+ log_debug("Prompting for keymap was not requested.");
return 0;
+ }
r = get_keymaps(&kmaps);
if (r == -ENOENT) /* no keymaps installed */
- return r;
+ return log_debug_errno(r, "No keymaps are installed.");
if (r < 0)
return log_error_errno(r, "Failed to read keymaps: %m");
@@ -387,8 +394,11 @@ static int process_keymap(void) {
int r;
etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf");
- if (laccess(etc_vconsoleconf, F_OK) >= 0 && !arg_force)
+ if (laccess(etc_vconsoleconf, F_OK) >= 0 && !arg_force) {
+ log_debug("Found %s, assuming console has been configured.",
+ etc_vconsoleconf);
return 0;
+ }
if (arg_copy_keymap && arg_root) {
@@ -445,8 +455,10 @@ static int prompt_timezone(void) {
return 0;
}
- if (!arg_prompt_timezone)
+ if (!arg_prompt_timezone) {
+ log_debug("Prompting for timezone was not requested.");
return 0;
+ }
r = get_timezones(&zones);
if (r < 0)
@@ -467,8 +479,11 @@ static int process_timezone(void) {
int r;
etc_localtime = prefix_roota(arg_root, "/etc/localtime");
- if (laccess(etc_localtime, F_OK) >= 0 && !arg_force)
+ if (laccess(etc_localtime, F_OK) >= 0 && !arg_force) {
+ log_debug("Found %s, assuming timezone has been configured.",
+ etc_localtime);
return 0;
+ }
if (arg_copy_timezone && arg_root) {
_cleanup_free_ char *p = NULL;
@@ -512,8 +527,10 @@ static int prompt_hostname(void) {
if (arg_hostname)
return 0;
- if (!arg_prompt_hostname)
+ if (!arg_prompt_hostname) {
+ log_debug("Prompting for hostname was not requested.");
return 0;
+ }
print_welcome();
putchar('\n');
@@ -549,8 +566,11 @@ static int process_hostname(void) {
int r;
etc_hostname = prefix_roota(arg_root, "/etc/hostname");
- if (laccess(etc_hostname, F_OK) >= 0 && !arg_force)
+ if (laccess(etc_hostname, F_OK) >= 0 && !arg_force) {
+ log_debug("Found %s, assuming hostname has been configured.",
+ etc_hostname);
return 0;
+ }
r = prompt_hostname();
if (r < 0)
@@ -574,11 +594,16 @@ static int process_machine_id(void) {
int r;
etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
- if (laccess(etc_machine_id, F_OK) >= 0 && !arg_force)
+ if (laccess(etc_machine_id, F_OK) >= 0 && !arg_force) {
+ log_debug("Found %s, assuming machine-id has been configured.",
+ etc_machine_id);
return 0;
+ }
- if (sd_id128_is_null(arg_machine_id))
+ if (sd_id128_is_null(arg_machine_id)) {
+ log_debug("Initialization of machine-id was not requested, skipping.");
return 0;
+ }
r = write_string_file(etc_machine_id, SD_ID128_TO_STRING(arg_machine_id),
WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
@@ -600,8 +625,10 @@ static int prompt_root_password(void) {
if (get_credential_user_password("root", &arg_root_password, &arg_root_password_is_hashed) >= 0)
return 0;
- if (!arg_prompt_root_password)
+ if (!arg_prompt_root_password) {
+ log_debug("Prompting for root password was not requested.");
return 0;
+ }
print_welcome();
putchar('\n');
@@ -684,8 +711,10 @@ static int prompt_root_shell(void) {
return 0;
}
- if (!arg_prompt_root_shell)
+ if (!arg_prompt_root_shell) {
+ log_debug("Prompting for root shell was not requested.");
return 0;
+ }
print_welcome();
putchar('\n');
@@ -850,7 +879,7 @@ static int write_root_shadow(const char *shadow_path, const char *hashed_passwor
return 0;
}
-static int process_root_args(void) {
+static int process_root_account(void) {
_cleanup_close_ int lock = -1;
_cleanup_(erase_and_freep) char *_hashed_password = NULL;
const char *password, *hashed_password;
@@ -860,13 +889,18 @@ static int process_root_args(void) {
etc_passwd = prefix_roota(arg_root, "/etc/passwd");
etc_shadow = prefix_roota(arg_root, "/etc/shadow");
- if (laccess(etc_passwd, F_OK) >= 0 && laccess(etc_shadow, F_OK) >= 0 && !arg_force)
+ if (laccess(etc_passwd, F_OK) >= 0 && laccess(etc_shadow, F_OK) >= 0 && !arg_force) {
+ log_debug("Found %s and %s, assuming root account has been initialized.",
+ etc_passwd, etc_shadow);
return 0;
+ }
/* Don't create/modify passwd and shadow if not asked */
if (!(arg_root_password || arg_prompt_root_password || arg_copy_root_password || arg_delete_root_password ||
- arg_root_shell || arg_prompt_root_shell || arg_copy_root_shell))
+ arg_root_shell || arg_prompt_root_shell || arg_copy_root_shell)) {
+ log_debug("Initialization of root account was not requested, skipping.");
return 0;
+ }
(void) mkdir_parents(etc_passwd, 0755);
@@ -945,11 +979,16 @@ static int process_kernel_cmdline(void) {
int r;
etc_kernel_cmdline = prefix_roota(arg_root, "/etc/kernel/cmdline");
- if (laccess(etc_kernel_cmdline, F_OK) >= 0 && !arg_force)
+ if (laccess(etc_kernel_cmdline, F_OK) >= 0 && !arg_force) {
+ log_debug("Found %s, assuming kernel has been configured.",
+ etc_kernel_cmdline);
return 0;
+ }
- if (!arg_kernel_cmdline)
+ if (!arg_kernel_cmdline) {
+ log_debug("Creation of /etc/kernel/cmdline was not requested, skipping.");
return 0;
+ }
r = write_string_file(etc_kernel_cmdline, arg_kernel_cmdline,
WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
@@ -1332,8 +1371,10 @@ static int run(int argc, char *argv[]) {
r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
if (r < 0)
return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
- if (r > 0 && !enabled)
+ if (r > 0 && !enabled) {
+ log_debug("Found systemd.firstboot=no kernel command line argument, terminating.");
return 0; /* disabled */
+ }
}
if (arg_image) {
@@ -1377,7 +1418,7 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
- r = process_root_args();
+ r = process_root_account();
if (r < 0)
return r;
diff --git a/src/shared/condition.c b/src/shared/condition.c
index ffca2006c0..aa34e1e285 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -824,27 +824,20 @@ static int condition_test_needs_update(Condition *c, char **env) {
static int condition_test_first_boot(Condition *c, char **env) {
int r, q;
- bool b;
assert(c);
assert(c->parameter);
assert(c->type == CONDITION_FIRST_BOOT);
- r = proc_cmdline_get_bool("systemd.condition-first-boot", &b);
- if (r < 0)
- log_debug_errno(r, "Failed to parse systemd.condition-first-boot= kernel command line argument, ignoring: %m");
- if (r > 0)
- return b == !!r;
-
r = parse_boolean(c->parameter);
if (r < 0)
return r;
q = access("/run/systemd/first-boot", F_OK);
if (q < 0 && errno != ENOENT)
- log_debug_errno(errno, "Failed to check if /run/systemd/first-boot exists, ignoring: %m");
+ log_debug_errno(errno, "Failed to check if /run/systemd/first-boot exists, assuming no: %m");
- return (q >= 0) == !!r;
+ return (q >= 0) == r;
}
static int condition_test_environment(Condition *c, char **env) {