--- /dev/null
+/*
+ * fireinfo.c
+ *
+ * Copyright (C) 2010 IPFire. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <Python.h>
+
+#include <sys/utsname.h>
+
+/*
+ Big parts of this were taken from
+ http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git;a=blob;f=sys-utils/lscpu.c
+*/
+
+/* /sys paths */
+#define _PATH_SYS_SYSTEM "/sys/devices/system"
+#define _PATH_SYS_CPU _PATH_SYS_SYSTEM "/cpu"
+#define _PATH_PROC_XEN "/proc/xen"
+#define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
+#define _PATH_PROC_CPUINFO "/proc/cpuinfo"
+#define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
+
+/* virtualization types */
+enum {
+ VIRT_NONE = 0,
+ VIRT_PARA,
+ VIRT_FULL
+};
+const char *virt_types[] = {
+ [VIRT_NONE] = "none",
+ [VIRT_PARA] = "para",
+ [VIRT_FULL] = "full"
+};
+
+/* hypervisor vendors */
+enum {
+ HYPER_NONE = 0,
+ HYPER_XEN,
+ HYPER_KVM,
+ HYPER_MSHV
+};
+const char *hv_vendors[] = {
+ [HYPER_NONE] = NULL,
+ [HYPER_XEN] = "Xen",
+ [HYPER_KVM] = "KVM",
+ [HYPER_MSHV] = "Microsoft"
+};
+
+/* CPU modes (bits) */
+enum {
+ MODE_REAL = (1 << 1),
+ MODE_TRANSPARENT = (1 << 2),
+ MODE_LONG = (1 << 3)
+};
+
+/* global description */
+struct lscpu_desc {
+ char *arch;
+ char *vendor;
+ char *family;
+ char *model;
+ char *virtflag; /* virtualization flag (vmx, svm) */
+ int hyper; /* hypervisor vendor ID */
+ int virtype; /* VIRT_PARA|FULL|NONE ? */
+ char *mhz;
+ char *stepping;
+ char *bogomips;
+ char *flags;
+ int mode; /* rm, lm or/and tm */
+
+ int ncpus; /* number of CPUs */
+};
+
+static size_t sysrootlen;
+static char pathbuf[PATH_MAX];
+static int maxcpus; /* size in bits of kernel cpu mask */
+
+static FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+static int path_exist(const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+
+static const char *
+path_vcreate(const char *path, va_list ap)
+{
+ if (sysrootlen)
+ vsnprintf(pathbuf + sysrootlen,
+ sizeof(pathbuf) - sysrootlen, path, ap);
+ else
+ vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
+ return pathbuf;
+}
+
+static FILE *
+path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
+{
+ FILE *f;
+ const char *p = path_vcreate(path, ap);
+
+ f = fopen(p, mode);
+ if (!f && exit_on_error)
+ PyErr_SetString(PyExc_Exception,
+ (const char *)sprintf("error: cannot open %s", p));
+ return f;
+}
+
+static FILE *
+path_fopen(const char *mode, int exit_on_error, const char *path, ...)
+{
+ FILE *fd;
+ va_list ap;
+
+ va_start(ap, path);
+ fd = path_vfopen(mode, exit_on_error, path, ap);
+ va_end(ap);
+
+ return fd;
+}
+
+static int
+path_getnum(const char *path, ...)
+{
+ FILE *fd;
+ va_list ap;
+ int result;
+
+ va_start(ap, path);
+ fd = path_vfopen("r", 1, path, ap);
+ va_end(ap);
+
+ if (fscanf(fd, "%d", &result) != 1) {
+ if (ferror(fd))
+ PyErr_SetString(PyExc_Exception, (const char *)sprintf("failed to read: %s", pathbuf));
+ else
+ PyErr_SetString(PyExc_Exception, (const char *)sprintf("parse error: %s", pathbuf));
+ }
+ fclose(fd);
+ return result;
+}
+
+static int
+path_exist(const char *path, ...)
+{
+ va_list ap;
+ const char *p;
+
+ va_start(ap, path);
+ p = path_vcreate(path, ap);
+ va_end(ap);
+
+ return access(p, F_OK) == 0;
+}
+
+static char *
+xstrdup(const char *str)
+{
+ char *s = strdup(str);
+ if (!s)
+ PyErr_SetString(PyExc_Exception, "error: strdup failed");
+ return s;
+}
+
+/* Lookup a pattern and get the value from cpuinfo.
+ * Format is:
+ *
+ * "<pattern> : <key>"
+ */
+int lookup(char *line, char *pattern, char **value)
+{
+ char *p, *v;
+ int len = strlen(pattern);
+
+ if (!*line)
+ return 0;
+
+ /* pattern */
+ if (strncmp(line, pattern, len))
+ return 0;
+
+ /* white spaces */
+ for (p = line + len; isspace(*p); p++);
+
+ /* separator */
+ if (*p != ':')
+ return 0;
+
+ /* white spaces */
+ for (++p; isspace(*p); p++);
+
+ /* value */
+ if (!*p)
+ return 0;
+ v = p;
+
+ /* end of value */
+ len = strlen(line) - 1;
+ for (p = line + len; isspace(*(p-1)); p--);
+ *p = '\0';
+
+ *value = xstrdup(v);
+ return 1;
+}
+
+static void
+read_basicinfo(struct lscpu_desc *desc)
+{
+ FILE *fp = path_fopen("r", 1, _PATH_PROC_CPUINFO);
+ char buf[BUFSIZ];
+ struct utsname utsbuf;
+
+ /* architecture */
+ if (uname(&utsbuf) == -1)
+ PyErr_SetString(PyExc_Exception, "error: uname failed");
+ desc->arch = xstrdup(utsbuf.machine);
+
+ /* count CPU(s) */
+ while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d", desc->ncpus))
+ desc->ncpus++;
+
+ /* details */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ /* IA64 */
+ if (lookup(buf, "vendor", &desc->vendor)) ;
+ else if (lookup(buf, "vendor_id", &desc->vendor)) ;
+ /* IA64 */
+ else if (lookup(buf, "family", &desc->family)) ;
+ else if (lookup(buf, "cpu family", &desc->family)) ;
+ else if (lookup(buf, "model", &desc->model)) ;
+ else if (lookup(buf, "stepping", &desc->stepping)) ;
+ else if (lookup(buf, "cpu MHz", &desc->mhz)) ;
+ else if (lookup(buf, "flags", &desc->flags)) ;
+ else if (lookup(buf, "bogomips", &desc->bogomips)) ;
+ else
+ continue;
+ }
+
+ if (desc->flags) {
+ snprintf(buf, sizeof(buf), " %s ", desc->flags);
+ if (strstr(buf, " svm "))
+ desc->virtflag = strdup("svm");
+ else if (strstr(buf, " vmx "))
+ desc->virtflag = strdup("vmx");
+
+ if (strstr(buf, " rm "))
+ desc->mode |= MODE_REAL;
+ if (strstr(buf, " tm "))
+ desc->mode |= MODE_TRANSPARENT;
+ if (strstr(buf, " lm "))
+ desc->mode |= MODE_LONG;
+ }
+
+ fclose(fp);
+
+ if (path_exist(_PATH_SYS_SYSTEM "/cpu/kernel_max"))
+ maxcpus = path_getnum(_PATH_SYS_SYSTEM "/cpu/kernel_max");
+
+ else
+ /* we are reading some /sys snapshot instead of the real /sys,
+ * let's use any crazy number... */
+ maxcpus = desc->ncpus > 2048 ? desc->ncpus : 2048;
+}
+
+static int
+has_pci_device(int vendor, int device)
+{
+ FILE *f;
+ int num, fn, ven, dev;
+ int res = 1;
+
+ f = path_fopen("r", 0, _PATH_PROC_PCIDEVS);
+ if (!f)
+ return 0;
+
+ /* for more details about bus/pci/devices format see
+ * drivers/pci/proc.c in linux kernel
+ */
+ while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
+ &num, &fn, &ven, &dev) == 4) {
+
+ if (ven == vendor && dev == device)
+ goto found;
+ }
+
+ res = 0;
+found:
+ fclose(f);
+ return res;
+}
+
+#if defined(__x86_64__) || defined(__i386__)
+
+/*
+ * This CPUID leaf returns the information about the hypervisor.
+ * EAX : maximum input value for CPUID supported by the hypervisor.
+ * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
+ */
+#define HYPERVISOR_INFO_LEAF 0x40000000
+
+static inline void
+cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ __asm__(
+#if defined(__PIC__) && defined(__i386__)
+ /* x86 PIC cannot clobber ebx -- gcc bitches */
+ "pushl %%ebx;"
+ "cpuid;"
+ "movl %%ebx, %%esi;"
+ "popl %%ebx;"
+ : "=S" (*ebx),
+#else
+ "cpuid;"
+ : "=b" (*ebx),
+#endif
+ "=a" (*eax),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "1" (op), "c"(0));
+}
+
+static void
+read_hypervisor_cpuid(struct lscpu_desc *desc)
+{
+ unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
+ char hyper_vendor_id[13];
+
+ memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
+
+ cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
+ memcpy(hyper_vendor_id + 0, &ebx, 4);
+ memcpy(hyper_vendor_id + 4, &ecx, 4);
+ memcpy(hyper_vendor_id + 8, &edx, 4);
+ hyper_vendor_id[12] = '\0';
+
+ if (!hyper_vendor_id[0])
+ return;
+
+ if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
+ desc->hyper = HYPER_XEN;
+ else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
+ desc->hyper = HYPER_KVM;
+ else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
+ desc->hyper = HYPER_MSHV;
+}
+
+#else /* ! __x86_64__ */
+static void
+read_hypervisor_cpuid(struct lscpu_desc *desc)
+{
+}
+#endif
+
+static void
+read_hypervisor(struct lscpu_desc *desc)
+{
+ read_hypervisor_cpuid(desc);
+
+ if (desc->hyper)
+ /* hvm */
+ desc->virtype = VIRT_FULL;
+
+ else if (path_exist(_PATH_PROC_XEN)) {
+ /* Xen para-virt or dom0 */
+ FILE *fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
+ int dom0 = 0;
+
+ if (fd) {
+ char buf[256];
+
+ if (fscanf(fd, "%s", buf) == 1 &&
+ !strcmp(buf, "control_d"))
+ dom0 = 1;
+ fclose(fd);
+ }
+ desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
+ desc->hyper = HYPER_XEN;
+
+ } else if (has_pci_device(0x5853, 0x0001)) {
+ /* Xen full-virt on non-x86_64 */
+ desc->hyper = HYPER_XEN;
+ desc->virtype = VIRT_FULL;
+ }
+}
+
+static PyObject *
+do_cpuinfo() {
+ /*
+ This function collects information about the CPU.
+ */
+ struct lscpu_desc _desc, *desc = &_desc;
+ memset(desc, 0, sizeof(*desc));
+
+ read_basicinfo(desc);
+ read_hypervisor(desc);
+
+ PyObject *o = NULL;
+ PyObject *d = PyDict_New();
+
+ /* Arch */
+ PyDict_SetItemString(d, "arch", PyString_FromString(desc->arch));
+
+ /* Vendor */
+ PyDict_SetItemString(d, "vendor", PyString_FromString(desc->vendor));
+
+ /* Family */
+ PyDict_SetItemString(d, "family", PyString_FromString(desc->family));
+
+ /* Model */
+ PyDict_SetItemString(d, "model", PyString_FromString(desc->model));
+
+ /* Virtualization flag */
+ PyDict_SetItemString(d, "virtflag", PyString_FromString(desc->virtflag));
+
+ /* Stepping */
+ PyDict_SetItemString(d, "stepping", PyString_FromString(desc->stepping));
+
+ /* MHz */
+ PyDict_SetItemString(d, "mhz", PyString_FromString(desc->mhz));
+
+ /* Bogomips */
+ PyDict_SetItemString(d, "bogomips", PyString_FromString(desc->bogomips));
+
+ /* Flags */
+ PyDict_SetItemString(d, "flags", PyString_FromString(desc->flags));
+
+ /* Mode */
+ if (desc->mode & (MODE_REAL | MODE_TRANSPARENT | MODE_LONG)) {
+ o = PyList_New(0);
+
+ if (desc->mode & MODE_REAL) {
+ PyList_Append(o, (PyObject *)PyInt_FromLong(16));
+ }
+ if (desc->mode & MODE_TRANSPARENT) {
+ PyList_Append(o, (PyObject *)PyInt_FromLong(32));
+ }
+ if (desc->mode & MODE_LONG) {
+ PyList_Append(o, (PyObject *)PyInt_FromLong(64));
+ }
+ PyDict_SetItemString(d, "mode", o);
+ }
+
+ /* Hypervisor */
+ if (desc->hyper == HYPER_NONE) {
+ o = Py_None;
+ } else {
+ o = PyString_FromString((const char *)hv_vendors[desc->hyper]);
+ }
+ PyDict_SetItemString(d, "hypervisor", o);
+
+ /* Virtualization type */
+ if (desc->virtype == VIRT_NONE) {
+ o = Py_None;
+ } else {
+ o = PyString_FromString((const char *)virt_types[desc->virtype]);
+ }
+ PyDict_SetItemString(d, "virtype", o);
+
+ /* Number of CPUs */
+ PyDict_SetItemString(d, "ncpus", PyInt_FromLong(desc->ncpus));
+
+ return d;
+}
+
+static PyMethodDef fireinfoModuleMethods[] = {
+ { "cpuinfo", (PyCFunction) do_cpuinfo, METH_NOARGS, NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+void init_fireinfo(void) {
+ PyObject *m;
+
+ m = Py_InitModule("_fireinfo", fireinfoModuleMethods);
+}