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