]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/machine-id-setup.c
f3b1b318e1bad88825253293ceee81912091a028
[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
40 static int shorten_uuid(char destination[36], const char *source) {
41 unsigned i, j;
42
43 for (i = 0, j = 0; i < 36 && j < 32; i++) {
44 int t;
45
46 t = unhexchar(source[i]);
47 if (t < 0)
48 continue;
49
50 destination[j++] = hexchar(t);
51 }
52
53 if (i == 36 && j == 32) {
54 destination[32] = '\n';
55 destination[33] = 0;
56 return 0;
57 }
58
59 return -EINVAL;
60 }
61
62 static int generate(char id[34], const char *root) {
63 int fd, r;
64 unsigned char *p;
65 sd_id128_t buf;
66 char *q;
67 ssize_t k;
68 const char *vm_id;
69 _cleanup_free_ char *dbus_machine_id = NULL;
70
71 assert(id);
72
73 if (asprintf(&dbus_machine_id, "%s/var/lib/dbus/machine-id", root) < 0)
74 return log_oom();
75
76 /* First, try reading the D-Bus machine id, unless it is a symlink */
77 fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
78 if (fd >= 0) {
79 k = loop_read(fd, id, 33, false);
80 close_nointr_nofail(fd);
81
82 if (k == 33 && id[32] == '\n') {
83
84 id[32] = 0;
85 if (id128_is_valid(id)) {
86 id[32] = '\n';
87 id[33] = 0;
88
89 log_info("Initializing machine ID from D-Bus machine ID.");
90 return 0;
91 }
92 }
93 }
94
95 /* If that didn't work, see if we are running in qemu/kvm and a
96 * machine ID was passed in via -uuid on the qemu/kvm command
97 * line */
98
99 r = detect_vm(&vm_id);
100 if (r > 0 && streq(vm_id, "kvm")) {
101 char uuid[37];
102
103 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
104 if (fd >= 0) {
105 k = loop_read(fd, uuid, 36, false);
106 close_nointr_nofail(fd);
107
108 if (k >= 36) {
109 r = shorten_uuid(id, uuid);
110 if (r >= 0) {
111 log_info("Initializing machine ID from KVM UUID.");
112 return 0;
113 }
114 }
115 }
116 }
117
118 /* If that didn't work either, see if we are running in a
119 * container, and a machine ID was passed in via
120 * $container_uuid the way libvirt/LXC does it */
121 r = detect_container(NULL);
122 if (r > 0) {
123 _cleanup_free_ char *e = NULL;
124
125 r = getenv_for_pid(1, "container_uuid", &e);
126 if (r > 0) {
127 if (strlen(e) >= 36) {
128 r = shorten_uuid(id, e);
129 if (r >= 0) {
130 log_info("Initializing machine ID from container UUID.");
131 return 0;
132 }
133 }
134 }
135 }
136
137 /* If that didn't work, generate a random machine id */
138 r = sd_id128_randomize(&buf);
139 if (r < 0) {
140 log_error("Failed to open /dev/urandom: %s", strerror(-r));
141 return r;
142 }
143
144 for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
145 q[0] = hexchar(*p >> 4);
146 q[1] = hexchar(*p & 15);
147 }
148
149 id[32] = '\n';
150 id[33] = 0;
151
152 log_info("Initializing machine ID from random generator.");
153
154 return 0;
155 }
156
157 int machine_id_setup(const char *root) {
158 _cleanup_close_ int fd = -1;
159 int r;
160 bool writable = false;
161 struct stat st;
162 char id[34]; /* 32 + \n + \0 */
163 _cleanup_free_ char *etc_machine_id = NULL;
164 _cleanup_free_ char *run_machine_id = NULL;
165
166 if (asprintf(&etc_machine_id, "%s/etc/machine-id", root) < 0)
167 return log_oom();
168
169 if (asprintf(&run_machine_id, "%s/run/machine-id", root) < 0)
170 return log_oom();
171
172 RUN_WITH_UMASK(0000) {
173 /* We create this 0444, to indicate that this isn't really
174 * something you should ever modify. Of course, since the file
175 * will be owned by root it doesn't matter much, but maybe
176 * people look. */
177
178 fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
179 if (fd >= 0)
180 writable = true;
181 else {
182 fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
183 if (fd < 0) {
184 log_error("Cannot open %s: %m", etc_machine_id);
185 return -errno;
186 }
187
188 writable = false;
189 }
190 }
191
192 if (fstat(fd, &st) < 0) {
193 log_error("fstat() failed: %m");
194 return -errno;
195 }
196
197 if (S_ISREG(st.st_mode))
198 if (loop_read(fd, id, 33, false) == 33 && id[32] == '\n') {
199 id[32] = 0;
200
201 if (id128_is_valid(id))
202 return 0;
203 }
204
205 /* Hmm, so, the id currently stored is not useful, then let's
206 * generate one */
207
208 r = generate(id, root);
209 if (r < 0)
210 return r;
211
212 if (S_ISREG(st.st_mode) && writable) {
213 lseek(fd, 0, SEEK_SET);
214
215 if (loop_write(fd, id, 33, false) == 33)
216 return 0;
217 }
218
219 close_nointr_nofail(fd);
220 fd = -1;
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 }