chiark / gitweb /
core: priorize notification fd processing over notification fd process via sd-event...
[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                 k = loop_read(fd, id, 33, false);
76                 close_nointr_nofail(fd);
77
78                 if (k == 33 && id[32] == '\n') {
79
80                         id[32] = 0;
81                         if (id128_is_valid(id)) {
82                                 id[32] = '\n';
83                                 id[33] = 0;
84
85                                 log_info("Initializing machine ID from D-Bus machine ID.");
86                                 return 0;
87                         }
88                 }
89         }
90
91         /* If that didn't work, see if we are running in qemu/kvm and a
92          * machine ID was passed in via -uuid on the qemu/kvm command
93          * line */
94
95         r = detect_vm(&vm_id);
96         if (r > 0 && streq(vm_id, "kvm")) {
97                 char uuid[37];
98
99                 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
100                 if (fd >= 0) {
101                         k = loop_read(fd, uuid, 36, false);
102                         close_nointr_nofail(fd);
103
104                         if (k >= 36) {
105                                 r = shorten_uuid(id, uuid);
106                                 if (r >= 0) {
107                                         log_info("Initializing machine ID from KVM UUID.");
108                                         return 0;
109                                 }
110                         }
111                 }
112         }
113
114         /* If that didn't work either, see if we are running in a
115          * container, and a machine ID was passed in via
116          * $container_uuid the way libvirt/LXC does it */
117         r = detect_container(NULL);
118         if (r > 0) {
119                 _cleanup_free_ char *e = NULL;
120
121                 r = getenv_for_pid(1, "container_uuid", &e);
122                 if (r > 0) {
123                         if (strlen(e) >= 36) {
124                                 r = shorten_uuid(id, e);
125                                 if (r >= 0) {
126                                         log_info("Initializing machine ID from container UUID.");
127                                         return 0;
128                                 }
129                         }
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 = false;
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, 33, false) == 33 && id[32] == '\n') {
187                         id[32] = 0;
188
189                         if (id128_is_valid(id))
190                                 return 0;
191                 }
192
193         /* Hmm, so, the id currently stored is not useful, then let's
194          * generate one */
195
196         r = generate(id);
197         if (r < 0)
198                 return r;
199
200         if (S_ISREG(st.st_mode) && writable) {
201                 lseek(fd, 0, SEEK_SET);
202
203                 if (loop_write(fd, id, 33, false) == 33)
204                         return 0;
205         }
206
207         close_nointr_nofail(fd);
208         fd = -1;
209
210         /* Hmm, we couldn't write it? So let's write it to
211          * /run/machine-id as a replacement */
212
213         RUN_WITH_UMASK(0022) {
214                 r = write_string_file("/run/machine-id", id);
215         }
216         if (r < 0) {
217                 log_error("Cannot write /run/machine-id: %s", strerror(-r));
218                 unlink("/run/machine-id");
219                 return r;
220         }
221
222         /* And now, let's mount it over */
223         r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL);
224         if (r < 0) {
225                 log_error("Failed to mount /etc/machine-id: %m");
226                 unlink_noerrno("/run/machine-id");
227                 return -errno;
228         }
229
230         log_info("Installed transient /etc/machine-id file.");
231
232         /* Mark the mount read-only */
233         if (mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
234                 log_warning("Failed to make transient /etc/machine-id read-only: %m");
235
236         return 0;
237 }