]> git.ipfire.org Git - oddments/fireinfo.git/blame - src/fireinfo.c
Add VMWare hypervisor detection.
[oddments/fireinfo.git] / src / fireinfo.c
CommitLineData
5f4159ab
MT
1/*
2 * fireinfo.c
3 *
4 * Copyright (C) 2010 IPFire. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <Python.h>
21
22#include <sys/utsname.h>
23
24/*
25 Big parts of this were taken from
26 http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git;a=blob;f=sys-utils/lscpu.c
27*/
28
29/* /sys paths */
30#define _PATH_SYS_SYSTEM "/sys/devices/system"
31#define _PATH_SYS_CPU _PATH_SYS_SYSTEM "/cpu"
32#define _PATH_PROC_XEN "/proc/xen"
33#define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
34#define _PATH_PROC_CPUINFO "/proc/cpuinfo"
35#define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
36
37/* virtualization types */
38enum {
39 VIRT_NONE = 0,
40 VIRT_PARA,
41 VIRT_FULL
42};
43const char *virt_types[] = {
44 [VIRT_NONE] = "none",
45 [VIRT_PARA] = "para",
46 [VIRT_FULL] = "full"
47};
48
49/* hypervisor vendors */
50enum {
51 HYPER_NONE = 0,
52 HYPER_XEN,
53 HYPER_KVM,
284eedb7
MT
54 HYPER_MSHV,
55 HYPER_VMWARE
5f4159ab
MT
56};
57const char *hv_vendors[] = {
58 [HYPER_NONE] = NULL,
59 [HYPER_XEN] = "Xen",
60 [HYPER_KVM] = "KVM",
284eedb7
MT
61 [HYPER_MSHV] = "Microsoft",
62 [HYPER_VMWARE] = "VMWare"
5f4159ab
MT
63};
64
65/* CPU modes (bits) */
66enum {
67 MODE_REAL = (1 << 1),
68 MODE_TRANSPARENT = (1 << 2),
69 MODE_LONG = (1 << 3)
70};
71
72/* global description */
73struct lscpu_desc {
74 char *arch;
75 char *vendor;
76 char *family;
77 char *model;
78 char *virtflag; /* virtualization flag (vmx, svm) */
79 int hyper; /* hypervisor vendor ID */
80 int virtype; /* VIRT_PARA|FULL|NONE ? */
81 char *mhz;
82 char *stepping;
83 char *bogomips;
84 char *flags;
85 int mode; /* rm, lm or/and tm */
86
87 int ncpus; /* number of CPUs */
88};
89
90static size_t sysrootlen;
91static char pathbuf[PATH_MAX];
92static int maxcpus; /* size in bits of kernel cpu mask */
93
94static FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...)
95 __attribute__ ((__format__ (__printf__, 3, 4)));
96static int path_exist(const char *path, ...)
97 __attribute__ ((__format__ (__printf__, 1, 2)));
98
99static const char *
100path_vcreate(const char *path, va_list ap)
101{
102 if (sysrootlen)
103 vsnprintf(pathbuf + sysrootlen,
104 sizeof(pathbuf) - sysrootlen, path, ap);
105 else
106 vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
107 return pathbuf;
108}
109
110static FILE *
111path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
112{
113 FILE *f;
114 const char *p = path_vcreate(path, ap);
115
116 f = fopen(p, mode);
117 if (!f && exit_on_error)
118 PyErr_SetString(PyExc_Exception,
119 (const char *)sprintf("error: cannot open %s", p));
120 return f;
121}
122
123static FILE *
124path_fopen(const char *mode, int exit_on_error, const char *path, ...)
125{
126 FILE *fd;
127 va_list ap;
128
129 va_start(ap, path);
130 fd = path_vfopen(mode, exit_on_error, path, ap);
131 va_end(ap);
132
133 return fd;
134}
135
136static int
137path_getnum(const char *path, ...)
138{
139 FILE *fd;
140 va_list ap;
141 int result;
142
143 va_start(ap, path);
144 fd = path_vfopen("r", 1, path, ap);
145 va_end(ap);
146
147 if (fscanf(fd, "%d", &result) != 1) {
148 if (ferror(fd))
149 PyErr_SetString(PyExc_Exception, (const char *)sprintf("failed to read: %s", pathbuf));
150 else
151 PyErr_SetString(PyExc_Exception, (const char *)sprintf("parse error: %s", pathbuf));
152 }
153 fclose(fd);
154 return result;
155}
156
157static int
158path_exist(const char *path, ...)
159{
160 va_list ap;
161 const char *p;
162
163 va_start(ap, path);
164 p = path_vcreate(path, ap);
165 va_end(ap);
166
167 return access(p, F_OK) == 0;
168}
169
170static char *
171xstrdup(const char *str)
172{
173 char *s = strdup(str);
174 if (!s)
175 PyErr_SetString(PyExc_Exception, "error: strdup failed");
176 return s;
177}
178
179/* Lookup a pattern and get the value from cpuinfo.
180 * Format is:
181 *
182 * "<pattern> : <key>"
183 */
184int lookup(char *line, char *pattern, char **value)
185{
186 char *p, *v;
187 int len = strlen(pattern);
188
189 if (!*line)
190 return 0;
191
192 /* pattern */
193 if (strncmp(line, pattern, len))
194 return 0;
195
196 /* white spaces */
197 for (p = line + len; isspace(*p); p++);
198
199 /* separator */
200 if (*p != ':')
201 return 0;
202
203 /* white spaces */
204 for (++p; isspace(*p); p++);
205
206 /* value */
207 if (!*p)
208 return 0;
209 v = p;
210
211 /* end of value */
212 len = strlen(line) - 1;
213 for (p = line + len; isspace(*(p-1)); p--);
214 *p = '\0';
215
216 *value = xstrdup(v);
217 return 1;
218}
219
220static void
221read_basicinfo(struct lscpu_desc *desc)
222{
223 FILE *fp = path_fopen("r", 1, _PATH_PROC_CPUINFO);
224 char buf[BUFSIZ];
225 struct utsname utsbuf;
226
227 /* architecture */
228 if (uname(&utsbuf) == -1)
229 PyErr_SetString(PyExc_Exception, "error: uname failed");
230 desc->arch = xstrdup(utsbuf.machine);
231
232 /* count CPU(s) */
233 while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", desc->ncpus))
234 desc->ncpus++;
235
236 /* details */
237 while (fgets(buf, sizeof(buf), fp) != NULL) {
238 /* IA64 */
239 if (lookup(buf, "vendor", &desc->vendor)) ;
240 else if (lookup(buf, "vendor_id", &desc->vendor)) ;
241 /* IA64 */
242 else if (lookup(buf, "family", &desc->family)) ;
243 else if (lookup(buf, "cpu family", &desc->family)) ;
244 else if (lookup(buf, "model", &desc->model)) ;
245 else if (lookup(buf, "stepping", &desc->stepping)) ;
246 else if (lookup(buf, "cpu MHz", &desc->mhz)) ;
247 else if (lookup(buf, "flags", &desc->flags)) ;
248 else if (lookup(buf, "bogomips", &desc->bogomips)) ;
249 else
250 continue;
251 }
252
253 if (desc->flags) {
254 snprintf(buf, sizeof(buf), " %s ", desc->flags);
255 if (strstr(buf, " svm "))
256 desc->virtflag = strdup("svm");
257 else if (strstr(buf, " vmx "))
258 desc->virtflag = strdup("vmx");
259
260 if (strstr(buf, " rm "))
261 desc->mode |= MODE_REAL;
262 if (strstr(buf, " tm "))
263 desc->mode |= MODE_TRANSPARENT;
264 if (strstr(buf, " lm "))
265 desc->mode |= MODE_LONG;
266 }
267
268 fclose(fp);
269
270 if (path_exist(_PATH_SYS_SYSTEM "/cpu/kernel_max"))
271 maxcpus = path_getnum(_PATH_SYS_SYSTEM "/cpu/kernel_max");
272
273 else
274 /* we are reading some /sys snapshot instead of the real /sys,
275 * let's use any crazy number... */
276 maxcpus = desc->ncpus > 2048 ? desc->ncpus : 2048;
277}
278
279static int
280has_pci_device(int vendor, int device)
281{
282 FILE *f;
283 int num, fn, ven, dev;
284 int res = 1;
285
286 f = path_fopen("r", 0, _PATH_PROC_PCIDEVS);
287 if (!f)
288 return 0;
289
290 /* for more details about bus/pci/devices format see
291 * drivers/pci/proc.c in linux kernel
292 */
293 while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
294 &num, &fn, &ven, &dev) == 4) {
295
296 if (ven == vendor && dev == device)
297 goto found;
298 }
299
300 res = 0;
301found:
302 fclose(f);
303 return res;
304}
305
306#if defined(__x86_64__) || defined(__i386__)
307
308/*
309 * This CPUID leaf returns the information about the hypervisor.
310 * EAX : maximum input value for CPUID supported by the hypervisor.
311 * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
312 */
313#define HYPERVISOR_INFO_LEAF 0x40000000
314
315static inline void
316cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
317 unsigned int *ecx, unsigned int *edx)
318{
319 __asm__(
320#if defined(__PIC__) && defined(__i386__)
321 /* x86 PIC cannot clobber ebx -- gcc bitches */
322 "pushl %%ebx;"
323 "cpuid;"
324 "movl %%ebx, %%esi;"
325 "popl %%ebx;"
326 : "=S" (*ebx),
327#else
328 "cpuid;"
329 : "=b" (*ebx),
330#endif
331 "=a" (*eax),
332 "=c" (*ecx),
333 "=d" (*edx)
334 : "1" (op), "c"(0));
335}
336
337static void
338read_hypervisor_cpuid(struct lscpu_desc *desc)
339{
340 unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
341 char hyper_vendor_id[13];
342
343 memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
344
345 cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
346 memcpy(hyper_vendor_id + 0, &ebx, 4);
347 memcpy(hyper_vendor_id + 4, &ecx, 4);
348 memcpy(hyper_vendor_id + 8, &edx, 4);
349 hyper_vendor_id[12] = '\0';
350
351 if (!hyper_vendor_id[0])
352 return;
353
354 if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
355 desc->hyper = HYPER_XEN;
356 else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
357 desc->hyper = HYPER_KVM;
358 else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
359 desc->hyper = HYPER_MSHV;
284eedb7
MT
360 else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
361 desc->hyper = HYPER_VMWARE;
5f4159ab
MT
362}
363
364#else /* ! __x86_64__ */
365static void
366read_hypervisor_cpuid(struct lscpu_desc *desc)
367{
368}
369#endif
370
371static void
372read_hypervisor(struct lscpu_desc *desc)
373{
374 read_hypervisor_cpuid(desc);
375
376 if (desc->hyper)
377 /* hvm */
378 desc->virtype = VIRT_FULL;
379
380 else if (path_exist(_PATH_PROC_XEN)) {
381 /* Xen para-virt or dom0 */
382 FILE *fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
383 int dom0 = 0;
384
385 if (fd) {
386 char buf[256];
387
388 if (fscanf(fd, "%s", buf) == 1 &&
389 !strcmp(buf, "control_d"))
390 dom0 = 1;
391 fclose(fd);
392 }
393 desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
394 desc->hyper = HYPER_XEN;
395
396 } else if (has_pci_device(0x5853, 0x0001)) {
397 /* Xen full-virt on non-x86_64 */
398 desc->hyper = HYPER_XEN;
399 desc->virtype = VIRT_FULL;
400 }
401}
402
403static PyObject *
404do_cpuinfo() {
405 /*
406 This function collects information about the CPU.
407 */
408 struct lscpu_desc _desc, *desc = &_desc;
409 memset(desc, 0, sizeof(*desc));
410
411 read_basicinfo(desc);
412 read_hypervisor(desc);
413
414 PyObject *o = NULL;
415 PyObject *d = PyDict_New();
416
417 /* Arch */
418 PyDict_SetItemString(d, "arch", PyString_FromString(desc->arch));
419
420 /* Vendor */
421 PyDict_SetItemString(d, "vendor", PyString_FromString(desc->vendor));
422
423 /* Family */
424 PyDict_SetItemString(d, "family", PyString_FromString(desc->family));
425
426 /* Model */
427 PyDict_SetItemString(d, "model", PyString_FromString(desc->model));
428
429 /* Virtualization flag */
a57a7635
MT
430 if (desc->virtflag) {
431 o = PyString_FromString(desc->virtflag);
432 } else {
433 o = Py_None;
434 }
435 PyDict_SetItemString(d, "virtflag", o);
5f4159ab
MT
436
437 /* Stepping */
438 PyDict_SetItemString(d, "stepping", PyString_FromString(desc->stepping));
439
440 /* MHz */
441 PyDict_SetItemString(d, "mhz", PyString_FromString(desc->mhz));
442
443 /* Bogomips */
444 PyDict_SetItemString(d, "bogomips", PyString_FromString(desc->bogomips));
445
446 /* Flags */
447 PyDict_SetItemString(d, "flags", PyString_FromString(desc->flags));
448
449 /* Mode */
450 if (desc->mode & (MODE_REAL | MODE_TRANSPARENT | MODE_LONG)) {
451 o = PyList_New(0);
452
453 if (desc->mode & MODE_REAL) {
454 PyList_Append(o, (PyObject *)PyInt_FromLong(16));
455 }
456 if (desc->mode & MODE_TRANSPARENT) {
457 PyList_Append(o, (PyObject *)PyInt_FromLong(32));
458 }
459 if (desc->mode & MODE_LONG) {
460 PyList_Append(o, (PyObject *)PyInt_FromLong(64));
461 }
462 PyDict_SetItemString(d, "mode", o);
463 }
464
465 /* Hypervisor */
466 if (desc->hyper == HYPER_NONE) {
467 o = Py_None;
468 } else {
469 o = PyString_FromString((const char *)hv_vendors[desc->hyper]);
470 }
471 PyDict_SetItemString(d, "hypervisor", o);
472
473 /* Virtualization type */
474 if (desc->virtype == VIRT_NONE) {
475 o = Py_None;
476 } else {
477 o = PyString_FromString((const char *)virt_types[desc->virtype]);
478 }
479 PyDict_SetItemString(d, "virtype", o);
480
481 /* Number of CPUs */
482 PyDict_SetItemString(d, "ncpus", PyInt_FromLong(desc->ncpus));
483
484 return d;
485}
486
487static PyMethodDef fireinfoModuleMethods[] = {
488 { "cpuinfo", (PyCFunction) do_cpuinfo, METH_NOARGS, NULL },
489 { NULL, NULL, 0, NULL }
490};
491
492void init_fireinfo(void) {
493 PyObject *m;
494
495 m = Py_InitModule("_fireinfo", fireinfoModuleMethods);
496}