From: Michael Tremer Date: Sun, 14 Nov 2010 23:38:11 +0000 (+0100) Subject: Initial import. X-Git-Tag: v0.2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5f4159abe56b5a78d8f2abd7930c46b871f90bb2;p=oddments%2Ffireinfo.git Initial import. --- 5f4159abe56b5a78d8f2abd7930c46b871f90bb2 diff --git a/fireinfo/__init__.py b/fireinfo/__init__.py new file mode 100644 index 0000000..7ac7f27 --- /dev/null +++ b/fireinfo/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/python + +# Empty module so far... diff --git a/setup.py b/setup.py new file mode 100644 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 index 0000000..f4ef19a --- /dev/null +++ b/src/fireinfo.c @@ -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 . + */ + +#include + +#include + +/* + 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: + * + * " : " + */ +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); +}