chiark / gitweb /
core: add bus API and systemctl commands for altering cgroup parameters during runtime
[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
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         r = detect_container(NULL);
114         if (r > 0) {
115                 char *e;
116
117                 r = getenv_for_pid(1, "container_uuid", &e);
118                 if (r > 0) {
119                         if (strlen(e) >= 36) {
120                                 r = shorten_uuid(id, e);
121                                 if (r >= 0) {
122                                         log_info("Initializing machine ID from container UUID.");
123                                         free(e);
124                                         return 0;
125                                 }
126                         }
127
128                         free(e);
129                 }
130         }
131
132         /* If that didn't work, generate a random machine id */
133         r = sd_id128_randomize(&buf);
134         if (r < 0) {
135                 log_error("Failed to open /dev/urandom: %s", strerror(-r));
136                 return r;
137         }
138
139         for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
140                 q[0] = hexchar(*p >> 4);
141                 q[1] = hexchar(*p & 15);
142         }
143
144         id[32] = '\n';
145         id[33] = 0;
146
147         log_info("Initializing machine ID from random generator.");
148
149         return 0;
150 }
151
152 int machine_id_setup(void) {
153         int fd, r;
154         bool writable;
155         struct stat st;
156         char id[34]; /* 32 + \n + \0 */
157         mode_t m;
158
159         m = umask(0000);
160
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                         umask(m);
173                         log_error("Cannot open /etc/machine-id: %m");
174                         return -errno;
175                 }
176
177                 writable = false;
178         }
179
180         umask(m);
181
182         if (fstat(fd, &st) < 0) {
183                 log_error("fstat() failed: %m");
184                 r = -errno;
185                 goto finish;
186         }
187
188         if (S_ISREG(st.st_mode)) {
189                 if (loop_read(fd, id, 32, false) >= 32) {
190                         r = 0;
191                         goto finish;
192                 }
193         }
194
195         /* Hmm, so, the id currently stored is not useful, then let's
196          * generate one */
197
198         r = generate(id);
199         if (r < 0)
200                 goto finish;
201
202         if (S_ISREG(st.st_mode) && writable) {
203                 lseek(fd, 0, SEEK_SET);
204
205                 if (loop_write(fd, id, 33, false) == 33) {
206                         r = 0;
207                         goto finish;
208                 }
209         }
210
211         close_nointr_nofail(fd);
212         fd = -1;
213
214         /* Hmm, we couldn't write it? So let's write it to
215          * /run/machine-id as a replacement */
216
217         m = umask(0022);
218         r = write_one_line_file("/run/machine-id", id);
219         umask(m);
220
221         if (r < 0) {
222                 log_error("Cannot write /run/machine-id: %s", strerror(-r));
223
224                 unlink("/run/machine-id");
225                 goto finish;
226         }
227
228         /* And now, let's mount it over */
229         r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL) < 0 ? -errno : 0;
230         if (r < 0) {
231                 unlink("/run/machine-id");
232                 log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
233         } else {
234                 log_info("Installed transient /etc/machine-id file.");
235
236                 /* Mark the mount read-only */
237                 mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
238         }
239
240 finish:
241
242         if (fd >= 0)
243                 close_nointr_nofail(fd);
244
245         return r;
246 }