]> git.proxmox.com Git - systemd.git/blame - src/shared/virt.c
Imported Upstream version 219
[systemd.git] / src / shared / virt.c
CommitLineData
663996b3
MS
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
60f067b4 30static int detect_vm_cpuid(const char **_id) {
663996b3
MS
31
32 /* Both CPUID and DMI are x86 specific interfaces... */
60f067b4 33#if defined(__i386__) || defined(__x86_64__)
663996b3
MS
34
35 static const char cpuid_vendor_table[] =
36 "XenVMMXenVMM\0" "xen\0"
37 "KVMKVMKVM\0" "kvm\0"
38 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
39 "VMwareVMware\0" "vmware\0"
40 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
41 "Microsoft Hv\0" "microsoft\0";
42
43 uint32_t eax, ecx;
44 union {
45 uint32_t sig32[3];
46 char text[13];
47 } sig = {};
663996b3
MS
48 const char *j, *k;
49 bool hypervisor;
663996b3
MS
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
77 /* There is a hypervisor, see what it is */
78 eax = 0x40000000U;
79 __asm__ __volatile__ (
80 /* ebx/rbx is being used for PIC! */
81 " push %%"REG_b" \n\t"
82 " cpuid \n\t"
83 " mov %%ebx, %1 \n\t"
84 " pop %%"REG_b" \n\t"
85
86 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
87 : "0" (eax)
88 );
89
90 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
91 if (streq(sig.text, j)) {
60f067b4 92 *_id = k;
663996b3
MS
93 return 1;
94 }
60f067b4
JS
95
96 *_id = "other";
97 return 0;
663996b3 98 }
60f067b4
JS
99#endif
100
101 return 0;
102}
103
e735f4d4
MP
104static int detect_vm_devicetree(const char **_id) {
105#if defined(__powerpc__) || defined(__powerpc64__)
106 _cleanup_free_ char *hvtype = NULL;
107 int r;
108
109 r = read_one_line_file("/sys/firmware/devicetree/base/hypervisor/compatible", &hvtype);
110 if (r >= 0) {
111 if (streq(hvtype, "linux,kvm")) {
112 *_id = "kvm";
113 return 1;
114 }
115 }
116#endif
117 return 0;
118}
119
60f067b4
JS
120static int detect_vm_dmi(const char **_id) {
121
122 /* Both CPUID and DMI are x86 specific interfaces... */
123#if defined(__i386__) || defined(__x86_64__)
124
125 static const char *const dmi_vendors[] = {
126 "/sys/class/dmi/id/sys_vendor",
127 "/sys/class/dmi/id/board_vendor",
128 "/sys/class/dmi/id/bios_vendor"
129 };
130
131 static const char dmi_vendor_table[] =
132 "QEMU\0" "qemu\0"
133 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
134 "VMware\0" "vmware\0"
135 "VMW\0" "vmware\0"
136 "innotek GmbH\0" "oracle\0"
137 "Xen\0" "xen\0"
138 "Bochs\0" "bochs\0";
139 unsigned i;
663996b3
MS
140
141 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
142 _cleanup_free_ char *s = NULL;
60f067b4
JS
143 const char *j, *k;
144 int r;
663996b3
MS
145
146 r = read_one_line_file(dmi_vendors[i], &s);
147 if (r < 0) {
148 if (r != -ENOENT)
149 return r;
150
151 continue;
152 }
153
154 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
60f067b4
JS
155 if (startswith(s, j)) {
156 *_id = k;
157 return 1;
158 }
159 }
160#endif
663996b3 161
60f067b4
JS
162 return 0;
163}
663996b3 164
60f067b4
JS
165/* Returns a short identifier for the various VM implementations */
166int detect_vm(const char **id) {
167 _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
168 static thread_local int cached_found = -1;
169 static thread_local const char *cached_id = NULL;
170 const char *_id = NULL;
171 int r;
172
173 if (_likely_(cached_found >= 0)) {
663996b3 174
663996b3 175 if (id)
60f067b4 176 *id = cached_id;
663996b3 177
60f067b4 178 return cached_found;
663996b3
MS
179 }
180
60f067b4
JS
181 /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
182 *
183 * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
184 r = read_one_line_file("/proc/xen/capabilities", &domcap);
185 if (r >= 0) {
186 char *cap, *i = domcap;
187
188 while ((cap = strsep(&i, ",")))
189 if (streq(cap, "control_d"))
190 break;
191
5eef597e 192 if (!cap) {
60f067b4
JS
193 _id = "xen";
194 r = 1;
195 }
196
197 goto finish;
198
199 } else if (r == -ENOENT) {
200 _cleanup_free_ char *hvtype = NULL;
201
202 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
203 if (r >= 0) {
204 if (streq(hvtype, "xen")) {
205 _id = "xen";
206 r = 1;
207 goto finish;
208 }
209 } else if (r != -ENOENT)
210 return r;
211 } else
212 return r;
213
214 /* this will set _id to "other" and return 0 for unknown hypervisors */
215 r = detect_vm_cpuid(&_id);
216 if (r != 0)
217 goto finish;
218
219 r = detect_vm_dmi(&_id);
220 if (r != 0)
221 goto finish;
222
e735f4d4
MP
223 r = detect_vm_devicetree(&_id);
224 if (r != 0)
225 goto finish;
226
60f067b4
JS
227 if (_id) {
228 /* "other" */
229 r = 1;
230 goto finish;
231 }
14228c0d
MB
232
233 /* Detect User-Mode Linux by reading /proc/cpuinfo */
234 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
235 if (r < 0)
236 return r;
237 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
60f067b4
JS
238 _id = "uml";
239 r = 1;
240 goto finish;
14228c0d
MB
241 }
242
5eef597e
MP
243#if defined(__s390__)
244 {
245 _cleanup_free_ char *t = NULL;
246
247 r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t);
248 if (r >= 0) {
249 if (streq(t, "z/VM"))
250 _id = "zvm";
251 else
252 _id = "kvm";
253 r = 1;
254
255 goto finish;
256 }
257 }
258#endif
259
60f067b4
JS
260 r = 0;
261
262finish:
263 cached_found = r;
264
265 cached_id = _id;
266 if (id)
267 *id = _id;
268
269 return r;
663996b3
MS
270}
271
272int detect_container(const char **id) {
663996b3 273
60f067b4
JS
274 static thread_local int cached_found = -1;
275 static thread_local const char *cached_id = NULL;
663996b3 276
60f067b4
JS
277 _cleanup_free_ char *m = NULL;
278 const char *_id = NULL, *e = NULL;
279 int r;
280
281 if (_likely_(cached_found >= 0)) {
663996b3
MS
282
283 if (id)
60f067b4 284 *id = cached_id;
663996b3 285
60f067b4 286 return cached_found;
663996b3
MS
287 }
288
289 /* /proc/vz exists in container and outside of the container,
290 * /proc/bc only outside of the container. */
291 if (access("/proc/vz", F_OK) >= 0 &&
292 access("/proc/bc", F_OK) < 0) {
60f067b4
JS
293 _id = "openvz";
294 r = 1;
295 goto finish;
663996b3
MS
296 }
297
60f067b4
JS
298 if (getpid() == 1) {
299 /* If we are PID 1 we can just check our own
300 * environment variable */
663996b3 301
60f067b4
JS
302 e = getenv("container");
303 if (isempty(e)) {
304 r = 0;
305 goto finish;
306 }
663996b3 307 } else {
663996b3 308
60f067b4
JS
309 /* Otherwise, PID 1 dropped this information into a
310 * file in /run. This is better than accessing
311 * /proc/1/environ, since we don't need CAP_SYS_PTRACE
312 * for that. */
663996b3 313
60f067b4
JS
314 r = read_one_line_file("/run/systemd/container", &m);
315 if (r == -ENOENT) {
f47781d8
MP
316
317 /* Fallback for cases where PID 1 was not
318 * systemd (for example, cases where
319 * init=/bin/sh is used. */
320
321 r = getenv_for_pid(1, "container", &m);
322 if (r <= 0) {
323
324 /* If that didn't work, give up,
325 * assume no container manager.
326 *
327 * Note: This means we still cannot
328 * detect containers if init=/bin/sh
329 * is passed but privileges dropped,
330 * as /proc/1/environ is only readable
331 * with privileges. */
332
333 r = 0;
334 goto finish;
335 }
60f067b4
JS
336 }
337 if (r < 0)
338 return r;
663996b3 339
60f067b4
JS
340 e = m;
341 }
663996b3 342
60f067b4
JS
343 /* We only recognize a selected few here, since we want to
344 * enforce a redacted namespace */
345 if (streq(e, "lxc"))
346 _id ="lxc";
347 else if (streq(e, "lxc-libvirt"))
348 _id = "lxc-libvirt";
349 else if (streq(e, "systemd-nspawn"))
350 _id = "systemd-nspawn";
5eef597e
MP
351 else if (streq(e, "docker"))
352 _id = "docker";
60f067b4
JS
353 else
354 _id = "other";
663996b3 355
60f067b4 356 r = 1;
663996b3 357
60f067b4
JS
358finish:
359 cached_found = r;
663996b3 360
60f067b4
JS
361 cached_id = _id;
362 if (id)
363 *id = _id;
663996b3 364
60f067b4
JS
365 return r;
366}
663996b3 367
60f067b4
JS
368/* Returns a short identifier for the various VM/container implementations */
369int detect_virtualization(const char **id) {
370 int r;
663996b3 371
60f067b4
JS
372 r = detect_container(id);
373 if (r < 0)
374 return r;
375 if (r > 0)
376 return VIRTUALIZATION_CONTAINER;
663996b3 377
60f067b4
JS
378 r = detect_vm(id);
379 if (r < 0)
380 return r;
381 if (r > 0)
382 return VIRTUALIZATION_VM;
663996b3 383
60f067b4 384 return VIRTUALIZATION_NONE;
663996b3 385}