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