From 07faed4f99d0c798f92de3032b9c20ca31388494 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 21 Feb 2011 21:48:59 +0100 Subject: [PATCH] virtualization: beef virtualization code --- .gitignore | 1 + Makefile.am | 10 ++++ src/detect-virt.c | 46 +++++++++++++++ src/readahead-collect.c | 4 +- src/readahead-replay.c | 4 +- src/util.c | 127 ++++++++++++++++++++++++++++++++-------- src/util.h | 3 +- 7 files changed, 165 insertions(+), 30 deletions(-) create mode 100644 src/detect-virt.c diff --git a/.gitignore b/.gitignore index 1bb184f8754..adee97f6ce3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-detect-virt systemd-sysctl test-strv systemd-ac-power diff --git a/Makefile.am b/Makefile.am index f508163d5b4..7e5d6d87646 100644 --- a/Makefile.am +++ b/Makefile.am @@ -129,6 +129,7 @@ rootlibexec_PROGRAMS = \ systemd-quotacheck \ systemd-timestamp \ systemd-ac-power \ + systemd-detect-virt \ systemd-sysctl if HAVE_LIBCRYPTSETUP @@ -783,6 +784,15 @@ systemd_ac_power_LDADD = \ libsystemd-basic.la \ $(UDEV_LIBS) +systemd_detect_virt_SOURCES = \ + src/detect-virt.c + +systemd_detect_virt_CFLAGS = \ + $(AM_CFLAGS) + +systemd_detect_virt_LDADD = \ + libsystemd-basic.la + systemd_cryptsetup_SOURCES = \ src/cryptsetup.c \ src/ask-password-api.c diff --git a/src/detect-virt.c b/src/detect-virt.c new file mode 100644 index 00000000000..7685d18cc62 --- /dev/null +++ b/src/detect-virt.c @@ -0,0 +1,46 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "util.h" + +int main(int argc, char *argv[]) { + int r; + const char *id; + + /* This is mostly intended to be used for scripts which want + * to detect whether we are being run in a virtualized + * environment or not */ + + if ((r = detect_virtualization(&id)) < 0) { + log_error("Failed to check for virtualization: %s", strerror(-r)); + return EXIT_FAILURE; + } + + if (r > 0) + printf("%s\n", id); + + return r == 0; +} diff --git a/src/readahead-collect.c b/src/readahead-collect.c index ea07b3ff497..330d10776fc 100644 --- a/src/readahead-collect.c +++ b/src/readahead-collect.c @@ -652,8 +652,8 @@ int main(int argc, char *argv[]) { return 0; } - if (running_in_vm()) { - log_info("Disabling readahead collector due to execution in virtual machine."); + if (detect_virtualization(NULL) > 0) { + log_info("Disabling readahead collector due to execution in virtualized environment."); return 0; } diff --git a/src/readahead-replay.c b/src/readahead-replay.c index 9447fe0775b..cd89654f983 100644 --- a/src/readahead-replay.c +++ b/src/readahead-replay.c @@ -346,8 +346,8 @@ int main(int argc, char*argv[]) { return 0; } - if (running_in_vm()) { - log_info("Disabling readahead replay due to execution in virtual machine."); + if (detect_virtualization(NULL) > 0) { + log_info("Disabling readahead replay due to execution in virtualized environment."); return 0; } diff --git a/src/util.c b/src/util.c index b8e73ef4bf4..7e8246bb611 100644 --- a/src/util.c +++ b/src/util.c @@ -3706,7 +3706,8 @@ const char *default_term_for_tty(const char *tty) { return term; } -bool running_in_vm(void) { +/* Returns a short identifier for the various VM implementations */ +int detect_vm(const char **id) { #if defined(__i386__) || defined(__x86_64__) @@ -3718,39 +3719,62 @@ bool running_in_vm(void) { "/sys/class/dmi/id/bios_vendor" }; - uint32_t eax = 0x40000000; + const char dmi_vendor_table[] = + "QEMU\0" "qemu\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMware\0" "vmware\0" + "VMW\0" "vmware\0" + "Microsoft Corporation\0" "microsoft\0" + "innotek GmbH\0" "oracle\0" + "Xen\0" "xen\0" + "\0"; + + const char cpuid_vendor_table[] = + "XenVMMXenVMM\0" "xen\0" + "KVMKVMKVM\0" "kvm\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMwareVMware\0" "vmware\0" + /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + "Microsoft Hv\0" "microsoft\0" + "\0"; + + uint32_t eax, ecx; union { uint32_t sig32[3]; char text[13]; } sig; unsigned i; + const char *j, *k; for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { char *s; - bool b; + int r; + const char *found = NULL; + + if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { + if (r != -ENOENT) + return r; - if (read_one_line_file(dmi_vendors[i], &s) < 0) continue; + } - b = startswith(s, "QEMU") || - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - startswith(s, "VMware") || - startswith(s, "VMW") || - startswith(s, "Microsoft Corporation") || - startswith(s, "innotek GmbH") || - startswith(s, "Xen"); - + NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) + if (startswith(s, j)) + found = k; free(s); - if (b) - return true; + if (found) { + if (id) + *id = found; + + return 1; + } } /* http://lwn.net/Articles/301888/ */ zero(sig); - #if defined (__i386__) #define REG_a "eax" #define REG_b "ebx" @@ -3759,27 +3783,80 @@ bool running_in_vm(void) { #define REG_b "rbx" #endif + /* First detect whether there is a hypervisor */ + eax = 1; __asm__ __volatile__ ( /* ebx/rbx is being used for PIC! */ " push %%"REG_b" \n\t" " cpuid \n\t" - " mov %%ebx, %1 \n\t" " pop %%"REG_b" \n\t" - : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "=a" (eax), "=c" (ecx) : "0" (eax) ); - if (streq(sig.text, "XenVMMXenVMM") || - streq(sig.text, "KVMKVMKVM") || - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - streq(sig.text, "VMwareVMware") || - /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */ - streq(sig.text, "Microsoft Hv")) - return true; + if (ecx & 0x80000000U) { + + /* There is a hypervisor, see what it is */ + eax = 0x40000000U; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " mov %%ebx, %1 \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "0" (eax) + ); + + NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table) + if (streq(sig.text, j)) { + + if (id) + *id = k; + + return 1; + } + + if (id) + *id = "other"; + + return 1; + } #endif - return false; + return 0; +} + +/* Returns a short identifier for the various VM/container implementations */ +int detect_virtualization(const char **id) { + int r; + + /* Unfortunately most of these operations require root access + * in one way or another */ + if (geteuid() != 0) + return -EPERM; + + if ((r = running_in_chroot()) > 0) { + if (id) + *id = "chroot"; + + return r; + } + + /* /proc/vz exists in container and outside of the container, + * /proc/bc only outside of the container. */ + if (access("/proc/vz", F_OK) >= 0 && + access("/proc/bc", F_OK) < 0) { + + if (id) + *id = "openvz"; + + return 1; + } + + return detect_vm(id); } void execute_directory(const char *directory, DIR *d, char *argv[]) { diff --git a/src/util.h b/src/util.h index 80f1a38b574..7f2cc080a75 100644 --- a/src/util.h +++ b/src/util.h @@ -378,7 +378,8 @@ void filter_environ(const char *prefix); bool tty_is_vc(const char *tty); const char *default_term_for_tty(const char *tty); -bool running_in_vm(void); +int detect_vm(const char **id); +int detect_virtualization(const char **id); void execute_directory(const char *directory, DIR *_d, char *argv[]);