chiark / gitweb /
nspawn: add --uuid= switch to allow setting the machine id for the container
[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         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 }