Code

Fix Py list length check in cpy_build_meta()
[collectd.git] / src / pyvalues.c
1 /**
2  * collectd - src/pyvalues.c
3  * Copyright (C) 2009  Sven Trenkel
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Sven Trenkel <collectd at semidefinite.de>  
25  **/
27 #include <Python.h>
28 #include <structmember.h>
30 #include "collectd.h"
31 #include "common.h"
33 #include "cpython.h"
35 #define FreeAll() do {\
36         PyMem_Free(type);\
37         PyMem_Free(plugin_instance);\
38         PyMem_Free(type_instance);\
39         PyMem_Free(plugin);\
40         PyMem_Free(host);\
41 } while(0)
43 static PyObject *cpy_common_repr(PyObject *s) {
44         PyObject *ret, *tmp;
45         static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
46         static PyObject *l_host = NULL, *l_time = NULL;
47         PluginData *self = (PluginData *) s;
48         
49         if (l_type == NULL)
50                 l_type = cpy_string_to_unicode_or_bytes("(type=");
51         if (l_type_instance == NULL)
52                 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
53         if (l_plugin == NULL)
54                 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
55         if (l_plugin_instance == NULL)
56                 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
57         if (l_host == NULL)
58                 l_host = cpy_string_to_unicode_or_bytes(",host=");
59         if (l_time == NULL)
60                 l_time = cpy_string_to_unicode_or_bytes(",time=");
61         
62         if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
63                 return NULL;
64         
65         ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
67         CPY_STRCAT(&ret, l_type);
68         tmp = cpy_string_to_unicode_or_bytes(self->type);
69         CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
70         CPY_STRCAT_AND_DEL(&ret, tmp);
72         if (self->type_instance[0] != 0) {
73                 CPY_STRCAT(&ret, l_type_instance);
74                 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
75                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
76                 CPY_STRCAT_AND_DEL(&ret, tmp);
77         }
79         if (self->plugin[0] != 0) {
80                 CPY_STRCAT(&ret, l_plugin);
81                 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
82                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
83                 CPY_STRCAT_AND_DEL(&ret, tmp);
84         }
86         if (self->plugin_instance[0] != 0) {
87                 CPY_STRCAT(&ret, l_plugin_instance);
88                 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
89                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
90                 CPY_STRCAT_AND_DEL(&ret, tmp);
91         }
93         if (self->host[0] != 0) {
94                 CPY_STRCAT(&ret, l_host);
95                 tmp = cpy_string_to_unicode_or_bytes(self->host);
96                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
97                 CPY_STRCAT_AND_DEL(&ret, tmp);
98         }
100         if (self->time != 0) {
101                 CPY_STRCAT(&ret, l_time);
102                 tmp = PyFloat_FromDouble(self->time);
103                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
104                 CPY_STRCAT_AND_DEL(&ret, tmp);
105         }
106         return ret;
109 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
110                 "For dispatching values this can be set to 0 which means \"now\".\n"
111                 "This means the time the value is actually dispatched, not the time\n"
112                 "it was set to 0.";
114 static char host_doc[] = "The hostname of the host this value was read from.\n"
115                 "For dispatching this can be set to an empty string which means\n"
116                 "the local hostname as defined in the collectd.conf.";
118 static char type_doc[] = "The type of this value. This type has to be defined\n"
119                 "in your types.db. Attempting to set it to any other value will\n"
120                 "raise a TypeError exception.\n"
121                 "Assigning a type is mandetory, calling dispatch without doing\n"
122                 "so will raise a RuntimeError exception.";
124 static char type_instance_doc[] = "";
126 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
127                 "member to an empty string will insert \"python\" upon dispatching.";
129 static char plugin_instance_doc[] = "";
131 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
132                 "and Notification. It is pretty useless by itself and was therefore not\n"
133                 "exported to the collectd module.";
135 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
136         PluginData *self;
137         
138         self = (PluginData *) type->tp_alloc(type, 0);
139         if (self == NULL)
140                 return NULL;
141         
142         self->time = 0;
143         self->host[0] = 0;
144         self->plugin[0] = 0;
145         self->plugin_instance[0] = 0;
146         self->type[0] = 0;
147         self->type_instance[0] = 0;
148         return (PyObject *) self;
151 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
152         PluginData *self = (PluginData *) s;
153         double time = 0;
154         char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
155         static char *kwlist[] = {"type", "plugin_instance", "type_instance",
156                         "plugin", "host", "time", NULL};
157         
158         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
159                         NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
160                 return -1;
161         
162         if (type && plugin_get_ds(type) == NULL) {
163                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
164                 FreeAll();
165                 return -1;
166         }
168         sstrncpy(self->host, host ? host : "", sizeof(self->host));
169         sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
170         sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->plugin_instance));
171         sstrncpy(self->type, type ? type : "", sizeof(self->type));
172         sstrncpy(self->type_instance, type_instance ? type_instance : "", sizeof(self->type_instance));
173         self->time = time;
175         FreeAll();
177         return 0;
180 static PyObject *PluginData_repr(PyObject *s) {
181         PyObject *ret;
182         static PyObject *l_closing = NULL;
183         
184         if (l_closing == NULL)
185                 l_closing = cpy_string_to_unicode_or_bytes(")");
186         
187         if (l_closing == NULL)
188                 return NULL;
189         
190         ret = cpy_common_repr(s);
191         CPY_STRCAT(&ret, l_closing);
192         return ret;
195 static PyMemberDef PluginData_members[] = {
196         {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
197         {NULL}
198 };
200 static PyObject *PluginData_getstring(PyObject *self, void *data) {
201         const char *value = ((char *) self) + (intptr_t) data;
202         
203         return cpy_string_to_unicode_or_bytes(value);
206 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
207         char *old;
208         const char *new;
209         
210         if (value == NULL) {
211                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
212                 return -1;
213         }
214         Py_INCREF(value);
215         new = cpy_unicode_or_bytes_to_string(&value);
216         if (new == NULL) {
217                 Py_DECREF(value);
218                 return -1;
219         }
220         old = ((char *) self) + (intptr_t) data;
221         sstrncpy(old, new, DATA_MAX_NAME_LEN);
222         Py_DECREF(value);
223         return 0;
226 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
227         char *old;
228         const char *new;
229         
230         if (value == NULL) {
231                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
232                 return -1;
233         }
234         Py_INCREF(value);
235         new = cpy_unicode_or_bytes_to_string(&value);
236         if (new == NULL) {
237                 Py_DECREF(value);
238                 return -1;
239         }
241         if (plugin_get_ds(new) == NULL) {
242                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
243                 Py_DECREF(value);
244                 return -1;
245         }
247         old = ((char *) self) + (intptr_t) data;
248         sstrncpy(old, new, DATA_MAX_NAME_LEN);
249         Py_DECREF(value);
250         return 0;
253 static PyGetSetDef PluginData_getseters[] = {
254         {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
255         {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
256         {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
257         {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
258         {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
259         {NULL}
260 };
262 PyTypeObject PluginDataType = {
263         CPY_INIT_TYPE
264         "collectd.PluginData",     /* tp_name */
265         sizeof(PluginData),        /* tp_basicsize */
266         0,                         /* Will be filled in later */
267         0,                         /* tp_dealloc */
268         0,                         /* tp_print */
269         0,                         /* tp_getattr */
270         0,                         /* tp_setattr */
271         0,                         /* tp_compare */
272         PluginData_repr,           /* tp_repr */
273         0,                         /* tp_as_number */
274         0,                         /* tp_as_sequence */
275         0,                         /* tp_as_mapping */
276         0,                         /* tp_hash */
277         0,                         /* tp_call */
278         0,                         /* tp_str */
279         0,                         /* tp_getattro */
280         0,                         /* tp_setattro */
281         0,                         /* tp_as_buffer */
282         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
283         PluginData_doc,            /* tp_doc */
284         0,                         /* tp_traverse */
285         0,                         /* tp_clear */
286         0,                         /* tp_richcompare */
287         0,                         /* tp_weaklistoffset */
288         0,                         /* tp_iter */
289         0,                         /* tp_iternext */
290         0,                         /* tp_methods */
291         PluginData_members,        /* tp_members */
292         PluginData_getseters,      /* tp_getset */
293         0,                         /* tp_base */
294         0,                         /* tp_dict */
295         0,                         /* tp_descr_get */
296         0,                         /* tp_descr_set */
297         0,                         /* tp_dictoffset */
298         PluginData_init,           /* tp_init */
299         0,                         /* tp_alloc */
300         PluginData_new             /* tp_new */
301 };
303 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
304                 "the same data source. This value has to be a positive integer, so you can't\n"
305                 "submit more than one value per second. If this member is set to a\n"
306                 "non-positive value, the default value as specified in the config file will\n"
307                 "be used (default: 10).\n"
308                 "\n"
309                 "If you submit values more often than the specified interval, the average\n"
310                 "will be used. If you submit less values, your graphs will have gaps.";
312 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
313                 "It has to be a sequence (a tuple or list) of numbers.\n"
314                 "The size of the sequence and the type of its content depend on the type\n"
315                 "member your types.db file. For more information on this read the types.db\n"
316                 "man page.\n"
317                 "\n"
318                 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
319                 "exception will be raised. If the content of the sequence is not a number,\n"
320                 "a TypeError exception will be raised.";
322 static char meta_doc[] = "These are the meta data for this Value object.\n"
323                 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
324                 "strings. int and long objects will be dispatched as signed integers unless\n"
325                 "they are between 2**63 and 2**64-1, which will result in a unsigned integer.\n"
326                 "You can force one of these storage classes by using the classes\n"
327                 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
328                 "callback will always contain Signed or Unsigned objects.";
330 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
331                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
332                 "\n"
333                 "Dispatch this instance to the collectd process. The object has members\n"
334                 "for each of the possible arguments for this method. For a detailed explanation\n"
335                 "of these parameters see the member of the same same.\n"
336                 "\n"
337                 "If you do not submit a parameter the value saved in its member will be submitted.\n"
338                 "If you do provide a parameter it will be used instead, without altering the member.";
340 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
341                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
342                 "\n"
343                 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
344                 "This will bypass the main collectd process and all filtering and caching.\n"
345                 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
346                 "used instead of 'write'.\n";
348 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
350 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
351         Values *self;
352         
353         self = (Values *) PluginData_new(type, args, kwds);
354         if (self == NULL)
355                 return NULL;
356         
357         self->values = PyList_New(0);
358         self->meta = PyDict_New();
359         self->interval = 0;
360         return (PyObject *) self;
363 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
364         Values *self = (Values *) s;
365         double interval = 0, time = 0;
366         PyObject *values = NULL, *meta = NULL, *tmp;
367         char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
368         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
369                         "plugin", "host", "time", "interval", "meta", NULL};
370         
371         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
372                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
373                         NULL, &plugin, NULL, &host, &time, &interval, &meta))
374                 return -1;
375         
376         if (type && plugin_get_ds(type) == NULL) {
377                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
378                 FreeAll();
379                 return -1;
380         }
382         sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
383         sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
384         sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
385         sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
386         sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
387         self->data.time = time;
389         FreeAll();
391         if (values == NULL) {
392                 values = PyList_New(0);
393                 PyErr_Clear();
394         } else {
395                 Py_INCREF(values);
396         }
397         
398         if (meta == NULL) {
399                 meta = PyDict_New();
400                 PyErr_Clear();
401         } else {
402                 Py_INCREF(meta);
403         }
404         
405         tmp = self->values;
406         self->values = values;
407         Py_XDECREF(tmp);
408         
409         tmp = self->meta;
410         self->meta = meta;
411         Py_XDECREF(tmp);
413         self->interval = interval;
414         return 0;
417 static meta_data_t *cpy_build_meta(PyObject *meta) {
418         int i, s;
419         meta_data_t *m = NULL;
420         PyObject *l;
421         
422         if ((meta == NULL) || (meta == Py_None))
423                 return NULL;
425         l = PyDict_Items(meta); /* New reference. */
426         if (!l) {
427                 cpy_log_exception("building meta data");
428                 return NULL;
429         }
430         s = PyList_Size(l);
431     if (s <= 0)
432         return NULL;
434         m = meta_data_create();
435         for (i = 0; i < s; ++i) {
436                 const char *string, *keystring;
437                 PyObject *key, *value, *item, *tmp;
438                 
439                 item = PyList_GET_ITEM(l, i);
440                 key = PyTuple_GET_ITEM(item, 0);
441                 Py_INCREF(key);
442                 keystring = cpy_unicode_or_bytes_to_string(&key);
443                 if (!keystring) {
444                         PyErr_Clear();
445                         Py_XDECREF(key);
446                         continue;
447                 }
448                 value = PyTuple_GET_ITEM(item, 1);
449                 Py_INCREF(value);
450                 if (value == Py_True) {
451                         meta_data_add_boolean(m, keystring, 1);
452                 } else if (value == Py_False) {
453                         meta_data_add_boolean(m, keystring, 0);
454                 } else if (PyFloat_Check(value)) {
455                         meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
456                 } else if (PyObject_TypeCheck(value, &SignedType)) {
457                         long long int lli;
458                         lli = PyLong_AsLongLong(value);
459                         if (!PyErr_Occurred() && (lli == (int64_t) lli))
460                                 meta_data_add_signed_int(m, keystring, lli);
461                 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
462                         long long unsigned llu;
463                         llu = PyLong_AsUnsignedLongLong(value);
464                         if (!PyErr_Occurred() && (llu == (uint64_t) llu))
465                                 meta_data_add_unsigned_int(m, keystring, llu);
466                 } else if (PyNumber_Check(value)) {
467                         long long int lli;
468                         long long unsigned llu;
469                         tmp = PyNumber_Long(value);
470                         lli = PyLong_AsLongLong(tmp);
471                         if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
472                                 meta_data_add_signed_int(m, keystring, lli);
473                         } else {
474                                 PyErr_Clear();
475                                 llu = PyLong_AsUnsignedLongLong(tmp);
476                                 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
477                                         meta_data_add_unsigned_int(m, keystring, llu);
478                         }
479                         Py_XDECREF(tmp);
480                 } else {
481                         string = cpy_unicode_or_bytes_to_string(&value);
482                         if (string) {
483                                 meta_data_add_string(m, keystring, string);
484                         } else {
485                                 PyErr_Clear();
486                                 tmp = PyObject_Str(value);
487                                 string = cpy_unicode_or_bytes_to_string(&tmp);
488                                 if (string)
489                                         meta_data_add_string(m, keystring, string);
490                                 Py_XDECREF(tmp);
491                         }
492                 }
493                 if (PyErr_Occurred())
494                         cpy_log_exception("building meta data");
495                 Py_XDECREF(value);
496                 Py_DECREF(key);
497         }
498         Py_XDECREF(l);
499         return m;
502 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
503         int i, ret;
504         const data_set_t *ds;
505         int size;
506         value_t *value;
507         value_list_t value_list = VALUE_LIST_INIT;
508         PyObject *values = self->values, *meta = self->meta;
509         double time = self->data.time, interval = self->interval;
510         char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
511         
512         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
513                         "plugin", "host", "time", "interval", "meta", NULL};
514         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
515                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
516                         NULL, &plugin, NULL, &host, &time, &interval, &meta))
517                 return NULL;
519         sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
520         sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
521         sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
522         sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
523         sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
524         FreeAll();
525         if (value_list.type[0] == 0) {
526                 PyErr_SetString(PyExc_RuntimeError, "type not set");
527                 FreeAll();
528                 return NULL;
529         }
530         ds = plugin_get_ds(value_list.type);
531         if (ds == NULL) {
532                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
533                 return NULL;
534         }
535         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
536                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
537                 return NULL;
538         }
539         if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
540                 PyErr_Format(PyExc_TypeError, "meta must be a dict");
541                 return NULL;
542         }
543         size = (int) PySequence_Length(values);
544         if (size != ds->ds_num) {
545                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
546                 return NULL;
547         }
548         value = malloc(size * sizeof(*value));
549         for (i = 0; i < size; ++i) {
550                 PyObject *item, *num;
551                 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
552                 if (ds->ds->type == DS_TYPE_COUNTER) {
553                         num = PyNumber_Long(item); /* New reference. */
554                         if (num != NULL) {
555                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
556                                 Py_XDECREF(num);
557                         }
558                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
559                         num = PyNumber_Float(item); /* New reference. */
560                         if (num != NULL) {
561                                 value[i].gauge = PyFloat_AsDouble(num);
562                                 Py_XDECREF(num);
563                         }
564                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
565                         /* This might overflow without raising an exception.
566                          * Not much we can do about it */
567                         num = PyNumber_Long(item); /* New reference. */
568                         if (num != NULL) {
569                                 value[i].derive = PyLong_AsLongLong(num);
570                                 Py_XDECREF(num);
571                         }
572                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
573                         /* This might overflow without raising an exception.
574                          * Not much we can do about it */
575                         num = PyNumber_Long(item); /* New reference. */
576                         if (num != NULL) {
577                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
578                                 Py_XDECREF(num);
579                         }
580                 } else {
581                         free(value);
582                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
583                         return NULL;
584                 }
585                 if (PyErr_Occurred() != NULL) {
586                         free(value);
587                         return NULL;
588                 }
589         }
590         value_list.values = value;
591         value_list.meta = cpy_build_meta(meta);
592         value_list.values_len = size;
593         value_list.time = DOUBLE_TO_CDTIME_T(time);
594         value_list.interval = DOUBLE_TO_CDTIME_T(interval);
595         if (value_list.host[0] == 0)
596                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
597         if (value_list.plugin[0] == 0)
598                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
599         Py_BEGIN_ALLOW_THREADS;
600         ret = plugin_dispatch_values(&value_list);
601         Py_END_ALLOW_THREADS;
602         meta_data_destroy(value_list.meta);
603         free(value);
604         if (ret != 0) {
605                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
606                 return NULL;
607         }
608         Py_RETURN_NONE;
611 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
612         int i, ret;
613         const data_set_t *ds;
614         int size;
615         value_t *value;
616         value_list_t value_list = VALUE_LIST_INIT;
617         PyObject *values = self->values, *meta = self->meta;
618         double time = self->data.time, interval = self->interval;
619         char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
620         
621         static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
622                         "plugin", "host", "time", "interval", "meta", NULL};
623         if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
624                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
625                         NULL, &plugin, NULL, &host, &time, &interval, &meta))
626                 return NULL;
628         sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
629         sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
630         sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
631         sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
632         sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
633         FreeAll();
634         if (value_list.type[0] == 0) {
635                 PyErr_SetString(PyExc_RuntimeError, "type not set");
636                 return NULL;
637         }
638         ds = plugin_get_ds(value_list.type);
639         if (ds == NULL) {
640                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
641                 return NULL;
642         }
643         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
644                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
645                 return NULL;
646         }
647         size = (int) PySequence_Length(values);
648         if (size != ds->ds_num) {
649                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
650                 return NULL;
651         }
652         value = malloc(size * sizeof(*value));
653         for (i = 0; i < size; ++i) {
654                 PyObject *item, *num;
655                 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
656                 if (ds->ds->type == DS_TYPE_COUNTER) {
657                         num = PyNumber_Long(item); /* New reference. */
658                         if (num != NULL) {
659                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
660                                 Py_XDECREF(num);
661                         }
662                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
663                         num = PyNumber_Float(item); /* New reference. */
664                         if (num != NULL) {
665                                 value[i].gauge = PyFloat_AsDouble(num);
666                                 Py_XDECREF(num);
667                         }
668                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
669                         /* This might overflow without raising an exception.
670                          * Not much we can do about it */
671                         num = PyNumber_Long(item); /* New reference. */
672                         if (num != NULL) {
673                                 value[i].derive = PyLong_AsLongLong(num);
674                                 Py_XDECREF(num);
675                         }
676                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
677                         /* This might overflow without raising an exception.
678                          * Not much we can do about it */
679                         num = PyNumber_Long(item); /* New reference. */
680                         if (num != NULL) {
681                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
682                                 Py_XDECREF(num);
683                         }
684                 } else {
685                         free(value);
686                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
687                         return NULL;
688                 }
689                 if (PyErr_Occurred() != NULL) {
690                         free(value);
691                         return NULL;
692                 }
693         }
694         value_list.values = value;
695         value_list.values_len = size;
696         value_list.time = DOUBLE_TO_CDTIME_T(time);
697         value_list.interval = DOUBLE_TO_CDTIME_T(interval);
698         value_list.meta = cpy_build_meta(meta);;
699         if (value_list.host[0] == 0)
700                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
701         if (value_list.plugin[0] == 0)
702                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
703         Py_BEGIN_ALLOW_THREADS;
704         ret = plugin_write(dest, NULL, &value_list);
705         Py_END_ALLOW_THREADS;
706         meta_data_destroy(value_list.meta);
707         free(value);
708         if (ret != 0) {
709                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
710                 return NULL;
711         }
712         Py_RETURN_NONE;
715 static PyObject *Values_repr(PyObject *s) {
716         PyObject *ret, *tmp;
717         static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
718         Values *self = (Values *) s;
719         
720         if (l_interval == NULL)
721                 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
722         if (l_values == NULL)
723                 l_values = cpy_string_to_unicode_or_bytes(",values=");
724         if (l_meta == NULL)
725                 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
726         if (l_closing == NULL)
727                 l_closing = cpy_string_to_unicode_or_bytes(")");
728         
729         if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
730                 return NULL;
731         
732         ret = cpy_common_repr(s);
733         if (self->interval != 0) {
734                 CPY_STRCAT(&ret, l_interval);
735                 tmp = PyFloat_FromDouble(self->interval);
736                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
737                 CPY_STRCAT_AND_DEL(&ret, tmp);
738         }
739         if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
740                 CPY_STRCAT(&ret, l_values);
741                 tmp = PyObject_Repr(self->values);
742                 CPY_STRCAT_AND_DEL(&ret, tmp);
743         }
744         if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
745                 CPY_STRCAT(&ret, l_meta);
746                 tmp = PyObject_Repr(self->meta);
747                 CPY_STRCAT_AND_DEL(&ret, tmp);
748         }
749         CPY_STRCAT(&ret, l_closing);
750         return ret;
753 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
754         Values *v = (Values *) self;
755         Py_VISIT(v->values);
756         Py_VISIT(v->meta);
757         return 0;
760 static int Values_clear(PyObject *self) {
761         Values *v = (Values *) self;
762         Py_CLEAR(v->values);
763         Py_CLEAR(v->meta);
764         return 0;
767 static void Values_dealloc(PyObject *self) {
768         Values_clear(self);
769         self->ob_type->tp_free(self);
772 static PyMemberDef Values_members[] = {
773         {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
774         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
775         {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
776         {NULL}
777 };
779 static PyMethodDef Values_methods[] = {
780         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
781         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
782         {NULL}
783 };
785 PyTypeObject ValuesType = {
786         CPY_INIT_TYPE
787         "collectd.Values",         /* tp_name */
788         sizeof(Values),            /* tp_basicsize */
789         0,                         /* Will be filled in later */
790         Values_dealloc,            /* tp_dealloc */
791         0,                         /* tp_print */
792         0,                         /* tp_getattr */
793         0,                         /* tp_setattr */
794         0,                         /* tp_compare */
795         Values_repr,               /* tp_repr */
796         0,                         /* tp_as_number */
797         0,                         /* tp_as_sequence */
798         0,                         /* tp_as_mapping */
799         0,                         /* tp_hash */
800         0,                         /* tp_call */
801         0,                         /* tp_str */
802         0,                         /* tp_getattro */
803         0,                         /* tp_setattro */
804         0,                         /* tp_as_buffer */
805         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
806         Values_doc,                /* tp_doc */
807         Values_traverse,           /* tp_traverse */
808         Values_clear,              /* tp_clear */
809         0,                         /* tp_richcompare */
810         0,                         /* tp_weaklistoffset */
811         0,                         /* tp_iter */
812         0,                         /* tp_iternext */
813         Values_methods,            /* tp_methods */
814         Values_members,            /* tp_members */
815         0,                         /* tp_getset */
816         0,                         /* tp_base */
817         0,                         /* tp_dict */
818         0,                         /* tp_descr_get */
819         0,                         /* tp_descr_set */
820         0,                         /* tp_dictoffset */
821         Values_init,               /* tp_init */
822         0,                         /* tp_alloc */
823         Values_new                 /* tp_new */
824 };
826 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
827                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
829 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
831 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
832                 "It can be used to notify other plugins about bad stuff happening. It works\n"
833                 "similar to Values but has a severity and a message instead of interval\n"
834                 "and time.\n"
835                 "Notifications can be dispatched at any time and can be received with register_notification.";
837 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
838         Notification *self = (Notification *) s;
839         int severity = 0;
840         double time = 0;
841         char *message = NULL;
842         char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
843         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
844                         "plugin", "host", "time", "severity", NULL};
845         
846         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
847                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
848                         NULL, &plugin, NULL, &host, &time, &severity))
849                 return -1;
850         
851         if (type && plugin_get_ds(type) == NULL) {
852                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
853                 FreeAll();
854                 PyMem_Free(message);
855                 return -1;
856         }
858         sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
859         sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
860         sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
861         sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
862         sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
863         sstrncpy(self->message, message ? message : "", sizeof(self->message));
864         self->data.time = time;
865         self->severity = severity;
867         FreeAll();
868         PyMem_Free(message);
869         return 0;
872 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
873         int ret;
874         const data_set_t *ds;
875         notification_t notification;
876         double t = self->data.time;
877         int severity = self->severity;
878         char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
879         char *message = NULL;
880         
881         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
882                         "plugin", "host", "time", "severity", NULL};
883         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
884                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
885                         NULL, &plugin, NULL, &host, &t, &severity))
886                 return NULL;
888         notification.time = DOUBLE_TO_CDTIME_T(t);
889         notification.severity = severity;
890         sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
891         sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
892         sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
893         sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
894         sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
895         sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
896         notification.meta = NULL;
897         FreeAll();
898         PyMem_Free(message);
900         if (notification.type[0] == 0) {
901                 PyErr_SetString(PyExc_RuntimeError, "type not set");
902                 return NULL;
903         }
904         ds = plugin_get_ds(notification.type);
905         if (ds == NULL) {
906                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
907                 return NULL;
908         }
910         if (notification.time == 0)
911                 notification.time = cdtime();
912         if (notification.host[0] == 0)
913                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
914         if (notification.plugin[0] == 0)
915                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
916         Py_BEGIN_ALLOW_THREADS;
917         ret = plugin_dispatch_notification(&notification);
918         Py_END_ALLOW_THREADS;
919         if (ret != 0) {
920                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
921                 return NULL;
922         }
923         Py_RETURN_NONE;
926 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
927         Notification *self;
928         
929         self = (Notification *) PluginData_new(type, args, kwds);
930         if (self == NULL)
931                 return NULL;
932         
933         self->message[0] = 0;
934         self->severity = 0;
935         return (PyObject *) self;
938 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
939         char *old;
940         const char *new;
941         
942         if (value == NULL) {
943                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
944                 return -1;
945         }
946         Py_INCREF(value);
947         new = cpy_unicode_or_bytes_to_string(&value);
948         if (new == NULL) {
949                 Py_DECREF(value);
950                 return -1;
951         }
952         old = ((char *) self) + (intptr_t) data;
953         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
954         Py_DECREF(value);
955         return 0;
958 static PyObject *Notification_repr(PyObject *s) {
959         PyObject *ret, *tmp;
960         static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
961         Notification *self = (Notification *) s;
962         
963         if (l_severity == NULL)
964                 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
965         if (l_message == NULL)
966                 l_message = cpy_string_to_unicode_or_bytes(",message=");
967         if (l_closing == NULL)
968                 l_closing = cpy_string_to_unicode_or_bytes(")");
969         
970         if (l_severity == NULL || l_message == NULL || l_closing == NULL)
971                 return NULL;
972         
973         ret = cpy_common_repr(s);
974         if (self->severity != 0) {
975                 CPY_STRCAT(&ret, l_severity);
976                 tmp = PyInt_FromLong(self->severity);
977                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
978                 CPY_STRCAT_AND_DEL(&ret, tmp);
979         }
980         if (self->message[0] != 0) {
981                 CPY_STRCAT(&ret, l_message);
982                 tmp = cpy_string_to_unicode_or_bytes(self->message);
983                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
984                 CPY_STRCAT_AND_DEL(&ret, tmp);
985         }
986         CPY_STRCAT(&ret, l_closing);
987         return ret;
990 static PyMethodDef Notification_methods[] = {
991         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
992         {NULL}
993 };
995 static PyMemberDef Notification_members[] = {
996         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
997         {NULL}
998 };
1000 static PyGetSetDef Notification_getseters[] = {
1001         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
1002         {NULL}
1003 };
1005 PyTypeObject NotificationType = {
1006         CPY_INIT_TYPE
1007         "collectd.Notification",   /* tp_name */
1008         sizeof(Notification),      /* tp_basicsize */
1009         0,                         /* Will be filled in later */
1010         0,                         /* tp_dealloc */
1011         0,                         /* tp_print */
1012         0,                         /* tp_getattr */
1013         0,                         /* tp_setattr */
1014         0,                         /* tp_compare */
1015         Notification_repr,         /* tp_repr */
1016         0,                         /* tp_as_number */
1017         0,                         /* tp_as_sequence */
1018         0,                         /* tp_as_mapping */
1019         0,                         /* tp_hash */
1020         0,                         /* tp_call */
1021         0,                         /* tp_str */
1022         0,                         /* tp_getattro */
1023         0,                         /* tp_setattro */
1024         0,                         /* tp_as_buffer */
1025         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1026         Notification_doc,          /* tp_doc */
1027         0,                         /* tp_traverse */
1028         0,                         /* tp_clear */
1029         0,                         /* tp_richcompare */
1030         0,                         /* tp_weaklistoffset */
1031         0,                         /* tp_iter */
1032         0,                         /* tp_iternext */
1033         Notification_methods,      /* tp_methods */
1034         Notification_members,      /* tp_members */
1035         Notification_getseters,    /* tp_getset */
1036         0,                         /* tp_base */
1037         0,                         /* tp_dict */
1038         0,                         /* tp_descr_get */
1039         0,                         /* tp_descr_set */
1040         0,                         /* tp_dictoffset */
1041         Notification_init,         /* tp_init */
1042         0,                         /* tp_alloc */
1043         Notification_new           /* tp_new */
1044 };
1046 static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1047                 "to choose the way it is stored in the meta data.";
1049 PyTypeObject SignedType = {
1050         CPY_INIT_TYPE
1051         "collectd.Signed",         /* tp_name */
1052         sizeof(Signed),            /* tp_basicsize */
1053         0,                         /* Will be filled in later */
1054         0,                         /* tp_dealloc */
1055         0,                         /* tp_print */
1056         0,                         /* tp_getattr */
1057         0,                         /* tp_setattr */
1058         0,                         /* tp_compare */
1059         0,                         /* tp_repr */
1060         0,                         /* tp_as_number */
1061         0,                         /* tp_as_sequence */
1062         0,                         /* tp_as_mapping */
1063         0,                         /* tp_hash */
1064         0,                         /* tp_call */
1065         0,                         /* tp_str */
1066         0,                         /* tp_getattro */
1067         0,                         /* tp_setattro */
1068         0,                         /* tp_as_buffer */
1069         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1070         Signed_doc                 /* tp_doc */
1071 };
1073 static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1074                 "to choose the way it is stored in the meta data.";
1076 PyTypeObject UnsignedType = {
1077         CPY_INIT_TYPE
1078         "collectd.Unsigned",       /* tp_name */
1079         sizeof(Unsigned),          /* tp_basicsize */
1080         0,                         /* Will be filled in later */
1081         0,                         /* tp_dealloc */
1082         0,                         /* tp_print */
1083         0,                         /* tp_getattr */
1084         0,                         /* tp_setattr */
1085         0,                         /* tp_compare */
1086         0,                         /* tp_repr */
1087         0,                         /* tp_as_number */
1088         0,                         /* tp_as_sequence */
1089         0,                         /* tp_as_mapping */
1090         0,                         /* tp_hash */
1091         0,                         /* tp_call */
1092         0,                         /* tp_str */
1093         0,                         /* tp_getattro */
1094         0,                         /* tp_setattro */
1095         0,                         /* tp_as_buffer */
1096         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1097         Unsigned_doc               /* tp_doc */
1098 };