]> git.ipfire.org Git - oddments/fireinfo.git/commitdiff
Initial import.
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 14 Nov 2010 23:38:11 +0000 (00:38 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 14 Nov 2010 23:38:11 +0000 (00:38 +0100)
fireinfo/__init__.py [new file with mode: 0644]
setup.py [new file with mode: 0644]
src/fireinfo.c [new file with mode: 0644]

diff --git a/fireinfo/__init__.py b/fireinfo/__init__.py
new file mode 100644 (file)
index 0000000..7ac7f27
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/python
+
+# Empty module so far...
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..aa586a8
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,11 @@
+
+from distutils.core import setup, Extension
+
+setup(
+       name = "fireinfo",
+       version = "0.1",
+       ext_modules = [
+               Extension("_fireinfo", ["src/fireinfo.c"])
+       ],
+       packages = ["fireinfo"],
+)
diff --git a/src/fireinfo.c b/src/fireinfo.c
new file mode 100644 (file)
index 0000000..f4ef19a
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * 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);
+}