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