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>
26 #include <sensors/error.h>
27 #include <sensors/sensors.h>
30 #include <sys/ioctl.h>
34 #define SERIAL_SIZE 20
36 #define PING_HISTORY_SIZE 1024
37 #define PING_DEFAULT_COUNT 10
38 #define PING_DEFAULT_TIMEOUT 8
43 struct hd_driveid identity
;
47 static void BlockDevice_dealloc(BlockDevice
* self
) {
49 sk_disk_free(self
->disk
);
54 Py_TYPE(self
)->tp_free((PyObject
*)self
);
57 static int BlockDevice_get_identity(BlockDevice
* device
) {
60 if ((fd
= open(device
->path
, O_RDONLY
| O_NONBLOCK
)) < 0) {
64 int r
= ioctl(fd
, HDIO_GET_IDENTITY
, &device
->identity
);
73 static int BlockDevice_smart_is_available(BlockDevice
* device
) {
74 SkBool available
= FALSE
;
76 int r
= sk_disk_smart_is_available(device
->disk
, &available
);
86 static int BlockDevice_check_sleep_mode(BlockDevice
* device
) {
89 int r
= sk_disk_check_sleep_mode(device
->disk
, &awake
);
99 static PyObject
* BlockDevice_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
100 BlockDevice
* self
= (BlockDevice
*)type
->tp_alloc(type
, 0);
109 return (PyObject
*)self
;
112 static int BlockDevice_init(BlockDevice
* self
, PyObject
* args
, PyObject
* kwds
) {
113 const char* path
= NULL
;
115 if (!PyArg_ParseTuple(args
, "s", &path
))
118 self
->path
= strdup(path
);
120 int r
= BlockDevice_get_identity(self
);
122 PyErr_Format(PyExc_OSError
, "Could not open block device: %s", path
);
126 r
= sk_disk_open(path
, &self
->disk
);
128 if (BlockDevice_smart_is_available(self
) == 0) {
129 if (BlockDevice_check_sleep_mode(self
) == 0) {
130 r
= sk_disk_smart_read_data(self
->disk
);
132 PyErr_Format(PyExc_OSError
, "Could not open block device %s: %s", path
,
139 PyErr_Format(PyExc_OSError
, "Could not open block device %s: %s", path
,
144 //sk_disk_identify_is_available
149 static PyObject
* BlockDevice_get_path(PyObject
* self
) {
150 BlockDevice
* device
= (BlockDevice
*)self
;
152 return PyUnicode_FromString(device
->path
);
155 static void clean_string(char *s
) {
156 for (char* e
= s
; *e
; e
++) {
157 if (*e
< ' ' || *e
>= 127)
162 static void drop_spaces(char *s
) {
164 bool prev_space
= false;
186 static void copy_string(char* d
, const char* s
, size_t n
) {
187 // Copy the source buffer to the destination buffer up to n
190 // Terminate the destination buffer with NULL
193 // Clean up the string from non-printable characters
198 static PyObject
* BlockDevice_get_model(PyObject
* self
) {
199 BlockDevice
* device
= (BlockDevice
*)self
;
201 char model
[MODEL_SIZE
+ 1];
202 copy_string(model
, device
->identity
.model
, sizeof(model
));
204 return PyUnicode_FromString(model
);
207 static PyObject
* BlockDevice_get_serial(PyObject
* self
) {
208 BlockDevice
* device
= (BlockDevice
*)self
;
210 char serial
[SERIAL_SIZE
+ 1];
211 copy_string(serial
, device
->identity
.serial_no
, sizeof(serial
));
213 return PyUnicode_FromString(serial
);
216 static PyObject
* BlockDevice_is_smart_supported(PyObject
* self
) {
217 BlockDevice
* device
= (BlockDevice
*)self
;
219 if (BlockDevice_smart_is_available(device
) == 0)
225 static PyObject
* BlockDevice_is_awake(PyObject
* self
) {
226 BlockDevice
* device
= (BlockDevice
*)self
;
228 if (BlockDevice_check_sleep_mode(device
) == 0)
234 static PyObject
* BlockDevice_get_bad_sectors(PyObject
* self
) {
235 BlockDevice
* device
= (BlockDevice
*)self
;
237 if (BlockDevice_smart_is_available(device
)) {
238 PyErr_Format(PyExc_OSError
, "Device does not support SMART");
242 uint64_t bad_sectors
;
243 int r
= sk_disk_smart_get_bad(device
->disk
, &bad_sectors
);
247 return PyLong_FromUnsignedLongLong((unsigned long long)bad_sectors
);
250 static PyObject
* BlockDevice_get_temperature(PyObject
* self
) {
251 BlockDevice
* device
= (BlockDevice
*)self
;
253 if (BlockDevice_smart_is_available(device
)) {
254 PyErr_Format(PyExc_OSError
, "Device does not support SMART");
259 int r
= sk_disk_smart_get_temperature(device
->disk
, &mkelvin
);
263 // Convert the temperature to Kelvin
264 return PyFloat_FromDouble((double)mkelvin
/ 1000.0);
267 static PyGetSetDef BlockDevice_getsetters
[] = {
268 {"path", (getter
)BlockDevice_get_path
, NULL
, NULL
, NULL
},
269 {"model", (getter
)BlockDevice_get_model
, NULL
, NULL
, NULL
},
270 {"serial", (getter
)BlockDevice_get_serial
, NULL
, NULL
, NULL
},
273 static PyMethodDef BlockDevice_methods
[] = {
274 {"get_bad_sectors", (PyCFunction
)BlockDevice_get_bad_sectors
, METH_NOARGS
, NULL
},
275 {"get_temperature", (PyCFunction
)BlockDevice_get_temperature
, METH_NOARGS
, NULL
},
276 {"is_smart_supported", (PyCFunction
)BlockDevice_is_smart_supported
, METH_NOARGS
, NULL
},
277 {"is_awake", (PyCFunction
)BlockDevice_is_awake
, METH_NOARGS
, NULL
},
281 static PyTypeObject BlockDeviceType
= {
282 PyVarObject_HEAD_INIT(NULL
, 0)
283 "_collecty.BlockDevice", /*tp_name*/
284 sizeof(BlockDevice
), /*tp_basicsize*/
286 (destructor
)BlockDevice_dealloc
, /*tp_dealloc*/
293 0, /*tp_as_sequence*/
301 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
302 "BlockDevice objects", /* tp_doc */
305 0, /* tp_richcompare */
306 0, /* tp_weaklistoffset */
309 BlockDevice_methods
, /* tp_methods */
311 BlockDevice_getsetters
, /* tp_getset */
314 0, /* tp_descr_get */
315 0, /* tp_descr_set */
316 0, /* tp_dictoffset */
317 (initproc
)BlockDevice_init
, /* tp_init */
319 BlockDevice_new
, /* tp_new */
322 static PyObject
* PyExc_PingError
;
323 static PyObject
* PyExc_PingAddHostError
;
330 double history
[PING_HISTORY_SIZE
];
331 size_t history_index
;
341 static void Ping_dealloc(PingObject
* self
) {
343 ping_destroy(self
->ping
);
345 Py_TYPE(self
)->tp_free((PyObject
*)self
);
348 static void Ping_init_stats(PingObject
* self
) {
349 self
->stats
.history_index
= 0;
350 self
->stats
.history_size
= 0;
351 self
->stats
.packets_sent
= 0;
352 self
->stats
.packets_rcvd
= 0;
354 self
->stats
.average
= 0.0;
355 self
->stats
.stddev
= 0.0;
356 self
->stats
.loss
= 0.0;
359 static PyObject
* Ping_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
360 PingObject
* self
= (PingObject
*)type
->tp_alloc(type
, 0);
366 Ping_init_stats(self
);
369 return (PyObject
*)self
;
372 static int Ping_init(PingObject
* self
, PyObject
* args
, PyObject
* kwds
) {
373 char* kwlist
[] = {"host", "family", "timeout", "ttl", NULL
};
374 int family
= PING_DEF_AF
;
375 double timeout
= PING_DEFAULT_TIMEOUT
;
376 int ttl
= PING_DEF_TTL
;
378 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s|idi", kwlist
, &self
->host
,
379 &family
, &timeout
, &ttl
))
382 if (family
!= AF_UNSPEC
&& family
!= AF_INET6
&& family
!= AF_INET
) {
383 PyErr_Format(PyExc_ValueError
, "Family must be AF_UNSPEC, AF_INET6, or AF_INET");
388 PyErr_Format(PyExc_ValueError
, "Timeout must be greater than zero");
392 if (ttl
< 1 || ttl
> 255) {
393 PyErr_Format(PyExc_ValueError
, "TTL must be between 1 and 255");
397 self
->ping
= ping_construct();
405 r
= ping_setopt(self
->ping
, PING_OPT_AF
, &family
);
407 PyErr_Format(PyExc_RuntimeError
, "Could not set address family: %s",
408 ping_get_error(self
->ping
));
413 r
= ping_setopt(self
->ping
, PING_OPT_TIMEOUT
, &timeout
);
416 PyErr_Format(PyExc_RuntimeError
, "Could not set timeout: %s",
417 ping_get_error(self
->ping
));
422 r
= ping_setopt(self
->ping
, PING_OPT_TTL
, &ttl
);
424 PyErr_Format(PyExc_RuntimeError
, "Could not set TTL: %s",
425 ping_get_error(self
->ping
));
432 static double Ping_compute_average(PingObject
* self
) {
433 assert(self
->stats
.packets_rcvd
> 0);
435 double total_latency
= 0.0;
437 for (int i
= 0; i
< self
->stats
.history_size
; i
++) {
438 if (self
->stats
.history
[i
] > 0)
439 total_latency
+= self
->stats
.history
[i
];
442 return total_latency
/ self
->stats
.packets_rcvd
;
445 static double Ping_compute_stddev(PingObject
* self
, double mean
) {
446 assert(self
->stats
.packets_rcvd
> 0);
448 double deviation
= 0.0;
450 for (int i
= 0; i
< self
->stats
.history_size
; i
++) {
451 if (self
->stats
.history
[i
] > 0) {
452 deviation
+= pow(self
->stats
.history
[i
] - mean
, 2);
457 deviation
/= self
->stats
.packets_rcvd
;
459 return sqrt(deviation
);
462 static void Ping_compute_stats(PingObject
* self
) {
463 // Compute the average latency
464 self
->stats
.average
= Ping_compute_average(self
);
466 // Compute the standard deviation
467 self
->stats
.stddev
= Ping_compute_stddev(self
, self
->stats
.average
);
469 // Compute lost packets
470 self
->stats
.loss
= 1.0;
471 self
->stats
.loss
-= (double)self
->stats
.packets_rcvd \
472 / (double)self
->stats
.packets_sent
;
475 static double time_elapsed(struct timeval
* t0
) {
477 gettimeofday(&now
, NULL
);
479 double r
= now
.tv_sec
- t0
->tv_sec
;
480 r
+= ((double)now
.tv_usec
/ 1000000) - ((double)t0
->tv_usec
/ 1000000);
485 static PyObject
* Ping_ping(PingObject
* self
, PyObject
* args
, PyObject
* kwds
) {
486 char* kwlist
[] = {"count", "deadline", NULL
};
487 size_t count
= PING_DEFAULT_COUNT
;
490 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "|Id", kwlist
, &count
, &deadline
))
493 int r
= ping_host_add(self
->ping
, self
->host
);
495 PyErr_Format(PyExc_PingAddHostError
, "Could not add host %s: %s",
496 self
->host
, ping_get_error(self
->ping
));
500 // Reset all collected statistics in case ping() is called more than once.
501 Ping_init_stats(self
);
504 struct timeval time_start
;
505 r
= gettimeofday(&time_start
, NULL
);
507 PyErr_Format(PyExc_RuntimeError
, "Could not determine start time");
513 self
->stats
.packets_sent
++;
515 Py_BEGIN_ALLOW_THREADS
516 r
= ping_send(self
->ping
);
519 // Count recieved packets
521 self
->stats
.packets_rcvd
+= r
;
525 PyErr_Format(PyExc_RuntimeError
, "Error executing ping_send(): %s",
526 ping_get_error(self
->ping
));
531 pingobj_iter_t
* iter
= ping_iterator_get(self
->ping
);
533 double* latency
= &self
->stats
.history
[self
->stats
.history_index
];
534 size_t buffer_size
= sizeof(latency
);
535 ping_iterator_get_info(iter
, PING_INFO_LATENCY
, latency
, &buffer_size
);
537 // Increase the history pointer
538 self
->stats
.history_index
++;
539 self
->stats
.history_index
%= sizeof(self
->stats
.history
);
541 // Increase the history size
542 if (self
->stats
.history_size
< sizeof(self
->stats
.history
))
543 self
->stats
.history_size
++;
545 // Check if the deadline is due
547 double elapsed_time
= time_elapsed(&time_start
);
549 // If we have run longer than the deadline is, we end the main loop
550 if (elapsed_time
>= deadline
)
555 if (self
->stats
.packets_rcvd
== 0) {
556 PyErr_Format(PyExc_PingError
, "No replies received from %s", self
->host
);
560 Ping_compute_stats(self
);
565 static PyObject
* Ping_get_packets_sent(PingObject
* self
) {
566 return PyLong_FromUnsignedLong(self
->stats
.packets_sent
);
569 static PyObject
* Ping_get_packets_rcvd(PingObject
* self
) {
570 return PyLong_FromUnsignedLong(self
->stats
.packets_rcvd
);
573 static PyObject
* Ping_get_average(PingObject
* self
) {
574 return PyFloat_FromDouble(self
->stats
.average
);
577 static PyObject
* Ping_get_stddev(PingObject
* self
) {
578 return PyFloat_FromDouble(self
->stats
.stddev
);
581 static PyObject
* Ping_get_loss(PingObject
* self
) {
582 return PyFloat_FromDouble(self
->stats
.loss
);
585 static PyGetSetDef Ping_getsetters
[] = {
586 {"average", (getter
)Ping_get_average
, NULL
, NULL
, NULL
},
587 {"loss", (getter
)Ping_get_loss
, NULL
, NULL
, NULL
},
588 {"stddev", (getter
)Ping_get_stddev
, NULL
, NULL
, NULL
},
589 {"packets_sent", (getter
)Ping_get_packets_sent
, NULL
, NULL
, NULL
},
590 {"packets_rcvd", (getter
)Ping_get_packets_rcvd
, NULL
, NULL
, NULL
},
594 static PyMethodDef Ping_methods
[] = {
595 {"ping", (PyCFunction
)Ping_ping
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
599 static PyTypeObject PingType
= {
600 PyVarObject_HEAD_INIT(NULL
, 0)
601 "_collecty.Ping", /*tp_name*/
602 sizeof(PingObject
), /*tp_basicsize*/
604 (destructor
)Ping_dealloc
, /*tp_dealloc*/
611 0, /*tp_as_sequence*/
619 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
620 "Ping object", /* tp_doc */
623 0, /* tp_richcompare */
624 0, /* tp_weaklistoffset */
627 Ping_methods
, /* tp_methods */
629 Ping_getsetters
, /* tp_getset */
632 0, /* tp_descr_get */
633 0, /* tp_descr_set */
634 0, /* tp_dictoffset */
635 (initproc
)Ping_init
, /* tp_init */
637 Ping_new
, /* tp_new */
642 const sensors_chip_name
* chip
;
643 const sensors_feature
* feature
;
646 static void Sensor_dealloc(SensorObject
* self
) {
647 Py_TYPE(self
)->tp_free((PyObject
*)self
);
650 static PyObject
* Sensor_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
651 SensorObject
* self
= (SensorObject
*)type
->tp_alloc(type
, 0);
653 return (PyObject
*)self
;
656 static int Sensor_init(SensorObject
* self
, PyObject
* args
, PyObject
* kwds
) {
660 static PyObject
* Sensor_get_label(SensorObject
* self
) {
661 char* label
= sensors_get_label(self
->chip
, self
->feature
);
664 PyObject
* string
= PyUnicode_FromString(label
);
673 static PyObject
* Sensor_get_name(SensorObject
* self
) {
676 int r
= sensors_snprintf_chip_name(chip_name
, sizeof(chip_name
), self
->chip
);
678 PyErr_Format(PyExc_RuntimeError
, "Could not print chip name");
682 return PyUnicode_FromString(chip_name
);
685 static PyObject
* Sensor_get_type(SensorObject
* self
) {
686 const char* type
= NULL
;
688 switch (self
->feature
->type
) {
689 case SENSORS_FEATURE_IN
:
693 case SENSORS_FEATURE_FAN
:
697 case SENSORS_FEATURE_TEMP
:
698 type
= "temperature";
701 case SENSORS_FEATURE_POWER
:
710 return PyUnicode_FromString(type
);
715 static PyObject
* Sensor_get_bus(SensorObject
* self
) {
716 const char* type
= NULL
;
718 switch (self
->chip
->bus
.type
) {
719 case SENSORS_BUS_TYPE_I2C
:
723 case SENSORS_BUS_TYPE_ISA
:
727 case SENSORS_BUS_TYPE_PCI
:
731 case SENSORS_BUS_TYPE_SPI
:
735 case SENSORS_BUS_TYPE_VIRTUAL
:
739 case SENSORS_BUS_TYPE_ACPI
:
743 case SENSORS_BUS_TYPE_HID
:
752 return PyUnicode_FromString(type
);
757 static const sensors_subfeature
* Sensor_get_subfeature(SensorObject
* sensor
, sensors_subfeature_type type
) {
758 const sensors_subfeature
* subfeature
;
759 int subfeature_num
= 0;
761 while ((subfeature
= sensors_get_all_subfeatures(sensor
->chip
, sensor
->feature
, &subfeature_num
))) {
762 if (subfeature
->type
== type
)
769 static PyObject
* Sensor_return_value(SensorObject
* sensor
, sensors_subfeature_type subfeature_type
) {
772 const sensors_subfeature
* subfeature
= Sensor_get_subfeature(sensor
, subfeature_type
);
774 PyErr_Format(PyExc_AttributeError
, "Could not find sensor of requested type");
778 // Fetch value from the sensor
779 int r
= sensors_get_value(sensor
->chip
, subfeature
->number
, &value
);
781 PyErr_Format(PyExc_ValueError
, "Error retrieving value from sensor: %s",
782 sensors_strerror(errno
));
786 // Convert all temperature values from Celcius to Kelvon
787 if (sensor
->feature
->type
== SENSORS_FEATURE_TEMP
)
790 return PyFloat_FromDouble(value
);
793 static PyObject
* Sensor_no_value() {
794 PyErr_Format(PyExc_ValueError
, "Value not supported for this sensor type");
798 static PyObject
* Sensor_get_value(SensorObject
* self
) {
799 sensors_subfeature_type subfeature_type
;
801 switch (self
->feature
->type
) {
802 case SENSORS_FEATURE_IN
:
803 subfeature_type
= SENSORS_SUBFEATURE_IN_INPUT
;
806 case SENSORS_FEATURE_FAN
:
807 subfeature_type
= SENSORS_SUBFEATURE_FAN_INPUT
;
810 case SENSORS_FEATURE_TEMP
:
811 subfeature_type
= SENSORS_SUBFEATURE_TEMP_INPUT
;
814 case SENSORS_FEATURE_POWER
:
815 subfeature_type
= SENSORS_SUBFEATURE_POWER_INPUT
;
819 return Sensor_no_value();
822 return Sensor_return_value(self
, subfeature_type
);
825 static PyObject
* Sensor_get_critical(SensorObject
* self
) {
826 sensors_subfeature_type subfeature_type
;
828 switch (self
->feature
->type
) {
829 case SENSORS_FEATURE_IN
:
830 subfeature_type
= SENSORS_SUBFEATURE_IN_CRIT
;
833 case SENSORS_FEATURE_TEMP
:
834 subfeature_type
= SENSORS_SUBFEATURE_TEMP_CRIT
;
837 case SENSORS_FEATURE_POWER
:
838 subfeature_type
= SENSORS_SUBFEATURE_POWER_CRIT
;
842 return Sensor_no_value();
845 return Sensor_return_value(self
, subfeature_type
);
848 static PyObject
* Sensor_get_maximum(SensorObject
* self
) {
849 sensors_subfeature_type subfeature_type
;
851 switch (self
->feature
->type
) {
852 case SENSORS_FEATURE_IN
:
853 subfeature_type
= SENSORS_SUBFEATURE_IN_MAX
;
856 case SENSORS_FEATURE_FAN
:
857 subfeature_type
= SENSORS_SUBFEATURE_FAN_MAX
;
860 case SENSORS_FEATURE_TEMP
:
861 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
864 case SENSORS_FEATURE_POWER
:
865 subfeature_type
= SENSORS_SUBFEATURE_POWER_MAX
;
869 return Sensor_no_value();
872 return Sensor_return_value(self
, subfeature_type
);
875 static PyObject
* Sensor_get_minimum(SensorObject
* self
) {
876 sensors_subfeature_type subfeature_type
;
878 switch (self
->feature
->type
) {
879 case SENSORS_FEATURE_IN
:
880 subfeature_type
= SENSORS_SUBFEATURE_IN_MIN
;
883 case SENSORS_FEATURE_FAN
:
884 subfeature_type
= SENSORS_SUBFEATURE_FAN_MIN
;
887 case SENSORS_FEATURE_TEMP
:
888 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MIN
;
892 return Sensor_no_value();
895 return Sensor_return_value(self
, subfeature_type
);
898 static PyObject
* Sensor_get_high(SensorObject
* self
) {
899 sensors_subfeature_type subfeature_type
;
901 switch (self
->feature
->type
) {
902 case SENSORS_FEATURE_TEMP
:
903 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
907 return Sensor_no_value();
910 return Sensor_return_value(self
, subfeature_type
);
913 static PyGetSetDef Sensor_getsetters
[] = {
914 {"bus", (getter
)Sensor_get_bus
, NULL
, NULL
, NULL
},
915 {"critical", (getter
)Sensor_get_critical
, NULL
, NULL
, NULL
},
916 {"high", (getter
)Sensor_get_high
, NULL
, NULL
, NULL
},
917 {"label", (getter
)Sensor_get_label
, NULL
, NULL
, NULL
},
918 {"maximum", (getter
)Sensor_get_maximum
, NULL
, NULL
, NULL
},
919 {"minumum", (getter
)Sensor_get_minimum
, NULL
, NULL
, NULL
},
920 {"name", (getter
)Sensor_get_name
, NULL
, NULL
, NULL
},
921 {"type", (getter
)Sensor_get_type
, NULL
, NULL
, NULL
},
922 {"value", (getter
)Sensor_get_value
, NULL
, NULL
, NULL
},
926 static PyTypeObject SensorType
= {
927 PyObject_HEAD_INIT(NULL
)
928 "_collecty.Sensor", /*tp_name*/
929 sizeof(SensorObject
), /*tp_basicsize*/
931 (destructor
)Sensor_dealloc
, /*tp_dealloc*/
938 0, /*tp_as_sequence*/
946 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
947 "Sensor objects", /* tp_doc */
950 0, /* tp_richcompare */
951 0, /* tp_weaklistoffset */
956 Sensor_getsetters
, /* tp_getset */
959 0, /* tp_descr_get */
960 0, /* tp_descr_set */
961 0, /* tp_dictoffset */
962 (initproc
)Sensor_init
, /* tp_init */
964 Sensor_new
, /* tp_new */
967 static SensorObject
* make_sensor_object(const sensors_chip_name
* chip
, const sensors_feature
* feature
) {
968 SensorObject
* sensor
= PyObject_New(SensorObject
, &SensorType
);
972 if (!PyObject_Init((PyObject
*)sensor
, &SensorType
)) {
978 sensor
->feature
= feature
;
983 static PyObject
* _collecty_sensors_init() {
984 // Clean up everything first in case sensors_init was called earlier
987 int r
= sensors_init(NULL
);
989 PyErr_Format(PyExc_OSError
, "Could not initialise sensors: %s",
990 sensors_strerror(errno
));
997 static PyObject
* _collecty_sensors_cleanup() {
1002 static PyObject
* _collecty_get_detected_sensors(PyObject
* o
, PyObject
* args
) {
1003 const char* name
= NULL
;
1004 sensors_chip_name chip_name
;
1006 if (!PyArg_ParseTuple(args
, "|z", &name
))
1010 int r
= sensors_parse_chip_name(name
, &chip_name
);
1012 PyErr_Format(PyExc_ValueError
, "Could not parse chip name: %s", name
);
1017 PyObject
* list
= PyList_New(0);
1019 const sensors_chip_name
* chip
;
1022 while ((chip
= sensors_get_detected_chips((name
) ? &chip_name
: NULL
, &chip_num
))) {
1023 const sensors_feature
* feature
;
1024 int feature_num
= 0;
1026 while ((feature
= sensors_get_features(chip
, &feature_num
))) {
1027 // Skip sensors we do not want to support
1028 switch (feature
->type
) {
1029 case SENSORS_FEATURE_IN
:
1030 case SENSORS_FEATURE_FAN
:
1031 case SENSORS_FEATURE_TEMP
:
1032 case SENSORS_FEATURE_POWER
:
1039 SensorObject
* sensor
= make_sensor_object(chip
, feature
);
1040 PyList_Append(list
, (PyObject
*)sensor
);
1047 static PyMethodDef collecty_module_methods
[] = {
1048 {"get_detected_sensors", (PyCFunction
)_collecty_get_detected_sensors
, METH_VARARGS
, NULL
},
1049 {"sensors_cleanup", (PyCFunction
)_collecty_sensors_cleanup
, METH_NOARGS
, NULL
},
1050 {"sensors_init", (PyCFunction
)_collecty_sensors_init
, METH_NOARGS
, NULL
},
1054 static struct PyModuleDef collecty_module
= {
1055 PyModuleDef_HEAD_INIT
,
1056 "_collecty", /* m_name */
1057 "_collecty module", /* m_doc */
1059 collecty_module_methods
, /* m_methods */
1060 NULL
, /* m_reload */
1061 NULL
, /* m_traverse */
1066 PyMODINIT_FUNC
PyInit__collecty(void) {
1067 if (PyType_Ready(&BlockDeviceType
) < 0)
1070 if (PyType_Ready(&PingType
) < 0)
1073 if (PyType_Ready(&SensorType
) < 0)
1076 PyObject
* m
= PyModule_Create(&collecty_module
);
1078 Py_INCREF(&BlockDeviceType
);
1079 PyModule_AddObject(m
, "BlockDevice", (PyObject
*)&BlockDeviceType
);
1081 Py_INCREF(&PingType
);
1082 PyModule_AddObject(m
, "Ping", (PyObject
*)&PingType
);
1084 PyExc_PingError
= PyErr_NewException("_collecty.PingError", NULL
, NULL
);
1085 Py_INCREF(PyExc_PingError
);
1086 PyModule_AddObject(m
, "PingError", PyExc_PingError
);
1088 PyExc_PingAddHostError
= PyErr_NewException("_collecty.PingAddHostError", NULL
, NULL
);
1089 Py_INCREF(PyExc_PingAddHostError
);
1090 PyModule_AddObject(m
, "PingAddHostError", PyExc_PingAddHostError
);
1092 Py_INCREF(&SensorType
);
1093 PyModule_AddObject(m
, "Sensor", (PyObject
*)&SensorType
);