mirror of
https://github.com/systemd/systemd
synced 2024-09-06 16:56:43 +00:00
Merge pull request #16939 from Rahix/robust-first-boot-machine-id
Make ConditionFirstBoot safe against power failures
This commit is contained in:
commit
0ce8a9d6e5
|
@ -82,10 +82,11 @@
|
||||||
|
|
||||||
<para>For operating system images which are created once and used on multiple
|
<para>For operating system images which are created once and used on multiple
|
||||||
machines, for example for containers or in the cloud,
|
machines, for example for containers or in the cloud,
|
||||||
<filename>/etc/machine-id</filename> should be an empty file in the generic file
|
<filename>/etc/machine-id</filename> should be either missing or an empty file in the generic file
|
||||||
system image. An ID will be generated during boot and saved to this file if
|
system image (the difference between the two options is described under "First Boot Semantics" below). An
|
||||||
possible. Having an empty file in place is useful because it allows a temporary file
|
ID will be generated during boot and saved to this file if possible. Having an empty file in place is
|
||||||
to be bind-mounted over the real file, in case the image is used read-only.</para>
|
useful because it allows a temporary file to be bind-mounted over the real file, in case the image is
|
||||||
|
used read-only.</para>
|
||||||
|
|
||||||
<para><citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
<para><citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
may be used to initialize <filename>/etc/machine-id</filename> on mounted (but not
|
may be used to initialize <filename>/etc/machine-id</filename> on mounted (but not
|
||||||
|
@ -115,6 +116,34 @@
|
||||||
early boot but become writable later on.</para>
|
early boot but become writable later on.</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>First Boot Semantics</title>
|
||||||
|
|
||||||
|
<para><filename>/etc/machine-id</filename> is used to decide whether a boot is the first one. The rules
|
||||||
|
are as follows:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>If <filename>/etc/machine-id</filename> does not exist, this is a first boot. During
|
||||||
|
early boot, <command>systemd</command> will write <literal>unitialized\n</literal> to this file and overmount
|
||||||
|
a temporary file which contains the actual machine ID. Later (after <filename>first-boot-complete.target</filename>
|
||||||
|
has been reached), the real machine ID will be written to disk.</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>If <filename>/etc/machine-id</filename> contains the string <literal>uninitialized</literal>,
|
||||||
|
a boot is also considered the first boot. The same mechanism as above applies.</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>If <filename>/etc/machine-id</filename> exists and is empty, a boot is
|
||||||
|
<emphasis>not</emphasis> considered the first boot. <command>systemd</command> will still bind-mount a file
|
||||||
|
containing the actual machine-id over it and later try to commit it to disk (if <filename>/etc/</filename> is
|
||||||
|
writable).</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>If <filename>/etc/machine-id</filename> already contains a valid machine-id, this is
|
||||||
|
not a first boot.</para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<para>If by any of the above rules, a first boot is detected, units with <varname>ConditionFirstBoot=yes</varname>
|
||||||
|
will be run.</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Relation to OSF UUIDs</title>
|
<title>Relation to OSF UUIDs</title>
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
<filename>emergency.target</filename>,
|
<filename>emergency.target</filename>,
|
||||||
<filename>exit.target</filename>,
|
<filename>exit.target</filename>,
|
||||||
<filename>final.target</filename>,
|
<filename>final.target</filename>,
|
||||||
|
<filename>first-boot-complete.target</filename>,
|
||||||
<filename>getty.target</filename>,
|
<filename>getty.target</filename>,
|
||||||
<filename>getty-pre.target</filename>,
|
<filename>getty-pre.target</filename>,
|
||||||
<filename>graphical.target</filename>,
|
<filename>graphical.target</filename>,
|
||||||
|
@ -878,6 +879,17 @@
|
||||||
stopped.</para>
|
stopped.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><filename>first-boot-complete.target</filename></term>
|
||||||
|
<listitem>
|
||||||
|
<para>This passive target is intended as a synchronization point for units that need to run once
|
||||||
|
during the first boot. Only after all units ordered before this target have finished, will the
|
||||||
|
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||||
|
be committed to disk, marking the first boot as completed. If the boot is aborted at any time
|
||||||
|
before that, the next boot will re-run any units with <varname>ConditionFirstBoot=yes</varname>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><filename>getty-pre.target</filename></term>
|
<term><filename>getty-pre.target</filename></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
|
|
@ -1315,10 +1315,16 @@
|
||||||
<term><varname>ConditionFirstBoot=</varname></term>
|
<term><varname>ConditionFirstBoot=</varname></term>
|
||||||
|
|
||||||
<listitem><para>Takes a boolean argument. This condition may be used to conditionalize units on
|
<listitem><para>Takes a boolean argument. This condition may be used to conditionalize units on
|
||||||
whether the system is booting up with an unpopulated <filename>/etc/</filename> directory
|
whether the system is booting up for the first time. This roughly means that <filename>/etc/</filename>
|
||||||
(specifically: an <filename>/etc/</filename> with no <filename>/etc/machine-id</filename>). This may
|
is unpopulated (for details, see "First Boot Semantics" in
|
||||||
be used to populate <filename>/etc/</filename> on the first boot after factory reset, or when a new
|
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
|
||||||
system instance boots up for the first time.</para>
|
This may be used to populate <filename>/etc/</filename> on the first boot after factory reset, or
|
||||||
|
when a new system instance boots up for the first time.</para>
|
||||||
|
|
||||||
|
<para>For robustness, units with <varname>ConditionFirstBoot=yes</varname> should order themselves
|
||||||
|
before <filename>first-boot-complete.target</filename> and pull in this passive target with
|
||||||
|
<varname>Wants=</varname>. This ensures that in a case of an aborted first boot, these units will
|
||||||
|
be re-run during the next system startup.</para>
|
||||||
|
|
||||||
<para>If the <varname>systemd.condition-first-boot=</varname> option is specified on the kernel
|
<para>If the <varname>systemd.condition-first-boot=</varname> option is specified on the kernel
|
||||||
command line (taking a boolean), it will override the result of this condition check, taking
|
command line (taking a boolean), it will override the result of this condition check, taking
|
||||||
|
|
|
@ -37,6 +37,7 @@ disable systemd-networkd-wait-online.service
|
||||||
disable systemd-time-wait-sync.service
|
disable systemd-time-wait-sync.service
|
||||||
disable systemd-boot-check-no-failures.service
|
disable systemd-boot-check-no-failures.service
|
||||||
disable systemd-network-generator.service
|
disable systemd-network-generator.service
|
||||||
|
disable proc-sys-fs-binfmt_misc.mount
|
||||||
|
|
||||||
disable syslog.socket
|
disable syslog.socket
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "id128-util.h"
|
#include "id128-util.h"
|
||||||
|
#include "io-util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "machine-id-setup.h"
|
#include "machine-id-setup.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
|
@ -86,7 +87,7 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
|
int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_id, sd_id128_t *ret) {
|
||||||
const char *etc_machine_id, *run_machine_id;
|
const char *etc_machine_id, *run_machine_id;
|
||||||
_cleanup_close_ int fd = -1;
|
_cleanup_close_ int fd = -1;
|
||||||
bool writable;
|
bool writable;
|
||||||
|
@ -143,13 +144,31 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
|
||||||
if (ftruncate(fd, 0) < 0)
|
if (ftruncate(fd, 0) < 0)
|
||||||
return log_error_errno(errno, "Failed to truncate %s: %m", etc_machine_id);
|
return log_error_errno(errno, "Failed to truncate %s: %m", etc_machine_id);
|
||||||
|
|
||||||
if (id128_write_fd(fd, ID128_PLAIN, machine_id, true) >= 0)
|
/* If the caller requested a transient machine-id, write the string "uninitialized\n" to
|
||||||
goto finish;
|
* disk and overmount it with a transient file.
|
||||||
|
*
|
||||||
|
* Otherwise write the machine-id directly to disk. */
|
||||||
|
if (force_transient) {
|
||||||
|
r = loop_write(fd, "uninitialized\n", strlen("uninitialized\n"), false);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to write uninitialized %s: %m", etc_machine_id);
|
||||||
|
|
||||||
|
r = fsync_full(fd);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to sync %s: %m", etc_machine_id);
|
||||||
|
} else {
|
||||||
|
r = id128_write_fd(fd, ID128_PLAIN, machine_id, true);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to write %s: %m", etc_machine_id);
|
||||||
|
else
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = safe_close(fd);
|
fd = safe_close(fd);
|
||||||
|
|
||||||
/* Hmm, we couldn't write it? So let's write it to /run/machine-id as a replacement */
|
/* Hmm, we couldn't or shouldn't write the machine-id to /etc?
|
||||||
|
* So let's write it to /run/machine-id as a replacement */
|
||||||
|
|
||||||
run_machine_id = prefix_roota(root, "/run/machine-id");
|
run_machine_id = prefix_roota(root, "/run/machine-id");
|
||||||
|
|
||||||
|
@ -167,7 +186,7 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("Installed transient %s file.", etc_machine_id);
|
log_full(force_transient ? LOG_DEBUG : LOG_INFO, "Installed transient %s file.", etc_machine_id);
|
||||||
|
|
||||||
/* Mark the mount read-only */
|
/* Mark the mount read-only */
|
||||||
r = mount_follow_verbose(LOG_WARNING, NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
|
r = mount_follow_verbose(LOG_WARNING, NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
|
||||||
|
@ -183,10 +202,22 @@ finish:
|
||||||
|
|
||||||
int machine_id_commit(const char *root) {
|
int machine_id_commit(const char *root) {
|
||||||
_cleanup_close_ int fd = -1, initial_mntns_fd = -1;
|
_cleanup_close_ int fd = -1, initial_mntns_fd = -1;
|
||||||
const char *etc_machine_id;
|
const char *etc_machine_id, *sync_path;
|
||||||
sd_id128_t id;
|
sd_id128_t id;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
/* Before doing anything, sync everything to ensure any changes by first-boot units are persisted.
|
||||||
|
*
|
||||||
|
* First, explicitly sync the file systems we care about and check if it worked. */
|
||||||
|
FOREACH_STRING(sync_path, "/etc/", "/var/") {
|
||||||
|
r = syncfs_path(AT_FDCWD, sync_path);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Cannot sync %s: %m", sync_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Afterwards, sync() the rest too, but we can't check the return value for these. */
|
||||||
|
sync();
|
||||||
|
|
||||||
/* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
|
/* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
|
||||||
* in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
|
* in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
|
||||||
* original mount namespace, thus revealing the file that was just created. */
|
* original mount namespace, thus revealing the file that was just created. */
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
int machine_id_commit(const char *root);
|
int machine_id_commit(const char *root);
|
||||||
int machine_id_setup(const char *root, sd_id128_t requested, sd_id128_t *ret);
|
int machine_id_setup(const char *root, bool force_transient, sd_id128_t requested, sd_id128_t *ret);
|
||||||
|
|
|
@ -2001,15 +2001,26 @@ static void log_execution_mode(bool *ret_first_boot) {
|
||||||
*ret_first_boot = false;
|
*ret_first_boot = false;
|
||||||
log_info("Running in initial RAM disk.");
|
log_info("Running in initial RAM disk.");
|
||||||
} else {
|
} else {
|
||||||
/* Let's check whether we are in first boot, i.e. whether /etc is still unpopulated. We use
|
int r;
|
||||||
* /etc/machine-id as flag file, for this: if it exists we assume /etc is populated, if it
|
_cleanup_free_ char *id_text = NULL;
|
||||||
* doesn't it's unpopulated. 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. */
|
|
||||||
|
|
||||||
*ret_first_boot = access("/etc/machine-id", F_OK) < 0;
|
/* Let's check whether we are in first boot. We use /etc/machine-id as flag file
|
||||||
if (*ret_first_boot)
|
* for this: If it is missing or contains the value "uninitialized", this is the
|
||||||
log_info("Running with unpopulated /etc.");
|
* 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. */
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
*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.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (DEBUG_LOGGING) {
|
if (DEBUG_LOGGING) {
|
||||||
|
@ -2026,6 +2037,7 @@ static void log_execution_mode(bool *ret_first_boot) {
|
||||||
|
|
||||||
static int initialize_runtime(
|
static int initialize_runtime(
|
||||||
bool skip_setup,
|
bool skip_setup,
|
||||||
|
bool first_boot,
|
||||||
struct rlimit *saved_rlimit_nofile,
|
struct rlimit *saved_rlimit_nofile,
|
||||||
struct rlimit *saved_rlimit_memlock,
|
struct rlimit *saved_rlimit_memlock,
|
||||||
const char **ret_error_message) {
|
const char **ret_error_message) {
|
||||||
|
@ -2059,7 +2071,8 @@ static int initialize_runtime(
|
||||||
|
|
||||||
status_welcome();
|
status_welcome();
|
||||||
hostname_setup();
|
hostname_setup();
|
||||||
machine_id_setup(NULL, arg_machine_id, NULL);
|
/* Force transient machine-id on first boot. */
|
||||||
|
machine_id_setup(NULL, first_boot, arg_machine_id, NULL);
|
||||||
(void) loopback_setup();
|
(void) loopback_setup();
|
||||||
bump_unix_max_dgram_qlen();
|
bump_unix_max_dgram_qlen();
|
||||||
bump_file_max_and_nr_open();
|
bump_file_max_and_nr_open();
|
||||||
|
@ -2787,6 +2800,7 @@ int main(int argc, char *argv[]) {
|
||||||
log_execution_mode(&first_boot);
|
log_execution_mode(&first_boot);
|
||||||
|
|
||||||
r = initialize_runtime(skip_setup,
|
r = initialize_runtime(skip_setup,
|
||||||
|
first_boot,
|
||||||
&saved_rlimit_nofile,
|
&saved_rlimit_nofile,
|
||||||
&saved_rlimit_memlock,
|
&saved_rlimit_memlock,
|
||||||
&error_message);
|
&error_message);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "id128-util.h"
|
#include "id128-util.h"
|
||||||
#include "io-util.h"
|
#include "io-util.h"
|
||||||
#include "stdio-util.h"
|
#include "stdio-util.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
|
||||||
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
|
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
|
||||||
unsigned n, k = 0;
|
unsigned n, k = 0;
|
||||||
|
@ -97,6 +98,11 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
|
||||||
|
|
||||||
switch (l) {
|
switch (l) {
|
||||||
|
|
||||||
|
case 13:
|
||||||
|
case 14:
|
||||||
|
/* Treat an "uninitialized" id file like an empty one */
|
||||||
|
return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL;
|
||||||
|
|
||||||
case 33: /* plain UUID with trailing newline */
|
case 33: /* plain UUID with trailing newline */
|
||||||
if (buffer[32] != '\n')
|
if (buffer[32] != '\n')
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -115,7 +121,7 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
|
||||||
|
|
||||||
_fallthrough_;
|
_fallthrough_;
|
||||||
case 36: /* RFC UUID without trailing newline */
|
case 36: /* RFC UUID without trailing newline */
|
||||||
if (f == ID128_PLAIN)
|
if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
buffer[36] = 0;
|
buffer[36] = 0;
|
||||||
|
|
|
@ -17,6 +17,10 @@ bool id128_is_valid(const char *s) _pure_;
|
||||||
typedef enum Id128Format {
|
typedef enum Id128Format {
|
||||||
ID128_ANY,
|
ID128_ANY,
|
||||||
ID128_PLAIN, /* formatted as 32 hex chars as-is */
|
ID128_PLAIN, /* formatted as 32 hex chars as-is */
|
||||||
|
ID128_PLAIN_OR_UNINIT, /* formatted as 32 hex chars as-is; allow special "uninitialized"
|
||||||
|
* value when reading from file (id128_read() and id128_read_fd()).
|
||||||
|
*
|
||||||
|
* This format should be used when reading a machine-id file. */
|
||||||
ID128_UUID, /* formatted as 36 character uuid string */
|
ID128_UUID, /* formatted as 36 character uuid string */
|
||||||
_ID128_FORMAT_MAX,
|
_ID128_FORMAT_MAX,
|
||||||
} Id128Format;
|
} Id128Format;
|
||||||
|
|
|
@ -128,7 +128,7 @@ static int run(int argc, char *argv[]) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to read machine ID back: %m");
|
return log_error_errno(r, "Failed to read machine ID back: %m");
|
||||||
} else {
|
} else {
|
||||||
r = machine_id_setup(arg_root, SD_ID128_NULL, &id);
|
r = machine_id_setup(arg_root, false, SD_ID128_NULL, &id);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2726,7 +2726,7 @@ static int setup_machine_id(const char *directory) {
|
||||||
|
|
||||||
etc_machine_id = prefix_roota(directory, "/etc/machine-id");
|
etc_machine_id = prefix_roota(directory, "/etc/machine-id");
|
||||||
|
|
||||||
r = id128_read(etc_machine_id, ID128_PLAIN, &id);
|
r = id128_read(etc_machine_id, ID128_PLAIN_OR_UNINIT, &id);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (!IN_SET(r, -ENOENT, -ENOMEDIUM)) /* If the file is missing or empty, we don't mind */
|
if (!IN_SET(r, -ENOENT, -ENOMEDIUM)) /* If the file is missing or empty, we don't mind */
|
||||||
return log_error_errno(r, "Failed to read machine ID from container image: %m");
|
return log_error_errno(r, "Failed to read machine ID from container image: %m");
|
||||||
|
|
|
@ -3245,7 +3245,7 @@ static int context_read_seed(Context *context, const char *root) {
|
||||||
else if (fd < 0)
|
else if (fd < 0)
|
||||||
return log_error_errno(fd, "Failed to determine machine ID of image: %m");
|
return log_error_errno(fd, "Failed to determine machine ID of image: %m");
|
||||||
else {
|
else {
|
||||||
r = id128_read_fd(fd, ID128_PLAIN, &context->seed);
|
r = id128_read_fd(fd, ID128_PLAIN_OR_UNINIT, &context->seed);
|
||||||
if (r == -ENOMEDIUM)
|
if (r == -ENOMEDIUM)
|
||||||
log_info("No machine ID set, using randomized partition UUIDs.");
|
log_info("No machine ID set, using randomized partition UUIDs.");
|
||||||
else if (r < 0)
|
else if (r < 0)
|
||||||
|
|
|
@ -2148,6 +2148,8 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||||
log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
|
log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
|
||||||
} else if (r == 0)
|
} else if (r == 0)
|
||||||
log_debug("/etc/machine-id file is empty.");
|
log_debug("/etc/machine-id file is empty.");
|
||||||
|
else if (streq(line, "uninitialized"))
|
||||||
|
log_debug("/etc/machine-id file is uninitialized (likely aborted first boot).");
|
||||||
else
|
else
|
||||||
log_debug("/etc/machine-id has unexpected length %i.", r);
|
log_debug("/etc/machine-id has unexpected length %i.", r);
|
||||||
|
|
||||||
|
|
14
units/first-boot-complete.target
Normal file
14
units/first-boot-complete.target
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
#
|
||||||
|
# This file is part of systemd.
|
||||||
|
#
|
||||||
|
# systemd is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=First Boot Complete
|
||||||
|
Documentation=man:systemd.special(7)
|
||||||
|
RefuseManualStart=yes
|
||||||
|
ConditionFirstBoot=yes
|
|
@ -17,6 +17,7 @@ units = [
|
||||||
['emergency.target', ''],
|
['emergency.target', ''],
|
||||||
['exit.target', ''],
|
['exit.target', ''],
|
||||||
['final.target', ''],
|
['final.target', ''],
|
||||||
|
['first-boot-complete.target', ''],
|
||||||
['getty.target', '',
|
['getty.target', '',
|
||||||
'multi-user.target.wants/'],
|
'multi-user.target.wants/'],
|
||||||
['getty-pre.target', ''],
|
['getty-pre.target', ''],
|
||||||
|
|
|
@ -13,7 +13,8 @@ Documentation=man:systemd-firstboot(1)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Conflicts=shutdown.target
|
Conflicts=shutdown.target
|
||||||
After=systemd-remount-fs.service
|
After=systemd-remount-fs.service
|
||||||
Before=systemd-sysusers.service sysinit.target shutdown.target
|
Before=systemd-sysusers.service sysinit.target first-boot-complete.target shutdown.target
|
||||||
|
Wants=first-boot-complete.target
|
||||||
ConditionPathIsReadWrite=/etc
|
ConditionPathIsReadWrite=/etc
|
||||||
ConditionFirstBoot=yes
|
ConditionFirstBoot=yes
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ Description=Commit a transient machine-id on disk
|
||||||
Documentation=man:systemd-machine-id-commit.service(8)
|
Documentation=man:systemd-machine-id-commit.service(8)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Conflicts=shutdown.target
|
Conflicts=shutdown.target
|
||||||
Before=sysinit.target shutdown.target
|
Before=shutdown.target
|
||||||
After=local-fs.target
|
After=local-fs.target first-boot-complete.target
|
||||||
ConditionPathIsReadWrite=/etc
|
ConditionPathIsReadWrite=/etc
|
||||||
ConditionPathIsMountPoint=/etc/machine-id
|
ConditionPathIsMountPoint=/etc/machine-id
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ DefaultDependencies=no
|
||||||
RequiresMountsFor=@RANDOM_SEED@
|
RequiresMountsFor=@RANDOM_SEED@
|
||||||
Conflicts=shutdown.target
|
Conflicts=shutdown.target
|
||||||
After=systemd-remount-fs.service
|
After=systemd-remount-fs.service
|
||||||
Before=shutdown.target
|
Before=first-boot-complete.target shutdown.target
|
||||||
|
Wants=first-boot-complete.target
|
||||||
ConditionVirtualization=!container
|
ConditionVirtualization=!container
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|
Loading…
Reference in a new issue