chiark / gitweb /
shared: fix build on !x86
[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 /* Returns a short identifier for the various VM implementations */
31 int detect_vm(const char **id) {
32         _cleanup_free_ char *cpuinfo_contents = NULL;
33         int r;
34
35 #if defined(__i386__) || defined(__x86_64__)
36
37         /* Both CPUID and DMI are x86 specific interfaces... */
38
39         static const char *const dmi_vendors[] = {
40                 "/sys/class/dmi/id/sys_vendor",
41                 "/sys/class/dmi/id/board_vendor",
42                 "/sys/class/dmi/id/bios_vendor"
43         };
44
45         static const char dmi_vendor_table[] =
46                 "QEMU\0"                  "qemu\0"
47                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
48                 "VMware\0"                "vmware\0"
49                 "VMW\0"                   "vmware\0"
50                 "Microsoft Corporation\0" "microsoft\0"
51                 "innotek GmbH\0"          "oracle\0"
52                 "Xen\0"                   "xen\0"
53                 "Bochs\0"                 "bochs\0";
54
55         static const char cpuid_vendor_table[] =
56                 "XenVMMXenVMM\0"          "xen\0"
57                 "KVMKVMKVM\0"             "kvm\0"
58                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
59                 "VMwareVMware\0"          "vmware\0"
60                 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
61                 "Microsoft Hv\0"          "microsoft\0";
62
63         uint32_t eax, ecx;
64         union {
65                 uint32_t sig32[3];
66                 char text[13];
67         } sig = {};
68         unsigned i;
69         const char *j, *k;
70         bool hypervisor;
71         _cleanup_free_ char *hvtype = NULL;
72
73         /* Try high-level hypervisor sysfs file first:
74          *
75          * https://bugs.freedesktop.org/show_bug.cgi?id=61491 */
76         r = read_one_line_file("/sys/hypervisor/type", &hvtype);
77         if (r >= 0) {
78                 if (streq(hvtype, "xen")) {
79                         if (id)
80                                 *id = "xen";
81
82                         return 1;
83                 }
84         } else if (r != -ENOENT)
85                 return r;
86
87         /* http://lwn.net/Articles/301888/ */
88
89 #if defined (__i386__)
90 #define REG_a "eax"
91 #define REG_b "ebx"
92 #elif defined (__amd64__)
93 #define REG_a "rax"
94 #define REG_b "rbx"
95 #endif
96
97         /* First detect whether there is a hypervisor */
98         eax = 1;
99         __asm__ __volatile__ (
100                 /* ebx/rbx is being used for PIC! */
101                 "  push %%"REG_b"         \n\t"
102                 "  cpuid                  \n\t"
103                 "  pop %%"REG_b"          \n\t"
104
105                 : "=a" (eax), "=c" (ecx)
106                 : "0" (eax)
107         );
108
109         hypervisor = !!(ecx & 0x80000000U);
110
111         if (hypervisor) {
112
113                 /* There is a hypervisor, see what it is */
114                 eax = 0x40000000U;
115                 __asm__ __volatile__ (
116                         /* ebx/rbx is being used for PIC! */
117                         "  push %%"REG_b"         \n\t"
118                         "  cpuid                  \n\t"
119                         "  mov %%ebx, %1          \n\t"
120                         "  pop %%"REG_b"          \n\t"
121
122                         : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
123                         : "0" (eax)
124                 );
125
126                 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
127                         if (streq(sig.text, j)) {
128
129                                 if (id)
130                                         *id = k;
131
132                                 return 1;
133                         }
134         }
135
136         for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
137                 _cleanup_free_ char *s = NULL;
138                 const char *found = NULL;
139
140                 r = read_one_line_file(dmi_vendors[i], &s);
141                 if (r < 0) {
142                         if (r != -ENOENT)
143                                 return r;
144
145                         continue;
146                 }
147
148                 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
149                         if (startswith(s, j))
150                                 found = k;
151
152                 if (found) {
153                         if (id)
154                                 *id = found;
155
156                         return 1;
157                 }
158         }
159
160         if (hypervisor || hvtype) {
161                 if (id)
162                         *id = "other";
163
164                 return 1;
165         }
166
167 #endif
168
169         /* Detect User-Mode Linux by reading /proc/cpuinfo */
170         r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
171         if (r < 0)
172                 return r;
173         if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
174                 *id = "uml";
175                 return 1;
176         }
177
178         return 0;
179 }
180
181 int detect_container(const char **id) {
182         _cleanup_free_ char *e = NULL;
183         int r;
184
185         /* Unfortunately many of these operations require root access
186          * in one way or another */
187
188         r = running_in_chroot();
189         if (r < 0)
190                 return r;
191         if (r > 0) {
192
193                 if (id)
194                         *id = "chroot";
195
196                 return 1;
197         }
198
199         /* /proc/vz exists in container and outside of the container,
200          * /proc/bc only outside of the container. */
201         if (access("/proc/vz", F_OK) >= 0 &&
202             access("/proc/bc", F_OK) < 0) {
203
204                 if (id)
205                         *id = "openvz";
206
207                 return 1;
208         }
209
210         r = getenv_for_pid(1, "container", &e);
211         if (r <= 0)
212                 return r;
213
214         /* We only recognize a selected few here, since we want to
215          * enforce a redacted namespace */
216         if (streq(e, "lxc")) {
217                 if (id)
218                         *id = "lxc";
219         } else if (streq(e, "lxc-libvirt")) {
220                 if (id)
221                         *id = "lxc-libvirt";
222         } else if (streq(e, "systemd-nspawn")) {
223                 if (id)
224                         *id = "systemd-nspawn";
225         } else {
226                 if (id)
227                         *id = "other";
228         }
229
230         return r;
231 }
232
233 /* Returns a short identifier for the various VM/container implementations */
234 Virtualization detect_virtualization(const char **id) {
235
236         static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
237         static __thread const char *cached_id = NULL;
238
239         const char *_id;
240         int r;
241         Virtualization v;
242
243         if (_likely_(cached_virt >= 0)) {
244
245                 if (id && cached_virt > 0)
246                         *id = cached_id;
247
248                 return cached_virt;
249         }
250
251         r = detect_container(&_id);
252         if (r < 0) {
253                 v = r;
254                 goto finish;
255         } else if (r > 0) {
256                 v = VIRTUALIZATION_CONTAINER;
257                 goto finish;
258         }
259
260         r = detect_vm(&_id);
261         if (r < 0) {
262                 v = r;
263                 goto finish;
264         } else if (r > 0) {
265                 v = VIRTUALIZATION_VM;
266                 goto finish;
267         }
268
269         v = VIRTUALIZATION_NONE;
270
271 finish:
272         if (v > 0) {
273                 cached_id = _id;
274
275                 if (id)
276                         *id = _id;
277         }
278
279         if (v >= 0)
280                 cached_virt = v;
281
282         return v;
283 }