Skip to content

Commit

Permalink
machine-id: be more aggressive about commiting transient ID
Browse files Browse the repository at this point in the history
Previously, systemd would error out when anything went wrong when
positionning /etc/machine-id. An error would be displayed and boot would
go on without a machine-id

Now, unless /etc/machine-id is correct, systemd will create a
/run/machine-id, even if it can't be bind mount it over /etc.

systemd-machine-id-commit will detect if /run/machine-id exists and always
commit it to /etc/machine-id instead of relying on the fact that
/etc/machine-id is a mountpoint.
  • Loading branch information
boucman committed Nov 24, 2019
1 parent 36102a5 commit d86960d
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 61 deletions.
154 changes: 95 additions & 59 deletions src/core/machine-id-setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) {
int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
const char *etc_machine_id, *run_machine_id;
_cleanup_close_ int fd = -1;
bool writable;
bool writable, readable;
int r;

etc_machine_id = prefix_roota(root, "/etc/machine-id");
Expand All @@ -107,26 +107,31 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (old_errno == EROFS && errno == ENOENT)
return log_error_errno(errno,
"System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
"Booting up is supported only when:\n"
"1) /etc/machine-id exists and is populated.\n"
"2) /etc/machine-id exists and is empty.\n"
"3) /etc/machine-id is missing and /etc is writable.\n");
log_error_errno(errno,
"System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
"Booting up is supported only when:\n"
"1) /etc/machine-id exists and is populated.\n"
"2) /etc/machine-id exists and is empty.\n"
"3) /etc/machine-id is missing and /etc is writable.\n");
else
return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
log_error_errno(errno, "Cannot find %s: %m", etc_machine_id);
writable = false;
readable = false;
} else {
writable = false;
readable = true;
}

writable = false;
} else
} else {
writable = true;
readable = true;
}
}

/* A we got a valid machine ID argument, that's what counts */
if (sd_id128_is_null(machine_id)) {

/* Try to read any existing machine ID */
if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
if (readable && id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
return 0;

/* Hmm, so, the id currently stored is not useful, then let's generate one */
Expand Down Expand Up @@ -161,7 +166,9 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {

/* 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);
/* We failed to mount over /etc/machine-id, but let's leave /run/machine-id
* so we can commit it later if /etc/machine-id becomes writable
* (this will be done by systemd-machine-id-commit.service) */
return log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id);
}

Expand All @@ -180,70 +187,99 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *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, *run_machine_id;
sd_id128_t id;
int r;

/* 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. */

etc_machine_id = prefix_roota(root, "/etc/machine-id");
run_machine_id = prefix_roota(root, "/run/machine-id");

r = path_is_mount_point(etc_machine_id, NULL, 0);
if (r < 0)
if (r < 0 && errno != ENOENT)
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
if (r == 0) {
log_debug("%s is not a mount point. Nothing to do.", etc_machine_id);
if (r == 0 || ( r < 0 && errno == ENOENT) ) {
/* Read existing machine-id in /etc */
fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd > 0) {
r = id128_read_fd(fd, ID128_PLAIN, &id);
fd = safe_close(fd);
if (r == 0)
/* /etc/machine-id already contains a valid machine-id, don't do anything */
return 0;
}
if (fd < 0 && errno != ENOENT)
return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);


/* Read existing machine-id in /run */
fd = open(run_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Cannot open %s: %m", run_machine_id);

r = id128_read_fd(fd, ID128_PLAIN, &id);
if (r < 0)
return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);
fd = safe_close(fd);

/* Update a persistent version of etc_machine_id */
r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
if (r < 0)
return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);

return 0;
}
} else {
/* 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. */

/* Read existing machine-id */
fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);

r = fd_is_temporary_fs(fd);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EROFS),
"%s is not on a temporary file system.",
etc_machine_id);
/* Read existing machine-id */
fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);

r = id128_read_fd(fd, ID128_PLAIN, &id);
if (r < 0)
return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);
r = fd_is_temporary_fs(fd);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EROFS),
"%s is not on a temporary file system.",
etc_machine_id);

fd = safe_close(fd);
r = id128_read_fd(fd, ID128_PLAIN, &id);
if (r < 0)
return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);

/* Store current mount namespace */
r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Can't fetch current mount namespace: %m");
fd = safe_close(fd);

/* Store current mount namespace */
r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
if (r < 0)
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");
/* 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");
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");

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);
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);

/* Update a persistent version of etc_machine_id */
r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
if (r < 0)
return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
/* Update a persistent version of etc_machine_id */
r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
if (r < 0)
return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);

/* Return to initial namespace and proceed a lazy tmpfs unmount */
r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
if (r < 0)
return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);
/* Return to initial namespace and proceed a lazy tmpfs unmount */
r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
if (r < 0)
return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);

if (umount2(etc_machine_id, MNT_DETACH) < 0)
return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id);
if (umount2(etc_machine_id, MNT_DETACH) < 0)
return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id);

return 0;
return 0;
}
}
3 changes: 1 addition & 2 deletions units/systemd-machine-id-commit.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ DefaultDependencies=no
Conflicts=shutdown.target
Before=sysinit.target shutdown.target
After=local-fs.target
ConditionPathIsReadWrite=/etc
ConditionPathIsMountPoint=/etc/machine-id
ConditionFileNotEmpty=/run/machine-id

[Service]
Type=oneshot
Expand Down

0 comments on commit d86960d

Please sign in to comment.