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>
27 #include <sensors/error.h>
28 #include <sensors/sensors.h>
31 #include <sys/ioctl.h>
35 #define SERIAL_SIZE 20
37 #define PING_HISTORY_SIZE 1024
38 #define PING_DEFAULT_COUNT 10
39 #define PING_DEFAULT_TIMEOUT 8
44 struct hd_driveid identity
;
48 static void BlockDevice_dealloc(BlockDevice
* self
) {
50 sk_disk_free(self
->disk
);
55 Py_TYPE(self
)->tp_free((PyObject
*)self
);
58 static int BlockDevice_get_identity(BlockDevice
* device
) {
61 if ((fd
= open(device
->path
, O_RDONLY
| O_NONBLOCK
)) < 0) {
65 int r
= ioctl(fd
, HDIO_GET_IDENTITY
, &device
->identity
);
74 static int BlockDevice_smart_is_available(BlockDevice
* device
) {
75 SkBool available
= FALSE
;
77 int r
= sk_disk_smart_is_available(device
->disk
, &available
);
87 static int BlockDevice_check_sleep_mode(BlockDevice
* device
) {
90 int r
= sk_disk_check_sleep_mode(device
->disk
, &awake
);
100 static PyObject
* BlockDevice_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
101 BlockDevice
* self
= (BlockDevice
*)type
->tp_alloc(type
, 0);
110 return (PyObject
*)self
;
113 static int BlockDevice_init(BlockDevice
* self
, PyObject
* args
, PyObject
* kwds
) {
114 const char* path
= NULL
;
116 if (!PyArg_ParseTuple(args
, "s", &path
))
119 self
->path
= strdup(path
);
121 int r
= BlockDevice_get_identity(self
);
123 PyErr_Format(PyExc_OSError
, "Could not open block device: %s", path
);
127 r
= sk_disk_open(path
, &self
->disk
);
129 if (BlockDevice_smart_is_available(self
) == 0) {
130 if (BlockDevice_check_sleep_mode(self
) == 0) {
131 r
= sk_disk_smart_read_data(self
->disk
);
133 PyErr_Format(PyExc_OSError
, "Could not open block device %s: %s", path
,
140 PyErr_Format(PyExc_OSError
, "Could not open block device %s: %s", path
,
145 //sk_disk_identify_is_available
150 static PyObject
* BlockDevice_get_path(PyObject
* self
) {
151 BlockDevice
* device
= (BlockDevice
*)self
;
153 return PyUnicode_FromString(device
->path
);
156 static void clean_string(char *s
) {
157 for (char* e
= s
; *e
; e
++) {
158 if (*e
< ' ' || *e
>= 127)
163 static void drop_spaces(char *s
) {
165 bool prev_space
= false;
187 static void copy_string(char* d
, const char* s
, size_t n
) {
188 // Copy the source buffer to the destination buffer up to n
191 // Terminate the destination buffer with NULL
194 // Clean up the string from non-printable characters
199 static PyObject
* BlockDevice_get_model(PyObject
* self
) {
200 BlockDevice
* device
= (BlockDevice
*)self
;
202 char model
[MODEL_SIZE
+ 1];
203 copy_string(model
, device
->identity
.model
, sizeof(model
));
205 return PyUnicode_FromString(model
);
208 static PyObject
* BlockDevice_get_serial(PyObject
* self
) {
209 BlockDevice
* device
= (BlockDevice
*)self
;
211 char serial
[SERIAL_SIZE
+ 1];
212 copy_string(serial
, device
->identity
.serial_no
, sizeof(serial
));
214 return PyUnicode_FromString(serial
);
217 static PyObject
* BlockDevice_is_smart_supported(PyObject
* self
) {
218 BlockDevice
* device
= (BlockDevice
*)self
;
220 if (BlockDevice_smart_is_available(device
) == 0)
226 static PyObject
* BlockDevice_is_awake(PyObject
* self
) {
227 BlockDevice
* device
= (BlockDevice
*)self
;
229 if (BlockDevice_check_sleep_mode(device
) == 0)
235 static PyObject
* BlockDevice_get_bad_sectors(PyObject
* self
) {
236 BlockDevice
* device
= (BlockDevice
*)self
;
238 if (BlockDevice_smart_is_available(device
)) {
239 PyErr_Format(PyExc_OSError
, "Device does not support SMART");
243 uint64_t bad_sectors
;
244 int r
= sk_disk_smart_get_bad(device
->disk
, &bad_sectors
);
248 return PyLong_FromUnsignedLongLong((unsigned long long)bad_sectors
);
251 static PyObject
* BlockDevice_get_temperature(PyObject
* self
) {
252 BlockDevice
* device
= (BlockDevice
*)self
;
254 if (BlockDevice_smart_is_available(device
)) {
255 PyErr_Format(PyExc_OSError
, "Device does not support SMART");
260 int r
= sk_disk_smart_get_temperature(device
->disk
, &mkelvin
);
264 // Convert the temperature to Kelvin
265 return PyFloat_FromDouble((double)mkelvin
/ 1000.0);
268 static PyGetSetDef BlockDevice_getsetters
[] = {
269 {"path", (getter
)BlockDevice_get_path
, NULL
, NULL
, NULL
},
270 {"model", (getter
)BlockDevice_get_model
, NULL
, NULL
, NULL
},
271 {"serial", (getter
)BlockDevice_get_serial
, NULL
, NULL
, NULL
},
274 static PyMethodDef BlockDevice_methods
[] = {
275 {"get_bad_sectors", (PyCFunction
)BlockDevice_get_bad_sectors
, METH_NOARGS
, NULL
},
276 {"get_temperature", (PyCFunction
)BlockDevice_get_temperature
, METH_NOARGS
, NULL
},
277 {"is_smart_supported", (PyCFunction
)BlockDevice_is_smart_supported
, METH_NOARGS
, NULL
},
278 {"is_awake", (PyCFunction
)BlockDevice_is_awake
, METH_NOARGS
, NULL
},
282 static PyTypeObject BlockDeviceType
= {
283 PyVarObject_HEAD_INIT(NULL
, 0)
284 "_collecty.BlockDevice", /*tp_name*/
285 sizeof(BlockDevice
), /*tp_basicsize*/
287 (destructor
)BlockDevice_dealloc
, /*tp_dealloc*/
294 0, /*tp_as_sequence*/
302 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
303 "BlockDevice objects", /* tp_doc */
306 0, /* tp_richcompare */
307 0, /* tp_weaklistoffset */
310 BlockDevice_methods
, /* tp_methods */
312 BlockDevice_getsetters
, /* tp_getset */
315 0, /* tp_descr_get */
316 0, /* tp_descr_set */
317 0, /* tp_dictoffset */
318 (initproc
)BlockDevice_init
, /* tp_init */
320 BlockDevice_new
, /* tp_new */
323 static PyObject
* PyExc_PingError
;
324 static PyObject
* PyExc_PingAddHostError
;
331 double history
[PING_HISTORY_SIZE
];
332 size_t history_index
;
342 static void Ping_dealloc(PingObject
* self
) {
344 ping_destroy(self
->ping
);
346 Py_TYPE(self
)->tp_free((PyObject
*)self
);
349 static void Ping_init_stats(PingObject
* self
) {
350 self
->stats
.history_index
= 0;
351 self
->stats
.history_size
= 0;
352 self
->stats
.packets_sent
= 0;
353 self
->stats
.packets_rcvd
= 0;
355 self
->stats
.average
= 0.0;
356 self
->stats
.stddev
= 0.0;
357 self
->stats
.loss
= 0.0;
360 static PyObject
* Ping_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
361 PingObject
* self
= (PingObject
*)type
->tp_alloc(type
, 0);
367 Ping_init_stats(self
);
370 return (PyObject
*)self
;
373 static int Ping_init(PingObject
* self
, PyObject
* args
, PyObject
* kwds
) {
374 char* kwlist
[] = {"host", "family", "timeout", "ttl", NULL
};
375 int family
= PING_DEF_AF
;
376 double timeout
= PING_DEFAULT_TIMEOUT
;
377 int ttl
= PING_DEF_TTL
;
379 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s|idi", kwlist
, &self
->host
,
380 &family
, &timeout
, &ttl
))
383 if (family
!= AF_UNSPEC
&& family
!= AF_INET6
&& family
!= AF_INET
) {
384 PyErr_Format(PyExc_ValueError
, "Family must be AF_UNSPEC, AF_INET6, or AF_INET");
389 PyErr_Format(PyExc_ValueError
, "Timeout must be greater than zero");
393 if (ttl
< 1 || ttl
> 255) {
394 PyErr_Format(PyExc_ValueError
, "TTL must be between 1 and 255");
398 self
->ping
= ping_construct();
406 r
= ping_setopt(self
->ping
, PING_OPT_AF
, &family
);
408 PyErr_Format(PyExc_RuntimeError
, "Could not set address family: %s",
409 ping_get_error(self
->ping
));
414 r
= ping_setopt(self
->ping
, PING_OPT_TIMEOUT
, &timeout
);
417 PyErr_Format(PyExc_RuntimeError
, "Could not set timeout: %s",
418 ping_get_error(self
->ping
));
423 r
= ping_setopt(self
->ping
, PING_OPT_TTL
, &ttl
);
425 PyErr_Format(PyExc_RuntimeError
, "Could not set TTL: %s",
426 ping_get_error(self
->ping
));
433 static double Ping_compute_average(PingObject
* self
) {
434 assert(self
->stats
.packets_rcvd
> 0);
436 double total_latency
= 0.0;
438 for (int i
= 0; i
< self
->stats
.history_size
; i
++) {
439 if (self
->stats
.history
[i
] > 0)
440 total_latency
+= self
->stats
.history
[i
];
443 return total_latency
/ self
->stats
.packets_rcvd
;
446 static double Ping_compute_stddev(PingObject
* self
, double mean
) {
447 assert(self
->stats
.packets_rcvd
> 0);
449 double deviation
= 0.0;
451 for (int i
= 0; i
< self
->stats
.history_size
; i
++) {
452 if (self
->stats
.history
[i
] > 0) {
453 deviation
+= pow(self
->stats
.history
[i
] - mean
, 2);
458 deviation
/= self
->stats
.packets_rcvd
;
460 return sqrt(deviation
);
463 static void Ping_compute_stats(PingObject
* self
) {
464 // Compute the average latency
465 self
->stats
.average
= Ping_compute_average(self
);
467 // Compute the standard deviation
468 self
->stats
.stddev
= Ping_compute_stddev(self
, self
->stats
.average
);
470 // Compute lost packets
471 self
->stats
.loss
= 1.0;
472 self
->stats
.loss
-= (double)self
->stats
.packets_rcvd \
473 / (double)self
->stats
.packets_sent
;
476 static double time_elapsed(struct timeval
* t0
) {
478 gettimeofday(&now
, NULL
);
480 double r
= now
.tv_sec
- t0
->tv_sec
;
481 r
+= ((double)now
.tv_usec
/ 1000000) - ((double)t0
->tv_usec
/ 1000000);
486 static PyObject
* Ping_ping(PingObject
* self
, PyObject
* args
, PyObject
* kwds
) {
487 char* kwlist
[] = {"count", "deadline", NULL
};
488 size_t count
= PING_DEFAULT_COUNT
;
491 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "|Id", kwlist
, &count
, &deadline
))
494 int r
= ping_host_add(self
->ping
, self
->host
);
496 PyErr_Format(PyExc_PingAddHostError
, "Could not add host %s: %s",
497 self
->host
, ping_get_error(self
->ping
));
501 // Reset all collected statistics in case ping() is called more than once.
502 Ping_init_stats(self
);
505 struct timeval time_start
;
506 r
= gettimeofday(&time_start
, NULL
);
508 PyErr_Format(PyExc_RuntimeError
, "Could not determine start time");
514 self
->stats
.packets_sent
++;
516 Py_BEGIN_ALLOW_THREADS
517 r
= ping_send(self
->ping
);
520 // Count recieved packets
522 self
->stats
.packets_rcvd
+= r
;
526 PyErr_Format(PyExc_RuntimeError
, "Error executing ping_send(): %s",
527 ping_get_error(self
->ping
));
532 pingobj_iter_t
* iter
= ping_iterator_get(self
->ping
);
534 double* latency
= &self
->stats
.history
[self
->stats
.history_index
];
535 size_t buffer_size
= sizeof(latency
);
536 ping_iterator_get_info(iter
, PING_INFO_LATENCY
, latency
, &buffer_size
);
538 // Increase the history pointer
539 self
->stats
.history_index
++;
540 self
->stats
.history_index
%= sizeof(self
->stats
.history
);
542 // Increase the history size
543 if (self
->stats
.history_size
< sizeof(self
->stats
.history
))
544 self
->stats
.history_size
++;
546 // Check if the deadline is due
548 double elapsed_time
= time_elapsed(&time_start
);
550 // If we have run longer than the deadline is, we end the main loop
551 if (elapsed_time
>= deadline
)
556 if (self
->stats
.packets_rcvd
== 0) {
557 PyErr_Format(PyExc_PingError
, "No replies received from %s", self
->host
);
561 Ping_compute_stats(self
);
566 static PyObject
* Ping_get_packets_sent(PingObject
* self
) {
567 return PyLong_FromUnsignedLong(self
->stats
.packets_sent
);
570 static PyObject
* Ping_get_packets_rcvd(PingObject
* self
) {
571 return PyLong_FromUnsignedLong(self
->stats
.packets_rcvd
);
574 static PyObject
* Ping_get_average(PingObject
* self
) {
575 return PyFloat_FromDouble(self
->stats
.average
);
578 static PyObject
* Ping_get_stddev(PingObject
* self
) {
579 return PyFloat_FromDouble(self
->stats
.stddev
);
582 static PyObject
* Ping_get_loss(PingObject
* self
) {
583 return PyFloat_FromDouble(self
->stats
.loss
);
586 static PyGetSetDef Ping_getsetters
[] = {
587 {"average", (getter
)Ping_get_average
, NULL
, NULL
, NULL
},
588 {"loss", (getter
)Ping_get_loss
, NULL
, NULL
, NULL
},
589 {"stddev", (getter
)Ping_get_stddev
, NULL
, NULL
, NULL
},
590 {"packets_sent", (getter
)Ping_get_packets_sent
, NULL
, NULL
, NULL
},
591 {"packets_rcvd", (getter
)Ping_get_packets_rcvd
, NULL
, NULL
, NULL
},
595 static PyMethodDef Ping_methods
[] = {
596 {"ping", (PyCFunction
)Ping_ping
, METH_VARARGS
|METH_KEYWORDS
, NULL
},
600 static PyTypeObject PingType
= {
601 PyVarObject_HEAD_INIT(NULL
, 0)
602 "_collecty.Ping", /*tp_name*/
603 sizeof(PingObject
), /*tp_basicsize*/
605 (destructor
)Ping_dealloc
, /*tp_dealloc*/
612 0, /*tp_as_sequence*/
620 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
621 "Ping object", /* tp_doc */
624 0, /* tp_richcompare */
625 0, /* tp_weaklistoffset */
628 Ping_methods
, /* tp_methods */
630 Ping_getsetters
, /* tp_getset */
633 0, /* tp_descr_get */
634 0, /* tp_descr_set */
635 0, /* tp_dictoffset */
636 (initproc
)Ping_init
, /* tp_init */
638 Ping_new
, /* tp_new */
643 const sensors_chip_name
* chip
;
644 const sensors_feature
* feature
;
647 static void Sensor_dealloc(SensorObject
* self
) {
648 Py_TYPE(self
)->tp_free((PyObject
*)self
);
651 static PyObject
* Sensor_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
652 SensorObject
* self
= (SensorObject
*)type
->tp_alloc(type
, 0);
654 return (PyObject
*)self
;
657 static int Sensor_init(SensorObject
* self
, PyObject
* args
, PyObject
* kwds
) {
661 static PyObject
* Sensor_get_label(SensorObject
* self
) {
662 char* label
= sensors_get_label(self
->chip
, self
->feature
);
665 PyObject
* string
= PyUnicode_FromString(label
);
674 static PyObject
* Sensor_get_name(SensorObject
* self
) {
677 int r
= sensors_snprintf_chip_name(chip_name
, sizeof(chip_name
), self
->chip
);
679 PyErr_Format(PyExc_RuntimeError
, "Could not print chip name");
683 return PyUnicode_FromString(chip_name
);
686 static PyObject
* Sensor_get_type(SensorObject
* self
) {
687 const char* type
= NULL
;
689 switch (self
->feature
->type
) {
690 case SENSORS_FEATURE_IN
:
694 case SENSORS_FEATURE_FAN
:
698 case SENSORS_FEATURE_TEMP
:
699 type
= "temperature";
702 case SENSORS_FEATURE_POWER
:
711 return PyUnicode_FromString(type
);
716 static PyObject
* Sensor_get_bus(SensorObject
* self
) {
717 const char* type
= NULL
;
719 switch (self
->chip
->bus
.type
) {
720 case SENSORS_BUS_TYPE_I2C
:
724 case SENSORS_BUS_TYPE_ISA
:
728 case SENSORS_BUS_TYPE_PCI
:
732 case SENSORS_BUS_TYPE_SPI
:
736 case SENSORS_BUS_TYPE_VIRTUAL
:
740 case SENSORS_BUS_TYPE_ACPI
:
744 case SENSORS_BUS_TYPE_HID
:
753 return PyUnicode_FromString(type
);
758 static const sensors_subfeature
* Sensor_get_subfeature(SensorObject
* sensor
, sensors_subfeature_type type
) {
759 const sensors_subfeature
* subfeature
;
760 int subfeature_num
= 0;
762 while ((subfeature
= sensors_get_all_subfeatures(sensor
->chip
, sensor
->feature
, &subfeature_num
))) {
763 if (subfeature
->type
== type
)
770 static PyObject
* Sensor_return_value(SensorObject
* sensor
, sensors_subfeature_type subfeature_type
) {
773 const sensors_subfeature
* subfeature
= Sensor_get_subfeature(sensor
, subfeature_type
);
775 PyErr_Format(PyExc_AttributeError
, "Could not find sensor of requested type");
779 // Fetch value from the sensor
780 int r
= sensors_get_value(sensor
->chip
, subfeature
->number
, &value
);
782 PyErr_Format(PyExc_ValueError
, "Error retrieving value from sensor: %s",
783 sensors_strerror(errno
));
787 // Convert all temperature values from Celcius to Kelvon
788 if (sensor
->feature
->type
== SENSORS_FEATURE_TEMP
)
791 return PyFloat_FromDouble(value
);
794 static PyObject
* Sensor_no_value() {
795 PyErr_Format(PyExc_ValueError
, "Value not supported for this sensor type");
799 static PyObject
* Sensor_get_value(SensorObject
* self
) {
800 sensors_subfeature_type subfeature_type
;
802 switch (self
->feature
->type
) {
803 case SENSORS_FEATURE_IN
:
804 subfeature_type
= SENSORS_SUBFEATURE_IN_INPUT
;
807 case SENSORS_FEATURE_FAN
:
808 subfeature_type
= SENSORS_SUBFEATURE_FAN_INPUT
;
811 case SENSORS_FEATURE_TEMP
:
812 subfeature_type
= SENSORS_SUBFEATURE_TEMP_INPUT
;
815 case SENSORS_FEATURE_POWER
:
816 subfeature_type
= SENSORS_SUBFEATURE_POWER_INPUT
;
820 return Sensor_no_value();
823 return Sensor_return_value(self
, subfeature_type
);
826 static PyObject
* Sensor_get_critical(SensorObject
* self
) {
827 sensors_subfeature_type subfeature_type
;
829 switch (self
->feature
->type
) {
830 case SENSORS_FEATURE_IN
:
831 subfeature_type
= SENSORS_SUBFEATURE_IN_CRIT
;
834 case SENSORS_FEATURE_TEMP
:
835 subfeature_type
= SENSORS_SUBFEATURE_TEMP_CRIT
;
838 case SENSORS_FEATURE_POWER
:
839 subfeature_type
= SENSORS_SUBFEATURE_POWER_CRIT
;
843 return Sensor_no_value();
846 return Sensor_return_value(self
, subfeature_type
);
849 static PyObject
* Sensor_get_maximum(SensorObject
* self
) {
850 sensors_subfeature_type subfeature_type
;
852 switch (self
->feature
->type
) {
853 case SENSORS_FEATURE_IN
:
854 subfeature_type
= SENSORS_SUBFEATURE_IN_MAX
;
857 case SENSORS_FEATURE_FAN
:
858 subfeature_type
= SENSORS_SUBFEATURE_FAN_MAX
;
861 case SENSORS_FEATURE_TEMP
:
862 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
865 case SENSORS_FEATURE_POWER
:
866 subfeature_type
= SENSORS_SUBFEATURE_POWER_MAX
;
870 return Sensor_no_value();
873 return Sensor_return_value(self
, subfeature_type
);
876 static PyObject
* Sensor_get_minimum(SensorObject
* self
) {
877 sensors_subfeature_type subfeature_type
;
879 switch (self
->feature
->type
) {
880 case SENSORS_FEATURE_IN
:
881 subfeature_type
= SENSORS_SUBFEATURE_IN_MIN
;
884 case SENSORS_FEATURE_FAN
:
885 subfeature_type
= SENSORS_SUBFEATURE_FAN_MIN
;
888 case SENSORS_FEATURE_TEMP
:
889 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MIN
;
893 return Sensor_no_value();
896 return Sensor_return_value(self
, subfeature_type
);
899 static PyObject
* Sensor_get_high(SensorObject
* self
) {
900 sensors_subfeature_type subfeature_type
;
902 switch (self
->feature
->type
) {
903 case SENSORS_FEATURE_TEMP
:
904 subfeature_type
= SENSORS_SUBFEATURE_TEMP_MAX
;
908 return Sensor_no_value();
911 return Sensor_return_value(self
, subfeature_type
);
914 static PyGetSetDef Sensor_getsetters
[] = {
915 {"bus", (getter
)Sensor_get_bus
, NULL
, NULL
, NULL
},
916 {"critical", (getter
)Sensor_get_critical
, NULL
, NULL
, NULL
},
917 {"high", (getter
)Sensor_get_high
, NULL
, NULL
, NULL
},
918 {"label", (getter
)Sensor_get_label
, NULL
, NULL
, NULL
},
919 {"maximum", (getter
)Sensor_get_maximum
, NULL
, NULL
, NULL
},
920 {"minumum", (getter
)Sensor_get_minimum
, NULL
, NULL
, NULL
},
921 {"name", (getter
)Sensor_get_name
, NULL
, NULL
, NULL
},
922 {"type", (getter
)Sensor_get_type
, NULL
, NULL
, NULL
},
923 {"value", (getter
)Sensor_get_value
, NULL
, NULL
, NULL
},
927 static PyTypeObject SensorType
= {
928 PyObject_HEAD_INIT(NULL
)
929 "_collecty.Sensor", /*tp_name*/
930 sizeof(SensorObject
), /*tp_basicsize*/
932 (destructor
)Sensor_dealloc
, /*tp_dealloc*/
939 0, /*tp_as_sequence*/
947 Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
, /*tp_flags*/
948 "Sensor objects", /* tp_doc */
951 0, /* tp_richcompare */
952 0, /* tp_weaklistoffset */
957 Sensor_getsetters
, /* tp_getset */
960 0, /* tp_descr_get */
961 0, /* tp_descr_set */
962 0, /* tp_dictoffset */
963 (initproc
)Sensor_init
, /* tp_init */
965 Sensor_new
, /* tp_new */
968 static SensorObject
* make_sensor_object(const sensors_chip_name
* chip
, const sensors_feature
* feature
) {
969 SensorObject
* sensor
= PyObject_New(SensorObject
, &SensorType
);
973 if (!PyObject_Init((PyObject
*)sensor
, &SensorType
)) {
979 sensor
->feature
= feature
;
984 static PyObject
* _collecty_sensors_init() {
985 // Clean up everything first in case sensors_init was called earlier
988 int r
= sensors_init(NULL
);
990 PyErr_Format(PyExc_OSError
, "Could not initialise sensors: %s",
991 sensors_strerror(errno
));
998 static PyObject
* _collecty_sensors_cleanup() {
1003 static PyObject
* _collecty_get_detected_sensors(PyObject
* o
, PyObject
* args
) {
1004 const char* name
= NULL
;
1005 sensors_chip_name chip_name
;
1007 if (!PyArg_ParseTuple(args
, "|z", &name
))
1011 int r
= sensors_parse_chip_name(name
, &chip_name
);
1013 PyErr_Format(PyExc_ValueError
, "Could not parse chip name: %s", name
);
1018 PyObject
* list
= PyList_New(0);
1020 const sensors_chip_name
* chip
;
1023 while ((chip
= sensors_get_detected_chips((name
) ? &chip_name
: NULL
, &chip_num
))) {
1024 const sensors_feature
* feature
;
1025 int feature_num
= 0;
1027 while ((feature
= sensors_get_features(chip
, &feature_num
))) {
1028 // Skip sensors we do not want to support
1029 switch (feature
->type
) {
1030 case SENSORS_FEATURE_IN
:
1031 case SENSORS_FEATURE_FAN
:
1032 case SENSORS_FEATURE_TEMP
:
1033 case SENSORS_FEATURE_POWER
:
1040 SensorObject
* sensor
= make_sensor_object(chip
, feature
);
1041 PyList_Append(list
, (PyObject
*)sensor
);
1048 static int _collecty_mountpoint_is_virtual(const struct mntent
* mp
) {
1049 // Ignore all ramdisks
1050 if (mp
->mnt_fsname
[0] != '/')
1053 // Ignore network mounts
1054 if (hasmntopt(mp
, "_netdev") != NULL
)
1060 static PyObject
* _collecty_get_mountpoints() {
1061 FILE* fp
= setmntent(_PATH_MOUNTED
, "r");
1065 PyObject
* list
= PyList_New(0);
1068 struct mntent
* mountpoint
= getmntent(fp
);
1069 while (mountpoint
) {
1070 if (!_collecty_mountpoint_is_virtual(mountpoint
)) {
1071 // Create a tuple with the information of the mountpoint
1072 PyObject
* mp
= PyTuple_New(4);
1073 PyTuple_SET_ITEM(mp
, 0, PyUnicode_FromString(mountpoint
->mnt_fsname
));
1074 PyTuple_SET_ITEM(mp
, 1, PyUnicode_FromString(mountpoint
->mnt_dir
));
1075 PyTuple_SET_ITEM(mp
, 2, PyUnicode_FromString(mountpoint
->mnt_type
));
1076 PyTuple_SET_ITEM(mp
, 3, PyUnicode_FromString(mountpoint
->mnt_opts
));
1078 // Append the tuple to the list
1079 r
= PyList_Append(list
, mp
);
1084 // Move on to the next mountpoint
1085 mountpoint
= getmntent(fp
);
1098 static PyMethodDef collecty_module_methods
[] = {
1099 {"get_detected_sensors", (PyCFunction
)_collecty_get_detected_sensors
, METH_VARARGS
, NULL
},
1100 {"get_mountpoints", (PyCFunction
)_collecty_get_mountpoints
, METH_NOARGS
, NULL
},
1101 {"sensors_cleanup", (PyCFunction
)_collecty_sensors_cleanup
, METH_NOARGS
, NULL
},
1102 {"sensors_init", (PyCFunction
)_collecty_sensors_init
, METH_NOARGS
, NULL
},
1106 static struct PyModuleDef collecty_module
= {
1107 PyModuleDef_HEAD_INIT
,
1108 "_collecty", /* m_name */
1109 "_collecty module", /* m_doc */
1111 collecty_module_methods
, /* m_methods */
1112 NULL
, /* m_reload */
1113 NULL
, /* m_traverse */
1118 PyMODINIT_FUNC
PyInit__collecty(void) {
1119 if (PyType_Ready(&BlockDeviceType
) < 0)
1122 if (PyType_Ready(&PingType
) < 0)
1125 if (PyType_Ready(&SensorType
) < 0)
1128 PyObject
* m
= PyModule_Create(&collecty_module
);
1130 Py_INCREF(&BlockDeviceType
);
1131 PyModule_AddObject(m
, "BlockDevice", (PyObject
*)&BlockDeviceType
);
1133 Py_INCREF(&PingType
);
1134 PyModule_AddObject(m
, "Ping", (PyObject
*)&PingType
);
1136 PyExc_PingError
= PyErr_NewException("_collecty.PingError", NULL
, NULL
);
1137 Py_INCREF(PyExc_PingError
);
1138 PyModule_AddObject(m
, "PingError", PyExc_PingError
);
1140 PyExc_PingAddHostError
= PyErr_NewException("_collecty.PingAddHostError", NULL
, NULL
);
1141 Py_INCREF(PyExc_PingAddHostError
);
1142 PyModule_AddObject(m
, "PingAddHostError", PyExc_PingAddHostError
);
1144 Py_INCREF(&SensorType
);
1145 PyModule_AddObject(m
, "Sensor", (PyObject
*)&SensorType
);