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 /* Both CPUID and DMI are x86 specific interfaces... */
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) {
148 /* Both CPUID and DMI are x86 specific interfaces... */
149 #if defined(__i386__) || defined(__x86_64__)
151 static const char *const dmi_vendors
[] = {
152 "/sys/class/dmi/id/sys_vendor",
153 "/sys/class/dmi/id/board_vendor",
154 "/sys/class/dmi/id/bios_vendor"
157 static const struct {
160 } dmi_vendor_table
[] = {
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 /* Try xen capabilities file first, if not found try
271 * high-level hypervisor sysfs file:
273 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
278 if (r
!= VIRTUALIZATION_NONE
)
284 if (r
!= VIRTUALIZATION_NONE
)
287 r
= detect_vm_cpuid();
290 if (r
!= VIRTUALIZATION_NONE
)
293 r
= detect_vm_hypervisor();
296 if (r
!= VIRTUALIZATION_NONE
)
299 r
= detect_vm_device_tree();
302 if (r
!= VIRTUALIZATION_NONE
)
308 if (r
!= VIRTUALIZATION_NONE
)
320 int detect_container(void) {
322 static const struct {
326 { "lxc", VIRTUALIZATION_LXC
},
327 { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT
},
328 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN
},
329 { "docker", VIRTUALIZATION_DOCKER
},
332 static thread_local
int cached_found
= _VIRTUALIZATION_INVALID
;
333 _cleanup_free_
char *m
= NULL
;
334 const char *e
= NULL
;
338 if (cached_found
>= 0)
341 /* /proc/vz exists in container and outside of the container,
342 * /proc/bc only outside of the container. */
343 if (access("/proc/vz", F_OK
) >= 0 &&
344 access("/proc/bc", F_OK
) < 0) {
345 r
= VIRTUALIZATION_OPENVZ
;
350 /* If we are PID 1 we can just check our own
351 * environment variable */
353 e
= getenv("container");
355 r
= VIRTUALIZATION_NONE
;
360 /* Otherwise, PID 1 dropped this information into a
361 * file in /run. This is better than accessing
362 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
365 r
= read_one_line_file("/run/systemd/container", &m
);
368 /* Fallback for cases where PID 1 was not
369 * systemd (for example, cases where
370 * init=/bin/sh is used. */
372 r
= getenv_for_pid(1, "container", &m
);
375 /* If that didn't work, give up,
376 * assume no container manager.
378 * Note: This means we still cannot
379 * detect containers if init=/bin/sh
380 * is passed but privileges dropped,
381 * as /proc/1/environ is only readable
382 * with privileges. */
384 r
= VIRTUALIZATION_NONE
;
394 for (j
= 0; j
< ELEMENTSOF(value_table
); j
++)
395 if (streq(e
, value_table
[j
].value
)) {
396 r
= value_table
[j
].id
;
400 r
= VIRTUALIZATION_NONE
;
407 int detect_virtualization(void) {
410 r
= detect_container();
417 int running_in_chroot(void) {
420 ret
= files_same("/proc/1/root", "/");
427 static const char *const virtualization_table
[_VIRTUALIZATION_MAX
] = {
428 [VIRTUALIZATION_NONE
] = "none",
429 [VIRTUALIZATION_KVM
] = "kvm",
430 [VIRTUALIZATION_QEMU
] = "qemu",
431 [VIRTUALIZATION_BOCHS
] = "bochs",
432 [VIRTUALIZATION_XEN
] = "xen",
433 [VIRTUALIZATION_UML
] = "uml",
434 [VIRTUALIZATION_VMWARE
] = "vmware",
435 [VIRTUALIZATION_ORACLE
] = "oracle",
436 [VIRTUALIZATION_MICROSOFT
] = "microsoft",
437 [VIRTUALIZATION_ZVM
] = "zvm",
438 [VIRTUALIZATION_PARALLELS
] = "parallels",
439 [VIRTUALIZATION_VM_OTHER
] = "vm-other",
441 [VIRTUALIZATION_SYSTEMD_NSPAWN
] = "systemd-nspawn",
442 [VIRTUALIZATION_LXC_LIBVIRT
] = "lxc-libvirt",
443 [VIRTUALIZATION_LXC
] = "lxc",
444 [VIRTUALIZATION_OPENVZ
] = "openvz",
445 [VIRTUALIZATION_DOCKER
] = "docker",
446 [VIRTUALIZATION_CONTAINER_OTHER
] = "container-other",
449 DEFINE_STRING_TABLE_LOOKUP(virtualization
, int);