]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/virt.c
basic: include only what we use
[thirdparty/systemd.git] / src / basic / virt.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 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 <errno.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "alloc-util.h"
29 #include "fileio.h"
30 #include "process-util.h"
31 #include "macro.h"
32 #include "stat-util.h"
33 #include "string-table.h"
34 #include "string-util.h"
35 #include "virt.h"
36
37 static int detect_vm_cpuid(void) {
38
39 /* CPUID is an x86 specific interface. */
40 #if defined(__i386__) || defined(__x86_64__)
41
42 static const struct {
43 const char *cpuid;
44 int id;
45 } cpuid_vendor_table[] = {
46 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
47 { "KVMKVMKVM", VIRTUALIZATION_KVM },
48 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
49 { "VMwareVMware", VIRTUALIZATION_VMWARE },
50 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
51 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
52 };
53
54 uint32_t eax, ecx;
55 bool hypervisor;
56
57 /* http://lwn.net/Articles/301888/ */
58
59 #if defined (__i386__)
60 #define REG_a "eax"
61 #define REG_b "ebx"
62 #elif defined (__amd64__)
63 #define REG_a "rax"
64 #define REG_b "rbx"
65 #endif
66
67 /* First detect whether there is a hypervisor */
68 eax = 1;
69 __asm__ __volatile__ (
70 /* ebx/rbx is being used for PIC! */
71 " push %%"REG_b" \n\t"
72 " cpuid \n\t"
73 " pop %%"REG_b" \n\t"
74
75 : "=a" (eax), "=c" (ecx)
76 : "0" (eax)
77 );
78
79 hypervisor = !!(ecx & 0x80000000U);
80
81 if (hypervisor) {
82 union {
83 uint32_t sig32[3];
84 char text[13];
85 } sig = {};
86 unsigned j;
87
88 /* There is a hypervisor, see what it is */
89 eax = 0x40000000U;
90 __asm__ __volatile__ (
91 /* ebx/rbx is being used for PIC! */
92 " push %%"REG_b" \n\t"
93 " cpuid \n\t"
94 " mov %%ebx, %1 \n\t"
95 " pop %%"REG_b" \n\t"
96
97 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
98 : "0" (eax)
99 );
100
101 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
102 if (streq(sig.text, cpuid_vendor_table[j].cpuid))
103 return cpuid_vendor_table[j].id;
104
105 return VIRTUALIZATION_VM_OTHER;
106 }
107 #endif
108
109 return VIRTUALIZATION_NONE;
110 }
111
112 static int detect_vm_device_tree(void) {
113 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
114 _cleanup_free_ char *hvtype = NULL;
115 int r;
116
117 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
118 if (r == -ENOENT) {
119 _cleanup_closedir_ DIR *dir = NULL;
120 struct dirent *dent;
121
122 dir = opendir("/proc/device-tree");
123 if (!dir) {
124 if (errno == ENOENT)
125 return VIRTUALIZATION_NONE;
126 return -errno;
127 }
128
129 FOREACH_DIRENT(dent, dir, return -errno)
130 if (strstr(dent->d_name, "fw-cfg"))
131 return VIRTUALIZATION_QEMU;
132
133 return VIRTUALIZATION_NONE;
134 } else if (r < 0)
135 return r;
136
137 if (streq(hvtype, "linux,kvm"))
138 return VIRTUALIZATION_KVM;
139 else if (strstr(hvtype, "xen"))
140 return VIRTUALIZATION_XEN;
141 else
142 return VIRTUALIZATION_VM_OTHER;
143 #else
144 return VIRTUALIZATION_NONE;
145 #endif
146 }
147
148 static int detect_vm_dmi(void) {
149 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
150
151 static const char *const dmi_vendors[] = {
152 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
153 "/sys/class/dmi/id/sys_vendor",
154 "/sys/class/dmi/id/board_vendor",
155 "/sys/class/dmi/id/bios_vendor"
156 };
157
158 static const struct {
159 const char *vendor;
160 int id;
161 } dmi_vendor_table[] = {
162 { "KVM", VIRTUALIZATION_KVM },
163 { "QEMU", VIRTUALIZATION_QEMU },
164 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
165 { "VMware", VIRTUALIZATION_VMWARE },
166 { "VMW", VIRTUALIZATION_VMWARE },
167 { "innotek GmbH", VIRTUALIZATION_ORACLE },
168 { "Xen", VIRTUALIZATION_XEN },
169 { "Bochs", VIRTUALIZATION_BOCHS },
170 { "Parallels", VIRTUALIZATION_PARALLELS },
171 };
172 unsigned i;
173 int r;
174
175 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
176 _cleanup_free_ char *s = NULL;
177 unsigned j;
178
179 r = read_one_line_file(dmi_vendors[i], &s);
180 if (r < 0) {
181 if (r == -ENOENT)
182 continue;
183
184 return r;
185 }
186
187 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
188 if (startswith(s, dmi_vendor_table[j].vendor))
189 return dmi_vendor_table[j].id;
190 }
191 #endif
192
193 return VIRTUALIZATION_NONE;
194 }
195
196 static int detect_vm_xen(void) {
197 _cleanup_free_ char *domcap = NULL;
198 char *cap, *i;
199 int r;
200
201 r = read_one_line_file("/proc/xen/capabilities", &domcap);
202 if (r == -ENOENT)
203 return VIRTUALIZATION_NONE;
204
205 i = domcap;
206 while ((cap = strsep(&i, ",")))
207 if (streq(cap, "control_d"))
208 break;
209
210 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
211 }
212
213 static int detect_vm_hypervisor(void) {
214 _cleanup_free_ char *hvtype = NULL;
215 int r;
216
217 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
218 if (r == -ENOENT)
219 return VIRTUALIZATION_NONE;
220 if (r < 0)
221 return r;
222
223 if (streq(hvtype, "xen"))
224 return VIRTUALIZATION_XEN;
225 else
226 return VIRTUALIZATION_VM_OTHER;
227 }
228
229 static int detect_vm_uml(void) {
230 _cleanup_free_ char *cpuinfo_contents = NULL;
231 int r;
232
233 /* Detect User-Mode Linux by reading /proc/cpuinfo */
234 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
235 if (r < 0)
236 return r;
237 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
238 return VIRTUALIZATION_UML;
239
240 return VIRTUALIZATION_NONE;
241 }
242
243 static int detect_vm_zvm(void) {
244
245 #if defined(__s390__)
246 _cleanup_free_ char *t = NULL;
247 int r;
248
249 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
250 if (r == -ENOENT)
251 return VIRTUALIZATION_NONE;
252 if (r < 0)
253 return r;
254
255 if (streq(t, "z/VM"))
256 return VIRTUALIZATION_ZVM;
257 else
258 return VIRTUALIZATION_KVM;
259 #else
260 return VIRTUALIZATION_NONE;
261 #endif
262 }
263
264 /* Returns a short identifier for the various VM implementations */
265 int detect_vm(void) {
266 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
267 int r;
268
269 if (cached_found >= 0)
270 return cached_found;
271
272 /* We have to use the correct order here:
273 * Some virtualization technologies do use KVM hypervisor but are
274 * expected to be detected as something else. So detect DMI first.
275 *
276 * An example is Virtualbox since version 5.0, which uses KVM backend.
277 * Detection via DMI works corretly, the CPU ID would find KVM
278 * only. */
279 r = detect_vm_dmi();
280 if (r < 0)
281 return r;
282 if (r != VIRTUALIZATION_NONE)
283 goto finish;
284
285 r = detect_vm_cpuid();
286 if (r < 0)
287 return r;
288 if (r != VIRTUALIZATION_NONE)
289 goto finish;
290
291 /* x86 xen will most likely be detected by cpuid. If not (most likely
292 * because we're not an x86 guest), then we should try the xen capabilities
293 * file next. If that's not found, then we check for the high-level
294 * hypervisor sysfs file:
295 *
296 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
297
298 r = detect_vm_xen();
299 if (r < 0)
300 return r;
301 if (r != VIRTUALIZATION_NONE)
302 goto finish;
303
304 r = detect_vm_hypervisor();
305 if (r < 0)
306 return r;
307 if (r != VIRTUALIZATION_NONE)
308 goto finish;
309
310 r = detect_vm_device_tree();
311 if (r < 0)
312 return r;
313 if (r != VIRTUALIZATION_NONE)
314 goto finish;
315
316 r = detect_vm_uml();
317 if (r < 0)
318 return r;
319 if (r != VIRTUALIZATION_NONE)
320 goto finish;
321
322 r = detect_vm_zvm();
323 if (r < 0)
324 return r;
325
326 finish:
327 cached_found = r;
328 return r;
329 }
330
331 int detect_container(void) {
332
333 static const struct {
334 const char *value;
335 int id;
336 } value_table[] = {
337 { "lxc", VIRTUALIZATION_LXC },
338 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT },
339 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
340 { "docker", VIRTUALIZATION_DOCKER },
341 { "rkt", VIRTUALIZATION_RKT },
342 };
343
344 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
345 _cleanup_free_ char *m = NULL;
346 const char *e = NULL;
347 unsigned j;
348 int r;
349
350 if (cached_found >= 0)
351 return cached_found;
352
353 /* /proc/vz exists in container and outside of the container,
354 * /proc/bc only outside of the container. */
355 if (access("/proc/vz", F_OK) >= 0 &&
356 access("/proc/bc", F_OK) < 0) {
357 r = VIRTUALIZATION_OPENVZ;
358 goto finish;
359 }
360
361 if (getpid() == 1) {
362 /* If we are PID 1 we can just check our own
363 * environment variable */
364
365 e = getenv("container");
366 if (isempty(e)) {
367 r = VIRTUALIZATION_NONE;
368 goto finish;
369 }
370 } else {
371
372 /* Otherwise, PID 1 dropped this information into a
373 * file in /run. This is better than accessing
374 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
375 * for that. */
376
377 r = read_one_line_file("/run/systemd/container", &m);
378 if (r == -ENOENT) {
379
380 /* Fallback for cases where PID 1 was not
381 * systemd (for example, cases where
382 * init=/bin/sh is used. */
383
384 r = getenv_for_pid(1, "container", &m);
385 if (r <= 0) {
386
387 /* If that didn't work, give up,
388 * assume no container manager.
389 *
390 * Note: This means we still cannot
391 * detect containers if init=/bin/sh
392 * is passed but privileges dropped,
393 * as /proc/1/environ is only readable
394 * with privileges. */
395
396 r = VIRTUALIZATION_NONE;
397 goto finish;
398 }
399 }
400 if (r < 0)
401 return r;
402
403 e = m;
404 }
405
406 for (j = 0; j < ELEMENTSOF(value_table); j++)
407 if (streq(e, value_table[j].value)) {
408 r = value_table[j].id;
409 goto finish;
410 }
411
412 r = VIRTUALIZATION_CONTAINER_OTHER;
413
414 finish:
415 cached_found = r;
416 return r;
417 }
418
419 int detect_virtualization(void) {
420 int r;
421
422 r = detect_container();
423 if (r != 0)
424 return r;
425
426 return detect_vm();
427 }
428
429 int running_in_chroot(void) {
430 int ret;
431
432 ret = files_same("/proc/1/root", "/");
433 if (ret < 0)
434 return ret;
435
436 return ret == 0;
437 }
438
439 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
440 [VIRTUALIZATION_NONE] = "none",
441 [VIRTUALIZATION_KVM] = "kvm",
442 [VIRTUALIZATION_QEMU] = "qemu",
443 [VIRTUALIZATION_BOCHS] = "bochs",
444 [VIRTUALIZATION_XEN] = "xen",
445 [VIRTUALIZATION_UML] = "uml",
446 [VIRTUALIZATION_VMWARE] = "vmware",
447 [VIRTUALIZATION_ORACLE] = "oracle",
448 [VIRTUALIZATION_MICROSOFT] = "microsoft",
449 [VIRTUALIZATION_ZVM] = "zvm",
450 [VIRTUALIZATION_PARALLELS] = "parallels",
451 [VIRTUALIZATION_VM_OTHER] = "vm-other",
452
453 [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
454 [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
455 [VIRTUALIZATION_LXC] = "lxc",
456 [VIRTUALIZATION_OPENVZ] = "openvz",
457 [VIRTUALIZATION_DOCKER] = "docker",
458 [VIRTUALIZATION_RKT] = "rkt",
459 [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
460 };
461
462 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);