chiark / gitweb /
rename basic.la to shared.la and put selinux deps in shared-selinx.la
[elogind.git] / src / 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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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/systemd/machine-id as a replacement */
239
240         mkdir_p("/run/systemd", 0755);
241
242         m = umask(0022);
243         r = write_one_line_file("/run/systemd/machine-id", id);
244         umask(m);
245
246         if (r < 0) {
247                 log_error("Cannot write /run/systemd/machine-id: %s", strerror(-r));
248
249                 unlink("/run/systemd/machine-id");
250                 goto finish;
251         }
252
253         /* And now, let's mount it over */
254         r = mount("/run/systemd/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0;
255         unlink("/run/systemd/machine-id");
256
257         if (r < 0)
258                 log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
259         else
260                 log_info("Installed transient /etc/machine-id file.");
261
262 finish:
263
264         if (fd >= 0)
265                 close_nointr_nofail(fd);
266
267         return r;
268 }