python: Fixed some memory leaks in the write and notification callbacks.
[collectd.git] / src / python.c
index 6464970..384ca8b 100644 (file)
@@ -1,3 +1,29 @@
+/**
+ * collectd - src/python.c
+ * Copyright (C) 2009  Sven Trenkel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sven Trenkel <collectd at semidefinite.de>  
+ **/
+
 #include <Python.h>
 #include <structmember.h>
 
@@ -46,13 +72,13 @@ static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifie
                "severity: An integer that should be compared to the LOG_ constants.\n"
                "message: The text to be logged.\n"
                "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was obmitted it will be obmitted here, too.";
+               "    If the parameter was omitted it will be omitted here, too.";
 
 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
                "\n"
                "Register a callback function that will be executed once after the config.\n"
                "file has been read, all plugins heve been loaded and the collectd has\n"
-               "forked into the backgroud.\n"
+               "forked into the background.\n"
                "\n"
                "'callback' is a callable object that will be executed.\n"
                "'data' is an optional object that will be passed back to the callback\n"
@@ -83,7 +109,7 @@ static char reg_config_doc[] = "register_config(callback[, data][, name]) -> ide
                "The callback function will be called with one or two parameters:\n"
                "config: A Config object.\n"
                "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was obmitted it will be obmitted here, too.";
+               "    If the parameter was omitted it will be omitted here, too.";
 
 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
                "\n"
@@ -122,7 +148,7 @@ static char reg_write_doc[] = "register_write(callback[, data][, name]) -> ident
                "The callback function will be called with one or two parameters:\n"
                "values: A Values object which is a copy of the dispatched values.\n"
                "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was obmitted it will be obmitted here, too.";
+               "    If the parameter was omitted it will be omitted here, too.";
 
 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
                "\n"
@@ -141,7 +167,7 @@ static char reg_notification_doc[] = "register_notification(callback[, data][, n
                "The callback function will be called with one or two parameters:\n"
                "notification: A copy of the notification that was dispatched.\n"
                "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was obmitted it will be obmitted here, too.";
+               "    If the parameter was omitted it will be omitted here, too.";
 
 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
                "\n"
@@ -162,7 +188,7 @@ static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> ident
                "    be flushed.\n"
                "id: Specifies which values are to be flushed.\n"
                "data: The optional data parameter passed to the register function.\n"
-               "    If the parameter was obmitted it will be obmitted here, too.";
+               "    If the parameter was omitted it will be omitted here, too.";
 
 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
                "\n"
@@ -276,7 +302,6 @@ static void cpy_log_exception(const char *context) {
                
                line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
                s = strdup(PyString_AsString(line));
-               Py_DECREF(line);
                if (s[strlen(s) - 1] == '\n')
                        s[strlen(s) - 1] = 0;
                Py_BEGIN_ALLOW_THREADS
@@ -343,14 +368,17 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                        }
                        if (PyErr_Occurred() != NULL) {
                                cpy_log_exception("value building for write callback");
+                               Py_DECREF(list);
                                CPY_RETURN_FROM_THREADS 0;
                        }
                }
-               v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
-                               value_list->plugin_instance, value_list->type_instance, value_list->plugin,
-                               value_list->host, (double) value_list->time, value_list->interval);
+               v = PyObject_CallFunction((void *) &ValuesType, "sOssssdi", value_list->type,
+                               list, value_list->plugin_instance, value_list->type_instance,
+                               value_list->plugin, value_list->host, (double) value_list->time,
+                               value_list->interval); /* New reference. */
                Py_DECREF(list);
                ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
+               Py_XDECREF(v);
                if (ret == NULL) {
                        cpy_log_exception("write callback");
                } else {
@@ -365,10 +393,11 @@ static int cpy_notification_callback(const notification_t *notification, user_da
        PyObject *ret, *n;
 
        CPY_LOCK_THREADS
-               n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message,
+               n = PyObject_CallFunction((void *) &NotificationType, "ssssssdi", notification->type, notification->message,
                                notification->plugin_instance, notification->type_instance, notification->plugin,
-                               notification->host, (double) notification->time, notification->severity);
+                               notification->host, (double) notification->time, notification->severity); /* New reference. */
                ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
+               Py_XDECREF(n);
                if (ret == NULL) {
                        cpy_log_exception("notification callback");
                } else {
@@ -531,19 +560,23 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
 }
 
 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds);
+       return cpy_register_generic_userdata((void *) plugin_register_log,
+                       (void *) cpy_log_callback, args, kwds);
 }
 
 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds);
+       return cpy_register_generic_userdata((void *) plugin_register_write,
+                       (void *) cpy_write_callback, args, kwds);
 }
 
 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds);
+       return cpy_register_generic_userdata((void *) plugin_register_notification,
+                       (void *) cpy_notification_callback, args, kwds);
 }
 
 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
-       return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds);
+       return cpy_register_generic_userdata((void *) plugin_register_flush,
+                       (void *) cpy_flush_callback, args, kwds);
 }
 
 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
@@ -799,6 +832,11 @@ static int cpy_init(void) {
        static pthread_t thread;
        sigset_t sigset;
        
+       if (!Py_IsInitialized()) {
+               WARNING("python: Plugin loaded but not configured.");
+               plugin_unregister_shutdown("python");
+               return 0;
+       }
        PyEval_InitThreads();
        /* Now it's finally OK to use python threads. */
        for (c = cpy_init_callbacks; c; c = c->next) {
@@ -839,7 +877,7 @@ static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
                }
        }
        
-       item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
+       item = PyObject_CallFunction((void *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
        if (item == NULL)
                return NULL;
        children = PyTuple_New(ci->children_num); /* New reference. */
@@ -884,9 +922,9 @@ static int cpy_config(oconfig_item_t *ci) {
                return 1;
        }
        module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
-       PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
-       PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
-       PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */
+       PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
+       PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
+       PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
        PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
        PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
        PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);