]> git.ipfire.org Git - collecty.git/blob - src/_collecty/sensors.c
Split the C module into multiple smaller files
[collecty.git] / src / _collecty / sensors.c
1 /*
2 * collecty
3 * Copyright (C) 2015 IPFire Team (www.ipfire.org)
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
19 #include <Python.h>
20
21 #include <errno.h>
22 #include <mntent.h>
23 #include <sensors/error.h>
24 #include <sensors/sensors.h>
25
26 #include "_collectymodule.h"
27
28 static PyGetSetDef Sensor_getsetters[] = {
29 {"bus", (getter)Sensor_get_bus, NULL, NULL, NULL},
30 {"critical", (getter)Sensor_get_critical, NULL, NULL, NULL},
31 {"high", (getter)Sensor_get_high, NULL, NULL, NULL},
32 {"label", (getter)Sensor_get_label, NULL, NULL, NULL},
33 {"maximum", (getter)Sensor_get_maximum, NULL, NULL, NULL},
34 {"minumum", (getter)Sensor_get_minimum, NULL, NULL, NULL},
35 {"name", (getter)Sensor_get_name, NULL, NULL, NULL},
36 {"type", (getter)Sensor_get_type, NULL, NULL, NULL},
37 {"value", (getter)Sensor_get_value, NULL, NULL, NULL},
38 {NULL},
39 };
40
41 PyTypeObject SensorType = {
42 PyObject_HEAD_INIT(NULL)
43 "_collecty.Sensor", /*tp_name*/
44 sizeof(SensorObject), /*tp_basicsize*/
45 0, /*tp_itemsize*/
46 (destructor)Sensor_dealloc, /*tp_dealloc*/
47 0, /*tp_print*/
48 0, /*tp_getattr*/
49 0, /*tp_setattr*/
50 0, /*tp_compare*/
51 0, /*tp_repr*/
52 0, /*tp_as_number*/
53 0, /*tp_as_sequence*/
54 0, /*tp_as_mapping*/
55 0, /*tp_hash */
56 0, /*tp_call*/
57 0, /*tp_str*/
58 0, /*tp_getattro*/
59 0, /*tp_setattro*/
60 0, /*tp_as_buffer*/
61 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
62 "Sensor objects", /* tp_doc */
63 0, /* tp_traverse */
64 0, /* tp_clear */
65 0, /* tp_richcompare */
66 0, /* tp_weaklistoffset */
67 0, /* tp_iter */
68 0, /* tp_iternext */
69 0, /* tp_methods */
70 0, /* tp_members */
71 Sensor_getsetters, /* tp_getset */
72 0, /* tp_base */
73 0, /* tp_dict */
74 0, /* tp_descr_get */
75 0, /* tp_descr_set */
76 0, /* tp_dictoffset */
77 (initproc)Sensor_init, /* tp_init */
78 0, /* tp_alloc */
79 Sensor_new, /* tp_new */
80 };
81
82 void Sensor_dealloc(SensorObject* self) {
83 Py_TYPE(self)->tp_free((PyObject*)self);
84 }
85
86 PyObject* Sensor_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
87 SensorObject* self = (SensorObject*)type->tp_alloc(type, 0);
88
89 return (PyObject *)self;
90 }
91
92 int Sensor_init(SensorObject* self, PyObject* args, PyObject* kwds) {
93 return 0;
94 }
95
96 PyObject* Sensor_get_label(SensorObject* self) {
97 char* label = sensors_get_label(self->chip, self->feature);
98
99 if (label) {
100 PyObject* string = PyUnicode_FromString(label);
101 free(label);
102
103 return string;
104 }
105
106 Py_RETURN_NONE;
107 }
108
109 PyObject* Sensor_get_name(SensorObject* self) {
110 char chip_name[512];
111
112 int r = sensors_snprintf_chip_name(chip_name, sizeof(chip_name), self->chip);
113 if (r < 0) {
114 PyErr_Format(PyExc_RuntimeError, "Could not print chip name");
115 return NULL;
116 }
117
118 return PyUnicode_FromString(chip_name);
119 }
120
121 PyObject* Sensor_get_type(SensorObject* self) {
122 const char* type = NULL;
123
124 switch (self->feature->type) {
125 case SENSORS_FEATURE_IN:
126 type = "voltage";
127 break;
128
129 case SENSORS_FEATURE_FAN:
130 type = "fan";
131 break;
132
133 case SENSORS_FEATURE_TEMP:
134 type = "temperature";
135 break;
136
137 case SENSORS_FEATURE_POWER:
138 type = "power";
139 break;
140
141 default:
142 break;
143 }
144
145 if (type)
146 return PyUnicode_FromString(type);
147
148 Py_RETURN_NONE;
149 }
150
151 PyObject* Sensor_get_bus(SensorObject* self) {
152 const char* type = NULL;
153
154 switch (self->chip->bus.type) {
155 case SENSORS_BUS_TYPE_I2C:
156 type = "i2c";
157 break;
158
159 case SENSORS_BUS_TYPE_ISA:
160 type = "isa";
161 break;
162
163 case SENSORS_BUS_TYPE_PCI:
164 type = "pci";
165 break;
166
167 case SENSORS_BUS_TYPE_SPI:
168 type = "spi";
169 break;
170
171 case SENSORS_BUS_TYPE_VIRTUAL:
172 type = "virtual";
173 break;
174
175 case SENSORS_BUS_TYPE_ACPI:
176 type = "acpi";
177 break;
178
179 case SENSORS_BUS_TYPE_HID:
180 type = "hid";
181 break;
182
183 default:
184 break;
185 }
186
187 if (type)
188 return PyUnicode_FromString(type);
189
190 Py_RETURN_NONE;
191 }
192
193 static const sensors_subfeature* Sensor_get_subfeature(SensorObject* sensor, sensors_subfeature_type type) {
194 const sensors_subfeature* subfeature;
195 int subfeature_num = 0;
196
197 while ((subfeature = sensors_get_all_subfeatures(sensor->chip, sensor->feature, &subfeature_num))) {
198 if (subfeature->type == type)
199 break;
200 }
201
202 return subfeature;
203 }
204
205 PyObject* Sensor_return_value(SensorObject* sensor, sensors_subfeature_type subfeature_type) {
206 double value;
207
208 const sensors_subfeature* subfeature = Sensor_get_subfeature(sensor, subfeature_type);
209 if (!subfeature) {
210 PyErr_Format(PyExc_AttributeError, "Could not find sensor of requested type");
211 return NULL;
212 }
213
214 // Fetch value from the sensor
215 int r = sensors_get_value(sensor->chip, subfeature->number, &value);
216 if (r < 0) {
217 PyErr_Format(PyExc_ValueError, "Error retrieving value from sensor: %s",
218 sensors_strerror(errno));
219 return NULL;
220 }
221
222 // Convert all temperature values from Celcius to Kelvon
223 if (sensor->feature->type == SENSORS_FEATURE_TEMP)
224 value += 273.15;
225
226 return PyFloat_FromDouble(value);
227 }
228
229 static PyObject* Sensor_no_value() {
230 PyErr_Format(PyExc_ValueError, "Value not supported for this sensor type");
231 return NULL;
232 }
233
234 PyObject* Sensor_get_value(SensorObject* self) {
235 sensors_subfeature_type subfeature_type;
236
237 switch (self->feature->type) {
238 case SENSORS_FEATURE_IN:
239 subfeature_type = SENSORS_SUBFEATURE_IN_INPUT;
240 break;
241
242 case SENSORS_FEATURE_FAN:
243 subfeature_type = SENSORS_SUBFEATURE_FAN_INPUT;
244 break;
245
246 case SENSORS_FEATURE_TEMP:
247 subfeature_type = SENSORS_SUBFEATURE_TEMP_INPUT;
248 break;
249
250 case SENSORS_FEATURE_POWER:
251 subfeature_type = SENSORS_SUBFEATURE_POWER_INPUT;
252 break;
253
254 default:
255 return Sensor_no_value();
256 }
257
258 return Sensor_return_value(self, subfeature_type);
259 }
260
261 PyObject* Sensor_get_critical(SensorObject* self) {
262 sensors_subfeature_type subfeature_type;
263
264 switch (self->feature->type) {
265 case SENSORS_FEATURE_IN:
266 subfeature_type = SENSORS_SUBFEATURE_IN_CRIT;
267 break;
268
269 case SENSORS_FEATURE_TEMP:
270 subfeature_type = SENSORS_SUBFEATURE_TEMP_CRIT;
271 break;
272
273 case SENSORS_FEATURE_POWER:
274 subfeature_type = SENSORS_SUBFEATURE_POWER_CRIT;
275 break;
276
277 default:
278 return Sensor_no_value();
279 }
280
281 return Sensor_return_value(self, subfeature_type);
282 }
283
284 PyObject* Sensor_get_maximum(SensorObject* self) {
285 sensors_subfeature_type subfeature_type;
286
287 switch (self->feature->type) {
288 case SENSORS_FEATURE_IN:
289 subfeature_type = SENSORS_SUBFEATURE_IN_MAX;
290 break;
291
292 case SENSORS_FEATURE_FAN:
293 subfeature_type = SENSORS_SUBFEATURE_FAN_MAX;
294 break;
295
296 case SENSORS_FEATURE_TEMP:
297 subfeature_type = SENSORS_SUBFEATURE_TEMP_MAX;
298 break;
299
300 case SENSORS_FEATURE_POWER:
301 subfeature_type = SENSORS_SUBFEATURE_POWER_MAX;
302 break;
303
304 default:
305 return Sensor_no_value();
306 }
307
308 return Sensor_return_value(self, subfeature_type);
309 }
310
311 PyObject* Sensor_get_minimum(SensorObject* self) {
312 sensors_subfeature_type subfeature_type;
313
314 switch (self->feature->type) {
315 case SENSORS_FEATURE_IN:
316 subfeature_type = SENSORS_SUBFEATURE_IN_MIN;
317 break;
318
319 case SENSORS_FEATURE_FAN:
320 subfeature_type = SENSORS_SUBFEATURE_FAN_MIN;
321 break;
322
323 case SENSORS_FEATURE_TEMP:
324 subfeature_type = SENSORS_SUBFEATURE_TEMP_MIN;
325 break;
326
327 default:
328 return Sensor_no_value();
329 }
330
331 return Sensor_return_value(self, subfeature_type);
332 }
333
334 PyObject* Sensor_get_high(SensorObject* self) {
335 sensors_subfeature_type subfeature_type;
336
337 switch (self->feature->type) {
338 case SENSORS_FEATURE_TEMP:
339 subfeature_type = SENSORS_SUBFEATURE_TEMP_MAX;
340 break;
341
342 default:
343 return Sensor_no_value();
344 }
345
346 return Sensor_return_value(self, subfeature_type);
347 }
348
349 static SensorObject* make_sensor_object(const sensors_chip_name* chip, const sensors_feature* feature) {
350 SensorObject* sensor = PyObject_New(SensorObject, &SensorType);
351 if (!sensor)
352 return NULL;
353
354 if (!PyObject_Init((PyObject*)sensor, &SensorType)) {
355 Py_DECREF(sensor);
356 return NULL;
357 }
358
359 sensor->chip = chip;
360 sensor->feature = feature;
361
362 return sensor;
363 }
364
365 PyObject* _collecty_sensors_init() {
366 // Clean up everything first in case sensors_init was called earlier
367 sensors_cleanup();
368
369 int r = sensors_init(NULL);
370 if (r) {
371 PyErr_Format(PyExc_OSError, "Could not initialise sensors: %s",
372 sensors_strerror(errno));
373 return NULL;
374 }
375
376 Py_RETURN_NONE;
377 }
378
379 PyObject* _collecty_sensors_cleanup() {
380 sensors_cleanup();
381 Py_RETURN_NONE;
382 }
383
384 PyObject* _collecty_get_detected_sensors(PyObject* o, PyObject* args) {
385 const char* name = NULL;
386 sensors_chip_name chip_name;
387
388 if (!PyArg_ParseTuple(args, "|z", &name))
389 return NULL;
390
391 if (name) {
392 int r = sensors_parse_chip_name(name, &chip_name);
393 if (r < 0) {
394 PyErr_Format(PyExc_ValueError, "Could not parse chip name: %s", name);
395 return NULL;
396 }
397 }
398
399 PyObject* list = PyList_New(0);
400
401 const sensors_chip_name* chip;
402 int chip_num = 0;
403
404 while ((chip = sensors_get_detected_chips((name) ? &chip_name : NULL, &chip_num))) {
405 const sensors_feature* feature;
406 int feature_num = 0;
407
408 while ((feature = sensors_get_features(chip, &feature_num))) {
409 // Skip sensors we do not want to support
410 switch (feature->type) {
411 case SENSORS_FEATURE_IN:
412 case SENSORS_FEATURE_FAN:
413 case SENSORS_FEATURE_TEMP:
414 case SENSORS_FEATURE_POWER:
415 break;
416
417 default:
418 continue;
419 }
420
421 SensorObject* sensor = make_sensor_object(chip, feature);
422 PyList_Append(list, (PyObject*)sensor);
423 }
424 }
425
426 return list;
427 }