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