1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
28 #include "alloc-util.h"
29 #include "dirent-util.h"
33 #include "process-util.h"
34 #include "stat-util.h"
35 #include "string-table.h"
36 #include "string-util.h"
39 static int detect_vm_cpuid(void) {
41 /* CPUID is an x86 specific interface. */
42 #if defined(__i386__) || defined(__x86_64__)
47 } cpuid_vendor_table
[] = {
48 { "XenVMMXenVMM", VIRTUALIZATION_XEN
},
49 { "KVMKVMKVM", VIRTUALIZATION_KVM
},
50 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
51 { "VMwareVMware", VIRTUALIZATION_VMWARE
},
52 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
53 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT
},
59 /* http://lwn.net/Articles/301888/ */
61 #if defined (__i386__)
64 #elif defined (__amd64__)
69 /* First detect whether there is a hypervisor */
71 __asm__
__volatile__ (
72 /* ebx/rbx is being used for PIC! */
73 " push %%"REG_b
" \n\t"
77 : "=a" (eax
), "=c" (ecx
)
81 hypervisor
= !!(ecx
& 0x80000000U
);
90 /* There is a hypervisor, see what it is */
92 __asm__
__volatile__ (
93 /* ebx/rbx is being used for PIC! */
94 " push %%"REG_b
" \n\t"
99 : "=a" (eax
), "=r" (sig
.sig32
[0]), "=c" (sig
.sig32
[1]), "=d" (sig
.sig32
[2])
103 for (j
= 0; j
< ELEMENTSOF(cpuid_vendor_table
); j
++)
104 if (streq(sig
.text
, cpuid_vendor_table
[j
].cpuid
))
105 return cpuid_vendor_table
[j
].id
;
107 return VIRTUALIZATION_VM_OTHER
;
111 return VIRTUALIZATION_NONE
;
114 static int detect_vm_device_tree(void) {
115 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
116 _cleanup_free_
char *hvtype
= NULL
;
119 r
= read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype
);
121 _cleanup_closedir_
DIR *dir
= NULL
;
124 dir
= opendir("/proc/device-tree");
127 return VIRTUALIZATION_NONE
;
131 FOREACH_DIRENT(dent
, dir
, return -errno
)
132 if (strstr(dent
->d_name
, "fw-cfg"))
133 return VIRTUALIZATION_QEMU
;
135 return VIRTUALIZATION_NONE
;
139 if (streq(hvtype
, "linux,kvm"))
140 return VIRTUALIZATION_KVM
;
141 else if (strstr(hvtype
, "xen"))
142 return VIRTUALIZATION_XEN
;
144 return VIRTUALIZATION_VM_OTHER
;
146 return VIRTUALIZATION_NONE
;
150 static int detect_vm_dmi(void) {
151 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
153 static const char *const dmi_vendors
[] = {
154 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
155 "/sys/class/dmi/id/sys_vendor",
156 "/sys/class/dmi/id/board_vendor",
157 "/sys/class/dmi/id/bios_vendor"
160 static const struct {
163 } dmi_vendor_table
[] = {
164 { "KVM", VIRTUALIZATION_KVM
},
165 { "QEMU", VIRTUALIZATION_QEMU
},
166 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
167 { "VMware", VIRTUALIZATION_VMWARE
},
168 { "VMW", VIRTUALIZATION_VMWARE
},
169 { "innotek GmbH", VIRTUALIZATION_ORACLE
},
170 { "Xen", VIRTUALIZATION_XEN
},
171 { "Bochs", VIRTUALIZATION_BOCHS
},
172 { "Parallels", VIRTUALIZATION_PARALLELS
},
177 for (i
= 0; i
< ELEMENTSOF(dmi_vendors
); i
++) {
178 _cleanup_free_
char *s
= NULL
;
181 r
= read_one_line_file(dmi_vendors
[i
], &s
);
189 for (j
= 0; j
< ELEMENTSOF(dmi_vendor_table
); j
++)
190 if (startswith(s
, dmi_vendor_table
[j
].vendor
))
191 return dmi_vendor_table
[j
].id
;
195 return VIRTUALIZATION_NONE
;
198 static int detect_vm_xen(void) {
199 _cleanup_free_
char *domcap
= NULL
;
203 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
205 return VIRTUALIZATION_NONE
;
208 while ((cap
= strsep(&i
, ",")))
209 if (streq(cap
, "control_d"))
212 return cap
? VIRTUALIZATION_NONE
: VIRTUALIZATION_XEN
;
215 static int detect_vm_hypervisor(void) {
216 _cleanup_free_
char *hvtype
= NULL
;
219 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
221 return VIRTUALIZATION_NONE
;
225 if (streq(hvtype
, "xen"))
226 return VIRTUALIZATION_XEN
;
228 return VIRTUALIZATION_VM_OTHER
;
231 static int detect_vm_uml(void) {
232 _cleanup_free_
char *cpuinfo_contents
= NULL
;
235 /* Detect User-Mode Linux by reading /proc/cpuinfo */
236 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
239 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n"))
240 return VIRTUALIZATION_UML
;
242 return VIRTUALIZATION_NONE
;
245 static int detect_vm_zvm(void) {
247 #if defined(__s390__)
248 _cleanup_free_
char *t
= NULL
;
251 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
253 return VIRTUALIZATION_NONE
;
257 if (streq(t
, "z/VM"))
258 return VIRTUALIZATION_ZVM
;
260 return VIRTUALIZATION_KVM
;
262 return VIRTUALIZATION_NONE
;
266 /* Returns a short identifier for the various VM implementations */
267 int detect_vm(void) {
268 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
271 if (cached_found
>= 0)
274 /* We have to use the correct order here:
275 * Some virtualization technologies do use KVM hypervisor but are
276 * expected to be detected as something else. So detect DMI first.
278 * An example is Virtualbox since version 5.0, which uses KVM backend.
279 * Detection via DMI works corretly, the CPU ID would find KVM
284 if (r
!= VIRTUALIZATION_NONE
)
287 r
= detect_vm_cpuid();
290 if (r
!= VIRTUALIZATION_NONE
)
293 /* x86 xen will most likely be detected by cpuid. If not (most likely
294 * because we're not an x86 guest), then we should try the xen capabilities
295 * file next. If that's not found, then we check for the high-level
296 * hypervisor sysfs file:
298 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
303 if (r
!= VIRTUALIZATION_NONE
)
306 r
= detect_vm_hypervisor();
309 if (r
!= VIRTUALIZATION_NONE
)
312 r
= detect_vm_device_tree();
315 if (r
!= VIRTUALIZATION_NONE
)
321 if (r
!= VIRTUALIZATION_NONE
)
333 int detect_container(void) {
335 static const struct {
339 { "lxc", VIRTUALIZATION_LXC
},
340 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
341 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
342 { "docker", VIRTUALIZATION_DOCKER
},
343 { "rkt", VIRTUALIZATION_RKT
},
346 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
347 _cleanup_free_
char *m
= NULL
;
348 const char *e
= NULL
;
352 if (cached_found
>= 0)
355 /* /proc/vz exists in container and outside of the container,
356 * /proc/bc only outside of the container. */
357 if (access("/proc/vz", F_OK
) >= 0 &&
358 access("/proc/bc", F_OK
) < 0) {
359 r
= VIRTUALIZATION_OPENVZ
;
364 /* If we are PID 1 we can just check our own
365 * environment variable */
367 e
= getenv("container");
369 r
= VIRTUALIZATION_NONE
;
374 /* Otherwise, PID 1 dropped this information into a
375 * file in /run. This is better than accessing
376 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
379 r
= read_one_line_file("/run/systemd/container", &m
);
382 /* Fallback for cases where PID 1 was not
383 * systemd (for example, cases where
384 * init=/bin/sh is used. */
386 r
= getenv_for_pid(1, "container", &m
);
389 /* If that didn't work, give up,
390 * assume no container manager.
392 * Note: This means we still cannot
393 * detect containers if init=/bin/sh
394 * is passed but privileges dropped,
395 * as /proc/1/environ is only readable
396 * with privileges. */
398 r
= VIRTUALIZATION_NONE
;
408 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
409 if (streq(e
, value_table
[j
].value
)) {
410 r
= value_table
[j
].id
;
414 r
= VIRTUALIZATION_CONTAINER_OTHER
;
421 int detect_virtualization(void) {
424 r
= detect_container();
431 int running_in_chroot(void) {
434 ret
= files_same("/proc/1/root", "/");
441 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
442 [VIRTUALIZATION_NONE
] = "none",
443 [VIRTUALIZATION_KVM
] = "kvm",
444 [VIRTUALIZATION_QEMU
] = "qemu",
445 [VIRTUALIZATION_BOCHS
] = "bochs",
446 [VIRTUALIZATION_XEN
] = "xen",
447 [VIRTUALIZATION_UML
] = "uml",
448 [VIRTUALIZATION_VMWARE
] = "vmware",
449 [VIRTUALIZATION_ORACLE
] = "oracle",
450 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
451 [VIRTUALIZATION_ZVM
] = "zvm",
452 [VIRTUALIZATION_PARALLELS
] = "parallels",
453 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
455 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
456 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
457 [VIRTUALIZATION_LXC
] = "lxc",
458 [VIRTUALIZATION_OPENVZ
] = "openvz",
459 [VIRTUALIZATION_DOCKER
] = "docker",
460 [VIRTUALIZATION_RKT
] = "rkt",
461 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
464 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);