-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <sched.h>
#include "fd-util.h"
#include "fs-util.h"
#include "id128-util.h"
+#include "io-util.h"
#include "log.h"
#include "machine-id-setup.h"
#include "macro.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "mountpoint-util.h"
+#include "namespace-util.h"
#include "path-util.h"
#include "process-util.h"
#include "stat-util.h"
fd = safe_close(fd);
}
- if (isempty(root)) {
+ if (isempty(root) && running_in_chroot() <= 0) {
/* If that didn't work, see if we are running in a container,
* and a machine ID was passed in via $container_uuid the way
* libvirt/LXC does it */
log_info("Initializing machine ID from KVM UUID.");
return 0;
}
+ /* on POWER, it's exported here instead */
+ if (id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, ret) >= 0) {
+ log_info("Initializing machine ID from KVM UUID.");
+ 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;
_cleanup_close_ int fd = -1;
bool writable;
if (ftruncate(fd, 0) < 0)
return log_error_errno(errno, "Failed to truncate %s: %m", etc_machine_id);
- if (id128_write_fd(fd, ID128_PLAIN, machine_id, true) >= 0)
- goto finish;
+ /* If the caller requested a transient machine-id, write the string "uninitialized\n" to
+ * 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);
- /* 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");
}
/* And now, let's mount it over */
- if (mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL) < 0) {
- (void) unlink_noerrno(run_machine_id);
- return log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id);
+ r = mount_follow_verbose(LOG_ERR, run_machine_id, etc_machine_id, NULL, MS_BIND, NULL);
+ if (r < 0) {
+ (void) unlink(run_machine_id);
+ 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 */
- if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
- log_warning_errno(errno, "Failed to make transient %s read-only, ignoring: %m", etc_machine_id);
+ r = mount_follow_verbose(LOG_WARNING, NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
+ if (r < 0)
+ return r;
finish:
if (ret)
int machine_id_commit(const char *root) {
_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;
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
* 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. */
return log_error_errno(r, "Can't fetch current mount namespace: %m");
/* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
- if (unshare(CLONE_NEWNS) < 0)
- return log_error_errno(errno, "Failed to enter new namespace: %m");
-
- if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
- return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m");
+ r = detach_mount_namespace();
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up new mount namespace: %m");
- if (umount(etc_machine_id) < 0)
- return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id);
+ r = umount_verbose(LOG_ERR, etc_machine_id, 0);
+ if (r < 0)
+ return r;
/* Update a persistent version of etc_machine_id */
r = id128_write(etc_machine_id, ID128_PLAIN, id, true);