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