chiark / gitweb /
Report about syntax errors with metadata
[elogind.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]) {
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
70         assert(id);
71
72         /* First, try reading the D-Bus machine id, unless it is a symlink */
73         fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
74         if (fd >= 0) {
75
76                 k = loop_read(fd, id, 32, false);
77                 close_nointr_nofail(fd);
78
79                 if (k >= 32) {
80                         id[32] = '\n';
81                         id[33] = 0;
82
83                         log_info("Initializing machine ID from D-Bus machine ID.");
84                         return 0;
85                 }
86         }
87
88         /* If that didn't work, see if we are running in qemu/kvm and a
89          * machine ID was passed in via -uuid on the qemu/kvm command
90          * line */
91
92         r = detect_vm(&vm_id);
93         if (r > 0 && streq(vm_id, "kvm")) {
94                 char uuid[37];
95
96                 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
97                 if (fd >= 0) {
98                         k = loop_read(fd, uuid, 36, false);
99                         close_nointr_nofail(fd);
100
101                         if (k >= 36) {
102                                 r = shorten_uuid(id, uuid);
103                                 if (r >= 0) {
104                                         log_info("Initializing machine ID from KVM UUID.");
105                                         return 0;
106                                 }
107                         }
108                 }
109         }
110
111         /* If that didn't work either, see if we are running in a
112          * container, and a machine ID was passed in via
113          * $container_uuid the way libvirt/LXC does it */
114         r = detect_container(NULL);
115         if (r > 0) {
116                 char *e;
117
118                 r = getenv_for_pid(1, "container_uuid", &e);
119                 if (r > 0) {
120                         if (strlen(e) >= 36) {
121                                 r = shorten_uuid(id, e);
122                                 if (r >= 0) {
123                                         log_info("Initializing machine ID from container UUID.");
124                                         free(e);
125                                         return 0;
126                                 }
127                         }
128
129                         free(e);
130                 }
131         }
132
133         /* If that didn't work, generate a random machine id */
134         r = sd_id128_randomize(&buf);
135         if (r < 0) {
136                 log_error("Failed to open /dev/urandom: %s", strerror(-r));
137                 return r;
138         }
139
140         for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
141                 q[0] = hexchar(*p >> 4);
142                 q[1] = hexchar(*p & 15);
143         }
144
145         id[32] = '\n';
146         id[33] = 0;
147
148         log_info("Initializing machine ID from random generator.");
149
150         return 0;
151 }
152
153 int machine_id_setup(void) {
154         _cleanup_close_ int fd = -1;
155         int r;
156         bool writable;
157         struct stat st;
158         char id[34]; /* 32 + \n + \0 */
159
160         RUN_WITH_UMASK(0000) {
161                 /* We create this 0444, to indicate that this isn't really
162                  * something you should ever modify. Of course, since the file
163                  * will be owned by root it doesn't matter much, but maybe
164                  * people look. */
165
166                 fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
167                 if (fd >= 0)
168                         writable = true;
169                 else {
170                         fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
171                         if (fd < 0) {
172                                 log_error("Cannot open /etc/machine-id: %m");
173                                 return -errno;
174                         }
175
176                         writable = false;
177                 }
178         }
179
180         if (fstat(fd, &st) < 0) {
181                 log_error("fstat() failed: %m");
182                 return -errno;
183         }
184
185         if (S_ISREG(st.st_mode))
186                 if (loop_read(fd, id, 32, false) >= 32)
187                         return 0;
188
189         /* Hmm, so, the id currently stored is not useful, then let's
190          * generate one */
191
192         r = generate(id);
193         if (r < 0)
194                 return r;
195
196         if (S_ISREG(st.st_mode) && writable) {
197                 lseek(fd, 0, SEEK_SET);
198
199                 if (loop_write(fd, id, 33, false) == 33)
200                         return 0;
201         }
202
203         close_nointr_nofail(fd);
204         fd = -1;
205
206         /* Hmm, we couldn't write it? So let's write it to
207          * /run/machine-id as a replacement */
208
209         RUN_WITH_UMASK(0022) {
210                 r = write_string_file("/run/machine-id", id);
211         }
212         if (r < 0) {
213                 log_error("Cannot write /run/machine-id: %s", strerror(-r));
214                 unlink("/run/machine-id");
215                 return r;
216         }
217
218         /* And now, let's mount it over */
219         r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL);
220         if (r < 0) {
221                 log_error("Failed to mount /etc/machine-id: %m");
222                 unlink_noerrno("/run/machine-id");
223                 return -errno;
224         }
225
226         log_info("Installed transient /etc/machine-id file.");
227
228         /* Mark the mount read-only */
229         if (mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
230                 log_warning("Failed to make transient /etc/machine-id read-only: %m");
231
232         return 0;
233 }