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