]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/machine-id-setup.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / core / machine-id-setup.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
d7ccca2e
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
d7ccca2e
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
d7ccca2e 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
d7ccca2e
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
d7ccca2e 21#include <fcntl.h>
618234a5 22#include <sched.h>
d7ccca2e 23#include <sys/mount.h>
618234a5 24#include <unistd.h>
d7ccca2e 25
618234a5 26#include "sd-id128.h"
81527be1 27
b5efdb8a 28#include "alloc-util.h"
3ffd4af2 29#include "fd-util.h"
f4f15635 30#include "fs-util.h"
910fd145 31#include "id128-util.h"
618234a5 32#include "log.h"
3ffd4af2 33#include "machine-id-setup.h"
d7ccca2e 34#include "macro.h"
49e942b2 35#include "mkdir.h"
4349cd7c 36#include "mount-util.h"
fe970a8a 37#include "path-util.h"
0b452006 38#include "process-util.h"
8fcde012 39#include "stat-util.h"
07630cea 40#include "string-util.h"
affb60b1 41#include "umask-util.h"
618234a5
LP
42#include "util.h"
43#include "virt.h"
8d41a963 44
4b1afed0 45static int generate_machine_id(const char *root, sd_id128_t *ret) {
75f86906 46 const char *dbus_machine_id;
4b1afed0
LP
47 _cleanup_close_ int fd = -1;
48 int r;
d7ccca2e 49
4b1afed0 50 assert(ret);
92f2f92e 51
d7ccca2e 52 /* First, try reading the D-Bus machine id, unless it is a symlink */
4b1afed0 53 dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
92f2f92e 54 fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
8d41a963 55 if (fd >= 0) {
4b1afed0 56 if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) {
c6ac7e4b
LP
57 log_info("Initializing machine ID from D-Bus machine ID.");
58 return 0;
d7ccca2e 59 }
4b1afed0
LP
60
61 fd = safe_close(fd);
d7ccca2e
LP
62 }
63
5dd6d0f8
LP
64 if (isempty(root)) {
65 /* If that didn't work, see if we are running in a container,
66 * and a machine ID was passed in via $container_uuid the way
67 * libvirt/LXC does it */
75f86906
LP
68
69 if (detect_container() > 0) {
5dd6d0f8 70 _cleanup_free_ char *e = NULL;
0b36bbc4 71
4b1afed0
LP
72 if (getenv_for_pid(1, "container_uuid", &e) > 0 &&
73 sd_id128_from_string(e, ret) >= 0) {
74 log_info("Initializing machine ID from container UUID.");
75 return 0;
0b36bbc4 76 }
5dd6d0f8 77
75f86906
LP
78 } else if (detect_vm() == VIRTUALIZATION_KVM) {
79
5dd6d0f8
LP
80 /* If we are not running in a container, see if we are
81 * running in qemu/kvm and a machine ID was passed in
82 * via -uuid on the qemu/kvm command line */
83
4b1afed0
LP
84 if (id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, ret) >= 0) {
85 log_info("Initializing machine ID from KVM UUID.");
86 return 0;
5dd6d0f8 87 }
0b36bbc4 88 }
d4eb120a
LP
89 }
90
d7ccca2e 91 /* If that didn't work, generate a random machine id */
4b1afed0 92 r = sd_id128_randomize(ret);
23bbb0de 93 if (r < 0)
4b1afed0 94 return log_error_errno(r, "Failed to generate randomized : %m");
d7ccca2e
LP
95
96 log_info("Initializing machine ID from random generator.");
d7ccca2e
LP
97 return 0;
98}
99
4b1afed0 100int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
c6ac7e4b
LP
101 const char *etc_machine_id, *run_machine_id;
102 _cleanup_close_ int fd = -1;
4b1afed0 103 bool writable;
c6ac7e4b 104 int r;
9496e375 105
f5e754e0 106 etc_machine_id = prefix_roota(root, "/etc/machine-id");
9496e375 107
c6ac7e4b
LP
108 RUN_WITH_UMASK(0000) {
109 /* We create this 0444, to indicate that this isn't really
110 * something you should ever modify. Of course, since the file
111 * will be owned by root it doesn't matter much, but maybe
112 * people look. */
9496e375 113
4b1afed0 114 (void) mkdir_parents(etc_machine_id, 0755);
c6ac7e4b
LP
115 fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
116 if (fd < 0) {
117 int old_errno = errno;
118
119 fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
120 if (fd < 0) {
121 if (old_errno == EROFS && errno == ENOENT)
122 log_error_errno(errno,
123 "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
124 "Booting up is supported only when:\n"
125 "1) /etc/machine-id exists and is populated.\n"
126 "2) /etc/machine-id exists and is empty.\n"
127 "3) /etc/machine-id is missing and /etc is writable.\n");
128 else
129 log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
130
131 return -errno;
132 }
9496e375 133
c6ac7e4b 134 writable = false;
4b1afed0
LP
135 } else
136 writable = true;
c6ac7e4b
LP
137 }
138
4b1afed0
LP
139 /* A we got a valid machine ID argument, that's what counts */
140 if (sd_id128_is_null(machine_id)) {
9496e375 141
4b1afed0
LP
142 /* Try to read any existing machine ID */
143 if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
144 return 0;
c6ac7e4b 145
4b1afed0
LP
146 /* Hmm, so, the id currently stored is not useful, then let's generate one */
147 r = generate_machine_id(root, &machine_id);
ee48dbd5
NC
148 if (r < 0)
149 return r;
fcb24270 150 }
4b1afed0 151
fcb24270 152 if (writable) {
4b1afed0 153 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
fcb24270
EV
154 return log_error_errno(errno, "Failed to seek %s: %m", etc_machine_id);
155
156 if (ftruncate(fd, 0) < 0)
157 return log_error_errno(errno, "Failed to truncate %s: %m", etc_machine_id);
c6ac7e4b 158
4b1afed0
LP
159 if (id128_write_fd(fd, ID128_PLAIN, machine_id, true) >= 0)
160 goto finish;
fcb24270 161 }
c6ac7e4b
LP
162
163 fd = safe_close(fd);
164
4b1afed0 165 /* Hmm, we couldn't write it? So let's write it to /run/machine-id as a replacement */
c6ac7e4b 166
4b1afed0
LP
167 run_machine_id = prefix_roota(root, "/run/machine-id");
168
169 RUN_WITH_UMASK(0022)
170 r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false);
171 if (r < 0) {
172 (void) unlink(run_machine_id);
173 return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
c6ac7e4b
LP
174 }
175
176 /* And now, let's mount it over */
177 if (mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL) < 0) {
178 (void) unlink_noerrno(run_machine_id);
179 return log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id);
180 }
181
182 log_info("Installed transient %s file.", etc_machine_id);
183
184 /* Mark the mount read-only */
185 if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
4b1afed0
LP
186 log_warning_errno(errno, "Failed to make transient %s read-only, ignoring: %m", etc_machine_id);
187
188finish:
189 if (ret)
190 *ret = machine_id;
c6ac7e4b
LP
191
192 return 0;
9496e375
DR
193}
194
979ef53a
DR
195int machine_id_commit(const char *root) {
196 _cleanup_close_ int fd = -1, initial_mntns_fd = -1;
197 const char *etc_machine_id;
15b1248a 198 sd_id128_t id;
979ef53a
DR
199 int r;
200
15b1248a
LP
201 /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
202 * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
203 * original mount namespace, thus revealing the file that was just created. */
204
f5e754e0 205 etc_machine_id = prefix_roota(root, "/etc/machine-id");
979ef53a 206
e1873695 207 r = path_is_mount_point(etc_machine_id, NULL, 0);
979ef53a 208 if (r < 0)
f131770b 209 return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
979ef53a 210 if (r == 0) {
61233823 211 log_debug("%s is not a mount point. Nothing to do.", etc_machine_id);
979ef53a
DR
212 return 0;
213 }
214
215 /* Read existing machine-id */
216 fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
217 if (fd < 0)
218 return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
219
c6878637 220 r = fd_is_temporary_fs(fd);
979ef53a
DR
221 if (r < 0)
222 return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
223 if (r == 0) {
224 log_error("%s is not on a temporary file system.", etc_machine_id);
225 return -EROFS;
226 }
227
15b1248a
LP
228 r = id128_read_fd(fd, ID128_PLAIN, &id);
229 if (r < 0)
230 return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id);
231
979ef53a
DR
232 fd = safe_close(fd);
233
234 /* Store current mount namespace */
671c3419 235 r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
979ef53a
DR
236 if (r < 0)
237 return log_error_errno(r, "Can't fetch current mount namespace: %m");
238
239 /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
240 if (unshare(CLONE_NEWNS) < 0)
241 return log_error_errno(errno, "Failed to enter new namespace: %m");
242
243 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
244 return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m");
245
246 if (umount(etc_machine_id) < 0)
247 return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id);
248
249 /* Update a persistent version of etc_machine_id */
15b1248a 250 r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
979ef53a 251 if (r < 0)
15b1248a 252 return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
979ef53a
DR
253
254 /* Return to initial namespace and proceed a lazy tmpfs unmount */
671c3419 255 r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
979ef53a
DR
256 if (r < 0)
257 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);
258
259 if (umount2(etc_machine_id, MNT_DETACH) < 0)
260 return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id);
261
262 return 0;
263}