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/>.
26 #include "alloc-util.h"
28 #include "process-util.h"
29 #include "stat-util.h"
30 #include "string-table.h"
31 #include "string-util.h"
35 static int detect_vm_cpuid(void) {
37 /* CPUID is an x86 specific interface. */
38 #if defined(__i386__) || defined(__x86_64__)
43 } cpuid_vendor_table
[] = {
44 { "XenVMMXenVMM", VIRTUALIZATION_XEN
},
45 { "KVMKVMKVM", VIRTUALIZATION_KVM
},
46 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
47 { "VMwareVMware", VIRTUALIZATION_VMWARE
},
48 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
49 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT
},
55 /* http://lwn.net/Articles/301888/ */
57 #if defined (__i386__)
60 #elif defined (__amd64__)
65 /* First detect whether there is a hypervisor */
67 __asm__
__volatile__ (
68 /* ebx/rbx is being used for PIC! */
69 " push %%"REG_b
" \n\t"
73 : "=a" (eax
), "=c" (ecx
)
77 hypervisor
= !!(ecx
& 0x80000000U
);
86 /* There is a hypervisor, see what it is */
88 __asm__
__volatile__ (
89 /* ebx/rbx is being used for PIC! */
90 " push %%"REG_b
" \n\t"
95 : "=a" (eax
), "=r" (sig
.sig32
[0]), "=c" (sig
.sig32
[1]), "=d" (sig
.sig32
[2])
99 for (j
= 0; j
< ELEMENTSOF(cpuid_vendor_table
); j
++)
100 if (streq(sig
.text
, cpuid_vendor_table
[j
].cpuid
))
101 return cpuid_vendor_table
[j
].id
;
103 return VIRTUALIZATION_VM_OTHER
;
107 return VIRTUALIZATION_NONE
;
110 static int detect_vm_device_tree(void) {
111 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
112 _cleanup_free_
char *hvtype
= NULL
;
115 r
= read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype
);
117 _cleanup_closedir_
DIR *dir
= NULL
;
120 dir
= opendir("/proc/device-tree");
123 return VIRTUALIZATION_NONE
;
127 FOREACH_DIRENT(dent
, dir
, return -errno
)
128 if (strstr(dent
->d_name
, "fw-cfg"))
129 return VIRTUALIZATION_QEMU
;
131 return VIRTUALIZATION_NONE
;
135 if (streq(hvtype
, "linux,kvm"))
136 return VIRTUALIZATION_KVM
;
137 else if (strstr(hvtype
, "xen"))
138 return VIRTUALIZATION_XEN
;
140 return VIRTUALIZATION_VM_OTHER
;
142 return VIRTUALIZATION_NONE
;
146 static int detect_vm_dmi(void) {
147 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
149 static const char *const dmi_vendors
[] = {
150 "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
151 "/sys/class/dmi/id/sys_vendor",
152 "/sys/class/dmi/id/board_vendor",
153 "/sys/class/dmi/id/bios_vendor"
156 static const struct {
159 } dmi_vendor_table
[] = {
160 { "KVM", VIRTUALIZATION_KVM
},
161 { "QEMU", VIRTUALIZATION_QEMU
},
162 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
163 { "VMware", VIRTUALIZATION_VMWARE
},
164 { "VMW", VIRTUALIZATION_VMWARE
},
165 { "innotek GmbH", VIRTUALIZATION_ORACLE
},
166 { "Xen", VIRTUALIZATION_XEN
},
167 { "Bochs", VIRTUALIZATION_BOCHS
},
168 { "Parallels", VIRTUALIZATION_PARALLELS
},
173 for (i
= 0; i
< ELEMENTSOF(dmi_vendors
); i
++) {
174 _cleanup_free_
char *s
= NULL
;
177 r
= read_one_line_file(dmi_vendors
[i
], &s
);
185 for (j
= 0; j
< ELEMENTSOF(dmi_vendor_table
); j
++)
186 if (startswith(s
, dmi_vendor_table
[j
].vendor
))
187 return dmi_vendor_table
[j
].id
;
191 return VIRTUALIZATION_NONE
;
194 static int detect_vm_xen(void) {
195 _cleanup_free_
char *domcap
= NULL
;
199 r
= read_one_line_file("/proc/xen/capabilities", &domcap
);
201 return VIRTUALIZATION_NONE
;
204 while ((cap
= strsep(&i
, ",")))
205 if (streq(cap
, "control_d"))
208 return cap
? VIRTUALIZATION_NONE
: VIRTUALIZATION_XEN
;
211 static int detect_vm_hypervisor(void) {
212 _cleanup_free_
char *hvtype
= NULL
;
215 r
= read_one_line_file("/sys/hypervisor/type", &hvtype
);
217 return VIRTUALIZATION_NONE
;
221 if (streq(hvtype
, "xen"))
222 return VIRTUALIZATION_XEN
;
224 return VIRTUALIZATION_VM_OTHER
;
227 static int detect_vm_uml(void) {
228 _cleanup_free_
char *cpuinfo_contents
= NULL
;
231 /* Detect User-Mode Linux by reading /proc/cpuinfo */
232 r
= read_full_file("/proc/cpuinfo", &cpuinfo_contents
, NULL
);
235 if (strstr(cpuinfo_contents
, "\nvendor_id\t: User Mode Linux\n"))
236 return VIRTUALIZATION_UML
;
238 return VIRTUALIZATION_NONE
;
241 static int detect_vm_zvm(void) {
243 #if defined(__s390__)
244 _cleanup_free_
char *t
= NULL
;
247 r
= get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE
, &t
);
249 return VIRTUALIZATION_NONE
;
253 if (streq(t
, "z/VM"))
254 return VIRTUALIZATION_ZVM
;
256 return VIRTUALIZATION_KVM
;
258 return VIRTUALIZATION_NONE
;
262 /* Returns a short identifier for the various VM implementations */
263 int detect_vm(void) {
264 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
267 if (cached_found
>= 0)
270 r
= detect_vm_cpuid();
273 if (r
!= VIRTUALIZATION_NONE
)
279 if (r
!= VIRTUALIZATION_NONE
)
282 /* x86 xen will most likely be detected by cpuid. If not (most likely
283 * because we're not an x86 guest), then we should try the xen capabilities
284 * file next. If that's not found, then we check for the high-level
285 * hypervisor sysfs file:
287 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
292 if (r
!= VIRTUALIZATION_NONE
)
295 r
= detect_vm_hypervisor();
298 if (r
!= VIRTUALIZATION_NONE
)
301 r
= detect_vm_device_tree();
304 if (r
!= VIRTUALIZATION_NONE
)
310 if (r
!= VIRTUALIZATION_NONE
)
322 int detect_container(void) {
324 static const struct {
328 { "lxc", VIRTUALIZATION_LXC
},
329 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
330 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
331 { "docker", VIRTUALIZATION_DOCKER
},
334 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
335 _cleanup_free_
char *m
= NULL
;
336 const char *e
= NULL
;
340 if (cached_found
>= 0)
343 /* /proc/vz exists in container and outside of the container,
344 * /proc/bc only outside of the container. */
345 if (access("/proc/vz", F_OK
) >= 0 &&
346 access("/proc/bc", F_OK
) < 0) {
347 r
= VIRTUALIZATION_OPENVZ
;
352 /* If we are PID 1 we can just check our own
353 * environment variable */
355 e
= getenv("container");
357 r
= VIRTUALIZATION_NONE
;
362 /* Otherwise, PID 1 dropped this information into a
363 * file in /run. This is better than accessing
364 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
367 r
= read_one_line_file("/run/systemd/container", &m
);
370 /* Fallback for cases where PID 1 was not
371 * systemd (for example, cases where
372 * init=/bin/sh is used. */
374 r
= getenv_for_pid(1, "container", &m
);
377 /* If that didn't work, give up,
378 * assume no container manager.
380 * Note: This means we still cannot
381 * detect containers if init=/bin/sh
382 * is passed but privileges dropped,
383 * as /proc/1/environ is only readable
384 * with privileges. */
386 r
= VIRTUALIZATION_NONE
;
396 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
397 if (streq(e
, value_table
[j
].value
)) {
398 r
= value_table
[j
].id
;
402 r
= VIRTUALIZATION_NONE
;
409 int detect_virtualization(void) {
412 r
= detect_container();
419 int running_in_chroot(void) {
422 ret
= files_same("/proc/1/root", "/");
429 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
430 [VIRTUALIZATION_NONE
] = "none",
431 [VIRTUALIZATION_KVM
] = "kvm",
432 [VIRTUALIZATION_QEMU
] = "qemu",
433 [VIRTUALIZATION_BOCHS
] = "bochs",
434 [VIRTUALIZATION_XEN
] = "xen",
435 [VIRTUALIZATION_UML
] = "uml",
436 [VIRTUALIZATION_VMWARE
] = "vmware",
437 [VIRTUALIZATION_ORACLE
] = "oracle",
438 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
439 [VIRTUALIZATION_ZVM
] = "zvm",
440 [VIRTUALIZATION_PARALLELS
] = "parallels",
441 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
443 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
444 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
445 [VIRTUALIZATION_LXC
] = "lxc",
446 [VIRTUALIZATION_OPENVZ
] = "openvz",
447 [VIRTUALIZATION_DOCKER
] = "docker",
448 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
451 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);