chiark / gitweb /
udev: set errno = ENOSYS for removed interfaces
[elogind.git] / src / 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 }