1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 #include "alloc-util.h"
12 #include "creds-util.h"
15 #include "id128-util.h"
16 #include "initrd-util.h"
19 #include "machine-id-setup.h"
20 #include "mount-util.h"
21 #include "mountpoint-util.h"
22 #include "namespace-util.h"
23 #include "path-util.h"
24 #include "process-util.h"
25 #include "stat-util.h"
26 #include "string-util.h"
28 #include "sync-util.h"
29 #include "umask-util.h"
32 static int acquire_machine_id_from_credential(sd_id128_t
*ret_machine_id
) {
33 _cleanup_free_
char *buf
= NULL
;
36 assert(ret_machine_id
);
38 r
= read_credential_with_decryption("system.machine_id", (void**) &buf
, /* ret_size= */ NULL
);
40 return log_warning_errno(r
, "Failed to read system.machine_id credential, ignoring: %m");
43 *ret_machine_id
= SD_ID128_NULL
;
47 if (streq(buf
, "firmware")) {
48 *ret_machine_id
= SD_ID128_NULL
;
52 r
= sd_id128_from_string(buf
, ret_machine_id
);
54 return log_warning_errno(r
, "Failed to parse system.machine_id credential, ignoring: %m");
56 log_info("Initializing machine ID from credential.");
60 static int acquire_machine_id(const char *root
, bool machine_id_from_firmware
, sd_id128_t
*ret
) {
61 _cleanup_close_
int fd
= -EBADF
;
66 /* First, try reading the machine ID from /run/machine-id, which may not be mounted on
67 * /etc/machine-id yet. This is important on switching root especially on soft-reboot, Otherwise,
68 * machine ID may be changed after the transition. */
69 if (isempty(root
) && running_in_chroot() <= 0 &&
70 id128_read("/run/machine-id", ID128_FORMAT_PLAIN
, ret
) >= 0) {
71 log_info("Reusing machine ID stored in /run/machine-id.");
72 return 1; /* Indicate that the machine ID is reused. */
75 /* Then, try reading the D-Bus machine ID, unless it is a symlink */
76 fd
= chase_and_open("/var/lib/dbus/machine-id", root
, CHASE_PREFIX_ROOT
|CHASE_NOFOLLOW
|CHASE_MUST_BE_REGULAR
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
, NULL
);
77 if (fd
>= 0 && id128_read_fd(fd
, ID128_FORMAT_PLAIN
| ID128_REFUSE_NULL
, ret
) >= 0) {
78 log_info("Initializing machine ID from D-Bus machine ID.");
82 if (isempty(root
) && running_in_chroot() <= 0) {
83 /* Let's use a system credential for the machine ID if we can */
84 sd_id128_t from_credential
;
85 r
= acquire_machine_id_from_credential(&from_credential
);
87 if (!sd_id128_is_null(from_credential
)) {
88 /* got a valid machine id from creds */
89 *ret
= from_credential
;
93 /* We got a credential, and it was set to "firmware", hence definitely try that */
94 machine_id_from_firmware
= true;
97 /* If that didn't work, see if we are running in a container,
98 * and a machine ID was passed in via $container_uuid the way
99 * libvirt/LXC does it */
101 if (detect_container() > 0) {
102 _cleanup_free_
char *e
= NULL
;
104 if (getenv_for_pid(1, "container_uuid", &e
) > 0 &&
105 sd_id128_from_string(e
, ret
) >= 0) {
106 log_info("Initializing machine ID from container UUID.");
110 } else if (IN_SET(detect_vm(), VIRTUALIZATION_KVM
, VIRTUALIZATION_AMAZON
, VIRTUALIZATION_QEMU
, VIRTUALIZATION_XEN
, VIRTUALIZATION_BHYVE
) || machine_id_from_firmware
) {
112 /* If we are not running in a container, see if we are running in a VM that provides
113 * a system UUID via the SMBIOS/DMI interfaces. Such environments include QEMU/KVM
114 * with the -uuid on the qemu command line or the Amazon EC2 Nitro hypervisor. */
116 if (id128_get_product(ret
) >= 0) {
117 log_info("Initializing machine ID from SMBIOS/DMI UUID.");
123 /* If that didn't work, generate a random machine ID */
124 r
= sd_id128_randomize(ret
);
126 return log_error_errno(r
, "Failed to generate randomized machine ID: %m");
128 log_info("Initializing machine ID from random generator.");
132 int machine_id_setup(const char *root
, sd_id128_t machine_id
, MachineIdSetupFlags flags
, sd_id128_t
*ret
) {
133 _cleanup_free_
char *etc_machine_id
= NULL
, *run_machine_id
= NULL
;
134 bool writable
, write_run_machine_id
= true;
135 _cleanup_close_
int fd
= -EBADF
, run_fd
= -EBADF
;
136 bool unlink_run_machine_id
= false;
140 _cleanup_close_
int inode_fd
= -EBADF
;
142 r
= chase("/etc/machine-id", root
, CHASE_PREFIX_ROOT
|CHASE_MUST_BE_REGULAR
, &etc_machine_id
, &inode_fd
);
144 _cleanup_close_
int etc_fd
= -EBADF
;
145 _cleanup_free_
char *etc
= NULL
;
147 r
= chase("/etc/", root
, CHASE_PREFIX_ROOT
|CHASE_MKDIR_0755
|CHASE_MUST_BE_DIRECTORY
, &etc
, &etc_fd
);
149 return log_error_errno(r
, "Failed to open %s: %m", "/etc/");
151 etc_machine_id
= path_join(etc
, "machine-id");
155 /* We create this 0444, to indicate that this isn't really something you should ever
156 * modify. Of course, since the file will be owned by root it doesn't matter much, but maybe
159 fd
= openat(etc_fd
, "machine-id", O_CREAT
|O_EXCL
|O_RDWR
|O_NOFOLLOW
|O_CLOEXEC
, 0444);
162 return log_error_errno(errno
,
163 "System cannot boot: Missing %s and %s/ is read-only.\n"
164 "Booting up is supported only when:\n"
165 "1) /etc/machine-id exists and is populated.\n"
166 "2) /etc/machine-id exists and is empty.\n"
167 "3) /etc/machine-id is missing and /etc/ is writable.",
171 return log_error_errno(errno
, "Cannot create '%s': %m", etc_machine_id
);
174 log_debug("Successfully opened new '%s' file.", etc_machine_id
);
177 return log_error_errno(r
, "Cannot open '/etc/machine-id': %m");
179 /* We pinned the inode, now try to convert it into a writable file */
181 fd
= xopenat_full(inode_fd
, /* path= */ NULL
, O_RDWR
|O_CLOEXEC
, XO_REGULAR
, 0444);
183 log_debug_errno(fd
, "Failed to open '%s' in writable mode, retrying in read-only mode: %m", etc_machine_id
);
185 /* If that didn't work, convert it into a readable file */
186 fd
= xopenat_full(inode_fd
, /* path= */ NULL
, O_RDONLY
|O_CLOEXEC
, XO_REGULAR
, MODE_INVALID
);
188 return log_error_errno(fd
, "Cannot open '%s' in neither writable nor read-only mode: %m", etc_machine_id
);
190 log_debug("Successfully opened existing '%s' file in read-only mode.", etc_machine_id
);
193 log_debug("Successfully opened existing '%s' file in writable mode.", etc_machine_id
);
199 /* A we got a valid machine ID argument, that's what counts */
200 if (sd_id128_is_null(machine_id
) || FLAGS_SET(flags
, MACHINE_ID_SETUP_FORCE_FIRMWARE
)) {
202 /* Try to read any existing machine ID */
203 r
= id128_read_fd(fd
, ID128_FORMAT_PLAIN
, &machine_id
);
207 log_debug_errno(r
, "Unable to read current machine ID, acquiring new one: %m");
209 /* Hmm, so, the id currently stored is not useful, then let's acquire one. */
210 r
= acquire_machine_id(root
, FLAGS_SET(flags
, MACHINE_ID_SETUP_FORCE_FIRMWARE
), &machine_id
);
214 write_run_machine_id
= !r
; /* acquire_machine_id() returns 1 in case we read this machine ID
215 * from /run/machine-id */
219 if (lseek(fd
, 0, SEEK_SET
) < 0)
220 return log_error_errno(errno
, "Failed to seek %s: %m", etc_machine_id
);
222 if (ftruncate(fd
, 0) < 0)
223 return log_error_errno(errno
, "Failed to truncate %s: %m", etc_machine_id
);
225 /* If the caller requested a transient machine-id, write the string "uninitialized\n" to
226 * disk and overmount it with a transient file.
228 * Otherwise write the machine-id directly to disk. */
229 if (FLAGS_SET(flags
, MACHINE_ID_SETUP_FORCE_TRANSIENT
)) {
230 r
= loop_write(fd
, "uninitialized\n", SIZE_MAX
);
232 return log_error_errno(r
, "Failed to write uninitialized %s: %m", etc_machine_id
);
236 return log_error_errno(r
, "Failed to sync %s: %m", etc_machine_id
);
238 r
= id128_write_fd(fd
, ID128_FORMAT_PLAIN
| ID128_SYNC_ON_WRITE
, machine_id
);
240 return log_error_errno(r
, "Failed to write %s: %m", etc_machine_id
);
246 /* Hmm, we couldn't or shouldn't write the machine-id to /etc/? So let's write it to /run/machine-id
247 * as a replacement */
249 if (write_run_machine_id
) {
250 _cleanup_free_
char *run
= NULL
;
252 r
= chase("/run/", root
, CHASE_PREFIX_ROOT
|CHASE_MKDIR_0755
|CHASE_MUST_BE_DIRECTORY
, &run
, &run_fd
);
254 return log_error_errno(r
, "Failed to open %s: %m", "/run/");
256 run_machine_id
= path_join(run
, "machine-id");
261 r
= id128_write_at(run_fd
, "machine-id", ID128_FORMAT_PLAIN
, machine_id
);
263 (void) unlinkat(run_fd
, "machine-id", /* flags = */ 0);
264 return log_error_errno(r
, "Cannot write '%s': %m", run_machine_id
);
268 unlink_run_machine_id
= true;
270 r
= chase("/run/machine-id", root
, CHASE_PREFIX_ROOT
|CHASE_MUST_BE_REGULAR
, &run_machine_id
, /* ret_fd= */ NULL
);
272 return log_error_errno(r
, "Failed to open %s: %m", "/run/machine-id");
275 /* And now, let's mount it over */
276 r
= mount_follow_verbose(LOG_ERR
, run_machine_id
, FORMAT_PROC_FD_PATH(fd
), /* fstype= */ NULL
, MS_BIND
, /* options= */ NULL
);
278 if (unlink_run_machine_id
)
279 (void) unlinkat(ASSERT_FD(run_fd
), "machine-id", /* flags = */ 0);
283 log_full(FLAGS_SET(flags
, MACHINE_ID_SETUP_FORCE_TRANSIENT
) ? LOG_DEBUG
: LOG_INFO
, "Installed transient '%s' file.", etc_machine_id
);
285 /* Mark the mount read-only (note: we are not going via FORMAT_PROC_FD_PATH() here because that fd is not updated to our new bind mount) */
286 (void) mount_follow_verbose(LOG_WARNING
, /* what= */ NULL
, etc_machine_id
, /* fstype= */ NULL
, MS_BIND
|MS_RDONLY
|MS_REMOUNT
, /* options= */ NULL
);
290 (void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_MACHINE_ID=" SD_ID128_FORMAT_STR
, SD_ID128_FORMAT_VAL(machine_id
));
298 int machine_id_commit(const char *root
) {
302 if (empty_or_root(root
)) {
303 /* Before doing anything, sync everything to ensure any changes by first-boot units are
306 * First, explicitly sync the file systems we care about and check if it worked. */
307 FOREACH_STRING(sync_path
, "/etc/", "/var/") {
308 r
= syncfs_path(AT_FDCWD
, sync_path
);
310 return log_error_errno(r
, "Cannot sync %s: %m", sync_path
);
313 /* Afterwards, sync() the rest too, but we can't check the return value for these. */
317 /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
318 * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
319 * original mount namespace, thus revealing the file that was just created. */
321 _cleanup_close_
int etc_fd
= -EBADF
;
322 _cleanup_free_
char *etc
= NULL
;
323 r
= chase("/etc/", root
, CHASE_PREFIX_ROOT
|CHASE_MUST_BE_DIRECTORY
, &etc
, &etc_fd
);
325 return log_error_errno(r
, "Failed to open %s: %m", "/etc/");
327 _cleanup_free_
char *etc_machine_id
= path_join(etc
, "machine-id");
331 r
= is_mount_point_at(etc_fd
, "machine-id", /* flags= */ 0);
333 return log_error_errno(r
, "Failed to determine whether %s is a mount point: %m", etc_machine_id
);
335 log_debug("%s is not a mount point. Nothing to do.", etc_machine_id
);
339 /* Read existing machine-id */
341 _cleanup_close_
int fd
= xopenat_full(etc_fd
, "machine-id", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
, XO_REGULAR
, MODE_INVALID
);
343 return log_error_errno(fd
, "Cannot open %s: %m", etc_machine_id
);
345 etc_fd
= safe_close(etc_fd
);
347 r
= fd_is_temporary_fs(fd
);
349 return log_error_errno(r
, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id
);
351 return log_error_errno(SYNTHETIC_ERRNO(EROFS
),
352 "%s is not on a temporary file system.",
355 r
= id128_read_fd(fd
, ID128_FORMAT_PLAIN
, &id
);
357 return log_error_errno(r
, "We didn't find a valid machine ID in %s: %m", etc_machine_id
);
359 /* Store current mount namespace */
360 _cleanup_close_
int initial_mntns_fd
= namespace_open_by_type(NAMESPACE_MOUNT
);
361 if (initial_mntns_fd
< 0)
362 return log_error_errno(initial_mntns_fd
, "Can't fetch current mount namespace: %m");
364 /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
365 r
= detach_mount_namespace();
367 return log_error_errno(r
, "Failed to set up new mount namespace: %m");
369 /* Open /etc/ again after we transitioned into our own private mount namespace */
370 _cleanup_close_
int etc_fd_again
= -EBADF
;
371 r
= chase("/etc/", root
, CHASE_PREFIX_ROOT
|CHASE_MUST_BE_DIRECTORY
, /* ret_path= */ NULL
, &etc_fd_again
);
373 return log_error_errno(r
, "Failed to open %s: %m", "/etc/");
375 r
= umountat_detach_verbose(LOG_ERR
, etc_fd_again
, "machine-id");
379 /* Update a persistent version of etc_machine_id */
380 r
= id128_write_at(etc_fd_again
, "machine-id", ID128_FORMAT_PLAIN
| ID128_SYNC_ON_WRITE
, id
);
382 return log_error_errno(r
, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id
);
384 etc_fd_again
= safe_close(etc_fd_again
);
386 /* Return to initial namespace and proceed a lazy tmpfs unmount */
387 r
= namespace_enter(/* pidns_fd = */ -EBADF
,
389 /* netns_fd = */ -EBADF
,
390 /* userns_fd = */ -EBADF
,
391 /* root_fd = */ -EBADF
);
393 return log_warning_errno(r
,
394 "Failed to switch back to initial mount namespace: %m.\n"
395 "We'll keep transient %s file until next reboot.", etc_machine_id
);
397 r
= umountat_detach_verbose(LOG_DEBUG
, fd
, /* where= */ NULL
);
399 return log_warning_errno(r
,
400 "Failed to unmount transient %s file: %m.\n"
401 "We keep that mount until next reboot.", etc_machine_id
);