chiark / gitweb /
journal, shared: fix warnings during compilation on 32 bits
[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
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         char *e = NULL;
157         int r;
158
159         /* Unfortunately many of these operations require root access
160          * in one way or another */
161
162         r = running_in_chroot();
163         if (r < 0)
164                 return r;
165         if (r > 0) {
166
167                 if (id)
168                         *id = "chroot";
169
170                 return 1;
171         }
172
173         /* /proc/vz exists in container and outside of the container,
174          * /proc/bc only outside of the container. */
175         if (access("/proc/vz", F_OK) >= 0 &&
176             access("/proc/bc", F_OK) < 0) {
177
178                 if (id)
179                         *id = "openvz";
180
181                 return 1;
182         }
183
184         r = getenv_for_pid(1, "container", &e);
185         if (r <= 0)
186                 return r;
187
188         /* We only recognize a selected few here, since we want to
189          * enforce a redacted namespace */
190         if (streq(e, "lxc")) {
191                 if (id)
192                         *id = "lxc";
193         } else if (streq(e, "lxc-libvirt")) {
194                 if (id)
195                         *id = "lxc-libvirt";
196         } else if (streq(e, "systemd-nspawn")) {
197                 if (id)
198                         *id = "systemd-nspawn";
199         } else {
200                 if (id)
201                         *id = "other";
202         }
203
204         free(e);
205
206         return r;
207 }
208
209 /* Returns a short identifier for the various VM/container implementations */
210 Virtualization detect_virtualization(const char **id) {
211
212         static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
213         static __thread const char *cached_id = NULL;
214
215         const char *_id;
216         int r;
217         Virtualization v;
218
219         if (_likely_(cached_virt >= 0)) {
220
221                 if (id && cached_virt > 0)
222                         *id = cached_id;
223
224                 return cached_virt;
225         }
226
227         r = detect_container(&_id);
228         if (r < 0) {
229                 v = r;
230                 goto finish;
231         } else if (r > 0) {
232                 v = VIRTUALIZATION_CONTAINER;
233                 goto finish;
234         }
235
236         r = detect_vm(&_id);
237         if (r < 0) {
238                 v = r;
239                 goto finish;
240         } else if (r > 0) {
241                 v = VIRTUALIZATION_VM;
242                 goto finish;
243         }
244
245         v = VIRTUALIZATION_NONE;
246
247 finish:
248         if (v > 0) {
249                 cached_id = _id;
250
251                 if (id)
252                         *id = _id;
253         }
254
255         if (v >= 0)
256                 cached_virt = v;
257
258         return v;
259 }