From b52aae1d934b006830e7d575e56e2a98b0765ad3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Sep 2011 17:00:33 +0200 Subject: [PATCH] util: move virtualization detection into its own files, and extend return codes --- Makefile.am | 1 + src/detect-virt.c | 5 +- src/hostnamed.c | 1 + src/main.c | 1 + src/readahead-collect.c | 1 + src/readahead-replay.c | 1 + src/shutdown.c | 1 + src/util.c | 269 ---------------------------------- src/util.h | 4 - src/vconsole-setup.c | 1 + src/virt.c | 314 ++++++++++++++++++++++++++++++++++++++++ src/virt.h | 38 +++++ 12 files changed, 362 insertions(+), 275 deletions(-) create mode 100644 src/virt.c create mode 100644 src/virt.h diff --git a/Makefile.am b/Makefile.am index 6f90e9c93..757b66ab9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -613,6 +613,7 @@ noinst_LTLIBRARIES = \ libsystemd_basic_la_SOURCES = \ src/util.c \ + src/virt.c \ src/label.c \ src/hashmap.c \ src/set.c \ diff --git a/src/detect-virt.c b/src/detect-virt.c index 324f182c7..79cad5d8a 100644 --- a/src/detect-virt.c +++ b/src/detect-virt.c @@ -25,9 +25,10 @@ #include #include "util.h" +#include "virt.h" int main(int argc, char *argv[]) { - int r; + Virtualization r; const char *id; /* This is mostly intended to be used for scripts which want @@ -43,5 +44,5 @@ int main(int argc, char *argv[]) { if (r > 0) puts(id); - return r == 0; + return r > 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/hostnamed.c b/src/hostnamed.c index b47f22770..f3b2c9417 100644 --- a/src/hostnamed.c +++ b/src/hostnamed.c @@ -31,6 +31,7 @@ #include "dbus-common.h" #include "polkit.h" #include "def.h" +#include "virt.h" #define INTERFACE \ " \n" \ diff --git a/src/main.c b/src/main.c index bfc48e5e3..5c28a6c19 100644 --- a/src/main.c +++ b/src/main.c @@ -52,6 +52,7 @@ #include "build.h" #include "strv.h" #include "def.h" +#include "virt.h" static enum { ACTION_RUN, diff --git a/src/readahead-collect.c b/src/readahead-collect.c index df467f1a4..eac11e7e5 100644 --- a/src/readahead-collect.c +++ b/src/readahead-collect.c @@ -49,6 +49,7 @@ #include "sd-daemon.h" #include "ioprio.h" #include "readahead-common.h" +#include "virt.h" /* fixme: * diff --git a/src/readahead-replay.c b/src/readahead-replay.c index e97a0cfbb..65011ac4b 100644 --- a/src/readahead-replay.c +++ b/src/readahead-replay.c @@ -41,6 +41,7 @@ #include "sd-daemon.h" #include "ioprio.h" #include "readahead-common.h" +#include "virt.h" static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX; diff --git a/src/shutdown.c b/src/shutdown.c index 1c6dc6597..11213f9d5 100644 --- a/src/shutdown.c +++ b/src/shutdown.c @@ -41,6 +41,7 @@ #include "log.h" #include "umount.h" #include "util.h" +#include "virt.h" #define TIMEOUT_USEC (5 * USEC_PER_SEC) #define FINALIZE_ATTEMPTS 50 diff --git a/src/util.c b/src/util.c index ed3b8d421..425a73234 100644 --- a/src/util.c +++ b/src/util.c @@ -4267,275 +4267,6 @@ const char *default_term_for_tty(const char *tty) { return term; } -/* Returns a short identifier for the various VM implementations */ -int detect_vm(const char **id) { - -#if defined(__i386__) || defined(__x86_64__) - - /* Both CPUID and DMI are x86 specific interfaces... */ - - static const char *const dmi_vendors[] = { - "/sys/class/dmi/id/sys_vendor", - "/sys/class/dmi/id/board_vendor", - "/sys/class/dmi/id/bios_vendor" - }; - - static 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" - "Bochs\0" "bochs\0"; - - static 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"; - - uint32_t eax, ecx; - union { - uint32_t sig32[3]; - char text[13]; - } sig; - unsigned i; - const char *j, *k; - bool hypervisor; - - /* http://lwn.net/Articles/301888/ */ - zero(sig); - -#if defined (__i386__) -#define REG_a "eax" -#define REG_b "ebx" -#elif defined (__amd64__) -#define REG_a "rax" -#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" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=c" (ecx) - : "0" (eax) - ); - - hypervisor = !!(ecx & 0x80000000U); - - if (hypervisor) { - - /* 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; - } - } - - for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { - char *s; - int r; - const char *found = NULL; - - if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { - if (r != -ENOENT) - return r; - - continue; - } - - NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) - if (startswith(s, j)) - found = k; - free(s); - - if (found) { - if (id) - *id = found; - - return 1; - } - } - - if (hypervisor) { - if (id) - *id = "other-vm"; - - return 1; - } - -#endif - return 0; -} - -int detect_container(const char **id) { - FILE *f; - - /* Unfortunately many of these operations require root access - * in one way or another */ - - if (geteuid() != 0) - return -EPERM; - - if (running_in_chroot() > 0) { - - if (id) - *id = "chroot"; - - return 1; - } - - /* /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; - } - - f = fopen("/proc/1/environ", "re"); - if (f) { - bool done = false; - - do { - char line[LINE_MAX]; - unsigned i; - - for (i = 0; i < sizeof(line)-1; i++) { - int c; - - c = getc(f); - if (_unlikely_(c == EOF)) { - done = true; - break; - } else if (c == 0) - break; - - line[i] = c; - } - line[i] = 0; - - if (streq(line, "container=lxc")) { - fclose(f); - - if (id) - *id = "lxc"; - return 1; - - } else if (streq(line, "container=systemd-nspawn")) { - fclose(f); - - if (id) - *id = "systemd-nspawn"; - return 1; - - } else if (startswith(line, "container=")) { - fclose(f); - - if (id) - *id = "other-container"; - return 1; - } - - } while (!done); - - fclose(f); - } - - f = fopen("/proc/self/cgroup", "re"); - if (f) { - - for (;;) { - char line[LINE_MAX], *p; - - if (!fgets(line, sizeof(line), f)) - break; - - p = strchr(strstrip(line), ':'); - if (!p) - continue; - - if (strncmp(p, ":ns:", 4)) - continue; - - if (!streq(p, ":ns:/")) { - fclose(f); - - if (id) - *id = "pidns"; - - return 1; - } - } - - fclose(f); - } - - return 0; -} - -/* Returns a short identifier for the various VM/container implementations */ -int detect_virtualization(const char **id) { - static __thread const char *cached_id = NULL; - const char *_id; - int r; - - if (_likely_(cached_id)) { - - if (cached_id == (const char*) -1) - return 0; - - if (id) - *id = cached_id; - - return 1; - } - - if ((r = detect_container(&_id)) != 0) - goto finish; - - r = detect_vm(&_id); - -finish: - if (r > 0) { - cached_id = _id; - - if (id) - *id = _id; - } else if (r == 0) - cached_id = (const char*) -1; - - return r; -} - bool dirent_is_file(struct dirent *de) { assert(de); diff --git a/src/util.h b/src/util.h index e254bc788..ba0800dc0 100644 --- a/src/util.h +++ b/src/util.h @@ -406,10 +406,6 @@ bool tty_is_vc(const char *tty); int vtnr_from_tty(const char *tty); const char *default_term_for_tty(const char *tty); -int detect_vm(const char **id); -int detect_container(const char **id); -int detect_virtualization(const char **id); - void execute_directory(const char *directory, DIR *_d, char *argv[]); int kill_and_sigcont(pid_t pid, int sig); diff --git a/src/vconsole-setup.c b/src/vconsole-setup.c index 4347a2078..8a89358a2 100644 --- a/src/vconsole-setup.c +++ b/src/vconsole-setup.c @@ -39,6 +39,7 @@ #include "util.h" #include "log.h" #include "macro.h" +#include "virt.h" static bool is_vconsole(int fd) { unsigned char data[1]; diff --git a/src/virt.c b/src/virt.c new file mode 100644 index 000000000..380fabded --- /dev/null +++ b/src/virt.c @@ -0,0 +1,314 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 "util.h" +#include "virt.h" + +/* Returns a short identifier for the various VM implementations */ +int detect_vm(const char **id) { + +#if defined(__i386__) || defined(__x86_64__) + + /* Both CPUID and DMI are x86 specific interfaces... */ + + static const char *const dmi_vendors[] = { + "/sys/class/dmi/id/sys_vendor", + "/sys/class/dmi/id/board_vendor", + "/sys/class/dmi/id/bios_vendor" + }; + + static 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" + "Bochs\0" "bochs\0"; + + static 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"; + + uint32_t eax, ecx; + union { + uint32_t sig32[3]; + char text[13]; + } sig; + unsigned i; + const char *j, *k; + bool hypervisor; + + /* http://lwn.net/Articles/301888/ */ + zero(sig); + +#if defined (__i386__) +#define REG_a "eax" +#define REG_b "ebx" +#elif defined (__amd64__) +#define REG_a "rax" +#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" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=c" (ecx) + : "0" (eax) + ); + + hypervisor = !!(ecx & 0x80000000U); + + if (hypervisor) { + + /* 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; + } + } + + for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { + char *s; + int r; + const char *found = NULL; + + if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { + if (r != -ENOENT) + return r; + + continue; + } + + NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) + if (startswith(s, j)) + found = k; + free(s); + + if (found) { + if (id) + *id = found; + + return 1; + } + } + + if (hypervisor) { + if (id) + *id = "other"; + + return 1; + } + +#endif + return 0; +} + +int detect_container(const char **id) { + FILE *f; + + /* Unfortunately many of these operations require root access + * in one way or another */ + + if (geteuid() != 0) + return -EPERM; + + if (running_in_chroot() > 0) { + + if (id) + *id = "chroot"; + + return 1; + } + + /* /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; + } + + f = fopen("/proc/1/environ", "re"); + if (f) { + bool done = false; + + do { + char line[LINE_MAX]; + unsigned i; + + for (i = 0; i < sizeof(line)-1; i++) { + int c; + + c = getc(f); + if (_unlikely_(c == EOF)) { + done = true; + break; + } else if (c == 0) + break; + + line[i] = c; + } + line[i] = 0; + + if (streq(line, "container=lxc")) { + fclose(f); + + if (id) + *id = "lxc"; + return 1; + + } else if (streq(line, "container=systemd-nspawn")) { + fclose(f); + + if (id) + *id = "systemd-nspawn"; + return 1; + + } else if (startswith(line, "container=")) { + fclose(f); + + if (id) + *id = "other"; + return 1; + } + + } while (!done); + + fclose(f); + } + + f = fopen("/proc/self/cgroup", "re"); + if (f) { + + for (;;) { + char line[LINE_MAX], *p; + + if (!fgets(line, sizeof(line), f)) + break; + + p = strchr(strstrip(line), ':'); + if (!p) + continue; + + if (strncmp(p, ":ns:", 4)) + continue; + + if (!streq(p, ":ns:/")) { + fclose(f); + + if (id) + *id = "pidns"; + + return 1; + } + } + + fclose(f); + } + + return 0; +} + +/* Returns a short identifier for the various VM/container implementations */ +Virtualization detect_virtualization(const char **id) { + + static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID; + static __thread const char *cached_id = NULL; + + const char *_id; + int r; + Virtualization v; + + if (_likely_(cached_virt >= 0)) { + + if (id && cached_virt > 0) + *id = cached_id; + + return cached_virt; + } + + r = detect_container(&_id); + if (r < 0) { + v = r; + goto finish; + } else if (r > 0) { + v = VIRTUALIZATION_CONTAINER; + goto finish; + } + + r = detect_vm(&_id); + if (r < 0) { + v = r; + goto finish; + } else if (r > 0) { + v = VIRTUALIZATION_VM; + goto finish; + } + + v = VIRTUALIZATION_NONE; + +finish: + if (v > 0) { + cached_id = _id; + + if (id) + *id = _id; + } + + if (v >= 0) + cached_virt = v; + + return v; +} diff --git a/src/virt.h b/src/virt.h new file mode 100644 index 000000000..f55c9a68f --- /dev/null +++ b/src/virt.h @@ -0,0 +1,38 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foovirthfoo +#define foovirthfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 . +***/ + +int detect_vm(const char **id); +int detect_container(const char **id); + +typedef enum Virtualization { + VIRTUALIZATION_NONE = 0, + VIRTUALIZATION_VM, + VIRTUALIZATION_CONTAINER, + _VIRTUALIZATION_MAX, + _VIRTUALIZATION_INVALID = -1 +} Virtualization; + +Virtualization detect_virtualization(const char **id); + +#endif -- 2.30.2