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