LCOV - code coverage report
Current view: top level - basic - virt.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 74 142 52.1 %
Date: 2015-07-29 18:47:03 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2011 Lennart Poettering
       7             : 
       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.
      12             : 
      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.
      17             : 
      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/>.
      20             : ***/
      21             : 
      22             : #include <string.h>
      23             : #include <errno.h>
      24             : #include <unistd.h>
      25             : 
      26             : #include "util.h"
      27             : #include "process-util.h"
      28             : #include "virt.h"
      29             : #include "fileio.h"
      30             : 
      31           2 : static int detect_vm_cpuid(const char **_id) {
      32             : 
      33             :         /* Both CPUID and DMI are x86 specific interfaces... */
      34             : #if defined(__i386__) || defined(__x86_64__)
      35             : 
      36             :         static const char cpuid_vendor_table[] =
      37             :                 "XenVMMXenVMM\0"          "xen\0"
      38             :                 "KVMKVMKVM\0"             "kvm\0"
      39             :                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
      40             :                 "VMwareVMware\0"          "vmware\0"
      41             :                 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
      42             :                 "Microsoft Hv\0"          "microsoft\0";
      43             : 
      44             :         uint32_t eax, ecx;
      45             :         union {
      46             :                 uint32_t sig32[3];
      47             :                 char text[13];
      48           2 :         } sig = {};
      49             :         const char *j, *k;
      50             :         bool hypervisor;
      51             : 
      52             :         /* http://lwn.net/Articles/301888/ */
      53             : 
      54             : #if defined (__i386__)
      55             : #define REG_a "eax"
      56             : #define REG_b "ebx"
      57             : #elif defined (__amd64__)
      58             : #define REG_a "rax"
      59             : #define REG_b "rbx"
      60             : #endif
      61             : 
      62             :         /* First detect whether there is a hypervisor */
      63           2 :         eax = 1;
      64           2 :         __asm__ __volatile__ (
      65             :                 /* ebx/rbx is being used for PIC! */
      66             :                 "  push %%"REG_b"         \n\t"
      67             :                 "  cpuid                  \n\t"
      68             :                 "  pop %%"REG_b"          \n\t"
      69             : 
      70             :                 : "=a" (eax), "=c" (ecx)
      71             :                 : "0" (eax)
      72             :         );
      73             : 
      74           2 :         hypervisor = !!(ecx & 0x80000000U);
      75             : 
      76           2 :         if (hypervisor) {
      77             : 
      78             :                 /* There is a hypervisor, see what it is */
      79           0 :                 eax = 0x40000000U;
      80           0 :                 __asm__ __volatile__ (
      81             :                         /* ebx/rbx is being used for PIC! */
      82             :                         "  push %%"REG_b"         \n\t"
      83             :                         "  cpuid                  \n\t"
      84             :                         "  mov %%ebx, %1          \n\t"
      85             :                         "  pop %%"REG_b"          \n\t"
      86             : 
      87             :                         : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
      88             :                         : "0" (eax)
      89             :                 );
      90             : 
      91           0 :                 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
      92           0 :                         if (streq(sig.text, j)) {
      93           0 :                                 *_id = k;
      94           0 :                                 return 1;
      95             :                         }
      96             : 
      97           0 :                 *_id = "other";
      98           0 :                 return 0;
      99             :         }
     100             : #endif
     101             : 
     102           2 :         return 0;
     103             : }
     104             : 
     105           2 : static int detect_vm_devicetree(const char **_id) {
     106             : #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
     107             :         _cleanup_free_ char *hvtype = NULL;
     108             :         int r;
     109             : 
     110             :         r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
     111             :         if (r >= 0) {
     112             :                 if (streq(hvtype, "linux,kvm")) {
     113             :                         *_id = "kvm";
     114             :                         return 1;
     115             :                 } else if (strstr(hvtype, "xen")) {
     116             :                         *_id = "xen";
     117             :                         return 1;
     118             :                 }
     119             :         } else if (r == -ENOENT) {
     120             :                 _cleanup_closedir_ DIR *dir = NULL;
     121             :                 struct dirent *dent;
     122             : 
     123             :                 dir = opendir("/proc/device-tree");
     124             :                 if (!dir) {
     125             :                         if (errno == ENOENT)
     126             :                                 return 0;
     127             :                         return -errno;
     128             :                 }
     129             : 
     130             :                 FOREACH_DIRENT(dent, dir, return -errno) {
     131             :                         if (strstr(dent->d_name, "fw-cfg")) {
     132             :                                 *_id = "qemu";
     133             :                                 return 1;
     134             :                         }
     135             :                 }
     136             :         }
     137             : #endif
     138           2 :         return 0;
     139             : }
     140             : 
     141           2 : static int detect_vm_dmi(const char **_id) {
     142             : 
     143             :         /* Both CPUID and DMI are x86 specific interfaces... */
     144             : #if defined(__i386__) || defined(__x86_64__)
     145             : 
     146             :         static const char *const dmi_vendors[] = {
     147             :                 "/sys/class/dmi/id/sys_vendor",
     148             :                 "/sys/class/dmi/id/board_vendor",
     149             :                 "/sys/class/dmi/id/bios_vendor"
     150             :         };
     151             : 
     152             :         static const char dmi_vendor_table[] =
     153             :                 "QEMU\0"                  "qemu\0"
     154             :                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
     155             :                 "VMware\0"                "vmware\0"
     156             :                 "VMW\0"                   "vmware\0"
     157             :                 "innotek GmbH\0"          "oracle\0"
     158             :                 "Xen\0"                   "xen\0"
     159             :                 "Bochs\0"                 "bochs\0";
     160             :         unsigned i;
     161             : 
     162           8 :         for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
     163          12 :                 _cleanup_free_ char *s = NULL;
     164             :                 const char *j, *k;
     165             :                 int r;
     166             : 
     167           6 :                 r = read_one_line_file(dmi_vendors[i], &s);
     168           6 :                 if (r < 0) {
     169           0 :                         if (r != -ENOENT)
     170           0 :                                 return r;
     171             : 
     172           0 :                         continue;
     173             :                 }
     174             : 
     175          42 :                 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
     176          36 :                         if (startswith(s, j)) {
     177           0 :                                 *_id = k;
     178           0 :                                 return 1;
     179             :                         }
     180             :         }
     181             : #endif
     182             : 
     183           2 :         return 0;
     184             : }
     185             : 
     186             : /* Returns a short identifier for the various VM implementations */
     187           2 : int detect_vm(const char **id) {
     188           4 :         _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
     189             :         static thread_local int cached_found = -1;
     190             :         static thread_local const char *cached_id = NULL;
     191           2 :         const char *_id = NULL, *_id_cpuid = NULL;
     192             :         int r;
     193             : 
     194           2 :         if (_likely_(cached_found >= 0)) {
     195             : 
     196           0 :                 if (id)
     197           0 :                         *id = cached_id;
     198             : 
     199           0 :                 return cached_found;
     200             :         }
     201             : 
     202             :         /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
     203             :          *
     204             :          * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
     205           2 :         r = read_one_line_file("/proc/xen/capabilities", &domcap);
     206           2 :         if (r >= 0) {
     207           0 :                 char *cap, *i = domcap;
     208             : 
     209           0 :                 while ((cap = strsep(&i, ",")))
     210           0 :                         if (streq(cap, "control_d"))
     211           0 :                                 break;
     212             : 
     213           0 :                 if (!cap)  {
     214           0 :                         _id = "xen";
     215           0 :                         r = 1;
     216             :                 }
     217             : 
     218           0 :                 goto finish;
     219             : 
     220           2 :         } else if (r == -ENOENT) {
     221           4 :                 _cleanup_free_ char *hvtype = NULL;
     222             : 
     223           2 :                 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
     224           2 :                 if (r >= 0) {
     225           0 :                         if (streq(hvtype, "xen")) {
     226           0 :                                 _id = "xen";
     227           0 :                                 r = 1;
     228           0 :                                 goto finish;
     229             :                         }
     230           2 :                 } else if (r != -ENOENT)
     231           0 :                         return r;
     232             :         } else
     233           0 :                 return r;
     234             : 
     235             :         /* this will set _id to "other" and return 0 for unknown hypervisors */
     236           2 :         r = detect_vm_cpuid(&_id);
     237             : 
     238             :         /* finish when found a known hypervisor other than kvm */
     239           2 :         if (r < 0 || (r > 0 && !streq(_id, "kvm")))
     240             :                 goto finish;
     241             : 
     242           2 :         _id_cpuid = _id;
     243             : 
     244           2 :         r = detect_vm_dmi(&_id);
     245             : 
     246             :         /* kvm with and without Virtualbox */
     247           2 :         if (streq_ptr(_id_cpuid, "kvm")) {
     248           0 :                 if (r > 0 && streq(_id, "oracle"))
     249           0 :                         goto finish;
     250             : 
     251           0 :                 _id = _id_cpuid;
     252           0 :                 r = 1;
     253           0 :                 goto finish;
     254             :         }
     255             : 
     256             :         /* information from dmi */
     257           2 :         if (r != 0)
     258           0 :                 goto finish;
     259             : 
     260           2 :         r = detect_vm_devicetree(&_id);
     261           2 :         if (r != 0)
     262           0 :                 goto finish;
     263             : 
     264           2 :         if (_id) {
     265             :                 /* "other" */
     266           0 :                 r = 1;
     267           0 :                 goto finish;
     268             :         }
     269             : 
     270             :         /* Detect User-Mode Linux by reading /proc/cpuinfo */
     271           2 :         r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
     272           2 :         if (r < 0)
     273           0 :                 return r;
     274           2 :         if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
     275           0 :                 _id = "uml";
     276           0 :                 r = 1;
     277           0 :                 goto finish;
     278             :         }
     279             : 
     280             : #if defined(__s390__)
     281             :         {
     282             :                 _cleanup_free_ char *t = NULL;
     283             : 
     284             :                 r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
     285             :                 if (r >= 0) {
     286             :                         if (streq(t, "z/VM"))
     287             :                                 _id = "zvm";
     288             :                         else
     289             :                                 _id = "kvm";
     290             :                         r = 1;
     291             : 
     292             :                         goto finish;
     293             :                 }
     294             :         }
     295             : #endif
     296             : 
     297           2 :         r = 0;
     298             : 
     299             : finish:
     300           2 :         cached_found = r;
     301             : 
     302           2 :         cached_id = _id;
     303           2 :         if (id)
     304           2 :                 *id = _id;
     305             : 
     306           2 :         return r;
     307             : }
     308             : 
     309          24 : int detect_container(const char **id) {
     310             : 
     311             :         static thread_local int cached_found = -1;
     312             :         static thread_local const char *cached_id = NULL;
     313             : 
     314          48 :         _cleanup_free_ char *m = NULL;
     315          24 :         const char *_id = NULL, *e = NULL;
     316             :         int r;
     317             : 
     318          24 :         if (_likely_(cached_found >= 0)) {
     319             : 
     320          14 :                 if (id)
     321           1 :                         *id = cached_id;
     322             : 
     323          14 :                 return cached_found;
     324             :         }
     325             : 
     326             :         /* /proc/vz exists in container and outside of the container,
     327             :          * /proc/bc only outside of the container. */
     328          10 :         if (access("/proc/vz", F_OK) >= 0 &&
     329           0 :             access("/proc/bc", F_OK) < 0) {
     330           0 :                 _id = "openvz";
     331           0 :                 r = 1;
     332           0 :                 goto finish;
     333             :         }
     334             : 
     335          10 :         if (getpid() == 1) {
     336             :                 /* If we are PID 1 we can just check our own
     337             :                  * environment variable */
     338             : 
     339           0 :                 e = getenv("container");
     340           0 :                 if (isempty(e)) {
     341           0 :                         r = 0;
     342           0 :                         goto finish;
     343             :                 }
     344             :         } else {
     345             : 
     346             :                 /* Otherwise, PID 1 dropped this information into a
     347             :                  * file in /run. This is better than accessing
     348             :                  * /proc/1/environ, since we don't need CAP_SYS_PTRACE
     349             :                  * for that. */
     350             : 
     351          10 :                 r = read_one_line_file("/run/systemd/container", &m);
     352          10 :                 if (r == -ENOENT) {
     353             : 
     354             :                         /* Fallback for cases where PID 1 was not
     355             :                          * systemd (for example, cases where
     356             :                          * init=/bin/sh is used. */
     357             : 
     358          10 :                         r = getenv_for_pid(1, "container", &m);
     359          10 :                         if (r <= 0) {
     360             : 
     361             :                                 /* If that didn't work, give up,
     362             :                                  * assume no container manager.
     363             :                                  *
     364             :                                  * Note: This means we still cannot
     365             :                                  * detect containers if init=/bin/sh
     366             :                                  * is passed but privileges dropped,
     367             :                                  * as /proc/1/environ is only readable
     368             :                                  * with privileges. */
     369             : 
     370          10 :                                 r = 0;
     371          10 :                                 goto finish;
     372             :                         }
     373             :                 }
     374           0 :                 if (r < 0)
     375           0 :                         return r;
     376             : 
     377           0 :                 e = m;
     378             :         }
     379             : 
     380             :         /* We only recognize a selected few here, since we want to
     381             :          * enforce a redacted namespace */
     382           0 :         if (streq(e, "lxc"))
     383           0 :                 _id ="lxc";
     384           0 :         else if (streq(e, "lxc-libvirt"))
     385           0 :                 _id = "lxc-libvirt";
     386           0 :         else if (streq(e, "systemd-nspawn"))
     387           0 :                 _id = "systemd-nspawn";
     388           0 :         else if (streq(e, "docker"))
     389           0 :                 _id = "docker";
     390             :         else
     391           0 :                 _id = "other";
     392             : 
     393           0 :         r = 1;
     394             : 
     395             : finish:
     396          10 :         cached_found = r;
     397             : 
     398          10 :         cached_id = _id;
     399          10 :         if (id)
     400           1 :                 *id = _id;
     401             : 
     402          10 :         return r;
     403             : }
     404             : 
     405             : /* Returns a short identifier for the various VM/container implementations */
     406           2 : int detect_virtualization(const char **id) {
     407             :         int r;
     408             : 
     409           2 :         r = detect_container(id);
     410           2 :         if (r < 0)
     411           0 :                 return r;
     412           2 :         if (r > 0)
     413           0 :                 return VIRTUALIZATION_CONTAINER;
     414             : 
     415           2 :         r = detect_vm(id);
     416           2 :         if (r < 0)
     417           0 :                 return r;
     418           2 :         if (r > 0)
     419           0 :                 return VIRTUALIZATION_VM;
     420             : 
     421           2 :         return VIRTUALIZATION_NONE;
     422             : }

Generated by: LCOV version 1.11