]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/machine-id-setup.c
Merge pull request #7376 from keszybz/simplify-root-options
[thirdparty/systemd.git] / src / core / machine-id-setup.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
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
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
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <fcntl.h>
22 #include <sched.h>
23 #include <sys/mount.h>
24 #include <unistd.h>
25
26 #include "sd-id128.h"
27
28 #include "alloc-util.h"
29 #include "fd-util.h"
30 #include "fs-util.h"
31 #include "id128-util.h"
32 #include "log.h"
33 #include "machine-id-setup.h"
34 #include "macro.h"
35 #include "mkdir.h"
36 #include "mount-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "stat-util.h"
40 #include "string-util.h"
41 #include "umask-util.h"
42 #include "util.h"
43 #include "virt.h"
44
45 static int generate_machine_id(const char *root, sd_id128_t *ret) {
46 const char *dbus_machine_id;
47 _cleanup_close_ int fd = -1;
48 int r;
49
50 assert(ret);
51
52 /* First, try reading the D-Bus machine id, unless it is a symlink */
53 dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
54 fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
55 if (fd >= 0) {
56 if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) {
57 log_info("Initializing machine ID from D-Bus machine ID.");
58 return 0;
59 }
60
61 fd = safe_close(fd);
62 }
63
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 */
68
69 if (detect_container() > 0) {
70 _cleanup_free_ char *e = NULL;
71
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;
76 }
77
78 } else if (detect_vm() == VIRTUALIZATION_KVM) {
79
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
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;
87 }
88 }
89 }
90
91 /* If that didn't work, generate a random machine id */
92 r = sd_id128_randomize(ret);
93 if (r < 0)
94 return log_error_errno(r, "Failed to generate randomized : %m");
95
96 log_info("Initializing machine ID from random generator.");
97 return 0;
98 }
99
100 int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
101 const char *etc_machine_id, *run_machine_id;
102 _cleanup_close_ int fd = -1;
103 bool writable;
104 int r;
105
106 etc_machine_id = prefix_roota(root, "/etc/machine-id");
107
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. */
113
114 (void) mkdir_parents(etc_machine_id, 0755);
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 }
133
134 writable = false;
135 } else
136 writable = true;
137 }
138
139 /* A we got a valid machine ID argument, that's what counts */
140 if (sd_id128_is_null(machine_id)) {
141
142 /* Try to read any existing machine ID */
143 if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
144 return 0;
145
146 /* Hmm, so, the id currently stored is not useful, then let's generate one */
147 r = generate_machine_id(root, &machine_id);
148 if (r < 0)
149 return r;
150 }
151
152 if (writable) {
153 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
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);
158
159 if (id128_write_fd(fd, ID128_PLAIN, machine_id, true) >= 0)
160 goto finish;
161 }
162
163 fd = safe_close(fd);
164
165 /* Hmm, we couldn't write it? So let's write it to /run/machine-id as a replacement */
166
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);
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)
186 log_warning_errno(errno, "Failed to make transient %s read-only, ignoring: %m", etc_machine_id);
187
188 finish:
189 if (ret)
190 *ret = machine_id;
191
192 return 0;
193 }
194
195 int machine_id_commit(const char *root) {
196 _cleanup_close_ int fd = -1, initial_mntns_fd = -1;
197 const char *etc_machine_id;
198 sd_id128_t id;
199 int r;
200
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
205 etc_machine_id = prefix_roota(root, "/etc/machine-id");
206
207 r = path_is_mount_point(etc_machine_id, NULL, 0);
208 if (r < 0)
209 return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
210 if (r == 0) {
211 log_debug("%s is not a mount point. Nothing to do.", etc_machine_id);
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
220 r = fd_is_temporary_fs(fd);
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
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
232 fd = safe_close(fd);
233
234 /* Store current mount namespace */
235 r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
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 */
250 r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
251 if (r < 0)
252 return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
253
254 /* Return to initial namespace and proceed a lazy tmpfs unmount */
255 r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
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 }