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