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