From: Florian Forster Date: Sat, 6 Nov 2010 11:08:20 +0000 (+0100) Subject: Merge branch 'collectd-4.9' into collectd-4.10 X-Git-Tag: collectd-4.10.2~3 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=171d6a4bd5e05b56be83860701d35bfe1373d6a7;hp=-c;p=collectd.git Merge branch 'collectd-4.9' into collectd-4.10 Conflicts: src/collectd.conf.in src/python.c --- 171d6a4bd5e05b56be83860701d35bfe1373d6a7 diff --combined README index ade430cc,2cb0a6fa..50630690 --- a/README +++ b/README @@@ -57,10 -57,6 +57,10 @@@ Feature 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. @@@ -147,10 -143,6 +147,10 @@@ Memory utilization: Memory occupied by running processes, page cache, buffer cache and free. + - modbus + Reads values from Modbus/TCP enabled devices. Supports reading values + from multiple "slaves" so gateway devices can be used. + - multimeter Information provided by serial multimeters, such as the `Metex M-4650CR'. @@@ -208,10 -200,6 +208,10 @@@ write your own plugins in Perl and return arbitrary values using this API. See collectd-perl(5). + - pinba + Receive and dispatch timing values from Pinba, a profiling extension for + PHP. + - ping Network latency: Time to reach the default gateway or another given host. @@@ -549,10 -537,6 +549,10 @@@ Prerequisite Used by the `memcachec' plugin to connect to a memcache daemon. + * libmodbus (optional) + Used by the `modbus' plugin to communicate with Modbus/TCP devices. + + * libmysqlclient (optional) Unsurprisingly used by the `mysql' plugin. @@@ -586,6 -570,9 +586,9 @@@ Used to capture packets by the `dns' plugin. + * libperfstat (optional) + Used by various plugins to gather statistics under AIX. + * libperl (optional) Obviously used by the `perl' plugin. The library has to be compiled with ithread support (introduced in Perl 5.6.0). @@@ -595,11 -582,6 +598,11 @@@ The PostgreSQL C client library used by the `postgresql' plugin. + * libprotobuf-c, protoc-c (optional) + Used by the `pinba' plugin to generate a parser for the network packets + sent by the Pinba PHP extension. + + * libpython (optional) Used by the `python' plugin. Currently, only 2.3 ≦ Python < 3 is supported. diff --combined configure.in index 75ac706a,8f6eadee..a47fed03 --- a/configure.in +++ b/configure.in @@@ -47,9 -47,6 +47,9 @@@ AC_PROG_LE AC_PROG_YACC PKG_PROG_PKG_CONFIG +AC_CHECK_PROG([have_protoc_c], [protoc-c], [yes], [no]) +AM_CONDITIONAL(HAVE_PROTOC_C, test "x$have_protoc_c" = "xyes") + AC_MSG_CHECKING([for kernel type ($host_os)]) case $host_os in *linux*) @@@ -91,6 -88,10 +91,10 @@@ if test "x$ac_system" = "xSolaris then AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Define to enforce POSIX thread semantics under Solaris.]) fi + if test "x$ac_system" = "xAIX" + then + AC_DEFINE(_THREAD_SAFE_ERRNO, 1, [Define to use the thread-safe version of errno under AIX.]) + fi # Where to install .pc files. pkgconfigdir="${libdir}/pkgconfig" @@@ -480,7 -481,7 +484,7 @@@ AC_HEADER_TIM # Checks for library functions. # AC_PROG_GCC_TRADITIONAL -AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf setenv) +AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf setenv if_indextoname) AC_FUNC_STRERROR_R @@@ -1003,13 -1004,6 +1007,13 @@@ AC_CHECK_MEMBERS([struct net_device_sta #include ]) +AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [], + [], + [ + #include + #include + ]) + AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct kinfo_proc.ki_rusage], [ AC_DEFINE(HAVE_STRUCT_KINFO_PROC_FREEBSD, 1, @@@ -1885,98 -1879,6 +1889,98 @@@ f AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes") # }}} +# --with-libmodbus {{{ +with_libmodbus_config="" +with_libmodbus_cflags="" +with_libmodbus_libs="" +AC_ARG_WITH(libmodbus, [AS_HELP_STRING([--with-libmodbus@<:@=PREFIX@:>@], [Path to the modbus library.])], +[ + if test "x$withval" = "xno" + then + with_libmodbus="no" + else if test "x$withval" = "xyes" + then + with_libmodbus="use_pkgconfig" + else if test -d "$with_libmodbus/lib" + then + AC_MSG_NOTICE([Not checking for libmodbus: Manually configured]) + with_libmodbus_cflags="-I$withval/include" + with_libmodbus_libs="-L$withval/lib -lmodbus" + with_libmodbus="yes" + fi; fi; fi +], +[with_libmodbus="use_pkgconfig"]) + +# configure using pkg-config +if test "x$with_libmodbus" = "xuse_pkgconfig" +then + if test "x$PKG_CONFIG" = "x" + then + with_libmodbus="no (Don't have pkg-config)" + fi +fi +if test "x$with_libmodbus" = "xuse_pkgconfig" +then + AC_MSG_NOTICE([Checking for modbus using $PKG_CONFIG]) + $PKG_CONFIG --exists 'modbus' 2>/dev/null + if test $? -ne 0 + then + with_libmodbus="no (pkg-config doesn't know library)" + fi +fi +if test "x$with_libmodbus" = "xuse_pkgconfig" +then + with_libmodbus_cflags="`$PKG_CONFIG --cflags 'modbus'`" + if test $? -ne 0 + then + with_libmodbus="no ($PKG_CONFIG failed)" + fi + with_libmodbus_libs="`$PKG_CONFIG --libs 'modbus'`" + if test $? -ne 0 + then + with_libmodbus="no ($PKG_CONFIG failed)" + fi +fi +if test "x$with_libmodbus" = "xuse_pkgconfig" +then + with_libmodbus="yes" +fi + +# with_libmodbus_cflags and with_libmodbus_libs are set up now, let's do +# the actual checks. +if test "x$with_libmodbus" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags" + + AC_CHECK_HEADERS(modbus/modbus.h, [], [with_libmodbus="no (modbus/modbus.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_libmodbus" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + + CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags" + LDFLAGS="$LDFLAGS $with_libmodbus_libs" + + AC_CHECK_LIB(modbus, modbus_init_tcp, + [with_libmodbus="yes"], + [with_libmodbus="no (symbol modbus_init_tcp not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_libmodbus" = "xyes" +then + BUILD_WITH_LIBMODBUS_CFLAGS="$with_libmodbus_cflags" + BUILD_WITH_LIBMODBUS_LIBS="$with_libmodbus_libs" + AC_SUBST(BUILD_WITH_LIBMODBUS_CFLAGS) + AC_SUBST(BUILD_WITH_LIBMODBUS_LIBS) +fi +# }}} + # --with-libmysql {{{ with_mysql_config="mysql_config" with_mysql_cflags="" @@@ -2922,7 -2824,7 +2926,7 @@@ f 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" @@@ -2945,7 -2847,7 +2949,7 @@@ f 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" @@@ -2960,7 -2862,7 +2964,7 @@@ f 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" @@@ -3920,7 -3822,7 +3924,7 @@@ AC_DEFUN enable_plugin="yes" force="yes" else - enable_plugin="no" + enable_plugin="no (disabled on command line)" fi; fi ], [ @@@ -3973,7 -3875,6 +3977,7 @@@ plugin_contextswitch="no plugin_cpu="no" plugin_cpufreq="no" plugin_curl_json="no" +plugin_curl_xml="no" plugin_df="no" plugin_disk="no" plugin_entropy="no" @@@ -4113,11 -4014,6 +4117,11 @@@ the 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" @@@ -4271,7 -4167,6 +4275,7 @@@ AC_PLUGIN([cpu], [$plugin_cpu] 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]) @@@ -4302,7 -4197,6 +4306,7 @@@ AC_PLUGIN([mbmon], [yes] AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics]) AC_PLUGIN([memcached], [yes], [memcached statistics]) AC_PLUGIN([memory], [$plugin_memory], [Memory usage]) +AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin]) AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values]) AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics]) AC_PLUGIN([netapp], [$with_libnetapp], [NetApp plugin]) @@@ -4319,8 -4213,6 +4323,8 @@@ AC_PLUGIN([onewire], [$with_libowca AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics]) AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin]) AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter]) +# FIXME: Check for libevent, too. +AC_PLUGIN([pinba], [$have_protoc_c], [Pinba statistics]) AC_PLUGIN([ping], [$with_liboping], [Network latency statistics]) AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics]) AC_PLUGIN([powerdns], [yes], [PowerDNS statistics]) @@@ -4538,7 -4430,6 +4542,7 @@@ Configuration libkstat . . . . . . $with_kstat libkvm . . . . . . . $with_libkvm libmemcached . . . . $with_libmemcached + libmodbus . . . . . . $with_libmodbus libmysql . . . . . . $with_libmysql libnetapp . . . . . . $with_libnetapp libnetlink . . . . . $with_libnetlink @@@ -4562,8 -4453,6 +4566,8 @@@ libxml2 . . . . . . . $with_libxml2 libxmms . . . . . . . $with_libxmms libyajl . . . . . . . $with_libyajl + libevent . . . . . . $with_libevent + protobuf-c . . . . . $have_protoc_c oracle . . . . . . . $with_oracle python . . . . . . . $with_python @@@ -4588,7 -4477,6 +4592,7 @@@ 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 @@@ -4619,7 -4507,6 +4623,7 @@@ memcachec . . . . . . $enable_memcachec memcached . . . . . . $enable_memcached memory . . . . . . . $enable_memory + modbus . . . . . . . $enable_modbus multimeter . . . . . $enable_multimeter mysql . . . . . . . . $enable_mysql netapp . . . . . . . $enable_netapp @@@ -4636,7 -4523,6 +4640,7 @@@ openvpn . . . . . . . $enable_openvpn oracle . . . . . . . $enable_oracle perl . . . . . . . . $enable_perl + pinba . . . . . . . . $enable_pinba ping . . . . . . . . $enable_ping postgresql . . . . . $enable_postgresql powerdns . . . . . . $enable_powerdns diff --combined src/collectd-python.pod index 81a78e7f,cbfd2672..267296cf --- a/src/collectd-python.pod +++ b/src/collectd-python.pod @@@ -1,3 -1,13 +1,13 @@@ + # Permission is hereby granted, free of charge, to any person obtaining a + # copy of this software and associated documentation files (the "Software"), + # to deal in the Software without restriction, including without limitation + # the rights to use, copy, modify, merge, publish, distribute, sublicense, + # and/or sell copies of the Software, and to permit persons to whom the + # Software is furnished to do so, subject to the following conditions: + # + # The above copyright notice and this permission notice shall be included in + # all copies or substantial portions of the Software. + =head1 NAME collectd-python - Documentation of collectd's C @@@ -27,7 -37,8 +37,7 @@@ for collectd in Python. This is a lot m Python-script every time you want to read a value with the C (see L) and provides a lot more functionality, too. -Currently only I is supported and at least I is -required. +At least python I is required. =head1 CONFIGURATION @@@ -105,15 -116,6 +115,15 @@@ environment variable, e.g. I exception which can be ignored. +If you really need to spawn new processes from python you can register an init +callback and reset the action for SIGCHLD to the default behavior. Please note +that this I break the exec plugin. Do not even load the exec plugin if +you intend to do this! + +There is an example script located in B to do +this. If you import this from I SIGCHLD will be handled +normally and spawning processes from python will work as intended. + =back =item EB IE block @@@ -127,29 -129,6 +137,29 @@@ The I 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, which is just bytes, and I. Python3 has I, +which is a unicode object, and I. + +When passing strings from python to collectd all of these object are supported +in all places, however I should be used if possible. These strings must +not contain a NUL byte. Ignoring this will result in a I 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 exception. + +Wenn passing strings from collectd to python the behavior depends on the +python version used. Python2 will always receive a I object. Python3 will +usually receive a I 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 object will be +returned instead. + =head1 WRITING YOUR OWN PLUGINS Writing your own plugins is quite simple. collectd manages plugins by means of @@@ -240,28 -219,6 +250,28 @@@ collectd you're done The following complex types are used to pass values between the Python plugin and collectd: +=head2 Signed + +The Signed class is just a long. It has all its methods and behaves exactly +like any other long object. It is used to indicate if an integer was or should +be stored as a signed or unsigned integer object. + + class Signed(long) + +This is a long by another name. Use it in meta data dicts +to choose the way it is stored in the meta data. + +=head2 Unsigned + +The Unsigned class is just a long. It has all its methods and behaves exactly +like any other long object. It is used to indicate if an integer was or should +be stored as a signed or unsigned integer object. + + class Unsigned(long) + +This is a long by another name. Use it in meta data dicts +to choose the way it is stored in the meta data. + =head2 Config The Config class is an object which keeps the information provided in the @@@ -425,16 -382,6 +435,16 @@@ If the sequence does not have the corre exception will be raised. If the content of the sequence is not a number, a I exception will be raised. +=item meta + +These are the meta data for this Value object. +It has to be a dictionary of numbers, strings or bools. All keys must be +strings. I and objects will be dispatched as signed integers unless +they are between 2**63 and 2**64-1, which will result in a unsigned integer. +You can force one of these storage classes by using the classes +B and B. A meta object received by a write +callback will always contain B or B objects. + =back =head2 Notification @@@ -693,8 -640,14 +703,8 @@@ dispatched by the python plugin after u =item -This plugin is not compatible with python3. Trying to compile it with python3 -will fail because of the ways string, unicode and bytearray behavior was -changed. - -=item - Not all aspects of the collectd API are accessible from python. This includes -but is not limited to meta-data, filters and data sets. +but is not limited to filters and data sets. =back diff --combined src/collectd.h index 8849b30b,31aae540..e7fc4e35 --- a/src/collectd.h +++ b/src/collectd.h @@@ -227,10 -227,6 +227,6 @@@ typedef bool _Bool # include #endif - #if HAVE_SENSORS_SENSORS_H - # include - #endif - #ifndef PACKAGE_NAME #define PACKAGE_NAME "collectd" #endif @@@ -300,6 -296,5 +296,6 @@@ extern char hostname_g[]; extern int interval_g; +extern int timeout_g; #endif /* COLLECTD_H */ diff --combined src/curl_json.c index 21deed61,0cf72ac3..0527dc8d --- a/src/curl_json.c +++ b/src/curl_json.c @@@ -671,7 -671,7 +671,7 @@@ static int cj_config_add_url (oconfig_i ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s", db->instance, db->url); - plugin_register_complex_read (cb_name, cj_read, + plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read, /* interval = */ NULL, &ud); } else @@@ -764,6 -764,7 +764,7 @@@ static int cj_curl_perform (cj_t *db, C if (db->yajl == NULL) { ERROR ("curl_json plugin: yajl_alloc failed."); + db->yajl = yprev; return (-1); } diff --combined src/netapp.c index 317b0fe4,1615c497..c50b3dbd --- a/src/netapp.c +++ b/src/netapp.c @@@ -566,7 -566,7 +566,7 @@@ static int submit_values (const char *h const char *plugin_inst, const char *type, const char *type_inst, value_t *values, int values_len, - time_t timestamp) + time_t timestamp, int interval) { value_list_t vl = VALUE_LIST_INIT; @@@ -576,6 -576,9 +576,9 @@@ if (timestamp > 0) vl.time = timestamp; + if (interval > 0) + vl.interval = interval; + if (host != NULL) sstrncpy (vl.host, host, sizeof (vl.host)); else @@@ -592,7 -595,7 +595,7 @@@ static int submit_two_counters (const char *host, const char *plugin_inst, /* {{{ */ const char *type, const char *type_inst, counter_t val0, counter_t val1, - time_t timestamp) + time_t timestamp, int interval) { value_t values[2]; @@@ -600,23 -603,23 +603,23 @@@ values[1].counter = val1; return (submit_values (host, plugin_inst, type, type_inst, - values, 2, timestamp)); + values, 2, timestamp, interval)); } /* }}} int submit_two_counters */ static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */ - const char *type, const char *type_inst, counter_t counter, time_t timestamp) + const char *type, const char *type_inst, counter_t counter, time_t timestamp, int interval) { value_t v; v.counter = counter; return (submit_values (host, plugin_inst, type, type_inst, - &v, 1, timestamp)); + &v, 1, timestamp, interval)); } /* }}} int submit_counter */ static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ */ const char *type, const char *type_inst, gauge_t val0, gauge_t val1, - time_t timestamp) + time_t timestamp, int interval) { value_t values[2]; @@@ -624,18 -627,18 +627,18 @@@ values[1].gauge = val1; return (submit_values (host, plugin_inst, type, type_inst, - values, 2, timestamp)); + values, 2, timestamp, interval)); } /* }}} int submit_two_gauge */ static int submit_double (const char *host, const char *plugin_inst, /* {{{ */ - const char *type, const char *type_inst, double d, time_t timestamp) + const char *type, const char *type_inst, double d, time_t timestamp, int interval) { value_t v; v.gauge = (gauge_t) d; return (submit_values (host, plugin_inst, type, type_inst, - &v, 1, timestamp)); + &v, 1, timestamp, interval)); } /* }}} int submit_uint64 */ /* Calculate hit ratio from old and new counters and submit the resulting @@@ -647,7 -650,8 +650,8 @@@ static int submit_cache_ratio (const ch uint64_t new_misses, uint64_t old_hits, uint64_t old_misses, - time_t timestamp) + time_t timestamp, + int interval) { value_t v; @@@ -664,12 -668,12 +668,12 @@@ } return (submit_values (host, plugin_inst, "cache_ratio", type_inst, - &v, 1, timestamp)); + &v, 1, timestamp, interval)); } /* }}} int submit_cache_ratio */ /* Submits all the caches used by WAFL. Uses "submit_cache_ratio". */ static int submit_wafl_data (const char *hostname, const char *instance, /* {{{ */ - cfg_wafl_t *old_data, const cfg_wafl_t *new_data) + cfg_wafl_t *old_data, const cfg_wafl_t *new_data, int interval) { /* Submit requested counters */ if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE) @@@ -677,28 -681,28 +681,28 @@@ submit_cache_ratio (hostname, instance, "name_cache_hit", new_data->name_cache_hit, new_data->name_cache_miss, old_data->name_cache_hit, old_data->name_cache_miss, - new_data->timestamp); + new_data->timestamp, interval); if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_DIR_CACHE | HAVE_WAFL_FIND_DIR) && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_FIND_DIR)) submit_cache_ratio (hostname, instance, "find_dir_hit", new_data->find_dir_hit, new_data->find_dir_miss, old_data->find_dir_hit, old_data->find_dir_miss, - new_data->timestamp); + new_data->timestamp, interval); if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_BUF_CACHE | HAVE_WAFL_BUF_HASH) && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_BUF_HASH)) submit_cache_ratio (hostname, instance, "buf_hash_hit", new_data->buf_hash_hit, new_data->buf_hash_miss, old_data->buf_hash_hit, old_data->buf_hash_miss, - new_data->timestamp); + new_data->timestamp, interval); if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_INODE_CACHE | HAVE_WAFL_INODE_CACHE) && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_INODE_CACHE)) submit_cache_ratio (hostname, instance, "inode_cache_hit", new_data->inode_cache_hit, new_data->inode_cache_miss, old_data->inode_cache_hit, old_data->inode_cache_miss, - new_data->timestamp); + new_data->timestamp, interval); /* Clear old HAVE_* flags */ old_data->flags &= ~HAVE_WAFL_ALL; @@@ -724,7 -728,7 +728,7 @@@ * update flags appropriately. */ static int submit_volume_perf_data (const char *hostname, /* {{{ */ data_volume_perf_t *old_data, - const data_volume_perf_t *new_data) + const data_volume_perf_t *new_data, int interval) { char plugin_instance[DATA_MAX_NAME_LEN]; @@@ -739,7 -743,7 +743,7 @@@ && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | HAVE_VOLUME_PERF_BYTES_WRITE)) { submit_two_counters (hostname, plugin_instance, "disk_octets", /* type instance = */ NULL, - (counter_t) new_data->read_bytes, (counter_t) new_data->write_bytes, new_data->timestamp); + (counter_t) new_data->read_bytes, (counter_t) new_data->write_bytes, new_data->timestamp, interval); } /* Check for and submit disk-operations values */ @@@ -747,7 -751,7 +751,7 @@@ && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE)) { submit_two_counters (hostname, plugin_instance, "disk_ops", /* type instance = */ NULL, - (counter_t) new_data->read_ops, (counter_t) new_data->write_ops, new_data->timestamp); + (counter_t) new_data->read_ops, (counter_t) new_data->write_ops, new_data->timestamp, interval); } /* Check for, calculate and submit disk-latency values */ @@@ -791,7 -795,7 +795,7 @@@ } submit_two_gauge (hostname, plugin_instance, "disk_latency", /* type instance = */ NULL, - latency_per_op_read, latency_per_op_write, new_data->timestamp); + latency_per_op_read, latency_per_op_write, new_data->timestamp, interval); } /* Clear all HAVE_* flags. */ @@@ -820,7 -824,7 +824,7 @@@ */ /* Data corresponding to */ static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* {{{ */ - na_elem_t *data) + na_elem_t *data, int interval) { cfg_wafl_t perf_data; const char *plugin_inst; @@@ -899,7 -903,7 +903,7 @@@ } } - return (submit_wafl_data (hostname, plugin_inst, cfg_wafl, &perf_data)); + return (submit_wafl_data (hostname, plugin_inst, cfg_wafl, &perf_data, interval)); } /* }}} void cna_handle_wafl_data */ static int cna_setup_wafl (cfg_wafl_t *cw) /* {{{ */ @@@ -973,7 -977,7 +977,7 @@@ static int cna_query_wafl (host_config_ return (-1); } - status = cna_handle_wafl_data (host->name, host->cfg_wafl, data); + status = cna_handle_wafl_data (host->name, host->cfg_wafl, data, host->interval); if (status == 0) host->cfg_wafl->interval.last_read = now; @@@ -984,7 -988,7 +988,7 @@@ /* Data corresponding to */ static int cna_handle_disk_data (const char *hostname, /* {{{ */ - cfg_disk_t *cfg_disk, na_elem_t *data) + cfg_disk_t *cfg_disk, na_elem_t *data, int interval) { time_t timestamp; na_elem_t *instances; @@@ -1098,7 -1102,7 +1102,7 @@@ if ((cfg_disk->flags & CFG_DISK_BUSIEST) && (worst_disk != NULL)) submit_double (hostname, "system", "percent", "disk_busy", - worst_disk->disk_busy_percent, timestamp); + worst_disk->disk_busy_percent, timestamp, interval); return (0); } /* }}} int cna_handle_disk_data */ @@@ -1168,7 -1172,7 +1172,7 @@@ static int cna_query_disk (host_config_ return (-1); } - status = cna_handle_disk_data (host->name, host->cfg_disk, data); + status = cna_handle_disk_data (host->name, host->cfg_disk, data, host->interval); if (status == 0) host->cfg_disk->interval.last_read = now; @@@ -1179,7 -1183,7 +1183,7 @@@ /* Data corresponding to */ static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */ - cfg_volume_perf_t *cvp, na_elem_t *data) + cfg_volume_perf_t *cvp, na_elem_t *data, int interval) { time_t timestamp; na_elem_t *elem_instances; @@@ -1264,7 -1268,7 +1268,7 @@@ } } /* for (elem_counter) */ - submit_volume_perf_data (hostname, v, &perf_data); + submit_volume_perf_data (hostname, v, &perf_data, interval); } /* for (volume) */ return (0); @@@ -1339,7 -1343,7 +1343,7 @@@ static int cna_query_volume_perf (host_ return (-1); } - status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data); + status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data, host->interval); if (status == 0) host->cfg_volume_perf->interval.last_read = now; @@@ -1350,7 -1354,7 +1354,7 @@@ /* Data corresponding to */ static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */ - cfg_volume_usage_t *cfg_volume) + cfg_volume_usage_t *cfg_volume, int interval) { data_volume_usage_t *v; @@@ -1398,32 -1402,32 +1402,32 @@@ if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_FREE)) submit_double (hostname, /* plugin instance = */ plugin_instance, "df_complex", "free", - (double) norm_free, /* timestamp = */ 0); + (double) norm_free, /* timestamp = */ 0, interval); if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SIS_SAVED)) submit_double (hostname, /* plugin instance = */ plugin_instance, "df_complex", "sis_saved", - (double) sis_saved, /* timestamp = */ 0); + (double) sis_saved, /* timestamp = */ 0, interval); if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED)) submit_double (hostname, /* plugin instance = */ plugin_instance, "df_complex", "used", - (double) norm_used, /* timestamp = */ 0); + (double) norm_used, /* timestamp = */ 0, interval); if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_RSVD)) submit_double (hostname, /* plugin instance = */ plugin_instance, "df_complex", "snap_reserved", - (double) snap_reserve_free, /* timestamp = */ 0); + (double) snap_reserve_free, /* timestamp = */ 0, interval); if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD)) submit_double (hostname, /* plugin instance = */ plugin_instance, "df_complex", "snap_reserve_used", - (double) snap_reserve_used, /* timestamp = */ 0); + (double) snap_reserve_used, /* timestamp = */ 0, interval); if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED)) submit_double (hostname, /* plugin instance = */ plugin_instance, "df_complex", "snap_normal_used", - (double) snap_norm_used, /* timestamp = */ 0); + (double) snap_norm_used, /* timestamp = */ 0, interval); /* Clear all the HAVE_* flags */ v->flags &= ~HAVE_VOLUME_USAGE_ALL; @@@ -1650,7 -1654,7 +1654,7 @@@ static int cna_handle_volume_usage_dat } /* }}} end of 32-bit workaround */ } /* for (elem_volume) */ - return (cna_submit_volume_usage_data (host->name, cfg_volume)); + return (cna_submit_volume_usage_data (host->name, cfg_volume, host->interval)); } /* }}} int cna_handle_volume_usage_data */ static int cna_setup_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */ @@@ -1714,7 -1718,7 +1718,7 @@@ static int cna_query_volume_usage (host /* Data corresponding to */ static int cna_handle_system_data (const char *hostname, /* {{{ */ - cfg_system_t *cfg_system, na_elem_t *data) + cfg_system_t *cfg_system, na_elem_t *data, int interval) { na_elem_t *instances; na_elem_t *counter; @@@ -1786,27 -1790,27 +1790,27 @@@ && (value > 0) && (strlen(name) > 4) && (!strcmp(name + strlen(name) - 4, "_ops"))) { submit_counter (hostname, instance, "disk_ops_complex", name, - (counter_t) value, timestamp); + (counter_t) value, timestamp, interval); } } /* for (counter) */ if ((cfg_system->flags & CFG_SYSTEM_DISK) && (HAS_ALL_FLAGS (counter_flags, 0x01 | 0x02))) submit_two_counters (hostname, instance, "disk_octets", NULL, - disk_read, disk_written, timestamp); + disk_read, disk_written, timestamp, interval); if ((cfg_system->flags & CFG_SYSTEM_NET) && (HAS_ALL_FLAGS (counter_flags, 0x04 | 0x08))) submit_two_counters (hostname, instance, "if_octets", NULL, - net_recv, net_sent, timestamp); + net_recv, net_sent, timestamp, interval); if ((cfg_system->flags & CFG_SYSTEM_CPU) && (HAS_ALL_FLAGS (counter_flags, 0x10 | 0x20))) { submit_counter (hostname, instance, "cpu", "system", - cpu_busy, timestamp); + cpu_busy, timestamp, interval); submit_counter (hostname, instance, "cpu", "idle", - cpu_total - cpu_busy, timestamp); + cpu_total - cpu_busy, timestamp, interval); } return (0); @@@ -1862,7 -1866,7 +1866,7 @@@ static int cna_query_system (host_confi return (-1); } - status = cna_handle_system_data (host->name, host->cfg_system, data); + status = cna_handle_system_data (host->name, host->cfg_system, data, host->interval); if (status == 0) host->cfg_system->interval.last_read = now; @@@ -2559,7 -2563,7 +2563,7 @@@ static int cna_config (oconfig_item_t * ud.data = host; ud.free_func = (void (*) (void *)) free_host_config; - plugin_register_complex_read (cb_name, + plugin_register_complex_read (/* group = */ NULL, cb_name, /* callback = */ cna_read, /* interval = */ (host->interval > 0) ? &interval : NULL, /* user data = */ &ud); diff --combined src/network.c index 457637be,242b9d25..412b457b --- a/src/network.c +++ b/src/network.c @@@ -31,6 -31,7 +31,7 @@@ #include "utils_fbhash.h" #include "utils_avltree.h" #include "utils_cache.h" + #include "utils_complain.h" #include "network.h" @@@ -52,9 -53,6 +53,9 @@@ #if HAVE_POLL_H # include #endif +#if HAVE_NET_IF_H +# include +#endif #if HAVE_LIBGCRYPT # include @@@ -121,7 -119,6 +122,7 @@@ typedef struct socken char *node; char *service; + int interface; union { @@@ -350,8 -347,7 +351,8 @@@ static _Bool check_send_okay (const val return (!received); } /* }}} _Bool check_send_okay */ -static int network_dispatch_values (value_list_t *vl) /* {{{ */ +static int network_dispatch_values (value_list_t *vl, /* {{{ */ + const char *username) { int status; @@@ -392,18 -388,6 +393,18 @@@ return (status); } + if (username != NULL) + { + status = meta_data_add_string (vl->meta, "network:username", username); + if (status != 0) + { + ERROR ("network plugin: meta_data_add_string failed."); + meta_data_destroy (vl->meta); + vl->meta = NULL; + return (status); + } + } + plugin_dispatch_values (vl); stats_values_dispatched++; @@@ -905,8 -889,7 +906,8 @@@ static int parse_part_string (void **re #define PP_SIGNED 0x01 #define PP_ENCRYPTED 0x02 static int parse_packet (sockent_t *se, - void *buffer, size_t buffer_size, int flags); + void *buffer, size_t buffer_size, int flags, + const char *username); #define BUFFER_READ(p,s) do { \ memcpy ((p), buffer + buffer_offset, (s)); \ @@@ -917,6 -900,8 +918,8 @@@ static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */ void **ret_buffer, size_t *ret_buffer_len, int flags) { + static c_complain_t complain_no_users = C_COMPLAIN_INIT_STATIC; + char *buffer; size_t buffer_len; size_t buffer_offset; @@@ -938,8 -923,9 +941,9 @@@ if (se->data.server.userdb == NULL) { - NOTICE ("network plugin: Received signed network packet but can't verify " - "it because no user DB has been configured. Will accept it."); + c_complain (LOG_NOTICE, &complain_no_users, + "network plugin: Received signed network packet but can't verify it " + "because no user DB has been configured. Will accept it."); return (0); } @@@ -1001,8 -987,6 +1005,8 @@@ { ERROR ("network plugin: gcry_md_setkey failed: %s", gcry_strerror (err)); gcry_md_close (hd); + sfree (secret); + sfree (pss.username); return (-1); } @@@ -1024,6 -1008,9 +1028,6 @@@ gcry_md_close (hd); hd = NULL; - sfree (secret); - sfree (pss.username); - if (memcmp (pss.hash, hash, sizeof (pss.hash)) != 0) { WARNING ("network plugin: Verifying HMAC-SHA-256 signature failed: " @@@ -1032,12 -1019,9 +1036,12 @@@ else { parse_packet (se, buffer + buffer_offset, buffer_len - buffer_offset, - flags | PP_SIGNED); + flags | PP_SIGNED, pss.username); } + sfree (secret); + sfree (pss.username); + *ret_buffer = buffer + buffer_len; *ret_buffer_len = 0; @@@ -1081,8 -1065,7 +1085,8 @@@ static int parse_part_sign_sha256 (sock warning_has_been_printed = 1; } - parse_packet (se, buffer + part_len, buffer_size - part_len, flags); + parse_packet (se, buffer + part_len, buffer_size - part_len, flags, + /* username = */ NULL); *ret_buffer = buffer + buffer_size; *ret_buffer_size = 0; @@@ -1201,9 -1184,7 +1205,9 @@@ static int parse_part_encr_aes256 (sock } parse_packet (se, buffer + buffer_offset, payload_len, - flags | PP_ENCRYPTED); + flags | PP_ENCRYPTED, pea.username); + + /* XXX: Free pea.username?!? */ /* Update return values */ *ret_buffer = buffer + part_size; @@@ -1265,8 -1246,7 +1269,8 @@@ static int parse_part_encr_aes256 (sock #undef BUFFER_READ static int parse_packet (sockent_t *se, /* {{{ */ - void *buffer, size_t buffer_size, int flags) + void *buffer, size_t buffer_size, int flags, + const char *username) { int status; @@@ -1366,7 -1346,7 +1370,7 @@@ if (status != 0) break; - network_dispatch_values (&vl); + network_dispatch_values (&vl, username); sfree (vl.values); } @@@ -1614,110 -1594,7 +1618,110 @@@ static int network_set_ttl (const socke return (0); } /* int network_set_ttl */ -static int network_bind_socket (int fd, const struct addrinfo *ai) +static int network_set_interface (const sockent_t *se, const struct addrinfo *ai) /* {{{ */ +{ + DEBUG ("network plugin: network_set_interface: interface index = %i;", + se->interface); + + assert (se->type == SOCKENT_TYPE_CLIENT); + + if (ai->ai_family == AF_INET) + { + struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr; + + if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr))) + { +#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + /* If possible, use the "ip_mreqn" structure which has + * an "interface index" member. Using the interface + * index is preferred here, because of its similarity + * to the way IPv6 handles this. Unfortunately, it + * appears not to be portable. */ + struct ip_mreqn mreq; + + memset (&mreq, 0, sizeof (mreq)); + mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; + mreq.imr_address.s_addr = ntohl (INADDR_ANY); + mreq.imr_ifindex = se->interface; +#else + struct ip_mreq mreq; + + memset (&mreq, 0, sizeof (mreq)); + mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; + mreq.imr_interface.s_addr = ntohl (INADDR_ANY); +#endif + + if (setsockopt (se->data.client.fd, IPPROTO_IP, IP_MULTICAST_IF, + &mreq, sizeof (mreq)) != 0) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + return (0); + } + } + else if (ai->ai_family == AF_INET6) + { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr; + + if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr)) + { + if (setsockopt (se->data.client.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &se->interface, + sizeof (se->interface)) != 0) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); + return (-1); + } + + return (0); + } + } + + /* else: Not a multicast interface. */ +#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE) + if (se->interface != 0) + { + char interface_name[IFNAMSIZ]; + + if (if_indextoname (se->interface, interface_name) == NULL) + return (-1); + + DEBUG ("network plugin: Binding socket to interface %s", interface_name); + + if (setsockopt (se->data.client.fd, SOL_SOCKET, SO_BINDTODEVICE, + interface_name, + sizeof(interface_name)) == -1 ) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + } +/* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */ + +#else + WARNING ("network plugin: Cannot set the interface on a unicast " + "socket because " +# if !defined(SO_BINDTODEVICE) + "the the \"SO_BINDTODEVICE\" socket option " +# else + "the \"if_indextoname\" function " +# endif + "is not available on your system."); +#endif + + return (0); +} /* }}} network_set_interface */ + +static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx) { int loop = 0; int yes = 1; @@@ -1746,24 -1623,12 +1750,24 @@@ struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr; if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr))) { +#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + struct ip_mreqn mreq; +#else struct ip_mreq mreq; +#endif DEBUG ("fd = %i; IPv4 multicast address found", fd); mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; - mreq.imr_interface.s_addr = htonl (INADDR_ANY); +#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + /* Set the interface using the interface index if + * possible (available). Unfortunately, the struct + * ip_mreqn is not portable. */ + mreq.imr_address.s_addr = ntohl (INADDR_ANY); + mreq.imr_ifindex = interface_idx; +#else + mreq.imr_interface.s_addr = ntohl (INADDR_ANY); +#endif if (setsockopt (fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof (loop)) == -1) @@@ -1784,8 -1649,6 +1788,8 @@@ sizeof (errbuf))); return (-1); } + + return (0); } } else if (ai->ai_family == AF_INET6) @@@ -1811,7 -1674,7 +1815,7 @@@ * single interface; programs running on * multihomed hosts may need to join the same * group on more than one interface.*/ - mreq.ipv6mr_interface = 0; + mreq.ipv6mr_interface = interface_idx; if (setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof (loop)) == -1) @@@ -1832,36 -1695,9 +1836,36 @@@ sizeof (errbuf))); return (-1); } + + return (0); } } +#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE) + /* if a specific interface was set, bind the socket to it. But to avoid + * possible problems with multicast routing, only do that for non-multicast + * addresses */ + if (interface_idx != 0) + { + char interface_name[IFNAMSIZ]; + + if (if_indextoname (interface_idx, interface_name) == NULL) + return (-1); + + DEBUG ("fd = %i; Binding socket to interface %s", fd, interface_name); + + if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, + interface_name, + sizeof(interface_name)) == -1 ) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + } +#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */ + return (0); } /* int network_bind_socket */ @@@ -1877,7 -1713,6 +1881,7 @@@ static int sockent_init (sockent_t *se se->type = SOCKENT_TYPE_CLIENT; se->node = NULL; se->service = NULL; + se->interface = 0; se->next = NULL; if (type == SOCKENT_TYPE_SERVER) @@@ -2026,7 -1861,7 +2030,7 @@@ static int sockent_open (sockent_t *se continue; } - status = network_bind_socket (*tmp, ai_ptr); + status = network_bind_socket (*tmp, ai_ptr, se->interface); if (status != 0) { close (*tmp); @@@ -2066,7 -1901,6 +2070,7 @@@ se->data.client.addrlen = ai_ptr->ai_addrlen; network_set_ttl (se, ai_ptr); + network_set_interface (se, ai_ptr); /* We don't open more than one write-socket per * node/service pair.. */ @@@ -2200,8 -2034,7 +2204,8 @@@ static void *dispatch_thread (void __at continue; } - parse_packet (se, ent->data, ent->data_len, /* flags = */ 0); + parse_packet (se, ent->data, ent->data_len, /* flags = */ 0, + /* username = */ NULL); sfree (ent->data); sfree (ent); } /* while (42) */ @@@ -2779,25 -2612,6 +2783,25 @@@ static int network_config_set_ttl (cons return (0); } /* }}} int network_config_set_ttl */ +static int network_config_set_interface (const oconfig_item_t *ci, /* {{{ */ + int *interface) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("network plugin: The `Interface' config option needs exactly " + "one string argument."); + return (-1); + } + + if (interface == NULL) + return (-1); + + *interface = if_nametoindex (ci->values[0].value.string); + + return (0); +} /* }}} int network_config_set_interface */ + static int network_config_set_buffer_size (const oconfig_item_t *ci) /* {{{ */ { int tmp; @@@ -2909,10 -2723,6 +2913,10 @@@ static int network_config_add_listen (c &se->data.server.security_level); else #endif /* HAVE_LIBGCRYPT */ + if (strcasecmp ("Interface", child->key) == 0) + network_config_set_interface (child, + &se->interface); + else { WARNING ("network plugin: Option `%s' is not allowed here.", child->key); @@@ -2991,10 -2801,6 +2995,10 @@@ static int network_config_add_server (c &se->data.client.security_level); else #endif /* HAVE_LIBGCRYPT */ + if (strcasecmp ("Interface", child->key) == 0) + network_config_set_interface (child, + &se->interface); + else { WARNING ("network plugin: Option `%s' is not allowed here.", child->key); diff --combined src/python.c index 8772cd1f,16de81d4..c056b5bf --- a/src/python.c +++ b/src/python.c @@@ -245,7 -245,7 +245,7 @@@ static void cpy_build_name(char *buf, s 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); @@@ -259,7 -259,7 +259,7 @@@ PyErr_Clear(); } -static void cpy_log_exception(const char *context) { +void cpy_log_exception(const char *context) { int l = 0, i; const char *typename = NULL, *message = NULL; PyObject *type, *value, *traceback, *tn, *m, *list; @@@ -268,11 -268,11 +268,11 @@@ 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,9 -301,7 +301,9 @@@ 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 @@@ -335,8 -333,7 +335,8 @@@ static int cpy_read_callback(user_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, *temp, *dict = NULL, *val; + Values *v; CPY_LOCK_THREADS list = PyList_New(value_list->values_len); /* New reference. */ @@@ -375,74 -372,13 +375,74 @@@ 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); /* New reference. */ - Py_DECREF(list); + dict = PyDict_New(); + if (value_list->meta) { + int i, num; + char **table; + meta_data_t *meta = value_list->meta; + + num = meta_data_toc(meta, &table); + for (i = 0; i < num; ++i) { + int type; + char *string; + int64_t si; + uint64_t ui; + double d; + _Bool b; + + type = meta_data_type(meta, table[i]); + if (type == MD_TYPE_STRING) { + if (meta_data_get_string(meta, table[i], &string)) + continue; + temp = cpy_string_to_unicode_or_bytes(string); + free(string); + PyDict_SetItemString(dict, table[i], temp); + Py_XDECREF(temp); + } else if (type == MD_TYPE_SIGNED_INT) { + if (meta_data_get_signed_int(meta, table[i], &si)) + continue; + temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0); + PyDict_SetItemString(dict, table[i], temp); + Py_XDECREF(temp); + } else if (type == MD_TYPE_UNSIGNED_INT) { + if (meta_data_get_unsigned_int(meta, table[i], &ui)) + continue; + temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0); + PyDict_SetItemString(dict, table[i], temp); + Py_XDECREF(temp); + } else if (type == MD_TYPE_DOUBLE) { + if (meta_data_get_double(meta, table[i], &d)) + continue; + temp = PyFloat_FromDouble(d); + PyDict_SetItemString(dict, table[i], temp); + Py_XDECREF(temp); + } else if (type == MD_TYPE_BOOLEAN) { + if (meta_data_get_boolean(meta, table[i], &b)) + continue; + if (b) + PyDict_SetItemString(dict, table[i], Py_True); + else + PyDict_SetItemString(dict, table[i], Py_False); + } + free(table[i]); + } + free(table); + } + val = Values_New(); /* New reference. */ + v = (Values *) val; + 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; + Py_CLEAR(v->values); + v->values = list; + Py_CLEAR(v->meta); + v->meta = dict; ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */ - Py_XDECREF(v); + Py_XDECREF(val); if (ret == NULL) { cpy_log_exception("write callback"); } else { @@@ -454,22 -390,14 +454,22 @@@ static int cpy_notification_callback(const notification_t *notification, user_data_t *data) { cpy_callback_t *c = data->data; - PyObject *ret, *n; + PyObject *ret, *notify; + 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); /* New reference. */ + notify = Notification_New(); /* New reference. */ + n = (Notification *) notify; + 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. */ - Py_XDECREF(n); + Py_XDECREF(notify); if (ret == NULL) { cpy_log_exception("notification callback"); } else { @@@ -481,14 -409,13 +481,14 @@@ 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 */ @@@ -505,14 -432,13 +505,14 @@@ 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"); @@@ -529,7 -455,7 +529,7 @@@ static PyObject *cpy_register_generic(c 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; @@@ -545,7 -471,7 +545,7 @@@ 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) { @@@ -553,7 -479,7 +553,7 @@@ 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 @@@ -579,7 -505,7 +579,7 @@@ static PyObject *cpy_register_generic_u 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; @@@ -597,7 -523,7 +597,7 @@@ 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) { @@@ -610,7 -536,7 +610,7 @@@ 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; @@@ -629,9 -555,8 +629,9 @@@ user_data->data = c; 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); + plugin_register_complex_read(/* group = */ NULL, buf, + cpy_read_callback, &ts, user_data); + return cpy_string_to_unicode_or_bytes(buf); } static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) { @@@ -660,7 -585,7 +660,7 @@@ static PyObject *cpy_register_shutdown( 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 @@@ -669,7 -594,7 +669,7 @@@ 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 @@@ -678,7 -603,7 +678,7 @@@ 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 @@@ -687,7 -612,7 +687,7 @@@ 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 @@@ -697,7 -622,7 +697,7 @@@ 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 @@@ -710,13 -635,17 +710,13 @@@ static PyObject *cpy_unregister_generic 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); @@@ -726,7 -655,6 +726,7 @@@ 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; @@@ -747,24 -675,25 +747,24 @@@ static PyObject *cpy_unregister_generic 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; } @@@ -940,7 -869,7 +940,7 @@@ static PyObject *cpy_oconfig_to_pyconfi 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) { @@@ -948,8 -877,7 +948,8 @@@ } } - 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. */ @@@ -962,22 -890,9 +962,23 @@@ 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; + char *argv = ""; PyObject *sys, *tb; PyObject *sys_path; PyObject *module; @@@ -988,12 -903,6 +989,12 @@@ * 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); @@@ -1002,10 -911,6 +1003,10 @@@ PyType_Ready(&ValuesType); NotificationType.tp_base = &PluginDataType; PyType_Ready(&NotificationType); + SignedType.tp_base = &PyLong_Type; + PyType_Ready(&SignedType); + UnsignedType.tp_base = &PyLong_Type; + PyType_Ready(&UnsignedType); sys = PyImport_ImportModule("sys"); /* New reference. */ if (sys == NULL) { cpy_log_exception("python initialization"); @@@ -1017,16 -922,13 +1018,19 @@@ cpy_log_exception("python initialization"); return 1; } + PySys_SetArgv(1, &argv); + PyList_SetSlice(sys_path, 0, 1, NULL); + +#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. */ + PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */ + PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */ PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG); PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO); PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE); @@@ -1073,7 -975,7 +1077,7 @@@ 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); @@@ -1098,6 -1000,7 +1102,6 @@@ 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 --combined src/utils_match.c index 4d4b57d0,2aaeb963..062bcfe3 --- a/src/utils_match.c +++ b/src/utils_match.c @@@ -29,12 -29,10 +29,12 @@@ #include #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01 +#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02 struct cu_match_s { regex_t regex; + regex_t excluderegex; int flags; int (*callback) (const char *str, char * const *matches, size_t matches_num, @@@ -212,7 -210,7 +212,7 @@@ static int default_callback (const cha /* * Public functions */ -cu_match_t *match_create_callback (const char *regex, +cu_match_t *match_create_callback (const char *regex, const char *excluderegex, int (*callback) (const char *str, char * const *matches, size_t matches_num, void *user_data), void *user_data) @@@ -220,15 -218,14 +220,15 @@@ cu_match_t *obj; int status; - DEBUG ("utils_match: match_create_callback: regex = %s", regex); + DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s", + regex, excluderegex); obj = (cu_match_t *) malloc (sizeof (cu_match_t)); if (obj == NULL) return (NULL); memset (obj, '\0', sizeof (cu_match_t)); - status = regcomp (&obj->regex, regex, REG_EXTENDED); + status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE); if (status != 0) { ERROR ("Compiling the regular expression \"%s\" failed.", regex); @@@ -236,26 -233,13 +236,26 @@@ return (NULL); } + if (excluderegex && strcmp(excluderegex, "") != 0) { + status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED); + if (status != 0) + { + ERROR ("Compiling the excluding regular expression \"%s\" failed.", + excluderegex); + sfree (obj); + return (NULL); + } + obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX; + } + obj->callback = callback; obj->user_data = user_data; return (obj); } /* cu_match_t *match_create_callback */ -cu_match_t *match_create_simple (const char *regex, int match_ds_type) +cu_match_t *match_create_simple (const char *regex, + const char *excluderegex, int match_ds_type) { cu_match_value_t *user_data; cu_match_t *obj; @@@ -266,8 -250,7 +266,8 @@@ memset (user_data, '\0', sizeof (cu_match_value_t)); user_data->ds_type = match_ds_type; - obj = match_create_callback (regex, default_callback, user_data); + obj = match_create_callback (regex, excluderegex, + default_callback, user_data); if (obj == NULL) { sfree (user_data); @@@ -303,17 -286,6 +303,17 @@@ int match_apply (cu_match_t *obj, cons if ((obj == NULL) || (str == NULL)) return (-1); + if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) { + status = regexec (&obj->excluderegex, str, + STATIC_ARRAY_SIZE (re_match), re_match, + /* eflags = */ 0); + /* Regex did match, so exclude this line */ + if (status == 0) { + DEBUG("ExludeRegex matched, don't count that line\n"); + return (0); + } + } + status = regexec (&obj->regex, str, STATIC_ARRAY_SIZE (re_match), re_match, /* eflags = */ 0);