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/>.
30 static int detect_vm_cpuid(const char **_id) {
32 /* Both CPUID and DMI are x86 specific interfaces... */
33 #if defined(__i386__) || defined(__x86_64__)
35 static const char cpuid_vendor_table[] =
36 "XenVMMXenVMM\0" "xen\0"
38 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
39 "VMwareVMware\0" "vmware\0"
40 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
41 "Microsoft Hv\0" "microsoft\0";
51 /* http://lwn.net/Articles/301888/ */
53 #if defined (__i386__)
56 #elif defined (__amd64__)
61 /* First detect whether there is a hypervisor */
63 __asm__ __volatile__ (
64 /* ebx/rbx is being used for PIC! */
65 " push %%"REG_b" \n\t"
69 : "=a" (eax), "=c" (ecx)
73 hypervisor = !!(ecx & 0x80000000U);
77 /* There is a hypervisor, see what it is */
79 __asm__ __volatile__ (
80 /* ebx/rbx is being used for PIC! */
81 " push %%"REG_b" \n\t"
86 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
90 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
91 if (streq(sig.text, j)) {
104 static int detect_vm_devicetree(const char **_id) {
105 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
106 _cleanup_free_ char *hvtype = NULL;
109 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
111 if (streq(hvtype, "linux,kvm")) {
114 } else if (strstr(hvtype, "xen")) {
118 } else if (r == -ENOENT) {
119 _cleanup_closedir_ DIR *dir = NULL;
122 dir = opendir("/proc/device-tree");
129 FOREACH_DIRENT(dent, dir, return -errno) {
130 if (strstr(dent->d_name, "fw-cfg")) {
140 static int detect_vm_dmi(const char **_id) {
142 /* Both CPUID and DMI are x86 specific interfaces... */
143 #if defined(__i386__) || defined(__x86_64__)
145 static const char *const dmi_vendors[] = {
146 "/sys/class/dmi/id/sys_vendor",
147 "/sys/class/dmi/id/board_vendor",
148 "/sys/class/dmi/id/bios_vendor"
151 static const char dmi_vendor_table[] =
153 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
154 "VMware\0" "vmware\0"
156 "innotek GmbH\0" "oracle\0"
161 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
162 _cleanup_free_ char *s = NULL;
166 r = read_one_line_file(dmi_vendors[i], &s);
174 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
175 if (startswith(s, j)) {
185 /* Returns a short identifier for the various VM implementations */
186 int detect_vm(const char **id) {
187 _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
188 static thread_local int cached_found = -1;
189 static thread_local const char *cached_id = NULL;
190 const char *_id = NULL;
193 if (_likely_(cached_found >= 0)) {
201 /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
203 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
204 r = read_one_line_file("/proc/xen/capabilities", &domcap);
206 char *cap, *i = domcap;
208 while ((cap = strsep(&i, ",")))
209 if (streq(cap, "control_d"))
219 } else if (r == -ENOENT) {
220 _cleanup_free_ char *hvtype = NULL;
222 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
224 if (streq(hvtype, "xen")) {
229 } else if (r != -ENOENT)
234 /* this will set _id to "other" and return 0 for unknown hypervisors */
235 r = detect_vm_cpuid(&_id);
239 r = detect_vm_dmi(&_id);
243 r = detect_vm_devicetree(&_id);
253 /* Detect User-Mode Linux by reading /proc/cpuinfo */
254 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
257 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
263 #if defined(__s390__)
265 _cleanup_free_ char *t = NULL;
267 r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
269 if (streq(t, "z/VM"))
292 int detect_container(const char **id) {
294 static thread_local int cached_found = -1;
295 static thread_local const char *cached_id = NULL;
297 _cleanup_free_ char *m = NULL;
298 const char *_id = NULL, *e = NULL;
301 if (_likely_(cached_found >= 0)) {
309 /* /proc/vz exists in container and outside of the container,
310 * /proc/bc only outside of the container. */
311 if (access("/proc/vz", F_OK) >= 0 &&
312 access("/proc/bc", F_OK) < 0) {
319 /* If we are PID 1 we can just check our own
320 * environment variable */
322 e = getenv("container");
329 /* Otherwise, PID 1 dropped this information into a
330 * file in /run. This is better than accessing
331 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
334 r = read_one_line_file("/run/systemd/container", &m);
337 /* Fallback for cases where PID 1 was not
338 * systemd (for example, cases where
339 * init=/bin/sh is used. */
341 r = getenv_for_pid(1, "container", &m);
344 /* If that didn't work, give up,
345 * assume no container manager.
347 * Note: This means we still cannot
348 * detect containers if init=/bin/sh
349 * is passed but privileges dropped,
350 * as /proc/1/environ is only readable
351 * with privileges. */
363 /* We only recognize a selected few here, since we want to
364 * enforce a redacted namespace */
367 else if (streq(e, "lxc-libvirt"))
369 else if (streq(e, "systemd-nspawn"))
370 _id = "systemd-nspawn";
371 else if (streq(e, "docker"))
388 /* Returns a short identifier for the various VM/container implementations */
389 int detect_virtualization(const char **id) {
392 r = detect_container(id);
396 return VIRTUALIZATION_CONTAINER;
402 return VIRTUALIZATION_VM;
404 return VIRTUALIZATION_NONE;