author | Florian Forster <octo@huhu.verplant.org> | |
Thu, 25 Feb 2010 23:17:18 +0000 (00:17 +0100) | ||
committer | Florian Forster <octo@huhu.verplant.org> | |
Thu, 25 Feb 2010 23:17:18 +0000 (00:17 +0100) |
25 files changed:
AUTHORS | patch | blob | history | |
README | patch | blob | history | |
configure.in | patch | blob | history | |
contrib/collection.cgi | patch | blob | history | |
src/Makefile.am | patch | blob | history | |
src/apache.c | patch | blob | history | |
src/battery.c | patch | blob | history | |
src/collectd-python.pod | patch | blob | history | |
src/collectd.c | patch | blob | history | |
src/collectd.conf.in | patch | blob | history | |
src/collectd.conf.pod | patch | blob | history | |
src/common.c | patch | blob | history | |
src/common.h | patch | blob | history | |
src/cpython.h | patch | blob | history | |
src/curl_xml.c | [new file with mode: 0644] | patch | blob |
src/filecount.c | patch | blob | history | |
src/meta_data.c | patch | blob | history | |
src/meta_data.h | patch | blob | history | |
src/pyconfig.c | patch | blob | history | |
src/python.c | patch | blob | history | |
src/pyvalues.c | patch | blob | history | |
src/routeros.c | patch | blob | history | |
src/swap.c | patch | blob | history | |
src/thermal.c | patch | blob | history | |
src/types.db | patch | blob | history |
index 4b133fa082296c2dc3ca0590e3e5017dc6983931..67c75f0b8bb13a5c73cd4bd377842d5285c81a8b 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
Amit Gupta <amit.gupta221 at gmail.com>
- Multiple servers in the apache plugin.
+ - curl_xml plugin.
Anthony Dewhurst <dewhurst at gmail.com>
- zfs_arc plugin.
index 96fbdf3e6fe496a1cdbb6b6ddae690cb9015a31b..3a56fbedcd8c2082b07cf8c40fd9358b3ddcdf93 100644 (file)
--- a/README
+++ b/README
Retrieves JSON data via cURL and parses it according to user
configuration.
+ - curl_xml
+ Retrieves XML data via cURL and parses it according to user
+ configuration.
+
- dbi
Executes SQL statements on various databases and interprets the returned
data.
diff --git a/configure.in b/configure.in
index b3a7dcc8064820029757a15e244bd505c23b1e1b..a3b2ab6c6c08a6bd3e3ab3743a88370e7fc8b7f0 100644 (file)
--- a/configure.in
+++ b/configure.in
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"
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"
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"
plugin_cpu="no"
plugin_cpufreq="no"
plugin_curl_json="no"
+plugin_curl_xml="no"
plugin_df="no"
plugin_disk="no"
plugin_entropy="no"
plugin_curl_json="yes"
fi
+if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
+then
+ plugin_curl_xml="yes"
+fi
+
if test "x$have_processor_info" = "xyes"
then
plugin_cpu="yes"
AC_PLUGIN([csv], [yes], [CSV output plugin])
AC_PLUGIN([curl], [$with_libcurl], [CURL generic web statistics])
AC_PLUGIN([curl_json], [$plugin_curl_json], [CouchDB statistics])
+AC_PLUGIN([curl_xml], [$plugin_curl_xml], [CURL generic xml statistics])
AC_PLUGIN([dbi], [$with_libdbi], [General database statistics])
AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics])
AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics])
csv . . . . . . . . . $enable_csv
curl . . . . . . . . $enable_curl
curl_json . . . . . . $enable_curl_json
+ curl_xml . . . . . . $enable_curl_xml
dbi . . . . . . . . . $enable_dbi
df . . . . . . . . . $enable_df
disk . . . . . . . . $enable_disk
diff --git a/contrib/collection.cgi b/contrib/collection.cgi
index 100c0c73b2a469d37081b64200bd3e3375ba43e2..af64fb1c2749143463c51ac797db6c239c2f3803 100755 (executable)
--- a/contrib/collection.cgi
+++ b/contrib/collection.cgi
'GPRINT:avg:LAST:%5.1lf%s Last',
'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
],
+ apache_connections => ['DEF:min={file}:count:MIN',
+ 'DEF:avg={file}:count:AVERAGE',
+ 'DEF:max={file}:count:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Connections",
+ 'GPRINT:min:MIN:%6.2lf Min,',
+ 'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+ 'GPRINT:max:MAX:%6.2lf Max,',
+ 'GPRINT:avg:LAST:%6.2lf Last'
+ ],
+ apache_idle_workers => ['DEF:min={file}:count:MIN',
+ 'DEF:avg={file}:count:AVERAGE',
+ 'DEF:max={file}:count:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Idle Workers",
+ 'GPRINT:min:MIN:%6.2lf Min,',
+ 'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+ 'GPRINT:max:MAX:%6.2lf Max,',
+ 'GPRINT:avg:LAST:%6.2lf Last'
+ ],
apache_requests => ['DEF:min={file}:count:MIN',
'DEF:avg={file}:count:AVERAGE',
'DEF:max={file}:count:MAX',
diff --git a/src/Makefile.am b/src/Makefile.am
index d928311a161e4a19f1aeb2edf9b441944464a155..02563509a8439b0e6c3d152c82e78339aedca58a 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
collectd_DEPENDENCIES += curl_json.la
endif
+if BUILD_PLUGIN_CURL_XML
+pkglib_LTLIBRARIES += curl_xml.la
+curl_xml_la_SOURCES = curl_xml.c
+curl_xml_la_LDFLAGS = -module -avoid-version
+curl_xml_la_CFLAGS = $(AM_CFLAGS) \
+ $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+curl_xml_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+collectd_LDADD += "-dlopen" curl_xml.la
+collectd_DEPENDENCIES += curl_xml.la
+endif
+
if BUILD_PLUGIN_DBI
pkglib_LTLIBRARIES += dbi.la
dbi_la_SOURCES = dbi.c \
diff --git a/src/apache.c b/src/apache.c
index ad877b5ff67d10b999c67192730ca14c74b9f20f..39849e6e05d6ebdc7699103957ecbaf2c351211b 100644 (file)
--- a/src/apache.c
+++ b/src/apache.c
else if ((strcmp (fields[0], "BusyServers:") == 0) /* Apache 1.* */
|| (strcmp (fields[0], "BusyWorkers:") == 0) /* Apache 2.* */)
submit_gauge ("apache_connections", NULL, atol (fields[1]), st);
+ else if ((strcmp (fields[0], "IdleServers:") == 0) /* Apache 1.x */
+ || (strcmp (fields[0], "IdleWorkers:") == 0) /* Apache 2.x */)
+ submit_gauge ("apache_idle_workers", NULL, atol (fields[1]), st);
}
}
diff --git a/src/battery.c b/src/battery.c
index b62ad81df35b4686d7844ccd2d565e38e1365e83..4178d8b5145f2b728e5c7c949d72ebd8d126a89e 100644 (file)
--- a/src/battery.c
+++ b/src/battery.c
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 45a06d1d34713d7933ee13607f9913955ec186c7..335f6a91c10a54669e1c5657080b1b90c5192291 100644 (file)
--- a/src/collectd-python.pod
+++ b/src/collectd-python.pod
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
=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
diff --git a/src/collectd.c b/src/collectd.c
index bc69a3b7fc47d2279f63b0d148b210f525bcf825..abab10f93c927eb013355da8d19659c5c69b0bca 100644 (file)
--- a/src/collectd.c
+++ b/src/collectd.c
#endif
" -h Display help (this message)\n"
"\nBuiltin defaults:\n"
- " Config-File "CONFIGFILE"\n"
- " PID-File "PIDFILE"\n"
- " Data-Directory "PKGLOCALSTATEDIR"\n"
+ " Config file "CONFIGFILE"\n"
+ " PID file "PIDFILE"\n"
+ " Plugin directory "PLUGINDIR"\n"
+ " Data directory "PKGLOCALSTATEDIR"\n"
"\n"PACKAGE" "VERSION", http://collectd.org/\n"
"by Florian octo Forster <octo@verplant.org>\n"
"for contributions see `AUTHORS'\n");
diff --git a/src/collectd.conf.in b/src/collectd.conf.in
index 844c83be05ba233202ccdc4069eee4838a481839..888875c2fa8af80c5fc03297b2a93f4c90eece07 100644 (file)
--- a/src/collectd.conf.in
+++ b/src/collectd.conf.in
@LOAD_PLUGIN_CSV@LoadPlugin csv
#@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl
#@BUILD_PLUGIN_CURL_JSON_TRUE@LoadPlugin curl_json
+#@BUILD_PLUGIN_CURL_XML_TRUE@LoadPlugin curl_xml
#@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi
#@BUILD_PLUGIN_DF_TRUE@LoadPlugin df
#@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
# </URL>
#</Plugin>
+#<Plugin "curl_xml">
+# <URL "http://localhost/stats.xml">
+# Host "my_host"
+# Instance "some_instance"
+# User "collectd"
+# Password "thaiNg0I"
+# VerifyPeer true
+# VerifyHost true
+# CACert "/path/to/ca.crt"
+#
+# <XPath "table[@id=\"magic_level\"]/tr">
+# Type "magic_level"
+# #InstancePrefix "prefix-"
+# InstanceFrom "td[1]"
+# ValuesFrom "td[2]/span[@class=\"level\"]"
+# </XPath>
+# </URL>
+#</Plugin>
+
#<Plugin dbi>
# <Query "num_of_customers">
# Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
# Password "dozaiTh4"
# CollectInterface true
# CollectRegistrationTable true
+# CollectCPULoad true
+# CollectMemory true
+# CollectDF true
+# CollectDisk true
# </Router>
#</Plugin>
diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
index 33d4f352fa9830cbdba1e7f83e618a495da133a3..3a6f9a28b55c0001ccd425e54f90013d24401bd7 100644 (file)
--- a/src/collectd.conf.pod
+++ b/src/collectd.conf.pod
@@ -619,6 +619,110 @@ Type-instance to use. Defaults to the current map key or current string array el
=back
+=head2 Plugin C<curl_xml>
+
+The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
+(L<http://xmlsoft.org/>) to retrieve XML data via cURL.
+
+ <Plugin "curl_xml">
+ <URL "http://localhost/stats.xml">
+ Host "my_host"
+ Instance "some_instance"
+ User "collectd"
+ Password "thaiNg0I"
+ VerifyPeer true
+ VerifyHost true
+ CACert "/path/to/ca.crt"
+
+ <XPath "table[@id=\"magic_level\"]/tr">
+ Type "magic_level"
+ #InstancePrefix "prefix-"
+ InstanceFrom "td[1]"
+ ValuesFrom "td[2]/span[@class=\"level\"]"
+ </XPath>
+ </URL>
+ </Plugin>
+
+In the B<Plugin> block, there may be one or more B<URL> blocks, each defining a
+URL to be fetched via HTTP (using libcurl). Within each B<URL> block there are
+options which specify the connection parameters, for example authentication
+information, and one or more B<XPath> blocks.
+
+Each B<XPath> block specifies how to get one type of information. The
+string argument must be a valid XPath expression which returns a list
+of "base elements". One value is dispatched for each "base element". The
+I<type instance> and values are looked up using further I<XPath> expressions
+that should be relative to the base element.
+
+Within the B<URL> block the following options are accepted:
+
+=over 4
+
+=item B<Host> I<Name>
+
+Use I<Name> as the host name when submitting values. Defaults to the global
+host name setting.
+
+=item B<Instance> I<Instance>
+
+Use I<Instance> as the plugin instance when submitting values. Defaults to an
+empty string (no plugin instance).
+
+=item B<User> I<User>
+=item B<Password> I<Password>
+=item B<VerifyPeer> B<true>|B<false>
+=item B<VerifyHost> B<true>|B<false>
+=item B<CACert> I<CA Cert File>
+
+These options behave exactly equivalent to the appropriate options of the
+I<cURL> and I<cURL-JSON> plugins. Please see there for a detailed description.
+
+=item E<lt>B<XPath> I<XPath-expression>E<gt>
+
+Within each B<URL> block, there must be one or more B<XPath> blocks. Each
+B<XPath> block specifies how to get one type of information. The string
+argument must be a valid XPath expression which returns a list of "base
+elements". One value is dispatched for each "base element".
+
+Within the B<XPath> block the following options are accepted:
+
+=over 4
+
+=item B<Type> I<Type>
+
+Specifies the I<Type> used for submitting patches. This determines the number
+of values that are required / expected and whether the strings are parsed as
+signed or unsigned integer or as double values. See L<types.db(5)> for details.
+This option is required.
+
+=item B<InstancePrefix> I<InstancePrefix>
+
+Prefix the I<type instance> with I<InstancePrefix>. The values are simply
+concatenated together without any separator.
+This option is optional.
+
+=item B<InstanceFrom> I<InstanceFrom>
+
+Specifies a XPath expression to use for determining the I<type instance>. The
+XPath expression must return exactly one element. The element's value is then
+used as I<type instance>, possibly prefixed with I<InstancePrefix> (see above).
+
+This value is required. As a special exception, if the "base XPath expression"
+(the argument to the B<XPath> block) returns exactly one argument, then this
+option may be omitted.
+
+=item B<ValuesFrom> I<ValuesFrom> [I<ValuesFrom> ...]
+
+Specifies one or more XPath expression to use for reading the values. The
+number of XPath expressions must match the number of data sources in the
+I<type> specified with B<Type> (see above). Each XPath expression must return
+exactly one element. The element's value is then parsed as a number and used as
+value for the appropriate value in the value list dispatched to the daemon.
+
+=back
+
+=back
+
=head2 Plugin C<dbi>
This plugin uses the B<dbi> library (L<http://libdbi.sourceforge.net/>) to
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>
User "collectd"
Password "secr3t"
CollectInterface true
+ CollectCPULoad true
+ CollectMemory true
</Router>
<Router>
Host "router1.example.com"
Password "5ecret"
CollectInterface true
CollectRegistrationTable true
+ CollectDF true
+ CollectDisk true
</Router>
</Plugin>
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>
diff --git a/src/common.c b/src/common.c
index c6a651dc56248050ccc0b7a50c7aca68e4f2c3f9..8c2aeaea653aefe0f5380783175b4cb75e10fde2 100644 (file)
--- a/src/common.c
+++ b/src/common.c
} /* 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;
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)
return (service_number);
return (-1);
} /* int service_name_to_port_number */
+
+int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
+{
+ derive_t tmp;
+ char *endptr;
+
+ if ((string == NULL) || (ret_value == NULL))
+ return (EINVAL);
+
+ errno = 0;
+ endptr = NULL;
+ tmp = (derive_t) strtoll (string, &endptr, /* base = */ 0);
+ if ((endptr == string) || (errno != 0))
+ return (-1);
+
+ *ret_value = tmp;
+ return (0);
+} /* }}} int strtoderive */
diff --git a/src/common.h b/src/common.h
index 019e8b69f9817e673df2b7f3f673401d91e7579f..fc809f2173d91a1e0c000052a00861e00e267af0 100644 (file)
--- a/src/common.h
+++ b/src/common.h
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);
* (in the range [1-65535]). Returns less than zero on error. */
int service_name_to_port_number (const char *service_name);
+int strtoderive (const char *string, derive_t *ret_value);
+
#endif /* COMMON_H */
diff --git a/src/cpython.h b/src/cpython.h
index 661bf6a20c0e8fce3e8c47a9ee50a2b216cbe82e..3e80cb0c4ee9223dc7a0959db571b6a503a973b3 100644 (file)
--- a/src/cpython.h
+++ b/src/cpython.h
# 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 */
diff --git a/src/curl_xml.c b/src/curl_xml.c
--- /dev/null
+++ b/src/curl_xml.c
@@ -0,0 +1,930 @@
+/**
+ * collectd - src/curl_xml.c
+ * Copyright (C) 2009,2010 Amit Gupta
+ *
+ * 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
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Amit Gupta <amit.gupta221 at gmail.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_llist.h"
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#include <curl/curl.h>
+
+#define CX_DEFAULT_HOST "localhost"
+
+/*
+ * Private data structures
+ */
+struct cx_values_s /* {{{ */
+{
+ char path[DATA_MAX_NAME_LEN];
+ size_t path_len;
+};
+typedef struct cx_values_s cx_values_t;
+/* }}} */
+
+struct cx_xpath_s /* {{{ */
+{
+ char *path;
+ char *type;
+ cx_values_t *values;
+ int values_len;
+ char *instance_prefix;
+ char *instance;
+ int is_table;
+ unsigned long magic;
+};
+typedef struct cx_xpath_s cx_xpath_t;
+/* }}} */
+
+struct cx_s /* {{{ */
+{
+ char *instance;
+ char *host;
+
+ char *url;
+ char *user;
+ char *pass;
+ char *credentials;
+ _Bool verify_peer;
+ _Bool verify_host;
+ char *cacert;
+
+ CURL *curl;
+ char curl_errbuf[CURL_ERROR_SIZE];
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_fill;
+
+ llist_t *list; /* list of xpath blocks */
+};
+typedef struct cx_s cx_t; /* }}} */
+
+/*
+ * Private functions
+ */
+static size_t cx_curl_callback (void *buf, /* {{{ */
+ size_t size, size_t nmemb, void *user_data)
+{
+ size_t len = size * nmemb;
+ cx_t *db;
+
+ db = user_data;
+ if (db == NULL)
+ {
+ ERROR ("curl_xml plugin: cx_curl_callback: "
+ "user_data pointer is NULL.");
+ return (0);
+ }
+
+ if (len <= 0)
+ return (len);
+
+ if ((db->buffer_fill + len) >= db->buffer_size)
+ {
+ char *temp;
+
+ temp = (char *) realloc (db->buffer,
+ db->buffer_fill + len + 1);
+ if (temp == NULL)
+ {
+ ERROR ("curl_xml plugin: realloc failed.");
+ return (0);
+ }
+ db->buffer = temp;
+ db->buffer_size = db->buffer_fill + len + 1;
+ }
+
+ memcpy (db->buffer + db->buffer_fill, (char *) buf, len);
+ db->buffer_fill += len;
+ db->buffer[db->buffer_fill] = 0;
+
+ return (len);
+} /* }}} size_t cx_curl_callback */
+
+static void cx_xpath_free (cx_xpath_t *xpath) /* {{{ */
+{
+ if (xpath == NULL)
+ return;
+
+ sfree (xpath->path);
+ sfree (xpath->type);
+ sfree (xpath->instance_prefix);
+ sfree (xpath->instance);
+ sfree (xpath->values);
+ sfree (xpath);
+} /* }}} void cx_xpath_free */
+
+static void cx_list_free (llist_t *list) /* {{{ */
+{
+ llentry_t *le;
+
+ le = llist_head (list);
+ while (le != NULL)
+ {
+ llentry_t *le_next;
+
+ le_next = le->next;
+
+ sfree (le->key);
+ cx_xpath_free (le->value);
+
+ le = le_next;
+ }
+
+ llist_destroy (list);
+ list = NULL;
+} /* }}} void cx_list_free */
+
+static void cx_free (void *arg) /* {{{ */
+{
+ cx_t *db;
+
+ DEBUG ("curl_xml plugin: cx_free (arg = %p);", arg);
+
+ db = (cx_t *) arg;
+
+ if (db == NULL)
+ return;
+
+ if (db->curl != NULL)
+ curl_easy_cleanup (db->curl);
+ db->curl = NULL;
+
+ if (db->list != NULL)
+ cx_list_free (db->list);
+
+ sfree (db->buffer);
+ sfree (db->instance);
+ sfree (db->host);
+
+ sfree (db->url);
+ sfree (db->user);
+ sfree (db->pass);
+ sfree (db->credentials);
+ sfree (db->cacert);
+
+ sfree (db);
+} /* }}} void cx_free */
+
+static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
+{
+ if (!ds)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' not defined.", xpath->type);
+ return (-1);
+ }
+
+ if (ds->ds_num != xpath->values_len)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' requires %i values, but config talks about %i",
+ xpath->type, ds->ds_num, xpath->values_len);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} cx_check_type */
+
+static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ xmlChar *expr)
+{
+ xmlXPathObjectPtr xpath_obj;
+
+ /* XXX: When to free this? */
+ xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+ if (xpath_obj == NULL)
+ {
+ WARNING ("curl_xml plugin: "
+ "Error unable to evaluate xpath expression \"%s\". Skipping...", expr);
+ return NULL;
+ }
+
+ return xpath_obj;
+} /* }}} cx_evaluate_xpath */
+
+static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */
+{
+ if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE)
+ return (0);
+
+ WARNING ("curl_xml plugin: "
+ "Node \"%s\" doesn't seem to be a text node. Skipping...", node->name);
+ return -1;
+} /* }}} cx_if_not_text_node */
+
+static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl, int index)
+{
+ xmlXPathObjectPtr values_node_obj;
+ xmlNodeSetPtr values_node;
+ int tmp_size;
+ char *node_value;
+
+ values_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->values[index].path);
+ if (values_node_obj == NULL)
+ return (-1); /* Error already logged. */
+
+ values_node = values_node_obj->nodesetval;
+ tmp_size = (values_node) ? values_node->nodeNr : 0;
+
+ if (tmp_size == 0)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only one node. Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute*/
+ if (cx_if_not_text_node(values_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only text/attribute node which is not the case. Skipping...",
+ xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ node_value = (char *) xmlNodeGetContent(values_node->nodeTab[0]);
+ switch (ds->ds[index].type)
+ {
+ case DS_TYPE_COUNTER:
+ vl->values[index].counter = (counter_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_DERIVE:
+ vl->values[index].derive = (derive_t) strtoll (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_ABSOLUTE:
+ vl->values[index].absolute = (absolute_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_GAUGE:
+ vl->values[index].gauge = (gauge_t) strtod (node_value,
+ /* endptr = */ NULL);
+ }
+
+ /* free up object */
+ xmlXPathFreeObject (values_node_obj);
+
+ /* We have reached here which means that
+ * we have got something to work */
+ return (0);
+} /* }}} int cx_handle_single_value_xpath */
+
+static int cx_handle_all_value_xpaths (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl)
+{
+ value_t values[xpath->values_len];
+ int status;
+ int i;
+
+ assert (xpath->values_len > 0);
+ assert (xpath->values_len == vl->values_len);
+ assert (xpath->values_len == ds->ds_num);
+ vl->values = values;
+
+ for (i = 0; i < xpath->values_len; i++)
+ {
+ status = cx_handle_single_value_xpath (xpath_ctx, xpath, ds, vl, i);
+ if (status != 0)
+ return (-1); /* An error has been printed. */
+ } /* for (i = 0; i < xpath->values_len; i++) */
+
+ plugin_dispatch_values (vl);
+ vl->values = NULL;
+
+ return (0);
+} /* }}} int cx_handle_all_value_xpaths */
+
+static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath, value_list_t *vl,
+ _Bool is_table)
+{
+ xmlXPathObjectPtr instance_node_obj = NULL;
+ xmlNodeSetPtr instance_node = NULL;
+
+ memset (vl->type_instance, 0, sizeof (vl->type_instance));
+
+ /* If the base xpath returns more than one block, the result is assumed to be
+ * a table. The `Instnce' option is not optional in this case. Check for the
+ * condition and inform the user. */
+ if (is_table && (vl->type_instance == NULL))
+ {
+ WARNING ("curl_xml plugin: "
+ "Base-XPath %s is a table (more than one result was returned), "
+ "but no instance-XPath has been defined.",
+ xpath->path);
+ return (-1);
+ }
+
+ /* instance has to be an xpath expression */
+ if (xpath->instance != NULL)
+ {
+ int tmp_size;
+
+ instance_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->instance);
+ if (instance_node_obj == NULL)
+ return (-1); /* error is logged already */
+
+ instance_node = instance_node_obj->nodesetval;
+ tmp_size = (instance_node) ? instance_node->nodeNr : 0;
+
+ if ( (tmp_size == 0) && (is_table) )
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
+ "any of the nodes. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
+ "to return only one text node. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute */
+ if (cx_if_not_text_node(instance_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return only text node "
+ "which is not the case. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+ } /* if (xpath->instance != NULL) */
+
+ if (xpath->instance_prefix != NULL)
+ {
+ if (instance_node != NULL)
+ ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s%s",
+ xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0]));
+ else
+ sstrncpy (vl->type_instance, xpath->instance_prefix,
+ sizeof (vl->type_instance));
+ }
+ else
+ {
+ /* If instance_prefix and instance_node are NULL, then
+ * don't set the type_instance */
+ if (instance_node != NULL)
+ sstrncpy (vl->type_instance, (char *) xmlNodeGetContent(instance_node->nodeTab[0]),
+ sizeof (vl->type_instance));
+ }
+
+ /* Free `instance_node_obj' this late, because `instance_node' points to
+ * somewhere inside this structure. */
+ xmlXPathFreeObject (instance_node_obj);
+
+ return (0);
+} /* }}} int cx_handle_instance_xpath */
+
+static int cx_handle_base_xpath (char *plugin_instance, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, const data_set_t *ds,
+ char *base_xpath, cx_xpath_t *xpath)
+{
+ int total_nodes;
+ int i;
+
+ xmlXPathObjectPtr base_node_obj = NULL;
+ xmlNodeSetPtr base_nodes = NULL;
+
+ value_list_t vl = VALUE_LIST_INIT;
+
+ base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath);
+ if (base_node_obj == NULL)
+ return -1; /* error is logged already */
+
+ base_nodes = base_node_obj->nodesetval;
+ total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+
+ if (total_nodes == 0)
+ {
+ ERROR ("curl_xml plugin: "
+ "xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping the xpath block...", base_xpath);
+ xmlXPathFreeObject (base_node_obj);
+ return -1;
+ }
+
+ /* If base_xpath returned multiple results, then */
+ /* Instance in the xpath block is required */
+ if (total_nodes > 1 && xpath->instance == NULL)
+ {
+ ERROR ("curl_xml plugin: "
+ "InstanceFrom is must in xpath block since the base xpath expression \"%s\" "
+ "returned multiple results. Skipping the xpath block...", base_xpath);
+ return -1;
+ }
+
+ /* set the values for the value_list */
+ vl.values_len = ds->ds_num;
+ sstrncpy (vl.type, xpath->type, sizeof (vl.type));
+ sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+
+ for (i = 0; i < total_nodes; i++)
+ {
+ int status;
+
+ xpath_ctx->node = base_nodes->nodeTab[i];
+
+ status = cx_handle_instance_xpath (xpath_ctx, xpath, &vl,
+ /* is_table = */ (total_nodes > 1));
+ if (status != 0)
+ continue; /* An error has already been reported. */
+
+ status = cx_handle_all_value_xpaths (xpath_ctx, xpath, ds, &vl);
+ if (status != 0)
+ continue; /* An error has been logged. */
+ } /* for (i = 0; i < total_nodes; i++) */
+
+ /* free up the allocated memory */
+ xmlXPathFreeObject (base_node_obj);
+
+ return (0);
+} /* }}} cx_handle_base_xpath */
+
+static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, cx_t *db)
+{
+ llentry_t *le;
+ const data_set_t *ds;
+ cx_xpath_t *xpath;
+ int status=-1;
+
+
+ le = llist_head (db->list);
+ while (le != NULL)
+ {
+ /* get the ds */
+ xpath = (cx_xpath_t *) le->value;
+ ds = plugin_get_ds (xpath->type);
+
+ if ( (cx_check_type(ds, xpath) == 0) &&
+ (cx_handle_base_xpath(db->instance, xpath_ctx, ds, le->key, xpath) == 0) )
+ status = 0; /* we got atleast one success */
+
+ le = le->next;
+ } /* while (le != NULL) */
+
+ return status;
+} /* }}} cx_handle_parsed_xml */
+
+static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
+{
+ int status;
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpath_ctx;
+
+ /* Load the XML */
+ doc = xmlParseDoc(xml);
+ if (doc == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to parse the xml document - %s", xml);
+ return (-1);
+ }
+
+ xpath_ctx = xmlXPathNewContext(doc);
+ if(xpath_ctx == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to create the xml context");
+ xmlFreeDoc(doc);
+ return (-1);
+ }
+
+ status = cx_handle_parsed_xml (doc, xpath_ctx, db);
+ /* Cleanup */
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(doc);
+ return status;
+} /* }}} cx_parse_stats_xml */
+
+static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
+{
+ int status;
+ long rc;
+ char *ptr;
+ char *url;
+
+ db->buffer_fill = 0;
+ status = curl_easy_perform (curl);
+
+ curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+
+ if (rc != 200)
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with response code %ld (%s)",
+ rc, url);
+ return (-1);
+ }
+
+ if (status != 0)
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
+ status, db->curl_errbuf, url);
+ return (-1);
+ }
+
+ ptr = db->buffer;
+
+ status = cx_parse_stats_xml(BAD_CAST ptr, db);
+ db->buffer_fill = 0;
+
+ return status;
+} /* }}} int cx_curl_perform */
+
+static int cx_read (user_data_t *ud) /* {{{ */
+{
+ cx_t *db;
+
+ if ((ud == NULL) || (ud->data == NULL))
+ {
+ ERROR ("curl_xml plugin: cx_read: Invalid user data.");
+ return (-1);
+ }
+
+ db = (cx_t *) ud->data;
+
+ return cx_curl_perform (db, db->curl);
+} /* }}} int cx_read */
+
+/* Configuration handling functions {{{ */
+
+static int cx_config_add_values (const char *name, cx_xpath_t *xpath, /* {{{ */
+ oconfig_item_t *ci)
+{
+ int i;
+
+ if (ci->values_num < 1)
+ {
+ WARNING ("curl_xml plugin: `ValuesFrom' needs at least one argument.");
+ return (-1);
+ }
+
+ for (i = 0; i < ci->values_num; i++)
+ if (ci->values[i].type != OCONFIG_TYPE_STRING)
+ {
+ WARNING ("curl_xml plugin: `ValuesFrom' needs only string argument.");
+ return (-1);
+ }
+
+ sfree (xpath->values);
+
+ xpath->values_len = 0;
+ xpath->values = (cx_values_t *) malloc (sizeof (cx_values_t) * ci->values_num);
+ if (xpath->values == NULL)
+ return (-1);
+ xpath->values_len = ci->values_num;
+
+ /* populate cx_values_t structure */
+ for (i = 0; i < ci->values_num; i++)
+ {
+ xpath->values[i].path_len = sizeof (ci->values[i].value.string);
+ sstrncpy (xpath->values[i].path, ci->values[i].value.string, sizeof (xpath->values[i].path));
+ }
+
+ return (0);
+} /* }}} cx_config_add_values */
+
+static int cx_config_add_xpath (cx_t *db, /* {{{ */
+ oconfig_item_t *ci)
+{
+ cx_xpath_t *xpath;
+ int status;
+ int i;
+
+ xpath = (cx_xpath_t *) malloc (sizeof (*xpath));
+ if (xpath == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+ memset (xpath, 0, sizeof (*xpath));
+
+ status = cf_util_get_string (ci, &xpath->path);
+ if (status != 0)
+ {
+ sfree (xpath);
+ return (status);
+ }
+
+ /* error out if xpath->path is an empty string */
+ if (*xpath->path == 0)
+ {
+ ERROR ("curl_xml plugin: invalid xpath. "
+ "xpath value can't be an empty string");
+ return (-1);
+ }
+
+ status = 0;
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Type", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->type);
+ else if (strcasecmp ("InstancePrefix", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->instance_prefix);
+ else if (strcasecmp ("InstanceFrom", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->instance);
+ else if (strcasecmp ("ValuesFrom", child->key) == 0)
+ status = cx_config_add_values ("ValuesFrom", xpath, child);
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ if (status == 0 && xpath->type == NULL)
+ {
+ WARNING ("curl_xml plugin: `Type' missing in `xpath' block.");
+ status = -1;
+ }
+
+ if (status == 0)
+ {
+ char *name;
+ llentry_t *le;
+
+ if (db->list == NULL)
+ {
+ db->list = llist_create();
+ if (db->list == NULL)
+ {
+ ERROR ("curl_xml plugin: list creation failed.");
+ return (-1);
+ }
+ }
+
+ name = strdup(xpath->path);
+ if (name == NULL)
+ {
+ ERROR ("curl_xml plugin: strdup failed.");
+ return (-1);
+ }
+
+ le = llentry_create (name, xpath);
+ if (le == NULL)
+ {
+ ERROR ("curl_xml plugin: llentry_create failed.");
+ return (-1);
+ }
+
+ llist_append (db->list, le);
+ }
+
+ return (status);
+} /* }}} int cx_config_add_xpath */
+
+/* Initialize db->curl */
+static int cx_init_curl (cx_t *db) /* {{{ */
+{
+ db->curl = curl_easy_init ();
+ if (db->curl == NULL)
+ {
+ ERROR ("curl_xml plugin: curl_easy_init failed.");
+ return (-1);
+ }
+
+ curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
+ curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
+ curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
+ PACKAGE_NAME"/"PACKAGE_VERSION);
+ curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
+ curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
+
+ if (db->user != NULL)
+ {
+ size_t credentials_size;
+
+ credentials_size = strlen (db->user) + 2;
+ if (db->pass != NULL)
+ credentials_size += strlen (db->pass);
+
+ db->credentials = (char *) malloc (credentials_size);
+ if (db->credentials == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+
+ ssnprintf (db->credentials, credentials_size, "%s:%s",
+ db->user, (db->pass == NULL) ? "" : db->pass);
+ curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
+ }
+
+ curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L);
+ curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
+ db->verify_host ? 2L : 0L);
+ if (db->cacert != NULL)
+ curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+
+ return (0);
+} /* }}} int cx_init_curl */
+
+static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
+{
+ cx_t *db;
+ int status = 0;
+ int i;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("curl_xml plugin: The `URL' block "
+ "needs exactly one string argument.");
+ return (-1);
+ }
+
+ db = (cx_t *) malloc (sizeof (*db));
+ if (db == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+ memset (db, 0, sizeof (*db));
+
+ if (strcasecmp ("URL", ci->key) == 0)
+ {
+ status = cf_util_get_string (ci, &db->url);
+ if (status != 0)
+ {
+ sfree (db);
+ return (status);
+ }
+ }
+ else
+ {
+ ERROR ("curl_xml plugin: cx_config: "
+ "Invalid key: %s", ci->key);
+ return (-1);
+ }
+
+ /* Fill the `cx_t' structure.. */
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ status = cf_util_get_string (child, &db->instance);
+ else if (strcasecmp ("Host", child->key) == 0)
+ status = cf_util_get_string (child, &db->host);
+ else if (strcasecmp ("User", child->key) == 0)
+ status = cf_util_get_string (child, &db->user);
+ else if (strcasecmp ("Password", child->key) == 0)
+ status = cf_util_get_string (child, &db->pass);
+ else if (strcasecmp ("VerifyPeer", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_peer);
+ else if (strcasecmp ("VerifyHost", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_host);
+ else if (strcasecmp ("CACert", child->key) == 0)
+ status = cf_util_get_string (child, &db->cacert);
+ else if (strcasecmp ("xpath", child->key) == 0)
+ status = cx_config_add_xpath (db, child);
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (status == 0)
+ {
+ if (db->list == NULL)
+ {
+ WARNING ("curl_xml plugin: No (valid) `Key' block "
+ "within `URL' block `%s'.", db->url);
+ status = -1;
+ }
+ if (status == 0)
+ status = cx_init_curl (db);
+ }
+
+ /* If all went well, register this database for reading */
+ if (status == 0)
+ {
+ user_data_t ud;
+ char cb_name[DATA_MAX_NAME_LEN];
+
+ if (db->instance == NULL)
+ db->instance = strdup("default");
+
+ DEBUG ("curl_xml plugin: Registering new read callback: %s",
+ db->instance);
+
+ memset (&ud, 0, sizeof (ud));
+ ud.data = (void *) db;
+ ud.free_func = cx_free;
+
+ ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
+ db->instance, db->url);
+
+ plugin_register_complex_read (cb_name, cx_read,
+ /* interval = */ NULL, &ud);
+ }
+ else
+ {
+ cx_free (db);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cx_config_add_url */
+
+/* }}} End of configuration handling functions */
+
+static int cx_config (oconfig_item_t *ci) /* {{{ */
+{
+ int success;
+ int errors;
+ int status;
+ int i;
+
+ success = 0;
+ errors = 0;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("URL", child->key) == 0)
+ {
+ status = cx_config_add_url (child);
+ if (status == 0)
+ success++;
+ else
+ errors++;
+ }
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ errors++;
+ }
+ }
+
+ if ((success == 0) && (errors > 0))
+ {
+ ERROR ("curl_xml plugin: All statements failed.");
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cx_config */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("curl_xml", cx_config);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/filecount.c b/src/filecount.c
index 05bb4b3792e16b72d83cb8cf00827cf58443c7f7..47f99e91c8e3df2a502208ba6f99f6596afe10e1 100644 (file)
--- a/src/filecount.c
+++ b/src/filecount.c
#include <fnmatch.h>
#define FC_RECURSIVE 1
+#define FC_HIDDEN 2
struct fc_directory_conf_s
{
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))
}
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)
{
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: "
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))
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);
diff --git a/src/meta_data.c b/src/meta_data.c
index 3a3f5e7918b4c44ba8da80367bf0d2597f6eff15..6a336c4b67c734b62cf1516c3375b649fd826cac 100644 (file)
--- a/src/meta_data.c
+++ b/src/meta_data.c
#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
*/
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;
diff --git a/src/meta_data.h b/src/meta_data.h
index 8e5a7852544a43e235919250ccf464de973b05e0..9ef7b0a8ddb58cbf654cca2a3ddc1e722b2d7b2e 100644 (file)
--- a/src/meta_data.h
+++ b/src/meta_data.h
#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;
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,
diff --git a/src/pyconfig.c b/src/pyconfig.c
index bac39ae90b7d1eccb537e5765d5b41648ffad9ff..b5c01aaf1d5481256bf872e6d078c6e4b74dc15c 100644 (file)
--- a/src/pyconfig.c
+++ b/src/pyconfig.c
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();
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) {
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;
};
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 */
diff --git a/src/python.c b/src/python.c
index d750d95b503e7fbbff9e78c375eeef3e809e79be..5664b0c6390095072abcba72faf249e090a1b36d 100644 (file)
--- a/src/python.c
+++ b/src/python.c
@@ -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);
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)
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
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) {
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) {
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
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
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
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
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;
* 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);
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. */
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);
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);
diff --git a/src/pyvalues.c b/src/pyvalues.c
index d83f541bb4a32420a11d516608494c795172acb6..a632dc16061d0005130cdff6f76a83bb5fe1beb8 100644 (file)
--- a/src/pyvalues.c
+++ b/src/pyvalues.c
#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"
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) {
}
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[] = {
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;
}
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;
}
};
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) {
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) {
}
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;
}
};
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;
}
};
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 */
diff --git a/src/routeros.c b/src/routeros.c
index 4fe33fa99bd9b9f0c82374082aedd331b7b8719d..2512613cd19436f74b3550341366b268c8baa853 100644 (file)
--- a/src/routeros.c
+++ b/src/routeros.c
_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;
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;
}
}
+#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 */
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++)
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);
diff --git a/src/swap.c b/src/swap.c
index 7f41c9eae263d664bd6d05dbf0439b6ac486168f..e467818db4ca01f65af8fd66eb0c7780d571fec3 100644 (file)
--- a/src/swap.c
+++ b/src/swap.c
#elif HAVE_PERFSTAT
static int pagesize;
static perfstat_memory_total_t pmemory;
-/*# endif HAVE_PERFSTAT */
+/*# endif HAVE_PERFSTAT */
#else
# error "No applicable input method."
char *fields[8];
int numfields;
+ _Bool old_kernel=0;
+
derive_t swap_used = 0;
derive_t swap_cached = 0;
derive_t swap_free = 0;
if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
{
- char errbuf[1024];
- WARNING ("swap: fopen: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return (-1);
+ // /proc/vmstat does not exist in kernels <2.6
+ if ((fh = fopen ("/proc/stat", "r")) == NULL )
+ {
+ char errbuf[1024];
+ WARNING ("swap: fopen: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ else
+ old_kernel = 1;
}
while (fgets (buffer, 1024, fh) != NULL)
{
- derive_t *val = NULL;
-
- if (strncasecmp (buffer, "pswpin", 6) == 0)
- val = &swap_in;
- else if (strncasecmp (buffer, "pswpout", 7) == 0)
- val = &swap_out;
- else
- continue;
-
- numfields = strsplit (buffer, fields, 8);
-
- if (numfields < 2)
- continue;
-
- *val = (derive_t) atoll (fields[1]);
- }
+ numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+
+ if (!old_kernel)
+ {
+ if (numfields != 2)
+ continue;
+
+ if (strcasecmp ("pswpin", fields[0]) != 0)
+ strtoderive (fields[1], &swap_in);
+ else if (strcasecmp ("pswpout", fields[0]) == 0)
+ strtoderive (fields[1], &swap_out);
+ }
+ else /* if (old_kernel) */
+ {
+ if (numfields != 3)
+ continue;
+
+ if (strcasecmp ("page", fields[0]) == 0)
+ {
+ strtoderive (fields[1], &swap_in);
+ strtoderive (fields[2], &swap_out);
+ }
+ }
+ } /* while (fgets) */
if (fclose (fh))
{
swap_submit ("cached", swap_cached, DS_TYPE_GAUGE);
swap_submit ("in", swap_in, DS_TYPE_DERIVE);
swap_submit ("out", swap_out, DS_TYPE_DERIVE);
-
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKSTAT
* However, Solaris does not allow to allocated/reserved more than the
* available swap (physical memory + disk swap), so the pedant may
* prefer: allocated + unallocated = reserved, available
- *
+ *
* We map the above to: used + resv = n/a, free
*
* Does your brain hurt yet? - Christophe Kalt
diff --git a/src/thermal.c b/src/thermal.c
index 2b708052d707461afa3f9976f3bd9132affa79a6..b9d07bf5ce22fcacd55f5dcac729e4ad665b12d5 100644 (file)
--- a/src/thermal.c
+++ b/src/thermal.c
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)
diff --git a/src/types.db b/src/types.db
index a5872ebf60e1dd8afec676b451b8a3c42711940a..dffb10a78d2ab3ac7ff9a7d6e7afd8e40bc2f36a 100644 (file)
--- a/src/types.db
+++ b/src/types.db
absolute count:ABSOLUTE:0:U
apache_bytes count:COUNTER:0:134217728
apache_connections count:GAUGE:0:65535
+apache_idle_workers count:GAUGE:0:65535
apache_requests count:COUNTER:0:134217728
apache_scoreboard count:GAUGE:0:65535
arc_counts demand_data:COUNTER:0:U, demand_metadata:COUNTER:0:U, prefetch_data:COUNTER:0:U, prefetch_metadata:COUNTER:0:U