]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/machine-id-setup.c
man: network - clarify which options may be specified more than once.
[thirdparty/systemd.git] / src / core / machine-id-setup.c
CommitLineData
d7ccca2e
LP
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
5430f7f2
LP
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
d7ccca2e
LP
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
5430f7f2 16 Lesser General Public License for more details.
d7ccca2e 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
d7ccca2e
LP
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
81527be1
LP
30#include <systemd/sd-id128.h>
31
b6e66135 32#include "machine-id-setup.h"
d7ccca2e
LP
33#include "macro.h"
34#include "util.h"
49e942b2 35#include "mkdir.h"
d7ccca2e 36#include "log.h"
d4eb120a 37#include "virt.h"
a5c32cff 38#include "fileio.h"
8d41a963 39
09b967ea
LP
40static 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
92f2f92e 62static int generate(char id[34], const char *root) {
87d2c1ff
LP
63 int fd, r;
64 unsigned char *p;
65 sd_id128_t buf;
8d41a963 66 char *q;
d7ccca2e 67 ssize_t k;
d4eb120a 68 const char *vm_id;
92f2f92e 69 _cleanup_free_ char *dbus_machine_id = NULL;
d7ccca2e
LP
70
71 assert(id);
72
92f2f92e
GKH
73 if (asprintf(&dbus_machine_id, "%s/var/lib/dbus/machine-id", root) < 0)
74 return log_oom();
75
d7ccca2e 76 /* First, try reading the D-Bus machine id, unless it is a symlink */
92f2f92e 77 fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
8d41a963 78 if (fd >= 0) {
aa96c6cb 79 k = loop_read(fd, id, 33, false);
d7ccca2e
LP
80 close_nointr_nofail(fd);
81
aa96c6cb 82 if (k == 33 && id[32] == '\n') {
d7ccca2e 83
aa96c6cb
LP
84 id[32] = 0;
85 if (id128_is_valid(id)) {
86 id[32] = '\n';
87 id[33] = 0;
88
89 log_info("Initializing machine ID from D-Bus machine ID.");
90 return 0;
91 }
d7ccca2e
LP
92 }
93 }
94
d4eb120a
LP
95 /* If that didn't work, see if we are running in qemu/kvm and a
96 * machine ID was passed in via -uuid on the qemu/kvm command
97 * line */
98
99 r = detect_vm(&vm_id);
100 if (r > 0 && streq(vm_id, "kvm")) {
101 char uuid[37];
102
103 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
104 if (fd >= 0) {
105 k = loop_read(fd, uuid, 36, false);
106 close_nointr_nofail(fd);
107
108 if (k >= 36) {
09b967ea
LP
109 r = shorten_uuid(id, uuid);
110 if (r >= 0) {
8e47b1d2 111 log_info("Initializing machine ID from KVM UUID.");
09b967ea
LP
112 return 0;
113 }
114 }
115 }
116 }
d4eb120a 117
09b967ea
LP
118 /* If that didn't work either, see if we are running in a
119 * container, and a machine ID was passed in via
120 * $container_uuid the way libvirt/LXC does it */
09b967ea
LP
121 r = detect_container(NULL);
122 if (r > 0) {
aa96c6cb 123 _cleanup_free_ char *e = NULL;
09b967ea 124
ab94af92
LP
125 r = getenv_for_pid(1, "container_uuid", &e);
126 if (r > 0) {
127 if (strlen(e) >= 36) {
128 r = shorten_uuid(id, e);
129 if (r >= 0) {
8e47b1d2 130 log_info("Initializing machine ID from container UUID.");
ab94af92 131 return 0;
09b967ea 132 }
ab94af92 133 }
d4eb120a
LP
134 }
135 }
136
d7ccca2e 137 /* If that didn't work, generate a random machine id */
87d2c1ff
LP
138 r = sd_id128_randomize(&buf);
139 if (r < 0) {
140 log_error("Failed to open /dev/urandom: %s", strerror(-r));
141 return r;
d7ccca2e
LP
142 }
143
87d2c1ff 144 for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
d7ccca2e
LP
145 q[0] = hexchar(*p >> 4);
146 q[1] = hexchar(*p & 15);
147 }
148
149 id[32] = '\n';
150 id[33] = 0;
151
152 log_info("Initializing machine ID from random generator.");
153
154 return 0;
155}
156
92f2f92e 157int machine_id_setup(const char *root) {
4b73a0c0
LP
158 _cleanup_close_ int fd = -1;
159 int r;
df28bc08 160 bool writable = false;
d7ccca2e
LP
161 struct stat st;
162 char id[34]; /* 32 + \n + \0 */
92f2f92e
GKH
163 _cleanup_free_ char *etc_machine_id = NULL;
164 _cleanup_free_ char *run_machine_id = NULL;
165
166 if (asprintf(&etc_machine_id, "%s/etc/machine-id", root) < 0)
167 return log_oom();
168
169 if (asprintf(&run_machine_id, "%s/run/machine-id", root) < 0)
170 return log_oom();
d7ccca2e 171
5c0d398d
LP
172 RUN_WITH_UMASK(0000) {
173 /* We create this 0444, to indicate that this isn't really
174 * something you should ever modify. Of course, since the file
175 * will be owned by root it doesn't matter much, but maybe
176 * people look. */
177
92f2f92e 178 fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
5c0d398d
LP
179 if (fd >= 0)
180 writable = true;
181 else {
92f2f92e 182 fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
5c0d398d 183 if (fd < 0) {
92f2f92e 184 log_error("Cannot open %s: %m", etc_machine_id);
5c0d398d
LP
185 return -errno;
186 }
76526bad 187
5c0d398d 188 writable = false;
d7ccca2e 189 }
d7ccca2e
LP
190 }
191
d7ccca2e
LP
192 if (fstat(fd, &st) < 0) {
193 log_error("fstat() failed: %m");
4b73a0c0 194 return -errno;
d7ccca2e
LP
195 }
196
4b73a0c0 197 if (S_ISREG(st.st_mode))
aa96c6cb
LP
198 if (loop_read(fd, id, 33, false) == 33 && id[32] == '\n') {
199 id[32] = 0;
200
201 if (id128_is_valid(id))
202 return 0;
203 }
d7ccca2e
LP
204
205 /* Hmm, so, the id currently stored is not useful, then let's
206 * generate one */
207
92f2f92e 208 r = generate(id, root);
8d41a963 209 if (r < 0)
4b73a0c0 210 return r;
d7ccca2e
LP
211
212 if (S_ISREG(st.st_mode) && writable) {
213 lseek(fd, 0, SEEK_SET);
214
4b73a0c0
LP
215 if (loop_write(fd, id, 33, false) == 33)
216 return 0;
d7ccca2e
LP
217 }
218
219 close_nointr_nofail(fd);
220 fd = -1;
221
222 /* Hmm, we couldn't write it? So let's write it to
6996295f 223 * /run/machine-id as a replacement */
d7ccca2e 224
5c0d398d 225 RUN_WITH_UMASK(0022) {
92f2f92e 226 r = write_string_file(run_machine_id, id);
5c0d398d 227 }
8d41a963 228 if (r < 0) {
92f2f92e
GKH
229 log_error("Cannot write %s: %s", run_machine_id, strerror(-r));
230 unlink(run_machine_id);
4b73a0c0 231 return r;
d7ccca2e
LP
232 }
233
234 /* And now, let's mount it over */
92f2f92e 235 r = mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL);
6996295f 236 if (r < 0) {
92f2f92e
GKH
237 log_error("Failed to mount %s: %m", etc_machine_id);
238 unlink_noerrno(run_machine_id);
4b73a0c0 239 return -errno;
aed5a525
LP
240 }
241
92f2f92e 242 log_info("Installed transient %s file.", etc_machine_id);
d7ccca2e 243
4b73a0c0 244 /* Mark the mount read-only */
92f2f92e
GKH
245 if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
246 log_warning("Failed to make transient %s read-only: %m", etc_machine_id);
d7ccca2e 247
4b73a0c0 248 return 0;
d7ccca2e 249}