chiark / gitweb /
20a8d7c5bfc4287bdb6bbea0da1aac0a7bf4734a
[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_dmi(const char **_id) {
105
106         /* Both CPUID and DMI are x86 specific interfaces... */
107 #if defined(__i386__) || defined(__x86_64__)
108
109         static const char *const dmi_vendors[] = {
110                 "/sys/class/dmi/id/sys_vendor",
111                 "/sys/class/dmi/id/board_vendor",
112                 "/sys/class/dmi/id/bios_vendor"
113         };
114
115         static const char dmi_vendor_table[] =
116                 "QEMU\0"                  "qemu\0"
117                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
118                 "VMware\0"                "vmware\0"
119                 "VMW\0"                   "vmware\0"
120                 "innotek GmbH\0"          "oracle\0"
121                 "Xen\0"                   "xen\0"
122                 "Bochs\0"                 "bochs\0";
123         unsigned i;
124
125         for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
126                 _cleanup_free_ char *s = NULL;
127                 const char *j, *k;
128                 int r;
129
130                 r = read_one_line_file(dmi_vendors[i], &s);
131                 if (r < 0) {
132                         if (r != -ENOENT)
133                                 return r;
134
135                         continue;
136                 }
137
138                 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
139                         if (startswith(s, j)) {
140                                 *_id = k;
141                                 return 1;
142                         }
143         }
144 #endif
145
146         return 0;
147 }
148
149 /* Returns a short identifier for the various VM implementations */
150 int detect_vm(const char **id) {
151         _cleanup_free_ char *domcap = NULL, *cpuinfo_contents = NULL;
152         static thread_local int cached_found = -1;
153         static thread_local const char *cached_id = NULL;
154         const char *_id = NULL;
155         int r;
156
157         if (_likely_(cached_found >= 0)) {
158
159                 if (id)
160                         *id = cached_id;
161
162                 return cached_found;
163         }
164
165         /* Try xen capabilities file first, if not found try high-level hypervisor sysfs file:
166          *
167          * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
168         r = read_one_line_file("/proc/xen/capabilities", &domcap);
169         if (r >= 0) {
170                 char *cap, *i = domcap;
171
172                 while ((cap = strsep(&i, ",")))
173                         if (streq(cap, "control_d"))
174                                 break;
175
176                 if (!cap)  {
177                         _id = "xen";
178                         r = 1;
179                 }
180
181                 goto finish;
182
183         } else if (r == -ENOENT) {
184                 _cleanup_free_ char *hvtype = NULL;
185
186                 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
187                 if (r >= 0) {
188                         if (streq(hvtype, "xen")) {
189                                 _id = "xen";
190                                 r = 1;
191                                 goto finish;
192                         }
193                 } else if (r != -ENOENT)
194                         return r;
195         } else
196                 return r;
197
198         /* this will set _id to "other" and return 0 for unknown hypervisors */
199         r = detect_vm_cpuid(&_id);
200         if (r != 0)
201                 goto finish;
202
203         r = detect_vm_dmi(&_id);
204         if (r != 0)
205                 goto finish;
206
207         if (_id) {
208                 /* "other" */
209                 r = 1;
210                 goto finish;
211         }
212
213         /* Detect User-Mode Linux by reading /proc/cpuinfo */
214         r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
215         if (r < 0)
216                 return r;
217         if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
218                 _id = "uml";
219                 r = 1;
220                 goto finish;
221         }
222
223         r = 0;
224
225 finish:
226         cached_found = r;
227
228         cached_id = _id;
229         if (id)
230                 *id = _id;
231
232         return r;
233 }
234
235 int detect_container(const char **id) {
236
237         static thread_local int cached_found = -1;
238         static thread_local const char *cached_id = NULL;
239
240         _cleanup_free_ char *m = NULL;
241         const char *_id = NULL, *e = NULL;
242         int r;
243
244         if (_likely_(cached_found >= 0)) {
245
246                 if (id)
247                         *id = cached_id;
248
249                 return cached_found;
250         }
251
252         /* /proc/vz exists in container and outside of the container,
253          * /proc/bc only outside of the container. */
254         if (access("/proc/vz", F_OK) >= 0 &&
255             access("/proc/bc", F_OK) < 0) {
256                 _id = "openvz";
257                 r = 1;
258                 goto finish;
259         }
260
261         if (getpid() == 1) {
262                 /* If we are PID 1 we can just check our own
263                  * environment variable */
264
265                 e = getenv("container");
266                 if (isempty(e)) {
267                         r = 0;
268                         goto finish;
269                 }
270         } else {
271
272                 /* Otherwise, PID 1 dropped this information into a
273                  * file in /run. This is better than accessing
274                  * /proc/1/environ, since we don't need CAP_SYS_PTRACE
275                  * for that. */
276
277                 r = read_one_line_file("/run/systemd/container", &m);
278                 if (r == -ENOENT) {
279                         r = 0;
280                         goto finish;
281                 }
282                 if (r < 0)
283                         return r;
284
285                 e = m;
286         }
287
288         /* We only recognize a selected few here, since we want to
289          * enforce a redacted namespace */
290         if (streq(e, "lxc"))
291                 _id ="lxc";
292         else if (streq(e, "lxc-libvirt"))
293                 _id = "lxc-libvirt";
294         else if (streq(e, "systemd-nspawn"))
295                 _id = "systemd-nspawn";
296         else
297                 _id = "other";
298
299         r = 1;
300
301 finish:
302         cached_found = r;
303
304         cached_id = _id;
305         if (id)
306                 *id = _id;
307
308         return r;
309 }
310
311 /* Returns a short identifier for the various VM/container implementations */
312 int detect_virtualization(const char **id) {
313         int r;
314
315         r = detect_container(id);
316         if (r < 0)
317                 return r;
318         if (r > 0)
319                 return VIRTUALIZATION_CONTAINER;
320
321         r = detect_vm(id);
322         if (r < 0)
323                 return r;
324         if (r > 0)
325                 return VIRTUALIZATION_VM;
326
327         return VIRTUALIZATION_NONE;
328 }