Merge branch 'ag/curl_xml'
authorFlorian Forster <octo@huhu.verplant.org>
Wed, 13 Jan 2010 15:16:28 +0000 (16:16 +0100)
committerFlorian Forster <octo@huhu.verplant.org>
Wed, 13 Jan 2010 15:16:28 +0000 (16:16 +0100)
18 files changed:
bindings/perl/lib/Collectd.pm
configure.in
contrib/snmp-probe-host.px
src/battery.c
src/collectd-python.pod
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/common.h
src/cpython.h
src/filecount.c
src/meta_data.c
src/meta_data.h
src/pyconfig.c
src/python.c
src/pyvalues.c
src/routeros.c
src/thermal.c

index ef2f3e5..f1b5d85 100644 (file)
@@ -116,6 +116,7 @@ my @fc_plugins : shared = ();
 my %cf_callbacks : shared = ();
 
 my %types = (
+       TYPE_CONFIG,   "config",
        TYPE_INIT,     "init",
        TYPE_READ,     "read",
        TYPE_WRITE,    "write",
index 913b0f6..ded06be 100644 (file)
@@ -2739,7 +2739,7 @@ fi
 if test "x$with_python" = "xyes"
 then
        AC_MSG_CHECKING([for Python CPPFLAGS])
-       python_include_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_python_inc()" | "$with_python_prog" 2>&1`
+       python_include_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_python_inc())" | "$with_python_prog" 2>&1`
        python_config_status=$?
 
        if test "$python_config_status" -ne 0 || test "x$python_include_path" = "x"
@@ -2762,7 +2762,7 @@ fi
 if test "x$with_python" = "xyes"
 then
        AC_MSG_CHECKING([for Python LDFLAGS])
-       python_library_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0)" | "$with_python_prog" 2>&1`
+       python_library_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0))" | "$with_python_prog" 2>&1`
        python_config_status=$?
 
        if test "$python_config_status" -ne 0 || test "x$python_library_path" = "x"
@@ -2777,7 +2777,7 @@ fi
 if test "x$with_python" = "xyes"
 then
        AC_MSG_CHECKING([for Python LIBS])
-       python_library_flags=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0)" | "$with_python_prog" 2>&1`
+       python_library_flags=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0))" | "$with_python_prog" 2>&1`
        python_config_status=$?
 
        if test "$python_config_status" -ne 0 || test "x$python_library_flags" = "x"
index 1d8f975..d1a7a88 100755 (executable)
@@ -1,7 +1,8 @@
 #!/usr/bin/perl
 #
 # collectd - snmp-probe-host.px
-# Copyright (C) 2008  Florian octo Forster
+# Copyright (C) 2008,2009  Florian octo Forster
+# Copyright (C) 2009       noris network AG
 #
 # This program is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by the
@@ -27,6 +28,12 @@ use Config::General ('ParseConfig');
 use Getopt::Long ('GetOptions');
 use Socket6;
 
+our %ExcludeOptions =
+(
+  'IF-MIB64' => qr/^\.?1\.3\.6\.1\.2\.1\.31/,
+  'IF-MIB32' => qr/^\.?1\.3\.6\.1\.2\.1\.2/
+);
+
 sub get_config
 {
   my %conf;
@@ -50,6 +57,7 @@ sub probe_one
 {
   my $sess = shift;
   my $conf = shift;
+  my $excludes = @_ ? shift : [];
   my @oids;
   my $cmd = 'GET';
   my $vl;
@@ -90,6 +98,14 @@ sub probe_one
       $oid_orig = $tmp;
     }
 
+    for (@$excludes)
+    {
+      if ($oid_orig =~ $_)
+      {
+        return;
+      }
+    }
+
     $vb = SNMP::Varbind->new ([$oid_orig]);
 
     if ($cmd eq 'GET')
@@ -148,6 +164,7 @@ sub probe_all
   my $host = shift;
   my $community = shift;
   my $data = shift;
+  my $excludes = @_ ? shift : [];
   my $version = 2;
   my @valid_data = ();
   my $begin;
@@ -203,7 +220,7 @@ sub probe_all
     for (keys %$data)
     {
       my $name = $_;
-      if (probe_one ($sess, $data->{$name}))
+      if (probe_one ($sess, $data->{$name}, $excludes))
       {
         push (@valid_data, $name);
       }
@@ -258,11 +275,24 @@ Options are:
   -C | --config        Path to config file holding the SNMP data blocks.
   -c | --community     SNMP community to use. Default: `public'.
   -h | --help          Print this information and exit.
+  -x | --exclude       Exclude a specific MIB. Call with "help" for more
+                       information.
 
 USAGE
   exit (1);
 }
 
+sub exit_usage_exclude
+{
+  print "Available exclude MIBs:\n\n";
+  for (sort (keys %ExcludeOptions))
+  {
+    print "  $_\n";
+  }
+  print "\n";
+  exit (1);
+}
+
 =head1 NAME
 
 snmp-probe-host.px - Find out what information an SNMP device provides.
@@ -294,6 +324,7 @@ my $file = '/etc/collectd/collectd.conf';
 my $community = 'public';
 my $conf;
 my $working_data;
+my @excludes = ();
 
 =head1 OPTIONS
 
@@ -318,6 +349,19 @@ Defaults to F</etc/collectd/collectd.conf>.
 
 SNMP community to use. Should be pretty straight forward.
 
+=item B<--exclude> I<MIB>
+
+This option can be used to exclude specific data from being enabled in the
+generated config. Currently the following MIBs are understood:
+
+=over 4
+
+=item B<IF-MIB>
+
+Exclude interface information, such as I<ifOctets> and I<ifPackets>.
+
+=back
+
 =back
 
 =cut
@@ -325,6 +369,7 @@ SNMP community to use. Should be pretty straight forward.
 GetOptions ('H|host|hostname=s' => \$host,
   'C|conf|config=s' => \$file,
   'c|community=s' => \$community,
+  'x|exclude=s' => \@excludes,
   'h|help' => \&exit_usage) or die;
 
 if (!$host)
@@ -333,6 +378,28 @@ if (!$host)
   exit (1);
 }
 
+if (@excludes)
+{
+  my $tmp = join (',', @excludes);
+  my @tmp = split (/\s*,\s*/, $tmp);
+
+  @excludes = ();
+  for (@tmp)
+  {
+    my $mib = uc ($_);
+    if ($mib eq 'HELP')
+    {
+      exit_usage_exclude ();
+    }
+    elsif (!exists ($ExcludeOptions{$mib}))
+    {
+      print STDERR "No such MIB: $mib\n";
+      exit_usage_exclude ();
+    }
+    push (@excludes, $ExcludeOptions{$mib});
+  }
+}
+
 $conf = get_config ($file) or die ("Cannot read config");
 
 if (!defined ($conf->{'plugin'})
@@ -343,7 +410,7 @@ if (!defined ($conf->{'plugin'})
   exit (1);
 }
 
-probe_all ($host, $community, $conf->{'plugin'}{'snmp'}{'data'});
+probe_all ($host, $community, $conf->{'plugin'}{'snmp'}{'data'}, \@excludes);
 
 exit (0);
 
index b62ad81..4178d8b 100644 (file)
@@ -514,7 +514,8 @@ static int battery_read (void)
 
        if (0 == access (battery_acpi_dir, R_OK))
                walk_directory (battery_acpi_dir, battery_read_acpi,
-                               /* user_data = */ NULL);
+                               /* user_data = */ NULL,
+                               /* include hidden */ 0);
        else
        {
                char errbuf[1024];
index 021a6c5..8742104 100644 (file)
@@ -27,8 +27,7 @@ for collectd in Python. This is a lot more efficient than executing a
 Python-script every time you want to read a value with the C<exec plugin> (see
 L<collectd-exec(5)>) and provides a lot more functionality, too.
 
-Currently only I<Python 2> is supported and at least I<version 2.3> is
-required.
+At least python I<version 2.3> is required.
 
 =head1 CONFIGURATION
 
@@ -119,6 +118,29 @@ The I<name> identifies the callback.
 
 =back
 
+=head1 STRINGS
+
+There are a lot of places where strings are send from collectd to python and
+from python to collectd. How exactly this works depends on wheather byte or
+unicode strings or python2 or python3 are used.
+
+Python2 has I<str>, which is just bytes, and I<unicode>. Python3 has I<str>,
+which is a unicode object, and I<bytes>.
+
+When passing strings from python to collectd all of these object are supported
+in all places, however I<str> should be used if possible. These strings must
+not contain a NUL byte. Ignoring this will result in a I<TypeError> exception.
+If a byte string was used it will be used as is by collectd. If a unicode
+object was used it will be encoded using the default encoding (see above). If
+this is not possible python will raise a I<UnicodeEncodeError> exception.
+
+Wenn passing strings from collectd to python the behavior depends on the
+python version used. Python2 will always receive a I<str> object. Python3 will
+usually receive a I<str> object as well, however the original string will be
+decoded to unicode using the default encoding. If this fails because the
+string is not a valid sequence for this encoding a I<bytes> object will be
+returned instead.
+
 =head1 WRITING YOUR OWN PLUGINS
 
 Writing your own plugins is quite simple. collectd manages plugins by means of
index 049b07d..8b98325 100644 (file)
@@ -665,6 +665,10 @@ FQDNLookup   true
 #              Password "dozaiTh4"
 #              CollectInterface true
 #              CollectRegistrationTable true
+#              CollectCPULoad true
+#              CollectMemory true
+#              CollectDF true
+#              CollectDisk true
 #      </Router>
 #</Plugin>
 
index ae3172f..45e2fac 100644 (file)
@@ -1208,6 +1208,12 @@ note that there are 1000 bytes in a kilobyte, not 1024.
 
 Controls whether or not to recurse into subdirectories. Enabled by default.
 
+=item B<IncludeHidden> I<true>|I<false>
+
+Controls whether or not to include "hidden" files and directories in the count.
+"Hidden" files and directories are those, whose name begins with a dot.
+Defaults to I<false>, i.e. by default hidden files and directories are ignored.
+
 =back
 
 =head2 Plugin C<GenericJMX>
@@ -3439,6 +3445,8 @@ multiple routers:
       User "collectd"
       Password "secr3t"
       CollectInterface true
+      CollectCPULoad true
+      CollectMemory true
     </Router>
     <Router>
       Host "router1.example.com"
@@ -3446,6 +3454,8 @@ multiple routers:
       Password "5ecret"
       CollectInterface true
       CollectRegistrationTable true
+      CollectDF true
+      CollectDisk true
     </Router>
   </Plugin>
 
@@ -3483,6 +3493,29 @@ present on the device. Defaults to B<false>.
 When set to B<true>, information about wireless LAN connections will be
 collected. Defaults to B<false>.
 
+=item B<CollectCPULoad> B<true>|B<false>
+
+When set to B<true>, information about the CPU usage will be collected. The
+number is a dimensionless value where zero indicates no CPU usage at all.
+Defaults to B<false>.
+
+=item B<CollectMemory> B<true>|B<false>
+
+When enabled, the amount of used and free memory will be collected. How used
+memory is calculated is unknown, for example whether or not caches are counted
+as used space.
+Defaults to B<false>.
+
+=item B<CollectDF> B<true>|B<false>
+
+When enabled, the amount of used and free disk space will be collected.
+Defaults to B<false>.
+
+=item B<CollectDisk> B<true>|B<false>
+
+When enabled, the number of sectors written and bad blocks will be collected.
+Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<rrdcached>
index c6a651d..3695a9b 100644 (file)
@@ -1008,7 +1008,7 @@ int notification_init (notification_t *n, int severity, const char *message,
 } /* int notification_init */
 
 int walk_directory (const char *dir, dirwalk_callback_f callback,
-               void *user_data)
+               void *user_data, int include_hidden)
 {
        struct dirent *ent;
        DIR *dh;
@@ -1029,9 +1029,18 @@ int walk_directory (const char *dir, dirwalk_callback_f callback,
        while ((ent = readdir (dh)) != NULL)
        {
                int status;
-
-               if (ent->d_name[0] == '.')
-                       continue;
+               
+               if (include_hidden)
+               {
+                       if ((strcmp (".", ent->d_name) == 0)
+                                       || (strcmp ("..", ent->d_name) == 0))
+                               continue;
+               }
+               else /* if (!include_hidden) */
+               {
+                       if (ent->d_name[0]=='.')
+                               continue;
+               }
 
                status = (*callback) (dir, ent->d_name, user_data);
                if (status != 0)
index 019e8b6..2d5c794 100644 (file)
@@ -280,7 +280,7 @@ int notification_init (notification_t *n, int severity, const char *message,
 typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
                void *user_data);
 int walk_directory (const char *dir, dirwalk_callback_f callback,
-               void *user_data);
+               void *user_data, int hidden);
 int read_file_contents (const char *filename, char *buf, int bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
index 661bf6a..3e80cb0 100644 (file)
 # define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
 #endif
 
+/* This macro is a shortcut for calls like
+ * x = PyObject_Repr(x);
+ * This can't be done like this example because this would leak
+ * a reference the the original x and crash in case of x == NULL.
+ * This calling syntax is less than elegant but it works, saves
+ * a lot of lines and avoids potential refcount errors. */
+
+#define CPY_SUBSTITUTE(func, a, ...) do {\
+       if ((a) != NULL) {\
+               PyObject *__tmp = (a);\
+               (a) = func(__VA_ARGS__);\
+               Py_DECREF(__tmp);\
+       }\
+} while(0)
+
+/* Python3 compatibility layer. To keep the actual code as clean as possible
+ * do a lot of defines here. */
+
+#if PY_MAJOR_VERSION >= 3
+#define IS_PY3K
+#endif
+
+#ifdef IS_PY3K
+
+#define PyInt_FromLong PyLong_FromLong
+#define CPY_INIT_TYPE         PyVarObject_HEAD_INIT(NULL, 0)
+#define IS_BYTES_OR_UNICODE(o) (PyUnicode_Check(o) || PyBytes_Check(o))
+#define CPY_STRCAT_AND_DEL(a, b) do {\
+       CPY_STRCAT((a), (b));\
+       Py_XDECREF((b));\
+} while (0)
+static inline void CPY_STRCAT(PyObject **a, PyObject *b) {
+       PyObject *ret;
+       
+       if (!a || !*a)
+               return;
+       
+       ret = PyUnicode_Concat(*a, b);
+       Py_DECREF(*a);
+       *a = ret;
+}
+
+#else
+
+#define CPY_INIT_TYPE         PyObject_HEAD_INIT(NULL) 0,
+#define IS_BYTES_OR_UNICODE(o) (PyUnicode_Check(o) || PyString_Check(o))
+#define CPY_STRCAT_AND_DEL PyString_ConcatAndDel
+#define CPY_STRCAT PyString_Concat
+
+#endif
+
+static inline const char *cpy_unicode_or_bytes_to_string(PyObject **o) {
+       if (PyUnicode_Check(*o)) {
+               PyObject *tmp;
+               tmp = PyUnicode_AsEncodedString(*o, NULL, NULL); /* New reference. */
+               if (tmp == NULL)
+                       return NULL;
+               Py_DECREF(*o);
+               *o = tmp;
+       }
+#ifdef IS_PY3K
+       return PyBytes_AsString(*o);
+#else
+       return PyString_AsString(*o);
+#endif
+}
+
+static inline PyObject *cpy_string_to_unicode_or_bytes(const char *buf) {
+#ifdef IS_PY3K
+/* Python3 preferrs unicode */
+       PyObject *ret;
+       ret = PyUnicode_Decode(buf, strlen(buf), NULL, NULL);
+       if (ret != NULL)
+               return ret;
+       PyErr_Clear();
+       return PyBytes_FromString(buf);
+#else
+       return PyString_FromString(buf);
+#endif 
+}
+
+ /* Python object declarations. */
+
 typedef struct {
        PyObject_HEAD        /* No semicolon! */
        PyObject *parent;    /* Config */
index 05bb4b3..47f99e9 100644 (file)
@@ -32,6 +32,7 @@
 #include <fnmatch.h>
 
 #define FC_RECURSIVE 1
+#define FC_HIDDEN 2
 
 struct fc_directory_conf_s
 {
@@ -310,8 +311,8 @@ static int fc_config_add_dir_size (fc_directory_conf_t *dir,
   return (0);
 } /* int fc_config_add_dir_size */
 
-static int fc_config_add_dir_recursive (fc_directory_conf_t *dir,
-    oconfig_item_t *ci)
+static int fc_config_add_dir_option (fc_directory_conf_t *dir,
+    oconfig_item_t *ci, int bit)
 {
   if ((ci->values_num != 1)
       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
@@ -322,12 +323,12 @@ static int fc_config_add_dir_recursive (fc_directory_conf_t *dir,
   }
 
   if (ci->values[0].value.boolean)
-    dir->options |= FC_RECURSIVE;
+    dir->options |= bit;
   else
-    dir->options &= ~FC_RECURSIVE;
+    dir->options &= ~bit;
 
   return (0);
-} /* int fc_config_add_dir_recursive */
+} /* int fc_config_add_dir_option */
 
 static int fc_config_add_dir (oconfig_item_t *ci)
 {
@@ -380,7 +381,9 @@ static int fc_config_add_dir (oconfig_item_t *ci)
     else if (strcasecmp ("Size", option->key) == 0)
       status = fc_config_add_dir_size (dir, option);
     else if (strcasecmp ("Recursive", option->key) == 0)
-      status = fc_config_add_dir_recursive (dir, option);
+      status = fc_config_add_dir_option (dir, option, FC_RECURSIVE);
+    else if (strcasecmp ("IncludeHidden", option->key) == 0)
+      status = fc_config_add_dir_option (dir, option, FC_HIDDEN);
     else
     {
       WARNING ("filecount plugin: fc_config_add_dir: "
@@ -475,7 +478,8 @@ static int fc_read_dir_callback (const char *dirname, const char *filename,
 
   if (S_ISDIR (statbuf.st_mode) && (dir->options & FC_RECURSIVE))
   {
-    status = walk_directory (abs_path, fc_read_dir_callback, dir);
+    status = walk_directory (abs_path, fc_read_dir_callback, dir,
+        /* include hidden = */ (dir->options & FC_HIDDEN) ? 1 : 0);
     return (status);
   }
   else if (!S_ISREG (statbuf.st_mode))
@@ -537,8 +541,9 @@ static int fc_read_dir (fc_directory_conf_t *dir)
 
   if (dir->mtime != 0)
     dir->now = time (NULL);
-
-  status = walk_directory (dir->path, fc_read_dir_callback, dir);
+    
+  status = walk_directory (dir->path, fc_read_dir_callback, dir,
+      /* include hidden */ (dir->options & FC_HIDDEN) ? 1 : 0);
   if (status != 0)
   {
     WARNING ("filecount plugin: walk_directory (%s) failed.", dir->path);
index 3a3f5e7..6a336c4 100644 (file)
 #include <pthread.h>
 
 /*
- * Defines
- */
-#define MD_TYPE_STRING       1
-#define MD_TYPE_SIGNED_INT   2
-#define MD_TYPE_UNSIGNED_INT 3
-#define MD_TYPE_DOUBLE       4
-#define MD_TYPE_BOOLEAN      5
-
-/*
  * Data types
  */
 union meta_value_u
@@ -249,6 +240,49 @@ int meta_data_exists (meta_data_t *md, const char *key) /* {{{ */
   return (0);
 } /* }}} int meta_data_exists */
 
+int meta_data_type (meta_data_t *md, const char *key) /* {{{ */
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return -EINVAL;
+
+  pthread_mutex_lock (&md->lock);
+
+  for (e = md->head; e != NULL; e = e->next)
+  {
+    if (strcasecmp (key, e->key) == 0)
+    {
+      pthread_mutex_unlock (&md->lock);
+      return e->type;
+    }
+  }
+
+  pthread_mutex_unlock (&md->lock);
+  return 0;
+} /* }}} int meta_data_type */
+
+int meta_data_toc (meta_data_t *md, char ***toc) /* {{{ */
+{
+  int i = 0, count = 0;
+  meta_entry_t *e;
+
+  if ((md == NULL) || (toc == NULL))
+    return -EINVAL;
+
+  pthread_mutex_lock (&md->lock);
+
+  for (e = md->head; e != NULL; e = e->next)
+    ++count;    
+
+  *toc = malloc(count * sizeof(**toc));
+  for (e = md->head; e != NULL; e = e->next)
+    (*toc)[i++] = strdup(e->key);
+  
+  pthread_mutex_unlock (&md->lock);
+  return count;
+} /* }}} int meta_data_toc */
+
 int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
 {
   meta_entry_t *this;
index 8e5a785..9ef7b0a 100644 (file)
 
 #include "collectd.h"
 
+/*
+ * Defines
+ */
+#define MD_TYPE_STRING       1
+#define MD_TYPE_SIGNED_INT   2
+#define MD_TYPE_UNSIGNED_INT 3
+#define MD_TYPE_DOUBLE       4
+#define MD_TYPE_BOOLEAN      5
+
 struct meta_data_s;
 typedef struct meta_data_s meta_data_t;
 
@@ -31,6 +40,8 @@ meta_data_t *meta_data_create (void);
 void meta_data_destroy (meta_data_t *md);
 
 int meta_data_exists (meta_data_t *md, const char *key);
+int meta_data_type (meta_data_t *md, const char *key);
+int meta_data_toc (meta_data_t *md, char ***toc);
 int meta_data_delete (meta_data_t *md, const char *key);
 
 int meta_data_add_string (meta_data_t *md,
index bac39ae..b5c01aa 100644 (file)
@@ -74,10 +74,17 @@ static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Config *self = (Config *) s;
        static char *kwlist[] = {"key", "parent", "values", "children", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist,
                        &key, &parent, &values, &children))
                return -1;
        
+       if (!IS_BYTES_OR_UNICODE(key)) {
+               PyErr_SetString(PyExc_TypeError, "argument 1 must be str");
+               Py_XDECREF(parent);
+               Py_XDECREF(values);
+               Py_XDECREF(children);
+               return -1;
+       }
        if (values == NULL) {
                values = PyTuple_New(0);
                PyErr_Clear();
@@ -113,8 +120,28 @@ static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
 
 static PyObject *Config_repr(PyObject *s) {
        Config *self = (Config *) s;
+       PyObject *ret = NULL;
+       static PyObject *node_prefix = NULL, *root_prefix = NULL, *ending = NULL;
+       
+       /* This is ok because we have the GIL, so this is thread-save by default. */
+       if (node_prefix == NULL)
+               node_prefix = cpy_string_to_unicode_or_bytes("<collectd.Config node ");
+       if (root_prefix == NULL)
+               root_prefix = cpy_string_to_unicode_or_bytes("<collectd.Config root node ");
+       if (ending == NULL)
+               ending = cpy_string_to_unicode_or_bytes(">");
+       if (node_prefix == NULL || root_prefix == NULL || ending == NULL)
+               return NULL;
        
-       return PyString_FromFormat("<collectd.Config %snode %s>", self->parent == Py_None ? "root " : "", PyString_AsString(PyObject_Str(self->key)));
+       ret = PyObject_Str(self->key);
+       CPY_SUBSTITUTE(PyObject_Repr, ret, ret);
+       if (self->parent == NULL || self->parent == Py_None)
+               CPY_STRCAT(&ret, root_prefix);
+       else
+               CPY_STRCAT(&ret, node_prefix);
+       CPY_STRCAT(&ret, ending);
+       
+       return ret;
 }
 
 static int Config_traverse(PyObject *self, visitproc visit, void *arg) {
@@ -123,8 +150,7 @@ static int Config_traverse(PyObject *self, visitproc visit, void *arg) {
        Py_VISIT(c->key);
        Py_VISIT(c->values);
        Py_VISIT(c->children);
-       return 0;
-}
+       return 0;}
 
 static int Config_clear(PyObject *self) {
        Config *c = (Config *) self;
@@ -149,8 +175,7 @@ static PyMemberDef Config_members[] = {
 };
 
 PyTypeObject ConfigType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                         /* Always 0 */
+       CPY_INIT_TYPE
        "collectd.Config",         /* tp_name */
        sizeof(Config),            /* tp_basicsize */
        0,                         /* Will be filled in later */
index d750d95..5664b0c 100644 (file)
@@ -245,7 +245,7 @@ static void cpy_build_name(char *buf, size_t size, PyObject *callback, const cha
        
        mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
        if (mod != NULL)
-               module = PyString_AsString(mod);
+               module = cpy_unicode_or_bytes_to_string(&mod);
        
        if (module != NULL) {
                snprintf(buf, size, "python.%s", module);
@@ -268,11 +268,11 @@ static void cpy_log_exception(const char *context) {
        PyErr_NormalizeException(&type, &value, &traceback);
        if (type == NULL) return;
        tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
-       m = PyObject_GetAttrString(value, "message"); /* New reference. */
+       m = PyObject_Str(value); /* New reference. */
        if (tn != NULL)
-               typename = PyString_AsString(tn);
+               typename = cpy_unicode_or_bytes_to_string(&tn);
        if (m != NULL)
-               message = PyString_AsString(m);
+               message = cpy_unicode_or_bytes_to_string(&m);
        if (typename == NULL)
                typename = "NamelessException";
        if (message == NULL)
@@ -301,7 +301,9 @@ static void cpy_log_exception(const char *context) {
                PyObject *line;
                
                line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
-               s = strdup(PyString_AsString(line));
+               Py_INCREF(line);
+               s = strdup(cpy_unicode_or_bytes_to_string(&line));
+               Py_DECREF(line);
                if (s[strlen(s) - 1] == '\n')
                        s[strlen(s) - 1] = 0;
                Py_BEGIN_ALLOW_THREADS
@@ -333,7 +335,8 @@ static int cpy_read_callback(user_data_t *data) {
 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
        int i;
        cpy_callback_t *c = data->data;
-       PyObject *ret, *v, *list;
+       PyObject *ret, *list;
+       Values *v;
 
        CPY_LOCK_THREADS
                list = PyList_New(value_list->values_len); /* New reference. */
@@ -371,10 +374,15 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                                CPY_RETURN_FROM_THREADS 0;
                        }
                }
-               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);
-               Py_DECREF(list);
+               v = PyObject_New(Values, (void *) &ValuesType);
+               sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
+               sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
+               sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
+               sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
+               sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance));
+               v->data.time = value_list->time;
+               v->interval = value_list->interval;
+               v->values = list;
                ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
                if (ret == NULL) {
                        cpy_log_exception("write callback");
@@ -387,12 +395,19 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
 
 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
        cpy_callback_t *c = data->data;
-       PyObject *ret, *n;
+       PyObject *ret;
+       Notification *n;
 
        CPY_LOCK_THREADS
-               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);
+               n = PyObject_New(Notification, (void *) &NotificationType);
+               sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
+               sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
+               sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance));
+               sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
+               sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance));
+               n->data.time = notification->time;
+               sstrncpy(n->message, notification->message, sizeof(n->message));
+               n->severity = notification->severity;
                ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
                if (ret == NULL) {
                        cpy_log_exception("notification callback");
@@ -405,13 +420,14 @@ static int cpy_notification_callback(const notification_t *notification, user_da
 
 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
        cpy_callback_t * c = data->data;
-       PyObject *ret;
+       PyObject *ret, *text;
 
        CPY_LOCK_THREADS
+       text = cpy_string_to_unicode_or_bytes(message);
        if (c->data == NULL)
-               ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. */
        else
-               ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. */
 
        if (ret == NULL) {
                /* FIXME */
@@ -428,13 +444,14 @@ static void cpy_log_callback(int severity, const char *message, user_data_t *dat
 
 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
        cpy_callback_t * c = data->data;
-       PyObject *ret;
+       PyObject *ret, *text;
 
        CPY_LOCK_THREADS
+       text = cpy_string_to_unicode_or_bytes(id);
        if (c->data == NULL)
-               ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iN", timeout, text); /* New reference. */
        else
-               ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iNO", timeout, text, c->data); /* New reference. */
 
        if (ret == NULL) {
                cpy_log_exception("flush callback");
@@ -451,7 +468,7 @@ static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args
        PyObject *callback = NULL, *data = NULL, *mod = NULL;
        static char *kwlist[] = {"callback", "data", "name", NULL};
        
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
@@ -467,7 +484,7 @@ static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args
        c->next = *list_head;
        *list_head = c;
        Py_XDECREF(mod);
-       return PyString_FromString(buf);
+       return cpy_string_to_unicode_or_bytes(buf);
 }
 
 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
@@ -475,7 +492,7 @@ static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject
        const char *plugin = NULL, *identifier = NULL;
        static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
        
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_flush(plugin, timeout, identifier);
        Py_END_ALLOW_THREADS
@@ -501,7 +518,7 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObjec
        PyObject *callback = NULL, *data = NULL;
        static char *kwlist[] = {"callback", "data", "name", NULL};
        
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
@@ -519,7 +536,7 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObjec
        user_data->free_func = cpy_destroy_user_data;
        user_data->data = c;
        register_function(buf, handler, user_data);
-       return PyString_FromString(buf);
+       return cpy_string_to_unicode_or_bytes(buf);
 }
 
 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
@@ -532,7 +549,7 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
        struct timespec ts;
        static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
        
-       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
@@ -552,7 +569,7 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
        ts.tv_sec = interval;
        ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
        plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
-       return PyString_FromString(buf);
+       return cpy_string_to_unicode_or_bytes(buf);
 }
 
 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
@@ -581,7 +598,7 @@ static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject
 
 static PyObject *cpy_error(PyObject *self, PyObject *args) {
        const char *text;
-       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_ERR, "%s", text);
        Py_END_ALLOW_THREADS
@@ -590,7 +607,7 @@ static PyObject *cpy_error(PyObject *self, PyObject *args) {
 
 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
        const char *text;
-       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_WARNING, "%s", text);
        Py_END_ALLOW_THREADS
@@ -599,7 +616,7 @@ static PyObject *cpy_warning(PyObject *self, PyObject *args) {
 
 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
        const char *text;
-       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_NOTICE, "%s", text);
        Py_END_ALLOW_THREADS
@@ -608,7 +625,7 @@ static PyObject *cpy_notice(PyObject *self, PyObject *args) {
 
 static PyObject *cpy_info(PyObject *self, PyObject *args) {
        const char *text;
-       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_INFO, "%s", text);
        Py_END_ALLOW_THREADS
@@ -618,7 +635,7 @@ static PyObject *cpy_info(PyObject *self, PyObject *args) {
 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
 #ifdef COLLECT_DEBUG
        const char *text;
-       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_DEBUG, "%s", text);
        Py_END_ALLOW_THREADS
@@ -631,17 +648,13 @@ static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *ar
        const char *name;
        cpy_callback_t *prev = NULL, *tmp;
 
-       if (PyUnicode_Check(arg)) {
-               arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
-               if (arg == NULL)
-                       return NULL;
-               name = PyString_AsString(arg);
-               Py_DECREF(arg);
-       } else if (PyString_Check(arg)) {
-               name = PyString_AsString(arg);
-       } else {
+       Py_INCREF(arg);
+       name = cpy_unicode_or_bytes_to_string(&arg);
+       if (name == NULL) {
+               PyErr_Clear();
                if (!PyCallable_Check(arg)) {
                        PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+                       Py_DECREF(arg);
                        return NULL;
                }
                cpy_build_name(buf, sizeof(buf), arg, NULL);
@@ -651,6 +664,7 @@ static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *ar
                if (strcmp(name, tmp->name) == 0)
                        break;
        
+       Py_DECREF(arg);
        if (tmp == NULL) {
                PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
                return NULL;
@@ -671,25 +685,24 @@ static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unre
        char buf[512];
        const char *name;
 
-       if (PyUnicode_Check(arg)) {
-               arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
-               if (arg == NULL)
-                       return NULL;
-               name = PyString_AsString(arg);
-               Py_DECREF(arg);
-       } else if (PyString_Check(arg)) {
-               name = PyString_AsString(arg);
-       } else {
+       Py_INCREF(arg);
+       name = cpy_unicode_or_bytes_to_string(&arg);
+       if (name == NULL) {
+               PyErr_Clear();
                if (!PyCallable_Check(arg)) {
                        PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+                       Py_DECREF(arg);
                        return NULL;
                }
                cpy_build_name(buf, sizeof(buf), arg, NULL);
                name = buf;
        }
-       if (unreg(name) == 0)
+       if (unreg(name) == 0) {
+               Py_DECREF(arg);
                Py_RETURN_NONE;
+       }
        PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
+       Py_DECREF(arg);
        return NULL;
 }
 
@@ -860,7 +873,7 @@ static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
        values = PyTuple_New(ci->values_num); /* New reference. */
        for (i = 0; i < ci->values_num; ++i) {
                if (ci->values[i].type == OCONFIG_TYPE_STRING) {
-                       PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
+                       PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
                } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
                        PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
                } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
@@ -868,7 +881,8 @@ static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
                }
        }
        
-       item = PyObject_CallFunction((void *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
+       tmp = cpy_string_to_unicode_or_bytes(ci->key);
+       item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
        if (item == NULL)
                return NULL;
        children = PyTuple_New(ci->children_num); /* New reference. */
@@ -881,6 +895,20 @@ static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
        return item;
 }
 
+#ifdef IS_PY3K
+static struct PyModuleDef collectdmodule = {
+       PyModuleDef_HEAD_INIT,
+       "collectd",   /* name of module */
+       "The python interface to collectd", /* module documentation, may be NULL */
+       -1,
+       cpy_methods
+};
+
+PyMODINIT_FUNC PyInit_collectd(void) {
+       return PyModule_Create(&collectdmodule);
+}
+#endif
+
 static int cpy_config(oconfig_item_t *ci) {
        int i;
        PyObject *sys, *tb;
@@ -893,6 +921,12 @@ static int cpy_config(oconfig_item_t *ci) {
         * python code during the config callback so we have to start
         * the interpreter here. */
        /* Do *not* use the python "thread" module at this point! */
+
+#ifdef IS_PY3K
+       /* Add a builtin module, before Py_Initialize */
+       PyImport_AppendInittab("collectd", PyInit_collectd);
+#endif
+       
        Py_Initialize();
        
        PyType_Ready(&ConfigType);
@@ -912,7 +946,11 @@ static int cpy_config(oconfig_item_t *ci) {
                cpy_log_exception("python initialization");
                return 1;
        }
+#ifdef IS_PY3K
+       module = PyImport_ImportModule("collectd");
+#else
        module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
+#endif
        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. */
@@ -962,7 +1000,7 @@ static int cpy_config(oconfig_item_t *ci) {
                        
                        if (cf_util_get_string(item, &dir) != 0) 
                                continue;
-                       dir_object = PyString_FromString(dir); /* New reference. */
+                       dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
                        if (dir_object == NULL) {
                                ERROR("python plugin: Unable to convert \"%s\" to "
                                      "a python object.", dir);
@@ -987,7 +1025,6 @@ static int cpy_config(oconfig_item_t *ci) {
                        if (module == NULL) {
                                ERROR("python plugin: Error importing module \"%s\".", module_name);
                                cpy_log_exception("importing module");
-                               PyErr_Print();
                        }
                        free(module_name);
                        Py_XDECREF(module);
index d83f541..a632dc1 100644 (file)
 
 #include "cpython.h"
 
+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;
+       static PyObject *l_host = NULL, *l_time = NULL;
+       PluginData *self = (PluginData *) s;
+       
+       if (l_type == NULL)
+               l_type = cpy_string_to_unicode_or_bytes("(type=");
+       if (l_type_instance == NULL)
+               l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
+       if (l_plugin == NULL)
+               l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
+       if (l_plugin_instance == NULL)
+               l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
+       if (l_host == NULL)
+               l_host = cpy_string_to_unicode_or_bytes(",host=");
+       if (l_time == NULL)
+               l_time = cpy_string_to_unicode_or_bytes(",time=");
+       
+       if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
+               return NULL;
+       
+       ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
+
+       CPY_STRCAT(&ret, l_type);
+       tmp = cpy_string_to_unicode_or_bytes(self->type);
+       CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+       CPY_STRCAT_AND_DEL(&ret, tmp);
+
+       if (self->type_instance[0] != 0) {
+               CPY_STRCAT(&ret, l_type_instance);
+               tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->plugin[0] != 0) {
+               CPY_STRCAT(&ret, l_plugin);
+               tmp = cpy_string_to_unicode_or_bytes(self->plugin);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->plugin_instance[0] != 0) {
+               CPY_STRCAT(&ret, l_plugin_instance);
+               tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->host[0] != 0) {
+               CPY_STRCAT(&ret, l_host);
+               tmp = cpy_string_to_unicode_or_bytes(self->host);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+
+       if (self->time != 0) {
+               CPY_STRCAT(&ret, l_time);
+               tmp = PyInt_FromLong(self->time);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, 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"
                "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"
@@ -81,8 +147,8 @@ static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
        static char *kwlist[] = {"type", "plugin_instance", "type_instance",
                        "plugin", "host", "time", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
-                       &plugin_instance, &type_instance, &plugin, &host, &time))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
+                       NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
                return -1;
        
        if (type[0] != 0 && plugin_get_ds(type) == NULL) {
@@ -101,14 +167,18 @@ static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
 }
 
 static PyObject *PluginData_repr(PyObject *s) {
-       PluginData *self = (PluginData *) s;
+       PyObject *ret;
+       static PyObject *l_closing = NULL;
+       
+       if (l_closing == NULL)
+               l_closing = cpy_string_to_unicode_or_bytes(")");
        
-       return PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu)", self->type,
-                       *self->type_instance ? "',type_instance='" : "", self->type_instance,
-                       *self->plugin ? "',plugin='" : "", self->plugin,
-                       *self->plugin_instance ? "',plugin_instance='" : "", self->plugin_instance,
-                       *self->host ? "',host='" : "", self->host,
-                       (long unsigned) self->time);
+       if (l_closing == NULL)
+               return NULL;
+       
+       ret = cpy_common_repr(s);
+       CPY_STRCAT(&ret, l_closing);
+       return ret;
 }
 
 static PyMemberDef PluginData_members[] = {
@@ -119,7 +189,7 @@ static PyMemberDef PluginData_members[] = {
 static PyObject *PluginData_getstring(PyObject *self, void *data) {
        const char *value = ((char *) self) + (intptr_t) data;
        
-       return PyString_FromString(value);
+       return cpy_string_to_unicode_or_bytes(value);
 }
 
 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
@@ -130,10 +200,15 @@ static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
                PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
                return -1;
        }
-       new = PyString_AsString(value);
-       if (new == NULL) return -1;
+       Py_INCREF(value);
+       new = cpy_unicode_or_bytes_to_string(&value);
+       if (new == NULL) {
+               Py_DECREF(value);
+               return -1;
+       }
        old = ((char *) self) + (intptr_t) data;
        sstrncpy(old, new, DATA_MAX_NAME_LEN);
+       Py_DECREF(value);
        return 0;
 }
 
@@ -145,16 +220,22 @@ static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
                PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
                return -1;
        }
-       new = PyString_AsString(value);
-       if (new == NULL) return -1;
+       Py_INCREF(value);
+       new = cpy_unicode_or_bytes_to_string(&value);
+       if (new == NULL) {
+               Py_DECREF(value);
+               return -1;
+       }
 
        if (plugin_get_ds(new) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
+               Py_DECREF(value);
                return -1;
        }
 
        old = ((char *) self) + (intptr_t) data;
        sstrncpy(old, new, DATA_MAX_NAME_LEN);
+       Py_DECREF(value);
        return 0;
 }
 
@@ -168,8 +249,7 @@ static PyGetSetDef PluginData_getseters[] = {
 };
 
 PyTypeObject PluginDataType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                         /* Always 0 */
+       CPY_INIT_TYPE
        "collectd.PluginData",     /* tp_name */
        sizeof(PluginData),        /* tp_basicsize */
        0,                         /* Will be filled in later */
@@ -262,26 +342,30 @@ static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Values *self = (Values *) s;
-       int interval = 0, ret;
+       int interval = 0;
        double time = 0;
        PyObject *values = NULL, *tmp;
        const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
-                       &type, &values, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &interval))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
+                       NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &interval))
                return -1;
        
-       tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
-       if (tmp == NULL)
-               return -1;
-       ret = PluginDataType.tp_init(s, tmp, NULL);
-       Py_DECREF(tmp);
-       if (ret != 0)
+       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
                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));
+       self->data.time = time;
+
        if (values == NULL) {
                values = PyList_New(0);
                PyErr_Clear();
@@ -314,9 +398,9 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
-                       &type, &values, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &interval))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
+                       NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &interval))
                return NULL;
 
        if (type[0] == 0) {
@@ -414,9 +498,9 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        
        static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
-                       &type, &values, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &interval))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
+                       NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &interval))
                return NULL;
 
        if (type[0] == 0) {
@@ -497,22 +581,33 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
 }
 
 static PyObject *Values_repr(PyObject *s) {
-       PyObject *ret, *valuestring = NULL;
+       PyObject *ret, *tmp;
+       static PyObject *l_interval = NULL, *l_values = NULL, *l_closing = NULL;
        Values *self = (Values *) s;
        
-       if (self->values != NULL)
-               valuestring = PyObject_Repr(self->values);
-       if (valuestring == NULL)
+       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_closing == NULL)
+               l_closing = cpy_string_to_unicode_or_bytes(")");
+       
+       if (l_interval == NULL || l_values == NULL || l_closing == NULL)
                return NULL;
        
-       ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", 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,
-                       (long unsigned) self->data.time, self->interval,
-                       valuestring ? PyString_AsString(valuestring) : "[]");
-       Py_XDECREF(valuestring);
+       ret = cpy_common_repr(s);
+       if (self->interval != 0) {
+               CPY_STRCAT(&ret, l_interval);
+               tmp = PyInt_FromLong(self->interval);
+               CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       if (self->values != NULL && PySequence_Length(self->values) > 0) {
+               CPY_STRCAT(&ret, l_values);
+               tmp = PyObject_Repr(self->values);
+               CPY_STRCAT_AND_DEL(&ret, tmp);
+       }
+       CPY_STRCAT(&ret, l_closing);
        return ret;
 }
 
@@ -546,8 +641,7 @@ static PyMethodDef Values_methods[] = {
 };
 
 PyTypeObject ValuesType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                         /* Always 0 */
+       CPY_INIT_TYPE
        "collectd.Values",         /* tp_name */
        sizeof(Values),            /* tp_basicsize */
        0,                         /* Will be filled in later */
@@ -600,27 +694,30 @@ static char Notification_doc[] = "The Notification class is a wrapper around the
 
 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Notification *self = (Notification *) s;
-       PyObject *tmp;
-       int severity = 0, ret;
+       int severity = 0;
        double time = 0;
        const char *message = "";
        const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
-                       &type, &message, &plugin_instance, &type_instance,
-                       &plugin, &host, &time, &severity))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
+                       NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &time, &severity))
                return -1;
        
-       tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
-       if (tmp == NULL)
-               return -1;
-       ret = PluginDataType.tp_init(s, tmp, NULL);
-       Py_DECREF(tmp);
-       if (ret != 0)
+       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
                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));
+       self->data.time = time;
+
        sstrncpy(self->message, message, sizeof(self->message));
        self->severity = severity;
        return 0;
@@ -641,9 +738,9 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
        
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
-                       &type, &message, &plugin_instance, &type_instance,
-                       &plugin, &host, &t, &severity))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
+                       NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
+                       NULL, &plugin, NULL, &host, &t, &severity))
                return NULL;
 
        if (type[0] == 0) {
@@ -701,24 +798,47 @@ static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
                PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
                return -1;
        }
-       new = PyString_AsString(value);
-       if (new == NULL) return -1;
+       Py_INCREF(value);
+       new = cpy_unicode_or_bytes_to_string(&value);
+       if (new == NULL) {
+               Py_DECREF(value);
+               return -1;
+       }
        old = ((char *) self) + (intptr_t) data;
        sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
+       Py_DECREF(value);
        return 0;
 }
 
 static PyObject *Notification_repr(PyObject *s) {
-       PyObject *ret;
+       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;
 }
 
@@ -738,8 +858,7 @@ static PyGetSetDef Notification_getseters[] = {
 };
 
 PyTypeObject NotificationType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                         /* Always 0 */
+       CPY_INIT_TYPE
        "collectd.Notification",   /* tp_name */
        sizeof(Notification),      /* tp_basicsize */
        0,                         /* Will be filled in later */
index 4fe33fa..2512613 100644 (file)
@@ -36,6 +36,10 @@ struct cr_data_s
 
   _Bool collect_interface;
   _Bool collect_regtable;
+  _Bool collect_cpu_load;
+  _Bool collect_memory;
+  _Bool collect_df;
+  _Bool collect_disk;
 };
 typedef struct cr_data_s cr_data_t;
 
@@ -110,6 +114,26 @@ static void cr_submit_gauge (cr_data_t *rd, const char *type, /* {{{ */
        plugin_dispatch_values (&vl);
 } /* }}} void cr_submit_gauge */
 
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+static void cr_submit_counter (cr_data_t *rd, const char *type, /* {{{ */
+    const char *type_instance, counter_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = value;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       sstrncpy (vl.host, rd->node, sizeof (vl.host)); /* FIXME */
+       sstrncpy (vl.plugin, "routeros", sizeof (vl.plugin));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* }}} void cr_submit_gauge */
+#endif
+
 static void submit_regtable (cr_data_t *rd, /* {{{ */
     const ros_registration_table_t *r)
 {
@@ -158,6 +182,44 @@ static int handle_regtable (__attribute__((unused)) ros_connection_t *c, /* {{{
   return (0);
 } /* }}} int handle_regtable */
 
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+static int handle_system_resource (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+        const ros_system_resource_t *r,
+       __attribute__((unused)) void *user_data)
+{
+  cr_data_t *rd;
+
+  if ((r == NULL) || (user_data == NULL))
+    return (EINVAL);
+  rd = user_data;
+
+  if (rd->collect_cpu_load)
+    cr_submit_gauge (rd, "gauge", "cpu_load", (gauge_t) r->cpu_load);
+
+  if (rd->collect_memory)
+  {
+    cr_submit_gauge (rd, "memory", "used",
+       (gauge_t) (r->total_memory - r->free_memory));
+    cr_submit_gauge (rd, "memory", "free", (gauge_t) r->free_memory);
+  }
+
+  if (rd->collect_df)
+  {
+    cr_submit_gauge (rd, "df_complex", "used",
+       (gauge_t) (r->total_memory - r->free_memory));
+    cr_submit_gauge (rd, "df_complex", "free", (gauge_t) r->free_memory);
+  }
+
+  if (rd->collect_disk)
+  {
+    cr_submit_counter (rd, "counter", "secors_written", (counter_t) r->write_sect_total);
+    cr_submit_gauge (rd, "gauge", "bad_blocks", (gauge_t) r->bad_blocks);
+  }
+
+  return (0);
+} /* }}} int handle_system_resource */
+#endif
+
 static int cr_read (user_data_t *user_data) /* {{{ */
 {
   int status;
@@ -214,6 +276,26 @@ static int cr_read (user_data_t *user_data) /* {{{ */
     }
   }
 
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+  if (rd->collect_cpu_load
+      || rd->collect_memory
+      || rd->collect_df
+      || rd->collect_disk)
+  {
+    status = ros_system_resource (rd->connection, handle_system_resource,
+       /* user data = */ rd);
+    if (status != 0)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_system_resource failed: %s",
+         sstrerror (status, errbuf, sizeof (errbuf)));
+      ros_disconnect (rd->connection);
+      rd->connection = NULL;
+      return (-1);
+    }
+  }
+#endif
+
   return (0);
 } /* }}} int cr_read */
 
@@ -250,8 +332,6 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
   router_data->service = NULL;
   router_data->username = NULL;
   router_data->password = NULL;
-  router_data->collect_interface = false;
-  router_data->collect_regtable = false;
 
   status = 0;
   for (i = 0; i < ci->children_num; i++)
@@ -270,6 +350,16 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
       cf_util_get_boolean (child, &router_data->collect_interface);
     else if (strcasecmp ("CollectRegistrationTable", child->key) == 0)
       cf_util_get_boolean (child, &router_data->collect_regtable);
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+    else if (strcasecmp ("CollectCPULoad", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_cpu_load);
+    else if (strcasecmp ("CollectMemory", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_memory);
+    else if (strcasecmp ("CollectDF", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_df);
+    else if (strcasecmp ("CollectDisk", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_disk);
+#endif
     else
     {
       WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
index 2b70805..b9d07bf 100644 (file)
@@ -218,13 +218,13 @@ static int thermal_config (const char *key, const char *value)
 static int thermal_sysfs_read (void)
 {
        return walk_directory (dirname_sysfs, thermal_sysfs_device_read,
-                       /* user_data = */ NULL);
+                       /* user_data = */ NULL, /* include hidden */ 0);
 }
 
 static int thermal_procfs_read (void)
 {
        return walk_directory (dirname_procfs, thermal_procfs_device_read,
-                       /* user_data = */ NULL);
+                       /* user_data = */ NULL, /* include hidden */ 0);
 }
 
 static int thermal_init (void)