chiark / gitweb /
udev: keymap - remove weird 'c2 a0' character sequences which break the check
[elogind.git] / src / shared / 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
39 static int shorten_uuid(char destination[36], const char *source) {
40         unsigned i, j;
41
42         for (i = 0, j = 0; i < 36 && j < 32; i++) {
43                 int t;
44
45                 t = unhexchar(source[i]);
46                 if (t < 0)
47                         continue;
48
49                 destination[j++] = hexchar(t);
50         }
51
52         if (i == 36 && j == 32) {
53                 destination[32] = '\n';
54                 destination[33] = 0;
55                 return 0;
56         }
57
58         return -EINVAL;
59 }
60
61 static int generate(char id[34]) {
62         int fd, r;
63         unsigned char *p;
64         sd_id128_t buf;
65         char *q;
66         ssize_t k;
67         const char *vm_id;
68
69         assert(id);
70
71         /* First, try reading the D-Bus machine id, unless it is a symlink */
72         fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
73         if (fd >= 0) {
74
75                 k = loop_read(fd, id, 32, false);
76                 close_nointr_nofail(fd);
77
78                 if (k >= 32) {
79                         id[32] = '\n';
80                         id[33] = 0;
81
82                         log_info("Initializing machine ID from D-Bus machine ID.");
83                         return 0;
84                 }
85         }
86
87         /* If that didn't work, see if we are running in qemu/kvm and a
88          * machine ID was passed in via -uuid on the qemu/kvm command
89          * line */
90
91         r = detect_vm(&vm_id);
92         if (r > 0 && streq(vm_id, "kvm")) {
93                 char uuid[37];
94
95                 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
96                 if (fd >= 0) {
97                         k = loop_read(fd, uuid, 36, false);
98                         close_nointr_nofail(fd);
99
100                         if (k >= 36) {
101                                 r = shorten_uuid(id, uuid);
102                                 if (r >= 0) {
103                                         log_info("Initializing machine ID from KVM UUID");
104                                         return 0;
105                                 }
106                         }
107                 }
108         }
109
110         /* If that didn't work either, see if we are running in a
111          * container, and a machine ID was passed in via
112          * $container_uuid the way libvirt/LXC does it */
113
114         r = detect_container(NULL);
115         if (r > 0) {
116                 FILE *f;
117
118                 f = fopen("/proc/1/environ", "re");
119                 if (f) {
120                         bool done = false;
121
122                         do {
123                                 char line[LINE_MAX];
124                                 unsigned i;
125
126                                 for (i = 0; i < sizeof(line)-1; i++) {
127                                         int c;
128
129                                         c = getc(f);
130                                         if (_unlikely_(c == EOF)) {
131                                                 done = true;
132                                                 break;
133                                         } else if (c == 0)
134                                                 break;
135
136                                         line[i] = c;
137                                 }
138                                 line[i] = 0;
139
140                                 if (startswith(line, "container_uuid=") &&
141                                     strlen(line + 15) >= 36) {
142                                         r = shorten_uuid(id, line + 15);
143                                         if (r >= 0) {
144                                                 log_info("Initializing machine ID from container UUID");
145                                                 return 0;
146                                         }
147                                 }
148
149                         } while (!done);
150
151                         fclose(f);
152                 }
153         }
154
155         /* If that didn't work, generate a random machine id */
156         r = sd_id128_randomize(&buf);
157         if (r < 0) {
158                 log_error("Failed to open /dev/urandom: %s", strerror(-r));
159                 return r;
160         }
161
162         for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
163                 q[0] = hexchar(*p >> 4);
164                 q[1] = hexchar(*p & 15);
165         }
166
167         id[32] = '\n';
168         id[33] = 0;
169
170         log_info("Initializing machine ID from random generator.");
171
172         return 0;
173 }
174
175 int machine_id_setup(void) {
176         int fd, r;
177         bool writable;
178         struct stat st;
179         char id[34]; /* 32 + \n + \0 */
180         mode_t m;
181
182         m = umask(0000);
183
184         /* We create this 0444, to indicate that this isn't really
185          * something you should ever modify. Of course, since the file
186          * will be owned by root it doesn't matter much, but maybe
187          * people look. */
188
189         fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
190         if (fd >= 0)
191                 writable = true;
192         else {
193                 fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
194                 if (fd < 0) {
195                         umask(m);
196                         log_error("Cannot open /etc/machine-id: %m");
197                         return -errno;
198                 }
199
200                 writable = false;
201         }
202
203         umask(m);
204
205         if (fstat(fd, &st) < 0) {
206                 log_error("fstat() failed: %m");
207                 r = -errno;
208                 goto finish;
209         }
210
211         if (S_ISREG(st.st_mode)) {
212                 if (loop_read(fd, id, 32, false) >= 32) {
213                         r = 0;
214                         goto finish;
215                 }
216         }
217
218         /* Hmm, so, the id currently stored is not useful, then let's
219          * generate one */
220
221         r = generate(id);
222         if (r < 0)
223                 goto finish;
224
225         if (S_ISREG(st.st_mode) && writable) {
226                 lseek(fd, 0, SEEK_SET);
227
228                 if (loop_write(fd, id, 33, false) == 33) {
229                         r = 0;
230                         goto finish;
231                 }
232         }
233
234         close_nointr_nofail(fd);
235         fd = -1;
236
237         /* Hmm, we couldn't write it? So let's write it to
238          * /run/machine-id as a replacement */
239
240         m = umask(0022);
241         r = write_one_line_file("/run/machine-id", id);
242         umask(m);
243
244         if (r < 0) {
245                 log_error("Cannot write /run/machine-id: %s", strerror(-r));
246
247                 unlink("/run/machine-id");
248                 goto finish;
249         }
250
251         /* And now, let's mount it over */
252         r = mount("/run/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0;
253         if (r < 0) {
254                 unlink("/run/machine-id");
255                 log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
256         } else
257                 log_info("Installed transient /etc/machine-id file.");
258
259 finish:
260
261         if (fd >= 0)
262                 close_nointr_nofail(fd);
263
264         return r;
265 }