3 * Copyright (C) 2015 IPFire Team (www.ipfire.org)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include <linux/hdreg.h>
25 #include <sensors/error.h>
26 #include <sensors/sensors.h>
29 #include <sys/ioctl.h>
32 #define SERIAL_SIZE 20
37 struct hd_driveid identity
;
41 static void BlockDevice_dealloc(BlockDevice
* self
) {
43 sk_disk_free(self
->disk
);
48 self
->ob_type
->tp_free((PyObject
*)self
);
51 static int BlockDevice_get_identity(BlockDevice
* device
) {
54 if ((fd
= open(device
->path
, O_RDONLY
| O_NONBLOCK
)) < 0) {
58 int r
= ioctl(fd
, HDIO_GET_IDENTITY
, &device
->identity
);
67 static int BlockDevice_smart_is_available(BlockDevice
* device
) {
68 SkBool available
= FALSE
;
70 int r
= sk_disk_smart_is_available(device
->disk
, &available
);
80 static int BlockDevice_check_sleep_mode(BlockDevice
* device
) {
83 int r
= sk_disk_check_sleep_mode(device
->disk
, &awake
);
93 static PyObject
* BlockDevice_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
94 BlockDevice
* self
= (BlockDevice
*)type
->tp_alloc(type
, 0);
103 return (PyObject
*)self
;
106 static int BlockDevice_init(BlockDevice
* self
, PyObject
* args
, PyObject
* kwds
) {
107 const char* path
= NULL
;
109 if (!PyArg_ParseTuple(args
, "s", &path
))
112 self
->path
= strdup(path
);
114 int r
= BlockDevice_get_identity(self
);
116 PyErr_Format(PyExc_OSError
, "Could not open block device: %s", path
);
120 r
= sk_disk_open(path
, &self
->disk
);
122 if (BlockDevice_smart_is_available(self
) == 0) {
123 if (BlockDevice_check_sleep_mode(self
) == 0) {
124 r
= sk_disk_smart_read_data(self
->disk
);
126 PyErr_Format(PyExc_OSError
, "Could not open block device %s: %s", path
,
133 PyErr_Format(PyExc_OSError
, "Could not open block device %s: %s", path
,
138 //sk_disk_identify_is_available
143 static PyObject
* BlockDevice_get_path(PyObject
* self
) {
144 BlockDevice
* device
= (BlockDevice
*)self
;
146 return PyString_FromString(device
->path
);
149 static void clean_string(char *s
) {
150 for (char* e
= s
; *e
; e
++) {
151 if (*e
< ' ' || *e
>= 127)
156 static void drop_spaces(char *s
) {
158 bool prev_space
= false;
180 static void copy_string(char* d
, const char* s
, size_t n
) {
181 // Copy the source buffer to the destination buffer up to n
184 // Terminate the destination buffer with NULL
187 // Clean up the string from non-printable characters
192 static PyObject
* BlockDevice_get_model(PyObject
* self
) {
193 BlockDevice
* device
= (BlockDevice
*)self
;
195 char model
[MODEL_SIZE
+ 1];
196 copy_string(model
, device
->identity
.model
, sizeof(model
));
198 return PyString_FromString(model
);
201 static PyObject
* BlockDevice_get_serial(PyObject
* self
) {
202 BlockDevice
* device
= (BlockDevice
*)self
;
204 char serial
[SERIAL_SIZE
+ 1];
205 copy_string(serial
, device
->identity
.serial_no
, sizeof(serial
));
207 return PyString_FromString(serial
);
210 static PyObject
* BlockDevice_is_smart_supported(PyObject
* self
) {
211 BlockDevice
* device
= (BlockDevice
*)self
;
213 if (BlockDevice_smart_is_available(device
) == 0)
219 static PyObject
* BlockDevice_is_awake(PyObject
* self
) {
220 BlockDevice
* device
= (BlockDevice
*)self
;
222 if (BlockDevice_check_sleep_mode(device
) == 0)
228 static PyObject
* BlockDevice_get_bad_sectors(PyObject
* self
) {
229 BlockDevice
* device
= (BlockDevice
*)self
;
231 if (BlockDevice_smart_is_available(device
)) {
232 PyErr_Format(PyExc_OSError
, "Device does not support SMART");
236 uint64_t bad_sectors
;
237 int r
= sk_disk_smart_get_bad(device
->disk
, &bad_sectors
);
241 return PyLong_FromUnsignedLongLong((unsigned long long)bad_sectors
);
244 static PyObject
* BlockDevice_get_temperature(PyObject
* self
) {
245 BlockDevice
* device
= (BlockDevice
*)self
;
247 if (BlockDevice_smart_is_available(device
)) {
248 PyErr_Format(PyExc_OSError
, "Device does not support SMART");
253 int r
= sk_disk_smart_get_temperature(device
->disk
, &mkelvin
);
257 // Convert the temperature to Kelvin
258 return PyFloat_FromDouble((double)mkelvin
/ 1000.0);
261 static PyGetSetDef BlockDevice_getsetters
[] = {
262 {"path", (getter
)BlockDevice_get_path
, NULL
, NULL
, NULL
},
263 {"model", (getter
)BlockDevice_get_model
, NULL
, NULL
, NULL
},
264 {"serial", (getter
)BlockDevice_get_serial
, NULL
, NULL
, NULL
},
267 static PyMethodDef BlockDevice_methods
[] = {
268 {"get_bad_sectors", (PyCFunction
)BlockDevice_get_bad_sectors
, METH_NOARGS
, NULL
},
269 {"get_temperature", (PyCFunction
)BlockDevice_get_temperature
, METH_NOARGS
, NULL
},
270 {"is_smart_supported", (PyCFunction
)BlockDevice_is_smart_supported
, METH_NOARGS
, NULL
},
271 {"is_awake", (PyCFunction
)BlockDevice_is_awake
, METH_NOARGS
, NULL
},
275 static PyTypeObject BlockDeviceType
= {
276 PyObject_HEAD_INIT(NULL
)
278 "_collecty.BlockDevice", /*tp_name*/
279 sizeof(BlockDevice
), /*tp_basicsize*/
281 (destructor
)BlockDevice_dealloc
, /*tp_dealloc*/
288 0, /*tp_as_sequence*/
296 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
297 "BlockDevice objects", /* tp_doc */
300 0, /* tp_richcompare */
301 0, /* tp_weaklistoffset */
304 BlockDevice_methods
, /* tp_methods */
306 BlockDevice_getsetters
, /* tp_getset */
309 0, /* tp_descr_get */
310 0, /* tp_descr_set */
311 0, /* tp_dictoffset */
312 (initproc
)BlockDevice_init
, /* tp_init */
314 BlockDevice_new
, /* tp_new */
319 const sensors_chip_name
* chip
;
320 const sensors_feature
* feature
;
323 static void Sensor_dealloc(SensorObject
* self
) {
324 self
->ob_type
->tp_free((PyObject
*)self
);
327 static PyObject
* Sensor_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
328 SensorObject
* self
= (SensorObject
*)type
->tp_alloc(type
, 0);
330 return (PyObject
*)self
;
333 static int Sensor_init(SensorObject
* self
, PyObject
* args
, PyObject
* kwds
) {
337 static PyObject
* Sensor_get_label(SensorObject
* self
) {
338 char* label
= sensors_get_label(self
->chip
, self
->feature
);
341 PyObject
* string
= PyString_FromString(label
);
350 static PyObject
* Sensor_get_name(SensorObject
* self
) {
353 int r
= sensors_snprintf_chip_name(chip_name
, sizeof(chip_name
), self
->chip
);
355 PyErr_Format(PyExc_RuntimeError
, "Could not print chip name");
359 return PyString_FromString(chip_name
);
362 static PyObject
* Sensor_get_type(SensorObject
* self
) {
363 const char* type
= NULL
;
365 switch (self
->feature
->type
) {
366 case SENSORS_FEATURE_IN
:
370 case SENSORS_FEATURE_FAN
:
374 case SENSORS_FEATURE_TEMP
:
375 type
= "temperature";
378 case SENSORS_FEATURE_POWER
:
387 return PyString_FromString(type
);
392 static PyObject
* Sensor_get_bus(SensorObject
* self
) {
393 const char* type
= NULL
;
395 switch (self
->chip
->bus
.type
) {
396 case SENSORS_BUS_TYPE_I2C
:
400 case SENSORS_BUS_TYPE_ISA
:
404 case SENSORS_BUS_TYPE_PCI
:
408 case SENSORS_BUS_TYPE_SPI
:
412 case SENSORS_BUS_TYPE_VIRTUAL
:
416 case SENSORS_BUS_TYPE_ACPI
:
420 case SENSORS_BUS_TYPE_HID
:
429 return PyString_FromString(type
);
434 static const sensors_subfeature
* Sensor_get_subfeature(SensorObject
* sensor
, sensors_subfeature_type type
) {
435 const sensors_subfeature
* subfeature
;
436 int subfeature_num
= 0;
438 while ((subfeature
= sensors_get_all_subfeatures(sensor
->chip
, sensor
->feature
, &subfeature_num
))) {
439 if (subfeature
->type
== type
)
446 static PyObject
* Sensor_return_value(SensorObject
* sensor
, sensors_subfeature_type subfeature_type
) {
449 const sensors_subfeature
* subfeature
= Sensor_get_subfeature(sensor
, subfeature_type
);
451 PyErr_Format(PyExc_AttributeError
, "Could not find sensor of requested type");
455 // Fetch value from the sensor
456 int r
= sensors_get_value(sensor
->chip
, subfeature
->number
, &value
);
458 PyErr_Format(PyExc_ValueError
, "Error retrieving value from sensor: %s",
459 sensors_strerror(errno
));
463 // Convert all temperature values from Celcius to Kelvon
464 if (sensor
->feature
->type
== SENSORS_FEATURE_TEMP
)
467 return PyFloat_FromDouble(value
);
470 static PyObject
* Sensor_no_value() {
471 PyErr_Format(PyExc_ValueError
, "Value not supported for this sensor type");
475 static PyObject
* Sensor_get_value(SensorObject
* self
) {
476 sensors_subfeature_type subfeature_type
;
478 switch (self
->feature
->type
) {
479 case SENSORS_FEATURE_IN
:
480 subfeature_type
= SENSORS_SUBFEATURE_IN_INPUT
;
483 case SENSORS_FEATURE_FAN
:
484 subfeature_type
= SENSORS_SUBFEATURE_FAN_INPUT
;
487 case SENSORS_FEATURE_TEMP
:
488 subfeature_type
= SENSORS_SUBFEATURE_TEMP_INPUT
;
491 case SENSORS_FEATURE_POWER
:
492 subfeature_type
= SENSORS_SUBFEATURE_POWER_INPUT
;
496 return Sensor_no_value();
499 return Sensor_return_value(self
, subfeature_type
);
502 static PyObject
* Sensor_get_critical(SensorObject
* self
) {
503 sensors_subfeature_type subfeature_type
;
505 switch (self
->feature
->type
) {
506 case SENSORS_FEATURE_IN
:
507 subfeature_type
= SENSORS_SUBFEATURE_IN_CRIT
;
510 case SENSORS_FEATURE_TEMP
:
511 subfeature_type
= SENSORS_SUBFEATURE_TEMP_CRIT
;
514 case SENSORS_FEATURE_POWER
:
515 subfeature_type
= SENSORS_SUBFEATURE_POWER_CRIT
;
519 return Sensor_no_value();
522 return Sensor_return_value(self
, subfeature_type
);
525 static PyObject
* Sensor_get_maximum(SensorObject
* self
) {
526 sensors_subfeature_type subfeature_type
;
528 switch (self
->feature
->type
) {
529 case SENSORS_FEATURE_IN
:
530 subfeature_type
= SENSORS_SUBFEATURE_IN_MAX
;
533 case SENSORS_FEATURE_FAN
:
534 subfeature_type
= SENSORS_SUBFEATURE_FAN_MAX
;
537 case SENSORS_FEATURE_TEMP
:
538 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
541 case SENSORS_FEATURE_POWER
:
542 subfeature_type
= SENSORS_SUBFEATURE_POWER_MAX
;
546 return Sensor_no_value();
549 return Sensor_return_value(self
, subfeature_type
);
552 static PyObject
* Sensor_get_minimum(SensorObject
* self
) {
553 sensors_subfeature_type subfeature_type
;
555 switch (self
->feature
->type
) {
556 case SENSORS_FEATURE_IN
:
557 subfeature_type
= SENSORS_SUBFEATURE_IN_MIN
;
560 case SENSORS_FEATURE_FAN
:
561 subfeature_type
= SENSORS_SUBFEATURE_FAN_MIN
;
564 case SENSORS_FEATURE_TEMP
:
565 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MIN
;
569 return Sensor_no_value();
572 return Sensor_return_value(self
, subfeature_type
);
575 static PyObject
* Sensor_get_high(SensorObject
* self
) {
576 sensors_subfeature_type subfeature_type
;
578 switch (self
->feature
->type
) {
579 case SENSORS_FEATURE_TEMP
:
580 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
584 return Sensor_no_value();
587 return Sensor_return_value(self
, subfeature_type
);
590 static PyGetSetDef Sensor_getsetters
[] = {
591 {"bus", (getter
)Sensor_get_bus
, NULL
, NULL
, NULL
},
592 {"critical", (getter
)Sensor_get_critical
, NULL
, NULL
, NULL
},
593 {"high", (getter
)Sensor_get_high
, NULL
, NULL
, NULL
},
594 {"label", (getter
)Sensor_get_label
, NULL
, NULL
, NULL
},
595 {"maximum", (getter
)Sensor_get_maximum
, NULL
, NULL
, NULL
},
596 {"minumum", (getter
)Sensor_get_minimum
, NULL
, NULL
, NULL
},
597 {"name", (getter
)Sensor_get_name
, NULL
, NULL
, NULL
},
598 {"type", (getter
)Sensor_get_type
, NULL
, NULL
, NULL
},
599 {"value", (getter
)Sensor_get_value
, NULL
, NULL
, NULL
},
603 static PyTypeObject SensorType
= {
604 PyObject_HEAD_INIT(NULL
)
606 "_collecty.Sensor", /*tp_name*/
607 sizeof(SensorObject
), /*tp_basicsize*/
609 (destructor
)Sensor_dealloc
, /*tp_dealloc*/
616 0, /*tp_as_sequence*/
624 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
625 "Sensor objects", /* tp_doc */
628 0, /* tp_richcompare */
629 0, /* tp_weaklistoffset */
634 Sensor_getsetters
, /* tp_getset */
637 0, /* tp_descr_get */
638 0, /* tp_descr_set */
639 0, /* tp_dictoffset */
640 (initproc
)Sensor_init
, /* tp_init */
642 Sensor_new
, /* tp_new */
645 static SensorObject
* make_sensor_object(const sensors_chip_name
* chip
, const sensors_feature
* feature
) {
646 SensorObject
* sensor
= PyObject_New(SensorObject
, &SensorType
);
650 if (!PyObject_Init((PyObject
*)sensor
, &SensorType
)) {
656 sensor
->feature
= feature
;
661 static PyObject
* _collecty_sensors_init() {
662 // Clean up everything first in case sensors_init was called earlier
665 int r
= sensors_init(NULL
);
667 PyErr_Format(PyExc_OSError
, "Could not initialise sensors: %s",
668 sensors_strerror(errno
));
675 static PyObject
* _collecty_sensors_cleanup() {
680 static PyObject
* _collecty_get_detected_sensors(PyObject
* o
, PyObject
* args
) {
681 const char* name
= NULL
;
682 sensors_chip_name chip_name
;
684 if (!PyArg_ParseTuple(args
, "|z", &name
))
688 int r
= sensors_parse_chip_name(name
, &chip_name
);
690 PyErr_Format(PyExc_ValueError
, "Could not parse chip name: %s", name
);
695 PyObject
* list
= PyList_New(0);
697 const sensors_chip_name
* chip
;
700 while ((chip
= sensors_get_detected_chips((name
) ? &chip_name
: NULL
, &chip_num
))) {
701 const sensors_feature
* feature
;
704 while ((feature
= sensors_get_features(chip
, &feature_num
))) {
705 // Skip sensors we do not want to support
706 switch (feature
->type
) {
707 case SENSORS_FEATURE_IN
:
708 case SENSORS_FEATURE_FAN
:
709 case SENSORS_FEATURE_TEMP
:
710 case SENSORS_FEATURE_POWER
:
717 SensorObject
* sensor
= make_sensor_object(chip
, feature
);
718 PyList_Append(list
, (PyObject
*)sensor
);
725 static PyMethodDef collecty_module_methods
[] = {
726 {"get_detected_sensors", (PyCFunction
)_collecty_get_detected_sensors
, METH_VARARGS
, NULL
},
727 {"sensors_cleanup", (PyCFunction
)_collecty_sensors_cleanup
, METH_NOARGS
, NULL
},
728 {"sensors_init", (PyCFunction
)_collecty_sensors_init
, METH_NOARGS
, NULL
},
732 void init_collecty(void) {
733 if (PyType_Ready(&BlockDeviceType
) < 0)
736 if (PyType_Ready(&SensorType
) < 0)
739 PyObject
* m
= Py_InitModule("_collecty", collecty_module_methods
);
741 PyModule_AddObject(m
, "BlockDevice", (PyObject
*)&BlockDeviceType
);
742 PyModule_AddObject(m
, "Sensor", (PyObject
*)&SensorType
);