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