]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/machine-id-setup.c
util-lib: split out IO related calls to io-util.[ch]
[thirdparty/systemd.git] / src / core / machine-id-setup.c
CommitLineData
d7ccca2e
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
d7ccca2e
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
d7ccca2e 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
d7ccca2e
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
d7ccca2e 22#include <errno.h>
d7ccca2e 23#include <fcntl.h>
618234a5
LP
24#include <sched.h>
25#include <stdio.h>
26#include <string.h>
d7ccca2e 27#include <sys/mount.h>
618234a5 28#include <unistd.h>
d7ccca2e 29
618234a5 30#include "sd-id128.h"
81527be1 31
3ffd4af2 32#include "fd-util.h"
618234a5 33#include "fileio.h"
c004493c 34#include "io-util.h"
618234a5 35#include "log.h"
3ffd4af2 36#include "machine-id-setup.h"
d7ccca2e 37#include "macro.h"
49e942b2 38#include "mkdir.h"
fe970a8a 39#include "path-util.h"
0b452006 40#include "process-util.h"
07630cea 41#include "string-util.h"
618234a5
LP
42#include "util.h"
43#include "virt.h"
8d41a963 44
34f750b7 45static int shorten_uuid(char destination[34], const char source[36]) {
09b967ea
LP
46 unsigned i, j;
47
c6ac7e4b
LP
48 assert(destination);
49 assert(source);
50
51 /* Converts a UUID into a machine ID, by lowercasing it and
52 * removing dashes. Validates everything. */
53
09b967ea
LP
54 for (i = 0, j = 0; i < 36 && j < 32; i++) {
55 int t;
56
57 t = unhexchar(source[i]);
58 if (t < 0)
59 continue;
60
61 destination[j++] = hexchar(t);
62 }
63
c6ac7e4b
LP
64 if (i != 36 || j != 32)
65 return -EINVAL;
66
67 destination[32] = '\n';
68 destination[33] = 0;
69 return 0;
70}
71
72static int read_machine_id(int fd, char id[34]) {
73 char id_to_validate[34];
74 int r;
75
76 assert(fd >= 0);
77 assert(id);
78
79 /* Reads a machine ID from a file, validates it, and returns
80 * it. The returned ID ends in a newline. */
81
82 r = loop_read_exact(fd, id_to_validate, 33, false);
83 if (r < 0)
84 return r;
85
86 if (id_to_validate[32] != '\n')
87 return -EINVAL;
88
89 id_to_validate[32] = 0;
90
91 if (!id128_is_valid(id_to_validate))
92 return -EINVAL;
93
94 memcpy(id, id_to_validate, 32);
95 id[32] = '\n';
96 id[33] = 0;
97 return 0;
98}
09b967ea 99
c6ac7e4b
LP
100static int write_machine_id(int fd, char id[34]) {
101 assert(fd >= 0);
102 assert(id);
103
104 if (lseek(fd, 0, SEEK_SET) < 0)
105 return -errno;
106
107 return loop_write(fd, id, 33, false);
09b967ea
LP
108}
109
c6ac7e4b 110static int generate_machine_id(char id[34], const char *root) {
87d2c1ff
LP
111 int fd, r;
112 unsigned char *p;
113 sd_id128_t buf;
5dd6d0f8 114 char *q;
75f86906 115 const char *dbus_machine_id;
d7ccca2e
LP
116
117 assert(id);
118
5dd6d0f8
LP
119 if (isempty(root))
120 dbus_machine_id = "/var/lib/dbus/machine-id";
121 else
63c372cb 122 dbus_machine_id = strjoina(root, "/var/lib/dbus/machine-id");
92f2f92e 123
d7ccca2e 124 /* First, try reading the D-Bus machine id, unless it is a symlink */
92f2f92e 125 fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
8d41a963 126 if (fd >= 0) {
c6ac7e4b 127 r = read_machine_id(fd, id);
03e334a1 128 safe_close(fd);
d7ccca2e 129
c6ac7e4b
LP
130 if (r >= 0) {
131 log_info("Initializing machine ID from D-Bus machine ID.");
132 return 0;
d7ccca2e
LP
133 }
134 }
135
5dd6d0f8
LP
136 if (isempty(root)) {
137 /* If that didn't work, see if we are running in a container,
138 * and a machine ID was passed in via $container_uuid the way
139 * libvirt/LXC does it */
75f86906
LP
140
141 if (detect_container() > 0) {
5dd6d0f8 142 _cleanup_free_ char *e = NULL;
0b36bbc4 143
5dd6d0f8
LP
144 r = getenv_for_pid(1, "container_uuid", &e);
145 if (r > 0) {
c6ac7e4b
LP
146 r = shorten_uuid(id, e);
147 if (r >= 0) {
148 log_info("Initializing machine ID from container UUID.");
149 return 0;
0b36bbc4
LP
150 }
151 }
5dd6d0f8 152
75f86906
LP
153 } else if (detect_vm() == VIRTUALIZATION_KVM) {
154
5dd6d0f8
LP
155 /* If we are not running in a container, see if we are
156 * running in qemu/kvm and a machine ID was passed in
157 * via -uuid on the qemu/kvm command line */
158
75f86906 159 char uuid[36];
5dd6d0f8 160
75f86906
LP
161 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
162 if (fd >= 0) {
163 r = loop_read_exact(fd, uuid, 36, false);
164 safe_close(fd);
5dd6d0f8 165
75f86906
LP
166 if (r >= 0) {
167 r = shorten_uuid(id, uuid);
a6dcc7e5 168 if (r >= 0) {
75f86906
LP
169 log_info("Initializing machine ID from KVM UUID.");
170 return 0;
5dd6d0f8
LP
171 }
172 }
173 }
0b36bbc4 174 }
d4eb120a
LP
175 }
176
d7ccca2e 177 /* If that didn't work, generate a random machine id */
87d2c1ff 178 r = sd_id128_randomize(&buf);
23bbb0de
MS
179 if (r < 0)
180 return log_error_errno(r, "Failed to open /dev/urandom: %m");
d7ccca2e 181
87d2c1ff 182 for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
d7ccca2e
LP
183 q[0] = hexchar(*p >> 4);
184 q[1] = hexchar(*p & 15);
185 }
186
187 id[32] = '\n';
188 id[33] = 0;
189
190 log_info("Initializing machine ID from random generator.");
191
192 return 0;
193}
194
c6ac7e4b
LP
195int machine_id_setup(const char *root) {
196 const char *etc_machine_id, *run_machine_id;
197 _cleanup_close_ int fd = -1;
198 bool writable = true;
199 char id[34]; /* 32 + \n + \0 */
200 int r;
9496e375 201
c6ac7e4b
LP
202 if (isempty(root)) {
203 etc_machine_id = "/etc/machine-id";
204 run_machine_id = "/run/machine-id";
205 } else {
206 char *x;
9496e375 207
c6ac7e4b
LP
208 x = strjoina(root, "/etc/machine-id");
209 etc_machine_id = path_kill_slashes(x);
9496e375 210
c6ac7e4b
LP
211 x = strjoina(root, "/run/machine-id");
212 run_machine_id = path_kill_slashes(x);
9496e375
DR
213 }
214
c6ac7e4b
LP
215 RUN_WITH_UMASK(0000) {
216 /* We create this 0444, to indicate that this isn't really
217 * something you should ever modify. Of course, since the file
218 * will be owned by root it doesn't matter much, but maybe
219 * people look. */
9496e375 220
c6ac7e4b
LP
221 mkdir_parents(etc_machine_id, 0755);
222 fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
223 if (fd < 0) {
224 int old_errno = errno;
225
226 fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
227 if (fd < 0) {
228 if (old_errno == EROFS && errno == ENOENT)
229 log_error_errno(errno,
230 "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
231 "Booting up is supported only when:\n"
232 "1) /etc/machine-id exists and is populated.\n"
233 "2) /etc/machine-id exists and is empty.\n"
234 "3) /etc/machine-id is missing and /etc is writable.\n");
235 else
236 log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
237
238 return -errno;
239 }
9496e375 240
c6ac7e4b
LP
241 writable = false;
242 }
243 }
244
245 if (read_machine_id(fd, id) >= 0)
9496e375
DR
246 return 0;
247
c6ac7e4b
LP
248 /* Hmm, so, the id currently stored is not useful, then let's
249 * generate one */
250
251 r = generate_machine_id(id, root);
252 if (r < 0)
253 return r;
254
255 if (writable)
256 if (write_machine_id(fd, id) >= 0)
257 return 0;
258
259 fd = safe_close(fd);
260
261 /* Hmm, we couldn't write it? So let's write it to
262 * /run/machine-id as a replacement */
263
264 RUN_WITH_UMASK(0022) {
4c1fc3e4 265 r = write_string_file(run_machine_id, id, WRITE_STRING_FILE_CREATE);
c6ac7e4b
LP
266 }
267 if (r < 0) {
268 (void) unlink(run_machine_id);
269 return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
270 }
271
272 /* And now, let's mount it over */
273 if (mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL) < 0) {
274 (void) unlink_noerrno(run_machine_id);
275 return log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id);
276 }
277
278 log_info("Installed transient %s file.", etc_machine_id);
279
280 /* Mark the mount read-only */
281 if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
282 log_warning_errno(errno, "Failed to make transient %s read-only: %m", etc_machine_id);
283
284 return 0;
9496e375
DR
285}
286
979ef53a
DR
287int machine_id_commit(const char *root) {
288 _cleanup_close_ int fd = -1, initial_mntns_fd = -1;
289 const char *etc_machine_id;
290 char id[34]; /* 32 + \n + \0 */
291 int r;
292
293 if (isempty(root))
294 etc_machine_id = "/etc/machine-id";
295 else {
296 char *x;
297
63c372cb 298 x = strjoina(root, "/etc/machine-id");
979ef53a
DR
299 etc_machine_id = path_kill_slashes(x);
300 }
301
e26d6ce5 302 r = path_is_mount_point(etc_machine_id, 0);
979ef53a 303 if (r < 0)
f131770b 304 return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
979ef53a
DR
305 if (r == 0) {
306 log_debug("%s is is not a mount point. Nothing to do.", etc_machine_id);
307 return 0;
308 }
309
310 /* Read existing machine-id */
311 fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
312 if (fd < 0)
313 return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
314
c6ac7e4b 315 r = read_machine_id(fd, id);
979ef53a
DR
316 if (r < 0)
317 return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id);
318
c6878637 319 r = fd_is_temporary_fs(fd);
979ef53a
DR
320 if (r < 0)
321 return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
322 if (r == 0) {
323 log_error("%s is not on a temporary file system.", etc_machine_id);
324 return -EROFS;
325 }
326
327 fd = safe_close(fd);
328
329 /* Store current mount namespace */
671c3419 330 r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
979ef53a
DR
331 if (r < 0)
332 return log_error_errno(r, "Can't fetch current mount namespace: %m");
333
334 /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
335 if (unshare(CLONE_NEWNS) < 0)
336 return log_error_errno(errno, "Failed to enter new namespace: %m");
337
338 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
339 return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m");
340
341 if (umount(etc_machine_id) < 0)
342 return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id);
343
344 /* Update a persistent version of etc_machine_id */
345 fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
346 if (fd < 0)
347 return log_error_errno(errno, "Cannot open for writing %s. This is mandatory to get a persistent machine-id: %m", etc_machine_id);
348
349 r = write_machine_id(fd, id);
350 if (r < 0)
351 return log_error_errno(r, "Cannot write %s: %m", etc_machine_id);
352
353 fd = safe_close(fd);
354
355 /* Return to initial namespace and proceed a lazy tmpfs unmount */
671c3419 356 r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
979ef53a
DR
357 if (r < 0)
358 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);
359
360 if (umount2(etc_machine_id, MNT_DETACH) < 0)
361 return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id);
362
363 return 0;
364}