Merge branch 'collectd-5.5'
[collectd.git] / src / pyvalues.c
index c6b4032..1097f7f 100644 (file)
 
 #include "cpython.h"
 
+#define FreeAll() do {\
+       PyMem_Free(type);\
+       PyMem_Free(plugin_instance);\
+       PyMem_Free(type_instance);\
+       PyMem_Free(plugin);\
+       PyMem_Free(host);\
+} while(0)
+
 static PyObject *cpy_common_repr(PyObject *s) {
        PyObject *ret, *tmp;
        static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
@@ -56,73 +64,61 @@ static PyObject *cpy_common_repr(PyObject *s) {
        
        ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
 
-       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_type);
+       CPY_STRCAT(&ret, l_type);
        tmp = cpy_string_to_unicode_or_bytes(self->type);
        CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
-       if (tmp)
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-       Py_XDECREF(tmp);
+       CPY_STRCAT_AND_DEL(&ret, tmp);
 
        if (self->type_instance[0] != 0) {
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_type_instance);
+               CPY_STRCAT(&ret, l_type_instance);
                tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
-               if (tmp)
-                       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-               Py_XDECREF(tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
        }
 
        if (self->plugin[0] != 0) {
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_plugin);
+               CPY_STRCAT(&ret, l_plugin);
                tmp = cpy_string_to_unicode_or_bytes(self->plugin);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
-               if (tmp)
-                       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-               Py_XDECREF(tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
        }
 
        if (self->plugin_instance[0] != 0) {
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_plugin_instance);
+               CPY_STRCAT(&ret, l_plugin_instance);
                tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
-               if (tmp)
-                       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-               Py_XDECREF(tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
        }
 
        if (self->host[0] != 0) {
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_host);
+               CPY_STRCAT(&ret, l_host);
                tmp = cpy_string_to_unicode_or_bytes(self->host);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
-               if (tmp)
-                       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-               Py_XDECREF(tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
        }
 
        if (self->time != 0) {
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_time);
-               tmp = PyInt_FromLong(self->time);
+               CPY_STRCAT(&ret, l_time);
+               tmp = PyFloat_FromDouble(self->time);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
-               if (tmp)
-                       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-               Py_XDECREF(tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
        }
        return ret;
 }
 
-static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
+static char time_doc[] = "This is the Unix timestamp of the time this value was read.\n"
                "For dispatching values this can be set to 0 which means \"now\".\n"
                "This means the time the value is actually dispatched, not the time\n"
                "it was set to 0.";
 
 static char host_doc[] = "The hostname of the host this value was read from.\n"
                "For dispatching this can be set to an empty string which means\n"
-               "the local hostname as defined in the collectd.conf.";
+               "the local hostname as defined in collectd.conf.";
 
 static char type_doc[] = "The type of this value. This type has to be defined\n"
-               "in your types.db. Attempting to set it to any other value will\n"
-               "raise a TypeError exception.\n"
-               "Assigning a type is mandetory, calling dispatch without doing\n"
+               "in the types.db file. Attempting to set it to any other value\n"
+               "will raise a TypeError exception.\n"
+               "Assigning a type is mandatory, calling dispatch without doing\n"
                "so will raise a RuntimeError exception.";
 
 static char type_instance_doc[] = "";
@@ -133,7 +129,7 @@ static char plugin_doc[] = "The name of the plugin that read the data. Setting t
 static char plugin_instance_doc[] = "";
 
 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
-               "and Notification. It is pretty useless by itself and was therefore not\n"
+               "and Notification. It is pretty useless by itself and is therefore not\n"
                "exported to the collectd module.";
 
 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
@@ -155,7 +151,7 @@ static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kw
 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
        PluginData *self = (PluginData *) s;
        double time = 0;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "plugin_instance", "type_instance",
                        "plugin", "host", "time", NULL};
        
@@ -163,18 +159,21 @@ static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
        }
 
-       sstrncpy(self->host, host, sizeof(self->host));
-       sstrncpy(self->plugin, plugin, sizeof(self->plugin));
-       sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
-       sstrncpy(self->type, type, sizeof(self->type));
-       sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
-       
+       sstrncpy(self->host, host ? host : "", sizeof(self->host));
+       sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
+       sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->plugin_instance));
+       sstrncpy(self->type, type ? type : "", sizeof(self->type));
+       sstrncpy(self->type_instance, type_instance ? type_instance : "", sizeof(self->type_instance));
        self->time = time;
+
+       FreeAll();
+
        return 0;
 }
 
@@ -189,7 +188,7 @@ static PyObject *PluginData_repr(PyObject *s) {
                return NULL;
        
        ret = cpy_common_repr(s);
-       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
+       CPY_STRCAT(&ret, l_closing);
        return ret;
 }
 
@@ -313,13 +312,21 @@ static char interval_doc[] = "The interval is the timespan in seconds between tw
 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
                "It has to be a sequence (a tuple or list) of numbers.\n"
                "The size of the sequence and the type of its content depend on the type\n"
-               "member your types.db file. For more information on this read the types.db\n"
-               "man page.\n"
+               "member in the types.db file. For more information on this read the\n"
+               "types.db man page.\n"
                "\n"
                "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
                "exception will be raised. If the content of the sequence is not a number,\n"
                "a TypeError exception will be raised.";
 
+static char meta_doc[] = "These are the meta data for this Value object.\n"
+               "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
+               "strings. int and long objects will be dispatched as signed integers unless\n"
+               "they are between 2**63 and 2**64-1, which will result in an unsigned integer.\n"
+               "You can force one of these storage classes by using the classes\n"
+               "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
+               "callback will always contain Signed or Unsigned objects.";
+
 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
                "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
                "\n"
@@ -333,7 +340,7 @@ static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, typ
 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
                "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
                "\n"
-               "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
+               "Write this instance to a single plugin or all plugins if 'destination' is omitted.\n"
                "This will bypass the main collectd process and all filtering and caching.\n"
                "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
                "used instead of 'write'.\n";
@@ -348,36 +355,39 @@ static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                return NULL;
        
        self->values = PyList_New(0);
+       self->meta = PyDict_New();
        self->interval = 0;
        return (PyObject *) self;
 }
 
 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Values *self = (Values *) s;
-       int interval = 0;
-       double time = 0;
-       PyObject *values = NULL, *tmp;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       double interval = 0, time = 0;
+       PyObject *values = NULL, *meta = NULL, *tmp;
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
-                       "plugin", "host", "time", "interval", NULL};
+                       "plugin", "host", "time", "interval", "meta", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
                        NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
-                       NULL, &plugin, NULL, &host, &time, &interval))
+                       NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
        }
 
-       sstrncpy(self->data.host, host, sizeof(self->data.host));
-       sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
-       sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
-       sstrncpy(self->data.type, type, sizeof(self->data.type));
-       sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
        self->data.time = time;
 
+       FreeAll();
+
        if (values == NULL) {
                values = PyList_New(0);
                PyErr_Clear();
@@ -385,81 +395,193 @@ static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
                Py_INCREF(values);
        }
        
+       if (meta == NULL) {
+               meta = PyDict_New();
+               PyErr_Clear();
+       } else {
+               Py_INCREF(meta);
+       }
+       
        tmp = self->values;
        self->values = values;
        Py_XDECREF(tmp);
        
+       tmp = self->meta;
+       self->meta = meta;
+       Py_XDECREF(tmp);
+
        self->interval = interval;
        return 0;
 }
 
+static meta_data_t *cpy_build_meta(PyObject *meta) {
+       int i, s;
+       meta_data_t *m = NULL;
+       PyObject *l;
+       
+       if ((meta == NULL) || (meta == Py_None))
+               return NULL;
+
+       l = PyDict_Items(meta); /* New reference. */
+       if (!l) {
+               cpy_log_exception("building meta data");
+               return NULL;
+       }
+       s = PyList_Size(l);
+       if (s <= 0) {
+               Py_XDECREF(l);
+               return NULL;
+       }
+
+       m = meta_data_create();
+       for (i = 0; i < s; ++i) {
+               const char *string, *keystring;
+               PyObject *key, *value, *item, *tmp;
+               
+               item = PyList_GET_ITEM(l, i);
+               key = PyTuple_GET_ITEM(item, 0);
+               Py_INCREF(key);
+               keystring = cpy_unicode_or_bytes_to_string(&key);
+               if (!keystring) {
+                       PyErr_Clear();
+                       Py_XDECREF(key);
+                       continue;
+               }
+               value = PyTuple_GET_ITEM(item, 1);
+               Py_INCREF(value);
+               if (value == Py_True) {
+                       meta_data_add_boolean(m, keystring, 1);
+               } else if (value == Py_False) {
+                       meta_data_add_boolean(m, keystring, 0);
+               } else if (PyFloat_Check(value)) {
+                       meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
+               } else if (PyObject_TypeCheck(value, &SignedType)) {
+                       long long int lli;
+                       lli = PyLong_AsLongLong(value);
+                       if (!PyErr_Occurred() && (lli == (int64_t) lli))
+                               meta_data_add_signed_int(m, keystring, lli);
+               } else if (PyObject_TypeCheck(value, &UnsignedType)) {
+                       long long unsigned llu;
+                       llu = PyLong_AsUnsignedLongLong(value);
+                       if (!PyErr_Occurred() && (llu == (uint64_t) llu))
+                               meta_data_add_unsigned_int(m, keystring, llu);
+               } else if (PyNumber_Check(value)) {
+                       long long int lli;
+                       long long unsigned llu;
+                       tmp = PyNumber_Long(value);
+                       lli = PyLong_AsLongLong(tmp);
+                       if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
+                               meta_data_add_signed_int(m, keystring, lli);
+                       } else {
+                               PyErr_Clear();
+                               llu = PyLong_AsUnsignedLongLong(tmp);
+                               if (!PyErr_Occurred() && (llu == (uint64_t) llu))
+                                       meta_data_add_unsigned_int(m, keystring, llu);
+                       }
+                       Py_XDECREF(tmp);
+               } else {
+                       string = cpy_unicode_or_bytes_to_string(&value);
+                       if (string) {
+                               meta_data_add_string(m, keystring, string);
+                       } else {
+                               PyErr_Clear();
+                               tmp = PyObject_Str(value);
+                               string = cpy_unicode_or_bytes_to_string(&tmp);
+                               if (string)
+                                       meta_data_add_string(m, keystring, string);
+                               Py_XDECREF(tmp);
+                       }
+               }
+               if (PyErr_Occurred())
+                       cpy_log_exception("building meta data");
+               Py_XDECREF(value);
+               Py_DECREF(key);
+       }
+       Py_XDECREF(l);
+       return m;
+}
+
 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
-       int i, ret;
+       int ret;
        const data_set_t *ds;
-       int size;
+       size_t size, i;
        value_t *value;
        value_list_t value_list = VALUE_LIST_INIT;
-       PyObject *values = self->values;
-       double time = self->data.time;
-       int interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
+       PyObject *values = self->values, *meta = self->meta;
+       double time = self->data.time, interval = self->interval;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
        
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
-                       "plugin", "host", "time", "interval", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
+                       "plugin", "host", "time", "interval", "meta", NULL};
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
                        NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
-                       NULL, &plugin, NULL, &host, &time, &interval))
+                       NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
+               FreeAll();
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
                PyErr_Format(PyExc_TypeError, "values must be list or tuple");
                return NULL;
        }
-       size = (int) PySequence_Length(values);
+       if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
+               PyErr_Format(PyExc_TypeError, "meta must be a dict");
+               return NULL;
+       }
+       size = (size_t) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
                return NULL;
        }
-       value = malloc(size * sizeof(*value));
+       value = calloc(size, sizeof(*value));
        for (i = 0; i < size; ++i) {
                PyObject *item, *num;
-               item = PySequence_GetItem(values, i);
+               item = PySequence_Fast_GET_ITEM(values, (int) i); /* Borrowed reference. */
                if (ds->ds->type == DS_TYPE_COUNTER) {
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].counter = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_GAUGE) {
-                       num = PyNumber_Float(item);
-                       if (num != NULL)
+                       num = PyNumber_Float(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].gauge = PyFloat_AsDouble(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_DERIVE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].derive = PyLong_AsLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].absolute = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -468,15 +590,10 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
                }
        }
        value_list.values = value;
+       value_list.meta = cpy_build_meta(meta);
        value_list.values_len = size;
-       value_list.time = time;
-       value_list.interval = interval;
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
-       value_list.meta = NULL;
+       value_list.time = DOUBLE_TO_CDTIME_T(time);
+       value_list.interval = DOUBLE_TO_CDTIME_T(interval);
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
        if (value_list.plugin[0] == 0)
@@ -484,82 +601,91 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        Py_BEGIN_ALLOW_THREADS;
        ret = plugin_dispatch_values(&value_list);
        Py_END_ALLOW_THREADS;
+       meta_data_destroy(value_list.meta);
+       free(value);
        if (ret != 0) {
                PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
                return NULL;
        }
-       free(value);
        Py_RETURN_NONE;
 }
 
 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
-       int i, ret;
+       int ret;
        const data_set_t *ds;
-       int size;
+       size_t size, i;
        value_t *value;
        value_list_t value_list = VALUE_LIST_INIT;
-       PyObject *values = self->values;
-       double time = self->data.time;
-       int interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *dest = NULL;
+       PyObject *values = self->values, *meta = self->meta;
+       double time = self->data.time, interval = self->interval;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
        
        static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
-                       "plugin", "host", "time", "interval", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
+                       "plugin", "host", "time", "interval", "meta", NULL};
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
                        NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
-                       NULL, &plugin, NULL, &host, &time, &interval))
+                       NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
                PyErr_Format(PyExc_TypeError, "values must be list or tuple");
                return NULL;
        }
-       size = (int) PySequence_Length(values);
+       size = (size_t) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
                return NULL;
        }
-       value = malloc(size * sizeof(*value));
+       value = calloc(size, sizeof(*value));
        for (i = 0; i < size; ++i) {
                PyObject *item, *num;
-               item = PySequence_GetItem(values, i);
+               item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
                if (ds->ds->type == DS_TYPE_COUNTER) {
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].counter = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_GAUGE) {
-                       num = PyNumber_Float(item);
-                       if (num != NULL)
+                       num = PyNumber_Float(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].gauge = PyFloat_AsDouble(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_DERIVE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].derive = PyLong_AsLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
                        /* This might overflow without raising an exception.
                         * Not much we can do about it */
-                       num = PyNumber_Long(item);
-                       if (num != NULL)
+                       num = PyNumber_Long(item); /* New reference. */
+                       if (num != NULL) {
                                value[i].absolute = PyLong_AsUnsignedLongLong(num);
+                               Py_XDECREF(num);
+                       }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -569,14 +695,9 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        }
        value_list.values = value;
        value_list.values_len = size;
-       value_list.time = time;
-       value_list.interval = interval;
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
-       value_list.meta = NULL;
+       value_list.time = DOUBLE_TO_CDTIME_T(time);
+       value_list.interval = DOUBLE_TO_CDTIME_T(interval);
+       value_list.meta = cpy_build_meta(meta);;
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
        if (value_list.plugin[0] == 0)
@@ -584,58 +705,64 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        Py_BEGIN_ALLOW_THREADS;
        ret = plugin_write(dest, NULL, &value_list);
        Py_END_ALLOW_THREADS;
+       meta_data_destroy(value_list.meta);
+       free(value);
        if (ret != 0) {
                PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
                return NULL;
        }
-       free(value);
        Py_RETURN_NONE;
 }
 
 static PyObject *Values_repr(PyObject *s) {
        PyObject *ret, *tmp;
-       static PyObject *l_interval = NULL, *l_values = NULL, *l_closing = NULL;
+       static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
        Values *self = (Values *) s;
        
        if (l_interval == NULL)
                l_interval = cpy_string_to_unicode_or_bytes(",interval=");
        if (l_values == NULL)
                l_values = cpy_string_to_unicode_or_bytes(",values=");
+       if (l_meta == NULL)
+               l_meta = cpy_string_to_unicode_or_bytes(",meta=");
        if (l_closing == NULL)
                l_closing = cpy_string_to_unicode_or_bytes(")");
        
-       if (l_interval == NULL || l_values == NULL || l_closing == NULL)
+       if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
                return NULL;
        
        ret = cpy_common_repr(s);
        if (self->interval != 0) {
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_interval);
-               tmp = PyInt_FromLong(self->interval);
+               CPY_STRCAT(&ret, l_interval);
+               tmp = PyFloat_FromDouble(self->interval);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
-               if (tmp)
-                       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-               Py_XDECREF(tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
        }
-       if (self->values != NULL && PySequence_Length(self->values) > 0) {
-               CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_values);
+       if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
+               CPY_STRCAT(&ret, l_values);
                tmp = PyObject_Repr(self->values);
-               if (tmp)
-                       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
-               Py_XDECREF(tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
        }
-       CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
+       if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
+               CPY_STRCAT(&ret, l_meta);
+               tmp = PyObject_Repr(self->meta);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       CPY_STRCAT(&ret, l_closing);
        return ret;
 }
 
 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
        Values *v = (Values *) self;
        Py_VISIT(v->values);
+       Py_VISIT(v->meta);
        return 0;
 }
 
 static int Values_clear(PyObject *self) {
        Values *v = (Values *) self;
        Py_CLEAR(v->values);
+       Py_CLEAR(v->meta);
        return 0;
 }
 
@@ -645,8 +772,9 @@ static void Values_dealloc(PyObject *self) {
 }
 
 static PyMemberDef Values_members[] = {
-       {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
+       {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
        {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
+       {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
        {NULL}
 };
 
@@ -712,8 +840,8 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Notification *self = (Notification *) s;
        int severity = 0;
        double time = 0;
-       const char *message = "";
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *message = NULL;
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
        
@@ -722,20 +850,24 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin, NULL, &host, &time, &severity))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
+               PyMem_Free(message);
                return -1;
        }
 
-       sstrncpy(self->data.host, host, sizeof(self->data.host));
-       sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
-       sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
-       sstrncpy(self->data.type, type, sizeof(self->data.type));
-       sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
+       sstrncpy(self->message, message ? message : "", sizeof(self->message));
        self->data.time = time;
-
-       sstrncpy(self->message, message, sizeof(self->message));
        self->severity = severity;
+
+       FreeAll();
+       PyMem_Free(message);
        return 0;
 }
 
@@ -745,12 +877,8 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
        notification_t notification;
        double t = self->data.time;
        int severity = self->severity;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *message = self->message;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
+       char *message = NULL;
        
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
@@ -759,27 +887,30 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
                        NULL, &plugin, NULL, &host, &t, &severity))
                return NULL;
 
-       if (type[0] == 0) {
+       notification.time = DOUBLE_TO_CDTIME_T(t);
+       notification.severity = severity;
+       sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
+       sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
+       sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
+       sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
+       sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
+       sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
+       notification.meta = NULL;
+       FreeAll();
+       PyMem_Free(message);
+
+       if (notification.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(notification.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
                return NULL;
        }
 
-       notification.time = t;
-       notification.severity = severity;
-       sstrncpy(notification.message, message, sizeof(notification.message));
-       sstrncpy(notification.host, host, sizeof(notification.host));
-       sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
-       sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
-       sstrncpy(notification.type, type, sizeof(notification.type));
-       sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
-       notification.meta = NULL;
-       if (notification.time < 1)
-               notification.time = time(0);
+       if (notification.time == 0)
+               notification.time = cdtime();
        if (notification.host[0] == 0)
                sstrncpy(notification.host, hostname_g, sizeof(notification.host));
        if (notification.plugin[0] == 0)
@@ -826,19 +957,37 @@ static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
        return 0;
 }
 
-/*static PyObject *Notification_repr(PyObject *s) {
-       PyObject *ret;
+static PyObject *Notification_repr(PyObject *s) {
+       PyObject *ret, *tmp;
+       static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
        Notification *self = (Notification *) s;
        
-       ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
-                       *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
-                       *self->data.plugin ? "',plugin='" : "", self->data.plugin,
-                       *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
-                       *self->data.host ? "',host='" : "", self->data.host,
-                       *self->message ? "',message='" : "", self->message,
-                       (long unsigned) self->data.time, self->severity);
+       if (l_severity == NULL)
+               l_severity = cpy_string_to_unicode_or_bytes(",severity=");
+       if (l_message == NULL)
+               l_message = cpy_string_to_unicode_or_bytes(",message=");
+       if (l_closing == NULL)
+               l_closing = cpy_string_to_unicode_or_bytes(")");
+       
+       if (l_severity == NULL || l_message == NULL || l_closing == NULL)
+               return NULL;
+       
+       ret = cpy_common_repr(s);
+       if (self->severity != 0) {
+               CPY_STRCAT(&ret, l_severity);
+               tmp = PyInt_FromLong(self->severity);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       if (self->message[0] != 0) {
+               CPY_STRCAT(&ret, l_message);
+               tmp = cpy_string_to_unicode_or_bytes(self->message);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       CPY_STRCAT(&ret, l_closing);
        return ret;
-}*/
+}
 
 static PyMethodDef Notification_methods[] = {
        {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
@@ -865,7 +1014,7 @@ PyTypeObject NotificationType = {
        0,                         /* tp_getattr */
        0,                         /* tp_setattr */
        0,                         /* tp_compare */
-       0/*Notification_repr*/,         /* tp_repr */
+       Notification_repr,         /* tp_repr */
        0,                         /* tp_as_number */
        0,                         /* tp_as_sequence */
        0,                         /* tp_as_mapping */
@@ -895,3 +1044,57 @@ PyTypeObject NotificationType = {
        0,                         /* tp_alloc */
        Notification_new           /* tp_new */
 };
+
+static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
+               "to choose the way it is stored in the meta data.";
+
+PyTypeObject SignedType = {
+       CPY_INIT_TYPE
+       "collectd.Signed",         /* tp_name */
+       sizeof(Signed),            /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       0,                         /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       0,                         /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+       Signed_doc                 /* tp_doc */
+};
+
+static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
+               "to choose the way it is stored in the meta data.";
+
+PyTypeObject UnsignedType = {
+       CPY_INIT_TYPE
+       "collectd.Unsigned",       /* tp_name */
+       sizeof(Unsigned),          /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       0,                         /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       0,                         /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+       Unsigned_doc               /* tp_doc */
+};