_collecty.la
_collecty_la_SOURCES = \
- src/_collectymodule.c
+ src/_collecty/_collectymodule.c \
+ src/_collecty/_collectymodule.h \
+ src/_collecty/blockdev.c \
+ src/_collecty/ping.c \
+ src/_collecty/sensors.c \
+ src/_collecty/utils.c
_collecty_la_CFLAGS = \
$(AM_CFLAGS) \
--- /dev/null
+/*
+ * collecty
+ * Copyright (C) 2015 IPFire Team (www.ipfire.org)
+ *
+ * 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 3 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 "_collectymodule.h"
+
+static PyMethodDef collecty_module_methods[] = {
+ {"get_detected_sensors", (PyCFunction)_collecty_get_detected_sensors, METH_VARARGS, NULL},
+ {"get_mountpoints", (PyCFunction)_collecty_get_mountpoints, METH_NOARGS, NULL},
+ {"sensors_cleanup", (PyCFunction)_collecty_sensors_cleanup, METH_NOARGS, NULL},
+ {"sensors_init", (PyCFunction)_collecty_sensors_init, METH_NOARGS, NULL},
+ {NULL},
+};
+
+static struct PyModuleDef collecty_module = {
+ PyModuleDef_HEAD_INIT,
+ "_collecty", /* m_name */
+ "_collecty module", /* m_doc */
+ -1, /* m_size */
+ collecty_module_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC PyInit__collecty(void) {
+ if (PyType_Ready(&BlockDeviceType) < 0)
+ return NULL;
+
+ if (PyType_Ready(&PingType) < 0)
+ return NULL;
+
+ if (PyType_Ready(&SensorType) < 0)
+ return NULL;
+
+ PyObject* m = PyModule_Create(&collecty_module);
+
+ Py_INCREF(&BlockDeviceType);
+ PyModule_AddObject(m, "BlockDevice", (PyObject*)&BlockDeviceType);
+
+ Py_INCREF(&PingType);
+ PyModule_AddObject(m, "Ping", (PyObject*)&PingType);
+
+ PyExc_PingError = PyErr_NewException("_collecty.PingError", NULL, NULL);
+ Py_INCREF(PyExc_PingError);
+ PyModule_AddObject(m, "PingError", PyExc_PingError);
+
+ PyExc_PingAddHostError = PyErr_NewException("_collecty.PingAddHostError", NULL, NULL);
+ Py_INCREF(PyExc_PingAddHostError);
+ PyModule_AddObject(m, "PingAddHostError", PyExc_PingAddHostError);
+
+ Py_INCREF(&SensorType);
+ PyModule_AddObject(m, "Sensor", (PyObject*)&SensorType);
+
+ return m;
+}
--- /dev/null
+/*
+ * collecty
+ * Copyright (C) 2015 IPFire Team (www.ipfire.org)
+ *
+ * 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 3 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 <atasmart.h>
+#include <linux/hdreg.h>
+#include <mntent.h>
+#include <oping.h>
+#include <sensors/error.h>
+#include <sensors/sensors.h>
+
+#define MODEL_SIZE 40
+#define SERIAL_SIZE 20
+
+#define PING_HISTORY_SIZE 1024
+#define PING_DEFAULT_COUNT 10
+#define PING_DEFAULT_TIMEOUT 8
+
+/* block devices */
+typedef struct {
+ PyObject_HEAD
+ char* path;
+ struct hd_driveid identity;
+ SkDisk* disk;
+} BlockDevice;
+
+PyTypeObject BlockDeviceType;
+
+void BlockDevice_dealloc(BlockDevice* self);
+int BlockDevice_get_identity(BlockDevice* device);
+int BlockDevice_smart_is_available(BlockDevice* device);
+int BlockDevice_check_sleep_mode(BlockDevice* device);
+PyObject * BlockDevice_new(PyTypeObject* type, PyObject* args, PyObject* kwds);
+int BlockDevice_init(BlockDevice* self, PyObject* args, PyObject* kwds);
+PyObject* BlockDevice_get_path(PyObject* self);
+PyObject* BlockDevice_get_model(PyObject* self);
+PyObject* BlockDevice_get_serial(PyObject* self);
+PyObject* BlockDevice_is_smart_supported(PyObject* self);
+PyObject* BlockDevice_is_awake(PyObject* self);
+PyObject* BlockDevice_get_bad_sectors(PyObject* self);
+PyObject* BlockDevice_get_temperature(PyObject* self);
+
+/* ping */
+PyObject* PyExc_PingError;
+PyObject* PyExc_PingAddHostError;
+
+typedef struct {
+ PyObject_HEAD
+ pingobj_t* ping;
+ const char* host;
+ struct {
+ double history[PING_HISTORY_SIZE];
+ size_t history_index;
+ size_t history_size;
+ size_t packets_sent;
+ size_t packets_rcvd;
+ double average;
+ double stddev;
+ double loss;
+ } stats;
+} PingObject;
+
+PyTypeObject PingType;
+
+void Ping_dealloc(PingObject* self);
+void Ping_init_stats(PingObject* self);
+PyObject* Ping_new(PyTypeObject* type, PyObject* args, PyObject* kwds);
+int Ping_init(PingObject* self, PyObject* args, PyObject* kwds);
+double Ping_compute_average(PingObject* self);
+double Ping_compute_stddev(PingObject* self, double mean);
+PyObject* Ping_ping(PingObject* self, PyObject* args, PyObject* kwds);
+PyObject* Ping_get_packets_sent(PingObject* self);
+PyObject* Ping_get_packets_rcvd(PingObject* self);
+PyObject* Ping_get_average(PingObject* self);
+PyObject* Ping_get_stddev(PingObject* self);
+PyObject* Ping_get_loss(PingObject* self);
+
+/* sensors */
+typedef struct {
+ PyObject_HEAD
+ const sensors_chip_name* chip;
+ const sensors_feature* feature;
+} SensorObject;
+
+PyTypeObject SensorType;
+
+void Sensor_dealloc(SensorObject* self);
+PyObject* Sensor_new(PyTypeObject* type, PyObject* args, PyObject* kwds);
+int Sensor_init(SensorObject* self, PyObject* args, PyObject* kwds);
+PyObject* Sensor_get_label(SensorObject* self);
+PyObject* Sensor_get_name(SensorObject* self);
+PyObject* Sensor_get_type(SensorObject* self);
+PyObject* Sensor_get_bus(SensorObject* self);
+PyObject* Sensor_return_value(SensorObject* sensor, sensors_subfeature_type subfeature_type);
+PyObject* Sensor_get_value(SensorObject* self);
+PyObject* Sensor_get_critical(SensorObject* self);
+PyObject* Sensor_get_maximum(SensorObject* self);
+PyObject* Sensor_get_minimum(SensorObject* self);
+PyObject* Sensor_get_high(SensorObject* self);
+
+PyObject* _collecty_sensors_init();
+PyObject* _collecty_sensors_cleanup();
+PyObject* _collecty_get_detected_sensors(PyObject* o, PyObject* args);
+
+/* utils */
+int _collecty_mountpoint_is_virtual(const struct mntent* mp);
+PyObject* _collecty_get_mountpoints();
--- /dev/null
+/*
+ * collecty
+ * Copyright (C) 2015 IPFire Team (www.ipfire.org)
+ *
+ * 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 3 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 <atasmart.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "_collectymodule.h"
+
+static PyGetSetDef BlockDevice_getsetters[] = {
+ {"path", (getter)BlockDevice_get_path, NULL, NULL, NULL},
+ {"model", (getter)BlockDevice_get_model, NULL, NULL, NULL},
+ {"serial", (getter)BlockDevice_get_serial, NULL, NULL, NULL},
+};
+
+static PyMethodDef BlockDevice_methods[] = {
+ {"get_bad_sectors", (PyCFunction)BlockDevice_get_bad_sectors, METH_NOARGS, NULL},
+ {"get_temperature", (PyCFunction)BlockDevice_get_temperature, METH_NOARGS, NULL},
+ {"is_smart_supported", (PyCFunction)BlockDevice_is_smart_supported, METH_NOARGS, NULL},
+ {"is_awake", (PyCFunction)BlockDevice_is_awake, METH_NOARGS, NULL},
+ {NULL}
+};
+
+PyTypeObject BlockDeviceType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_collecty.BlockDevice", /*tp_name*/
+ sizeof(BlockDevice), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)BlockDevice_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "BlockDevice objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ BlockDevice_methods, /* tp_methods */
+ 0, /* tp_members */
+ BlockDevice_getsetters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)BlockDevice_init, /* tp_init */
+ 0, /* tp_alloc */
+ BlockDevice_new, /* tp_new */
+};
+
+void BlockDevice_dealloc(BlockDevice* self) {
+ if (self->disk)
+ sk_disk_free(self->disk);
+
+ if (self->path)
+ free(self->path);
+
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+int BlockDevice_get_identity(BlockDevice* device) {
+ int fd;
+
+ if ((fd = open(device->path, O_RDONLY | O_NONBLOCK)) < 0) {
+ return 1;
+ }
+
+ int r = ioctl(fd, HDIO_GET_IDENTITY, &device->identity);
+ close(fd);
+
+ if (r)
+ return 1;
+
+ return 0;
+}
+
+int BlockDevice_smart_is_available(BlockDevice* device) {
+ SkBool available = FALSE;
+
+ int r = sk_disk_smart_is_available(device->disk, &available);
+ if (r)
+ return -1;
+
+ if (available)
+ return 0;
+
+ return 1;
+}
+
+int BlockDevice_check_sleep_mode(BlockDevice* device) {
+ SkBool awake = FALSE;
+
+ int r = sk_disk_check_sleep_mode(device->disk, &awake);
+ if (r)
+ return -1;
+
+ if (awake)
+ return 0;
+
+ return 1;
+}
+
+PyObject * BlockDevice_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
+ BlockDevice* self = (BlockDevice*)type->tp_alloc(type, 0);
+
+ if (self) {
+ self->path = NULL;
+
+ // libatasmart
+ self->disk = NULL;
+ }
+
+ return (PyObject *)self;
+}
+
+int BlockDevice_init(BlockDevice* self, PyObject* args, PyObject* kwds) {
+ const char* path = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return -1;
+
+ self->path = strdup(path);
+
+ int r = BlockDevice_get_identity(self);
+ if (r) {
+ PyErr_Format(PyExc_OSError, "Could not open block device: %s", path);
+ return -1;
+ }
+
+ r = sk_disk_open(path, &self->disk);
+ if (r == 0) {
+ if (BlockDevice_smart_is_available(self) == 0) {
+ if (BlockDevice_check_sleep_mode(self) == 0) {
+ r = sk_disk_smart_read_data(self->disk);
+ if (r) {
+ PyErr_Format(PyExc_OSError, "Could not open block device %s: %s", path,
+ strerror(errno));
+ return -1;
+ }
+ }
+ }
+ } else {
+ PyErr_Format(PyExc_OSError, "Could not open block device %s: %s", path,
+ strerror(errno));
+ return -1;
+ }
+
+ //sk_disk_identify_is_available
+
+ return 0;
+}
+
+PyObject* BlockDevice_get_path(PyObject* self) {
+ BlockDevice* device = (BlockDevice*)self;
+
+ return PyUnicode_FromString(device->path);
+}
+
+static void clean_string(char *s) {
+ for (char* e = s; *e; e++) {
+ if (*e < ' ' || *e >= 127)
+ *e = ' ';
+ }
+}
+
+static void drop_spaces(char *s) {
+ char *d = s;
+ bool prev_space = false;
+
+ s += strspn(s, " ");
+
+ for (; *s; s++) {
+ if (prev_space) {
+ if (*s != ' ') {
+ prev_space = false;
+ *(d++) = ' ';
+ *(d++) = *s;
+ }
+ } else {
+ if (*s == ' ')
+ prev_space = true;
+ else
+ *(d++) = *s;
+ }
+ }
+
+ *d = 0;
+}
+
+static void copy_string(char* d, const char* s, size_t n) {
+ // Copy the source buffer to the destination buffer up to n
+ memcpy(d, s, n);
+
+ // Terminate the destination buffer with NULL
+ d[n] = '\0';
+
+ // Clean up the string from non-printable characters
+ clean_string(d);
+ drop_spaces(d);
+}
+
+PyObject* BlockDevice_get_model(PyObject* self) {
+ BlockDevice* device = (BlockDevice*)self;
+
+ char model[MODEL_SIZE + 1];
+ copy_string(model, device->identity.model, sizeof(model));
+
+ return PyUnicode_FromString(model);
+}
+
+PyObject* BlockDevice_get_serial(PyObject* self) {
+ BlockDevice* device = (BlockDevice*)self;
+
+ char serial[SERIAL_SIZE + 1];
+ copy_string(serial, device->identity.serial_no, sizeof(serial));
+
+ return PyUnicode_FromString(serial);
+}
+
+PyObject* BlockDevice_is_smart_supported(PyObject* self) {
+ BlockDevice* device = (BlockDevice*)self;
+
+ if (BlockDevice_smart_is_available(device) == 0)
+ Py_RETURN_TRUE;
+
+ Py_RETURN_FALSE;
+}
+
+PyObject* BlockDevice_is_awake(PyObject* self) {
+ BlockDevice* device = (BlockDevice*)self;
+
+ if (BlockDevice_check_sleep_mode(device) == 0)
+ Py_RETURN_TRUE;
+
+ Py_RETURN_FALSE;
+}
+
+PyObject* BlockDevice_get_bad_sectors(PyObject* self) {
+ BlockDevice* device = (BlockDevice*)self;
+
+ if (BlockDevice_smart_is_available(device)) {
+ PyErr_Format(PyExc_OSError, "Device does not support SMART");
+ return NULL;
+ }
+
+ uint64_t bad_sectors;
+ int r = sk_disk_smart_get_bad(device->disk, &bad_sectors);
+ if (r)
+ return NULL;
+
+ return PyLong_FromUnsignedLongLong((unsigned long long)bad_sectors);
+}
+
+PyObject* BlockDevice_get_temperature(PyObject* self) {
+ BlockDevice* device = (BlockDevice*)self;
+
+ if (BlockDevice_smart_is_available(device)) {
+ PyErr_Format(PyExc_OSError, "Device does not support SMART");
+ return NULL;
+ }
+
+ uint64_t mkelvin;
+ int r = sk_disk_smart_get_temperature(device->disk, &mkelvin);
+ if (r)
+ return NULL;
+
+ // Convert the temperature to Kelvin
+ return PyFloat_FromDouble((double)mkelvin / 1000.0);
+}
--- /dev/null
+/*
+ * collecty
+ * Copyright (C) 2015 IPFire Team (www.ipfire.org)
+ *
+ * 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 3 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 <errno.h>
+#include <oping.h>
+#include <time.h>
+
+#include "_collectymodule.h"
+
+static PyGetSetDef Ping_getsetters[] = {
+ {"average", (getter)Ping_get_average, NULL, NULL, NULL},
+ {"loss", (getter)Ping_get_loss, NULL, NULL, NULL},
+ {"stddev", (getter)Ping_get_stddev, NULL, NULL, NULL},
+ {"packets_sent", (getter)Ping_get_packets_sent, NULL, NULL, NULL},
+ {"packets_rcvd", (getter)Ping_get_packets_rcvd, NULL, NULL, NULL},
+ {NULL}
+};
+
+static PyMethodDef Ping_methods[] = {
+ {"ping", (PyCFunction)Ping_ping, METH_VARARGS|METH_KEYWORDS, NULL},
+ {NULL}
+};
+
+PyTypeObject PingType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_collecty.Ping", /*tp_name*/
+ sizeof(PingObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Ping_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "Ping object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Ping_methods, /* tp_methods */
+ 0, /* tp_members */
+ Ping_getsetters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Ping_init, /* tp_init */
+ 0, /* tp_alloc */
+ Ping_new, /* tp_new */
+};
+
+void Ping_dealloc(PingObject* self) {
+ if (self->ping)
+ ping_destroy(self->ping);
+
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+void Ping_init_stats(PingObject* self) {
+ self->stats.history_index = 0;
+ self->stats.history_size = 0;
+ self->stats.packets_sent = 0;
+ self->stats.packets_rcvd = 0;
+
+ self->stats.average = 0.0;
+ self->stats.stddev = 0.0;
+ self->stats.loss = 0.0;
+}
+
+PyObject* Ping_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
+ PingObject* self = (PingObject*)type->tp_alloc(type, 0);
+
+ if (self) {
+ self->ping = NULL;
+ self->host = NULL;
+
+ Ping_init_stats(self);
+ }
+
+ return (PyObject*)self;
+}
+
+int Ping_init(PingObject* self, PyObject* args, PyObject* kwds) {
+ char* kwlist[] = {"host", "family", "timeout", "ttl", NULL};
+ int family = PING_DEF_AF;
+ double timeout = PING_DEFAULT_TIMEOUT;
+ int ttl = PING_DEF_TTL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|idi", kwlist, &self->host,
+ &family, &timeout, &ttl))
+ return -1;
+
+ if (family != AF_UNSPEC && family != AF_INET6 && family != AF_INET) {
+ PyErr_Format(PyExc_ValueError, "Family must be AF_UNSPEC, AF_INET6, or AF_INET");
+ return -1;
+ }
+
+ if (timeout < 0) {
+ PyErr_Format(PyExc_ValueError, "Timeout must be greater than zero");
+ return -1;
+ }
+
+ if (ttl < 1 || ttl > 255) {
+ PyErr_Format(PyExc_ValueError, "TTL must be between 1 and 255");
+ return -1;
+ }
+
+ self->ping = ping_construct();
+ if (!self->ping) {
+ return -1;
+ }
+
+ // Set options
+ int r;
+
+ r = ping_setopt(self->ping, PING_OPT_AF, &family);
+ if (r) {
+ PyErr_Format(PyExc_RuntimeError, "Could not set address family: %s",
+ ping_get_error(self->ping));
+ return -1;
+ }
+
+ if (timeout > 0) {
+ r = ping_setopt(self->ping, PING_OPT_TIMEOUT, &timeout);
+
+ if (r) {
+ PyErr_Format(PyExc_RuntimeError, "Could not set timeout: %s",
+ ping_get_error(self->ping));
+ return -1;
+ }
+ }
+
+ r = ping_setopt(self->ping, PING_OPT_TTL, &ttl);
+ if (r) {
+ PyErr_Format(PyExc_RuntimeError, "Could not set TTL: %s",
+ ping_get_error(self->ping));
+ return -1;
+ }
+
+ return 0;
+}
+
+double Ping_compute_average(PingObject* self) {
+ assert(self->stats.packets_rcvd > 0);
+
+ double total_latency = 0.0;
+
+ for (int i = 0; i < self->stats.history_size; i++) {
+ if (self->stats.history[i] > 0)
+ total_latency += self->stats.history[i];
+ }
+
+ return total_latency / self->stats.packets_rcvd;
+}
+
+double Ping_compute_stddev(PingObject* self, double mean) {
+ assert(self->stats.packets_rcvd > 0);
+
+ double deviation = 0.0;
+
+ for (int i = 0; i < self->stats.history_size; i++) {
+ if (self->stats.history[i] > 0) {
+ deviation += pow(self->stats.history[i] - mean, 2);
+ }
+ }
+
+ // Normalise
+ deviation /= self->stats.packets_rcvd;
+
+ return sqrt(deviation);
+}
+
+static void Ping_compute_stats(PingObject* self) {
+ // Compute the average latency
+ self->stats.average = Ping_compute_average(self);
+
+ // Compute the standard deviation
+ self->stats.stddev = Ping_compute_stddev(self, self->stats.average);
+
+ // Compute lost packets
+ self->stats.loss = 1.0;
+ self->stats.loss -= (double)self->stats.packets_rcvd \
+ / (double)self->stats.packets_sent;
+}
+
+static double time_elapsed(struct timeval* t0) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ double r = now.tv_sec - t0->tv_sec;
+ r += ((double)now.tv_usec / 1000000) - ((double)t0->tv_usec / 1000000);
+
+ return r;
+}
+
+PyObject* Ping_ping(PingObject* self, PyObject* args, PyObject* kwds) {
+ char* kwlist[] = {"count", "deadline", NULL};
+ size_t count = PING_DEFAULT_COUNT;
+ double deadline = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Id", kwlist, &count, &deadline))
+ return NULL;
+
+ int r = ping_host_add(self->ping, self->host);
+ if (r) {
+ PyErr_Format(PyExc_PingAddHostError, "Could not add host %s: %s",
+ self->host, ping_get_error(self->ping));
+ return NULL;
+ }
+
+ // Reset all collected statistics in case ping() is called more than once.
+ Ping_init_stats(self);
+
+ // Save start time
+ struct timeval time_start;
+ r = gettimeofday(&time_start, NULL);
+ if (r) {
+ PyErr_Format(PyExc_RuntimeError, "Could not determine start time");
+ return NULL;
+ }
+
+ // Do the pinging
+ while (count--) {
+ self->stats.packets_sent++;
+
+ Py_BEGIN_ALLOW_THREADS
+ r = ping_send(self->ping);
+ Py_END_ALLOW_THREADS
+
+ // Count recieved packets
+ if (r >= 0) {
+ self->stats.packets_rcvd += r;
+
+ // Raise any errors
+ } else {
+ PyErr_Format(PyExc_RuntimeError, "Error executing ping_send(): %s",
+ ping_get_error(self->ping));
+ return NULL;
+ }
+
+ // Extract all data
+ pingobj_iter_t* iter = ping_iterator_get(self->ping);
+
+ double* latency = &self->stats.history[self->stats.history_index];
+ size_t buffer_size = sizeof(latency);
+ ping_iterator_get_info(iter, PING_INFO_LATENCY, latency, &buffer_size);
+
+ // Increase the history pointer
+ self->stats.history_index++;
+ self->stats.history_index %= sizeof(self->stats.history);
+
+ // Increase the history size
+ if (self->stats.history_size < sizeof(self->stats.history))
+ self->stats.history_size++;
+
+ // Check if the deadline is due
+ if (deadline > 0) {
+ double elapsed_time = time_elapsed(&time_start);
+
+ // If we have run longer than the deadline is, we end the main loop
+ if (elapsed_time >= deadline)
+ break;
+ }
+ }
+
+ if (self->stats.packets_rcvd == 0) {
+ PyErr_Format(PyExc_PingError, "No replies received from %s", self->host);
+ return NULL;
+ }
+
+ Ping_compute_stats(self);
+
+ Py_RETURN_NONE;
+}
+
+PyObject* Ping_get_packets_sent(PingObject* self) {
+ return PyLong_FromUnsignedLong(self->stats.packets_sent);
+}
+
+PyObject* Ping_get_packets_rcvd(PingObject* self) {
+ return PyLong_FromUnsignedLong(self->stats.packets_rcvd);
+}
+
+PyObject* Ping_get_average(PingObject* self) {
+ return PyFloat_FromDouble(self->stats.average);
+}
+
+PyObject* Ping_get_stddev(PingObject* self) {
+ return PyFloat_FromDouble(self->stats.stddev);
+}
+
+PyObject* Ping_get_loss(PingObject* self) {
+ return PyFloat_FromDouble(self->stats.loss);
+}
--- /dev/null
+/*
+ * collecty
+ * Copyright (C) 2015 IPFire Team (www.ipfire.org)
+ *
+ * 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 3 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 <errno.h>
+#include <mntent.h>
+#include <sensors/error.h>
+#include <sensors/sensors.h>
+
+#include "_collectymodule.h"
+
+static PyGetSetDef Sensor_getsetters[] = {
+ {"bus", (getter)Sensor_get_bus, NULL, NULL, NULL},
+ {"critical", (getter)Sensor_get_critical, NULL, NULL, NULL},
+ {"high", (getter)Sensor_get_high, NULL, NULL, NULL},
+ {"label", (getter)Sensor_get_label, NULL, NULL, NULL},
+ {"maximum", (getter)Sensor_get_maximum, NULL, NULL, NULL},
+ {"minumum", (getter)Sensor_get_minimum, NULL, NULL, NULL},
+ {"name", (getter)Sensor_get_name, NULL, NULL, NULL},
+ {"type", (getter)Sensor_get_type, NULL, NULL, NULL},
+ {"value", (getter)Sensor_get_value, NULL, NULL, NULL},
+ {NULL},
+};
+
+PyTypeObject SensorType = {
+ PyObject_HEAD_INIT(NULL)
+ "_collecty.Sensor", /*tp_name*/
+ sizeof(SensorObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Sensor_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "Sensor objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ Sensor_getsetters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Sensor_init, /* tp_init */
+ 0, /* tp_alloc */
+ Sensor_new, /* tp_new */
+};
+
+void Sensor_dealloc(SensorObject* self) {
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+PyObject* Sensor_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
+ SensorObject* self = (SensorObject*)type->tp_alloc(type, 0);
+
+ return (PyObject *)self;
+}
+
+int Sensor_init(SensorObject* self, PyObject* args, PyObject* kwds) {
+ return 0;
+}
+
+PyObject* Sensor_get_label(SensorObject* self) {
+ char* label = sensors_get_label(self->chip, self->feature);
+
+ if (label) {
+ PyObject* string = PyUnicode_FromString(label);
+ free(label);
+
+ return string;
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject* Sensor_get_name(SensorObject* self) {
+ char chip_name[512];
+
+ int r = sensors_snprintf_chip_name(chip_name, sizeof(chip_name), self->chip);
+ if (r < 0) {
+ PyErr_Format(PyExc_RuntimeError, "Could not print chip name");
+ return NULL;
+ }
+
+ return PyUnicode_FromString(chip_name);
+}
+
+PyObject* Sensor_get_type(SensorObject* self) {
+ const char* type = NULL;
+
+ switch (self->feature->type) {
+ case SENSORS_FEATURE_IN:
+ type = "voltage";
+ break;
+
+ case SENSORS_FEATURE_FAN:
+ type = "fan";
+ break;
+
+ case SENSORS_FEATURE_TEMP:
+ type = "temperature";
+ break;
+
+ case SENSORS_FEATURE_POWER:
+ type = "power";
+ break;
+
+ default:
+ break;
+ }
+
+ if (type)
+ return PyUnicode_FromString(type);
+
+ Py_RETURN_NONE;
+}
+
+PyObject* Sensor_get_bus(SensorObject* self) {
+ const char* type = NULL;
+
+ switch (self->chip->bus.type) {
+ case SENSORS_BUS_TYPE_I2C:
+ type = "i2c";
+ break;
+
+ case SENSORS_BUS_TYPE_ISA:
+ type = "isa";
+ break;
+
+ case SENSORS_BUS_TYPE_PCI:
+ type = "pci";
+ break;
+
+ case SENSORS_BUS_TYPE_SPI:
+ type = "spi";
+ break;
+
+ case SENSORS_BUS_TYPE_VIRTUAL:
+ type = "virtual";
+ break;
+
+ case SENSORS_BUS_TYPE_ACPI:
+ type = "acpi";
+ break;
+
+ case SENSORS_BUS_TYPE_HID:
+ type = "hid";
+ break;
+
+ default:
+ break;
+ }
+
+ if (type)
+ return PyUnicode_FromString(type);
+
+ Py_RETURN_NONE;
+}
+
+static const sensors_subfeature* Sensor_get_subfeature(SensorObject* sensor, sensors_subfeature_type type) {
+ const sensors_subfeature* subfeature;
+ int subfeature_num = 0;
+
+ while ((subfeature = sensors_get_all_subfeatures(sensor->chip, sensor->feature, &subfeature_num))) {
+ if (subfeature->type == type)
+ break;
+ }
+
+ return subfeature;
+}
+
+PyObject* Sensor_return_value(SensorObject* sensor, sensors_subfeature_type subfeature_type) {
+ double value;
+
+ const sensors_subfeature* subfeature = Sensor_get_subfeature(sensor, subfeature_type);
+ if (!subfeature) {
+ PyErr_Format(PyExc_AttributeError, "Could not find sensor of requested type");
+ return NULL;
+ }
+
+ // Fetch value from the sensor
+ int r = sensors_get_value(sensor->chip, subfeature->number, &value);
+ if (r < 0) {
+ PyErr_Format(PyExc_ValueError, "Error retrieving value from sensor: %s",
+ sensors_strerror(errno));
+ return NULL;
+ }
+
+ // Convert all temperature values from Celcius to Kelvon
+ if (sensor->feature->type == SENSORS_FEATURE_TEMP)
+ value += 273.15;
+
+ return PyFloat_FromDouble(value);
+}
+
+static PyObject* Sensor_no_value() {
+ PyErr_Format(PyExc_ValueError, "Value not supported for this sensor type");
+ return NULL;
+}
+
+PyObject* Sensor_get_value(SensorObject* self) {
+ sensors_subfeature_type subfeature_type;
+
+ switch (self->feature->type) {
+ case SENSORS_FEATURE_IN:
+ subfeature_type = SENSORS_SUBFEATURE_IN_INPUT;
+ break;
+
+ case SENSORS_FEATURE_FAN:
+ subfeature_type = SENSORS_SUBFEATURE_FAN_INPUT;
+ break;
+
+ case SENSORS_FEATURE_TEMP:
+ subfeature_type = SENSORS_SUBFEATURE_TEMP_INPUT;
+ break;
+
+ case SENSORS_FEATURE_POWER:
+ subfeature_type = SENSORS_SUBFEATURE_POWER_INPUT;
+ break;
+
+ default:
+ return Sensor_no_value();
+ }
+
+ return Sensor_return_value(self, subfeature_type);
+}
+
+PyObject* Sensor_get_critical(SensorObject* self) {
+ sensors_subfeature_type subfeature_type;
+
+ switch (self->feature->type) {
+ case SENSORS_FEATURE_IN:
+ subfeature_type = SENSORS_SUBFEATURE_IN_CRIT;
+ break;
+
+ case SENSORS_FEATURE_TEMP:
+ subfeature_type = SENSORS_SUBFEATURE_TEMP_CRIT;
+ break;
+
+ case SENSORS_FEATURE_POWER:
+ subfeature_type = SENSORS_SUBFEATURE_POWER_CRIT;
+ break;
+
+ default:
+ return Sensor_no_value();
+ }
+
+ return Sensor_return_value(self, subfeature_type);
+}
+
+PyObject* Sensor_get_maximum(SensorObject* self) {
+ sensors_subfeature_type subfeature_type;
+
+ switch (self->feature->type) {
+ case SENSORS_FEATURE_IN:
+ subfeature_type = SENSORS_SUBFEATURE_IN_MAX;
+ break;
+
+ case SENSORS_FEATURE_FAN:
+ subfeature_type = SENSORS_SUBFEATURE_FAN_MAX;
+ break;
+
+ case SENSORS_FEATURE_TEMP:
+ subfeature_type = SENSORS_SUBFEATURE_TEMP_MAX;
+ break;
+
+ case SENSORS_FEATURE_POWER:
+ subfeature_type = SENSORS_SUBFEATURE_POWER_MAX;
+ break;
+
+ default:
+ return Sensor_no_value();
+ }
+
+ return Sensor_return_value(self, subfeature_type);
+}
+
+PyObject* Sensor_get_minimum(SensorObject* self) {
+ sensors_subfeature_type subfeature_type;
+
+ switch (self->feature->type) {
+ case SENSORS_FEATURE_IN:
+ subfeature_type = SENSORS_SUBFEATURE_IN_MIN;
+ break;
+
+ case SENSORS_FEATURE_FAN:
+ subfeature_type = SENSORS_SUBFEATURE_FAN_MIN;
+ break;
+
+ case SENSORS_FEATURE_TEMP:
+ subfeature_type = SENSORS_SUBFEATURE_TEMP_MIN;
+ break;
+
+ default:
+ return Sensor_no_value();
+ }
+
+ return Sensor_return_value(self, subfeature_type);
+}
+
+PyObject* Sensor_get_high(SensorObject* self) {
+ sensors_subfeature_type subfeature_type;
+
+ switch (self->feature->type) {
+ case SENSORS_FEATURE_TEMP:
+ subfeature_type = SENSORS_SUBFEATURE_TEMP_MAX;
+ break;
+
+ default:
+ return Sensor_no_value();
+ }
+
+ return Sensor_return_value(self, subfeature_type);
+}
+
+static SensorObject* make_sensor_object(const sensors_chip_name* chip, const sensors_feature* feature) {
+ SensorObject* sensor = PyObject_New(SensorObject, &SensorType);
+ if (!sensor)
+ return NULL;
+
+ if (!PyObject_Init((PyObject*)sensor, &SensorType)) {
+ Py_DECREF(sensor);
+ return NULL;
+ }
+
+ sensor->chip = chip;
+ sensor->feature = feature;
+
+ return sensor;
+}
+
+PyObject* _collecty_sensors_init() {
+ // Clean up everything first in case sensors_init was called earlier
+ sensors_cleanup();
+
+ int r = sensors_init(NULL);
+ if (r) {
+ PyErr_Format(PyExc_OSError, "Could not initialise sensors: %s",
+ sensors_strerror(errno));
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject* _collecty_sensors_cleanup() {
+ sensors_cleanup();
+ Py_RETURN_NONE;
+}
+
+PyObject* _collecty_get_detected_sensors(PyObject* o, PyObject* args) {
+ const char* name = NULL;
+ sensors_chip_name chip_name;
+
+ if (!PyArg_ParseTuple(args, "|z", &name))
+ return NULL;
+
+ if (name) {
+ int r = sensors_parse_chip_name(name, &chip_name);
+ if (r < 0) {
+ PyErr_Format(PyExc_ValueError, "Could not parse chip name: %s", name);
+ return NULL;
+ }
+ }
+
+ PyObject* list = PyList_New(0);
+
+ const sensors_chip_name* chip;
+ int chip_num = 0;
+
+ while ((chip = sensors_get_detected_chips((name) ? &chip_name : NULL, &chip_num))) {
+ const sensors_feature* feature;
+ int feature_num = 0;
+
+ while ((feature = sensors_get_features(chip, &feature_num))) {
+ // Skip sensors we do not want to support
+ switch (feature->type) {
+ case SENSORS_FEATURE_IN:
+ case SENSORS_FEATURE_FAN:
+ case SENSORS_FEATURE_TEMP:
+ case SENSORS_FEATURE_POWER:
+ break;
+
+ default:
+ continue;
+ }
+
+ SensorObject* sensor = make_sensor_object(chip, feature);
+ PyList_Append(list, (PyObject*)sensor);
+ }
+ }
+
+ return list;
+}
--- /dev/null
+/*
+ * collecty
+ * Copyright (C) 2015 IPFire Team (www.ipfire.org)
+ *
+ * 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 3 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 <mntent.h>
+
+#include "_collectymodule.h"
+
+int _collecty_mountpoint_is_virtual(const struct mntent* mp) {
+ // Ignore all ramdisks
+ if (mp->mnt_fsname[0] != '/')
+ return 1;
+
+ // Ignore network mounts
+ if (hasmntopt(mp, "_netdev") != NULL)
+ return 1;
+
+ return 0;
+}
+
+PyObject* _collecty_get_mountpoints() {
+ FILE* fp = setmntent(_PATH_MOUNTED, "r");
+ if (!fp)
+ return NULL;
+
+ PyObject* list = PyList_New(0);
+ int r = 0;
+
+ struct mntent* mountpoint = getmntent(fp);
+ while (mountpoint) {
+ if (!_collecty_mountpoint_is_virtual(mountpoint)) {
+ // Create a tuple with the information of the mountpoint
+ PyObject* mp = PyTuple_New(4);
+ PyTuple_SET_ITEM(mp, 0, PyUnicode_FromString(mountpoint->mnt_fsname));
+ PyTuple_SET_ITEM(mp, 1, PyUnicode_FromString(mountpoint->mnt_dir));
+ PyTuple_SET_ITEM(mp, 2, PyUnicode_FromString(mountpoint->mnt_type));
+ PyTuple_SET_ITEM(mp, 3, PyUnicode_FromString(mountpoint->mnt_opts));
+
+ // Append the tuple to the list
+ r = PyList_Append(list, mp);
+ if (r)
+ break;
+ }
+
+ // Move on to the next mountpoint
+ mountpoint = getmntent(fp);
+ }
+
+ endmntent(fp);
+
+ if (r) {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ return list;
+}
+++ /dev/null
-/*
- * collecty
- * Copyright (C) 2015 IPFire Team (www.ipfire.org)
- *
- * 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 3 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 <atasmart.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/hdreg.h>
-#include <mntent.h>
-#include <oping.h>
-#include <sensors/error.h>
-#include <sensors/sensors.h>
-#include <stdbool.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <time.h>
-
-#define MODEL_SIZE 40
-#define SERIAL_SIZE 20
-
-#define PING_HISTORY_SIZE 1024
-#define PING_DEFAULT_COUNT 10
-#define PING_DEFAULT_TIMEOUT 8
-
-typedef struct {
- PyObject_HEAD
- char* path;
- struct hd_driveid identity;
- SkDisk* disk;
-} BlockDevice;
-
-static void BlockDevice_dealloc(BlockDevice* self) {
- if (self->disk)
- sk_disk_free(self->disk);
-
- if (self->path)
- free(self->path);
-
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static int BlockDevice_get_identity(BlockDevice* device) {
- int fd;
-
- if ((fd = open(device->path, O_RDONLY | O_NONBLOCK)) < 0) {
- return 1;
- }
-
- int r = ioctl(fd, HDIO_GET_IDENTITY, &device->identity);
- close(fd);
-
- if (r)
- return 1;
-
- return 0;
-}
-
-static int BlockDevice_smart_is_available(BlockDevice* device) {
- SkBool available = FALSE;
-
- int r = sk_disk_smart_is_available(device->disk, &available);
- if (r)
- return -1;
-
- if (available)
- return 0;
-
- return 1;
-}
-
-static int BlockDevice_check_sleep_mode(BlockDevice* device) {
- SkBool awake = FALSE;
-
- int r = sk_disk_check_sleep_mode(device->disk, &awake);
- if (r)
- return -1;
-
- if (awake)
- return 0;
-
- return 1;
-}
-
-static PyObject * BlockDevice_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
- BlockDevice* self = (BlockDevice*)type->tp_alloc(type, 0);
-
- if (self) {
- self->path = NULL;
-
- // libatasmart
- self->disk = NULL;
- }
-
- return (PyObject *)self;
-}
-
-static int BlockDevice_init(BlockDevice* self, PyObject* args, PyObject* kwds) {
- const char* path = NULL;
-
- if (!PyArg_ParseTuple(args, "s", &path))
- return -1;
-
- self->path = strdup(path);
-
- int r = BlockDevice_get_identity(self);
- if (r) {
- PyErr_Format(PyExc_OSError, "Could not open block device: %s", path);
- return -1;
- }
-
- r = sk_disk_open(path, &self->disk);
- if (r == 0) {
- if (BlockDevice_smart_is_available(self) == 0) {
- if (BlockDevice_check_sleep_mode(self) == 0) {
- r = sk_disk_smart_read_data(self->disk);
- if (r) {
- PyErr_Format(PyExc_OSError, "Could not open block device %s: %s", path,
- strerror(errno));
- return -1;
- }
- }
- }
- } else {
- PyErr_Format(PyExc_OSError, "Could not open block device %s: %s", path,
- strerror(errno));
- return -1;
- }
-
- //sk_disk_identify_is_available
-
- return 0;
-}
-
-static PyObject* BlockDevice_get_path(PyObject* self) {
- BlockDevice* device = (BlockDevice*)self;
-
- return PyUnicode_FromString(device->path);
-}
-
-static void clean_string(char *s) {
- for (char* e = s; *e; e++) {
- if (*e < ' ' || *e >= 127)
- *e = ' ';
- }
-}
-
-static void drop_spaces(char *s) {
- char *d = s;
- bool prev_space = false;
-
- s += strspn(s, " ");
-
- for (; *s; s++) {
- if (prev_space) {
- if (*s != ' ') {
- prev_space = false;
- *(d++) = ' ';
- *(d++) = *s;
- }
- } else {
- if (*s == ' ')
- prev_space = true;
- else
- *(d++) = *s;
- }
- }
-
- *d = 0;
-}
-
-static void copy_string(char* d, const char* s, size_t n) {
- // Copy the source buffer to the destination buffer up to n
- memcpy(d, s, n);
-
- // Terminate the destination buffer with NULL
- d[n] = '\0';
-
- // Clean up the string from non-printable characters
- clean_string(d);
- drop_spaces(d);
-}
-
-static PyObject* BlockDevice_get_model(PyObject* self) {
- BlockDevice* device = (BlockDevice*)self;
-
- char model[MODEL_SIZE + 1];
- copy_string(model, device->identity.model, sizeof(model));
-
- return PyUnicode_FromString(model);
-}
-
-static PyObject* BlockDevice_get_serial(PyObject* self) {
- BlockDevice* device = (BlockDevice*)self;
-
- char serial[SERIAL_SIZE + 1];
- copy_string(serial, device->identity.serial_no, sizeof(serial));
-
- return PyUnicode_FromString(serial);
-}
-
-static PyObject* BlockDevice_is_smart_supported(PyObject* self) {
- BlockDevice* device = (BlockDevice*)self;
-
- if (BlockDevice_smart_is_available(device) == 0)
- Py_RETURN_TRUE;
-
- Py_RETURN_FALSE;
-}
-
-static PyObject* BlockDevice_is_awake(PyObject* self) {
- BlockDevice* device = (BlockDevice*)self;
-
- if (BlockDevice_check_sleep_mode(device) == 0)
- Py_RETURN_TRUE;
-
- Py_RETURN_FALSE;
-}
-
-static PyObject* BlockDevice_get_bad_sectors(PyObject* self) {
- BlockDevice* device = (BlockDevice*)self;
-
- if (BlockDevice_smart_is_available(device)) {
- PyErr_Format(PyExc_OSError, "Device does not support SMART");
- return NULL;
- }
-
- uint64_t bad_sectors;
- int r = sk_disk_smart_get_bad(device->disk, &bad_sectors);
- if (r)
- return NULL;
-
- return PyLong_FromUnsignedLongLong((unsigned long long)bad_sectors);
-}
-
-static PyObject* BlockDevice_get_temperature(PyObject* self) {
- BlockDevice* device = (BlockDevice*)self;
-
- if (BlockDevice_smart_is_available(device)) {
- PyErr_Format(PyExc_OSError, "Device does not support SMART");
- return NULL;
- }
-
- uint64_t mkelvin;
- int r = sk_disk_smart_get_temperature(device->disk, &mkelvin);
- if (r)
- return NULL;
-
- // Convert the temperature to Kelvin
- return PyFloat_FromDouble((double)mkelvin / 1000.0);
-}
-
-static PyGetSetDef BlockDevice_getsetters[] = {
- {"path", (getter)BlockDevice_get_path, NULL, NULL, NULL},
- {"model", (getter)BlockDevice_get_model, NULL, NULL, NULL},
- {"serial", (getter)BlockDevice_get_serial, NULL, NULL, NULL},
-};
-
-static PyMethodDef BlockDevice_methods[] = {
- {"get_bad_sectors", (PyCFunction)BlockDevice_get_bad_sectors, METH_NOARGS, NULL},
- {"get_temperature", (PyCFunction)BlockDevice_get_temperature, METH_NOARGS, NULL},
- {"is_smart_supported", (PyCFunction)BlockDevice_is_smart_supported, METH_NOARGS, NULL},
- {"is_awake", (PyCFunction)BlockDevice_is_awake, METH_NOARGS, NULL},
- {NULL}
-};
-
-static PyTypeObject BlockDeviceType = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "_collecty.BlockDevice", /*tp_name*/
- sizeof(BlockDevice), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)BlockDevice_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
- "BlockDevice objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- BlockDevice_methods, /* tp_methods */
- 0, /* tp_members */
- BlockDevice_getsetters, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)BlockDevice_init, /* tp_init */
- 0, /* tp_alloc */
- BlockDevice_new, /* tp_new */
-};
-
-static PyObject* PyExc_PingError;
-static PyObject* PyExc_PingAddHostError;
-
-typedef struct {
- PyObject_HEAD
- pingobj_t* ping;
- const char* host;
- struct {
- double history[PING_HISTORY_SIZE];
- size_t history_index;
- size_t history_size;
- size_t packets_sent;
- size_t packets_rcvd;
- double average;
- double stddev;
- double loss;
- } stats;
-} PingObject;
-
-static void Ping_dealloc(PingObject* self) {
- if (self->ping)
- ping_destroy(self->ping);
-
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static void Ping_init_stats(PingObject* self) {
- self->stats.history_index = 0;
- self->stats.history_size = 0;
- self->stats.packets_sent = 0;
- self->stats.packets_rcvd = 0;
-
- self->stats.average = 0.0;
- self->stats.stddev = 0.0;
- self->stats.loss = 0.0;
-}
-
-static PyObject* Ping_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
- PingObject* self = (PingObject*)type->tp_alloc(type, 0);
-
- if (self) {
- self->ping = NULL;
- self->host = NULL;
-
- Ping_init_stats(self);
- }
-
- return (PyObject*)self;
-}
-
-static int Ping_init(PingObject* self, PyObject* args, PyObject* kwds) {
- char* kwlist[] = {"host", "family", "timeout", "ttl", NULL};
- int family = PING_DEF_AF;
- double timeout = PING_DEFAULT_TIMEOUT;
- int ttl = PING_DEF_TTL;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|idi", kwlist, &self->host,
- &family, &timeout, &ttl))
- return -1;
-
- if (family != AF_UNSPEC && family != AF_INET6 && family != AF_INET) {
- PyErr_Format(PyExc_ValueError, "Family must be AF_UNSPEC, AF_INET6, or AF_INET");
- return -1;
- }
-
- if (timeout < 0) {
- PyErr_Format(PyExc_ValueError, "Timeout must be greater than zero");
- return -1;
- }
-
- if (ttl < 1 || ttl > 255) {
- PyErr_Format(PyExc_ValueError, "TTL must be between 1 and 255");
- return -1;
- }
-
- self->ping = ping_construct();
- if (!self->ping) {
- return -1;
- }
-
- // Set options
- int r;
-
- r = ping_setopt(self->ping, PING_OPT_AF, &family);
- if (r) {
- PyErr_Format(PyExc_RuntimeError, "Could not set address family: %s",
- ping_get_error(self->ping));
- return -1;
- }
-
- if (timeout > 0) {
- r = ping_setopt(self->ping, PING_OPT_TIMEOUT, &timeout);
-
- if (r) {
- PyErr_Format(PyExc_RuntimeError, "Could not set timeout: %s",
- ping_get_error(self->ping));
- return -1;
- }
- }
-
- r = ping_setopt(self->ping, PING_OPT_TTL, &ttl);
- if (r) {
- PyErr_Format(PyExc_RuntimeError, "Could not set TTL: %s",
- ping_get_error(self->ping));
- return -1;
- }
-
- return 0;
-}
-
-static double Ping_compute_average(PingObject* self) {
- assert(self->stats.packets_rcvd > 0);
-
- double total_latency = 0.0;
-
- for (int i = 0; i < self->stats.history_size; i++) {
- if (self->stats.history[i] > 0)
- total_latency += self->stats.history[i];
- }
-
- return total_latency / self->stats.packets_rcvd;
-}
-
-static double Ping_compute_stddev(PingObject* self, double mean) {
- assert(self->stats.packets_rcvd > 0);
-
- double deviation = 0.0;
-
- for (int i = 0; i < self->stats.history_size; i++) {
- if (self->stats.history[i] > 0) {
- deviation += pow(self->stats.history[i] - mean, 2);
- }
- }
-
- // Normalise
- deviation /= self->stats.packets_rcvd;
-
- return sqrt(deviation);
-}
-
-static void Ping_compute_stats(PingObject* self) {
- // Compute the average latency
- self->stats.average = Ping_compute_average(self);
-
- // Compute the standard deviation
- self->stats.stddev = Ping_compute_stddev(self, self->stats.average);
-
- // Compute lost packets
- self->stats.loss = 1.0;
- self->stats.loss -= (double)self->stats.packets_rcvd \
- / (double)self->stats.packets_sent;
-}
-
-static double time_elapsed(struct timeval* t0) {
- struct timeval now;
- gettimeofday(&now, NULL);
-
- double r = now.tv_sec - t0->tv_sec;
- r += ((double)now.tv_usec / 1000000) - ((double)t0->tv_usec / 1000000);
-
- return r;
-}
-
-static PyObject* Ping_ping(PingObject* self, PyObject* args, PyObject* kwds) {
- char* kwlist[] = {"count", "deadline", NULL};
- size_t count = PING_DEFAULT_COUNT;
- double deadline = 0;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Id", kwlist, &count, &deadline))
- return NULL;
-
- int r = ping_host_add(self->ping, self->host);
- if (r) {
- PyErr_Format(PyExc_PingAddHostError, "Could not add host %s: %s",
- self->host, ping_get_error(self->ping));
- return NULL;
- }
-
- // Reset all collected statistics in case ping() is called more than once.
- Ping_init_stats(self);
-
- // Save start time
- struct timeval time_start;
- r = gettimeofday(&time_start, NULL);
- if (r) {
- PyErr_Format(PyExc_RuntimeError, "Could not determine start time");
- return NULL;
- }
-
- // Do the pinging
- while (count--) {
- self->stats.packets_sent++;
-
- Py_BEGIN_ALLOW_THREADS
- r = ping_send(self->ping);
- Py_END_ALLOW_THREADS
-
- // Count recieved packets
- if (r >= 0) {
- self->stats.packets_rcvd += r;
-
- // Raise any errors
- } else {
- PyErr_Format(PyExc_RuntimeError, "Error executing ping_send(): %s",
- ping_get_error(self->ping));
- return NULL;
- }
-
- // Extract all data
- pingobj_iter_t* iter = ping_iterator_get(self->ping);
-
- double* latency = &self->stats.history[self->stats.history_index];
- size_t buffer_size = sizeof(latency);
- ping_iterator_get_info(iter, PING_INFO_LATENCY, latency, &buffer_size);
-
- // Increase the history pointer
- self->stats.history_index++;
- self->stats.history_index %= sizeof(self->stats.history);
-
- // Increase the history size
- if (self->stats.history_size < sizeof(self->stats.history))
- self->stats.history_size++;
-
- // Check if the deadline is due
- if (deadline > 0) {
- double elapsed_time = time_elapsed(&time_start);
-
- // If we have run longer than the deadline is, we end the main loop
- if (elapsed_time >= deadline)
- break;
- }
- }
-
- if (self->stats.packets_rcvd == 0) {
- PyErr_Format(PyExc_PingError, "No replies received from %s", self->host);
- return NULL;
- }
-
- Ping_compute_stats(self);
-
- Py_RETURN_NONE;
-}
-
-static PyObject* Ping_get_packets_sent(PingObject* self) {
- return PyLong_FromUnsignedLong(self->stats.packets_sent);
-}
-
-static PyObject* Ping_get_packets_rcvd(PingObject* self) {
- return PyLong_FromUnsignedLong(self->stats.packets_rcvd);
-}
-
-static PyObject* Ping_get_average(PingObject* self) {
- return PyFloat_FromDouble(self->stats.average);
-}
-
-static PyObject* Ping_get_stddev(PingObject* self) {
- return PyFloat_FromDouble(self->stats.stddev);
-}
-
-static PyObject* Ping_get_loss(PingObject* self) {
- return PyFloat_FromDouble(self->stats.loss);
-}
-
-static PyGetSetDef Ping_getsetters[] = {
- {"average", (getter)Ping_get_average, NULL, NULL, NULL},
- {"loss", (getter)Ping_get_loss, NULL, NULL, NULL},
- {"stddev", (getter)Ping_get_stddev, NULL, NULL, NULL},
- {"packets_sent", (getter)Ping_get_packets_sent, NULL, NULL, NULL},
- {"packets_rcvd", (getter)Ping_get_packets_rcvd, NULL, NULL, NULL},
- {NULL}
-};
-
-static PyMethodDef Ping_methods[] = {
- {"ping", (PyCFunction)Ping_ping, METH_VARARGS|METH_KEYWORDS, NULL},
- {NULL}
-};
-
-static PyTypeObject PingType = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "_collecty.Ping", /*tp_name*/
- sizeof(PingObject), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)Ping_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
- "Ping object", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- Ping_methods, /* tp_methods */
- 0, /* tp_members */
- Ping_getsetters, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)Ping_init, /* tp_init */
- 0, /* tp_alloc */
- Ping_new, /* tp_new */
-};
-
-typedef struct {
- PyObject_HEAD
- const sensors_chip_name* chip;
- const sensors_feature* feature;
-} SensorObject;
-
-static void Sensor_dealloc(SensorObject* self) {
- Py_TYPE(self)->tp_free((PyObject*)self);
-}
-
-static PyObject* Sensor_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
- SensorObject* self = (SensorObject*)type->tp_alloc(type, 0);
-
- return (PyObject *)self;
-}
-
-static int Sensor_init(SensorObject* self, PyObject* args, PyObject* kwds) {
- return 0;
-}
-
-static PyObject* Sensor_get_label(SensorObject* self) {
- char* label = sensors_get_label(self->chip, self->feature);
-
- if (label) {
- PyObject* string = PyUnicode_FromString(label);
- free(label);
-
- return string;
- }
-
- Py_RETURN_NONE;
-}
-
-static PyObject* Sensor_get_name(SensorObject* self) {
- char chip_name[512];
-
- int r = sensors_snprintf_chip_name(chip_name, sizeof(chip_name), self->chip);
- if (r < 0) {
- PyErr_Format(PyExc_RuntimeError, "Could not print chip name");
- return NULL;
- }
-
- return PyUnicode_FromString(chip_name);
-}
-
-static PyObject* Sensor_get_type(SensorObject* self) {
- const char* type = NULL;
-
- switch (self->feature->type) {
- case SENSORS_FEATURE_IN:
- type = "voltage";
- break;
-
- case SENSORS_FEATURE_FAN:
- type = "fan";
- break;
-
- case SENSORS_FEATURE_TEMP:
- type = "temperature";
- break;
-
- case SENSORS_FEATURE_POWER:
- type = "power";
- break;
-
- default:
- break;
- }
-
- if (type)
- return PyUnicode_FromString(type);
-
- Py_RETURN_NONE;
-}
-
-static PyObject* Sensor_get_bus(SensorObject* self) {
- const char* type = NULL;
-
- switch (self->chip->bus.type) {
- case SENSORS_BUS_TYPE_I2C:
- type = "i2c";
- break;
-
- case SENSORS_BUS_TYPE_ISA:
- type = "isa";
- break;
-
- case SENSORS_BUS_TYPE_PCI:
- type = "pci";
- break;
-
- case SENSORS_BUS_TYPE_SPI:
- type = "spi";
- break;
-
- case SENSORS_BUS_TYPE_VIRTUAL:
- type = "virtual";
- break;
-
- case SENSORS_BUS_TYPE_ACPI:
- type = "acpi";
- break;
-
- case SENSORS_BUS_TYPE_HID:
- type = "hid";
- break;
-
- default:
- break;
- }
-
- if (type)
- return PyUnicode_FromString(type);
-
- Py_RETURN_NONE;
-}
-
-static const sensors_subfeature* Sensor_get_subfeature(SensorObject* sensor, sensors_subfeature_type type) {
- const sensors_subfeature* subfeature;
- int subfeature_num = 0;
-
- while ((subfeature = sensors_get_all_subfeatures(sensor->chip, sensor->feature, &subfeature_num))) {
- if (subfeature->type == type)
- break;
- }
-
- return subfeature;
-}
-
-static PyObject* Sensor_return_value(SensorObject* sensor, sensors_subfeature_type subfeature_type) {
- double value;
-
- const sensors_subfeature* subfeature = Sensor_get_subfeature(sensor, subfeature_type);
- if (!subfeature) {
- PyErr_Format(PyExc_AttributeError, "Could not find sensor of requested type");
- return NULL;
- }
-
- // Fetch value from the sensor
- int r = sensors_get_value(sensor->chip, subfeature->number, &value);
- if (r < 0) {
- PyErr_Format(PyExc_ValueError, "Error retrieving value from sensor: %s",
- sensors_strerror(errno));
- return NULL;
- }
-
- // Convert all temperature values from Celcius to Kelvon
- if (sensor->feature->type == SENSORS_FEATURE_TEMP)
- value += 273.15;
-
- return PyFloat_FromDouble(value);
-}
-
-static PyObject* Sensor_no_value() {
- PyErr_Format(PyExc_ValueError, "Value not supported for this sensor type");
- return NULL;
-}
-
-static PyObject* Sensor_get_value(SensorObject* self) {
- sensors_subfeature_type subfeature_type;
-
- switch (self->feature->type) {
- case SENSORS_FEATURE_IN:
- subfeature_type = SENSORS_SUBFEATURE_IN_INPUT;
- break;
-
- case SENSORS_FEATURE_FAN:
- subfeature_type = SENSORS_SUBFEATURE_FAN_INPUT;
- break;
-
- case SENSORS_FEATURE_TEMP:
- subfeature_type = SENSORS_SUBFEATURE_TEMP_INPUT;
- break;
-
- case SENSORS_FEATURE_POWER:
- subfeature_type = SENSORS_SUBFEATURE_POWER_INPUT;
- break;
-
- default:
- return Sensor_no_value();
- }
-
- return Sensor_return_value(self, subfeature_type);
-}
-
-static PyObject* Sensor_get_critical(SensorObject* self) {
- sensors_subfeature_type subfeature_type;
-
- switch (self->feature->type) {
- case SENSORS_FEATURE_IN:
- subfeature_type = SENSORS_SUBFEATURE_IN_CRIT;
- break;
-
- case SENSORS_FEATURE_TEMP:
- subfeature_type = SENSORS_SUBFEATURE_TEMP_CRIT;
- break;
-
- case SENSORS_FEATURE_POWER:
- subfeature_type = SENSORS_SUBFEATURE_POWER_CRIT;
- break;
-
- default:
- return Sensor_no_value();
- }
-
- return Sensor_return_value(self, subfeature_type);
-}
-
-static PyObject* Sensor_get_maximum(SensorObject* self) {
- sensors_subfeature_type subfeature_type;
-
- switch (self->feature->type) {
- case SENSORS_FEATURE_IN:
- subfeature_type = SENSORS_SUBFEATURE_IN_MAX;
- break;
-
- case SENSORS_FEATURE_FAN:
- subfeature_type = SENSORS_SUBFEATURE_FAN_MAX;
- break;
-
- case SENSORS_FEATURE_TEMP:
- subfeature_type = SENSORS_SUBFEATURE_TEMP_MAX;
- break;
-
- case SENSORS_FEATURE_POWER:
- subfeature_type = SENSORS_SUBFEATURE_POWER_MAX;
- break;
-
- default:
- return Sensor_no_value();
- }
-
- return Sensor_return_value(self, subfeature_type);
-}
-
-static PyObject* Sensor_get_minimum(SensorObject* self) {
- sensors_subfeature_type subfeature_type;
-
- switch (self->feature->type) {
- case SENSORS_FEATURE_IN:
- subfeature_type = SENSORS_SUBFEATURE_IN_MIN;
- break;
-
- case SENSORS_FEATURE_FAN:
- subfeature_type = SENSORS_SUBFEATURE_FAN_MIN;
- break;
-
- case SENSORS_FEATURE_TEMP:
- subfeature_type = SENSORS_SUBFEATURE_TEMP_MIN;
- break;
-
- default:
- return Sensor_no_value();
- }
-
- return Sensor_return_value(self, subfeature_type);
-}
-
-static PyObject* Sensor_get_high(SensorObject* self) {
- sensors_subfeature_type subfeature_type;
-
- switch (self->feature->type) {
- case SENSORS_FEATURE_TEMP:
- subfeature_type = SENSORS_SUBFEATURE_TEMP_MAX;
- break;
-
- default:
- return Sensor_no_value();
- }
-
- return Sensor_return_value(self, subfeature_type);
-}
-
-static PyGetSetDef Sensor_getsetters[] = {
- {"bus", (getter)Sensor_get_bus, NULL, NULL, NULL},
- {"critical", (getter)Sensor_get_critical, NULL, NULL, NULL},
- {"high", (getter)Sensor_get_high, NULL, NULL, NULL},
- {"label", (getter)Sensor_get_label, NULL, NULL, NULL},
- {"maximum", (getter)Sensor_get_maximum, NULL, NULL, NULL},
- {"minumum", (getter)Sensor_get_minimum, NULL, NULL, NULL},
- {"name", (getter)Sensor_get_name, NULL, NULL, NULL},
- {"type", (getter)Sensor_get_type, NULL, NULL, NULL},
- {"value", (getter)Sensor_get_value, NULL, NULL, NULL},
- {NULL},
-};
-
-static PyTypeObject SensorType = {
- PyObject_HEAD_INIT(NULL)
- "_collecty.Sensor", /*tp_name*/
- sizeof(SensorObject), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- (destructor)Sensor_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
- "Sensor objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- 0, /* tp_members */
- Sensor_getsetters, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc)Sensor_init, /* tp_init */
- 0, /* tp_alloc */
- Sensor_new, /* tp_new */
-};
-
-static SensorObject* make_sensor_object(const sensors_chip_name* chip, const sensors_feature* feature) {
- SensorObject* sensor = PyObject_New(SensorObject, &SensorType);
- if (!sensor)
- return NULL;
-
- if (!PyObject_Init((PyObject*)sensor, &SensorType)) {
- Py_DECREF(sensor);
- return NULL;
- }
-
- sensor->chip = chip;
- sensor->feature = feature;
-
- return sensor;
-}
-
-static PyObject* _collecty_sensors_init() {
- // Clean up everything first in case sensors_init was called earlier
- sensors_cleanup();
-
- int r = sensors_init(NULL);
- if (r) {
- PyErr_Format(PyExc_OSError, "Could not initialise sensors: %s",
- sensors_strerror(errno));
- return NULL;
- }
-
- Py_RETURN_NONE;
-}
-
-static PyObject* _collecty_sensors_cleanup() {
- sensors_cleanup();
- Py_RETURN_NONE;
-}
-
-static PyObject* _collecty_get_detected_sensors(PyObject* o, PyObject* args) {
- const char* name = NULL;
- sensors_chip_name chip_name;
-
- if (!PyArg_ParseTuple(args, "|z", &name))
- return NULL;
-
- if (name) {
- int r = sensors_parse_chip_name(name, &chip_name);
- if (r < 0) {
- PyErr_Format(PyExc_ValueError, "Could not parse chip name: %s", name);
- return NULL;
- }
- }
-
- PyObject* list = PyList_New(0);
-
- const sensors_chip_name* chip;
- int chip_num = 0;
-
- while ((chip = sensors_get_detected_chips((name) ? &chip_name : NULL, &chip_num))) {
- const sensors_feature* feature;
- int feature_num = 0;
-
- while ((feature = sensors_get_features(chip, &feature_num))) {
- // Skip sensors we do not want to support
- switch (feature->type) {
- case SENSORS_FEATURE_IN:
- case SENSORS_FEATURE_FAN:
- case SENSORS_FEATURE_TEMP:
- case SENSORS_FEATURE_POWER:
- break;
-
- default:
- continue;
- }
-
- SensorObject* sensor = make_sensor_object(chip, feature);
- PyList_Append(list, (PyObject*)sensor);
- }
- }
-
- return list;
-}
-
-static int _collecty_mountpoint_is_virtual(const struct mntent* mp) {
- // Ignore all ramdisks
- if (mp->mnt_fsname[0] != '/')
- return 1;
-
- // Ignore network mounts
- if (hasmntopt(mp, "_netdev") != NULL)
- return 1;
-
- return 0;
-}
-
-static PyObject* _collecty_get_mountpoints() {
- FILE* fp = setmntent(_PATH_MOUNTED, "r");
- if (!fp)
- return NULL;
-
- PyObject* list = PyList_New(0);
- int r = 0;
-
- struct mntent* mountpoint = getmntent(fp);
- while (mountpoint) {
- if (!_collecty_mountpoint_is_virtual(mountpoint)) {
- // Create a tuple with the information of the mountpoint
- PyObject* mp = PyTuple_New(4);
- PyTuple_SET_ITEM(mp, 0, PyUnicode_FromString(mountpoint->mnt_fsname));
- PyTuple_SET_ITEM(mp, 1, PyUnicode_FromString(mountpoint->mnt_dir));
- PyTuple_SET_ITEM(mp, 2, PyUnicode_FromString(mountpoint->mnt_type));
- PyTuple_SET_ITEM(mp, 3, PyUnicode_FromString(mountpoint->mnt_opts));
-
- // Append the tuple to the list
- r = PyList_Append(list, mp);
- if (r)
- break;
- }
-
- // Move on to the next mountpoint
- mountpoint = getmntent(fp);
- }
-
- endmntent(fp);
-
- if (r) {
- Py_DECREF(list);
- return NULL;
- }
-
- return list;
-}
-
-static PyMethodDef collecty_module_methods[] = {
- {"get_detected_sensors", (PyCFunction)_collecty_get_detected_sensors, METH_VARARGS, NULL},
- {"get_mountpoints", (PyCFunction)_collecty_get_mountpoints, METH_NOARGS, NULL},
- {"sensors_cleanup", (PyCFunction)_collecty_sensors_cleanup, METH_NOARGS, NULL},
- {"sensors_init", (PyCFunction)_collecty_sensors_init, METH_NOARGS, NULL},
- {NULL},
-};
-
-static struct PyModuleDef collecty_module = {
- PyModuleDef_HEAD_INIT,
- "_collecty", /* m_name */
- "_collecty module", /* m_doc */
- -1, /* m_size */
- collecty_module_methods, /* m_methods */
- NULL, /* m_reload */
- NULL, /* m_traverse */
- NULL, /* m_clear */
- NULL, /* m_free */
-};
-
-PyMODINIT_FUNC PyInit__collecty(void) {
- if (PyType_Ready(&BlockDeviceType) < 0)
- return NULL;
-
- if (PyType_Ready(&PingType) < 0)
- return NULL;
-
- if (PyType_Ready(&SensorType) < 0)
- return NULL;
-
- PyObject* m = PyModule_Create(&collecty_module);
-
- Py_INCREF(&BlockDeviceType);
- PyModule_AddObject(m, "BlockDevice", (PyObject*)&BlockDeviceType);
-
- Py_INCREF(&PingType);
- PyModule_AddObject(m, "Ping", (PyObject*)&PingType);
-
- PyExc_PingError = PyErr_NewException("_collecty.PingError", NULL, NULL);
- Py_INCREF(PyExc_PingError);
- PyModule_AddObject(m, "PingError", PyExc_PingError);
-
- PyExc_PingAddHostError = PyErr_NewException("_collecty.PingAddHostError", NULL, NULL);
- Py_INCREF(PyExc_PingAddHostError);
- PyModule_AddObject(m, "PingAddHostError", PyExc_PingAddHostError);
-
- Py_INCREF(&SensorType);
- PyModule_AddObject(m, "Sensor", (PyObject*)&SensorType);
-
- return m;
-}