chiark / gitweb /
path-util: unify code for detecting OS trees
[elogind.git] / src / shared / virt.c
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 "virt.h"
28 #include "fileio.h"
29
30 /* Returns a short identifier for the various VM implementations */
31 int detect_vm(const char **id) {
32
33 #if defined(__i386__) || defined(__x86_64__)
34
35         /* Both CPUID and DMI are x86 specific interfaces... */
36
37         static const char *const dmi_vendors[] = {
38                 "/sys/class/dmi/id/sys_vendor",
39                 "/sys/class/dmi/id/board_vendor",
40                 "/sys/class/dmi/id/bios_vendor"
41         };
42
43         static const char dmi_vendor_table[] =
44                 "QEMU\0"                  "qemu\0"
45                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
46                 "VMware\0"                "vmware\0"
47                 "VMW\0"                   "vmware\0"
48                 "Microsoft Corporation\0" "microsoft\0"
49                 "innotek GmbH\0"          "oracle\0"
50                 "Xen\0"                   "xen\0"
51                 "Bochs\0"                 "bochs\0";
52
53         static const char cpuid_vendor_table[] =
54                 "XenVMMXenVMM\0"          "xen\0"
55                 "KVMKVMKVM\0"             "kvm\0"
56                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
57                 "VMwareVMware\0"          "vmware\0"
58                 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
59                 "Microsoft Hv\0"          "microsoft\0";
60
61         uint32_t eax, ecx;
62         union {
63                 uint32_t sig32[3];
64                 char text[13];
65         } sig = {};
66         unsigned i;
67         const char *j, *k;
68         bool hypervisor;
69         _cleanup_free_ char *hvtype = NULL;
70         int r;
71
72         /* Try high-level hypervisor sysfs file first:
73          *
74          * https://bugs.freedesktop.org/show_bug.cgi?id=61491 */
75         r = read_one_line_file("/sys/hypervisor/type", &hvtype);
76         if (r >= 0) {
77                 if (streq(hvtype, "xen")) {
78                         if (id)
79                                 *id = "xen";
80
81                         return 1;
82                 }
83         } else if (r != -ENOENT)
84                 return r;
85
86         /* http://lwn.net/Articles/301888/ */
87
88 #if defined (__i386__)
89 #define REG_a "eax"
90 #define REG_b "ebx"
91 #elif defined (__amd64__)
92 #define REG_a "rax"
93 #define REG_b "rbx"
94 #endif
95
96         /* First detect whether there is a hypervisor */
97         eax = 1;
98         __asm__ __volatile__ (
99                 /* ebx/rbx is being used for PIC! */
100                 "  push %%"REG_b"         \n\t"
101                 "  cpuid                  \n\t"
102                 "  pop %%"REG_b"          \n\t"
103
104                 : "=a" (eax), "=c" (ecx)
105                 : "0" (eax)
106         );
107
108         hypervisor = !!(ecx & 0x80000000U);
109
110         if (hypervisor) {
111
112                 /* There is a hypervisor, see what it is */
113                 eax = 0x40000000U;
114                 __asm__ __volatile__ (
115                         /* ebx/rbx is being used for PIC! */
116                         "  push %%"REG_b"         \n\t"
117                         "  cpuid                  \n\t"
118                         "  mov %%ebx, %1          \n\t"
119                         "  pop %%"REG_b"          \n\t"
120
121                         : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
122                         : "0" (eax)
123                 );
124
125                 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
126                         if (streq(sig.text, j)) {
127
128                                 if (id)
129                                         *id = k;
130
131                                 return 1;
132                         }
133         }
134
135         for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
136                 _cleanup_free_ char *s = NULL;
137                 const char *found = NULL;
138
139                 r = read_one_line_file(dmi_vendors[i], &s);
140                 if (r < 0) {
141                         if (r != -ENOENT)
142                                 return r;
143
144                         continue;
145                 }
146
147                 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
148                         if (startswith(s, j))
149                                 found = k;
150
151                 if (found) {
152                         if (id)
153                                 *id = found;
154
155                         return 1;
156                 }
157         }
158
159         if (hypervisor || hvtype) {
160                 if (id)
161                         *id = "other";
162
163                 return 1;
164         }
165
166 #endif
167         return 0;
168 }
169
170 int detect_container(const char **id) {
171         char *e = NULL;
172         int r;
173
174         /* Unfortunately many of these operations require root access
175          * in one way or another */
176
177         r = running_in_chroot();
178         if (r < 0)
179                 return r;
180         if (r > 0) {
181
182                 if (id)
183                         *id = "chroot";
184
185                 return 1;
186         }
187
188         /* /proc/vz exists in container and outside of the container,
189          * /proc/bc only outside of the container. */
190         if (access("/proc/vz", F_OK) >= 0 &&
191             access("/proc/bc", F_OK) < 0) {
192
193                 if (id)
194                         *id = "openvz";
195
196                 return 1;
197         }
198
199         r = getenv_for_pid(1, "container", &e);
200         if (r <= 0)
201                 return r;
202
203         /* We only recognize a selected few here, since we want to
204          * enforce a redacted namespace */
205         if (streq(e, "lxc")) {
206                 if (id)
207                         *id = "lxc";
208         } else if (streq(e, "lxc-libvirt")) {
209                 if (id)
210                         *id = "lxc-libvirt";
211         } else if (streq(e, "systemd-nspawn")) {
212                 if (id)
213                         *id = "systemd-nspawn";
214         } else {
215                 if (id)
216                         *id = "other";
217         }
218
219         free(e);
220
221         return r;
222 }
223
224 /* Returns a short identifier for the various VM/container implementations */
225 Virtualization detect_virtualization(const char **id) {
226
227         static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
228         static __thread const char *cached_id = NULL;
229
230         const char *_id;
231         int r;
232         Virtualization v;
233
234         if (_likely_(cached_virt >= 0)) {
235
236                 if (id && cached_virt > 0)
237                         *id = cached_id;
238
239                 return cached_virt;
240         }
241
242         r = detect_container(&_id);
243         if (r < 0) {
244                 v = r;
245                 goto finish;
246         } else if (r > 0) {
247                 v = VIRTUALIZATION_CONTAINER;
248                 goto finish;
249         }
250
251         r = detect_vm(&_id);
252         if (r < 0) {
253                 v = r;
254                 goto finish;
255         } else if (r > 0) {
256                 v = VIRTUALIZATION_VM;
257                 goto finish;
258         }
259
260         v = VIRTUALIZATION_NONE;
261
262 finish:
263         if (v > 0) {
264                 cached_id = _id;
265
266                 if (id)
267                         *id = _id;
268         }
269
270         if (v >= 0)
271                 cached_virt = v;
272
273         return v;
274 }