]> git.ipfire.org Git - oddments/collecty.git/blame - src/_collecty/blockdev.c
python: Declare functions as static when possible
[oddments/collecty.git] / src / _collecty / blockdev.c
CommitLineData
86d23308
MT
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 <atasmart.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <mntent.h>
25#include <stdbool.h>
26#include <string.h>
27#include <sys/ioctl.h>
28
29#include "_collectymodule.h"
30
44fb106a 31static void BlockDevice_dealloc(BlockDevice* self) {
86d23308
MT
32 if (self->disk)
33 sk_disk_free(self->disk);
34
35 if (self->path)
36 free(self->path);
37
38 Py_TYPE(self)->tp_free((PyObject*)self);
39}
40
44fb106a 41static int BlockDevice_get_identity(BlockDevice* device) {
86d23308
MT
42 int fd;
43
44 if ((fd = open(device->path, O_RDONLY | O_NONBLOCK)) < 0) {
45 return 1;
46 }
47
48 int r = ioctl(fd, HDIO_GET_IDENTITY, &device->identity);
49 close(fd);
50
51 if (r)
52 return 1;
53
54 return 0;
55}
56
44fb106a 57static int BlockDevice_smart_is_available(BlockDevice* device) {
86d23308
MT
58 SkBool available = FALSE;
59
60 int r = sk_disk_smart_is_available(device->disk, &available);
61 if (r)
62 return -1;
63
64 if (available)
65 return 0;
66
67 return 1;
68}
69
44fb106a 70static int BlockDevice_check_sleep_mode(BlockDevice* device) {
86d23308
MT
71 SkBool awake = FALSE;
72
73 int r = sk_disk_check_sleep_mode(device->disk, &awake);
74 if (r)
75 return -1;
76
77 if (awake)
78 return 0;
79
80 return 1;
81}
82
44fb106a 83static PyObject * BlockDevice_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
86d23308
MT
84 BlockDevice* self = (BlockDevice*)type->tp_alloc(type, 0);
85
86 if (self) {
87 self->path = NULL;
88
89 // libatasmart
90 self->disk = NULL;
91 }
92
93 return (PyObject *)self;
94}
95
44fb106a 96static int BlockDevice_init(BlockDevice* self, PyObject* args, PyObject* kwds) {
86d23308
MT
97 const char* path = NULL;
98
99 if (!PyArg_ParseTuple(args, "s", &path))
100 return -1;
101
102 self->path = strdup(path);
103
104 int r = BlockDevice_get_identity(self);
105 if (r) {
106 PyErr_Format(PyExc_OSError, "Could not open block device: %s", path);
107 return -1;
108 }
109
110 r = sk_disk_open(path, &self->disk);
111 if (r == 0) {
112 if (BlockDevice_smart_is_available(self) == 0) {
113 if (BlockDevice_check_sleep_mode(self) == 0) {
114 r = sk_disk_smart_read_data(self->disk);
115 if (r) {
116 PyErr_Format(PyExc_OSError, "Could not open block device %s: %s", path,
117 strerror(errno));
118 return -1;
119 }
120 }
121 }
122 } else {
123 PyErr_Format(PyExc_OSError, "Could not open block device %s: %s", path,
124 strerror(errno));
125 return -1;
126 }
127
128 //sk_disk_identify_is_available
129
130 return 0;
131}
132
44fb106a 133static PyObject* BlockDevice_get_path(PyObject* self) {
86d23308
MT
134 BlockDevice* device = (BlockDevice*)self;
135
136 return PyUnicode_FromString(device->path);
137}
138
139static void clean_string(char *s) {
140 for (char* e = s; *e; e++) {
141 if (*e < ' ' || *e >= 127)
142 *e = ' ';
143 }
144}
145
146static void drop_spaces(char *s) {
147 char *d = s;
148 bool prev_space = false;
149
150 s += strspn(s, " ");
151
152 for (; *s; s++) {
153 if (prev_space) {
154 if (*s != ' ') {
155 prev_space = false;
156 *(d++) = ' ';
157 *(d++) = *s;
158 }
159 } else {
160 if (*s == ' ')
161 prev_space = true;
162 else
163 *(d++) = *s;
164 }
165 }
166
167 *d = 0;
168}
169
170static void copy_string(char* d, const char* s, size_t n) {
171 // Copy the source buffer to the destination buffer up to n
172 memcpy(d, s, n);
173
174 // Terminate the destination buffer with NULL
175 d[n] = '\0';
176
177 // Clean up the string from non-printable characters
178 clean_string(d);
179 drop_spaces(d);
180}
181
44fb106a 182static PyObject* BlockDevice_get_model(PyObject* self) {
86d23308
MT
183 BlockDevice* device = (BlockDevice*)self;
184
185 char model[MODEL_SIZE + 1];
186 copy_string(model, device->identity.model, sizeof(model));
187
188 return PyUnicode_FromString(model);
189}
190
44fb106a 191static PyObject* BlockDevice_get_serial(PyObject* self) {
86d23308
MT
192 BlockDevice* device = (BlockDevice*)self;
193
194 char serial[SERIAL_SIZE + 1];
195 copy_string(serial, device->identity.serial_no, sizeof(serial));
196
197 return PyUnicode_FromString(serial);
198}
199
44fb106a 200static PyObject* BlockDevice_is_smart_supported(PyObject* self) {
86d23308
MT
201 BlockDevice* device = (BlockDevice*)self;
202
203 if (BlockDevice_smart_is_available(device) == 0)
204 Py_RETURN_TRUE;
205
206 Py_RETURN_FALSE;
207}
208
44fb106a 209static PyObject* BlockDevice_is_awake(PyObject* self) {
86d23308
MT
210 BlockDevice* device = (BlockDevice*)self;
211
212 if (BlockDevice_check_sleep_mode(device) == 0)
213 Py_RETURN_TRUE;
214
215 Py_RETURN_FALSE;
216}
217
44fb106a 218static PyObject* BlockDevice_get_bad_sectors(PyObject* self) {
86d23308
MT
219 BlockDevice* device = (BlockDevice*)self;
220
221 if (BlockDevice_smart_is_available(device)) {
222 PyErr_Format(PyExc_OSError, "Device does not support SMART");
223 return NULL;
224 }
225
226 uint64_t bad_sectors;
227 int r = sk_disk_smart_get_bad(device->disk, &bad_sectors);
228 if (r)
229 return NULL;
230
231 return PyLong_FromUnsignedLongLong((unsigned long long)bad_sectors);
232}
233
44fb106a 234static PyObject* BlockDevice_get_temperature(PyObject* self) {
86d23308
MT
235 BlockDevice* device = (BlockDevice*)self;
236
237 if (BlockDevice_smart_is_available(device)) {
238 PyErr_Format(PyExc_OSError, "Device does not support SMART");
239 return NULL;
240 }
241
242 uint64_t mkelvin;
243 int r = sk_disk_smart_get_temperature(device->disk, &mkelvin);
60b179c1
AF
244 if (r) {
245 // Temperature not available but SMART is supported
246 if (errno == ENOENT) {
247 PyErr_Format(PyExc_OSError, "Device does not have a temperature");
248 }
249
86d23308 250 return NULL;
60b179c1 251 }
86d23308
MT
252
253 // Convert the temperature to Kelvin
254 return PyFloat_FromDouble((double)mkelvin / 1000.0);
255}
44fb106a
MT
256
257static PyGetSetDef BlockDevice_getsetters[] = {
258 {"path", (getter)BlockDevice_get_path, NULL, NULL, NULL},
259 {"model", (getter)BlockDevice_get_model, NULL, NULL, NULL},
260 {"serial", (getter)BlockDevice_get_serial, NULL, NULL, NULL},
261 { NULL },
262};
263
264static PyMethodDef BlockDevice_methods[] = {
265 {"get_bad_sectors", (PyCFunction)BlockDevice_get_bad_sectors, METH_NOARGS, NULL},
266 {"get_temperature", (PyCFunction)BlockDevice_get_temperature, METH_NOARGS, NULL},
267 {"is_smart_supported", (PyCFunction)BlockDevice_is_smart_supported, METH_NOARGS, NULL},
268 {"is_awake", (PyCFunction)BlockDevice_is_awake, METH_NOARGS, NULL},
269 { NULL },
270};
271
272PyTypeObject BlockDeviceType = {
273 PyVarObject_HEAD_INIT(NULL, 0)
274 "_collecty.BlockDevice", /*tp_name*/
275 sizeof(BlockDevice), /*tp_basicsize*/
276 0, /*tp_itemsize*/
277 (destructor)BlockDevice_dealloc, /*tp_dealloc*/
278 0, /*tp_print*/
279 0, /*tp_getattr*/
280 0, /*tp_setattr*/
281 0, /*tp_compare*/
282 0, /*tp_repr*/
283 0, /*tp_as_number*/
284 0, /*tp_as_sequence*/
285 0, /*tp_as_mapping*/
286 0, /*tp_hash */
287 0, /*tp_call*/
288 0, /*tp_str*/
289 0, /*tp_getattro*/
290 0, /*tp_setattro*/
291 0, /*tp_as_buffer*/
292 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
293 "BlockDevice objects", /* tp_doc */
294 0, /* tp_traverse */
295 0, /* tp_clear */
296 0, /* tp_richcompare */
297 0, /* tp_weaklistoffset */
298 0, /* tp_iter */
299 0, /* tp_iternext */
300 BlockDevice_methods, /* tp_methods */
301 0, /* tp_members */
302 BlockDevice_getsetters, /* tp_getset */
303 0, /* tp_base */
304 0, /* tp_dict */
305 0, /* tp_descr_get */
306 0, /* tp_descr_set */
307 0, /* tp_dictoffset */
308 (initproc)BlockDevice_init, /* tp_init */
309 0, /* tp_alloc */
310 BlockDevice_new, /* tp_new */
311};