]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/virt.c
util-lib: move /proc/cmdline parsing code to proc-cmdline.[ch]
[thirdparty/systemd.git] / src / basic / virt.c
CommitLineData
b52aae1d
LP
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
5430f7f2
LP
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
b52aae1d
LP
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
5430f7f2 16 Lesser General Public License for more details.
b52aae1d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
b52aae1d
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
b52aae1d 22#include <errno.h>
07630cea 23#include <string.h>
b52aae1d
LP
24#include <unistd.h>
25
07630cea 26#include "fileio.h"
0b452006 27#include "process-util.h"
8b43440b 28#include "string-table.h"
07630cea
LP
29#include "string-util.h"
30#include "util.h"
b52aae1d
LP
31#include "virt.h"
32
75f86906 33static int detect_vm_cpuid(void) {
b52aae1d
LP
34
35 /* Both CPUID and DMI are x86 specific interfaces... */
bdb628ee 36#if defined(__i386__) || defined(__x86_64__)
b52aae1d 37
75f86906
LP
38 static const struct {
39 const char *cpuid;
40 int id;
41 } cpuid_vendor_table[] = {
42 { "XenVMMXenVMM", VIRTUALIZATION_XEN },
43 { "KVMKVMKVM", VIRTUALIZATION_KVM },
b52aae1d 44 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
75f86906 45 { "VMwareVMware", VIRTUALIZATION_VMWARE },
b52aae1d 46 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
75f86906
LP
47 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
48 };
b52aae1d
LP
49
50 uint32_t eax, ecx;
b52aae1d
LP
51 bool hypervisor;
52
53 /* http://lwn.net/Articles/301888/ */
b52aae1d
LP
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) {
75f86906
LP
78 union {
79 uint32_t sig32[3];
80 char text[13];
81 } sig = {};
82 unsigned j;
b52aae1d
LP
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
75f86906
LP
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;
bdb628ee 100
75f86906 101 return VIRTUALIZATION_VM_OTHER;
b52aae1d 102 }
bdb628ee
ZJS
103#endif
104
75f86906 105 return VIRTUALIZATION_NONE;
bdb628ee
ZJS
106}
107
75f86906 108static int detect_vm_device_tree(void) {
db6a8689 109#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
d831deb5
CA
110 _cleanup_free_ char *hvtype = NULL;
111 int r;
112
b8f1df82 113 r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
75f86906 114 if (r == -ENOENT) {
ce09c71d
AJ
115 _cleanup_closedir_ DIR *dir = NULL;
116 struct dirent *dent;
117
118 dir = opendir("/proc/device-tree");
119 if (!dir) {
120 if (errno == ENOENT)
75f86906 121 return VIRTUALIZATION_NONE;
ce09c71d
AJ
122 return -errno;
123 }
124
75f86906
LP
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;
d831deb5 141#endif
d831deb5
CA
142}
143
75f86906 144static int detect_vm_dmi(void) {
bdb628ee
ZJS
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
75f86906
LP
155 static const struct {
156 const char *vendor;
157 int id;
158 } dmi_vendor_table[] = {
159 { "QEMU", VIRTUALIZATION_QEMU },
bdb628ee 160 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
75f86906
LP
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 };
bdb628ee 168 unsigned i;
75f86906 169 int r;
b52aae1d
LP
170
171 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
b1b8e816 172 _cleanup_free_ char *s = NULL;
75f86906 173 unsigned j;
b52aae1d 174
b1b8e816
LP
175 r = read_one_line_file(dmi_vendors[i], &s);
176 if (r < 0) {
75f86906
LP
177 if (r == -ENOENT)
178 continue;
b52aae1d 179
75f86906 180 return r;
b52aae1d
LP
181 }
182
75f86906
LP
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;
b52aae1d 186 }
bdb628ee
ZJS
187#endif
188
75f86906 189 return VIRTUALIZATION_NONE;
bdb628ee
ZJS
190}
191
75f86906
LP
192static int detect_vm_xen(void) {
193 _cleanup_free_ char *domcap = NULL;
194 char *cap, *i;
bdb628ee 195 int r;
b52aae1d 196
75f86906
LP
197 r = read_one_line_file("/proc/xen/capabilities", &domcap);
198 if (r == -ENOENT)
199 return VIRTUALIZATION_NONE;
bdb628ee 200
75f86906
LP
201 i = domcap;
202 while ((cap = strsep(&i, ",")))
203 if (streq(cap, "control_d"))
204 break;
bdb628ee 205
75f86906
LP
206 return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
207}
37287585 208
75f86906
LP
209static int detect_vm_hypervisor(void) {
210 _cleanup_free_ char *hvtype = NULL;
211 int r;
37287585 212
75f86906
LP
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;
37287585 218
75f86906
LP
219 if (streq(hvtype, "xen"))
220 return VIRTUALIZATION_XEN;
221 else
222 return VIRTUALIZATION_VM_OTHER;
223}
37287585 224
75f86906
LP
225static int detect_vm_uml(void) {
226 _cleanup_free_ char *cpuinfo_contents = NULL;
227 int r;
37287585 228
75f86906
LP
229 /* Detect User-Mode Linux by reading /proc/cpuinfo */
230 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
231 if (r < 0)
bdb628ee 232 return r;
75f86906
LP
233 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
234 return VIRTUALIZATION_UML;
bdb628ee 235
75f86906
LP
236 return VIRTUALIZATION_NONE;
237}
e32886e0 238
75f86906 239static int detect_vm_zvm(void) {
bdb628ee 240
75f86906
LP
241#if defined(__s390__)
242 _cleanup_free_ char *t = NULL;
243 int r;
e32886e0 244
c4cd1d4d 245 r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
75f86906
LP
246 if (r == -ENOENT)
247 return VIRTUALIZATION_NONE;
248 if (r < 0)
249 return r;
e32886e0 250
75f86906
LP
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}
e32886e0 259
75f86906
LP
260/* Returns a short identifier for the various VM implementations */
261int detect_vm(void) {
262 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
263 int r;
e32886e0 264
75f86906
LP
265 if (cached_found >= 0)
266 return cached_found;
bdb628ee 267
75f86906
LP
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)
d831deb5
CA
277 goto finish;
278
75f86906
LP
279 r = detect_vm_dmi();
280 if (r < 0)
281 return r;
282 if (r != VIRTUALIZATION_NONE)
0fb533a5 283 goto finish;
b52aae1d 284
75f86906 285 r = detect_vm_cpuid();
7080ea16
RR
286 if (r < 0)
287 return r;
75f86906 288 if (r != VIRTUALIZATION_NONE)
0fb533a5 289 goto finish;
7080ea16 290
75f86906
LP
291 r = detect_vm_hypervisor();
292 if (r < 0)
293 return r;
294 if (r != VIRTUALIZATION_NONE)
295 goto finish;
f41925b4 296
75f86906
LP
297 r = detect_vm_device_tree();
298 if (r < 0)
299 return r;
300 if (r != VIRTUALIZATION_NONE)
301 goto finish;
f41925b4 302
75f86906
LP
303 r = detect_vm_uml();
304 if (r < 0)
305 return r;
306 if (r != VIRTUALIZATION_NONE)
307 goto finish;
f41925b4 308
75f86906
LP
309 r = detect_vm_zvm();
310 if (r < 0)
311 return r;
0fb533a5
LP
312
313finish:
314 cached_found = r;
0fb533a5 315 return r;
b52aae1d
LP
316}
317
75f86906 318int detect_container(void) {
0fb533a5 319
75f86906
LP
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 };
0fb533a5 329
75f86906 330 static thread_local int cached_found = _VIRTUALIZATION_INVALID;
fdd25311 331 _cleanup_free_ char *m = NULL;
75f86906
LP
332 const char *e = NULL;
333 unsigned j;
ab94af92 334 int r;
b52aae1d 335
75f86906 336 if (cached_found >= 0)
0fb533a5 337 return cached_found;
0fb533a5 338
b52aae1d
LP
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) {
75f86906 343 r = VIRTUALIZATION_OPENVZ;
0fb533a5 344 goto finish;
b52aae1d
LP
345 }
346
fdd25311
LP
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)) {
75f86906 353 r = VIRTUALIZATION_NONE;
fdd25311
LP
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) {
536bfdab
LP
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
75f86906 382 r = VIRTUALIZATION_NONE;
536bfdab
LP
383 goto finish;
384 }
fdd25311
LP
385 }
386 if (r < 0)
387 return r;
388
389 e = m;
390 }
b52aae1d 391
75f86906
LP
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 }
0fb533a5 397
75f86906 398 r = VIRTUALIZATION_NONE;
fdd25311 399
0fb533a5
LP
400finish:
401 cached_found = r;
ab94af92 402 return r;
b52aae1d
LP
403}
404
75f86906 405int detect_virtualization(void) {
b52aae1d 406 int r;
b52aae1d 407
75f86906
LP
408 r = detect_container();
409 if (r != 0)
0fb533a5 410 return r;
b52aae1d 411
75f86906 412 return detect_vm();
b52aae1d 413}
75f86906
LP
414
415static 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
437DEFINE_STRING_TABLE_LOOKUP(virtualization, int);