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 return PyLong_FromUnsignedLongLong((unsigned long long)mkelvin
);
260 static PyGetSetDef BlockDevice_getsetters
[] = {
261 {"path", (getter
)BlockDevice_get_path
, NULL
, NULL
, NULL
},
262 {"model", (getter
)BlockDevice_get_model
, NULL
, NULL
, NULL
},
263 {"serial", (getter
)BlockDevice_get_serial
, NULL
, NULL
, NULL
},
266 static PyMethodDef BlockDevice_methods
[] = {
267 {"get_bad_sectors", (PyCFunction
)BlockDevice_get_bad_sectors
, METH_NOARGS
, NULL
},
268 {"get_temperature", (PyCFunction
)BlockDevice_get_temperature
, METH_NOARGS
, NULL
},
269 {"is_smart_supported", (PyCFunction
)BlockDevice_is_smart_supported
, METH_NOARGS
, NULL
},
270 {"is_awake", (PyCFunction
)BlockDevice_is_awake
, METH_NOARGS
, NULL
},
274 static PyTypeObject BlockDeviceType
= {
275 PyObject_HEAD_INIT(NULL
)
277 "_collecty.BlockDevice", /*tp_name*/
278 sizeof(BlockDevice
), /*tp_basicsize*/
280 (destructor
)BlockDevice_dealloc
, /*tp_dealloc*/
287 0, /*tp_as_sequence*/
295 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
296 "BlockDevice objects", /* tp_doc */
299 0, /* tp_richcompare */
300 0, /* tp_weaklistoffset */
303 BlockDevice_methods
, /* tp_methods */
305 BlockDevice_getsetters
, /* tp_getset */
308 0, /* tp_descr_get */
309 0, /* tp_descr_set */
310 0, /* tp_dictoffset */
311 (initproc
)BlockDevice_init
, /* tp_init */
313 BlockDevice_new
, /* tp_new */
318 const sensors_chip_name
* chip
;
319 const sensors_feature
* feature
;
322 static void Sensor_dealloc(SensorObject
* self
) {
323 self
->ob_type
->tp_free((PyObject
*)self
);
326 static PyObject
* Sensor_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
327 SensorObject
* self
= (SensorObject
*)type
->tp_alloc(type
, 0);
329 return (PyObject
*)self
;
332 static int Sensor_init(SensorObject
* self
, PyObject
* args
, PyObject
* kwds
) {
336 static PyObject
* Sensor_get_label(SensorObject
* self
) {
337 char* label
= sensors_get_label(self
->chip
, self
->feature
);
340 PyObject
* string
= PyString_FromString(label
);
349 static PyObject
* Sensor_get_name(SensorObject
* self
) {
352 int r
= sensors_snprintf_chip_name(chip_name
, sizeof(chip_name
), self
->chip
);
354 PyErr_Format(PyExc_RuntimeError
, "Could not print chip name");
358 return PyString_FromString(chip_name
);
361 static PyObject
* Sensor_get_type(SensorObject
* self
) {
362 const char* type
= NULL
;
364 switch (self
->feature
->type
) {
365 case SENSORS_FEATURE_IN
:
369 case SENSORS_FEATURE_FAN
:
373 case SENSORS_FEATURE_TEMP
:
374 type
= "temperature";
377 case SENSORS_FEATURE_POWER
:
386 return PyString_FromString(type
);
391 static PyObject
* Sensor_get_bus(SensorObject
* self
) {
392 const char* type
= NULL
;
394 switch (self
->chip
->bus
.type
) {
395 case SENSORS_BUS_TYPE_I2C
:
399 case SENSORS_BUS_TYPE_ISA
:
403 case SENSORS_BUS_TYPE_PCI
:
407 case SENSORS_BUS_TYPE_SPI
:
411 case SENSORS_BUS_TYPE_VIRTUAL
:
415 case SENSORS_BUS_TYPE_ACPI
:
419 case SENSORS_BUS_TYPE_HID
:
428 return PyString_FromString(type
);
433 static const sensors_subfeature
* Sensor_get_subfeature(SensorObject
* sensor
, sensors_subfeature_type type
) {
434 const sensors_subfeature
* subfeature
;
435 int subfeature_num
= 0;
437 while ((subfeature
= sensors_get_all_subfeatures(sensor
->chip
, sensor
->feature
, &subfeature_num
))) {
438 if (subfeature
->type
== type
)
445 static PyObject
* Sensor_return_value(SensorObject
* sensor
, sensors_subfeature_type subfeature_type
) {
448 const sensors_subfeature
* subfeature
= Sensor_get_subfeature(sensor
, subfeature_type
);
450 PyErr_Format(PyExc_AttributeError
, "Could not find sensor of requested type");
454 // Fetch value from the sensor
455 int r
= sensors_get_value(sensor
->chip
, subfeature
->number
, &value
);
457 PyErr_Format(PyExc_ValueError
, "Error retrieving value from sensor: %s",
458 sensors_strerror(errno
));
462 // Convert all temperature values from Celcius to Kelvon
463 if (sensor
->feature
->type
== SENSORS_FEATURE_TEMP
)
466 return PyFloat_FromDouble(value
);
469 static PyObject
* Sensor_no_value() {
470 PyErr_Format(PyExc_ValueError
, "Value not supported for this sensor type");
474 static PyObject
* Sensor_get_value(SensorObject
* self
) {
475 sensors_subfeature_type subfeature_type
;
477 switch (self
->feature
->type
) {
478 case SENSORS_FEATURE_IN
:
479 subfeature_type
= SENSORS_SUBFEATURE_IN_INPUT
;
482 case SENSORS_FEATURE_FAN
:
483 subfeature_type
= SENSORS_SUBFEATURE_FAN_INPUT
;
486 case SENSORS_FEATURE_TEMP
:
487 subfeature_type
= SENSORS_SUBFEATURE_TEMP_INPUT
;
490 case SENSORS_FEATURE_POWER
:
491 subfeature_type
= SENSORS_SUBFEATURE_POWER_INPUT
;
495 return Sensor_no_value();
498 return Sensor_return_value(self
, subfeature_type
);
501 static PyObject
* Sensor_get_critical(SensorObject
* self
) {
502 sensors_subfeature_type subfeature_type
;
504 switch (self
->feature
->type
) {
505 case SENSORS_FEATURE_IN
:
506 subfeature_type
= SENSORS_SUBFEATURE_IN_CRIT
;
509 case SENSORS_FEATURE_TEMP
:
510 subfeature_type
= SENSORS_SUBFEATURE_TEMP_CRIT
;
513 case SENSORS_FEATURE_POWER
:
514 subfeature_type
= SENSORS_SUBFEATURE_POWER_CRIT
;
518 return Sensor_no_value();
521 return Sensor_return_value(self
, subfeature_type
);
524 static PyObject
* Sensor_get_maximum(SensorObject
* self
) {
525 sensors_subfeature_type subfeature_type
;
527 switch (self
->feature
->type
) {
528 case SENSORS_FEATURE_IN
:
529 subfeature_type
= SENSORS_SUBFEATURE_IN_MAX
;
532 case SENSORS_FEATURE_FAN
:
533 subfeature_type
= SENSORS_SUBFEATURE_FAN_MAX
;
536 case SENSORS_FEATURE_TEMP
:
537 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
540 case SENSORS_FEATURE_POWER
:
541 subfeature_type
= SENSORS_SUBFEATURE_POWER_MAX
;
545 return Sensor_no_value();
548 return Sensor_return_value(self
, subfeature_type
);
551 static PyObject
* Sensor_get_minimum(SensorObject
* self
) {
552 sensors_subfeature_type subfeature_type
;
554 switch (self
->feature
->type
) {
555 case SENSORS_FEATURE_IN
:
556 subfeature_type
= SENSORS_SUBFEATURE_IN_MIN
;
559 case SENSORS_FEATURE_FAN
:
560 subfeature_type
= SENSORS_SUBFEATURE_FAN_MIN
;
563 case SENSORS_FEATURE_TEMP
:
564 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MIN
;
568 return Sensor_no_value();
571 return Sensor_return_value(self
, subfeature_type
);
574 static PyObject
* Sensor_get_high(SensorObject
* self
) {
575 sensors_subfeature_type subfeature_type
;
577 switch (self
->feature
->type
) {
578 case SENSORS_FEATURE_TEMP
:
579 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
583 return Sensor_no_value();
586 return Sensor_return_value(self
, subfeature_type
);
589 static PyGetSetDef Sensor_getsetters
[] = {
590 {"bus", (getter
)Sensor_get_bus
, NULL
, NULL
, NULL
},
591 {"critical", (getter
)Sensor_get_critical
, NULL
, NULL
, NULL
},
592 {"high", (getter
)Sensor_get_high
, NULL
, NULL
, NULL
},
593 {"label", (getter
)Sensor_get_label
, NULL
, NULL
, NULL
},
594 {"maximum", (getter
)Sensor_get_maximum
, NULL
, NULL
, NULL
},
595 {"minumum", (getter
)Sensor_get_minimum
, NULL
, NULL
, NULL
},
596 {"name", (getter
)Sensor_get_name
, NULL
, NULL
, NULL
},
597 {"type", (getter
)Sensor_get_type
, NULL
, NULL
, NULL
},
598 {"value", (getter
)Sensor_get_value
, NULL
, NULL
, NULL
},
602 static PyTypeObject SensorType
= {
603 PyObject_HEAD_INIT(NULL
)
605 "_collecty.Sensor", /*tp_name*/
606 sizeof(SensorObject
), /*tp_basicsize*/
608 (destructor
)Sensor_dealloc
, /*tp_dealloc*/
615 0, /*tp_as_sequence*/
623 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
624 "Sensor objects", /* tp_doc */
627 0, /* tp_richcompare */
628 0, /* tp_weaklistoffset */
633 Sensor_getsetters
, /* tp_getset */
636 0, /* tp_descr_get */
637 0, /* tp_descr_set */
638 0, /* tp_dictoffset */
639 (initproc
)Sensor_init
, /* tp_init */
641 Sensor_new
, /* tp_new */
644 static SensorObject
* make_sensor_object(const sensors_chip_name
* chip
, const sensors_feature
* feature
) {
645 SensorObject
* sensor
= PyObject_New(SensorObject
, &SensorType
);
649 if (!PyObject_Init((PyObject
*)sensor
, &SensorType
)) {
655 sensor
->feature
= feature
;
660 static PyObject
* _collecty_sensors_init() {
661 // Clean up everything first in case sensors_init was called earlier
664 int r
= sensors_init(NULL
);
666 PyErr_Format(PyExc_OSError
, "Could not initialise sensors: %s",
667 sensors_strerror(errno
));
674 static PyObject
* _collecty_sensors_cleanup() {
679 static PyObject
* _collecty_get_detected_sensors(PyObject
* o
, PyObject
* args
) {
680 const char* name
= NULL
;
681 sensors_chip_name chip_name
;
683 if (!PyArg_ParseTuple(args
, "|z", &name
))
687 int r
= sensors_parse_chip_name(name
, &chip_name
);
689 PyErr_Format(PyExc_ValueError
, "Could not parse chip name: %s", name
);
694 PyObject
* list
= PyList_New(0);
696 const sensors_chip_name
* chip
;
699 while ((chip
= sensors_get_detected_chips((name
) ? &chip_name
: NULL
, &chip_num
))) {
700 const sensors_feature
* feature
;
703 while ((feature
= sensors_get_features(chip
, &feature_num
))) {
704 // Skip sensors we do not want to support
705 switch (feature
->type
) {
706 case SENSORS_FEATURE_IN
:
707 case SENSORS_FEATURE_FAN
:
708 case SENSORS_FEATURE_TEMP
:
709 case SENSORS_FEATURE_POWER
:
716 SensorObject
* sensor
= make_sensor_object(chip
, feature
);
717 PyList_Append(list
, (PyObject
*)sensor
);
724 static PyMethodDef collecty_module_methods
[] = {
725 {"get_detected_sensors", (PyCFunction
)_collecty_get_detected_sensors
, METH_VARARGS
, NULL
},
726 {"sensors_cleanup", (PyCFunction
)_collecty_sensors_cleanup
, METH_NOARGS
, NULL
},
727 {"sensors_init", (PyCFunction
)_collecty_sensors_init
, METH_NOARGS
, NULL
},
731 void init_collecty(void) {
732 if (PyType_Ready(&BlockDeviceType
) < 0)
735 if (PyType_Ready(&SensorType
) < 0)
738 PyObject
* m
= Py_InitModule("_collecty", collecty_module_methods
);
740 PyModule_AddObject(m
, "BlockDevice", (PyObject
*)&BlockDeviceType
);
741 PyModule_AddObject(m
, "Sensor", (PyObject
*)&SensorType
);