]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/machine-id-setup.c
machine-id: only look into KVM uuid when we are not running in a
[thirdparty/systemd.git] / src / core / machine-id-setup.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <sys/mount.h>
29
30 #include <systemd/sd-id128.h>
31
32 #include "machine-id-setup.h"
33 #include "macro.h"
34 #include "util.h"
35 #include "mkdir.h"
36 #include "log.h"
37 #include "virt.h"
38 #include "fileio.h"
39 #include "path-util.h"
40
41 static int shorten_uuid(char destination[36], const char *source) {
42 unsigned i, j;
43
44 for (i = 0, j = 0; i < 36 && j < 32; i++) {
45 int t;
46
47 t = unhexchar(source[i]);
48 if (t < 0)
49 continue;
50
51 destination[j++] = hexchar(t);
52 }
53
54 if (i == 36 && j == 32) {
55 destination[32] = '\n';
56 destination[33] = 0;
57 return 0;
58 }
59
60 return -EINVAL;
61 }
62
63 static int generate(char id[34], const char *root) {
64 int fd, r;
65 unsigned char *p;
66 sd_id128_t buf;
67 char *q;
68 ssize_t k;
69 const char *vm_id;
70 _cleanup_free_ char *dbus_machine_id = NULL;
71
72 assert(id);
73
74 if (asprintf(&dbus_machine_id, "%s/var/lib/dbus/machine-id", root) < 0)
75 return log_oom();
76
77 /* First, try reading the D-Bus machine id, unless it is a symlink */
78 fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
79 if (fd >= 0) {
80 k = loop_read(fd, id, 33, false);
81 safe_close(fd);
82
83 if (k == 33 && id[32] == '\n') {
84
85 id[32] = 0;
86 if (id128_is_valid(id)) {
87 id[32] = '\n';
88 id[33] = 0;
89
90 log_info("Initializing machine ID from D-Bus machine ID.");
91 return 0;
92 }
93 }
94 }
95
96 /* If that didn't work, see if we are running in a container,
97 * and a machine ID was passed in via $container_uuid the way
98 * libvirt/LXC does it */
99 r = detect_container(NULL);
100 if (r > 0) {
101 _cleanup_free_ char *e = NULL;
102
103 r = getenv_for_pid(1, "container_uuid", &e);
104 if (r > 0) {
105 if (strlen(e) >= 36) {
106 r = shorten_uuid(id, e);
107 if (r >= 0) {
108 log_info("Initializing machine ID from container UUID.");
109 return 0;
110 }
111 }
112 }
113
114 } else {
115 /* If we are not running in a container, see if we are
116 * running in qemu/kvm and a machine ID was passed in
117 * via -uuid on the qemu/kvm command line */
118
119 r = detect_vm(&vm_id);
120 if (r > 0 && streq(vm_id, "kvm")) {
121 char uuid[37];
122
123 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
124 if (fd >= 0) {
125 k = loop_read(fd, uuid, 36, false);
126 safe_close(fd);
127
128 if (k >= 36) {
129 r = shorten_uuid(id, uuid);
130 if (r >= 0) {
131 log_info("Initializing machine ID from KVM UUID.");
132 return 0;
133 }
134 }
135 }
136 }
137 }
138
139 /* If that didn't work, generate a random machine id */
140 r = sd_id128_randomize(&buf);
141 if (r < 0) {
142 log_error("Failed to open /dev/urandom: %s", strerror(-r));
143 return r;
144 }
145
146 for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
147 q[0] = hexchar(*p >> 4);
148 q[1] = hexchar(*p & 15);
149 }
150
151 id[32] = '\n';
152 id[33] = 0;
153
154 log_info("Initializing machine ID from random generator.");
155
156 return 0;
157 }
158
159 int machine_id_setup(const char *root) {
160 _cleanup_close_ int fd = -1;
161 int r;
162 bool writable = false;
163 struct stat st;
164 char id[34]; /* 32 + \n + \0 */
165 char *etc_machine_id, *run_machine_id;
166
167 etc_machine_id = strappenda(root, "/etc/machine-id");
168 path_kill_slashes(etc_machine_id);
169
170 run_machine_id = strappenda(root, "/run/machine-id");
171 path_kill_slashes(run_machine_id);
172
173 RUN_WITH_UMASK(0000) {
174 /* We create this 0444, to indicate that this isn't really
175 * something you should ever modify. Of course, since the file
176 * will be owned by root it doesn't matter much, but maybe
177 * people look. */
178
179 fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
180 if (fd >= 0)
181 writable = true;
182 else {
183 fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
184 if (fd < 0) {
185 log_error("Cannot open %s: %m", etc_machine_id);
186 return -errno;
187 }
188
189 writable = false;
190 }
191 }
192
193 if (fstat(fd, &st) < 0) {
194 log_error("fstat() failed: %m");
195 return -errno;
196 }
197
198 if (S_ISREG(st.st_mode))
199 if (loop_read(fd, id, 33, false) == 33 && id[32] == '\n') {
200 id[32] = 0;
201
202 if (id128_is_valid(id))
203 return 0;
204 }
205
206 /* Hmm, so, the id currently stored is not useful, then let's
207 * generate one */
208
209 r = generate(id, root);
210 if (r < 0)
211 return r;
212
213 if (S_ISREG(st.st_mode) && writable) {
214 lseek(fd, 0, SEEK_SET);
215
216 if (loop_write(fd, id, 33, false) == 33)
217 return 0;
218 }
219
220 fd = safe_close(fd);
221
222 /* Hmm, we couldn't write it? So let's write it to
223 * /run/machine-id as a replacement */
224
225 RUN_WITH_UMASK(0022) {
226 r = write_string_file(run_machine_id, id);
227 }
228 if (r < 0) {
229 log_error("Cannot write %s: %s", run_machine_id, strerror(-r));
230 unlink(run_machine_id);
231 return r;
232 }
233
234 /* And now, let's mount it over */
235 r = mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL);
236 if (r < 0) {
237 log_error("Failed to mount %s: %m", etc_machine_id);
238 unlink_noerrno(run_machine_id);
239 return -errno;
240 }
241
242 log_info("Installed transient %s file.", etc_machine_id);
243
244 /* Mark the mount read-only */
245 if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
246 log_warning("Failed to make transient %s read-only: %m", etc_machine_id);
247
248 return 0;
249 }