src/daemon/filter_chain.h \
src/daemon/globals.c \
src/daemon/globals.h \
- src/daemon/meta_data.c \
- src/daemon/meta_data.h \
+ src/utils/metadata/meta_data.c \
+ src/utils/metadata/meta_data.h \
src/daemon/plugin.c \
src/daemon/plugin.h \
src/daemon/utils_cache.c \
test_common_SOURCES = \
- src/daemon/common_test.c \
+ src/utils/common/common_test.c \
src/testing.h
test_common_LDADD = libplugin_mock.la
test_meta_data_SOURCES = \
- src/daemon/meta_data_test.c \
+ src/utils/metadata/meta_data_test.c \
src/testing.h
test_meta_data_LDADD = libmetadata.la libplugin_mock.la
test_utils_avltree_SOURCES = \
- src/daemon/utils_avltree_test.c \
+ src/utils/avltree/avltree_test.c \
src/testing.h
test_utils_avltree_LDADD = libavltree.la $(COMMON_LIBS)
test_utils_heap_SOURCES = \
- src/daemon/utils_heap_test.c \
+ src/utils/heap/heap_test.c \
src/testing.h
test_utils_heap_LDADD = libheap.la $(COMMON_LIBS)
test_utils_subst_LDADD = libplugin_mock.la
test_utils_config_cores_SOURCES = \
- src/utils_config_cores_test.c \
+ src/utils/config_cores/config_cores_test.c \
src/testing.h
test_utils_config_cores_LDADD = libplugin_mock.la
libavltree_la_SOURCES = \
- src/daemon/utils_avltree.c \
- src/daemon/utils_avltree.h
+ src/utils/avltree/avltree.c \
+ src/utils/avltree/avltree.h
libcommon_la_SOURCES = \
- src/daemon/common.c \
- src/daemon/common.h
+ src/utils/common/common.c \
+ src/utils/common/common.h
libcommon_la_LIBADD = $(COMMON_LIBS)
libheap_la_SOURCES = \
- src/daemon/utils_heap.c \
- src/daemon/utils_heap.h
+ src/utils/heap/heap.c \
+ src/utils/heap/heap.h
libignorelist_la_SOURCES = \
- src/utils_ignorelist.c \
- src/utils_ignorelist.h
+ src/utils/ignorelist/ignorelist.c \
+ src/utils/ignorelist/ignorelist.h
libmetadata_la_SOURCES = \
- src/daemon/meta_data.c \
- src/daemon/meta_data.h
+ src/utils/metadata/meta_data.c \
+ src/utils/metadata/meta_data.h
libplugin_mock_la_SOURCES = \
src/daemon/plugin_mock.c \
libplugin_mock_la_LIBADD = libcommon.la libignorelist.la $(COMMON_LIBS)
libformat_graphite_la_SOURCES = \
- src/utils_format_graphite.c \
- src/utils_format_graphite.h
+ src/utils/format_graphite/format_graphite.c \
+ src/utils/format_graphite/format_graphite.h
test_format_graphite_SOURCES = \
- src/utils_format_graphite_test.c \
+ src/utils/format_graphite/format_graphite_test.c \
src/testing.h
test_format_graphite_LDADD = \
libformat_graphite.la \
-lm
libformat_json_la_SOURCES = \
- src/utils_format_json.c \
- src/utils_format_json.h
+ src/utils/format_json/format_json.c \
+ src/utils/format_json/format_json.h
libformat_json_la_CPPFLAGS = $(AM_CPPFLAGS)
libformat_json_la_LDFLAGS = $(AM_LDFLAGS)
libformat_json_la_LIBADD =
check_PROGRAMS += test_format_json
test_format_json_SOURCES = \
- src/utils_format_json_test.c \
+ src/utils/format_json/format_json_test.c \
src/testing.h
test_format_json_LDADD = \
libformat_json.la \
endif
liblatency_la_SOURCES = \
- src/utils_latency.c \
- src/utils_latency.h \
- src/utils_latency_config.c \
- src/utils_latency_config.h
+ src/utils/latency/latency.c \
+ src/utils/latency/latency.h \
+ src/utils/latency/latency_config.c \
+ src/utils/latency/latency_config.h
liblatency_la_LIBADD = \
libcommon.la \
-lm
test_utils_latency_SOURCES = \
- src/utils_latency_test.c \
+ src/utils/latency/latency_test.c \
src/testing.h
test_utils_latency_LDADD = \
liblatency.la \
-lm
libcmds_la_SOURCES = \
- src/utils_cmds.c \
- src/utils_cmds.h \
- src/utils_cmd_flush.c \
- src/utils_cmd_flush.h \
- src/utils_cmd_getthreshold.c \
- src/utils_cmd_getthreshold.h \
- src/utils_cmd_getval.c \
- src/utils_cmd_getval.h \
- src/utils_cmd_listval.c \
- src/utils_cmd_listval.h \
- src/utils_cmd_putnotif.c \
- src/utils_cmd_putnotif.h \
- src/utils_cmd_putval.c \
- src/utils_cmd_putval.h \
- src/utils_parse_option.c \
- src/utils_parse_option.h
+ src/utils/cmds/cmds.c \
+ src/utils/cmds/cmds.h \
+ src/utils/cmds/flush.c \
+ src/utils/cmds/flush.h \
+ src/utils/cmds/getthreshold.c \
+ src/utils/cmds/getthreshold.h \
+ src/utils/cmds/getval.c \
+ src/utils/cmds/getval.h \
+ src/utils/cmds/listval.c \
+ src/utils/cmds/listval.h \
+ src/utils/cmds/putnotif.c \
+ src/utils/cmds/putnotif.h \
+ src/utils/cmds/putval.c \
+ src/utils/cmds/putval.h \
+ src/utils/cmds/parse_option.c \
+ src/utils/cmds/parse_option.h
libcmds_la_LIBADD = \
libcommon.la \
libmetadata.la \
-lm
test_utils_cmds_SOURCES = \
- src/utils_cmds_test.c \
+ src/utils/cmds/cmds_test.c \
src/testing.h
test_utils_cmds_LDADD = \
libcmds.la \
libplugin_mock.la
liblookup_la_SOURCES = \
- src/utils_vl_lookup.c \
- src/utils_vl_lookup.h
+ src/utils/lookup/vl_lookup.c \
+ src/utils/lookup/vl_lookup.h
liblookup_la_LIBADD = libavltree.la
test_utils_vl_lookup_SOURCES = \
- src/utils_vl_lookup_test.c \
+ src/utils/lookup/vl_lookup_test.c \
src/testing.h
test_utils_vl_lookup_LDADD = \
liblookup.la \
endif
libmount_la_SOURCES = \
- src/utils_mount.c \
- src/utils_mount.h
+ src/utils/mount/mount.c \
+ src/utils/mount/mount.h
test_utils_mount_SOURCES = \
- src/utils_mount_test.c \
+ src/utils/mount/mount_test.c \
src/testing.h
test_utils_mount_LDADD = \
libmount.la \
if BUILD_WITH_LIBYAJL2
noinst_LTLIBRARIES += liboauth.la
liboauth_la_SOURCES = \
- src/utils_oauth.c \
- src/utils_oauth.h
+ src/utils/oauth/oauth.c \
+ src/utils/oauth/oauth.h
liboauth_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(BUILD_WITH_LIBCURL_CFLAGS) \
check_PROGRAMS += test_utils_oauth
TESTS += test_utils_oauth
test_utils_oauth_SOURCES = \
- src/utils_oauth_test.c
+ src/utils/oauth/oauth_test.c
test_utils_oauth_LDADD = \
liboauth.la \
libcommon.la \
noinst_LTLIBRARIES += libgce.la
libgce_la_SOURCES = \
- src/utils_gce.c \
- src/utils_gce.h
+ src/utils/gce/gce.c \
+ src/utils/gce/gce.h
libgce_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(BUILD_WITH_LIBCURL_CFLAGS)
if BUILD_WITH_LIBYAJL2
noinst_LTLIBRARIES += libformat_stackdriver.la
libformat_stackdriver_la_SOURCES = \
- src/utils_format_stackdriver.c \
- src/utils_format_stackdriver.h
+ src/utils/format_stackdriver/format_stackdriver.c \
+ src/utils/format_stackdriver/format_stackdriver.h
libformat_stackdriver_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(BUILD_WITH_LIBYAJL_CPPFLAGS)
check_PROGRAMS += test_format_stackdriver
TESTS += test_format_stackdriver
test_format_stackdriver_SOURCES = \
- src/utils_format_stackdriver_test.c \
+ src/utils/format_stackdriver/format_stackdriver_test.c \
src/testing.h
test_format_stackdriver_LDADD = \
libformat_stackdriver.la \
pkglib_LTLIBRARIES += aggregation.la
aggregation_la_SOURCES = \
src/aggregation.c \
- src/utils_vl_lookup.c \
- src/utils_vl_lookup.h
+ src/utils/lookup/vl_lookup.c \
+ src/utils/lookup/vl_lookup.h
aggregation_la_LDFLAGS = $(PLUGIN_LDFLAGS)
aggregation_la_LIBADD = -lm
endif
pkglib_LTLIBRARIES += amqp1.la
amqp1_la_SOURCES = \
src/amqp1.c \
- src/utils_deq.h
+ src/utils/deq/deq.h
amqp1_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBQPIDPROTON_CPPFLAGS)
amqp1_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBQPIDPROTON_LDFLAGS)
amqp1_la_LIBADD = \
pkglib_LTLIBRARIES += curl.la
curl_la_SOURCES = \
src/curl.c \
- src/utils_curl_stats.c \
- src/utils_curl_stats.h \
- src/utils_match.c \
- src/utils_match.h
+ src/utils/curl_stats/curl_stats.c \
+ src/utils/curl_stats/curl_stats.h \
+ src/utils/match/match.c \
+ src/utils/match/match.h
curl_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
curl_la_LDFLAGS = $(PLUGIN_LDFLAGS)
curl_la_LIBADD = liblatency.la $(BUILD_WITH_LIBCURL_LIBS)
pkglib_LTLIBRARIES += curl_json.la
curl_json_la_SOURCES = \
src/curl_json.c \
- src/utils_curl_stats.c \
- src/utils_curl_stats.h
+ src/utils/curl_stats/curl_stats.c \
+ src/utils/curl_stats/curl_stats.h
curl_json_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
curl_json_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
curl_json_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBYAJL_LIBS)
test_plugin_curl_json_SOURCES = src/curl_json_test.c \
- src/utils_curl_stats.c \
+ src/utils/curl_stats/curl_stats.c \
src/daemon/configfile.c \
src/daemon/types_list.c
test_plugin_curl_json_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
pkglib_LTLIBRARIES += curl_xml.la
curl_xml_la_SOURCES = \
src/curl_xml.c \
- src/utils_curl_stats.c \
- src/utils_curl_stats.h
+ src/utils/curl_stats/curl_stats.c \
+ src/utils/curl_stats/curl_stats.h
curl_xml_la_CFLAGS = $(AM_CFLAGS) \
$(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
curl_xml_la_LDFLAGS = $(PLUGIN_LDFLAGS)
pkglib_LTLIBRARIES += dbi.la
dbi_la_SOURCES = \
src/dbi.c \
- src/utils_db_query.c \
- src/utils_db_query.h
+ src/utils/db_query/db_query.c \
+ src/utils/db_query/db_query.h
dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS)
dbi_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBDBI_LDFLAGS)
dbi_la_LIBADD = $(BUILD_WITH_LIBDBI_LIBS)
pkglib_LTLIBRARIES += dns.la
dns_la_SOURCES = \
src/dns.c \
- src/utils_dns.c \
- src/utils_dns.h
+ src/utils/dns/dns.c \
+ src/utils/dns/dns.h
dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPCAP_CPPFLAGS)
dns_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPCAP_LDFLAGS)
dns_la_LIBADD = $(BUILD_WITH_LIBPCAP_LIBS)
if BUILD_PLUGIN_DPDKEVENTS
pkglib_LTLIBRARIES += dpdkevents.la
-dpdkevents_la_SOURCES = src/dpdkevents.c src/utils_dpdk.c src/utils_dpdk.h
+dpdkevents_la_SOURCES = src/dpdkevents.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h
dpdkevents_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
dpdkevents_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
dpdkevents_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
if BUILD_PLUGIN_DPDKSTAT
pkglib_LTLIBRARIES += dpdkstat.la
-dpdkstat_la_SOURCES = src/dpdkstat.c src/utils_dpdk.c src/utils_dpdk.h
+dpdkstat_la_SOURCES = src/dpdkstat.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h
dpdkstat_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
dpdkstat_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
dpdkstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
pkglib_LTLIBRARIES += intel_pmu.la
intel_pmu_la_SOURCES = \
src/intel_pmu.c \
- src/utils_config_cores.h \
- src/utils_config_cores.c
+ src/utils/config_cores/config_cores.h \
+ src/utils/config_cores/config_cores.c
intel_pmu_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBJEVENTS_CPPFLAGS)
intel_pmu_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBJEVENTS_LDFLAGS)
intel_pmu_la_LIBADD = $(BUILD_WITH_LIBJEVENTS_LIBS)
pkglib_LTLIBRARIES += intel_rdt.la
intel_rdt_la_SOURCES = \
src/intel_rdt.c \
- src/utils_config_cores.h \
- src/utils_config_cores.c
+ src/utils/config_cores/config_cores.h \
+ src/utils/config_cores/config_cores.c
intel_rdt_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBPQOS_CPPFLAGS)
intel_rdt_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPQOS_LDFLAGS)
intel_rdt_la_LIBADD = $(BUILD_WITH_LIBPQOS_LIBS)
pkglib_LTLIBRARIES += memcachec.la
memcachec_la_SOURCES = \
src/memcachec.c \
- src/utils_match.c \
- src/utils_match.h
+ src/utils/match/match.c \
+ src/utils/match/match.h
memcachec_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS)
memcachec_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMEMCACHED_LDFLAGS)
memcachec_la_LIBADD = liblatency.la $(BUILD_WITH_LIBMEMCACHED_LIBS)
pkglib_LTLIBRARIES += oracle.la
oracle_la_SOURCES = \
src/oracle.c \
- src/utils_db_query.c \
- src/utils_db_query.h
+ src/utils/db_query/db_query.c \
+ src/utils/db_query/db_query.h
oracle_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_ORACLE_CPPFLAGS)
oracle_la_LIBADD = $(BUILD_WITH_ORACLE_LIBS)
oracle_la_LDFLAGS = $(PLUGIN_LDFLAGS)
pkglib_LTLIBRARIES += ovs_events.la
ovs_events_la_SOURCES = \
src/ovs_events.c \
- src/utils_ovs.c \
- src/utils_ovs.h
+ src/utils/ovs/ovs.c \
+ src/utils/ovs/ovs.h
ovs_events_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
ovs_events_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
ovs_events_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
pkglib_LTLIBRARIES += ovs_stats.la
ovs_stats_la_SOURCES = \
src/ovs_stats.c \
- src/utils_ovs.c \
- src/utils_ovs.h
+ src/utils/ovs/ovs.c \
+ src/utils/ovs/ovs.h
ovs_stats_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
ovs_stats_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
ovs_stats_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
pkglib_LTLIBRARIES += postgresql.la
postgresql_la_SOURCES = \
src/postgresql.c \
- src/utils_db_query.c \
- src/utils_db_query.h
+ src/utils/db_query/db_query.c \
+ src/utils/db_query/db_query.h
postgresql_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPQ_CPPFLAGS)
postgresql_la_LDFLAGS = $(PLUGIN_LDFLAGS) \
$(BUILD_WITH_LIBPQ_LDFLAGS)
if HAVE_LIBMNL
noinst_LTLIBRARIES += libtaskstats.la
libtaskstats_la_SOURCES = \
- src/utils_taskstats.c \
- src/utils_taskstats.h
+ src/utils/taskstats/taskstats.c \
+ src/utils/taskstats/taskstats.h
libtaskstats_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
libtaskstats_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
endif
pkglib_LTLIBRARIES += rrdcached.la
rrdcached_la_SOURCES = \
src/rrdcached.c \
- src/utils_rrdcreate.c \
- src/utils_rrdcreate.h
+ src/utils/rrdcreate/rrdcreate.c \
+ src/utils/rrdcreate/rrdcreate.h
rrdcached_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
rrdcached_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRRD_LDFLAGS)
rrdcached_la_LIBADD = $(BUILD_WITH_LIBRRD_LIBS)
pkglib_LTLIBRARIES += rrdtool.la
rrdtool_la_SOURCES = \
src/rrdtool.c \
- src/utils_rrdcreate.c \
- src/utils_rrdcreate.h
+ src/utils/rrdcreate/rrdcreate.c \
+ src/utils/rrdcreate/rrdcreate.h
rrdtool_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
rrdtool_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRRD_LDFLAGS)
rrdtool_la_LIBADD = $(BUILD_WITH_LIBRRD_LIBS)
snmp_agent_la_LIBADD = $(BUILD_WITH_LIBNETSNMPAGENT_LIBS)
test_plugin_snmp_agent_SOURCES = src/snmp_agent_test.c \
- src/daemon/utils_avltree.c \
+ src/utils/avltree/avltree.c \
src/daemon/utils_llist.c \
src/daemon/configfile.c \
src/daemon/types_list.c
pkglib_LTLIBRARIES += tail.la
tail_la_SOURCES = \
src/tail.c \
- src/utils_match.c \
- src/utils_match.h \
- src/utils_tail.c \
- src/utils_tail.h \
+ src/utils/match/match.c \
+ src/utils/match/match.h \
+ src/utils/tail/tail.c \
+ src/utils/tail/tail.h \
src/utils_tail_match.c \
src/utils_tail_match.h
tail_la_LDFLAGS = $(PLUGIN_LDFLAGS)
pkglib_LTLIBRARIES += tail_csv.la
tail_csv_la_SOURCES = \
src/tail_csv.c \
- src/utils_tail.c \
- src/utils_tail.h
+ src/utils/tail/tail.c \
+ src/utils/tail/tail.h
tail_csv_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
pkglib_LTLIBRARIES += write_http.la
write_http_la_SOURCES = \
src/write_http.c \
- src/utils_format_kairosdb.c \
- src/utils_format_kairosdb.h
+ src/utils/format_kairosdb/format_kairosdb.c \
+ src/utils/format_kairosdb/format_kairosdb.h
write_http_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
write_http_la_LDFLAGS = $(PLUGIN_LDFLAGS)
write_http_la_LIBADD = libformat_json.la $(BUILD_WITH_LIBCURL_LIBS)
#include "collectd.h"
-#include "common.h"
-#include "meta_data.h"
#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/lookup/vl_lookup.h"
+#include "utils/metadata/meta_data.h"
#include "utils_cache.h" /* for uc_get_rate() */
#include "utils_subst.h"
-#include "utils_vl_lookup.h"
#define AGG_MATCHES_ALL(str) (strcmp("/.*/", str) == 0)
#define AGG_FUNC_PLACEHOLDER "%{aggregation}"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_cmd_putval.h"
-#include "utils_format_graphite.h"
-#include "utils_format_json.h"
+#include "utils/cmds/putval.h"
+#include "utils/common/common.h"
+#include "utils/format_graphite/format_graphite.h"
+#include "utils/format_json/format_json.h"
#include <amqp.h>
#include <amqp_framing.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_cmd_putval.h"
-#include "utils_deq.h"
-#include "utils_format_graphite.h"
-#include "utils_format_json.h"
+#include "utils/cmds/putval.h"
+#include "utils/common/common.h"
+#include "utils/deq/deq.h"
+#include "utils/format_graphite/format_graphite.h"
+#include "utils/format_json/format_json.h"
#include "utils_random.h"
#include <proton/condition.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <curl/curl.h>
#include "collectd.h"
-#include "common.h" /* rrd_update_file */
-#include "plugin.h" /* plugin_register, plugin_submit */
+#include "plugin.h" /* plugin_register, plugin_submit */
+#include "utils/common/common.h" /* rrd_update_file */
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_MACH_MACH_TYPES_H
#include <mach/mach_types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <libaquaero5.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <curl/curl.h>
#include <libxml/parser.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <fcntl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_MACH_MACH_TYPES_H
#include <mach/mach_types.h>
**/
-#include "common.h"
-#include "plugin.h"
#include "collectd.h"
+#include "plugin.h"
+#include "utils/common/common.h"
#include <stdio.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <time.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <arpa/inet.h>
#include <errno.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
-#include "utils_mount.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
+#include "utils/mount/mount.h"
static char const *config_keys[] = {"CGroup", "IgnoreSelected"};
static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
#include "collectd.h"
-#include "common.h" /* auxiliary functions */
-#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "utils/common/common.h" /* auxiliary functions */
#if HAVE_NETDB_H
#include <netdb.h> /* struct addrinfo */
#include <time.h>
#include <unistd.h>
-#include "utils_heap.h"
+#include "utils/heap/heap.h"
#include "collectd/client.h"
#include "collectd/network.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_MACH_KERN_RETURN_H
#include <mach/kern_return.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#define MAX_AVAIL_FREQS 20
#include "collectd.h"
-#include <time.h>
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
+#include <time.h>
static void cpusleep_submit(derive_t cpu_sleep) {
value_list_t vl = VALUE_LIST_INIT;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
/*
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_curl_stats.h"
-#include "utils_match.h"
+#include "utils/common/common.h"
+#include "utils/curl_stats/curl_stats.h"
+#include "utils/match/match.h"
#include "utils_time.h"
#include <curl/curl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils/curl_stats/curl_stats.h"
#include "utils_complain.h"
-#include "utils_curl_stats.h"
#include <sys/types.h>
#include <sys/un.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_curl_stats.h"
+#include "utils/common/common.h"
+#include "utils/curl_stats/curl_stats.h"
#include "utils_llist.h"
#include <libxml/parser.h>
#include "cmd.h"
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include <sys/un.h>
static void *do_flush(void __attribute__((unused)) * arg) {
#include "cmd.h"
#include "collectd.h"
-#include "common.h"
#include "configfile.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <sys/types.h>
+++ /dev/null
-/**
- * collectd - src/common.c
- * Copyright (C) 2005-2014 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- * Niki W. Waibel <niki.waibel@gmx.net>
- * Sebastian Harl <sh at tokkee.org>
- * Michał Mirosław <mirq-linux at rere.qmqm.pl>
-**/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_cache.h"
-
-/* for getaddrinfo */
-#include <netdb.h>
-#include <sys/types.h>
-
-#include <poll.h>
-
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#if HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-
-/* for ntohl and htonl */
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-#if HAVE_CAPABILITY
-#include <sys/capability.h>
-#endif
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-
-#ifdef HAVE_LIBKSTAT
-extern kstat_ctl_t *kc;
-#endif
-
-#if !defined(MSG_DONTWAIT)
-#if defined(MSG_NONBLOCK)
-/* AIX doesn't have MSG_DONTWAIT */
-#define MSG_DONTWAIT MSG_NONBLOCK
-#else
-/* Windows doesn't have MSG_DONTWAIT or MSG_NONBLOCK */
-#define MSG_DONTWAIT 0
-#endif /* defined(MSG_NONBLOCK) */
-#endif /* !defined(MSG_DONTWAIT) */
-
-#if !HAVE_GETPWNAM_R && defined(HAVE_GETPWNAM)
-static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-#if !HAVE_STRERROR_R
-static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-char *sstrncpy(char *dest, const char *src, size_t n) {
- strncpy(dest, src, n);
- dest[n - 1] = '\0';
-
- return dest;
-} /* char *sstrncpy */
-
-char *ssnprintf_alloc(char const *format, ...) /* {{{ */
-{
- char static_buffer[1024] = "";
- char *alloc_buffer;
- size_t alloc_buffer_size;
- int status;
- va_list ap;
-
- /* Try printing into the static buffer. In many cases it will be
- * sufficiently large and we can simply return a strdup() of this
- * buffer. */
- va_start(ap, format);
- status = vsnprintf(static_buffer, sizeof(static_buffer), format, ap);
- va_end(ap);
- if (status < 0)
- return NULL;
-
- /* "status" does not include the null byte. */
- alloc_buffer_size = (size_t)(status + 1);
- if (alloc_buffer_size <= sizeof(static_buffer))
- return strdup(static_buffer);
-
- /* Allocate a buffer large enough to hold the string. */
- alloc_buffer = calloc(1, alloc_buffer_size);
- if (alloc_buffer == NULL)
- return NULL;
-
- /* Print again into this new buffer. */
- va_start(ap, format);
- status = vsnprintf(alloc_buffer, alloc_buffer_size, format, ap);
- va_end(ap);
- if (status < 0) {
- sfree(alloc_buffer);
- return NULL;
- }
-
- return alloc_buffer;
-} /* }}} char *ssnprintf_alloc */
-
-char *sstrdup(const char *s) {
- char *r;
- size_t sz;
-
- if (s == NULL)
- return NULL;
-
- /* Do not use `strdup' here, because it's not specified in POSIX. It's
- * ``only'' an XSI extension. */
- sz = strlen(s) + 1;
- r = malloc(sz);
- if (r == NULL) {
- ERROR("sstrdup: Out of memory.");
- exit(3);
- }
- memcpy(r, s, sz);
-
- return r;
-} /* char *sstrdup */
-
-/* Even though Posix requires "strerror_r" to return an "int",
- * some systems (e.g. the GNU libc) return a "char *" _and_
- * ignore the second argument ... -tokkee */
-char *sstrerror(int errnum, char *buf, size_t buflen) {
- buf[0] = '\0';
-
-#if !HAVE_STRERROR_R
- {
- char *temp;
-
- pthread_mutex_lock(&strerror_r_lock);
-
- temp = strerror(errnum);
- sstrncpy(buf, temp, buflen);
-
- pthread_mutex_unlock(&strerror_r_lock);
- }
-/* #endif !HAVE_STRERROR_R */
-
-#elif STRERROR_R_CHAR_P
- {
- char *temp;
- temp = strerror_r(errnum, buf, buflen);
- if (buf[0] == '\0') {
- if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
- sstrncpy(buf, temp, buflen);
- else
- sstrncpy(buf, "strerror_r did not return "
- "an error message",
- buflen);
- }
- }
-/* #endif STRERROR_R_CHAR_P */
-
-#else
- if (strerror_r(errnum, buf, buflen) != 0) {
- snprintf(buf, buflen, "Error #%i; "
- "Additionally, strerror_r failed.",
- errnum);
- }
-#endif /* STRERROR_R_CHAR_P */
-
- return buf;
-} /* char *sstrerror */
-
-void *smalloc(size_t size) {
- void *r;
-
- if ((r = malloc(size)) == NULL) {
- ERROR("Not enough memory.");
- exit(3);
- }
-
- return r;
-} /* void *smalloc */
-
-#if 0
-void sfree (void **ptr)
-{
- if (ptr == NULL)
- return;
-
- if (*ptr != NULL)
- free (*ptr);
-
- *ptr = NULL;
-}
-#endif
-
-int sread(int fd, void *buf, size_t count) {
- char *ptr;
- size_t nleft;
- ssize_t status;
-
- ptr = (char *)buf;
- nleft = count;
-
- while (nleft > 0) {
- status = read(fd, (void *)ptr, nleft);
-
- if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
- continue;
-
- if (status < 0)
- return status;
-
- if (status == 0) {
- DEBUG("Received EOF from fd %i. ", fd);
- return -1;
- }
-
- assert((0 > status) || (nleft >= (size_t)status));
-
- nleft = nleft - ((size_t)status);
- ptr = ptr + ((size_t)status);
- }
-
- return 0;
-}
-
-int swrite(int fd, const void *buf, size_t count) {
- const char *ptr;
- size_t nleft;
- ssize_t status;
- struct pollfd pfd;
-
- ptr = (const char *)buf;
- nleft = count;
-
- if (fd < 0) {
- errno = EINVAL;
- return errno;
- }
-
- /* checking for closed peer connection */
- pfd.fd = fd;
- pfd.events = POLLIN | POLLHUP;
- pfd.revents = 0;
- if (poll(&pfd, 1, 0) > 0) {
- char buffer[32];
- if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
- /* if recv returns zero (even though poll() said there is data to be
- * read), that means the connection has been closed */
- errno = ECONNRESET;
- return -1;
- }
- }
-
- while (nleft > 0) {
- status = write(fd, (const void *)ptr, nleft);
-
- if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
- continue;
-
- if (status < 0)
- return errno ? errno : status;
-
- nleft = nleft - ((size_t)status);
- ptr = ptr + ((size_t)status);
- }
-
- return 0;
-}
-
-int strsplit(char *string, char **fields, size_t size) {
- size_t i;
- char *ptr;
- char *saveptr;
-
- i = 0;
- ptr = string;
- saveptr = NULL;
- while ((fields[i] = strtok_r(ptr, " \t\r\n", &saveptr)) != NULL) {
- ptr = NULL;
- i++;
-
- if (i >= size)
- break;
- }
-
- return (int)i;
-}
-
-int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num,
- const char *sep) {
- size_t avail = 0;
- char *ptr = buffer;
- size_t sep_len = 0;
-
- size_t buffer_req = 0;
-
- if (((fields_num != 0) && (fields == NULL)) ||
- ((buffer_size != 0) && (buffer == NULL)))
- return -EINVAL;
-
- if (buffer != NULL)
- buffer[0] = 0;
-
- if (buffer_size != 0)
- avail = buffer_size - 1;
-
- if (sep != NULL)
- sep_len = strlen(sep);
-
- for (size_t i = 0; i < fields_num; i++) {
- size_t field_len = strlen(fields[i]);
-
- if (i != 0)
- buffer_req += sep_len;
- buffer_req += field_len;
-
- if (buffer_size == 0)
- continue;
-
- if ((i != 0) && (sep_len > 0)) {
- if (sep_len >= avail) {
- /* prevent subsequent iterations from writing to the
- * buffer. */
- avail = 0;
- continue;
- }
-
- memcpy(ptr, sep, sep_len);
-
- ptr += sep_len;
- avail -= sep_len;
- }
-
- if (field_len > avail)
- field_len = avail;
-
- memcpy(ptr, fields[i], field_len);
- ptr += field_len;
-
- avail -= field_len;
- if (ptr != NULL)
- *ptr = 0;
- }
-
- return (int)buffer_req;
-}
-
-int escape_string(char *buffer, size_t buffer_size) {
- char *temp;
- size_t j;
-
- /* Check if we need to escape at all first */
- temp = strpbrk(buffer, " \t\"\\");
- if (temp == NULL)
- return 0;
-
- if (buffer_size < 3)
- return EINVAL;
-
- temp = calloc(1, buffer_size);
- if (temp == NULL)
- return ENOMEM;
-
- temp[0] = '"';
- j = 1;
-
- for (size_t i = 0; i < buffer_size; i++) {
- if (buffer[i] == 0) {
- break;
- } else if ((buffer[i] == '"') || (buffer[i] == '\\')) {
- if (j > (buffer_size - 4))
- break;
- temp[j] = '\\';
- temp[j + 1] = buffer[i];
- j += 2;
- } else {
- if (j > (buffer_size - 3))
- break;
- temp[j] = buffer[i];
- j++;
- }
- }
-
- assert((j + 1) < buffer_size);
- temp[j] = '"';
- temp[j + 1] = 0;
-
- sstrncpy(buffer, temp, buffer_size);
- sfree(temp);
- return 0;
-} /* int escape_string */
-
-int strunescape(char *buf, size_t buf_len) {
- for (size_t i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) {
- if (buf[i] != '\\')
- continue;
-
- if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
- P_ERROR("string unescape: backslash found at end of string.");
- /* Ensure null-byte at the end of the buffer. */
- buf[i] = 0;
- return -1;
- }
-
- switch (buf[i + 1]) {
- case 't':
- buf[i] = '\t';
- break;
- case 'n':
- buf[i] = '\n';
- break;
- case 'r':
- buf[i] = '\r';
- break;
- default:
- buf[i] = buf[i + 1];
- break;
- }
-
- /* Move everything after the position one position to the left.
- * Add a null-byte as last character in the buffer. */
- memmove(buf + i + 1, buf + i + 2, buf_len - i - 2);
- buf[buf_len - 1] = '\0';
- }
- return 0;
-} /* int strunescape */
-
-size_t strstripnewline(char *buffer) {
- size_t buffer_len = strlen(buffer);
-
- while (buffer_len > 0) {
- if ((buffer[buffer_len - 1] != '\n') && (buffer[buffer_len - 1] != '\r'))
- break;
- buffer_len--;
- buffer[buffer_len] = 0;
- }
-
- return buffer_len;
-} /* size_t strstripnewline */
-
-int escape_slashes(char *buffer, size_t buffer_size) {
- size_t buffer_len;
-
- buffer_len = strlen(buffer);
-
- if (buffer_len <= 1) {
- if (strcmp("/", buffer) == 0) {
- if (buffer_size < 5)
- return -1;
- sstrncpy(buffer, "root", buffer_size);
- }
- return 0;
- }
-
- /* Move one to the left */
- if (buffer[0] == '/') {
- memmove(buffer, buffer + 1, buffer_len);
- buffer_len--;
- }
-
- for (size_t i = 0; i < buffer_len; i++) {
- if (buffer[i] == '/')
- buffer[i] = '_';
- }
-
- return 0;
-} /* int escape_slashes */
-
-void replace_special(char *buffer, size_t buffer_size) {
- for (size_t i = 0; i < buffer_size; i++) {
- if (buffer[i] == 0)
- return;
- if ((!isalnum((int)buffer[i])) && (buffer[i] != '-'))
- buffer[i] = '_';
- }
-} /* void replace_special */
-
-int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta) {
- struct timeval *larger;
- struct timeval *smaller;
-
- int status;
-
- NORMALIZE_TIMEVAL(tv0);
- NORMALIZE_TIMEVAL(tv1);
-
- if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec)) {
- if (delta != NULL) {
- delta->tv_sec = 0;
- delta->tv_usec = 0;
- }
- return 0;
- }
-
- if ((tv0.tv_sec < tv1.tv_sec) ||
- ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec))) {
- larger = &tv1;
- smaller = &tv0;
- status = -1;
- } else {
- larger = &tv0;
- smaller = &tv1;
- status = 1;
- }
-
- if (delta != NULL) {
- delta->tv_sec = larger->tv_sec - smaller->tv_sec;
-
- if (smaller->tv_usec <= larger->tv_usec)
- delta->tv_usec = larger->tv_usec - smaller->tv_usec;
- else {
- --delta->tv_sec;
- delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec;
- }
- }
-
- assert((delta == NULL) ||
- ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000)));
-
- return status;
-} /* int timeval_cmp */
-
-int check_create_dir(const char *file_orig) {
- struct stat statbuf;
-
- char file_copy[PATH_MAX];
- char dir[PATH_MAX];
- char *fields[16];
- int fields_num;
- char *ptr;
- char *saveptr;
- int last_is_file = 1;
- int path_is_absolute = 0;
- size_t len;
-
- /*
- * Sanity checks first
- */
- if (file_orig == NULL)
- return -1;
-
- if ((len = strlen(file_orig)) < 1)
- return -1;
- else if (len >= sizeof(file_copy)) {
- ERROR("check_create_dir: name (%s) is too long.", file_orig);
- return -1;
- }
-
- /*
- * If `file_orig' ends in a slash the last component is a directory,
- * otherwise it's a file. Act accordingly..
- */
- if (file_orig[len - 1] == '/')
- last_is_file = 0;
- if (file_orig[0] == '/')
- path_is_absolute = 1;
-
- /*
- * Create a copy for `strtok_r' to destroy
- */
- sstrncpy(file_copy, file_orig, sizeof(file_copy));
-
- /*
- * Break into components. This will eat up several slashes in a row and
- * remove leading and trailing slashes..
- */
- ptr = file_copy;
- saveptr = NULL;
- fields_num = 0;
- while ((fields[fields_num] = strtok_r(ptr, "/", &saveptr)) != NULL) {
- ptr = NULL;
- fields_num++;
-
- if (fields_num >= 16)
- break;
- }
-
- /*
- * For each component, do..
- */
- for (int i = 0; i < (fields_num - last_is_file); i++) {
- /*
- * Do not create directories that start with a dot. This
- * prevents `../../' attacks and other likely malicious
- * behavior.
- */
- if (fields[i][0] == '.') {
- P_ERROR("Cowardly refusing to create a directory that "
- "begins with a `.' (dot): `%s'",
- file_orig);
- return -2;
- }
-
- /*
- * Join the components together again
- */
- dir[0] = '/';
- if (strjoin(dir + path_is_absolute,
- (size_t)(sizeof(dir) - path_is_absolute), fields,
- (size_t)(i + 1), "/") < 0) {
- P_ERROR("strjoin failed: `%s', component #%i", file_orig, i);
- return -1;
- }
-
- while (42) {
- if ((stat(dir, &statbuf) == -1) && (lstat(dir, &statbuf) == -1)) {
- if (errno == ENOENT) {
- if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
- break;
-
- /* this might happen, if a different thread created
- * the directory in the meantime
- * => call stat() again to check for S_ISDIR() */
- if (EEXIST == errno)
- continue;
-
- P_ERROR("check_create_dir: mkdir (%s): %s", dir, STRERRNO);
- return -1;
- } else {
- P_ERROR("check_create_dir: stat (%s): %s", dir, STRERRNO);
- return -1;
- }
- } else if (!S_ISDIR(statbuf.st_mode)) {
- P_ERROR("check_create_dir: `%s' exists but is not "
- "a directory!",
- dir);
- return -1;
- }
- break;
- }
- }
-
- return 0;
-} /* check_create_dir */
-
-#ifdef HAVE_LIBKSTAT
-int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) {
- char ident[128];
-
- *ksp_ptr = NULL;
-
- if (kc == NULL)
- return -1;
-
- snprintf(ident, sizeof(ident), "%s,%i,%s", module, instance, name);
-
- *ksp_ptr = kstat_lookup(kc, module, instance, name);
- if (*ksp_ptr == NULL) {
- P_ERROR("get_kstat: Cound not find kstat %s", ident);
- return -1;
- }
-
- if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
- P_ERROR("get_kstat: kstat %s has wrong type", ident);
- *ksp_ptr = NULL;
- return -1;
- }
-
-#ifdef assert
- assert(*ksp_ptr != NULL);
- assert((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
-#endif
-
- if (kstat_read(kc, *ksp_ptr, NULL) == -1) {
- P_ERROR("get_kstat: kstat %s could not be read", ident);
- return -1;
- }
-
- if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
- P_ERROR("get_kstat: kstat %s has wrong type", ident);
- return -1;
- }
-
- return 0;
-}
-
-long long get_kstat_value(kstat_t *ksp, char *name) {
- kstat_named_t *kn;
- long long retval = -1LL;
-
- if (ksp == NULL) {
- P_ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name);
- return -1LL;
- } else if (ksp->ks_type != KSTAT_TYPE_NAMED) {
- P_ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
- "is not KSTAT_TYPE_NAMED (%#x).",
- name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED);
- return -1LL;
- }
-
- if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, name)) == NULL)
- return -1LL;
-
- if (kn->data_type == KSTAT_DATA_INT32)
- retval = (long long)kn->value.i32;
- else if (kn->data_type == KSTAT_DATA_UINT32)
- retval = (long long)kn->value.ui32;
- else if (kn->data_type == KSTAT_DATA_INT64)
- retval =
- (long long)kn->value.i64; /* According to ANSI C99 `long long' must hold
- at least 64 bits */
- else if (kn->data_type == KSTAT_DATA_UINT64)
- retval = (long long)kn->value.ui64; /* XXX: Might overflow! */
- else
- P_WARNING("get_kstat_value: Not a numeric value: %s", name);
-
- return retval;
-}
-#endif /* HAVE_LIBKSTAT */
-
-#ifndef HAVE_HTONLL
-unsigned long long ntohll(unsigned long long n) {
-#if BYTE_ORDER == BIG_ENDIAN
- return n;
-#else
- return (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32);
-#endif
-} /* unsigned long long ntohll */
-
-unsigned long long htonll(unsigned long long n) {
-#if BYTE_ORDER == BIG_ENDIAN
- return n;
-#else
- return (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32);
-#endif
-} /* unsigned long long htonll */
-#endif /* HAVE_HTONLL */
-
-#if FP_LAYOUT_NEED_NOTHING
-/* Well, we need nothing.. */
-/* #endif FP_LAYOUT_NEED_NOTHING */
-
-#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
-#if FP_LAYOUT_NEED_ENDIANFLIP
-#define FP_CONVERT(A) \
- ((((uint64_t)(A)&0xff00000000000000LL) >> 56) | \
- (((uint64_t)(A)&0x00ff000000000000LL) >> 40) | \
- (((uint64_t)(A)&0x0000ff0000000000LL) >> 24) | \
- (((uint64_t)(A)&0x000000ff00000000LL) >> 8) | \
- (((uint64_t)(A)&0x00000000ff000000LL) << 8) | \
- (((uint64_t)(A)&0x0000000000ff0000LL) << 24) | \
- (((uint64_t)(A)&0x000000000000ff00LL) << 40) | \
- (((uint64_t)(A)&0x00000000000000ffLL) << 56))
-#else
-#define FP_CONVERT(A) \
- ((((uint64_t)(A)&0xffffffff00000000LL) >> 32) | \
- (((uint64_t)(A)&0x00000000ffffffffLL) << 32))
-#endif
-
-double ntohd(double d) {
- union {
- uint8_t byte[8];
- uint64_t integer;
- double floating;
- } ret;
-
- ret.floating = d;
-
- /* NAN in x86 byte order */
- if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00) && (ret.byte[2] == 0x00) &&
- (ret.byte[3] == 0x00) && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00) &&
- (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f)) {
- return NAN;
- } else {
- uint64_t tmp;
-
- tmp = ret.integer;
- ret.integer = FP_CONVERT(tmp);
- return ret.floating;
- }
-} /* double ntohd */
-
-double htond(double d) {
- union {
- uint8_t byte[8];
- uint64_t integer;
- double floating;
- } ret;
-
- if (isnan(d)) {
- ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00;
- ret.byte[4] = ret.byte[5] = 0x00;
- ret.byte[6] = 0xf8;
- ret.byte[7] = 0x7f;
- return ret.floating;
- } else {
- uint64_t tmp;
-
- ret.floating = d;
- tmp = FP_CONVERT(ret.integer);
- ret.integer = tmp;
- return ret.floating;
- }
-} /* double htond */
-#endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */
-
-int format_name(char *ret, int ret_len, const char *hostname,
- const char *plugin, const char *plugin_instance,
- const char *type, const char *type_instance) {
- char *buffer;
- size_t buffer_size;
-
- buffer = ret;
- buffer_size = (size_t)ret_len;
-
-#define APPEND(str) \
- do { \
- size_t l = strlen(str); \
- if (l >= buffer_size) \
- return ENOBUFS; \
- memcpy(buffer, (str), l); \
- buffer += l; \
- buffer_size -= l; \
- } while (0)
-
- assert(plugin != NULL);
- assert(type != NULL);
-
- APPEND(hostname);
- APPEND("/");
- APPEND(plugin);
- if ((plugin_instance != NULL) && (plugin_instance[0] != 0)) {
- APPEND("-");
- APPEND(plugin_instance);
- }
- APPEND("/");
- APPEND(type);
- if ((type_instance != NULL) && (type_instance[0] != 0)) {
- APPEND("-");
- APPEND(type_instance);
- }
- assert(buffer_size > 0);
- buffer[0] = 0;
-
-#undef APPEND
- return 0;
-} /* int format_name */
-
-int format_values(char *ret, size_t ret_len, /* {{{ */
- const data_set_t *ds, const value_list_t *vl,
- bool store_rates) {
- size_t offset = 0;
- int status;
- gauge_t *rates = NULL;
-
- assert(0 == strcmp(ds->type, vl->type));
-
- memset(ret, 0, ret_len);
-
-#define BUFFER_ADD(...) \
- do { \
- status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \
- if (status < 1) { \
- sfree(rates); \
- return -1; \
- } else if (((size_t)status) >= (ret_len - offset)) { \
- sfree(rates); \
- return -1; \
- } else \
- offset += ((size_t)status); \
- } while (0)
-
- BUFFER_ADD("%.3f", CDTIME_T_TO_DOUBLE(vl->time));
-
- for (size_t i = 0; i < ds->ds_num; i++) {
- if (ds->ds[i].type == DS_TYPE_GAUGE)
- BUFFER_ADD(":" GAUGE_FORMAT, vl->values[i].gauge);
- else if (store_rates) {
- if (rates == NULL)
- rates = uc_get_rate(ds, vl);
- if (rates == NULL) {
- WARNING("format_values: uc_get_rate failed.");
- return -1;
- }
- BUFFER_ADD(":" GAUGE_FORMAT, rates[i]);
- } else if (ds->ds[i].type == DS_TYPE_COUNTER)
- BUFFER_ADD(":%" PRIu64, (uint64_t)vl->values[i].counter);
- else if (ds->ds[i].type == DS_TYPE_DERIVE)
- BUFFER_ADD(":%" PRIi64, vl->values[i].derive);
- else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
- BUFFER_ADD(":%" PRIu64, vl->values[i].absolute);
- else {
- ERROR("format_values: Unknown data source type: %i", ds->ds[i].type);
- sfree(rates);
- return -1;
- }
- } /* for ds->ds_num */
-
-#undef BUFFER_ADD
-
- sfree(rates);
- return 0;
-} /* }}} int format_values */
-
-int parse_identifier(char *str, char **ret_host, char **ret_plugin,
- char **ret_plugin_instance, char **ret_type,
- char **ret_type_instance, char *default_host) {
- char *hostname = NULL;
- char *plugin = NULL;
- char *plugin_instance = NULL;
- char *type = NULL;
- char *type_instance = NULL;
-
- hostname = str;
- if (hostname == NULL)
- return -1;
-
- plugin = strchr(hostname, '/');
- if (plugin == NULL)
- return -1;
- *plugin = '\0';
- plugin++;
-
- type = strchr(plugin, '/');
- if (type == NULL) {
- if (default_host == NULL)
- return -1;
- /* else: no host specified; use default */
- type = plugin;
- plugin = hostname;
- hostname = default_host;
- } else {
- *type = '\0';
- type++;
- }
-
- plugin_instance = strchr(plugin, '-');
- if (plugin_instance != NULL) {
- *plugin_instance = '\0';
- plugin_instance++;
- }
-
- type_instance = strchr(type, '-');
- if (type_instance != NULL) {
- *type_instance = '\0';
- type_instance++;
- }
-
- *ret_host = hostname;
- *ret_plugin = plugin;
- *ret_plugin_instance = plugin_instance;
- *ret_type = type;
- *ret_type_instance = type_instance;
- return 0;
-} /* int parse_identifier */
-
-int parse_identifier_vl(const char *str, value_list_t *vl) /* {{{ */
-{
- char str_copy[6 * DATA_MAX_NAME_LEN];
- char *host = NULL;
- char *plugin = NULL;
- char *plugin_instance = NULL;
- char *type = NULL;
- char *type_instance = NULL;
- int status;
-
- if ((str == NULL) || (vl == NULL))
- return EINVAL;
-
- sstrncpy(str_copy, str, sizeof(str_copy));
-
- status = parse_identifier(str_copy, &host, &plugin, &plugin_instance, &type,
- &type_instance,
- /* default_host = */ NULL);
- if (status != 0)
- return status;
-
- sstrncpy(vl->host, host, sizeof(vl->host));
- sstrncpy(vl->plugin, plugin, sizeof(vl->plugin));
- sstrncpy(vl->plugin_instance,
- (plugin_instance != NULL) ? plugin_instance : "",
- sizeof(vl->plugin_instance));
- sstrncpy(vl->type, type, sizeof(vl->type));
- sstrncpy(vl->type_instance, (type_instance != NULL) ? type_instance : "",
- sizeof(vl->type_instance));
-
- return 0;
-} /* }}} int parse_identifier_vl */
-
-int parse_value(const char *value_orig, value_t *ret_value, int ds_type) {
- char *value;
- char *endptr = NULL;
- size_t value_len;
-
- if (value_orig == NULL)
- return EINVAL;
-
- value = strdup(value_orig);
- if (value == NULL)
- return ENOMEM;
- value_len = strlen(value);
-
- while ((value_len > 0) && isspace((int)value[value_len - 1])) {
- value[value_len - 1] = '\0';
- value_len--;
- }
-
- switch (ds_type) {
- case DS_TYPE_COUNTER:
- ret_value->counter = (counter_t)strtoull(value, &endptr, 0);
- break;
-
- case DS_TYPE_GAUGE:
- ret_value->gauge = (gauge_t)strtod(value, &endptr);
- break;
-
- case DS_TYPE_DERIVE:
- ret_value->derive = (derive_t)strtoll(value, &endptr, 0);
- break;
-
- case DS_TYPE_ABSOLUTE:
- ret_value->absolute = (absolute_t)strtoull(value, &endptr, 0);
- break;
-
- default:
- sfree(value);
- P_ERROR("parse_value: Invalid data source type: %i.", ds_type);
- return -1;
- }
-
- if (value == endptr) {
- P_ERROR("parse_value: Failed to parse string as %s: \"%s\".",
- DS_TYPE_TO_STRING(ds_type), value);
- sfree(value);
- return -1;
- } else if ((NULL != endptr) && ('\0' != *endptr))
- P_INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
- "Input string was \"%s\".",
- endptr, DS_TYPE_TO_STRING(ds_type), value_orig);
-
- sfree(value);
- return 0;
-} /* int parse_value */
-
-int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds) {
- size_t i;
- char *dummy;
- char *ptr;
- char *saveptr;
-
- if ((buffer == NULL) || (vl == NULL) || (ds == NULL))
- return EINVAL;
-
- i = 0;
- dummy = buffer;
- saveptr = NULL;
- vl->time = 0;
- while ((ptr = strtok_r(dummy, ":", &saveptr)) != NULL) {
- dummy = NULL;
-
- if (i >= vl->values_len) {
- /* Make sure i is invalid. */
- i = 0;
- break;
- }
-
- if (vl->time == 0) {
- if (strcmp("N", ptr) == 0)
- vl->time = cdtime();
- else {
- char *endptr = NULL;
- double tmp;
-
- errno = 0;
- tmp = strtod(ptr, &endptr);
- if ((errno != 0) /* Overflow */
- || (endptr == ptr) /* Invalid string */
- || (endptr == NULL) /* This should not happen */
- || (*endptr != 0)) /* Trailing chars */
- return -1;
-
- vl->time = DOUBLE_TO_CDTIME_T(tmp);
- }
-
- continue;
- }
-
- if ((strcmp("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
- vl->values[i].gauge = NAN;
- else if (0 != parse_value(ptr, &vl->values[i], ds->ds[i].type))
- return -1;
-
- i++;
- } /* while (strtok_r) */
-
- if ((ptr != NULL) || (i == 0))
- return -1;
- return 0;
-} /* int parse_values */
-
-int parse_value_file(char const *path, value_t *ret_value, int ds_type) {
- FILE *fh;
- char buffer[256];
-
- fh = fopen(path, "r");
- if (fh == NULL)
- return -1;
-
- if (fgets(buffer, sizeof(buffer), fh) == NULL) {
- fclose(fh);
- return -1;
- }
-
- fclose(fh);
-
- strstripnewline(buffer);
-
- return parse_value(buffer, ret_value, ds_type);
-} /* int parse_value_file */
-
-#if !HAVE_GETPWNAM_R
-int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
- struct passwd **pwbufp) {
-#ifndef HAVE_GETPWNAM
- return -1;
-#else
- int status = 0;
- struct passwd *pw;
-
- memset(pwbuf, '\0', sizeof(struct passwd));
-
- pthread_mutex_lock(&getpwnam_r_lock);
-
- do {
- pw = getpwnam(name);
- if (pw == NULL) {
- status = (errno != 0) ? errno : ENOENT;
- break;
- }
-
-#define GETPWNAM_COPY_MEMBER(member) \
- if (pw->member != NULL) { \
- int len = strlen(pw->member); \
- if (len >= buflen) { \
- status = ENOMEM; \
- break; \
- } \
- sstrncpy(buf, pw->member, buflen); \
- pwbuf->member = buf; \
- buf += (len + 1); \
- buflen -= (len + 1); \
- }
- GETPWNAM_COPY_MEMBER(pw_name);
- GETPWNAM_COPY_MEMBER(pw_passwd);
- GETPWNAM_COPY_MEMBER(pw_gecos);
- GETPWNAM_COPY_MEMBER(pw_dir);
- GETPWNAM_COPY_MEMBER(pw_shell);
-
- pwbuf->pw_uid = pw->pw_uid;
- pwbuf->pw_gid = pw->pw_gid;
-
- if (pwbufp != NULL)
- *pwbufp = pwbuf;
- } while (0);
-
- pthread_mutex_unlock(&getpwnam_r_lock);
-
- return status;
-#endif /* HAVE_GETPWNAM */
-} /* int getpwnam_r */
-#endif /* !HAVE_GETPWNAM_R */
-
-int notification_init(notification_t *n, int severity, const char *message,
- const char *host, const char *plugin,
- const char *plugin_instance, const char *type,
- const char *type_instance) {
- memset(n, '\0', sizeof(notification_t));
-
- n->severity = severity;
-
- if (message != NULL)
- sstrncpy(n->message, message, sizeof(n->message));
- if (host != NULL)
- sstrncpy(n->host, host, sizeof(n->host));
- if (plugin != NULL)
- sstrncpy(n->plugin, plugin, sizeof(n->plugin));
- if (plugin_instance != NULL)
- sstrncpy(n->plugin_instance, plugin_instance, sizeof(n->plugin_instance));
- if (type != NULL)
- sstrncpy(n->type, type, sizeof(n->type));
- if (type_instance != NULL)
- sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
-
- return 0;
-} /* int notification_init */
-
-int walk_directory(const char *dir, dirwalk_callback_f callback,
- void *user_data, int include_hidden) {
- struct dirent *ent;
- DIR *dh;
- int success;
- int failure;
-
- success = 0;
- failure = 0;
-
- if ((dh = opendir(dir)) == NULL) {
- P_ERROR("walk_directory: Cannot open '%s': %s", dir, STRERRNO);
- return -1;
- }
-
- while ((ent = readdir(dh)) != NULL) {
- int status;
-
- 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)
- failure++;
- else
- success++;
- }
-
- closedir(dh);
-
- if ((success == 0) && (failure > 0))
- return -1;
- return 0;
-}
-
-ssize_t read_file_contents(const char *filename, char *buf, size_t bufsize) {
- FILE *fh;
- ssize_t ret;
-
- fh = fopen(filename, "r");
- if (fh == NULL)
- return -1;
-
- ret = (ssize_t)fread(buf, 1, bufsize, fh);
- if ((ret == 0) && (ferror(fh) != 0)) {
- P_ERROR("read_file_contents: Reading file \"%s\" failed.", filename);
- ret = -1;
- }
-
- fclose(fh);
- return ret;
-}
-
-counter_t counter_diff(counter_t old_value, counter_t new_value) {
- counter_t diff;
-
- if (old_value > new_value) {
- if (old_value <= 4294967295U)
- diff = (4294967295U - old_value) + new_value + 1;
- else
- diff = (18446744073709551615ULL - old_value) + new_value + 1;
- } else {
- diff = new_value - old_value;
- }
-
- return diff;
-} /* counter_t counter_diff */
-
-int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */
- rate_to_value_state_t *state, int ds_type, cdtime_t t) {
- gauge_t delta_gauge;
- cdtime_t delta_t;
-
- if (ds_type == DS_TYPE_GAUGE) {
- state->last_value.gauge = rate;
- state->last_time = t;
-
- *ret_value = state->last_value;
- return 0;
- }
-
- /* Counter and absolute can't handle negative rates. Reset "last time"
- * to zero, so that the next valid rate will re-initialize the
- * structure. */
- if ((rate < 0.0) &&
- ((ds_type == DS_TYPE_COUNTER) || (ds_type == DS_TYPE_ABSOLUTE))) {
- memset(state, 0, sizeof(*state));
- return EINVAL;
- }
-
- /* Another invalid state: The time is not increasing. */
- if (t <= state->last_time) {
- memset(state, 0, sizeof(*state));
- return EINVAL;
- }
-
- delta_t = t - state->last_time;
- delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual;
-
- /* Previous value is invalid. */
- if (state->last_time == 0) /* {{{ */
- {
- if (ds_type == DS_TYPE_DERIVE) {
- state->last_value.derive = (derive_t)rate;
- state->residual = rate - ((gauge_t)state->last_value.derive);
- } else if (ds_type == DS_TYPE_COUNTER) {
- state->last_value.counter = (counter_t)rate;
- state->residual = rate - ((gauge_t)state->last_value.counter);
- } else if (ds_type == DS_TYPE_ABSOLUTE) {
- state->last_value.absolute = (absolute_t)rate;
- state->residual = rate - ((gauge_t)state->last_value.absolute);
- } else {
- assert(23 == 42);
- }
-
- state->last_time = t;
- return EAGAIN;
- } /* }}} */
-
- if (ds_type == DS_TYPE_DERIVE) {
- derive_t delta_derive = (derive_t)delta_gauge;
-
- state->last_value.derive += delta_derive;
- state->residual = delta_gauge - ((gauge_t)delta_derive);
- } else if (ds_type == DS_TYPE_COUNTER) {
- counter_t delta_counter = (counter_t)delta_gauge;
-
- state->last_value.counter += delta_counter;
- state->residual = delta_gauge - ((gauge_t)delta_counter);
- } else if (ds_type == DS_TYPE_ABSOLUTE) {
- absolute_t delta_absolute = (absolute_t)delta_gauge;
-
- state->last_value.absolute = delta_absolute;
- state->residual = delta_gauge - ((gauge_t)delta_absolute);
- } else {
- assert(23 == 42);
- }
-
- state->last_time = t;
- *ret_value = state->last_value;
- return 0;
-} /* }}} value_t rate_to_value */
-
-int value_to_rate(gauge_t *ret_rate, /* {{{ */
- value_t value, int ds_type, cdtime_t t,
- value_to_rate_state_t *state) {
- gauge_t interval;
-
- /* Another invalid state: The time is not increasing. */
- if (t <= state->last_time) {
- memset(state, 0, sizeof(*state));
- return EINVAL;
- }
-
- interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
-
- /* Previous value is invalid. */
- if (state->last_time == 0) {
- state->last_value = value;
- state->last_time = t;
- return EAGAIN;
- }
-
- switch (ds_type) {
- case DS_TYPE_DERIVE: {
- derive_t diff = value.derive - state->last_value.derive;
- *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
- break;
- }
- case DS_TYPE_GAUGE: {
- *ret_rate = value.gauge;
- break;
- }
- case DS_TYPE_COUNTER: {
- counter_t diff = counter_diff(state->last_value.counter, value.counter);
- *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
- break;
- }
- case DS_TYPE_ABSOLUTE: {
- absolute_t diff = value.absolute;
- *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
- break;
- }
- default:
- return EINVAL;
- }
-
- state->last_value = value;
- state->last_time = t;
- return 0;
-} /* }}} value_t rate_to_value */
-
-int service_name_to_port_number(const char *service_name) {
- struct addrinfo *ai_list;
- int status;
- int service_number;
-
- if (service_name == NULL)
- return -1;
-
- struct addrinfo ai_hints = {.ai_family = AF_UNSPEC};
-
- status = getaddrinfo(/* node = */ NULL, service_name, &ai_hints, &ai_list);
- if (status != 0) {
- P_ERROR("service_name_to_port_number: getaddrinfo failed: %s",
- gai_strerror(status));
- return -1;
- }
-
- service_number = -1;
- for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
- ai_ptr = ai_ptr->ai_next) {
- if (ai_ptr->ai_family == AF_INET) {
- struct sockaddr_in *sa;
-
- sa = (void *)ai_ptr->ai_addr;
- service_number = (int)ntohs(sa->sin_port);
- } else if (ai_ptr->ai_family == AF_INET6) {
- struct sockaddr_in6 *sa;
-
- sa = (void *)ai_ptr->ai_addr;
- service_number = (int)ntohs(sa->sin6_port);
- }
-
- if ((service_number > 0) && (service_number <= 65535))
- break;
- }
-
- freeaddrinfo(ai_list);
-
- if ((service_number > 0) && (service_number <= 65535))
- return service_number;
- return -1;
-} /* int service_name_to_port_number */
-
-void set_sock_opts(int sockfd) /* {{{ */
-{
- int status;
- int socktype;
-
- status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype,
- &(socklen_t){sizeof(socktype)});
- if (status != 0) {
- P_WARNING("set_sock_opts: failed to determine socket type");
- return;
- }
-
- if (socktype == SOCK_STREAM) {
- status =
- setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int));
- if (status != 0)
- P_WARNING("set_sock_opts: failed to set socket keepalive flag");
-
-#ifdef TCP_KEEPIDLE
- int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1);
- status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepidle,
- sizeof(tcp_keepidle));
- if (status != 0)
- P_WARNING("set_sock_opts: failed to set socket tcp keepalive time");
-#endif
-
-#ifdef TCP_KEEPINTVL
- int tcp_keepintvl =
- ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1);
- status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepintvl,
- sizeof(tcp_keepintvl));
- if (status != 0)
- P_WARNING("set_sock_opts: failed to set socket tcp keepalive interval");
-#endif
- }
-} /* }}} void set_sock_opts */
-
-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 */
-
-int strtogauge(const char *string, gauge_t *ret_value) /* {{{ */
-{
- gauge_t tmp;
- char *endptr = NULL;
-
- if ((string == NULL) || (ret_value == NULL))
- return EINVAL;
-
- errno = 0;
- endptr = NULL;
- tmp = (gauge_t)strtod(string, &endptr);
- if (errno != 0)
- return errno;
- else if ((endptr == NULL) || (*endptr != 0))
- return EINVAL;
-
- *ret_value = tmp;
- return 0;
-} /* }}} int strtogauge */
-
-int strarray_add(char ***ret_array, size_t *ret_array_len,
- char const *str) /* {{{ */
-{
- char **array;
- size_t array_len = *ret_array_len;
-
- if (str == NULL)
- return EINVAL;
-
- array = realloc(*ret_array, (array_len + 1) * sizeof(*array));
- if (array == NULL)
- return ENOMEM;
- *ret_array = array;
-
- array[array_len] = strdup(str);
- if (array[array_len] == NULL)
- return ENOMEM;
-
- array_len++;
- *ret_array_len = array_len;
- return 0;
-} /* }}} int strarray_add */
-
-void strarray_free(char **array, size_t array_len) /* {{{ */
-{
- for (size_t i = 0; i < array_len; i++)
- sfree(array[i]);
- sfree(array);
-} /* }}} void strarray_free */
-
-#if HAVE_CAPABILITY
-int check_capability(int arg) /* {{{ */
-{
- cap_value_t cap_value = (cap_value_t)arg;
- cap_t cap;
- cap_flag_value_t cap_flag_value;
-
- if (!CAP_IS_SUPPORTED(cap_value))
- return -1;
-
- if (!(cap = cap_get_proc())) {
- P_ERROR("check_capability: cap_get_proc failed.");
- return -1;
- }
-
- if (cap_get_flag(cap, cap_value, CAP_EFFECTIVE, &cap_flag_value) < 0) {
- P_ERROR("check_capability: cap_get_flag failed.");
- cap_free(cap);
- return -1;
- }
- cap_free(cap);
-
- return cap_flag_value != CAP_SET;
-} /* }}} int check_capability */
-#else
-int check_capability(__attribute__((unused)) int arg) /* {{{ */
-{
- P_WARNING("check_capability: unsupported capability implementation. "
- "Some plugin(s) may require elevated privileges to work properly.");
- return 0;
-} /* }}} int check_capability */
-#endif /* HAVE_CAPABILITY */
+++ /dev/null
-/**
- * collectd - src/common.h
- * Copyright (C) 2005-2014 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- * Niki W. Waibel <niki.waibel@gmx.net>
-**/
-
-#ifndef COMMON_H
-#define COMMON_H
-
-#include "collectd.h"
-
-#include "plugin.h"
-
-#if HAVE_PWD_H
-#include <pwd.h>
-#endif
-
-#define sfree(ptr) \
- do { \
- free(ptr); \
- (ptr) = NULL; \
- } while (0)
-
-#define STATIC_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
-
-#define IS_TRUE(s) \
- ((strcasecmp("true", (s)) == 0) || (strcasecmp("yes", (s)) == 0) || \
- (strcasecmp("on", (s)) == 0))
-#define IS_FALSE(s) \
- ((strcasecmp("false", (s)) == 0) || (strcasecmp("no", (s)) == 0) || \
- (strcasecmp("off", (s)) == 0))
-
-struct rate_to_value_state_s {
- value_t last_value;
- cdtime_t last_time;
- gauge_t residual;
-};
-typedef struct rate_to_value_state_s rate_to_value_state_t;
-
-struct value_to_rate_state_s {
- value_t last_value;
- cdtime_t last_time;
-};
-typedef struct value_to_rate_state_s value_to_rate_state_t;
-
-char *sstrncpy(char *dest, const char *src, size_t n);
-
-__attribute__((format(printf, 1, 2))) char *ssnprintf_alloc(char const *format,
- ...);
-
-char *sstrdup(const char *s);
-void *smalloc(size_t size);
-char *sstrerror(int errnum, char *buf, size_t buflen);
-
-#ifndef ERRBUF_SIZE
-#define ERRBUF_SIZE 256
-#endif
-
-#define STRERROR(e) sstrerror((e), (char[ERRBUF_SIZE]){0}, ERRBUF_SIZE)
-#define STRERRNO STRERROR(errno)
-
-/*
- * NAME
- * sread
- *
- * DESCRIPTION
- * Reads exactly `n' bytes or fails. Syntax and other behavior is analogous
- * to `read(2)'.
- *
- * PARAMETERS
- * `fd' File descriptor to write to.
- * `buf' Buffer that is to be written.
- * `count' Number of bytes in the buffer.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred. `errno' is set in this
- * case.
- */
-int sread(int fd, void *buf, size_t count);
-
-/*
- * NAME
- * swrite
- *
- * DESCRIPTION
- * Writes exactly `n' bytes or fails. Syntax and other behavior is analogous
- * to `write(2)'.
- *
- * PARAMETERS
- * `fd' File descriptor to write to.
- * `buf' Buffer that is to be written.
- * `count' Number of bytes in the buffer.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred. `errno' is set in this
- * case.
- */
-int swrite(int fd, const void *buf, size_t count);
-
-/*
- * NAME
- * strsplit
- *
- * DESCRIPTION
- * Splits a string into parts and stores pointers to the parts in `fields'.
- * The characters split at are: " ", "\t", "\r", and "\n".
- *
- * PARAMETERS
- * `string' String to split. This string will be modified. `fields' will
- * contain pointers to parts of this string, so free'ing it
- * will destroy `fields' as well.
- * `fields' Array of strings where pointers to the parts will be stored.
- * `size' Number of elements in the array. No more than `size'
- * pointers will be stored in `fields'.
- *
- * RETURN VALUE
- * Returns the number of parts stored in `fields'.
- */
-int strsplit(char *string, char **fields, size_t size);
-
-/*
- * NAME
- * strjoin
- *
- * DESCRIPTION
- * Joins together several parts of a string using `sep' as a separator. This
- * is equivalent to the Perl built-in `join'.
- *
- * PARAMETERS
- * `dst' Buffer where the result is stored. Can be NULL if you need to
- * determine the required buffer size only.
- * `dst_len' Length of the destination buffer. No more than this many
- * bytes will be written to the memory pointed to by `dst',
- * including the trailing null-byte. Must be zero if dst is
- * NULL.
- * `fields' Array of strings to be joined.
- * `fields_num' Number of elements in the `fields' array.
- * `sep' String to be inserted between any two elements of `fields'.
- * This string is neither prepended nor appended to the result.
- * Instead of passing "" (empty string) one can pass NULL.
- *
- * RETURN VALUE
- * Returns the number of characters in the resulting string, excluding a
- * tailing null byte. If this value is greater than or equal to "dst_len", the
- * result in "dst" is truncated (but still null terminated). On error a
- * negative value is returned.
- */
-int strjoin(char *dst, size_t dst_len, char **fields, size_t fields_num,
- const char *sep);
-
-/*
- * NAME
- * escape_slashes
- *
- * DESCRIPTION
- * Removes slashes ("/") from "buffer". If buffer contains a single slash,
- * the result will be "root". Leading slashes are removed. All other slashes
- * are replaced with underscores ("_").
- * This function is used by plugin_dispatch_values() to escape all parts of
- * the identifier.
- *
- * PARAMETERS
- * `buffer' String to be escaped.
- * `buffer_size' Size of the buffer. No more then this many bytes will be
- * written to `buffer', including the trailing null-byte.
- *
- * RETURN VALUE
- * Returns zero upon success and a value smaller than zero upon failure.
- */
-int escape_slashes(char *buffer, size_t buffer_size);
-
-/**
- * NAME
- * escape_string
- *
- * DESCRIPTION
- * escape_string quotes and escapes a string to be usable with collectd's
- * plain text protocol. "simple" strings are left as they are, for example if
- * buffer is 'simple' before the call, it will remain 'simple'. However, if
- * buffer contains 'more "complex"' before the call, the returned buffer will
- * contain '"more \"complex\""'.
- *
- * If the buffer is too small to contain the escaped string, the string will
- * be truncated. However, leading and trailing double quotes, as well as an
- * ending null byte are guaranteed.
- *
- * RETURN VALUE
- * Returns zero on success, even if the string was truncated. Non-zero on
- * failure.
- */
-int escape_string(char *buffer, size_t buffer_size);
-
-/*
- * NAME
- * replace_special
- *
- * DESCRIPTION
- * Replaces any special characters (anything that's not alpha-numeric or a
- * dash) with an underscore.
- *
- * E.g. "foo$bar&" would become "foo_bar_".
- *
- * PARAMETERS
- * `buffer' String to be handled.
- * `buffer_size' Length of the string. The function returns after
- * encountering a null-byte or reading this many bytes.
- */
-void replace_special(char *buffer, size_t buffer_size);
-
-/*
- * NAME
- * strunescape
- *
- * DESCRIPTION
- * Replaces any escaped characters in a string with the appropriate special
- * characters. The following escaped characters are recognized:
- *
- * \t -> <tab>
- * \n -> <newline>
- * \r -> <carriage return>
- *
- * For all other escacped characters only the backslash will be removed.
- *
- * PARAMETERS
- * `buf' String to be unescaped.
- * `buf_len' Length of the string, including the terminating null-byte.
- *
- * RETURN VALUE
- * Returns zero upon success, a value less than zero else.
- */
-int strunescape(char *buf, size_t buf_len);
-
-/**
- * Removed trailing newline characters (CR and LF) from buffer, which must be
- * null terminated. Returns the length of the resulting string.
- */
-__attribute__((nonnull(1))) size_t strstripnewline(char *buffer);
-
-/*
- * NAME
- * timeval_cmp
- *
- * DESCRIPTION
- * Compare the two time values `tv0' and `tv1' and store the absolut value
- * of the difference in the time value pointed to by `delta' if it does not
- * equal NULL.
- *
- * RETURN VALUE
- * Returns an integer less than, equal to, or greater than zero if `tv0' is
- * less than, equal to, or greater than `tv1' respectively.
- */
-int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta);
-
-/* make sure tv_usec stores less than a second */
-#define NORMALIZE_TIMEVAL(tv) \
- do { \
- (tv).tv_sec += (tv).tv_usec / 1000000; \
- (tv).tv_usec = (tv).tv_usec % 1000000; \
- } while (0)
-
-/* make sure tv_sec stores less than a second */
-#define NORMALIZE_TIMESPEC(tv) \
- do { \
- (tv).tv_sec += (tv).tv_nsec / 1000000000; \
- (tv).tv_nsec = (tv).tv_nsec % 1000000000; \
- } while (0)
-
-int check_create_dir(const char *file_orig);
-
-#ifdef HAVE_LIBKSTAT
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name);
-long long get_kstat_value(kstat_t *ksp, char *name);
-#endif
-
-#ifndef HAVE_HTONLL
-unsigned long long ntohll(unsigned long long n);
-unsigned long long htonll(unsigned long long n);
-#endif
-
-#if FP_LAYOUT_NEED_NOTHING
-#define ntohd(d) (d)
-#define htond(d) (d)
-#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
-double ntohd(double d);
-double htond(double d);
-#else
-#error \
- "Don't know how to convert between host and network representation of doubles."
-#endif
-
-int format_name(char *ret, int ret_len, const char *hostname,
- const char *plugin, const char *plugin_instance,
- const char *type, const char *type_instance);
-#define FORMAT_VL(ret, ret_len, vl) \
- format_name(ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
- (vl)->type, (vl)->type_instance)
-int format_values(char *ret, size_t ret_len, const data_set_t *ds,
- const value_list_t *vl, bool store_rates);
-
-int parse_identifier(char *str, char **ret_host, char **ret_plugin,
- char **ret_plugin_instance, char **ret_type,
- char **ret_type_instance, char *default_host);
-int parse_identifier_vl(const char *str, value_list_t *vl);
-int parse_value(const char *value, value_t *ret_value, int ds_type);
-int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds);
-
-/* parse_value_file reads "path" and parses its content as an integer or
- * floating point, depending on "ds_type". On success, the value is stored in
- * "ret_value" and zero is returned. On failure, a non-zero value is returned.
- */
-int parse_value_file(char const *path, value_t *ret_value, int ds_type);
-
-#if !HAVE_GETPWNAM_R
-struct passwd;
-int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
- struct passwd **pwbufp);
-#endif
-
-int notification_init(notification_t *n, int severity, const char *message,
- const char *host, const char *plugin,
- const char *plugin_instance, const char *type,
- const char *type_instance);
-#define NOTIFICATION_INIT_VL(n, vl) \
- notification_init(n, NOTIF_FAILURE, NULL, (vl)->host, (vl)->plugin, \
- (vl)->plugin_instance, (vl)->type, (vl)->type_instance)
-
-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, int hidden);
-/* Returns the number of bytes read or negative on error. */
-ssize_t read_file_contents(char const *filename, char *buf, size_t bufsize);
-
-counter_t counter_diff(counter_t old_value, counter_t new_value);
-
-/* Convert a rate back to a value_t. When converting to a derive_t, counter_t
- * or absolute_t, take fractional residuals into account. This is important
- * when scaling counters, for example.
- * Returns zero on success. Returns EAGAIN when called for the first time; in
- * this case the value_t is invalid and the next call should succeed. Other
- * return values indicate an error. */
-int rate_to_value(value_t *ret_value, gauge_t rate,
- rate_to_value_state_t *state, int ds_type, cdtime_t t);
-
-int value_to_rate(gauge_t *ret_rate, value_t value, int ds_type, cdtime_t t,
- value_to_rate_state_t *state);
-
-/* Converts a service name (a string) to a port number
- * (in the range [1-65535]). Returns less than zero on error. */
-int service_name_to_port_number(const char *service_name);
-
-/* Sets various, non-default, socket options */
-void set_sock_opts(int sockfd);
-
-/** Parse a string to a derive_t value. Returns zero on success or non-zero on
- * failure. If failure is returned, ret_value is not touched. */
-int strtoderive(const char *string, derive_t *ret_value);
-
-/** Parse a string to a gauge_t value. Returns zero on success or non-zero on
- * failure. If failure is returned, ret_value is not touched. */
-int strtogauge(const char *string, gauge_t *ret_value);
-
-int strarray_add(char ***ret_array, size_t *ret_array_len, char const *str);
-void strarray_free(char **array, size_t array_len);
-
-/** Check if the current process benefits from the capability passed in
- * argument. Returns zero if it does, less than zero if it doesn't or on error.
- * See capabilities(7) for the list of possible capabilities.
- * */
-int check_capability(int arg);
-
-#endif /* COMMON_H */
+++ /dev/null
-/**
- * collectd - src/tests/test_common.c
- * Copyright (C) 2013 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#include "common.h"
-#include "testing.h"
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-
-#if HAVE_LIBKSTAT
-kstat_ctl_t *kc;
-#endif /* HAVE_LIBKSTAT */
-
-DEF_TEST(sstrncpy) {
- char buffer[16] = "";
- char *ptr = &buffer[4];
- char *ret;
-
- buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff;
- buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff;
-
- ret = sstrncpy(ptr, "foobar", 8);
- OK(ret == ptr);
- EXPECT_EQ_STR("foobar", ptr);
- OK(buffer[3] == buffer[12]);
-
- ret = sstrncpy(ptr, "abc", 8);
- OK(ret == ptr);
- EXPECT_EQ_STR("abc", ptr);
- OK(buffer[3] == buffer[12]);
-
- ret = sstrncpy(ptr, "collectd", 8);
- OK(ret == ptr);
- OK(ptr[7] == 0);
- EXPECT_EQ_STR("collect", ptr);
- OK(buffer[3] == buffer[12]);
-
- return 0;
-}
-
-DEF_TEST(sstrdup) {
- char *ptr;
-
- ptr = sstrdup("collectd");
- OK(ptr != NULL);
- EXPECT_EQ_STR("collectd", ptr);
-
- sfree(ptr);
-
- ptr = sstrdup(NULL);
- OK(ptr == NULL);
-
- return 0;
-}
-
-DEF_TEST(strsplit) {
- char buffer[32];
- char *fields[8];
- int status;
-
- strncpy(buffer, "foo bar", sizeof(buffer));
- status = strsplit(buffer, fields, 8);
- OK(status == 2);
- EXPECT_EQ_STR("foo", fields[0]);
- EXPECT_EQ_STR("bar", fields[1]);
-
- strncpy(buffer, "foo \t bar", sizeof(buffer));
- status = strsplit(buffer, fields, 8);
- OK(status == 2);
- EXPECT_EQ_STR("foo", fields[0]);
- EXPECT_EQ_STR("bar", fields[1]);
-
- strncpy(buffer, "one two\tthree\rfour\nfive", sizeof(buffer));
- status = strsplit(buffer, fields, 8);
- OK(status == 5);
- EXPECT_EQ_STR("one", fields[0]);
- EXPECT_EQ_STR("two", fields[1]);
- EXPECT_EQ_STR("three", fields[2]);
- EXPECT_EQ_STR("four", fields[3]);
- EXPECT_EQ_STR("five", fields[4]);
-
- strncpy(buffer, "\twith trailing\n", sizeof(buffer));
- status = strsplit(buffer, fields, 8);
- OK(status == 2);
- EXPECT_EQ_STR("with", fields[0]);
- EXPECT_EQ_STR("trailing", fields[1]);
-
- strncpy(buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof(buffer));
- status = strsplit(buffer, fields, 8);
- OK(status == 8);
- EXPECT_EQ_STR("7", fields[6]);
- EXPECT_EQ_STR("8", fields[7]);
-
- strncpy(buffer, "single", sizeof(buffer));
- status = strsplit(buffer, fields, 8);
- OK(status == 1);
- EXPECT_EQ_STR("single", fields[0]);
-
- strncpy(buffer, "", sizeof(buffer));
- status = strsplit(buffer, fields, 8);
- OK(status == 0);
-
- return 0;
-}
-
-DEF_TEST(strjoin) {
- struct {
- char **fields;
- size_t fields_num;
- char *separator;
-
- int want_return;
- char *want_buffer;
- } cases[] = {
- /* Normal case. */
- {(char *[]){"foo", "bar"}, 2, "!", 7, "foo!bar"},
- /* One field only. */
- {(char *[]){"foo"}, 1, "!", 3, "foo"},
- /* No fields at all. */
- {NULL, 0, "!", 0, ""},
- /* Longer separator. */
- {(char *[]){"foo", "bar"}, 2, "rcht", 10, "foorchtbar"},
- /* Empty separator. */
- {(char *[]){"foo", "bar"}, 2, "", 6, "foobar"},
- /* NULL separator. */
- {(char *[]){"foo", "bar"}, 2, NULL, 6, "foobar"},
- /* buffer not large enough -> string is truncated. */
- {(char *[]){"aaaaaa", "bbbbbb", "c!"}, 3, "-", 16, "aaaaaa-bbbbbb-c"},
- /* buffer not large enough -> last field fills buffer completely. */
- {(char *[]){"aaaaaaa", "bbbbbbb", "!"}, 3, "-", 17, "aaaaaaa-bbbbbbb"},
- /* buffer not large enough -> string does *not* end in separator. */
- {(char *[]){"aaaa", "bbbb", "cccc", "!"}, 4, "-", 16, "aaaa-bbbb-cccc"},
- /* buffer not large enough -> string does not end with partial
- separator. */
- {(char *[]){"aaaaaa", "bbbbbb", "!"}, 3, "+-", 17, "aaaaaa+-bbbbbb"},
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- char buffer[16];
- int status;
-
- memset(buffer, 0xFF, sizeof(buffer));
- status = strjoin(buffer, sizeof(buffer), cases[i].fields,
- cases[i].fields_num, cases[i].separator);
- EXPECT_EQ_INT(cases[i].want_return, status);
- EXPECT_EQ_STR(cases[i].want_buffer, buffer);
-
- /* use (NULL, 0) to determine required buffer size. */
- EXPECT_EQ_INT(cases[i].want_return,
- strjoin(NULL, 0, cases[i].fields, cases[i].fields_num,
- cases[i].separator));
- }
-
- return 0;
-}
-
-DEF_TEST(escape_slashes) {
- struct {
- char *str;
- char *want;
- } cases[] = {
- {"foo/bar/baz", "foo_bar_baz"},
- {"/like/a/path", "like_a_path"},
- {"trailing/slash/", "trailing_slash_"},
- {"foo//bar", "foo__bar"},
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- char buffer[32];
-
- strncpy(buffer, cases[i].str, sizeof(buffer));
- OK(escape_slashes(buffer, sizeof(buffer)) == 0);
- EXPECT_EQ_STR(cases[i].want, buffer);
- }
-
- return 0;
-}
-
-DEF_TEST(escape_string) {
- struct {
- char *str;
- char *want;
- } cases[] = {
- {"foobar", "foobar"},
- {"f00bar", "f00bar"},
- {"foo bar", "\"foo bar\""},
- {"foo \"bar\"", "\"foo \\\"bar\\\"\""},
- {"012345678901234", "012345678901234"},
- {"012345 78901234", "\"012345 789012\""},
- {"012345 78901\"34", "\"012345 78901\""},
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- char buffer[16];
-
- strncpy(buffer, cases[i].str, sizeof(buffer));
- OK(escape_string(buffer, sizeof(buffer)) == 0);
- EXPECT_EQ_STR(cases[i].want, buffer);
- }
-
- return 0;
-}
-
-DEF_TEST(strunescape) {
- char buffer[16];
- int status;
-
- strncpy(buffer, "foo\\tbar", sizeof(buffer));
- status = strunescape(buffer, sizeof(buffer));
- OK(status == 0);
- EXPECT_EQ_STR("foo\tbar", buffer);
-
- strncpy(buffer, "\\tfoo\\r\\n", sizeof(buffer));
- status = strunescape(buffer, sizeof(buffer));
- OK(status == 0);
- EXPECT_EQ_STR("\tfoo\r\n", buffer);
-
- strncpy(buffer, "With \\\"quotes\\\"", sizeof(buffer));
- status = strunescape(buffer, sizeof(buffer));
- OK(status == 0);
- EXPECT_EQ_STR("With \"quotes\"", buffer);
-
- /* Backslash before null byte */
- strncpy(buffer, "\\tbackslash end\\", sizeof(buffer));
- status = strunescape(buffer, sizeof(buffer));
- OK(status != 0);
- EXPECT_EQ_STR("\tbackslash end", buffer);
- return 0;
-
- /* Backslash at buffer end */
- strncpy(buffer, "\\t3\\56", sizeof(buffer));
- status = strunescape(buffer, 4);
- OK(status != 0);
- OK(buffer[0] == '\t');
- OK(buffer[1] == '3');
- OK(buffer[2] == 0);
- OK(buffer[3] == 0);
- OK(buffer[4] == '5');
- OK(buffer[5] == '6');
- OK(buffer[6] == '7');
-
- return 0;
-}
-
-DEF_TEST(parse_values) {
- struct {
- char buffer[64];
- int status;
- gauge_t value;
- } cases[] = {
- {"1435044576:42", 0, 42.0}, {"1435044576:42:23", -1, NAN},
- {"1435044576:U", 0, NAN}, {"N:12.3", 0, 12.3},
- {"N:42.0:23", -1, NAN}, {"N:U", 0, NAN},
- {"T:42.0", -1, NAN},
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- data_source_t dsrc = {
- .name = "value", .type = DS_TYPE_GAUGE, .min = 0.0, .max = NAN,
- };
- data_set_t ds = {
- .type = "example", .ds_num = 1, .ds = &dsrc,
- };
-
- value_t v = {
- .gauge = NAN,
- };
- value_list_t vl = {
- .values = &v,
- .values_len = 1,
- .time = 0,
- .interval = 0,
- .host = "example.com",
- .plugin = "common_test",
- .type = "example",
- .meta = NULL,
- };
-
- int status = parse_values(cases[i].buffer, &vl, &ds);
- EXPECT_EQ_INT(cases[i].status, status);
- if (status != 0)
- continue;
-
- EXPECT_EQ_DOUBLE(cases[i].value, vl.values[0].gauge);
- }
-
- return 0;
-}
-
-DEF_TEST(value_to_rate) {
- struct {
- time_t t0;
- time_t t1;
- int ds_type;
- value_t v0;
- value_t v1;
- gauge_t want;
- } cases[] = {
- {0, 10, DS_TYPE_DERIVE, {.derive = 0}, {.derive = 1000}, NAN},
- {10, 20, DS_TYPE_DERIVE, {.derive = 1000}, {.derive = 2000}, 100.0},
- {20, 30, DS_TYPE_DERIVE, {.derive = 2000}, {.derive = 1800}, -20.0},
- {0, 10, DS_TYPE_COUNTER, {.counter = 0}, {.counter = 1000}, NAN},
- {10, 20, DS_TYPE_COUNTER, {.counter = 1000}, {.counter = 5000}, 400.0},
- /* 32bit wrap-around. */
- {20,
- 30,
- DS_TYPE_COUNTER,
- {.counter = 4294967238ULL},
- {.counter = 42},
- 10.0},
- /* 64bit wrap-around. */
- {30,
- 40,
- DS_TYPE_COUNTER,
- {.counter = 18446744073709551558ULL},
- {.counter = 42},
- 10.0},
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- cdtime_t t0 = TIME_T_TO_CDTIME_T(cases[i].t0);
- value_to_rate_state_t state = {
- .last_value = cases[i].v0, .last_time = t0,
- };
- gauge_t got;
-
- if (cases[i].t0 == 0) {
- EXPECT_EQ_INT(EAGAIN,
- value_to_rate(&got, cases[i].v1, cases[i].ds_type,
- TIME_T_TO_CDTIME_T(cases[i].t1), &state));
- continue;
- }
-
- EXPECT_EQ_INT(0, value_to_rate(&got, cases[i].v1, cases[i].ds_type,
- TIME_T_TO_CDTIME_T(cases[i].t1), &state));
- EXPECT_EQ_DOUBLE(cases[i].want, got);
- }
-
- return 0;
-}
-
-int main(void) {
- RUN_TEST(sstrncpy);
- RUN_TEST(sstrdup);
- RUN_TEST(strsplit);
- RUN_TEST(strjoin);
- RUN_TEST(escape_slashes);
- RUN_TEST(escape_string);
- RUN_TEST(strunescape);
- RUN_TEST(parse_values);
- RUN_TEST(value_to_rate);
-
- END_TEST;
-}
#include "liboconfig/oconfig.h"
-#include "common.h"
#include "configfile.h"
#include "filter_chain.h"
#include "plugin.h"
#include "types_list.h"
+#include "utils/common/common.h"
#if HAVE_WORDEXP_H
#include <wordexp.h>
#include "collectd.h"
-#include "common.h"
#include "configfile.h"
#include "filter_chain.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_complain.h"
/*
* DEALINGS IN THE SOFTWARE.
**/
-#include "common.h"
#include "globals.h"
+#include "utils/common/common.h"
#if HAVE_KSTAT_H
#include <kstat.h>
+++ /dev/null
-/**
- * collectd - src/meta_data.c
- * Copyright (C) 2008-2011 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "meta_data.h"
-#include "plugin.h"
-
-#define MD_MAX_NONSTRING_CHARS 128
-
-/*
- * Data types
- */
-union meta_value_u {
- char *mv_string;
- int64_t mv_signed_int;
- uint64_t mv_unsigned_int;
- double mv_double;
- bool mv_boolean;
-};
-typedef union meta_value_u meta_value_t;
-
-struct meta_entry_s;
-typedef struct meta_entry_s meta_entry_t;
-struct meta_entry_s {
- char *key;
- meta_value_t value;
- int type;
- meta_entry_t *next;
-};
-
-struct meta_data_s {
- meta_entry_t *head;
- pthread_mutex_t lock;
-};
-
-/*
- * Private functions
- */
-static char *md_strdup(const char *orig) /* {{{ */
-{
- size_t sz;
- char *dest;
-
- if (orig == NULL)
- return NULL;
-
- sz = strlen(orig) + 1;
- dest = malloc(sz);
- if (dest == NULL)
- return NULL;
-
- memcpy(dest, orig, sz);
-
- return dest;
-} /* }}} char *md_strdup */
-
-static meta_entry_t *md_entry_alloc(const char *key) /* {{{ */
-{
- meta_entry_t *e;
-
- e = calloc(1, sizeof(*e));
- if (e == NULL) {
- ERROR("md_entry_alloc: calloc failed.");
- return NULL;
- }
-
- e->key = md_strdup(key);
- if (e->key == NULL) {
- free(e);
- ERROR("md_entry_alloc: md_strdup failed.");
- return NULL;
- }
-
- e->type = 0;
- e->next = NULL;
-
- return e;
-} /* }}} meta_entry_t *md_entry_alloc */
-
-/* XXX: The lock on md must be held while calling this function! */
-static meta_entry_t *md_entry_clone_contents(const meta_entry_t *orig) /* {{{ */
-{
- meta_entry_t *copy;
-
- /* WARNINGS :
- * - we do not check that orig != NULL here. You should have done it before.
- * - we do not set copy->next. DO NOT FORGET TO SET copy->next IN YOUR
- * FUNCTION
- */
-
- copy = md_entry_alloc(orig->key);
- if (copy == NULL)
- return NULL;
- copy->type = orig->type;
- if (copy->type == MD_TYPE_STRING)
- copy->value.mv_string = strdup(orig->value.mv_string);
- else
- copy->value = orig->value;
-
- return copy;
-} /* }}} meta_entry_t *md_entry_clone_contents */
-
-static meta_entry_t *md_entry_clone(const meta_entry_t *orig) /* {{{ */
-{
- meta_entry_t *copy;
-
- if (orig == NULL)
- return NULL;
-
- copy = md_entry_clone_contents(orig);
-
- copy->next = md_entry_clone(orig->next);
- return copy;
-} /* }}} meta_entry_t *md_entry_clone */
-
-static void md_entry_free(meta_entry_t *e) /* {{{ */
-{
- if (e == NULL)
- return;
-
- free(e->key);
-
- if (e->type == MD_TYPE_STRING)
- free(e->value.mv_string);
-
- if (e->next != NULL)
- md_entry_free(e->next);
-
- free(e);
-} /* }}} void md_entry_free */
-
-static int md_entry_insert(meta_data_t *md, meta_entry_t *e) /* {{{ */
-{
- meta_entry_t *this;
- meta_entry_t *prev;
-
- if ((md == NULL) || (e == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- prev = NULL;
- this = md->head;
- while (this != NULL) {
- if (strcasecmp(e->key, this->key) == 0)
- break;
-
- prev = this;
- this = this->next;
- }
-
- if (this == NULL) {
- /* This key does not exist yet. */
- if (md->head == NULL)
- md->head = e;
- else {
- assert(prev != NULL);
- prev->next = e;
- }
-
- e->next = NULL;
- } else /* (this != NULL) */
- {
- if (prev == NULL)
- md->head = e;
- else
- prev->next = e;
-
- e->next = this->next;
- }
-
- pthread_mutex_unlock(&md->lock);
-
- if (this != NULL) {
- this->next = NULL;
- md_entry_free(this);
- }
-
- return 0;
-} /* }}} int md_entry_insert */
-
-/* XXX: The lock on md must be held while calling this function! */
-static int md_entry_insert_clone(meta_data_t *md, meta_entry_t *orig) /* {{{ */
-{
- meta_entry_t *e;
- meta_entry_t *this;
- meta_entry_t *prev;
-
- /* WARNINGS :
- * - we do not check that md and e != NULL here. You should have done it
- * before.
- * - we do not use the lock. You should have set it before.
- */
-
- e = md_entry_clone_contents(orig);
-
- prev = NULL;
- this = md->head;
- while (this != NULL) {
- if (strcasecmp(e->key, this->key) == 0)
- break;
-
- prev = this;
- this = this->next;
- }
-
- if (this == NULL) {
- /* This key does not exist yet. */
- if (md->head == NULL)
- md->head = e;
- else {
- assert(prev != NULL);
- prev->next = e;
- }
-
- e->next = NULL;
- } else /* (this != NULL) */
- {
- if (prev == NULL)
- md->head = e;
- else
- prev->next = e;
-
- e->next = this->next;
- }
-
- if (this != NULL) {
- this->next = NULL;
- md_entry_free(this);
- }
-
- return 0;
-} /* }}} int md_entry_insert_clone */
-
-/* XXX: The lock on md must be held while calling this function! */
-static meta_entry_t *md_entry_lookup(meta_data_t *md, /* {{{ */
- const char *key) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL))
- return NULL;
-
- for (e = md->head; e != NULL; e = e->next)
- if (strcasecmp(key, e->key) == 0)
- break;
-
- return e;
-} /* }}} meta_entry_t *md_entry_lookup */
-
-/*
- * Each value_list_t*, as it is going through the system, is handled by exactly
- * one thread. Plugins which pass a value_list_t* to another thread, e.g. the
- * rrdtool plugin, must create a copy first. The meta data within a
- * value_list_t* is not thread safe and doesn't need to be.
- *
- * The meta data associated with cache entries are a different story. There, we
- * need to ensure exclusive locking to prevent leaks and other funky business.
- * This is ensured by the uc_meta_data_get_*() functions.
- */
-
-/*
- * Public functions
- */
-meta_data_t *meta_data_create(void) /* {{{ */
-{
- meta_data_t *md;
-
- md = calloc(1, sizeof(*md));
- if (md == NULL) {
- ERROR("meta_data_create: calloc failed.");
- return NULL;
- }
-
- pthread_mutex_init(&md->lock, /* attr = */ NULL);
-
- return md;
-} /* }}} meta_data_t *meta_data_create */
-
-meta_data_t *meta_data_clone(meta_data_t *orig) /* {{{ */
-{
- meta_data_t *copy;
-
- if (orig == NULL)
- return NULL;
-
- copy = meta_data_create();
- if (copy == NULL)
- return NULL;
-
- pthread_mutex_lock(&orig->lock);
- copy->head = md_entry_clone(orig->head);
- pthread_mutex_unlock(&orig->lock);
-
- return copy;
-} /* }}} meta_data_t *meta_data_clone */
-
-int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig) /* {{{ */
-{
- if (orig == NULL)
- return 0;
-
- if (*dest == NULL) {
- *dest = meta_data_clone(orig);
- return 0;
- }
-
- pthread_mutex_lock(&orig->lock);
- for (meta_entry_t *e = orig->head; e != NULL; e = e->next) {
- md_entry_insert_clone((*dest), e);
- }
- pthread_mutex_unlock(&orig->lock);
-
- return 0;
-} /* }}} int meta_data_clone_merge */
-
-void meta_data_destroy(meta_data_t *md) /* {{{ */
-{
- if (md == NULL)
- return;
-
- md_entry_free(md->head);
- pthread_mutex_destroy(&md->lock);
- free(md);
-} /* }}} void meta_data_destroy */
-
-int meta_data_exists(meta_data_t *md, const char *key) /* {{{ */
-{
- if ((md == NULL) || (key == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- for (meta_entry_t *e = md->head; e != NULL; e = e->next) {
- if (strcasecmp(key, e->key) == 0) {
- pthread_mutex_unlock(&md->lock);
- return 1;
- }
- }
-
- pthread_mutex_unlock(&md->lock);
- return 0;
-} /* }}} int meta_data_exists */
-
-int meta_data_type(meta_data_t *md, const char *key) /* {{{ */
-{
- if ((md == NULL) || (key == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- for (meta_entry_t *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;
-
- if ((md == NULL) || (toc == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- for (meta_entry_t *e = md->head; e != NULL; e = e->next)
- ++count;
-
- if (count == 0) {
- pthread_mutex_unlock(&md->lock);
- return count;
- }
-
- *toc = calloc(count, sizeof(**toc));
- for (meta_entry_t *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;
- meta_entry_t *prev;
-
- if ((md == NULL) || (key == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- prev = NULL;
- this = md->head;
- while (this != NULL) {
- if (strcasecmp(key, this->key) == 0)
- break;
-
- prev = this;
- this = this->next;
- }
-
- if (this == NULL) {
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- if (prev == NULL)
- md->head = this->next;
- else
- prev->next = this->next;
-
- pthread_mutex_unlock(&md->lock);
-
- this->next = NULL;
- md_entry_free(this);
-
- return 0;
-} /* }}} int meta_data_delete */
-
-/*
- * Add functions
- */
-int meta_data_add_string(meta_data_t *md, /* {{{ */
- const char *key, const char *value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL) || (value == NULL))
- return -EINVAL;
-
- e = md_entry_alloc(key);
- if (e == NULL)
- return -ENOMEM;
-
- e->value.mv_string = md_strdup(value);
- if (e->value.mv_string == NULL) {
- ERROR("meta_data_add_string: md_strdup failed.");
- md_entry_free(e);
- return -ENOMEM;
- }
- e->type = MD_TYPE_STRING;
-
- return md_entry_insert(md, e);
-} /* }}} int meta_data_add_string */
-
-int meta_data_add_signed_int(meta_data_t *md, /* {{{ */
- const char *key, int64_t value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL))
- return -EINVAL;
-
- e = md_entry_alloc(key);
- if (e == NULL)
- return -ENOMEM;
-
- e->value.mv_signed_int = value;
- e->type = MD_TYPE_SIGNED_INT;
-
- return md_entry_insert(md, e);
-} /* }}} int meta_data_add_signed_int */
-
-int meta_data_add_unsigned_int(meta_data_t *md, /* {{{ */
- const char *key, uint64_t value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL))
- return -EINVAL;
-
- e = md_entry_alloc(key);
- if (e == NULL)
- return -ENOMEM;
-
- e->value.mv_unsigned_int = value;
- e->type = MD_TYPE_UNSIGNED_INT;
-
- return md_entry_insert(md, e);
-} /* }}} int meta_data_add_unsigned_int */
-
-int meta_data_add_double(meta_data_t *md, /* {{{ */
- const char *key, double value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL))
- return -EINVAL;
-
- e = md_entry_alloc(key);
- if (e == NULL)
- return -ENOMEM;
-
- e->value.mv_double = value;
- e->type = MD_TYPE_DOUBLE;
-
- return md_entry_insert(md, e);
-} /* }}} int meta_data_add_double */
-
-int meta_data_add_boolean(meta_data_t *md, /* {{{ */
- const char *key, bool value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL))
- return -EINVAL;
-
- e = md_entry_alloc(key);
- if (e == NULL)
- return -ENOMEM;
-
- e->value.mv_boolean = value;
- e->type = MD_TYPE_BOOLEAN;
-
- return md_entry_insert(md, e);
-} /* }}} int meta_data_add_boolean */
-
-/*
- * Get functions
- */
-int meta_data_get_string(meta_data_t *md, /* {{{ */
- const char *key, char **value) {
- meta_entry_t *e;
- char *temp;
-
- if ((md == NULL) || (key == NULL) || (value == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- e = md_entry_lookup(md, key);
- if (e == NULL) {
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- if (e->type != MD_TYPE_STRING) {
- ERROR("meta_data_get_string: Type mismatch for key `%s'", e->key);
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- temp = md_strdup(e->value.mv_string);
- if (temp == NULL) {
- pthread_mutex_unlock(&md->lock);
- ERROR("meta_data_get_string: md_strdup failed.");
- return -ENOMEM;
- }
-
- pthread_mutex_unlock(&md->lock);
-
- *value = temp;
-
- return 0;
-} /* }}} int meta_data_get_string */
-
-int meta_data_get_signed_int(meta_data_t *md, /* {{{ */
- const char *key, int64_t *value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL) || (value == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- e = md_entry_lookup(md, key);
- if (e == NULL) {
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- if (e->type != MD_TYPE_SIGNED_INT) {
- ERROR("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- *value = e->value.mv_signed_int;
-
- pthread_mutex_unlock(&md->lock);
- return 0;
-} /* }}} int meta_data_get_signed_int */
-
-int meta_data_get_unsigned_int(meta_data_t *md, /* {{{ */
- const char *key, uint64_t *value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL) || (value == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- e = md_entry_lookup(md, key);
- if (e == NULL) {
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- if (e->type != MD_TYPE_UNSIGNED_INT) {
- ERROR("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key);
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- *value = e->value.mv_unsigned_int;
-
- pthread_mutex_unlock(&md->lock);
- return 0;
-} /* }}} int meta_data_get_unsigned_int */
-
-int meta_data_get_double(meta_data_t *md, /* {{{ */
- const char *key, double *value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL) || (value == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- e = md_entry_lookup(md, key);
- if (e == NULL) {
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- if (e->type != MD_TYPE_DOUBLE) {
- ERROR("meta_data_get_double: Type mismatch for key `%s'", e->key);
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- *value = e->value.mv_double;
-
- pthread_mutex_unlock(&md->lock);
- return 0;
-} /* }}} int meta_data_get_double */
-
-int meta_data_get_boolean(meta_data_t *md, /* {{{ */
- const char *key, bool *value) {
- meta_entry_t *e;
-
- if ((md == NULL) || (key == NULL) || (value == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- e = md_entry_lookup(md, key);
- if (e == NULL) {
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- if (e->type != MD_TYPE_BOOLEAN) {
- ERROR("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- *value = e->value.mv_boolean;
-
- pthread_mutex_unlock(&md->lock);
- return 0;
-} /* }}} int meta_data_get_boolean */
-
-int meta_data_as_string(meta_data_t *md, /* {{{ */
- const char *key, char **value) {
- meta_entry_t *e;
- const char *actual;
- char buffer[MD_MAX_NONSTRING_CHARS]; /* For non-string types. */
- char *temp;
- int type;
-
- if ((md == NULL) || (key == NULL) || (value == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&md->lock);
-
- e = md_entry_lookup(md, key);
- if (e == NULL) {
- pthread_mutex_unlock(&md->lock);
- return -ENOENT;
- }
-
- type = e->type;
-
- switch (type) {
- case MD_TYPE_STRING:
- actual = e->value.mv_string;
- break;
- case MD_TYPE_SIGNED_INT:
- snprintf(buffer, sizeof(buffer), "%" PRIi64, e->value.mv_signed_int);
- actual = buffer;
- break;
- case MD_TYPE_UNSIGNED_INT:
- snprintf(buffer, sizeof(buffer), "%" PRIu64, e->value.mv_unsigned_int);
- actual = buffer;
- break;
- case MD_TYPE_DOUBLE:
- snprintf(buffer, sizeof(buffer), GAUGE_FORMAT, e->value.mv_double);
- actual = buffer;
- break;
- case MD_TYPE_BOOLEAN:
- actual = e->value.mv_boolean ? "true" : "false";
- break;
- default:
- pthread_mutex_unlock(&md->lock);
- ERROR("meta_data_as_string: unknown type %d for key `%s'", type, key);
- return -ENOENT;
- }
-
- pthread_mutex_unlock(&md->lock);
-
- temp = md_strdup(actual);
- if (temp == NULL) {
- ERROR("meta_data_as_string: md_strdup failed for key `%s'.", key);
- return -ENOMEM;
- }
-
- *value = temp;
-
- return 0;
-} /* }}} int meta_data_as_string */
+++ /dev/null
-/**
- * collectd - src/meta_data.h
- * Copyright (C) 2008-2011 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef META_DATA_H
-#define 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;
-
-meta_data_t *meta_data_create(void);
-meta_data_t *meta_data_clone(meta_data_t *orig);
-int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig);
-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, const char *key, const char *value);
-int meta_data_add_signed_int(meta_data_t *md, const char *key, int64_t value);
-int meta_data_add_unsigned_int(meta_data_t *md, const char *key,
- uint64_t value);
-int meta_data_add_double(meta_data_t *md, const char *key, double value);
-int meta_data_add_boolean(meta_data_t *md, const char *key, bool value);
-
-int meta_data_get_string(meta_data_t *md, const char *key, char **value);
-int meta_data_get_signed_int(meta_data_t *md, const char *key, int64_t *value);
-int meta_data_get_unsigned_int(meta_data_t *md, const char *key,
- uint64_t *value);
-int meta_data_get_double(meta_data_t *md, const char *key, double *value);
-int meta_data_get_boolean(meta_data_t *md, const char *key, bool *value);
-
-/* Returns the value as a string, regardless of the type. */
-int meta_data_as_string(meta_data_t *md, const char *key, char **value);
-
-#endif /* META_DATA_H */
+++ /dev/null
-/**
- * collectd - src/daemon/meta_data_test.c
- * Copyright (C) 2015 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#include "collectd.h"
-
-#include "common.h" /* for STATIC_ARRAY_SIZE */
-
-#include "meta_data.h"
-#include "testing.h"
-
-DEF_TEST(base) {
- meta_data_t *m;
-
- char *s;
- int64_t si;
- uint64_t ui;
- double d;
- bool b;
-
- CHECK_NOT_NULL(m = meta_data_create());
-
- /* all of these are absent */
- OK(meta_data_get_string(m, "string", &s) != 0);
- OK(meta_data_get_signed_int(m, "signed_int", &si) != 0);
- OK(meta_data_get_unsigned_int(m, "unsigned_int", &ui) != 0);
- OK(meta_data_get_double(m, "double", &d) != 0);
- OK(meta_data_get_boolean(m, "boolean", &b) != 0);
-
- /* populate structure */
- CHECK_ZERO(meta_data_add_string(m, "string", "foobar"));
- OK(meta_data_exists(m, "string"));
- OK(meta_data_type(m, "string") == MD_TYPE_STRING);
-
- CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", -1));
- OK(meta_data_exists(m, "signed_int"));
- OK(meta_data_type(m, "signed_int") == MD_TYPE_SIGNED_INT);
-
- CHECK_ZERO(meta_data_add_unsigned_int(m, "unsigned_int", 1));
- OK(meta_data_exists(m, "unsigned_int"));
- OK(meta_data_type(m, "unsigned_int") == MD_TYPE_UNSIGNED_INT);
-
- CHECK_ZERO(meta_data_add_double(m, "double", 47.11));
- OK(meta_data_exists(m, "double"));
- OK(meta_data_type(m, "double") == MD_TYPE_DOUBLE);
-
- CHECK_ZERO(meta_data_add_boolean(m, "boolean", 1));
- OK(meta_data_exists(m, "boolean"));
- OK(meta_data_type(m, "boolean") == MD_TYPE_BOOLEAN);
-
- /* retrieve and check all values */
- CHECK_ZERO(meta_data_get_string(m, "string", &s));
- EXPECT_EQ_STR("foobar", s);
- sfree(s);
-
- CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si));
- EXPECT_EQ_INT(-1, (int)si);
-
- CHECK_ZERO(meta_data_get_unsigned_int(m, "unsigned_int", &ui));
- EXPECT_EQ_INT(1, (int)ui);
-
- CHECK_ZERO(meta_data_get_double(m, "double", &d));
- EXPECT_EQ_DOUBLE(47.11, d);
-
- CHECK_ZERO(meta_data_get_boolean(m, "boolean", &b));
- OK1(b, "b evaluates to true");
-
- /* retrieving the wrong type always fails */
- EXPECT_EQ_INT(-2, meta_data_get_boolean(m, "string", &b));
- EXPECT_EQ_INT(-2, meta_data_get_string(m, "signed_int", &s));
- EXPECT_EQ_INT(-2, meta_data_get_string(m, "unsigned_int", &s));
- EXPECT_EQ_INT(-2, meta_data_get_string(m, "double", &s));
- EXPECT_EQ_INT(-2, meta_data_get_string(m, "boolean", &s));
-
- /* replace existing keys */
- CHECK_ZERO(meta_data_add_signed_int(m, "string", 666));
- OK(meta_data_type(m, "string") == MD_TYPE_SIGNED_INT);
-
- CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", 666));
- CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si));
- EXPECT_EQ_INT(666, (int)si);
-
- /* deleting keys */
- CHECK_ZERO(meta_data_delete(m, "signed_int"));
- EXPECT_EQ_INT(-2, meta_data_delete(m, "doesnt exist"));
-
- meta_data_destroy(m);
- return 0;
-}
-
-int main(void) {
- RUN_TEST(base);
-
- END_TEST;
-}
#include "collectd.h"
-#include "common.h"
#include "configfile.h"
#include "filter_chain.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils/heap/heap.h"
#include "utils_cache.h"
#include "utils_complain.h"
-#include "utils_heap.h"
#include "utils_llist.h"
#include "utils_random.h"
#include "utils_time.h"
#include "collectd.h"
#include "configfile.h"
-#include "meta_data.h"
+#include "utils/metadata/meta_data.h"
#include "utils_time.h"
#include <inttypes.h>
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "configfile.h"
#include "plugin.h"
+++ /dev/null
-/**
- * collectd - src/utils_avltree.c
- * Copyright (C) 2006,2007 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "utils_avltree.h"
-
-#define BALANCE(n) \
- ((((n)->left == NULL) ? 0 : (n)->left->height) - \
- (((n)->right == NULL) ? 0 : (n)->right->height))
-
-/*
- * private data types
- */
-struct c_avl_node_s {
- void *key;
- void *value;
-
- int height;
- struct c_avl_node_s *left;
- struct c_avl_node_s *right;
- struct c_avl_node_s *parent;
-};
-typedef struct c_avl_node_s c_avl_node_t;
-
-struct c_avl_tree_s {
- c_avl_node_t *root;
- int (*compare)(const void *, const void *);
- int size;
-};
-
-struct c_avl_iterator_s {
- c_avl_tree_t *tree;
- c_avl_node_t *node;
-};
-
-/*
- * private functions
- */
-#if 0
-static void verify_tree (c_avl_node_t *n)
-{
- if (n == NULL)
- return;
-
- verify_tree (n->left);
- verify_tree (n->right);
-
- assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1));
- assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n));
-} /* void verify_tree */
-#else
-#define verify_tree(n) /**/
-#endif
-
-static void free_node(c_avl_node_t *n) {
- if (n == NULL)
- return;
-
- if (n->left != NULL)
- free_node(n->left);
- if (n->right != NULL)
- free_node(n->right);
-
- free(n);
-}
-
-static int calc_height(c_avl_node_t *n) {
- int height_left;
- int height_right;
-
- if (n == NULL)
- return 0;
-
- height_left = (n->left == NULL) ? 0 : n->left->height;
- height_right = (n->right == NULL) ? 0 : n->right->height;
-
- return ((height_left > height_right) ? height_left : height_right) + 1;
-} /* int calc_height */
-
-static c_avl_node_t *search(c_avl_tree_t *t, const void *key) {
- c_avl_node_t *n;
- int cmp;
-
- n = t->root;
- while (n != NULL) {
- cmp = t->compare(key, n->key);
- if (cmp == 0)
- return n;
- else if (cmp < 0)
- n = n->left;
- else
- n = n->right;
- }
-
- return NULL;
-}
-
-/* (x) (y)
- * / \ / \
- * (y) /\ /\ (x)
- * / \ /_c\ ==> / a\ / \
- * /\ /\ /____\/\ /\
- * / a\ /_b\ /_b\ /_c\
- * /____\
- */
-static c_avl_node_t *rotate_right(c_avl_tree_t *t, c_avl_node_t *x) {
- c_avl_node_t *p;
- c_avl_node_t *y;
- c_avl_node_t *b;
-
- assert(x != NULL);
- assert(x->left != NULL);
-
- p = x->parent;
- y = x->left;
- b = y->right;
-
- x->left = b;
- if (b != NULL)
- b->parent = x;
-
- x->parent = y;
- y->right = x;
-
- y->parent = p;
- assert((p == NULL) || (p->left == x) || (p->right == x));
- if (p == NULL)
- t->root = y;
- else if (p->left == x)
- p->left = y;
- else
- p->right = y;
-
- x->height = calc_height(x);
- y->height = calc_height(y);
-
- return y;
-} /* void rotate_right */
-
-/*
- * (x) (y)
- * / \ / \
- * /\ (y) (x) /\
- * /_a\ / \ ==> / \ / c\
- * /\ /\ /\ /\/____\
- * /_b\ / c\ /_a\ /_b\
- * /____\
- */
-static c_avl_node_t *rotate_left(c_avl_tree_t *t, c_avl_node_t *x) {
- c_avl_node_t *p;
- c_avl_node_t *y;
- c_avl_node_t *b;
-
- assert(x != NULL);
- assert(x->right != NULL);
-
- p = x->parent;
- y = x->right;
- b = y->left;
-
- x->right = b;
- if (b != NULL)
- b->parent = x;
-
- x->parent = y;
- y->left = x;
-
- y->parent = p;
- assert((p == NULL) || (p->left == x) || (p->right == x));
- if (p == NULL)
- t->root = y;
- else if (p->left == x)
- p->left = y;
- else
- p->right = y;
-
- x->height = calc_height(x);
- y->height = calc_height(y);
-
- return y;
-} /* void rotate_left */
-
-static c_avl_node_t *rotate_left_right(c_avl_tree_t *t, c_avl_node_t *x) {
- rotate_left(t, x->left);
- return rotate_right(t, x);
-} /* void rotate_left_right */
-
-static c_avl_node_t *rotate_right_left(c_avl_tree_t *t, c_avl_node_t *x) {
- rotate_right(t, x->right);
- return rotate_left(t, x);
-} /* void rotate_right_left */
-
-static void rebalance(c_avl_tree_t *t, c_avl_node_t *n) {
- int b_top;
- int b_bottom;
-
- while (n != NULL) {
- b_top = BALANCE(n);
- assert((b_top >= -2) && (b_top <= 2));
-
- if (b_top == -2) {
- assert(n->right != NULL);
- b_bottom = BALANCE(n->right);
- assert((b_bottom >= -1) && (b_bottom <= 1));
- if (b_bottom == 1)
- n = rotate_right_left(t, n);
- else
- n = rotate_left(t, n);
- } else if (b_top == 2) {
- assert(n->left != NULL);
- b_bottom = BALANCE(n->left);
- assert((b_bottom >= -1) && (b_bottom <= 1));
- if (b_bottom == -1)
- n = rotate_left_right(t, n);
- else
- n = rotate_right(t, n);
- } else {
- int height = calc_height(n);
- if (height == n->height)
- break;
- n->height = height;
- }
-
- assert(n->height == calc_height(n));
-
- n = n->parent;
- } /* while (n != NULL) */
-} /* void rebalance */
-
-static c_avl_node_t *c_avl_node_next(c_avl_node_t *n) {
- c_avl_node_t *r; /* return node */
-
- if (n == NULL) {
- return NULL;
- }
-
- /* If we can't descent any further, we have to backtrack to the first
- * parent that's bigger than we, i. e. who's _left_ child we are. */
- if (n->right == NULL) {
- r = n->parent;
- while ((r != NULL) && (r->parent != NULL)) {
- if (r->left == n)
- break;
- n = r;
- r = n->parent;
- }
-
- /* n->right == NULL && r == NULL => t is root and has no next
- * r->left != n => r->right = n => r->parent == NULL */
- if ((r == NULL) || (r->left != n)) {
- assert((r == NULL) || (r->parent == NULL));
- return NULL;
- } else {
- assert(r->left == n);
- return r;
- }
- } else {
- r = n->right;
- while (r->left != NULL)
- r = r->left;
- }
-
- return r;
-} /* c_avl_node_t *c_avl_node_next */
-
-static c_avl_node_t *c_avl_node_prev(c_avl_node_t *n) {
- c_avl_node_t *r; /* return node */
-
- if (n == NULL) {
- return NULL;
- }
-
- /* If we can't descent any further, we have to backtrack to the first
- * parent that's smaller than we, i. e. who's _right_ child we are. */
- if (n->left == NULL) {
- r = n->parent;
- while ((r != NULL) && (r->parent != NULL)) {
- if (r->right == n)
- break;
- n = r;
- r = n->parent;
- }
-
- /* n->left == NULL && r == NULL => t is root and has no next
- * r->right != n => r->left = n => r->parent == NULL */
- if ((r == NULL) || (r->right != n)) {
- assert((r == NULL) || (r->parent == NULL));
- return NULL;
- } else {
- assert(r->right == n);
- return r;
- }
- } else {
- r = n->left;
- while (r->right != NULL)
- r = r->right;
- }
-
- return r;
-} /* c_avl_node_t *c_avl_node_prev */
-
-static int _remove(c_avl_tree_t *t, c_avl_node_t *n) {
- assert((t != NULL) && (n != NULL));
-
- if ((n->left != NULL) && (n->right != NULL)) {
- c_avl_node_t *r; /* replacement node */
- if (BALANCE(n) > 0) /* left subtree is higher */
- {
- assert(n->left != NULL);
- r = c_avl_node_prev(n);
-
- } else /* right subtree is higher */
- {
- assert(n->right != NULL);
- r = c_avl_node_next(n);
- }
-
- assert((r->left == NULL) || (r->right == NULL));
-
- /* copy content */
- n->key = r->key;
- n->value = r->value;
-
- n = r;
- }
-
- assert((n->left == NULL) || (n->right == NULL));
-
- if ((n->left == NULL) && (n->right == NULL)) {
- /* Deleting a leave is easy */
- if (n->parent == NULL) {
- assert(t->root == n);
- t->root = NULL;
- } else {
- assert((n->parent->left == n) || (n->parent->right == n));
- if (n->parent->left == n)
- n->parent->left = NULL;
- else
- n->parent->right = NULL;
-
- rebalance(t, n->parent);
- }
-
- free_node(n);
- } else if (n->left == NULL) {
- assert(BALANCE(n) == -1);
- assert((n->parent == NULL) || (n->parent->left == n) ||
- (n->parent->right == n));
- if (n->parent == NULL) {
- assert(t->root == n);
- t->root = n->right;
- } else if (n->parent->left == n) {
- n->parent->left = n->right;
- } else {
- n->parent->right = n->right;
- }
- n->right->parent = n->parent;
-
- if (n->parent != NULL)
- rebalance(t, n->parent);
-
- n->right = NULL;
- free_node(n);
- } else if (n->right == NULL) {
- assert(BALANCE(n) == 1);
- assert((n->parent == NULL) || (n->parent->left == n) ||
- (n->parent->right == n));
- if (n->parent == NULL) {
- assert(t->root == n);
- t->root = n->left;
- } else if (n->parent->left == n) {
- n->parent->left = n->left;
- } else {
- n->parent->right = n->left;
- }
- n->left->parent = n->parent;
-
- if (n->parent != NULL)
- rebalance(t, n->parent);
-
- n->left = NULL;
- free_node(n);
- } else {
- assert(0);
- }
-
- return 0;
-} /* void *_remove */
-
-/*
- * public functions
- */
-c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *)) {
- c_avl_tree_t *t;
-
- if (compare == NULL)
- return NULL;
-
- if ((t = malloc(sizeof(*t))) == NULL)
- return NULL;
-
- t->root = NULL;
- t->compare = compare;
- t->size = 0;
-
- return t;
-}
-
-void c_avl_destroy(c_avl_tree_t *t) {
- if (t == NULL)
- return;
- free_node(t->root);
- free(t);
-}
-
-int c_avl_insert(c_avl_tree_t *t, void *key, void *value) {
- c_avl_node_t *new;
- c_avl_node_t *nptr;
- int cmp;
-
- if ((new = malloc(sizeof(*new))) == NULL)
- return -1;
-
- new->key = key;
- new->value = value;
- new->height = 1;
- new->left = NULL;
- new->right = NULL;
-
- if (t->root == NULL) {
- new->parent = NULL;
- t->root = new;
- t->size = 1;
- return 0;
- }
-
- nptr = t->root;
- while (42) {
- cmp = t->compare(nptr->key, new->key);
- if (cmp == 0) {
- free_node(new);
- return 1;
- } else if (cmp < 0) {
- /* nptr < new */
- if (nptr->right == NULL) {
- nptr->right = new;
- new->parent = nptr;
- rebalance(t, nptr);
- break;
- } else {
- nptr = nptr->right;
- }
- } else /* if (cmp > 0) */
- {
- /* nptr > new */
- if (nptr->left == NULL) {
- nptr->left = new;
- new->parent = nptr;
- rebalance(t, nptr);
- break;
- } else {
- nptr = nptr->left;
- }
- }
- } /* while (42) */
-
- verify_tree(t->root);
- ++t->size;
- return 0;
-} /* int c_avl_insert */
-
-int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue) {
- c_avl_node_t *n;
- int status;
-
- assert(t != NULL);
-
- n = search(t, key);
- if (n == NULL)
- return -1;
-
- if (rkey != NULL)
- *rkey = n->key;
- if (rvalue != NULL)
- *rvalue = n->value;
-
- status = _remove(t, n);
- verify_tree(t->root);
- --t->size;
- return status;
-} /* void *c_avl_remove */
-
-int c_avl_get(c_avl_tree_t *t, const void *key, void **value) {
- c_avl_node_t *n;
-
- assert(t != NULL);
-
- n = search(t, key);
- if (n == NULL)
- return -1;
-
- if (value != NULL)
- *value = n->value;
-
- return 0;
-}
-
-int c_avl_pick(c_avl_tree_t *t, void **key, void **value) {
- c_avl_node_t *n;
- c_avl_node_t *p;
-
- assert(t != NULL);
-
- if ((key == NULL) || (value == NULL))
- return -1;
- if (t->root == NULL)
- return -1;
-
- n = t->root;
- while ((n->left != NULL) || (n->right != NULL)) {
- if (n->left == NULL) {
- n = n->right;
- continue;
- } else if (n->right == NULL) {
- n = n->left;
- continue;
- }
-
- if (n->left->height > n->right->height)
- n = n->left;
- else
- n = n->right;
- }
-
- p = n->parent;
- if (p == NULL)
- t->root = NULL;
- else if (p->left == n)
- p->left = NULL;
- else
- p->right = NULL;
-
- *key = n->key;
- *value = n->value;
-
- free_node(n);
- --t->size;
- rebalance(t, p);
-
- return 0;
-} /* int c_avl_pick */
-
-c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t) {
- c_avl_iterator_t *iter;
-
- if (t == NULL)
- return NULL;
-
- iter = calloc(1, sizeof(*iter));
- if (iter == NULL)
- return NULL;
- iter->tree = t;
-
- return iter;
-} /* c_avl_iterator_t *c_avl_get_iterator */
-
-int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value) {
- c_avl_node_t *n;
-
- if ((iter == NULL) || (key == NULL) || (value == NULL))
- return -1;
-
- if (iter->node == NULL) {
- for (n = iter->tree->root; n != NULL; n = n->left)
- if (n->left == NULL)
- break;
- iter->node = n;
- } else {
- n = c_avl_node_next(iter->node);
- }
-
- if (n == NULL)
- return -1;
-
- iter->node = n;
- *key = n->key;
- *value = n->value;
-
- return 0;
-} /* int c_avl_iterator_next */
-
-int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value) {
- c_avl_node_t *n;
-
- if ((iter == NULL) || (key == NULL) || (value == NULL))
- return -1;
-
- if (iter->node == NULL) {
- for (n = iter->tree->root; n != NULL; n = n->right)
- if (n->right == NULL)
- break;
- iter->node = n;
- } else {
- n = c_avl_node_prev(iter->node);
- }
-
- if (n == NULL)
- return -1;
-
- iter->node = n;
- *key = n->key;
- *value = n->value;
-
- return 0;
-} /* int c_avl_iterator_prev */
-
-void c_avl_iterator_destroy(c_avl_iterator_t *iter) { free(iter); }
-
-int c_avl_size(c_avl_tree_t *t) {
- if (t == NULL)
- return 0;
- return t->size;
-}
+++ /dev/null
-/**
- * collectd - src/utils_avltree.h
- * Copyright (C) 2006,2007 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_AVLTREE_H
-#define UTILS_AVLTREE_H 1
-
-struct c_avl_tree_s;
-typedef struct c_avl_tree_s c_avl_tree_t;
-
-struct c_avl_iterator_s;
-typedef struct c_avl_iterator_s c_avl_iterator_t;
-
-/*
- * NAME
- * c_avl_create
- *
- * DESCRIPTION
- * Allocates a new AVL-tree.
- *
- * PARAMETERS
- * `compare' The function-pointer `compare' is used to compare two keys. It
- * has to return less than zero if its first argument is smaller
- * then the second argument, more than zero if the first argument
- * is bigger than the second argument and zero if they are equal.
- * If your keys are char-pointers, you can use the `strcmp'
- * function from the libc here.
- *
- * RETURN VALUE
- * A c_avl_tree_t-pointer upon success or NULL upon failure.
- */
-c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *));
-
-/*
- * NAME
- * c_avl_destroy
- *
- * DESCRIPTION
- * Deallocates an AVL-tree. Stored value- and key-pointer are lost, but of
- * course not freed.
- */
-void c_avl_destroy(c_avl_tree_t *t);
-
-/*
- * NAME
- * c_avl_insert
- *
- * DESCRIPTION
- * Stores the key-value-pair in the AVL-tree pointed to by `t'.
- *
- * PARAMETERS
- * `t' AVL-tree to store the data in.
- * `key' Key used to store the value under. This is used to get back to
- * the value again. The pointer is stored in an internal structure
- * and _not_ copied. So the memory pointed to may _not_ be freed
- * before this entry is removed. You can use the `rkey' argument
- * to `avl_remove' to get the original pointer back and free it.
- * `value' Value to be stored.
- *
- * RETURN VALUE
- * Zero upon success, non-zero otherwise. It's less than zero if an error
- * occurred or greater than zero if the key is already stored in the tree.
- */
-int c_avl_insert(c_avl_tree_t *t, void *key, void *value);
-
-/*
- * NAME
- * c_avl_remove
- *
- * DESCRIPTION
- * Removes a key-value-pair from the tree t. The stored key and value may be
- * returned in `rkey' and `rvalue'.
- *
- * PARAMETERS
- * `t' AVL-tree to remove key-value-pair from.
- * `key' Key to identify the entry.
- * `rkey' Pointer to a pointer in which to store the key. May be NULL.
- * Since the `key' pointer is not copied when creating an entry,
- * the pointer may not be available anymore from outside the tree.
- * You can use this argument to get the actual pointer back and
- * free the memory pointed to by it.
- * `rvalue' Pointer to a pointer in which to store the value. May be NULL.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if the key isn't found in the tree.
- */
-int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue);
-
-/*
- * NAME
- * c_avl_get
- *
- * DESCRIPTION
- * Retrieve the `value' belonging to `key'.
- *
- * PARAMETERS
- * `t' AVL-tree to get the value from.
- * `key' Key to identify the entry.
- * `value' Pointer to a pointer in which to store the value. May be NULL.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if the key isn't found in the tree.
- */
-int c_avl_get(c_avl_tree_t *t, const void *key, void **value);
-
-/*
- * NAME
- * c_avl_pick
- *
- * DESCRIPTION
- * Remove a (pseudo-)random element from the tree and return its `key' and
- * `value'. Entries are not returned in any particular order. This function
- * is intended for cache-flushes that don't care about the order but simply
- * want to remove all elements, one at a time.
- *
- * PARAMETERS
- * `t' AVL-tree to get the value from.
- * `key' Pointer to a pointer in which to store the key.
- * `value' Pointer to a pointer in which to store the value.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if the tree is empty or key or value is
- * NULL.
- */
-int c_avl_pick(c_avl_tree_t *t, void **key, void **value);
-
-c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t);
-int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value);
-int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value);
-void c_avl_iterator_destroy(c_avl_iterator_t *iter);
-
-/*
- * NAME
- * c_avl_size
- *
- * DESCRIPTION
- * Return the size (number of nodes) of the specified tree.
- *
- * PARAMETERS
- * `t' AVL-tree to get the size of.
- *
- * RETURN VALUE
- * Number of nodes in the tree, 0 if the tree is empty or NULL.
- */
-int c_avl_size(c_avl_tree_t *t);
-
-#endif /* UTILS_AVLTREE_H */
+++ /dev/null
-/**
- * collectd - src/tests/test_utils_avltree.c
- * Copyright (C) 2013 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#include "collectd.h"
-#include "common.h" /* STATIC_ARRAY_SIZE */
-
-#include "testing.h"
-#include "utils_avltree.h"
-
-static int compare_total_count;
-
-#define RESET_COUNTS() \
- do { \
- compare_total_count = 0; \
- } while (0)
-
-static int compare_callback(void const *v0, void const *v1) {
- assert(v0 != NULL);
- assert(v1 != NULL);
-
- compare_total_count++;
- return strcmp(v0, v1);
-}
-
-struct kv_t {
- char *key;
- char *value;
-};
-
-static int kv_compare(const void *a_ptr, const void *b_ptr) {
- return strcmp(((struct kv_t *)a_ptr)->key, ((struct kv_t *)b_ptr)->key);
-}
-
-DEF_TEST(success) {
- struct kv_t cases[] = {
- {"Eeph7chu", "vai1reiV"}, {"igh3Paiz", "teegh1Ee"},
- {"caip6Uu8", "ooteQu8n"}, {"Aech6vah", "AijeeT0l"},
- {"Xah0et2L", "gah8Taep"}, {"BocaeB8n", "oGaig8io"},
- {"thai8AhM", "ohjeFo3f"}, {"ohth6ieC", "hoo8ieWo"},
- {"aej7Woow", "phahuC2s"}, {"Hai8ier2", "Yie6eimi"},
- {"phuXi3Li", "JaiF7ieb"}, {"Shaig5ef", "aihi5Zai"},
- {"voh6Aith", "Oozaeto0"}, {"zaiP5kie", "seep5veM"},
- {"pae7ba7D", "chie8Ojo"}, {"Gou2ril3", "ouVoo0ha"},
- {"lo3Thee3", "ahDu4Zuj"}, {"Rah8kohv", "ieShoc7E"},
- {"ieN5engi", "Aevou1ah"}, {"ooTe4OhP", "aingai5Y"},
- };
-
- struct kv_t sorted_cases[STATIC_ARRAY_SIZE(cases)];
- memcpy(sorted_cases, cases, sizeof(cases));
- qsort(sorted_cases, STATIC_ARRAY_SIZE(cases), sizeof(struct kv_t),
- kv_compare);
-
- c_avl_tree_t *t;
-
- RESET_COUNTS();
- CHECK_NOT_NULL(t = c_avl_create(compare_callback));
-
- /* insert */
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- char *key;
- char *value;
-
- CHECK_NOT_NULL(key = strdup(cases[i].key));
- CHECK_NOT_NULL(value = strdup(cases[i].value));
-
- CHECK_ZERO(c_avl_insert(t, key, value));
- EXPECT_EQ_INT((int)(i + 1), c_avl_size(t));
- }
-
- /* Key already exists. */
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++)
- EXPECT_EQ_INT(1, c_avl_insert(t, cases[i].key, cases[i].value));
-
- /* get */
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- char *value_ret = NULL;
-
- CHECK_ZERO(c_avl_get(t, cases[i].key, (void *)&value_ret));
- EXPECT_EQ_STR(cases[i].value, value_ret);
- }
-
- /* iterate forward */
- {
- c_avl_iterator_t *iter = c_avl_get_iterator(t);
- char *key;
- char *value;
- size_t i = 0;
- while (c_avl_iterator_next(iter, (void **)&key, (void **)&value) == 0) {
- EXPECT_EQ_STR(sorted_cases[i].key, key);
- EXPECT_EQ_STR(sorted_cases[i].value, value);
- i++;
- }
- c_avl_iterator_destroy(iter);
- EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases));
- }
-
- /* iterate backward */
- {
- c_avl_iterator_t *iter = c_avl_get_iterator(t);
- char *key;
- char *value;
- size_t i = 0;
- while (c_avl_iterator_prev(iter, (void **)&key, (void **)&value) == 0) {
- EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].key, key);
- EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].value,
- value);
- i++;
- }
- c_avl_iterator_destroy(iter);
- EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases));
- }
-
- /* remove half */
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases) / 2; i++) {
- char *key = NULL;
- char *value = NULL;
-
- int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1));
-
- CHECK_ZERO(c_avl_remove(t, cases[i].key, (void *)&key, (void *)&value));
-
- EXPECT_EQ_STR(cases[i].key, key);
- EXPECT_EQ_STR(cases[i].value, value);
-
- free(key);
- free(value);
-
- EXPECT_EQ_INT(expected_size, c_avl_size(t));
- }
-
- /* pick the other half */
- for (size_t i = STATIC_ARRAY_SIZE(cases) / 2; i < STATIC_ARRAY_SIZE(cases);
- i++) {
- char *key = NULL;
- char *value = NULL;
-
- int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1));
-
- EXPECT_EQ_INT(expected_size + 1, c_avl_size(t));
- EXPECT_EQ_INT(0, c_avl_pick(t, (void *)&key, (void *)&value));
-
- free(key);
- free(value);
-
- EXPECT_EQ_INT(expected_size, c_avl_size(t));
- }
-
- c_avl_destroy(t);
-
- return 0;
-}
-
-int main(void) {
- RUN_TEST(success);
-
- END_TEST;
-}
#include "collectd.h"
-#include "common.h"
-#include "meta_data.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils/metadata/meta_data.h"
#include "utils_cache.h"
#include <assert.h>
+++ /dev/null
-/**
- * collectd - src/utils_heap.c
- * Copyright (C) 2009 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <pthread.h>
-#include <stdlib.h>
-
-#include "utils_heap.h"
-
-struct c_heap_s {
- pthread_mutex_t lock;
- int (*compare)(const void *, const void *);
-
- void **list;
- size_t list_len; /* # entries used */
- size_t list_size; /* # entries allocated */
-};
-
-enum reheap_direction { DIR_UP, DIR_DOWN };
-
-static void reheap(c_heap_t *h, size_t root, enum reheap_direction dir) {
- size_t left;
- size_t right;
- size_t min;
- int status;
-
- /* Calculate the positions of the children */
- left = (2 * root) + 1;
- if (left >= h->list_len)
- left = 0;
-
- right = (2 * root) + 2;
- if (right >= h->list_len)
- right = 0;
-
- /* Check which one of the children is smaller. */
- if ((left == 0) && (right == 0))
- return;
- else if (left == 0)
- min = right;
- else if (right == 0)
- min = left;
- else {
- status = h->compare(h->list[left], h->list[right]);
- if (status > 0)
- min = right;
- else
- min = left;
- }
-
- status = h->compare(h->list[root], h->list[min]);
- if (status <= 0) {
- /* We didn't need to change anything, so the rest of the tree should be
- * okay now. */
- return;
- } else /* if (status > 0) */
- {
- void *tmp;
-
- tmp = h->list[root];
- h->list[root] = h->list[min];
- h->list[min] = tmp;
- }
-
- if ((dir == DIR_UP) && (root == 0))
- return;
-
- if (dir == DIR_UP)
- reheap(h, (root - 1) / 2, dir);
- else if (dir == DIR_DOWN)
- reheap(h, min, dir);
-} /* void reheap */
-
-c_heap_t *c_heap_create(int (*compare)(const void *, const void *)) {
- c_heap_t *h;
-
- if (compare == NULL)
- return NULL;
-
- h = calloc(1, sizeof(*h));
- if (h == NULL)
- return NULL;
-
- pthread_mutex_init(&h->lock, /* attr = */ NULL);
- h->compare = compare;
-
- h->list = NULL;
- h->list_len = 0;
- h->list_size = 0;
-
- return h;
-} /* c_heap_t *c_heap_create */
-
-void c_heap_destroy(c_heap_t *h) {
- if (h == NULL)
- return;
-
- h->list_len = 0;
- h->list_size = 0;
- free(h->list);
- h->list = NULL;
-
- pthread_mutex_destroy(&h->lock);
-
- free(h);
-} /* void c_heap_destroy */
-
-int c_heap_insert(c_heap_t *h, void *ptr) {
- size_t index;
-
- if ((h == NULL) || (ptr == NULL))
- return -EINVAL;
-
- pthread_mutex_lock(&h->lock);
-
- assert(h->list_len <= h->list_size);
- if (h->list_len == h->list_size) {
- void **tmp;
-
- tmp = realloc(h->list, (h->list_size + 16) * sizeof(*h->list));
- if (tmp == NULL) {
- pthread_mutex_unlock(&h->lock);
- return -ENOMEM;
- }
-
- h->list = tmp;
- h->list_size += 16;
- }
-
- /* Insert the new node as a leaf. */
- index = h->list_len;
- h->list[index] = ptr;
- h->list_len++;
-
- /* Reorganize the heap from bottom up. */
- reheap(h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
-
- pthread_mutex_unlock(&h->lock);
- return 0;
-} /* int c_heap_insert */
-
-void *c_heap_get_root(c_heap_t *h) {
- void *ret = NULL;
-
- if (h == NULL)
- return NULL;
-
- pthread_mutex_lock(&h->lock);
-
- if (h->list_len == 0) {
- pthread_mutex_unlock(&h->lock);
- return NULL;
- } else if (h->list_len == 1) {
- ret = h->list[0];
- h->list[0] = NULL;
- h->list_len = 0;
- } else /* if (h->list_len > 1) */
- {
- ret = h->list[0];
- h->list[0] = h->list[h->list_len - 1];
- h->list[h->list_len - 1] = NULL;
- h->list_len--;
-
- reheap(h, /* root = */ 0, DIR_DOWN);
- }
-
- /* free some memory */
- if ((h->list_len + 32) < h->list_size) {
- void **tmp;
-
- tmp = realloc(h->list, (h->list_len + 16) * sizeof(*h->list));
- if (tmp != NULL) {
- h->list = tmp;
- h->list_size = h->list_len + 16;
- }
- }
-
- pthread_mutex_unlock(&h->lock);
-
- return ret;
-} /* void *c_heap_get_root */
+++ /dev/null
-/**
- * collectd - src/utils_heap.h
- * Copyright (C) 2009 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_HEAP_H
-#define UTILS_HEAP_H 1
-
-struct c_heap_s;
-typedef struct c_heap_s c_heap_t;
-
-/*
- * NAME
- * c_heap_create
- *
- * DESCRIPTION
- * Allocates a new heap.
- *
- * PARAMETERS
- * `compare' The function-pointer `compare' is used to compare two keys. It
- * has to return less than zero if its first argument is smaller
- * then the second argument, more than zero if the first argument
- * is bigger than the second argument and zero if they are equal.
- * If your keys are char-pointers, you can use the `strcmp'
- * function from the libc here.
- *
- * RETURN VALUE
- * A c_heap_t-pointer upon success or NULL upon failure.
- */
-c_heap_t *c_heap_create(int (*compare)(const void *, const void *));
-
-/*
- * NAME
- * c_heap_destroy
- *
- * DESCRIPTION
- * Deallocates a heap. Stored value- and key-pointer are lost, but of course
- * not freed.
- */
-void c_heap_destroy(c_heap_t *h);
-
-/*
- * NAME
- * c_heap_insert
- *
- * DESCRIPTION
- * Stores the key-value-pair in the heap pointed to by `h'.
- *
- * PARAMETERS
- * `h' Heap to store the data in.
- * `ptr' Value to be stored. This is typically a pointer to a data
- * structure. The data structure is of course *not* copied and may
- * not be free'd before the pointer has been removed from the heap
- * again.
- *
- * RETURN VALUE
- * Zero upon success, non-zero otherwise. It's less than zero if an error
- * occurred or greater than zero if the key is already stored in the tree.
- */
-int c_heap_insert(c_heap_t *h, void *ptr);
-
-/*
- * NAME
- * c_heap_get_root
- *
- * DESCRIPTION
- * Removes the value at the root of the heap and returns both, key and value.
- *
- * PARAMETERS
- * `h' Heap to remove key-value-pair from.
- *
- * RETURN VALUE
- * The pointer passed to `c_heap_insert' or NULL if there are no more
- * elements in the heap (or an error occurred).
- */
-void *c_heap_get_root(c_heap_t *h);
-
-#endif /* UTILS_HEAP_H */
+++ /dev/null
-/**
- * collectd - src/tests/test_utils_heap.c
- * Copyright (C) 2013 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#include "collectd.h"
-
-#include "testing.h"
-#include "utils_heap.h"
-
-static int compare(void const *v0, void const *v1) {
- int const *i0 = v0;
- int const *i1 = v1;
-
- if ((*i0) < (*i1))
- return -1;
- else if ((*i0) > (*i1))
- return 1;
- else
- return 0;
-}
-
-DEF_TEST(simple) {
- int values[] = {9, 5, 6, 1, 3, 4, 0, 8, 2, 7};
- c_heap_t *h;
-
- CHECK_NOT_NULL(h = c_heap_create(compare));
- for (int i = 0; i < 10; i++)
- CHECK_ZERO(c_heap_insert(h, &values[i]));
-
- for (int i = 0; i < 5; i++) {
- int *ret = NULL;
- CHECK_NOT_NULL(ret = c_heap_get_root(h));
- OK(*ret == i);
- }
-
- CHECK_ZERO(c_heap_insert(h, &values[6] /* = 0 */));
- CHECK_ZERO(c_heap_insert(h, &values[3] /* = 1 */));
- CHECK_ZERO(c_heap_insert(h, &values[8] /* = 2 */));
- CHECK_ZERO(c_heap_insert(h, &values[4] /* = 3 */));
- CHECK_ZERO(c_heap_insert(h, &values[5] /* = 4 */));
-
- for (int i = 0; i < 10; i++) {
- int *ret = NULL;
- CHECK_NOT_NULL(ret = c_heap_get_root(h));
- OK(*ret == i);
- }
-
- c_heap_destroy(h);
- return 0;
-}
-
-int main(void) {
- RUN_TEST(simple);
-
- END_TEST;
-}
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "utils_subst.h"
char *subst(char *buf, size_t buflen, const char *string, size_t off1,
*/
#include "collectd.h"
-#include "common.h" /* for STATIC_ARRAY_SIZE */
+#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */
#include "testing.h"
#include "utils_subst.h"
#include "collectd.h"
-#include "common.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
#include "utils_threshold.h"
#include <pthread.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_time.h"
#ifndef DEFAULT_MOCK_TIME
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_db_query.h"
+#include "utils/common/common.h"
+#include "utils/db_query/db_query.h"
#include <dbi/dbi.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
-#include "utils_mount.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
+#include "utils/mount/mount.h"
#if HAVE_STATVFS
#if HAVE_SYS_STATVFS_H
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#if HAVE_MACH_MACH_TYPES_H
#include <mach/mach_types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
-#include "utils_dns.h"
+#include "utils/dns/dns.h"
#include <poll.h>
#include <pcap.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "semaphore.h"
#include "sys/mman.h"
-#include "utils_dpdk.h"
+#include "utils/dpdk/dpdk.h"
#include "utils_time.h"
#include <rte_config.h>
#include "collectd.h"
-#include "common.h"
-#include "utils_dpdk.h"
+#include "utils/common/common.h"
+#include "utils/dpdk/dpdk.h"
#include <rte_config.h>
#include <rte_ethdev.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
static const char *drbd_stats = "/proc/drbd";
static const char *drbd_names[] = {
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <stddef.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
#include "utils_complain.h"
#if HAVE_SYS_IOCTL_H
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
-#include "utils_cmd_putnotif.h"
-#include "utils_cmd_putval.h"
+#include "utils/cmds/putnotif.h"
+#include "utils/cmds/putval.h"
#include <grp.h>
#include <pwd.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
static const char *config_keys[] = {"ValuesAbsolute", "ValuesPercentage"};
static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <dirent.h>
#include <fcntl.h>
#include "collectd.h"
+#include "plugin.h"
+#include "utils/common/common.h"
#include <stdio.h> /* a header needed for FILE */
#include <stdlib.h> /* used for atoi */
#include <string.h> /* a header needed for scanf function */
-#include "common.h"
-#include "plugin.h"
#if !KERNEL_LINUX
#error "This module only supports the Linux implementation of fscache"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
#if HAVE_NETDB_H
#include <netdb.h>
**/
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_time.h"
#define CGPS_TRUE 1
*/
#include "daemon/collectd.h"
-#include "daemon/common.h"
#include "daemon/plugin.h"
+#include "utils/common/common.h"
#include <nvml.h>
#include <stdint.h>
#include <stdbool.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "daemon/utils_cache.h"
}
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <assert.h>
#include <libgen.h> /* for basename */
#include "collectd.h"
-#include "common.h" /* auxiliary functions */
-#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "utils/common/common.h" /* auxiliary functions */
static const char g_plugin_name[] = "hugepages";
**/
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
-#include "utils_config_cores.h"
+#include "utils/config_cores/config_cores.h"
#include <jevents.h>
#include <jsession.h>
**/
#include "collectd.h"
-#include "common.h"
-#include "utils_config_cores.h"
+#include "utils/common/common.h"
+#include "utils/config_cores/config_cores.h"
#include <pqos.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if KERNEL_LINUX
/* _GNU_SOURCE is needed for struct shm_info.used_ids on musl libc */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <OpenIPMI/ipmi_auth.h>
#include <OpenIPMI/ipmi_conn.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <libiptc/libip6tc.h>
#include <libiptc/libiptc.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <jni.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <unistd.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <sys/types.h>
#include <yajl/yajl_common.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if COLLECT_DEBUG
static int log_level = LOG_DEBUG;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <libperfstat.h>
#include <sys/protosw.h>
**/
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_lua.h"
/* Include the Lua API header files. */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <lvm2app.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <dirent.h>
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
/*
* internal helper functions
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
/*
* private data types
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
-#include "meta_data.h"
+#include "utils/common/common.h"
+#include "utils/metadata/meta_data.h"
#include "utils_llist.h"
#include <regex.h>
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#define SATISFY_ALL 0
#define SATISFY_ANY 1
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#define SATISFY_ALL 0
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "utils_llist.h"
#include <poll.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_match.h"
+#include "utils/common/common.h"
+#include "utils/match/match.h"
#include <libmemcached/memcached.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <MicAccessApi.h>
#include <MicAccessErrorTypes.h>
#include "collectd.h"
-#include "common.h"
#include "configfile.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <modbus.h>
#include <netdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_complain.h"
#include <mosquitto.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_MYSQL_H
#include <mysql.h>
#include "collectd.h"
-#include "common.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <netapp_api.h>
#include <netapp_errno.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <asm/types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_complain.h"
#include "utils_fbhash.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_KSTAT_H
#include <kstat.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <curl/curl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <glib.h>
#include <libnotify/notify.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <auth-client.h>
#include <libesmtp.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#define NAGIOS_OK 0
#define NAGIOS_WARNING 1
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_NETDB_H
#include <netdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <upsclient.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <owcapi.h>
#include <regex.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if defined(__APPLE__)
#pragma clang diagnostic push
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
/**
* There is two main kinds of OpenVPN status file:
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_db_query.h"
+#include "utils/common/common.h"
+#include "utils/db_query/db_query.h"
#include <oci.h>
#include "collectd.h"
-#include "common.h" /* auxiliary functions */
+#include "utils/common/common.h" /* auxiliary functions */
-#include "utils_ovs.h" /* OVS helpers */
+#include "utils/ovs/ovs.h" /* OVS helpers */
#define OVS_EVENTS_IFACE_NAME_SIZE 128
#define OVS_EVENTS_IFACE_UUID_SIZE 64
* Taras Chornyi <tarasx.chornyi@intel.com>
*/
-#include "common.h"
+#include "utils/common/common.h"
-#include "utils_ovs.h" /* OvS helpers */
+#include "utils/ovs/ovs.h" /* OvS helpers */
/* Plugin name */
static const char plugin_name[] = "ovs_stats";
if (strlen(iface->ex_iface_id))
meta_data_add_string(meta, "iface-id", iface->ex_iface_id);
}
- snprintf(devname, sizeof(devname), "%s.%s.%s", bridge->name, port->name,
- iface->name);
+ strjoin(devname, sizeof(devname),
+ (char *[]){
+ bridge->name, port->name, iface->name,
+ },
+ 3, ".");
ovs_stats_submit_one(devname, "if_collisions", NULL,
iface->stats[collisions], meta);
ovs_stats_submit_two(devname, "if_dropped", NULL, iface->stats[rx_dropped],
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "utils_llist.h"
#include <linux/pci_regs.h>
#endif /* DEBUG */
/* ... while we want the definition found in plugin.h. */
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "filter_chain.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <poll.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_complain.h"
#include <netinet/in.h>
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "plugin.h"
+#include "utils/db_query/db_query.h"
#include "utils_cache.h"
#include "utils_complain.h"
-#include "utils_db_query.h"
#include <libpq-fe.h>
#include <pg_config_manual.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_llist.h"
#include <errno.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_LIBTASKSTATS
+#include "utils/taskstats/taskstats.h"
#include "utils_complain.h"
-#include "utils_taskstats.h"
#endif
/* Include header files for the mach system, if they exist.. */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "cpython.h"
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "cpython.h"
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "cpython.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <hiredis/hiredis.h>
#include <sys/time.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <routeros_api.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_rrdcreate.h"
+#include "utils/common/common.h"
+#include "utils/rrdcreate/rrdcreate.h"
#undef HAVE_CONFIG_H
#include <rrd.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils/rrdcreate/rrdcreate.h"
#include "utils_random.h"
-#include "utils_rrdcreate.h"
#include <rrd.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#if defined(HAVE_SENSORS_SENSORS_H)
#include <sensors/sensors.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <stdio.h>
#include <stdlib.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <atasmart.h>
#include <libudev.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include "utils_complain.h"
-#include "utils_ignorelist.h"
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include "collectd.h"
-#include "common.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_llist.h"
#include <regex.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
-#include "utils_latency.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils/latency/latency.h"
#include <netdb.h>
#include <poll.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYS_SWAP_H
#include <sys/swap.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYSLOG_H
#include <syslog.h>
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "plugin.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_latency_config.h"
+#include "utils/common/common.h"
+#include "utils/latency/latency_config.h"
#include "utils_tail_match.h"
/*
#include "collectd.h"
-#include "common.h" /* auxiliary functions */
-#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
-#include "utils_tail.h"
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "utils/common/common.h" /* auxiliary functions */
+#include "utils/tail/tail.h"
#include <fcntl.h>
#include <stdlib.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !HAVE_LIBKSTAT
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_subst.h"
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_subst.h"
#include <regex.h>
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
-#include "meta_data.h"
+#include "utils/common/common.h"
+#include "utils/metadata/meta_data.h"
#include "utils_subst.h"
struct ts_key_list_s {
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
#include "plugin.h"
+#include "utils/common/common.h"
static void v5_swap_instances(value_list_t *vl) /* {{{ */
{
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if defined(__OpenBSD__)
#define HAVE_KVM_GETFILES 1
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <arpa/inet.h>
#include <netdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#if !KERNEL_LINUX
#error "This module is for Linux only."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_threshold.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <tcrdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_time.h"
#include "msr-index.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-
-#include "utils_cmd_flush.h"
-#include "utils_cmd_getthreshold.h"
-#include "utils_cmd_getval.h"
-#include "utils_cmd_listval.h"
-#include "utils_cmd_putnotif.h"
-#include "utils_cmd_putval.h"
+#include "utils/common/common.h"
+
+#include "utils/cmds/flush.h"
+#include "utils/cmds/getthreshold.h"
+#include "utils/cmds/getval.h"
+#include "utils/cmds/listval.h"
+#include "utils/cmds/putnotif.h"
+#include "utils/cmds/putval.h"
#include <sys/stat.h>
#include <sys/un.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if KERNEL_LINUX
#include <sys/sysinfo.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_STATGRAB_H
#include <statgrab.h>
--- /dev/null
+/**
+ * collectd - src/utils_avltree.c
+ * Copyright (C) 2006,2007 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "utils/avltree/avltree.h"
+
+#define BALANCE(n) \
+ ((((n)->left == NULL) ? 0 : (n)->left->height) - \
+ (((n)->right == NULL) ? 0 : (n)->right->height))
+
+/*
+ * private data types
+ */
+struct c_avl_node_s {
+ void *key;
+ void *value;
+
+ int height;
+ struct c_avl_node_s *left;
+ struct c_avl_node_s *right;
+ struct c_avl_node_s *parent;
+};
+typedef struct c_avl_node_s c_avl_node_t;
+
+struct c_avl_tree_s {
+ c_avl_node_t *root;
+ int (*compare)(const void *, const void *);
+ int size;
+};
+
+struct c_avl_iterator_s {
+ c_avl_tree_t *tree;
+ c_avl_node_t *node;
+};
+
+/*
+ * private functions
+ */
+#if 0
+static void verify_tree (c_avl_node_t *n)
+{
+ if (n == NULL)
+ return;
+
+ verify_tree (n->left);
+ verify_tree (n->right);
+
+ assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1));
+ assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n));
+} /* void verify_tree */
+#else
+#define verify_tree(n) /**/
+#endif
+
+static void free_node(c_avl_node_t *n) {
+ if (n == NULL)
+ return;
+
+ if (n->left != NULL)
+ free_node(n->left);
+ if (n->right != NULL)
+ free_node(n->right);
+
+ free(n);
+}
+
+static int calc_height(c_avl_node_t *n) {
+ int height_left;
+ int height_right;
+
+ if (n == NULL)
+ return 0;
+
+ height_left = (n->left == NULL) ? 0 : n->left->height;
+ height_right = (n->right == NULL) ? 0 : n->right->height;
+
+ return ((height_left > height_right) ? height_left : height_right) + 1;
+} /* int calc_height */
+
+static c_avl_node_t *search(c_avl_tree_t *t, const void *key) {
+ c_avl_node_t *n;
+ int cmp;
+
+ n = t->root;
+ while (n != NULL) {
+ cmp = t->compare(key, n->key);
+ if (cmp == 0)
+ return n;
+ else if (cmp < 0)
+ n = n->left;
+ else
+ n = n->right;
+ }
+
+ return NULL;
+}
+
+/* (x) (y)
+ * / \ / \
+ * (y) /\ /\ (x)
+ * / \ /_c\ ==> / a\ / \
+ * /\ /\ /____\/\ /\
+ * / a\ /_b\ /_b\ /_c\
+ * /____\
+ */
+static c_avl_node_t *rotate_right(c_avl_tree_t *t, c_avl_node_t *x) {
+ c_avl_node_t *p;
+ c_avl_node_t *y;
+ c_avl_node_t *b;
+
+ assert(x != NULL);
+ assert(x->left != NULL);
+
+ p = x->parent;
+ y = x->left;
+ b = y->right;
+
+ x->left = b;
+ if (b != NULL)
+ b->parent = x;
+
+ x->parent = y;
+ y->right = x;
+
+ y->parent = p;
+ assert((p == NULL) || (p->left == x) || (p->right == x));
+ if (p == NULL)
+ t->root = y;
+ else if (p->left == x)
+ p->left = y;
+ else
+ p->right = y;
+
+ x->height = calc_height(x);
+ y->height = calc_height(y);
+
+ return y;
+} /* void rotate_right */
+
+/*
+ * (x) (y)
+ * / \ / \
+ * /\ (y) (x) /\
+ * /_a\ / \ ==> / \ / c\
+ * /\ /\ /\ /\/____\
+ * /_b\ / c\ /_a\ /_b\
+ * /____\
+ */
+static c_avl_node_t *rotate_left(c_avl_tree_t *t, c_avl_node_t *x) {
+ c_avl_node_t *p;
+ c_avl_node_t *y;
+ c_avl_node_t *b;
+
+ assert(x != NULL);
+ assert(x->right != NULL);
+
+ p = x->parent;
+ y = x->right;
+ b = y->left;
+
+ x->right = b;
+ if (b != NULL)
+ b->parent = x;
+
+ x->parent = y;
+ y->left = x;
+
+ y->parent = p;
+ assert((p == NULL) || (p->left == x) || (p->right == x));
+ if (p == NULL)
+ t->root = y;
+ else if (p->left == x)
+ p->left = y;
+ else
+ p->right = y;
+
+ x->height = calc_height(x);
+ y->height = calc_height(y);
+
+ return y;
+} /* void rotate_left */
+
+static c_avl_node_t *rotate_left_right(c_avl_tree_t *t, c_avl_node_t *x) {
+ rotate_left(t, x->left);
+ return rotate_right(t, x);
+} /* void rotate_left_right */
+
+static c_avl_node_t *rotate_right_left(c_avl_tree_t *t, c_avl_node_t *x) {
+ rotate_right(t, x->right);
+ return rotate_left(t, x);
+} /* void rotate_right_left */
+
+static void rebalance(c_avl_tree_t *t, c_avl_node_t *n) {
+ int b_top;
+ int b_bottom;
+
+ while (n != NULL) {
+ b_top = BALANCE(n);
+ assert((b_top >= -2) && (b_top <= 2));
+
+ if (b_top == -2) {
+ assert(n->right != NULL);
+ b_bottom = BALANCE(n->right);
+ assert((b_bottom >= -1) && (b_bottom <= 1));
+ if (b_bottom == 1)
+ n = rotate_right_left(t, n);
+ else
+ n = rotate_left(t, n);
+ } else if (b_top == 2) {
+ assert(n->left != NULL);
+ b_bottom = BALANCE(n->left);
+ assert((b_bottom >= -1) && (b_bottom <= 1));
+ if (b_bottom == -1)
+ n = rotate_left_right(t, n);
+ else
+ n = rotate_right(t, n);
+ } else {
+ int height = calc_height(n);
+ if (height == n->height)
+ break;
+ n->height = height;
+ }
+
+ assert(n->height == calc_height(n));
+
+ n = n->parent;
+ } /* while (n != NULL) */
+} /* void rebalance */
+
+static c_avl_node_t *c_avl_node_next(c_avl_node_t *n) {
+ c_avl_node_t *r; /* return node */
+
+ if (n == NULL) {
+ return NULL;
+ }
+
+ /* If we can't descent any further, we have to backtrack to the first
+ * parent that's bigger than we, i. e. who's _left_ child we are. */
+ if (n->right == NULL) {
+ r = n->parent;
+ while ((r != NULL) && (r->parent != NULL)) {
+ if (r->left == n)
+ break;
+ n = r;
+ r = n->parent;
+ }
+
+ /* n->right == NULL && r == NULL => t is root and has no next
+ * r->left != n => r->right = n => r->parent == NULL */
+ if ((r == NULL) || (r->left != n)) {
+ assert((r == NULL) || (r->parent == NULL));
+ return NULL;
+ } else {
+ assert(r->left == n);
+ return r;
+ }
+ } else {
+ r = n->right;
+ while (r->left != NULL)
+ r = r->left;
+ }
+
+ return r;
+} /* c_avl_node_t *c_avl_node_next */
+
+static c_avl_node_t *c_avl_node_prev(c_avl_node_t *n) {
+ c_avl_node_t *r; /* return node */
+
+ if (n == NULL) {
+ return NULL;
+ }
+
+ /* If we can't descent any further, we have to backtrack to the first
+ * parent that's smaller than we, i. e. who's _right_ child we are. */
+ if (n->left == NULL) {
+ r = n->parent;
+ while ((r != NULL) && (r->parent != NULL)) {
+ if (r->right == n)
+ break;
+ n = r;
+ r = n->parent;
+ }
+
+ /* n->left == NULL && r == NULL => t is root and has no next
+ * r->right != n => r->left = n => r->parent == NULL */
+ if ((r == NULL) || (r->right != n)) {
+ assert((r == NULL) || (r->parent == NULL));
+ return NULL;
+ } else {
+ assert(r->right == n);
+ return r;
+ }
+ } else {
+ r = n->left;
+ while (r->right != NULL)
+ r = r->right;
+ }
+
+ return r;
+} /* c_avl_node_t *c_avl_node_prev */
+
+static int _remove(c_avl_tree_t *t, c_avl_node_t *n) {
+ assert((t != NULL) && (n != NULL));
+
+ if ((n->left != NULL) && (n->right != NULL)) {
+ c_avl_node_t *r; /* replacement node */
+ if (BALANCE(n) > 0) /* left subtree is higher */
+ {
+ assert(n->left != NULL);
+ r = c_avl_node_prev(n);
+
+ } else /* right subtree is higher */
+ {
+ assert(n->right != NULL);
+ r = c_avl_node_next(n);
+ }
+
+ assert((r->left == NULL) || (r->right == NULL));
+
+ /* copy content */
+ n->key = r->key;
+ n->value = r->value;
+
+ n = r;
+ }
+
+ assert((n->left == NULL) || (n->right == NULL));
+
+ if ((n->left == NULL) && (n->right == NULL)) {
+ /* Deleting a leave is easy */
+ if (n->parent == NULL) {
+ assert(t->root == n);
+ t->root = NULL;
+ } else {
+ assert((n->parent->left == n) || (n->parent->right == n));
+ if (n->parent->left == n)
+ n->parent->left = NULL;
+ else
+ n->parent->right = NULL;
+
+ rebalance(t, n->parent);
+ }
+
+ free_node(n);
+ } else if (n->left == NULL) {
+ assert(BALANCE(n) == -1);
+ assert((n->parent == NULL) || (n->parent->left == n) ||
+ (n->parent->right == n));
+ if (n->parent == NULL) {
+ assert(t->root == n);
+ t->root = n->right;
+ } else if (n->parent->left == n) {
+ n->parent->left = n->right;
+ } else {
+ n->parent->right = n->right;
+ }
+ n->right->parent = n->parent;
+
+ if (n->parent != NULL)
+ rebalance(t, n->parent);
+
+ n->right = NULL;
+ free_node(n);
+ } else if (n->right == NULL) {
+ assert(BALANCE(n) == 1);
+ assert((n->parent == NULL) || (n->parent->left == n) ||
+ (n->parent->right == n));
+ if (n->parent == NULL) {
+ assert(t->root == n);
+ t->root = n->left;
+ } else if (n->parent->left == n) {
+ n->parent->left = n->left;
+ } else {
+ n->parent->right = n->left;
+ }
+ n->left->parent = n->parent;
+
+ if (n->parent != NULL)
+ rebalance(t, n->parent);
+
+ n->left = NULL;
+ free_node(n);
+ } else {
+ assert(0);
+ }
+
+ return 0;
+} /* void *_remove */
+
+/*
+ * public functions
+ */
+c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *)) {
+ c_avl_tree_t *t;
+
+ if (compare == NULL)
+ return NULL;
+
+ if ((t = malloc(sizeof(*t))) == NULL)
+ return NULL;
+
+ t->root = NULL;
+ t->compare = compare;
+ t->size = 0;
+
+ return t;
+}
+
+void c_avl_destroy(c_avl_tree_t *t) {
+ if (t == NULL)
+ return;
+ free_node(t->root);
+ free(t);
+}
+
+int c_avl_insert(c_avl_tree_t *t, void *key, void *value) {
+ c_avl_node_t *new;
+ c_avl_node_t *nptr;
+ int cmp;
+
+ if ((new = malloc(sizeof(*new))) == NULL)
+ return -1;
+
+ new->key = key;
+ new->value = value;
+ new->height = 1;
+ new->left = NULL;
+ new->right = NULL;
+
+ if (t->root == NULL) {
+ new->parent = NULL;
+ t->root = new;
+ t->size = 1;
+ return 0;
+ }
+
+ nptr = t->root;
+ while (42) {
+ cmp = t->compare(nptr->key, new->key);
+ if (cmp == 0) {
+ free_node(new);
+ return 1;
+ } else if (cmp < 0) {
+ /* nptr < new */
+ if (nptr->right == NULL) {
+ nptr->right = new;
+ new->parent = nptr;
+ rebalance(t, nptr);
+ break;
+ } else {
+ nptr = nptr->right;
+ }
+ } else /* if (cmp > 0) */
+ {
+ /* nptr > new */
+ if (nptr->left == NULL) {
+ nptr->left = new;
+ new->parent = nptr;
+ rebalance(t, nptr);
+ break;
+ } else {
+ nptr = nptr->left;
+ }
+ }
+ } /* while (42) */
+
+ verify_tree(t->root);
+ ++t->size;
+ return 0;
+} /* int c_avl_insert */
+
+int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue) {
+ c_avl_node_t *n;
+ int status;
+
+ assert(t != NULL);
+
+ n = search(t, key);
+ if (n == NULL)
+ return -1;
+
+ if (rkey != NULL)
+ *rkey = n->key;
+ if (rvalue != NULL)
+ *rvalue = n->value;
+
+ status = _remove(t, n);
+ verify_tree(t->root);
+ --t->size;
+ return status;
+} /* void *c_avl_remove */
+
+int c_avl_get(c_avl_tree_t *t, const void *key, void **value) {
+ c_avl_node_t *n;
+
+ assert(t != NULL);
+
+ n = search(t, key);
+ if (n == NULL)
+ return -1;
+
+ if (value != NULL)
+ *value = n->value;
+
+ return 0;
+}
+
+int c_avl_pick(c_avl_tree_t *t, void **key, void **value) {
+ c_avl_node_t *n;
+ c_avl_node_t *p;
+
+ assert(t != NULL);
+
+ if ((key == NULL) || (value == NULL))
+ return -1;
+ if (t->root == NULL)
+ return -1;
+
+ n = t->root;
+ while ((n->left != NULL) || (n->right != NULL)) {
+ if (n->left == NULL) {
+ n = n->right;
+ continue;
+ } else if (n->right == NULL) {
+ n = n->left;
+ continue;
+ }
+
+ if (n->left->height > n->right->height)
+ n = n->left;
+ else
+ n = n->right;
+ }
+
+ p = n->parent;
+ if (p == NULL)
+ t->root = NULL;
+ else if (p->left == n)
+ p->left = NULL;
+ else
+ p->right = NULL;
+
+ *key = n->key;
+ *value = n->value;
+
+ free_node(n);
+ --t->size;
+ rebalance(t, p);
+
+ return 0;
+} /* int c_avl_pick */
+
+c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t) {
+ c_avl_iterator_t *iter;
+
+ if (t == NULL)
+ return NULL;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL)
+ return NULL;
+ iter->tree = t;
+
+ return iter;
+} /* c_avl_iterator_t *c_avl_get_iterator */
+
+int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value) {
+ c_avl_node_t *n;
+
+ if ((iter == NULL) || (key == NULL) || (value == NULL))
+ return -1;
+
+ if (iter->node == NULL) {
+ for (n = iter->tree->root; n != NULL; n = n->left)
+ if (n->left == NULL)
+ break;
+ iter->node = n;
+ } else {
+ n = c_avl_node_next(iter->node);
+ }
+
+ if (n == NULL)
+ return -1;
+
+ iter->node = n;
+ *key = n->key;
+ *value = n->value;
+
+ return 0;
+} /* int c_avl_iterator_next */
+
+int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value) {
+ c_avl_node_t *n;
+
+ if ((iter == NULL) || (key == NULL) || (value == NULL))
+ return -1;
+
+ if (iter->node == NULL) {
+ for (n = iter->tree->root; n != NULL; n = n->right)
+ if (n->right == NULL)
+ break;
+ iter->node = n;
+ } else {
+ n = c_avl_node_prev(iter->node);
+ }
+
+ if (n == NULL)
+ return -1;
+
+ iter->node = n;
+ *key = n->key;
+ *value = n->value;
+
+ return 0;
+} /* int c_avl_iterator_prev */
+
+void c_avl_iterator_destroy(c_avl_iterator_t *iter) { free(iter); }
+
+int c_avl_size(c_avl_tree_t *t) {
+ if (t == NULL)
+ return 0;
+ return t->size;
+}
--- /dev/null
+/**
+ * collectd - src/utils_avltree.h
+ * Copyright (C) 2006,2007 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_AVLTREE_H
+#define UTILS_AVLTREE_H 1
+
+struct c_avl_tree_s;
+typedef struct c_avl_tree_s c_avl_tree_t;
+
+struct c_avl_iterator_s;
+typedef struct c_avl_iterator_s c_avl_iterator_t;
+
+/*
+ * NAME
+ * c_avl_create
+ *
+ * DESCRIPTION
+ * Allocates a new AVL-tree.
+ *
+ * PARAMETERS
+ * `compare' The function-pointer `compare' is used to compare two keys. It
+ * has to return less than zero if its first argument is smaller
+ * then the second argument, more than zero if the first argument
+ * is bigger than the second argument and zero if they are equal.
+ * If your keys are char-pointers, you can use the `strcmp'
+ * function from the libc here.
+ *
+ * RETURN VALUE
+ * A c_avl_tree_t-pointer upon success or NULL upon failure.
+ */
+c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *));
+
+/*
+ * NAME
+ * c_avl_destroy
+ *
+ * DESCRIPTION
+ * Deallocates an AVL-tree. Stored value- and key-pointer are lost, but of
+ * course not freed.
+ */
+void c_avl_destroy(c_avl_tree_t *t);
+
+/*
+ * NAME
+ * c_avl_insert
+ *
+ * DESCRIPTION
+ * Stores the key-value-pair in the AVL-tree pointed to by `t'.
+ *
+ * PARAMETERS
+ * `t' AVL-tree to store the data in.
+ * `key' Key used to store the value under. This is used to get back to
+ * the value again. The pointer is stored in an internal structure
+ * and _not_ copied. So the memory pointed to may _not_ be freed
+ * before this entry is removed. You can use the `rkey' argument
+ * to `avl_remove' to get the original pointer back and free it.
+ * `value' Value to be stored.
+ *
+ * RETURN VALUE
+ * Zero upon success, non-zero otherwise. It's less than zero if an error
+ * occurred or greater than zero if the key is already stored in the tree.
+ */
+int c_avl_insert(c_avl_tree_t *t, void *key, void *value);
+
+/*
+ * NAME
+ * c_avl_remove
+ *
+ * DESCRIPTION
+ * Removes a key-value-pair from the tree t. The stored key and value may be
+ * returned in `rkey' and `rvalue'.
+ *
+ * PARAMETERS
+ * `t' AVL-tree to remove key-value-pair from.
+ * `key' Key to identify the entry.
+ * `rkey' Pointer to a pointer in which to store the key. May be NULL.
+ * Since the `key' pointer is not copied when creating an entry,
+ * the pointer may not be available anymore from outside the tree.
+ * You can use this argument to get the actual pointer back and
+ * free the memory pointed to by it.
+ * `rvalue' Pointer to a pointer in which to store the value. May be NULL.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if the key isn't found in the tree.
+ */
+int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue);
+
+/*
+ * NAME
+ * c_avl_get
+ *
+ * DESCRIPTION
+ * Retrieve the `value' belonging to `key'.
+ *
+ * PARAMETERS
+ * `t' AVL-tree to get the value from.
+ * `key' Key to identify the entry.
+ * `value' Pointer to a pointer in which to store the value. May be NULL.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if the key isn't found in the tree.
+ */
+int c_avl_get(c_avl_tree_t *t, const void *key, void **value);
+
+/*
+ * NAME
+ * c_avl_pick
+ *
+ * DESCRIPTION
+ * Remove a (pseudo-)random element from the tree and return its `key' and
+ * `value'. Entries are not returned in any particular order. This function
+ * is intended for cache-flushes that don't care about the order but simply
+ * want to remove all elements, one at a time.
+ *
+ * PARAMETERS
+ * `t' AVL-tree to get the value from.
+ * `key' Pointer to a pointer in which to store the key.
+ * `value' Pointer to a pointer in which to store the value.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if the tree is empty or key or value is
+ * NULL.
+ */
+int c_avl_pick(c_avl_tree_t *t, void **key, void **value);
+
+c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t);
+int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value);
+int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value);
+void c_avl_iterator_destroy(c_avl_iterator_t *iter);
+
+/*
+ * NAME
+ * c_avl_size
+ *
+ * DESCRIPTION
+ * Return the size (number of nodes) of the specified tree.
+ *
+ * PARAMETERS
+ * `t' AVL-tree to get the size of.
+ *
+ * RETURN VALUE
+ * Number of nodes in the tree, 0 if the tree is empty or NULL.
+ */
+int c_avl_size(c_avl_tree_t *t);
+
+#endif /* UTILS_AVLTREE_H */
--- /dev/null
+/**
+ * collectd - src/tests/test_utils_avltree.c
+ * Copyright (C) 2013 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "utils/common/common.h" /* STATIC_ARRAY_SIZE */
+
+#include "testing.h"
+#include "utils/avltree/avltree.h"
+
+static int compare_total_count;
+
+#define RESET_COUNTS() \
+ do { \
+ compare_total_count = 0; \
+ } while (0)
+
+static int compare_callback(void const *v0, void const *v1) {
+ assert(v0 != NULL);
+ assert(v1 != NULL);
+
+ compare_total_count++;
+ return strcmp(v0, v1);
+}
+
+struct kv_t {
+ char *key;
+ char *value;
+};
+
+static int kv_compare(const void *a_ptr, const void *b_ptr) {
+ return strcmp(((struct kv_t *)a_ptr)->key, ((struct kv_t *)b_ptr)->key);
+}
+
+DEF_TEST(success) {
+ struct kv_t cases[] = {
+ {"Eeph7chu", "vai1reiV"}, {"igh3Paiz", "teegh1Ee"},
+ {"caip6Uu8", "ooteQu8n"}, {"Aech6vah", "AijeeT0l"},
+ {"Xah0et2L", "gah8Taep"}, {"BocaeB8n", "oGaig8io"},
+ {"thai8AhM", "ohjeFo3f"}, {"ohth6ieC", "hoo8ieWo"},
+ {"aej7Woow", "phahuC2s"}, {"Hai8ier2", "Yie6eimi"},
+ {"phuXi3Li", "JaiF7ieb"}, {"Shaig5ef", "aihi5Zai"},
+ {"voh6Aith", "Oozaeto0"}, {"zaiP5kie", "seep5veM"},
+ {"pae7ba7D", "chie8Ojo"}, {"Gou2ril3", "ouVoo0ha"},
+ {"lo3Thee3", "ahDu4Zuj"}, {"Rah8kohv", "ieShoc7E"},
+ {"ieN5engi", "Aevou1ah"}, {"ooTe4OhP", "aingai5Y"},
+ };
+
+ struct kv_t sorted_cases[STATIC_ARRAY_SIZE(cases)];
+ memcpy(sorted_cases, cases, sizeof(cases));
+ qsort(sorted_cases, STATIC_ARRAY_SIZE(cases), sizeof(struct kv_t),
+ kv_compare);
+
+ c_avl_tree_t *t;
+
+ RESET_COUNTS();
+ CHECK_NOT_NULL(t = c_avl_create(compare_callback));
+
+ /* insert */
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ char *key;
+ char *value;
+
+ CHECK_NOT_NULL(key = strdup(cases[i].key));
+ CHECK_NOT_NULL(value = strdup(cases[i].value));
+
+ CHECK_ZERO(c_avl_insert(t, key, value));
+ EXPECT_EQ_INT((int)(i + 1), c_avl_size(t));
+ }
+
+ /* Key already exists. */
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++)
+ EXPECT_EQ_INT(1, c_avl_insert(t, cases[i].key, cases[i].value));
+
+ /* get */
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ char *value_ret = NULL;
+
+ CHECK_ZERO(c_avl_get(t, cases[i].key, (void *)&value_ret));
+ EXPECT_EQ_STR(cases[i].value, value_ret);
+ }
+
+ /* iterate forward */
+ {
+ c_avl_iterator_t *iter = c_avl_get_iterator(t);
+ char *key;
+ char *value;
+ size_t i = 0;
+ while (c_avl_iterator_next(iter, (void **)&key, (void **)&value) == 0) {
+ EXPECT_EQ_STR(sorted_cases[i].key, key);
+ EXPECT_EQ_STR(sorted_cases[i].value, value);
+ i++;
+ }
+ c_avl_iterator_destroy(iter);
+ EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases));
+ }
+
+ /* iterate backward */
+ {
+ c_avl_iterator_t *iter = c_avl_get_iterator(t);
+ char *key;
+ char *value;
+ size_t i = 0;
+ while (c_avl_iterator_prev(iter, (void **)&key, (void **)&value) == 0) {
+ EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].key, key);
+ EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].value,
+ value);
+ i++;
+ }
+ c_avl_iterator_destroy(iter);
+ EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases));
+ }
+
+ /* remove half */
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases) / 2; i++) {
+ char *key = NULL;
+ char *value = NULL;
+
+ int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1));
+
+ CHECK_ZERO(c_avl_remove(t, cases[i].key, (void *)&key, (void *)&value));
+
+ EXPECT_EQ_STR(cases[i].key, key);
+ EXPECT_EQ_STR(cases[i].value, value);
+
+ free(key);
+ free(value);
+
+ EXPECT_EQ_INT(expected_size, c_avl_size(t));
+ }
+
+ /* pick the other half */
+ for (size_t i = STATIC_ARRAY_SIZE(cases) / 2; i < STATIC_ARRAY_SIZE(cases);
+ i++) {
+ char *key = NULL;
+ char *value = NULL;
+
+ int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1));
+
+ EXPECT_EQ_INT(expected_size + 1, c_avl_size(t));
+ EXPECT_EQ_INT(0, c_avl_pick(t, (void *)&key, (void *)&value));
+
+ free(key);
+ free(value);
+
+ EXPECT_EQ_INT(expected_size, c_avl_size(t));
+ }
+
+ c_avl_destroy(t);
+
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(success);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_cmds.c
+ * Copyright (C) 2008 Florian Forster
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#include "utils/cmds/cmds.h"
+#include "utils/cmds/flush.h"
+#include "utils/cmds/getval.h"
+#include "utils/cmds/listval.h"
+#include "utils/cmds/parse_option.h"
+#include "utils/cmds/putval.h"
+#include "utils/common/common.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+static cmd_options_t default_options = {
+ /* identifier_default_host = */ NULL,
+};
+
+/*
+ * private helper functions
+ */
+
+static cmd_status_t cmd_split(char *buffer, size_t *ret_len, char ***ret_fields,
+ cmd_error_handler_t *err) {
+ char *field;
+ bool in_field, in_quotes;
+
+ size_t estimate, len;
+ char **fields;
+
+ estimate = 0;
+ in_field = false;
+ for (char *string = buffer; *string != '\0'; ++string) {
+ /* Make a quick worst-case estimate of the number of fields by
+ * counting spaces and ignoring quotation marks. */
+ if (!isspace((int)*string)) {
+ if (!in_field) {
+ estimate++;
+ in_field = true;
+ }
+ } else {
+ in_field = false;
+ }
+ }
+
+ /* fields will be NULL-terminated */
+ fields = malloc((estimate + 1) * sizeof(*fields));
+ if (fields == NULL) {
+ cmd_error(CMD_ERROR, err, "malloc failed.");
+ return CMD_ERROR;
+ }
+
+#define END_FIELD() \
+ do { \
+ *field = '\0'; \
+ field = NULL; \
+ in_field = false; \
+ } while (0)
+#define NEW_FIELD() \
+ do { \
+ field = string; \
+ in_field = true; \
+ assert(len < estimate); \
+ fields[len] = field; \
+ field++; \
+ len++; \
+ } while (0)
+
+ len = 0;
+ field = NULL;
+ in_field = false;
+ in_quotes = false;
+ for (char *string = buffer; *string != '\0'; string++) {
+ if (isspace((int)string[0])) {
+ if (!in_quotes) {
+ if (in_field)
+ END_FIELD();
+
+ /* skip space */
+ continue;
+ }
+ } else if (string[0] == '"') {
+ /* Note: Two consecutive quoted fields not separated by space are
+ * treated as different fields. This is the collectd 5.x behavior
+ * around splitting fields. */
+
+ if (in_quotes) {
+ /* end of quoted field */
+ if (!in_field) /* empty quoted string */
+ NEW_FIELD();
+ END_FIELD();
+ in_quotes = false;
+ continue;
+ }
+
+ in_quotes = true;
+ /* if (! in_field): add new field on next iteration
+ * else: quoted string following an unquoted string (one field)
+ * in either case: skip quotation mark */
+ continue;
+ } else if ((string[0] == '\\') && in_quotes) {
+ /* Outside of quotes, a backslash is a regular character (mostly
+ * for backward compatibility). */
+
+ if (string[1] == '\0') {
+ free(fields);
+ cmd_error(CMD_PARSE_ERROR, err, "Backslash at end of string.");
+ return CMD_PARSE_ERROR;
+ }
+
+ /* un-escape the next character; skip backslash */
+ string++;
+ }
+
+ if (!in_field)
+ NEW_FIELD();
+ else {
+ *field = string[0];
+ field++;
+ }
+ }
+
+ if (in_quotes) {
+ free(fields);
+ cmd_error(CMD_PARSE_ERROR, err, "Unterminated quoted string.");
+ return CMD_PARSE_ERROR;
+ }
+
+#undef NEW_FIELD
+#undef END_FIELD
+
+ fields[len] = NULL;
+ if (ret_len != NULL)
+ *ret_len = len;
+ if (ret_fields != NULL)
+ *ret_fields = fields;
+ else
+ free(fields);
+ return CMD_OK;
+} /* int cmd_split */
+
+/*
+ * public API
+ */
+
+void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
+ const char *format, ...) {
+ va_list ap;
+
+ if ((err == NULL) || (err->cb == NULL))
+ return;
+
+ va_start(ap, format);
+ err->cb(err->ud, status, format, ap);
+ va_end(ap);
+} /* void cmd_error */
+
+cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
+ const cmd_options_t *opts, cmd_error_handler_t *err) {
+ char *command = NULL;
+ cmd_status_t status;
+
+ if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Missing command.");
+ return CMD_ERROR;
+ }
+
+ if (opts == NULL)
+ opts = &default_options;
+
+ memset(ret_cmd, 0, sizeof(*ret_cmd));
+ command = argv[0];
+ if (strcasecmp("FLUSH", command) == 0) {
+ ret_cmd->type = CMD_FLUSH;
+ status =
+ cmd_parse_flush(argc - 1, argv + 1, &ret_cmd->cmd.flush, opts, err);
+ } else if (strcasecmp("GETVAL", command) == 0) {
+ ret_cmd->type = CMD_GETVAL;
+ status =
+ cmd_parse_getval(argc - 1, argv + 1, &ret_cmd->cmd.getval, opts, err);
+ } else if (strcasecmp("LISTVAL", command) == 0) {
+ ret_cmd->type = CMD_LISTVAL;
+ status = cmd_parse_listval(argc - 1, argv + 1, opts, err);
+ } else if (strcasecmp("PUTVAL", command) == 0) {
+ ret_cmd->type = CMD_PUTVAL;
+ status =
+ cmd_parse_putval(argc - 1, argv + 1, &ret_cmd->cmd.putval, opts, err);
+ } else {
+ ret_cmd->type = CMD_UNKNOWN;
+ cmd_error(CMD_UNKNOWN_COMMAND, err, "Unknown command `%s'.", command);
+ return CMD_UNKNOWN_COMMAND;
+ }
+
+ if (status != CMD_OK)
+ ret_cmd->type = CMD_UNKNOWN;
+ return status;
+} /* cmd_status_t cmd_parsev */
+
+cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
+ char **fields = NULL;
+ size_t fields_num = 0;
+ cmd_status_t status;
+
+ if ((status = cmd_split(buffer, &fields_num, &fields, err)) != CMD_OK)
+ return status;
+
+ status = cmd_parsev(fields_num, fields, ret_cmd, opts, err);
+ free(fields);
+ return status;
+} /* cmd_status_t cmd_parse */
+
+void cmd_destroy(cmd_t *cmd) {
+ if (cmd == NULL)
+ return;
+
+ switch (cmd->type) {
+ case CMD_UNKNOWN:
+ /* nothing to do */
+ break;
+ case CMD_FLUSH:
+ cmd_destroy_flush(&cmd->cmd.flush);
+ break;
+ case CMD_GETVAL:
+ cmd_destroy_getval(&cmd->cmd.getval);
+ break;
+ case CMD_LISTVAL:
+ break;
+ case CMD_PUTVAL:
+ cmd_destroy_putval(&cmd->cmd.putval);
+ break;
+ }
+} /* void cmd_destroy */
+
+cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value,
+ cmd_error_handler_t *err) {
+ char *key, *value;
+
+ if (field == NULL) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid argument to cmd_parse_option.");
+ return CMD_ERROR;
+ }
+ key = value = field;
+
+ /* Look for the equal sign. */
+ while (isalnum((int)value[0]) || (value[0] == '_') || (value[0] == ':'))
+ value++;
+ if ((value[0] != '=') || (value == key)) {
+ /* Whether this is a fatal error is up to the caller. */
+ return CMD_NO_OPTION;
+ }
+ *value = '\0';
+ value++;
+
+ if (ret_key != NULL)
+ *ret_key = key;
+ if (ret_value != NULL)
+ *ret_value = value;
+
+ return CMD_OK;
+} /* cmd_status_t cmd_parse_option */
+
+void cmd_error_fh(void *ud, cmd_status_t status, const char *format,
+ va_list ap) {
+ FILE *fh = ud;
+ int code = -1;
+ char buf[1024];
+
+ if (status == CMD_OK)
+ code = 0;
+
+ vsnprintf(buf, sizeof(buf), format, ap);
+ buf[sizeof(buf) - 1] = '\0';
+ if (fprintf(fh, "%i %s\n", code, buf) < 0) {
+ WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh),
+ STRERRNO);
+ return;
+ }
+
+ fflush(fh);
+} /* void cmd_error_fh */
--- /dev/null
+/**
+ * collectd - src/utils_cmds.h
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_CMDS_H
+#define UTILS_CMDS_H 1
+
+#include "plugin.h"
+
+#include <stdarg.h>
+
+typedef enum {
+ CMD_UNKNOWN = 0,
+ CMD_FLUSH = 1,
+ CMD_GETVAL = 2,
+ CMD_LISTVAL = 3,
+ CMD_PUTVAL = 4,
+} cmd_type_t;
+#define CMD_TO_STRING(type) \
+ ((type) == CMD_FLUSH) \
+ ? "FLUSH" \
+ : ((type) == CMD_GETVAL) \
+ ? "GETVAL" \
+ : ((type) == CMD_LISTVAL) \
+ ? "LISTVAL" \
+ : ((type) == CMD_PUTVAL) ? "PUTVAL" : "UNKNOWN"
+
+typedef struct {
+ double timeout;
+
+ char **plugins;
+ size_t plugins_num;
+ identifier_t *identifiers;
+ size_t identifiers_num;
+} cmd_flush_t;
+
+typedef struct {
+ char *raw_identifier;
+ identifier_t identifier;
+} cmd_getval_t;
+
+typedef struct {
+ /* The raw identifier as provided by the user. */
+ char *raw_identifier;
+
+ /* An array of the fully parsed identifier and all value lists, and their
+ * options as provided by the user. */
+ value_list_t *vl;
+ size_t vl_num;
+} cmd_putval_t;
+
+/*
+ * NAME
+ * cmd_t
+ *
+ * DESCRIPTION
+ * The representation of a fully parsed command.
+ */
+typedef struct {
+ cmd_type_t type;
+ union {
+ cmd_flush_t flush;
+ cmd_getval_t getval;
+ cmd_putval_t putval;
+ } cmd;
+} cmd_t;
+
+/*
+ * NAME
+ * cmd_options_t
+ *
+ * DESCRIPTIONS
+ * Optional settings for tuning the parser behavior.
+ */
+typedef struct {
+ /* identifier_default_host: If non-NULL, the hostname is optional and will
+ * default to the specified value. */
+ char *identifier_default_host;
+} cmd_options_t;
+
+/*
+ * NAME
+ * cmd_status_t
+ *
+ * DESCRIPTION
+ * Status codes describing the parse result.
+ */
+typedef enum {
+ CMD_OK = 0,
+ CMD_ERROR = -1,
+ CMD_PARSE_ERROR = -2,
+ CMD_UNKNOWN_COMMAND = -3,
+
+ /* Not necessarily fatal errors. */
+ CMD_NO_OPTION = 1,
+} cmd_status_t;
+
+/*
+ * NAME
+ * cmd_error_handler_t
+ *
+ * DESCRIPTION
+ * An error handler describes a callback to be invoked when the parser
+ * encounters an error. The user data pointer will be passed to the callback
+ * as the first argument.
+ */
+typedef struct {
+ void (*cb)(void *, cmd_status_t, const char *, va_list);
+ void *ud;
+} cmd_error_handler_t;
+
+/*
+ * NAME:
+ * cmd_error
+ *
+ * DESCRIPTION
+ * Reports an error via the specified error handler (if set).
+ */
+void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
+ const char *format, ...);
+
+/*
+ * NAME
+ * cmd_parse
+ *
+ * DESCRIPTION
+ * Parse a command string and populate a command object.
+ *
+ * PARAMETERS
+ * `buffer' The command string to be parsed.
+ * `ret_cmd' The parse result will be stored at this location.
+ * `opts' Parser options. If NULL, defaults will be used.
+ * `err' An optional error handler to invoke on error.
+ *
+ * RETURN VALUE
+ * CMD_OK on success or the respective error code otherwise.
+ */
+cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
+ const cmd_options_t *opts, cmd_error_handler_t *err);
+
+void cmd_destroy(cmd_t *cmd);
+
+/*
+ * NAME
+ * cmd_parse_option
+ *
+ * DESCRIPTION
+ * Parses a command option which must be of the form:
+ * name=value with \ and spaces
+ *
+ * PARAMETERS
+ * `field' The parsed input field with any quotes removed and special
+ * characters unescaped.
+ * `ret_key' The parsed key will be stored at this location.
+ * `ret_value' The parsed value will be stored at this location.
+ *
+ * RETURN VALUE
+ * CMD_OK on success or an error code otherwise.
+ * CMD_NO_OPTION if `field' does not represent an option at all (missing
+ * equal sign).
+ */
+cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value,
+ cmd_error_handler_t *err);
+
+/*
+ * NAME
+ * cmd_error_fh
+ *
+ * DESCRIPTION
+ * An error callback writing the message to an open file handle using the
+ * format expected by the unixsock or exec plugins.
+ *
+ * PARAMETERS
+ * `ud' Error handler user-data pointer. This must be an open
+ * file-handle (FILE *).
+ * `status' The error status code.
+ * `format' Printf-style format string.
+ * `ap' Variable argument list providing the arguments for the format
+ * string.
+ */
+void cmd_error_fh(void *ud, cmd_status_t status, const char *format,
+ va_list ap);
+
+#endif /* UTILS_CMDS_H */
--- /dev/null
+/**
+ * collectd - src/tests/utils_cmds_test.c
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#include "testing.h"
+#include "utils/cmds/cmds.h"
+#include "utils/common/common.h"
+
+static void error_cb(void *ud, cmd_status_t status, const char *format,
+ va_list ap) {
+ if (status == CMD_OK)
+ return;
+
+ printf("ERROR[%d]: ", status);
+ vprintf(format, ap);
+ printf("\n");
+ fflush(stdout);
+} /* void error_cb */
+
+static cmd_options_t default_host_opts = {
+ /* identifier_default_host = */ "dummy-host",
+};
+
+static struct {
+ char *input;
+ cmd_options_t *opts;
+ cmd_status_t expected_status;
+ cmd_type_t expected_type;
+} parse_data[] = {
+ /* Valid FLUSH commands. */
+ {
+ "FLUSH", NULL, CMD_OK, CMD_FLUSH,
+ },
+ {
+ "FLUSH identifier=myhost/magic/MAGIC", NULL, CMD_OK, CMD_FLUSH,
+ },
+ {
+ "FLUSH identifier=magic/MAGIC", &default_host_opts, CMD_OK, CMD_FLUSH,
+ },
+ {
+ "FLUSH timeout=123 plugin=\"A\"", NULL, CMD_OK, CMD_FLUSH,
+ },
+ /* Invalid FLUSH commands. */
+ {
+ /* Missing hostname; no default. */
+ "FLUSH identifier=magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Missing 'identifier' key. */
+ "FLUSH myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Invalid timeout. */
+ "FLUSH timeout=A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Invalid identifier. */
+ "FLUSH identifier=invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Invalid option. */
+ "FLUSH invalid=option", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+
+ /* Valid GETVAL commands. */
+ {
+ "GETVAL myhost/magic/MAGIC", NULL, CMD_OK, CMD_GETVAL,
+ },
+ {
+ "GETVAL magic/MAGIC", &default_host_opts, CMD_OK, CMD_GETVAL,
+ },
+
+ /* Invalid GETVAL commands. */
+ {
+ "GETVAL magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "GETVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "GETVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+
+ /* Valid LISTVAL commands. */
+ {
+ "LISTVAL", NULL, CMD_OK, CMD_LISTVAL,
+ },
+
+ /* Invalid LISTVAL commands. */
+ {
+ "LISTVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+
+ /* Valid PUTVAL commands. */
+ {
+ "PUTVAL magic/MAGIC N:42", &default_host_opts, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC N:42", NULL, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC 1234:42", NULL, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC 1234:42 2345:23", NULL, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC interval=2 1234:42", NULL, CMD_OK,
+ CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23", NULL,
+ CMD_OK, CMD_PUTVAL,
+ },
+
+ /* Invalid PUTVAL commands. */
+ {
+ "PUTVAL magic/MAGIC N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL invalid N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC A:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/UNKNOWN 1234:42", NULL, CMD_PARSE_ERROR,
+ CMD_UNKNOWN,
+ },
+ /*
+ * As of collectd 5.x, PUTVAL accepts invalid options.
+ {
+ "PUTVAL myhost/magic/MAGIC invalid=2 1234:42",
+ NULL,
+ CMD_PARSE_ERROR,
+ CMD_UNKNOWN,
+ },
+ */
+
+ /* Invalid commands. */
+ {
+ "INVALID", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN,
+ },
+ {
+ "INVALID interval=2", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN,
+ },
+};
+
+DEF_TEST(parse) {
+ cmd_error_handler_t err = {error_cb, NULL};
+ int test_result = 0;
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(parse_data); i++) {
+ char *input = strdup(parse_data[i].input);
+
+ char description[1024];
+ cmd_status_t status;
+ cmd_t cmd;
+
+ bool result;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ status = cmd_parse(input, &cmd, parse_data[i].opts, &err);
+ snprintf(description, sizeof(description), "cmd_parse (\"%s\", opts=%p) = "
+ "%d (type=%d [%s]); want %d "
+ "(type=%d [%s])",
+ parse_data[i].input, parse_data[i].opts, status, cmd.type,
+ CMD_TO_STRING(cmd.type), parse_data[i].expected_status,
+ parse_data[i].expected_type,
+ CMD_TO_STRING(parse_data[i].expected_type));
+ result = (status == parse_data[i].expected_status) &&
+ (cmd.type == parse_data[i].expected_type);
+ LOG(result, description);
+
+ /* Run all tests before failing. */
+ if (!result)
+ test_result = -1;
+
+ cmd_destroy(&cmd);
+ free(input);
+ }
+
+ return test_result;
+}
+
+int main(int argc, char **argv) {
+ RUN_TEST(parse);
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_cmd_flush.c
+ * Copyright (C) 2008, 2016 Sebastian Harl
+ * Copyright (C) 2008 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian "tokkee" Harl <sh at tokkee.org>
+ * Florian "octo" Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/cmds/flush.h"
+#include "utils/common/common.h"
+
+cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
+
+ if ((ret_flush == NULL) || (opts == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_flush.");
+ return CMD_ERROR;
+ }
+
+ for (size_t i = 0; i < argc; i++) {
+ char *opt_key;
+ char *opt_value;
+ int status;
+
+ opt_key = NULL;
+ opt_value = NULL;
+ status = cmd_parse_option(argv[i], &opt_key, &opt_value, err);
+ if (status != 0) {
+ if (status == CMD_NO_OPTION)
+ cmd_error(CMD_PARSE_ERROR, err, "Invalid option string `%s'.", argv[i]);
+ cmd_destroy_flush(ret_flush);
+ return CMD_PARSE_ERROR;
+ }
+
+ if (strcasecmp("plugin", opt_key) == 0) {
+ strarray_add(&ret_flush->plugins, &ret_flush->plugins_num, opt_value);
+ } else if (strcasecmp("identifier", opt_key) == 0) {
+ identifier_t *id =
+ realloc(ret_flush->identifiers,
+ (ret_flush->identifiers_num + 1) * sizeof(*id));
+ if (id == NULL) {
+ cmd_error(CMD_ERROR, err, "realloc failed.");
+ cmd_destroy_flush(ret_flush);
+ return CMD_ERROR;
+ }
+
+ ret_flush->identifiers = id;
+ id = ret_flush->identifiers + ret_flush->identifiers_num;
+ ret_flush->identifiers_num++;
+ if (parse_identifier(opt_value, &id->host, &id->plugin,
+ &id->plugin_instance, &id->type, &id->type_instance,
+ opts->identifier_default_host) != 0) {
+ cmd_error(CMD_PARSE_ERROR, err, "Invalid identifier `%s'.", opt_value);
+ cmd_destroy_flush(ret_flush);
+ return CMD_PARSE_ERROR;
+ }
+ } else if (strcasecmp("timeout", opt_key) == 0) {
+ char *endptr;
+
+ errno = 0;
+ endptr = NULL;
+ ret_flush->timeout = strtod(opt_value, &endptr);
+
+ if ((endptr == opt_value) || (errno != 0) ||
+ (!isfinite(ret_flush->timeout))) {
+ cmd_error(CMD_PARSE_ERROR, err,
+ "Invalid value for option `timeout': %s", opt_value);
+ cmd_destroy_flush(ret_flush);
+ return CMD_PARSE_ERROR;
+ } else if (ret_flush->timeout < 0.0) {
+ ret_flush->timeout = 0.0;
+ }
+ } else {
+ cmd_error(CMD_PARSE_ERROR, err, "Cannot parse option `%s'.", opt_key);
+ cmd_destroy_flush(ret_flush);
+ return CMD_PARSE_ERROR;
+ }
+ }
+
+ return CMD_OK;
+} /* cmd_status_t cmd_parse_flush */
+
+cmd_status_t cmd_handle_flush(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_t cmd;
+
+ int success = 0;
+ int error = 0;
+ int status;
+
+ if ((fh == NULL) || (buffer == NULL))
+ return -1;
+
+ DEBUG("utils_cmd_flush: cmd_handle_flush (fh = %p, buffer = %s);", (void *)fh,
+ buffer);
+
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return status;
+ if (cmd.type != CMD_FLUSH) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ cmd_destroy(&cmd);
+ return CMD_UNKNOWN_COMMAND;
+ }
+
+ for (size_t i = 0; (i == 0) || (i < cmd.cmd.flush.plugins_num); i++) {
+ char *plugin = NULL;
+
+ if (cmd.cmd.flush.plugins_num != 0)
+ plugin = cmd.cmd.flush.plugins[i];
+
+ for (size_t j = 0; (j == 0) || (j < cmd.cmd.flush.identifiers_num); j++) {
+ char *identifier = NULL;
+ char buf[1024];
+
+ if (cmd.cmd.flush.identifiers_num != 0) {
+ identifier_t *id = cmd.cmd.flush.identifiers + j;
+ if (format_name(buf, sizeof(buf), id->host, id->plugin,
+ id->plugin_instance, id->type,
+ id->type_instance) != 0) {
+ error++;
+ continue;
+ }
+ identifier = buf;
+ }
+
+ if (plugin_flush(plugin, DOUBLE_TO_CDTIME_T(cmd.cmd.flush.timeout),
+ identifier) == 0)
+ success++;
+ else
+ error++;
+ }
+ }
+
+ cmd_error(CMD_OK, &err, "Done: %i successful, %i errors", success, error);
+
+ cmd_destroy(&cmd);
+ return 0;
+#undef PRINT_TO_SOCK
+} /* cmd_status_t cmd_handle_flush */
+
+void cmd_destroy_flush(cmd_flush_t *flush) {
+ if (flush == NULL)
+ return;
+
+ strarray_free(flush->plugins, flush->plugins_num);
+ flush->plugins = NULL;
+ flush->plugins_num = 0;
+
+ sfree(flush->identifiers);
+ flush->identifiers_num = 0;
+} /* void cmd_destroy_flush */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_flush.h
+ * Copyright (C) 2008, 2016 Sebastian Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian "tokkee" Harl <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_CMD_FLUSH_H
+#define UTILS_CMD_FLUSH_H 1
+
+#include "utils/cmds/cmds.h"
+
+#include <stdio.h>
+
+cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_flush(FILE *fh, char *buffer);
+
+void cmd_destroy_flush(cmd_flush_t *flush);
+
+#endif /* UTILS_CMD_FLUSH_H */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_getthreshold.c
+ * Copyright (C) 2008,2009 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/avltree/avltree.h"
+#include "utils/cmds/getthreshold.h"
+#include "utils/cmds/parse_option.h" /* for `parse_string' */
+#include "utils_threshold.h"
+
+#define print_to_socket(fh, ...) \
+ if (fprintf(fh, __VA_ARGS__) < 0) { \
+ WARNING("handle_getthreshold: failed to write to socket #%i: %s", \
+ fileno(fh), STRERRNO); \
+ return -1; \
+ }
+
+int handle_getthreshold(FILE *fh, char *buffer) {
+ char *command;
+ char *identifier;
+ char *identifier_copy;
+
+ char *host;
+ char *plugin;
+ char *plugin_instance;
+ char *type;
+ char *type_instance;
+
+ threshold_t threshold;
+
+ int status;
+ size_t i;
+
+ if ((fh == NULL) || (buffer == NULL))
+ return -1;
+
+ DEBUG("utils_cmd_getthreshold: handle_getthreshold (fh = %p, buffer = %s);",
+ (void *)fh, buffer);
+
+ command = NULL;
+ status = parse_string(&buffer, &command);
+ if (status != 0) {
+ print_to_socket(fh, "-1 Cannot parse command.\n");
+ return -1;
+ }
+ assert(command != NULL);
+
+ if (strcasecmp("GETTHRESHOLD", command) != 0) {
+ print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
+ return -1;
+ }
+
+ identifier = NULL;
+ status = parse_string(&buffer, &identifier);
+ if (status != 0) {
+ print_to_socket(fh, "-1 Cannot parse identifier.\n");
+ return -1;
+ }
+ assert(identifier != NULL);
+
+ if (*buffer != 0) {
+ print_to_socket(fh, "-1 Garbage after end of command: %s\n", buffer);
+ return -1;
+ }
+
+ /* parse_identifier() modifies its first argument,
+ * returning pointers into it */
+ identifier_copy = sstrdup(identifier);
+
+ status = parse_identifier(identifier_copy, &host, &plugin, &plugin_instance,
+ &type, &type_instance,
+ /* default_host = */ NULL);
+ if (status != 0) {
+ DEBUG("handle_getthreshold: Cannot parse identifier `%s'.", identifier);
+ print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier);
+ sfree(identifier_copy);
+ return -1;
+ }
+
+ value_list_t vl = {.values = NULL};
+ sstrncpy(vl.host, host, sizeof(vl.host));
+ sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
+ if (plugin_instance != NULL)
+ sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ if (type_instance != NULL)
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+ sfree(identifier_copy);
+
+ status = ut_search_threshold(&vl, &threshold);
+ if (status == ENOENT) {
+ print_to_socket(fh, "-1 No threshold found for identifier %s\n",
+ identifier);
+ return 0;
+ } else if (status != 0) {
+ print_to_socket(fh, "-1 Error while looking up threshold: %i\n", status);
+ return -1;
+ }
+
+ /* Lets count the number of lines we'll return. */
+ i = 0;
+ if (threshold.host[0] != 0)
+ i++;
+ if (threshold.plugin[0] != 0)
+ i++;
+ if (threshold.plugin_instance[0] != 0)
+ i++;
+ if (threshold.type[0] != 0)
+ i++;
+ if (threshold.type_instance[0] != 0)
+ i++;
+ if (threshold.data_source[0] != 0)
+ i++;
+ if (!isnan(threshold.warning_min))
+ i++;
+ if (!isnan(threshold.warning_max))
+ i++;
+ if (!isnan(threshold.failure_min))
+ i++;
+ if (!isnan(threshold.failure_max))
+ i++;
+ if (threshold.hysteresis > 0.0)
+ i++;
+ if (threshold.hits > 1)
+ i++;
+
+ /* Print the response */
+ print_to_socket(fh, "%" PRIsz " Threshold found\n", i);
+
+ if (threshold.host[0] != 0)
+ print_to_socket(fh, "Host: %s\n", threshold.host);
+ if (threshold.plugin[0] != 0)
+ print_to_socket(fh, "Plugin: %s\n", threshold.plugin);
+ if (threshold.plugin_instance[0] != 0)
+ print_to_socket(fh, "Plugin Instance: %s\n", threshold.plugin_instance);
+ if (threshold.type[0] != 0)
+ print_to_socket(fh, "Type: %s\n", threshold.type);
+ if (threshold.type_instance[0] != 0)
+ print_to_socket(fh, "Type Instance: %s\n", threshold.type_instance);
+ if (threshold.data_source[0] != 0)
+ print_to_socket(fh, "Data Source: %s\n", threshold.data_source);
+ if (!isnan(threshold.warning_min))
+ print_to_socket(fh, "Warning Min: %g\n", threshold.warning_min);
+ if (!isnan(threshold.warning_max))
+ print_to_socket(fh, "Warning Max: %g\n", threshold.warning_max);
+ if (!isnan(threshold.failure_min))
+ print_to_socket(fh, "Failure Min: %g\n", threshold.failure_min);
+ if (!isnan(threshold.failure_max))
+ print_to_socket(fh, "Failure Max: %g\n", threshold.failure_max);
+ if (threshold.hysteresis > 0.0)
+ print_to_socket(fh, "Hysteresis: %g\n", threshold.hysteresis);
+ if (threshold.hits > 1)
+ print_to_socket(fh, "Hits: %i\n", threshold.hits);
+
+ return 0;
+} /* int handle_getthreshold */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_getthreshold.h
+ * Copyright (C) 2009 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_GETTHRESHOLD_H
+#define UTILS_CMD_GETTHRESHOLD_H 1
+
+#include <stdio.h>
+
+int handle_getthreshold(FILE *fh, char *buffer);
+
+#endif /* UTILS_CMD_GETTHRESHOLD_H */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_getval.c
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/cmds/getval.h"
+#include "utils/cmds/parse_option.h"
+#include "utils_cache.h"
+
+cmd_status_t cmd_parse_getval(size_t argc, char **argv,
+ cmd_getval_t *ret_getval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
+ char *identifier_copy;
+ int status;
+
+ if ((ret_getval == NULL) || (opts == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_getval.");
+ return CMD_ERROR;
+ }
+
+ if (argc != 1) {
+ if (argc == 0)
+ cmd_error(CMD_PARSE_ERROR, err, "Missing identifier.");
+ else
+ cmd_error(CMD_PARSE_ERROR, err, "Garbage after identifier: `%s'.",
+ argv[1]);
+ return CMD_PARSE_ERROR;
+ }
+
+ /* parse_identifier() modifies its first argument,
+ * returning pointers into it */
+ identifier_copy = sstrdup(argv[0]);
+
+ status = parse_identifier(
+ argv[0], &ret_getval->identifier.host, &ret_getval->identifier.plugin,
+ &ret_getval->identifier.plugin_instance, &ret_getval->identifier.type,
+ &ret_getval->identifier.type_instance, opts->identifier_default_host);
+ if (status != 0) {
+ DEBUG("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy);
+ cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
+ identifier_copy);
+ sfree(identifier_copy);
+ return CMD_PARSE_ERROR;
+ }
+
+ ret_getval->raw_identifier = identifier_copy;
+ return CMD_OK;
+} /* cmd_status_t cmd_parse_getval */
+
+#define print_to_socket(fh, ...) \
+ do { \
+ if (fprintf(fh, __VA_ARGS__) < 0) { \
+ WARNING("cmd_handle_getval: failed to write to socket #%i: %s", \
+ fileno(fh), STRERRNO); \
+ return -1; \
+ } \
+ fflush(fh); \
+ } while (0)
+
+cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_status_t status;
+ cmd_t cmd;
+
+ gauge_t *values;
+ size_t values_num;
+
+ const data_set_t *ds;
+
+ if ((fh == NULL) || (buffer == NULL))
+ return -1;
+
+ DEBUG("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);",
+ (void *)fh, buffer);
+
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return status;
+ if (cmd.type != CMD_GETVAL) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ cmd_destroy(&cmd);
+ return CMD_UNKNOWN_COMMAND;
+ }
+
+ ds = plugin_get_ds(cmd.cmd.getval.identifier.type);
+ if (ds == NULL) {
+ DEBUG("cmd_handle_getval: plugin_get_ds (%s) == NULL;",
+ cmd.cmd.getval.identifier.type);
+ cmd_error(CMD_ERROR, &err, "Type `%s' is unknown.\n",
+ cmd.cmd.getval.identifier.type);
+ cmd_destroy(&cmd);
+ return -1;
+ }
+
+ values = NULL;
+ values_num = 0;
+ status =
+ uc_get_rate_by_name(cmd.cmd.getval.raw_identifier, &values, &values_num);
+ if (status != 0) {
+ cmd_error(CMD_ERROR, &err, "No such value.");
+ cmd_destroy(&cmd);
+ return CMD_ERROR;
+ }
+
+ if (ds->ds_num != values_num) {
+ ERROR("ds[%s]->ds_num = %" PRIsz ", "
+ "but uc_get_rate_by_name returned %" PRIsz " values.",
+ ds->type, ds->ds_num, values_num);
+ cmd_error(CMD_ERROR, &err, "Error reading value from cache.");
+ sfree(values);
+ cmd_destroy(&cmd);
+ return CMD_ERROR;
+ }
+
+ print_to_socket(fh, "%" PRIsz " Value%s found\n", values_num,
+ (values_num == 1) ? "" : "s");
+ for (size_t i = 0; i < values_num; i++) {
+ print_to_socket(fh, "%s=", ds->ds[i].name);
+ if (isnan(values[i])) {
+ print_to_socket(fh, "NaN\n");
+ } else {
+ print_to_socket(fh, "%12e\n", values[i]);
+ }
+ }
+
+ sfree(values);
+ cmd_destroy(&cmd);
+
+ return CMD_OK;
+} /* cmd_status_t cmd_handle_getval */
+
+void cmd_destroy_getval(cmd_getval_t *getval) {
+ if (getval == NULL)
+ return;
+
+ sfree(getval->raw_identifier);
+} /* void cmd_destroy_getval */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_getval.h
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_GETVAL_H
+#define UTILS_CMD_GETVAL_H 1
+
+#include <stdio.h>
+
+#include "utils/cmds/cmds.h"
+
+cmd_status_t cmd_parse_getval(size_t argc, char **argv,
+ cmd_getval_t *ret_getval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_getval(FILE *fh, char *buffer);
+
+void cmd_destroy_getval(cmd_getval_t *getval);
+
+#endif /* UTILS_CMD_GETVAL_H */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_listval.c
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/cmds/listval.h"
+#include "utils/cmds/parse_option.h"
+#include "utils_cache.h"
+
+cmd_status_t cmd_parse_listval(size_t argc, char **argv,
+ const cmd_options_t *opts
+ __attribute__((unused)),
+ cmd_error_handler_t *err) {
+ if (argc != 0) {
+ cmd_error(CMD_PARSE_ERROR, err, "Garbage after end of command: `%s'.",
+ argv[0]);
+ return CMD_PARSE_ERROR;
+ }
+
+ return CMD_OK;
+} /* cmd_status_t cmd_parse_listval */
+
+#define free_everything_and_return(status) \
+ do { \
+ for (size_t j = 0; j < number; j++) { \
+ sfree(names[j]); \
+ names[j] = NULL; \
+ } \
+ sfree(names); \
+ sfree(times); \
+ return status; \
+ } while (0)
+
+#define print_to_socket(fh, ...) \
+ do { \
+ if (fprintf(fh, __VA_ARGS__) < 0) { \
+ WARNING("handle_listval: failed to write to socket #%i: %s", fileno(fh), \
+ STRERRNO); \
+ free_everything_and_return(CMD_ERROR); \
+ } \
+ fflush(fh); \
+ } while (0)
+
+cmd_status_t cmd_handle_listval(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_status_t status;
+ cmd_t cmd;
+
+ char **names = NULL;
+ cdtime_t *times = NULL;
+ size_t number = 0;
+
+ DEBUG("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);", (void *)fh,
+ buffer);
+
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return status;
+ if (cmd.type != CMD_LISTVAL) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ free_everything_and_return(CMD_UNKNOWN_COMMAND);
+ }
+
+ status = uc_get_names(&names, ×, &number);
+ if (status != 0) {
+ DEBUG("command listval: uc_get_names failed with status %i", status);
+ cmd_error(CMD_ERROR, &err, "uc_get_names failed.");
+ free_everything_and_return(CMD_ERROR);
+ }
+
+ print_to_socket(fh, "%i Value%s found\n", (int)number,
+ (number == 1) ? "" : "s");
+ for (size_t i = 0; i < number; i++)
+ print_to_socket(fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE(times[i]), names[i]);
+
+ free_everything_and_return(CMD_OK);
+} /* cmd_status_t cmd_handle_listval */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_listval.h
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_LISTVAL_H
+#define UTILS_CMD_LISTVAL_H 1
+
+#include <stdio.h>
+
+#include "utils/cmds/cmds.h"
+
+cmd_status_t cmd_parse_listval(size_t argc, char **argv,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_listval(FILE *fh, char *buffer);
+
+#endif /* UTILS_CMD_LISTVAL_H */
--- /dev/null
+/**
+ * collectd - src/utils_parse_option.c
+ * Copyright (C) 2008 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/cmds/parse_option.h"
+
+int parse_string(char **ret_buffer, char **ret_string) {
+ char *buffer;
+ char *string;
+
+ buffer = *ret_buffer;
+
+ /* Eat up leading spaces. */
+ string = buffer;
+ while (isspace((int)*string))
+ string++;
+ if (*string == 0)
+ return 1;
+
+ /* A quoted string */
+ if (*string == '"') {
+ char *dst;
+
+ string++;
+ if (*string == 0)
+ return 1;
+
+ dst = string;
+ buffer = string;
+ while ((*buffer != '"') && (*buffer != 0)) {
+ /* Un-escape backslashes */
+ if (*buffer == '\\') {
+ buffer++;
+ /* Catch a backslash at the end of buffer */
+ if (*buffer == 0)
+ return -1;
+ }
+ *dst = *buffer;
+ buffer++;
+ dst++;
+ }
+ /* No quote sign has been found */
+ if (*buffer == 0)
+ return -1;
+
+ *dst = 0;
+ dst++;
+ *buffer = 0;
+ buffer++;
+
+ /* Check for trailing spaces. */
+ if ((*buffer != 0) && !isspace((int)*buffer))
+ return -1;
+ } else /* an unquoted string */
+ {
+ buffer = string;
+ while ((*buffer != 0) && !isspace((int)*buffer))
+ buffer++;
+ if (*buffer != 0) {
+ *buffer = 0;
+ buffer++;
+ }
+ }
+
+ /* Eat up trailing spaces */
+ while (isspace((int)*buffer))
+ buffer++;
+
+ *ret_buffer = buffer;
+ *ret_string = string;
+
+ return 0;
+} /* int parse_string */
+
+/*
+ * parse_option
+ * ------------
+ * Parses an ``option'' as used with the unixsock and exec commands. An
+ * option is of the form:
+ * name0="value"
+ * name1="value with \"quotes\""
+ * name2="value \\ backslash"
+ * However, if the value does *not* contain a space character, you can skip
+ * the quotes.
+ */
+int parse_option(char **ret_buffer, char **ret_key, char **ret_value) {
+ char *buffer;
+ char *key;
+ char *value;
+ int status;
+
+ buffer = *ret_buffer;
+
+ /* Eat up leading spaces */
+ key = buffer;
+ while (isspace((int)*key))
+ key++;
+ if (*key == 0)
+ return 1;
+
+ /* Look for the equal sign */
+ buffer = key;
+ while (isalnum((int)*buffer) || *buffer == '_' || *buffer == ':')
+ buffer++;
+ if ((*buffer != '=') || (buffer == key))
+ return 1;
+ *buffer = 0;
+ buffer++;
+ /* Empty values must be written as "" */
+ if (isspace((int)*buffer) || (*buffer == 0))
+ return -1;
+
+ status = parse_string(&buffer, &value);
+ if (status != 0)
+ return -1;
+
+ /* NB: parse_string will have eaten up all trailing spaces. */
+
+ *ret_buffer = buffer;
+ *ret_key = key;
+ *ret_value = value;
+
+ return 0;
+} /* int parse_option */
--- /dev/null
+/**
+ * collectd - src/utils_parse_option.h
+ * Copyright (C) 2008 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_PARSE_OPTION
+#define UTILS_PARSE_OPTION 1
+
+int parse_string(char **ret_buffer, char **ret_string);
+int parse_option(char **ret_buffer, char **ret_key, char **ret_value);
+
+#endif /* UTILS_PARSE_OPTION */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_putnotif.c
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/cmds/parse_option.h"
+#include "utils/cmds/putnotif.h"
+
+#define print_to_socket(fh, ...) \
+ do { \
+ if (fprintf(fh, __VA_ARGS__) < 0) { \
+ WARNING("handle_putnotif: failed to write to socket #%i: %s", \
+ fileno(fh), STRERRNO); \
+ return -1; \
+ } \
+ fflush(fh); \
+ } while (0)
+
+static int set_option_severity(notification_t *n, const char *value) {
+ if (strcasecmp(value, "Failure") == 0)
+ n->severity = NOTIF_FAILURE;
+ else if (strcasecmp(value, "Warning") == 0)
+ n->severity = NOTIF_WARNING;
+ else if (strcasecmp(value, "Okay") == 0)
+ n->severity = NOTIF_OKAY;
+ else
+ return -1;
+
+ return 0;
+} /* int set_option_severity */
+
+static int set_option_time(notification_t *n, const char *value) {
+ char *endptr = NULL;
+ double tmp;
+
+ errno = 0;
+ tmp = strtod(value, &endptr);
+ if ((errno != 0) /* Overflow */
+ || (endptr == value) /* Invalid string */
+ || (endptr == NULL) /* This should not happen */
+ || (*endptr != 0)) /* Trailing chars */
+ return -1;
+
+ n->time = DOUBLE_TO_CDTIME_T(tmp);
+
+ return 0;
+} /* int set_option_time */
+
+static int set_option(notification_t *n, const char *option,
+ const char *value) {
+ if ((n == NULL) || (option == NULL) || (value == NULL))
+ return -1;
+
+ DEBUG("utils_cmd_putnotif: set_option (option = %s, value = %s);", option,
+ value);
+
+ /* Add a meta option in the form: <type>:<key> */
+ if (option[0] != '\0' && option[1] == ':') {
+ /* Refuse empty key */
+ if (option[2] == '\0')
+ return 1;
+
+ if (option[0] == 's')
+ return plugin_notification_meta_add_string(n, option + 2, value);
+ else
+ return 1;
+ }
+
+ if (strcasecmp("severity", option) == 0)
+ return set_option_severity(n, value);
+ else if (strcasecmp("time", option) == 0)
+ return set_option_time(n, value);
+ else if (strcasecmp("message", option) == 0)
+ sstrncpy(n->message, value, sizeof(n->message));
+ else if (strcasecmp("host", option) == 0)
+ sstrncpy(n->host, value, sizeof(n->host));
+ else if (strcasecmp("plugin", option) == 0)
+ sstrncpy(n->plugin, value, sizeof(n->plugin));
+ else if (strcasecmp("plugin_instance", option) == 0)
+ sstrncpy(n->plugin_instance, value, sizeof(n->plugin_instance));
+ else if (strcasecmp("type", option) == 0)
+ sstrncpy(n->type, value, sizeof(n->type));
+ else if (strcasecmp("type_instance", option) == 0)
+ sstrncpy(n->type_instance, value, sizeof(n->type_instance));
+ else
+ return 1;
+
+ return 0;
+} /* int set_option */
+
+int handle_putnotif(FILE *fh, char *buffer) {
+ char *command;
+ notification_t n = {0};
+ int status;
+
+ if ((fh == NULL) || (buffer == NULL))
+ return -1;
+
+ DEBUG("utils_cmd_putnotif: handle_putnotif (fh = %p, buffer = %s);",
+ (void *)fh, buffer);
+
+ command = NULL;
+ status = parse_string(&buffer, &command);
+ if (status != 0) {
+ print_to_socket(fh, "-1 Cannot parse command.\n");
+ return -1;
+ }
+ assert(command != NULL);
+
+ if (strcasecmp("PUTNOTIF", command) != 0) {
+ print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
+ return -1;
+ }
+
+ status = 0;
+ while (*buffer != 0) {
+ char *key;
+ char *value;
+
+ status = parse_option(&buffer, &key, &value);
+ if (status != 0) {
+ print_to_socket(fh, "-1 Malformed option.\n");
+ break;
+ }
+
+ status = set_option(&n, key, value);
+ if (status != 0) {
+ print_to_socket(fh, "-1 Error parsing option `%s'\n", key);
+ break;
+ }
+ } /* for (i) */
+
+ /* Check for required fields and complain if anything is missing. */
+ if ((status == 0) && (n.severity == 0)) {
+ print_to_socket(fh, "-1 Option `severity' missing.\n");
+ status = -1;
+ }
+ if ((status == 0) && (n.time == 0)) {
+ print_to_socket(fh, "-1 Option `time' missing.\n");
+ status = -1;
+ }
+ if ((status == 0) && (strlen(n.message) == 0)) {
+ print_to_socket(fh, "-1 No message or message of length 0 given.\n");
+ status = -1;
+ }
+
+ /* If status is still zero the notification is fine and we can finally
+ * dispatch it. */
+ if (status == 0) {
+ plugin_dispatch_notification(&n);
+ print_to_socket(fh, "0 Success\n");
+ }
+
+ return 0;
+} /* int handle_putnotif */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_putnotif.h
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_PUTNOTIF_H
+#define UTILS_CMD_PUTNOTIF_H 1
+
+#include <stdio.h>
+
+int handle_putnotif(FILE *fh, char *buffer);
+
+#endif /* UTILS_CMD_PUTNOTIF_H */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_putval.c
+ * Copyright (C) 2007-2009 Florian octo Forster
+ * Copyright (C) 2016 Sebastian tokkee Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/cmds/putval.h"
+#include "utils/common/common.h"
+
+/*
+ * private helper functions
+ */
+
+static int set_option(value_list_t *vl, const char *key, const char *value) {
+ if ((vl == NULL) || (key == NULL) || (value == NULL))
+ return -1;
+
+ if (strcasecmp("interval", key) == 0) {
+ double tmp;
+ char *endptr;
+
+ endptr = NULL;
+ errno = 0;
+ tmp = strtod(value, &endptr);
+
+ if ((errno == 0) && (endptr != NULL) && (endptr != value) && (tmp > 0.0))
+ vl->interval = DOUBLE_TO_CDTIME_T(tmp);
+ } else
+ return 1;
+
+ return 0;
+} /* int set_option */
+
+/*
+ * public API
+ */
+
+cmd_status_t cmd_parse_putval(size_t argc, char **argv,
+ cmd_putval_t *ret_putval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
+ cmd_status_t result;
+
+ char *identifier;
+ char *hostname;
+ char *plugin;
+ char *plugin_instance;
+ char *type;
+ char *type_instance;
+ int status;
+
+ char *identifier_copy;
+
+ const data_set_t *ds;
+ value_list_t vl = VALUE_LIST_INIT;
+
+ if ((ret_putval == NULL) || (opts == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_putval.");
+ return CMD_ERROR;
+ }
+
+ if (argc < 2) {
+ cmd_error(CMD_PARSE_ERROR, err, "Missing identifier and/or value-list.");
+ return CMD_PARSE_ERROR;
+ }
+
+ identifier = argv[0];
+
+ /* parse_identifier() modifies its first argument, returning pointers into
+ * it; retain the old value for later. */
+ identifier_copy = sstrdup(identifier);
+
+ status =
+ parse_identifier(identifier, &hostname, &plugin, &plugin_instance, &type,
+ &type_instance, opts->identifier_default_host);
+ if (status != 0) {
+ DEBUG("cmd_handle_putval: Cannot parse identifier `%s'.", identifier_copy);
+ cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
+ identifier_copy);
+ sfree(identifier_copy);
+ return CMD_PARSE_ERROR;
+ }
+
+ if ((strlen(hostname) >= sizeof(vl.host)) ||
+ (strlen(plugin) >= sizeof(vl.plugin)) ||
+ ((plugin_instance != NULL) &&
+ (strlen(plugin_instance) >= sizeof(vl.plugin_instance))) ||
+ ((type_instance != NULL) &&
+ (strlen(type_instance) >= sizeof(vl.type_instance)))) {
+ cmd_error(CMD_PARSE_ERROR, err, "Identifier too long.");
+ sfree(identifier_copy);
+ return CMD_PARSE_ERROR;
+ }
+
+ sstrncpy(vl.host, hostname, sizeof(vl.host));
+ sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ if (plugin_instance != NULL)
+ sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+ if (type_instance != NULL)
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+
+ ds = plugin_get_ds(type);
+ if (ds == NULL) {
+ cmd_error(CMD_PARSE_ERROR, err, "1 Type `%s' isn't defined.", type);
+ sfree(identifier_copy);
+ return CMD_PARSE_ERROR;
+ }
+
+ hostname = NULL;
+ plugin = NULL;
+ plugin_instance = NULL;
+ type = NULL;
+ type_instance = NULL;
+
+ ret_putval->raw_identifier = identifier_copy;
+ if (ret_putval->raw_identifier == NULL) {
+ cmd_error(CMD_ERROR, err, "malloc failed.");
+ cmd_destroy_putval(ret_putval);
+ sfree(vl.values);
+ return CMD_ERROR;
+ }
+
+ /* All the remaining fields are part of the option list. */
+ result = CMD_OK;
+ for (size_t i = 1; i < argc; ++i) {
+ value_list_t *tmp;
+
+ char *key = NULL;
+ char *value = NULL;
+
+ status = cmd_parse_option(argv[i], &key, &value, err);
+ if (status == CMD_OK) {
+ assert(key != NULL);
+ assert(value != NULL);
+ set_option(&vl, key, value);
+ continue;
+ } else if (status != CMD_NO_OPTION) {
+ /* parse_option failed, buffer has been modified.
+ * => we need to abort */
+ result = status;
+ break;
+ }
+ /* else: cmd_parse_option did not find an option; treat this as a
+ * value list. */
+
+ vl.values_len = ds->ds_num;
+ vl.values = calloc(vl.values_len, sizeof(*vl.values));
+ if (vl.values == NULL) {
+ cmd_error(CMD_ERROR, err, "malloc failed.");
+ result = CMD_ERROR;
+ break;
+ }
+
+ status = parse_values(argv[i], &vl, ds);
+ if (status != 0) {
+ cmd_error(CMD_PARSE_ERROR, err, "Parsing the values string failed.");
+ result = CMD_PARSE_ERROR;
+ vl.values_len = 0;
+ sfree(vl.values);
+ break;
+ }
+
+ tmp = realloc(ret_putval->vl,
+ (ret_putval->vl_num + 1) * sizeof(*ret_putval->vl));
+ if (tmp == NULL) {
+ cmd_error(CMD_ERROR, err, "realloc failed.");
+ cmd_destroy_putval(ret_putval);
+ result = CMD_ERROR;
+ vl.values_len = 0;
+ sfree(vl.values);
+ break;
+ }
+
+ ret_putval->vl = tmp;
+ ret_putval->vl_num++;
+ memcpy(&ret_putval->vl[ret_putval->vl_num - 1], &vl, sizeof(vl));
+
+ /* pointer is now owned by ret_putval->vl[] */
+ vl.values_len = 0;
+ vl.values = NULL;
+ } /* while (*buffer != 0) */
+ /* Done parsing the options. */
+
+ if (result != CMD_OK)
+ cmd_destroy_putval(ret_putval);
+
+ return result;
+} /* cmd_status_t cmd_parse_putval */
+
+void cmd_destroy_putval(cmd_putval_t *putval) {
+ if (putval == NULL)
+ return;
+
+ sfree(putval->raw_identifier);
+
+ for (size_t i = 0; i < putval->vl_num; ++i) {
+ sfree(putval->vl[i].values);
+ meta_data_destroy(putval->vl[i].meta);
+ putval->vl[i].meta = NULL;
+ }
+ sfree(putval->vl);
+ putval->vl = NULL;
+ putval->vl_num = 0;
+} /* void cmd_destroy_putval */
+
+cmd_status_t cmd_handle_putval(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_t cmd;
+
+ int status;
+
+ DEBUG("utils_cmd_putval: cmd_handle_putval (fh = %p, buffer = %s);",
+ (void *)fh, buffer);
+
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return status;
+ if (cmd.type != CMD_PUTVAL) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ cmd_destroy(&cmd);
+ return CMD_UNKNOWN_COMMAND;
+ }
+
+ for (size_t i = 0; i < cmd.cmd.putval.vl_num; ++i)
+ plugin_dispatch_values(&cmd.cmd.putval.vl[i]);
+
+ if (fh != stdout)
+ cmd_error(CMD_OK, &err, "Success: %i %s been dispatched.",
+ (int)cmd.cmd.putval.vl_num,
+ (cmd.cmd.putval.vl_num == 1) ? "value has" : "values have");
+
+ cmd_destroy(&cmd);
+ return CMD_OK;
+} /* int cmd_handle_putval */
+
+int cmd_create_putval(char *ret, size_t ret_len, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl) {
+ char buffer_ident[6 * DATA_MAX_NAME_LEN];
+ char buffer_values[1024];
+ int status;
+
+ status = FORMAT_VL(buffer_ident, sizeof(buffer_ident), vl);
+ if (status != 0)
+ return status;
+ escape_string(buffer_ident, sizeof(buffer_ident));
+
+ status = format_values(buffer_values, sizeof(buffer_values), ds, vl,
+ /* store rates = */ false);
+ if (status != 0)
+ return status;
+ escape_string(buffer_values, sizeof(buffer_values));
+
+ snprintf(ret, ret_len, "PUTVAL %s interval=%.3f %s", buffer_ident,
+ (vl->interval > 0) ? CDTIME_T_TO_DOUBLE(vl->interval)
+ : CDTIME_T_TO_DOUBLE(plugin_get_interval()),
+ buffer_values);
+
+ return 0;
+} /* }}} int cmd_create_putval */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_putval.h
+ * Copyright (C) 2007 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_PUTVAL_H
+#define UTILS_CMD_PUTVAL_H 1
+
+#include "plugin.h"
+#include "utils/cmds/cmds.h"
+
+#include <stdio.h>
+
+cmd_status_t cmd_parse_putval(size_t argc, char **argv,
+ cmd_putval_t *ret_putval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_putval(FILE *fh, char *buffer);
+
+void cmd_destroy_putval(cmd_putval_t *putval);
+
+int cmd_create_putval(char *ret, size_t ret_len, const data_set_t *ds,
+ const value_list_t *vl);
+
+#endif /* UTILS_CMD_PUTVAL_H */
--- /dev/null
+/**
+ * collectd - src/common.c
+ * Copyright (C) 2005-2014 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Niki W. Waibel <niki.waibel@gmx.net>
+ * Sebastian Harl <sh at tokkee.org>
+ * Michał Mirosław <mirq-linux at rere.qmqm.pl>
+**/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils_cache.h"
+
+/* for getaddrinfo */
+#include <netdb.h>
+#include <sys/types.h>
+
+#include <poll.h>
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_CAPABILITY
+#include <sys/capability.h>
+#endif
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
+
+#ifdef HAVE_LIBKSTAT
+extern kstat_ctl_t *kc;
+#endif
+
+#if !defined(MSG_DONTWAIT)
+#if defined(MSG_NONBLOCK)
+/* AIX doesn't have MSG_DONTWAIT */
+#define MSG_DONTWAIT MSG_NONBLOCK
+#else
+/* Windows doesn't have MSG_DONTWAIT or MSG_NONBLOCK */
+#define MSG_DONTWAIT 0
+#endif /* defined(MSG_NONBLOCK) */
+#endif /* !defined(MSG_DONTWAIT) */
+
+#if !HAVE_GETPWNAM_R && defined(HAVE_GETPWNAM)
+static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#if !HAVE_STRERROR_R
+static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+char *sstrncpy(char *dest, const char *src, size_t n) {
+ strncpy(dest, src, n);
+ dest[n - 1] = '\0';
+
+ return dest;
+} /* char *sstrncpy */
+
+char *ssnprintf_alloc(char const *format, ...) /* {{{ */
+{
+ char static_buffer[1024] = "";
+ char *alloc_buffer;
+ size_t alloc_buffer_size;
+ int status;
+ va_list ap;
+
+ /* Try printing into the static buffer. In many cases it will be
+ * sufficiently large and we can simply return a strdup() of this
+ * buffer. */
+ va_start(ap, format);
+ status = vsnprintf(static_buffer, sizeof(static_buffer), format, ap);
+ va_end(ap);
+ if (status < 0)
+ return NULL;
+
+ /* "status" does not include the null byte. */
+ alloc_buffer_size = (size_t)(status + 1);
+ if (alloc_buffer_size <= sizeof(static_buffer))
+ return strdup(static_buffer);
+
+ /* Allocate a buffer large enough to hold the string. */
+ alloc_buffer = calloc(1, alloc_buffer_size);
+ if (alloc_buffer == NULL)
+ return NULL;
+
+ /* Print again into this new buffer. */
+ va_start(ap, format);
+ status = vsnprintf(alloc_buffer, alloc_buffer_size, format, ap);
+ va_end(ap);
+ if (status < 0) {
+ sfree(alloc_buffer);
+ return NULL;
+ }
+
+ return alloc_buffer;
+} /* }}} char *ssnprintf_alloc */
+
+char *sstrdup(const char *s) {
+ char *r;
+ size_t sz;
+
+ if (s == NULL)
+ return NULL;
+
+ /* Do not use `strdup' here, because it's not specified in POSIX. It's
+ * ``only'' an XSI extension. */
+ sz = strlen(s) + 1;
+ r = malloc(sz);
+ if (r == NULL) {
+ ERROR("sstrdup: Out of memory.");
+ exit(3);
+ }
+ memcpy(r, s, sz);
+
+ return r;
+} /* char *sstrdup */
+
+/* Even though Posix requires "strerror_r" to return an "int",
+ * some systems (e.g. the GNU libc) return a "char *" _and_
+ * ignore the second argument ... -tokkee */
+char *sstrerror(int errnum, char *buf, size_t buflen) {
+ buf[0] = '\0';
+
+#if !HAVE_STRERROR_R
+ {
+ char *temp;
+
+ pthread_mutex_lock(&strerror_r_lock);
+
+ temp = strerror(errnum);
+ sstrncpy(buf, temp, buflen);
+
+ pthread_mutex_unlock(&strerror_r_lock);
+ }
+/* #endif !HAVE_STRERROR_R */
+
+#elif STRERROR_R_CHAR_P
+ {
+ char *temp;
+ temp = strerror_r(errnum, buf, buflen);
+ if (buf[0] == '\0') {
+ if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
+ sstrncpy(buf, temp, buflen);
+ else
+ sstrncpy(buf, "strerror_r did not return "
+ "an error message",
+ buflen);
+ }
+ }
+/* #endif STRERROR_R_CHAR_P */
+
+#else
+ if (strerror_r(errnum, buf, buflen) != 0) {
+ snprintf(buf, buflen, "Error #%i; "
+ "Additionally, strerror_r failed.",
+ errnum);
+ }
+#endif /* STRERROR_R_CHAR_P */
+
+ return buf;
+} /* char *sstrerror */
+
+void *smalloc(size_t size) {
+ void *r;
+
+ if ((r = malloc(size)) == NULL) {
+ ERROR("Not enough memory.");
+ exit(3);
+ }
+
+ return r;
+} /* void *smalloc */
+
+#if 0
+void sfree (void **ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ if (*ptr != NULL)
+ free (*ptr);
+
+ *ptr = NULL;
+}
+#endif
+
+int sread(int fd, void *buf, size_t count) {
+ char *ptr;
+ size_t nleft;
+ ssize_t status;
+
+ ptr = (char *)buf;
+ nleft = count;
+
+ while (nleft > 0) {
+ status = read(fd, (void *)ptr, nleft);
+
+ if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+ continue;
+
+ if (status < 0)
+ return status;
+
+ if (status == 0) {
+ DEBUG("Received EOF from fd %i. ", fd);
+ return -1;
+ }
+
+ assert((0 > status) || (nleft >= (size_t)status));
+
+ nleft = nleft - ((size_t)status);
+ ptr = ptr + ((size_t)status);
+ }
+
+ return 0;
+}
+
+int swrite(int fd, const void *buf, size_t count) {
+ const char *ptr;
+ size_t nleft;
+ ssize_t status;
+ struct pollfd pfd;
+
+ ptr = (const char *)buf;
+ nleft = count;
+
+ if (fd < 0) {
+ errno = EINVAL;
+ return errno;
+ }
+
+ /* checking for closed peer connection */
+ pfd.fd = fd;
+ pfd.events = POLLIN | POLLHUP;
+ pfd.revents = 0;
+ if (poll(&pfd, 1, 0) > 0) {
+ char buffer[32];
+ if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
+ /* if recv returns zero (even though poll() said there is data to be
+ * read), that means the connection has been closed */
+ errno = ECONNRESET;
+ return -1;
+ }
+ }
+
+ while (nleft > 0) {
+ status = write(fd, (const void *)ptr, nleft);
+
+ if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+ continue;
+
+ if (status < 0)
+ return errno ? errno : status;
+
+ nleft = nleft - ((size_t)status);
+ ptr = ptr + ((size_t)status);
+ }
+
+ return 0;
+}
+
+int strsplit(char *string, char **fields, size_t size) {
+ size_t i;
+ char *ptr;
+ char *saveptr;
+
+ i = 0;
+ ptr = string;
+ saveptr = NULL;
+ while ((fields[i] = strtok_r(ptr, " \t\r\n", &saveptr)) != NULL) {
+ ptr = NULL;
+ i++;
+
+ if (i >= size)
+ break;
+ }
+
+ return (int)i;
+}
+
+int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num,
+ const char *sep) {
+ size_t avail = 0;
+ char *ptr = buffer;
+ size_t sep_len = 0;
+
+ size_t buffer_req = 0;
+
+ if (((fields_num != 0) && (fields == NULL)) ||
+ ((buffer_size != 0) && (buffer == NULL)))
+ return -EINVAL;
+
+ if (buffer != NULL)
+ buffer[0] = 0;
+
+ if (buffer_size != 0)
+ avail = buffer_size - 1;
+
+ if (sep != NULL)
+ sep_len = strlen(sep);
+
+ for (size_t i = 0; i < fields_num; i++) {
+ size_t field_len = strlen(fields[i]);
+
+ if (i != 0)
+ buffer_req += sep_len;
+ buffer_req += field_len;
+
+ if (buffer_size == 0)
+ continue;
+
+ if ((i != 0) && (sep_len > 0)) {
+ if (sep_len >= avail) {
+ /* prevent subsequent iterations from writing to the
+ * buffer. */
+ avail = 0;
+ continue;
+ }
+
+ memcpy(ptr, sep, sep_len);
+
+ ptr += sep_len;
+ avail -= sep_len;
+ }
+
+ if (field_len > avail)
+ field_len = avail;
+
+ memcpy(ptr, fields[i], field_len);
+ ptr += field_len;
+
+ avail -= field_len;
+ if (ptr != NULL)
+ *ptr = 0;
+ }
+
+ return (int)buffer_req;
+}
+
+int escape_string(char *buffer, size_t buffer_size) {
+ char *temp;
+ size_t j;
+
+ /* Check if we need to escape at all first */
+ temp = strpbrk(buffer, " \t\"\\");
+ if (temp == NULL)
+ return 0;
+
+ if (buffer_size < 3)
+ return EINVAL;
+
+ temp = calloc(1, buffer_size);
+ if (temp == NULL)
+ return ENOMEM;
+
+ temp[0] = '"';
+ j = 1;
+
+ for (size_t i = 0; i < buffer_size; i++) {
+ if (buffer[i] == 0) {
+ break;
+ } else if ((buffer[i] == '"') || (buffer[i] == '\\')) {
+ if (j > (buffer_size - 4))
+ break;
+ temp[j] = '\\';
+ temp[j + 1] = buffer[i];
+ j += 2;
+ } else {
+ if (j > (buffer_size - 3))
+ break;
+ temp[j] = buffer[i];
+ j++;
+ }
+ }
+
+ assert((j + 1) < buffer_size);
+ temp[j] = '"';
+ temp[j + 1] = 0;
+
+ sstrncpy(buffer, temp, buffer_size);
+ sfree(temp);
+ return 0;
+} /* int escape_string */
+
+int strunescape(char *buf, size_t buf_len) {
+ for (size_t i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) {
+ if (buf[i] != '\\')
+ continue;
+
+ if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
+ P_ERROR("string unescape: backslash found at end of string.");
+ /* Ensure null-byte at the end of the buffer. */
+ buf[i] = 0;
+ return -1;
+ }
+
+ switch (buf[i + 1]) {
+ case 't':
+ buf[i] = '\t';
+ break;
+ case 'n':
+ buf[i] = '\n';
+ break;
+ case 'r':
+ buf[i] = '\r';
+ break;
+ default:
+ buf[i] = buf[i + 1];
+ break;
+ }
+
+ /* Move everything after the position one position to the left.
+ * Add a null-byte as last character in the buffer. */
+ memmove(buf + i + 1, buf + i + 2, buf_len - i - 2);
+ buf[buf_len - 1] = '\0';
+ }
+ return 0;
+} /* int strunescape */
+
+size_t strstripnewline(char *buffer) {
+ size_t buffer_len = strlen(buffer);
+
+ while (buffer_len > 0) {
+ if ((buffer[buffer_len - 1] != '\n') && (buffer[buffer_len - 1] != '\r'))
+ break;
+ buffer_len--;
+ buffer[buffer_len] = 0;
+ }
+
+ return buffer_len;
+} /* size_t strstripnewline */
+
+int escape_slashes(char *buffer, size_t buffer_size) {
+ size_t buffer_len;
+
+ buffer_len = strlen(buffer);
+
+ if (buffer_len <= 1) {
+ if (strcmp("/", buffer) == 0) {
+ if (buffer_size < 5)
+ return -1;
+ sstrncpy(buffer, "root", buffer_size);
+ }
+ return 0;
+ }
+
+ /* Move one to the left */
+ if (buffer[0] == '/') {
+ memmove(buffer, buffer + 1, buffer_len);
+ buffer_len--;
+ }
+
+ for (size_t i = 0; i < buffer_len; i++) {
+ if (buffer[i] == '/')
+ buffer[i] = '_';
+ }
+
+ return 0;
+} /* int escape_slashes */
+
+void replace_special(char *buffer, size_t buffer_size) {
+ for (size_t i = 0; i < buffer_size; i++) {
+ if (buffer[i] == 0)
+ return;
+ if ((!isalnum((int)buffer[i])) && (buffer[i] != '-'))
+ buffer[i] = '_';
+ }
+} /* void replace_special */
+
+int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta) {
+ struct timeval *larger;
+ struct timeval *smaller;
+
+ int status;
+
+ NORMALIZE_TIMEVAL(tv0);
+ NORMALIZE_TIMEVAL(tv1);
+
+ if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec)) {
+ if (delta != NULL) {
+ delta->tv_sec = 0;
+ delta->tv_usec = 0;
+ }
+ return 0;
+ }
+
+ if ((tv0.tv_sec < tv1.tv_sec) ||
+ ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec))) {
+ larger = &tv1;
+ smaller = &tv0;
+ status = -1;
+ } else {
+ larger = &tv0;
+ smaller = &tv1;
+ status = 1;
+ }
+
+ if (delta != NULL) {
+ delta->tv_sec = larger->tv_sec - smaller->tv_sec;
+
+ if (smaller->tv_usec <= larger->tv_usec)
+ delta->tv_usec = larger->tv_usec - smaller->tv_usec;
+ else {
+ --delta->tv_sec;
+ delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec;
+ }
+ }
+
+ assert((delta == NULL) ||
+ ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000)));
+
+ return status;
+} /* int timeval_cmp */
+
+int check_create_dir(const char *file_orig) {
+ struct stat statbuf;
+
+ char file_copy[PATH_MAX];
+ char dir[PATH_MAX];
+ char *fields[16];
+ int fields_num;
+ char *ptr;
+ char *saveptr;
+ int last_is_file = 1;
+ int path_is_absolute = 0;
+ size_t len;
+
+ /*
+ * Sanity checks first
+ */
+ if (file_orig == NULL)
+ return -1;
+
+ if ((len = strlen(file_orig)) < 1)
+ return -1;
+ else if (len >= sizeof(file_copy)) {
+ ERROR("check_create_dir: name (%s) is too long.", file_orig);
+ return -1;
+ }
+
+ /*
+ * If `file_orig' ends in a slash the last component is a directory,
+ * otherwise it's a file. Act accordingly..
+ */
+ if (file_orig[len - 1] == '/')
+ last_is_file = 0;
+ if (file_orig[0] == '/')
+ path_is_absolute = 1;
+
+ /*
+ * Create a copy for `strtok_r' to destroy
+ */
+ sstrncpy(file_copy, file_orig, sizeof(file_copy));
+
+ /*
+ * Break into components. This will eat up several slashes in a row and
+ * remove leading and trailing slashes..
+ */
+ ptr = file_copy;
+ saveptr = NULL;
+ fields_num = 0;
+ while ((fields[fields_num] = strtok_r(ptr, "/", &saveptr)) != NULL) {
+ ptr = NULL;
+ fields_num++;
+
+ if (fields_num >= 16)
+ break;
+ }
+
+ /*
+ * For each component, do..
+ */
+ for (int i = 0; i < (fields_num - last_is_file); i++) {
+ /*
+ * Do not create directories that start with a dot. This
+ * prevents `../../' attacks and other likely malicious
+ * behavior.
+ */
+ if (fields[i][0] == '.') {
+ P_ERROR("Cowardly refusing to create a directory that "
+ "begins with a `.' (dot): `%s'",
+ file_orig);
+ return -2;
+ }
+
+ /*
+ * Join the components together again
+ */
+ dir[0] = '/';
+ if (strjoin(dir + path_is_absolute,
+ (size_t)(sizeof(dir) - path_is_absolute), fields,
+ (size_t)(i + 1), "/") < 0) {
+ P_ERROR("strjoin failed: `%s', component #%i", file_orig, i);
+ return -1;
+ }
+
+ while (42) {
+ if ((stat(dir, &statbuf) == -1) && (lstat(dir, &statbuf) == -1)) {
+ if (errno == ENOENT) {
+ if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+ break;
+
+ /* this might happen, if a different thread created
+ * the directory in the meantime
+ * => call stat() again to check for S_ISDIR() */
+ if (EEXIST == errno)
+ continue;
+
+ P_ERROR("check_create_dir: mkdir (%s): %s", dir, STRERRNO);
+ return -1;
+ } else {
+ P_ERROR("check_create_dir: stat (%s): %s", dir, STRERRNO);
+ return -1;
+ }
+ } else if (!S_ISDIR(statbuf.st_mode)) {
+ P_ERROR("check_create_dir: `%s' exists but is not "
+ "a directory!",
+ dir);
+ return -1;
+ }
+ break;
+ }
+ }
+
+ return 0;
+} /* check_create_dir */
+
+#ifdef HAVE_LIBKSTAT
+int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) {
+ char ident[128];
+
+ *ksp_ptr = NULL;
+
+ if (kc == NULL)
+ return -1;
+
+ snprintf(ident, sizeof(ident), "%s,%i,%s", module, instance, name);
+
+ *ksp_ptr = kstat_lookup(kc, module, instance, name);
+ if (*ksp_ptr == NULL) {
+ P_ERROR("get_kstat: Cound not find kstat %s", ident);
+ return -1;
+ }
+
+ if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
+ P_ERROR("get_kstat: kstat %s has wrong type", ident);
+ *ksp_ptr = NULL;
+ return -1;
+ }
+
+#ifdef assert
+ assert(*ksp_ptr != NULL);
+ assert((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
+#endif
+
+ if (kstat_read(kc, *ksp_ptr, NULL) == -1) {
+ P_ERROR("get_kstat: kstat %s could not be read", ident);
+ return -1;
+ }
+
+ if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
+ P_ERROR("get_kstat: kstat %s has wrong type", ident);
+ return -1;
+ }
+
+ return 0;
+}
+
+long long get_kstat_value(kstat_t *ksp, char *name) {
+ kstat_named_t *kn;
+ long long retval = -1LL;
+
+ if (ksp == NULL) {
+ P_ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name);
+ return -1LL;
+ } else if (ksp->ks_type != KSTAT_TYPE_NAMED) {
+ P_ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
+ "is not KSTAT_TYPE_NAMED (%#x).",
+ name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED);
+ return -1LL;
+ }
+
+ if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, name)) == NULL)
+ return -1LL;
+
+ if (kn->data_type == KSTAT_DATA_INT32)
+ retval = (long long)kn->value.i32;
+ else if (kn->data_type == KSTAT_DATA_UINT32)
+ retval = (long long)kn->value.ui32;
+ else if (kn->data_type == KSTAT_DATA_INT64)
+ retval =
+ (long long)kn->value.i64; /* According to ANSI C99 `long long' must hold
+ at least 64 bits */
+ else if (kn->data_type == KSTAT_DATA_UINT64)
+ retval = (long long)kn->value.ui64; /* XXX: Might overflow! */
+ else
+ P_WARNING("get_kstat_value: Not a numeric value: %s", name);
+
+ return retval;
+}
+#endif /* HAVE_LIBKSTAT */
+
+#ifndef HAVE_HTONLL
+unsigned long long ntohll(unsigned long long n) {
+#if BYTE_ORDER == BIG_ENDIAN
+ return n;
+#else
+ return (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32);
+#endif
+} /* unsigned long long ntohll */
+
+unsigned long long htonll(unsigned long long n) {
+#if BYTE_ORDER == BIG_ENDIAN
+ return n;
+#else
+ return (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32);
+#endif
+} /* unsigned long long htonll */
+#endif /* HAVE_HTONLL */
+
+#if FP_LAYOUT_NEED_NOTHING
+/* Well, we need nothing.. */
+/* #endif FP_LAYOUT_NEED_NOTHING */
+
+#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
+#if FP_LAYOUT_NEED_ENDIANFLIP
+#define FP_CONVERT(A) \
+ ((((uint64_t)(A)&0xff00000000000000LL) >> 56) | \
+ (((uint64_t)(A)&0x00ff000000000000LL) >> 40) | \
+ (((uint64_t)(A)&0x0000ff0000000000LL) >> 24) | \
+ (((uint64_t)(A)&0x000000ff00000000LL) >> 8) | \
+ (((uint64_t)(A)&0x00000000ff000000LL) << 8) | \
+ (((uint64_t)(A)&0x0000000000ff0000LL) << 24) | \
+ (((uint64_t)(A)&0x000000000000ff00LL) << 40) | \
+ (((uint64_t)(A)&0x00000000000000ffLL) << 56))
+#else
+#define FP_CONVERT(A) \
+ ((((uint64_t)(A)&0xffffffff00000000LL) >> 32) | \
+ (((uint64_t)(A)&0x00000000ffffffffLL) << 32))
+#endif
+
+double ntohd(double d) {
+ union {
+ uint8_t byte[8];
+ uint64_t integer;
+ double floating;
+ } ret;
+
+ ret.floating = d;
+
+ /* NAN in x86 byte order */
+ if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00) && (ret.byte[2] == 0x00) &&
+ (ret.byte[3] == 0x00) && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00) &&
+ (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f)) {
+ return NAN;
+ } else {
+ uint64_t tmp;
+
+ tmp = ret.integer;
+ ret.integer = FP_CONVERT(tmp);
+ return ret.floating;
+ }
+} /* double ntohd */
+
+double htond(double d) {
+ union {
+ uint8_t byte[8];
+ uint64_t integer;
+ double floating;
+ } ret;
+
+ if (isnan(d)) {
+ ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00;
+ ret.byte[4] = ret.byte[5] = 0x00;
+ ret.byte[6] = 0xf8;
+ ret.byte[7] = 0x7f;
+ return ret.floating;
+ } else {
+ uint64_t tmp;
+
+ ret.floating = d;
+ tmp = FP_CONVERT(ret.integer);
+ ret.integer = tmp;
+ return ret.floating;
+ }
+} /* double htond */
+#endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */
+
+int format_name(char *ret, int ret_len, const char *hostname,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance) {
+ char *buffer;
+ size_t buffer_size;
+
+ buffer = ret;
+ buffer_size = (size_t)ret_len;
+
+#define APPEND(str) \
+ do { \
+ size_t l = strlen(str); \
+ if (l >= buffer_size) \
+ return ENOBUFS; \
+ memcpy(buffer, (str), l); \
+ buffer += l; \
+ buffer_size -= l; \
+ } while (0)
+
+ assert(plugin != NULL);
+ assert(type != NULL);
+
+ APPEND(hostname);
+ APPEND("/");
+ APPEND(plugin);
+ if ((plugin_instance != NULL) && (plugin_instance[0] != 0)) {
+ APPEND("-");
+ APPEND(plugin_instance);
+ }
+ APPEND("/");
+ APPEND(type);
+ if ((type_instance != NULL) && (type_instance[0] != 0)) {
+ APPEND("-");
+ APPEND(type_instance);
+ }
+ assert(buffer_size > 0);
+ buffer[0] = 0;
+
+#undef APPEND
+ return 0;
+} /* int format_name */
+
+int format_values(char *ret, size_t ret_len, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl,
+ bool store_rates) {
+ size_t offset = 0;
+ int status;
+ gauge_t *rates = NULL;
+
+ assert(0 == strcmp(ds->type, vl->type));
+
+ memset(ret, 0, ret_len);
+
+#define BUFFER_ADD(...) \
+ do { \
+ status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \
+ if (status < 1) { \
+ sfree(rates); \
+ return -1; \
+ } else if (((size_t)status) >= (ret_len - offset)) { \
+ sfree(rates); \
+ return -1; \
+ } else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ BUFFER_ADD("%.3f", CDTIME_T_TO_DOUBLE(vl->time));
+
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ if (ds->ds[i].type == DS_TYPE_GAUGE)
+ BUFFER_ADD(":" GAUGE_FORMAT, vl->values[i].gauge);
+ else if (store_rates) {
+ if (rates == NULL)
+ rates = uc_get_rate(ds, vl);
+ if (rates == NULL) {
+ WARNING("format_values: uc_get_rate failed.");
+ return -1;
+ }
+ BUFFER_ADD(":" GAUGE_FORMAT, rates[i]);
+ } else if (ds->ds[i].type == DS_TYPE_COUNTER)
+ BUFFER_ADD(":%" PRIu64, (uint64_t)vl->values[i].counter);
+ else if (ds->ds[i].type == DS_TYPE_DERIVE)
+ BUFFER_ADD(":%" PRIi64, vl->values[i].derive);
+ else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+ BUFFER_ADD(":%" PRIu64, vl->values[i].absolute);
+ else {
+ ERROR("format_values: Unknown data source type: %i", ds->ds[i].type);
+ sfree(rates);
+ return -1;
+ }
+ } /* for ds->ds_num */
+
+#undef BUFFER_ADD
+
+ sfree(rates);
+ return 0;
+} /* }}} int format_values */
+
+int parse_identifier(char *str, char **ret_host, char **ret_plugin,
+ char **ret_plugin_instance, char **ret_type,
+ char **ret_type_instance, char *default_host) {
+ char *hostname = NULL;
+ char *plugin = NULL;
+ char *plugin_instance = NULL;
+ char *type = NULL;
+ char *type_instance = NULL;
+
+ hostname = str;
+ if (hostname == NULL)
+ return -1;
+
+ plugin = strchr(hostname, '/');
+ if (plugin == NULL)
+ return -1;
+ *plugin = '\0';
+ plugin++;
+
+ type = strchr(plugin, '/');
+ if (type == NULL) {
+ if (default_host == NULL)
+ return -1;
+ /* else: no host specified; use default */
+ type = plugin;
+ plugin = hostname;
+ hostname = default_host;
+ } else {
+ *type = '\0';
+ type++;
+ }
+
+ plugin_instance = strchr(plugin, '-');
+ if (plugin_instance != NULL) {
+ *plugin_instance = '\0';
+ plugin_instance++;
+ }
+
+ type_instance = strchr(type, '-');
+ if (type_instance != NULL) {
+ *type_instance = '\0';
+ type_instance++;
+ }
+
+ *ret_host = hostname;
+ *ret_plugin = plugin;
+ *ret_plugin_instance = plugin_instance;
+ *ret_type = type;
+ *ret_type_instance = type_instance;
+ return 0;
+} /* int parse_identifier */
+
+int parse_identifier_vl(const char *str, value_list_t *vl) /* {{{ */
+{
+ char str_copy[6 * DATA_MAX_NAME_LEN];
+ char *host = NULL;
+ char *plugin = NULL;
+ char *plugin_instance = NULL;
+ char *type = NULL;
+ char *type_instance = NULL;
+ int status;
+
+ if ((str == NULL) || (vl == NULL))
+ return EINVAL;
+
+ sstrncpy(str_copy, str, sizeof(str_copy));
+
+ status = parse_identifier(str_copy, &host, &plugin, &plugin_instance, &type,
+ &type_instance,
+ /* default_host = */ NULL);
+ if (status != 0)
+ return status;
+
+ sstrncpy(vl->host, host, sizeof(vl->host));
+ sstrncpy(vl->plugin, plugin, sizeof(vl->plugin));
+ sstrncpy(vl->plugin_instance,
+ (plugin_instance != NULL) ? plugin_instance : "",
+ sizeof(vl->plugin_instance));
+ sstrncpy(vl->type, type, sizeof(vl->type));
+ sstrncpy(vl->type_instance, (type_instance != NULL) ? type_instance : "",
+ sizeof(vl->type_instance));
+
+ return 0;
+} /* }}} int parse_identifier_vl */
+
+int parse_value(const char *value_orig, value_t *ret_value, int ds_type) {
+ char *value;
+ char *endptr = NULL;
+ size_t value_len;
+
+ if (value_orig == NULL)
+ return EINVAL;
+
+ value = strdup(value_orig);
+ if (value == NULL)
+ return ENOMEM;
+ value_len = strlen(value);
+
+ while ((value_len > 0) && isspace((int)value[value_len - 1])) {
+ value[value_len - 1] = '\0';
+ value_len--;
+ }
+
+ switch (ds_type) {
+ case DS_TYPE_COUNTER:
+ ret_value->counter = (counter_t)strtoull(value, &endptr, 0);
+ break;
+
+ case DS_TYPE_GAUGE:
+ ret_value->gauge = (gauge_t)strtod(value, &endptr);
+ break;
+
+ case DS_TYPE_DERIVE:
+ ret_value->derive = (derive_t)strtoll(value, &endptr, 0);
+ break;
+
+ case DS_TYPE_ABSOLUTE:
+ ret_value->absolute = (absolute_t)strtoull(value, &endptr, 0);
+ break;
+
+ default:
+ sfree(value);
+ P_ERROR("parse_value: Invalid data source type: %i.", ds_type);
+ return -1;
+ }
+
+ if (value == endptr) {
+ P_ERROR("parse_value: Failed to parse string as %s: \"%s\".",
+ DS_TYPE_TO_STRING(ds_type), value);
+ sfree(value);
+ return -1;
+ } else if ((NULL != endptr) && ('\0' != *endptr))
+ P_INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
+ "Input string was \"%s\".",
+ endptr, DS_TYPE_TO_STRING(ds_type), value_orig);
+
+ sfree(value);
+ return 0;
+} /* int parse_value */
+
+int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds) {
+ size_t i;
+ char *dummy;
+ char *ptr;
+ char *saveptr;
+
+ if ((buffer == NULL) || (vl == NULL) || (ds == NULL))
+ return EINVAL;
+
+ i = 0;
+ dummy = buffer;
+ saveptr = NULL;
+ vl->time = 0;
+ while ((ptr = strtok_r(dummy, ":", &saveptr)) != NULL) {
+ dummy = NULL;
+
+ if (i >= vl->values_len) {
+ /* Make sure i is invalid. */
+ i = 0;
+ break;
+ }
+
+ if (vl->time == 0) {
+ if (strcmp("N", ptr) == 0)
+ vl->time = cdtime();
+ else {
+ char *endptr = NULL;
+ double tmp;
+
+ errno = 0;
+ tmp = strtod(ptr, &endptr);
+ if ((errno != 0) /* Overflow */
+ || (endptr == ptr) /* Invalid string */
+ || (endptr == NULL) /* This should not happen */
+ || (*endptr != 0)) /* Trailing chars */
+ return -1;
+
+ vl->time = DOUBLE_TO_CDTIME_T(tmp);
+ }
+
+ continue;
+ }
+
+ if ((strcmp("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
+ vl->values[i].gauge = NAN;
+ else if (0 != parse_value(ptr, &vl->values[i], ds->ds[i].type))
+ return -1;
+
+ i++;
+ } /* while (strtok_r) */
+
+ if ((ptr != NULL) || (i == 0))
+ return -1;
+ return 0;
+} /* int parse_values */
+
+int parse_value_file(char const *path, value_t *ret_value, int ds_type) {
+ FILE *fh;
+ char buffer[256];
+
+ fh = fopen(path, "r");
+ if (fh == NULL)
+ return -1;
+
+ if (fgets(buffer, sizeof(buffer), fh) == NULL) {
+ fclose(fh);
+ return -1;
+ }
+
+ fclose(fh);
+
+ strstripnewline(buffer);
+
+ return parse_value(buffer, ret_value, ds_type);
+} /* int parse_value_file */
+
+#if !HAVE_GETPWNAM_R
+int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
+ struct passwd **pwbufp) {
+#ifndef HAVE_GETPWNAM
+ return -1;
+#else
+ int status = 0;
+ struct passwd *pw;
+
+ memset(pwbuf, '\0', sizeof(struct passwd));
+
+ pthread_mutex_lock(&getpwnam_r_lock);
+
+ do {
+ pw = getpwnam(name);
+ if (pw == NULL) {
+ status = (errno != 0) ? errno : ENOENT;
+ break;
+ }
+
+#define GETPWNAM_COPY_MEMBER(member) \
+ if (pw->member != NULL) { \
+ int len = strlen(pw->member); \
+ if (len >= buflen) { \
+ status = ENOMEM; \
+ break; \
+ } \
+ sstrncpy(buf, pw->member, buflen); \
+ pwbuf->member = buf; \
+ buf += (len + 1); \
+ buflen -= (len + 1); \
+ }
+ GETPWNAM_COPY_MEMBER(pw_name);
+ GETPWNAM_COPY_MEMBER(pw_passwd);
+ GETPWNAM_COPY_MEMBER(pw_gecos);
+ GETPWNAM_COPY_MEMBER(pw_dir);
+ GETPWNAM_COPY_MEMBER(pw_shell);
+
+ pwbuf->pw_uid = pw->pw_uid;
+ pwbuf->pw_gid = pw->pw_gid;
+
+ if (pwbufp != NULL)
+ *pwbufp = pwbuf;
+ } while (0);
+
+ pthread_mutex_unlock(&getpwnam_r_lock);
+
+ return status;
+#endif /* HAVE_GETPWNAM */
+} /* int getpwnam_r */
+#endif /* !HAVE_GETPWNAM_R */
+
+int notification_init(notification_t *n, int severity, const char *message,
+ const char *host, const char *plugin,
+ const char *plugin_instance, const char *type,
+ const char *type_instance) {
+ memset(n, '\0', sizeof(notification_t));
+
+ n->severity = severity;
+
+ if (message != NULL)
+ sstrncpy(n->message, message, sizeof(n->message));
+ if (host != NULL)
+ sstrncpy(n->host, host, sizeof(n->host));
+ if (plugin != NULL)
+ sstrncpy(n->plugin, plugin, sizeof(n->plugin));
+ if (plugin_instance != NULL)
+ sstrncpy(n->plugin_instance, plugin_instance, sizeof(n->plugin_instance));
+ if (type != NULL)
+ sstrncpy(n->type, type, sizeof(n->type));
+ if (type_instance != NULL)
+ sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
+
+ return 0;
+} /* int notification_init */
+
+int walk_directory(const char *dir, dirwalk_callback_f callback,
+ void *user_data, int include_hidden) {
+ struct dirent *ent;
+ DIR *dh;
+ int success;
+ int failure;
+
+ success = 0;
+ failure = 0;
+
+ if ((dh = opendir(dir)) == NULL) {
+ P_ERROR("walk_directory: Cannot open '%s': %s", dir, STRERRNO);
+ return -1;
+ }
+
+ while ((ent = readdir(dh)) != NULL) {
+ int status;
+
+ 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)
+ failure++;
+ else
+ success++;
+ }
+
+ closedir(dh);
+
+ if ((success == 0) && (failure > 0))
+ return -1;
+ return 0;
+}
+
+ssize_t read_file_contents(const char *filename, char *buf, size_t bufsize) {
+ FILE *fh;
+ ssize_t ret;
+
+ fh = fopen(filename, "r");
+ if (fh == NULL)
+ return -1;
+
+ ret = (ssize_t)fread(buf, 1, bufsize, fh);
+ if ((ret == 0) && (ferror(fh) != 0)) {
+ P_ERROR("read_file_contents: Reading file \"%s\" failed.", filename);
+ ret = -1;
+ }
+
+ fclose(fh);
+ return ret;
+}
+
+counter_t counter_diff(counter_t old_value, counter_t new_value) {
+ counter_t diff;
+
+ if (old_value > new_value) {
+ if (old_value <= 4294967295U)
+ diff = (4294967295U - old_value) + new_value + 1;
+ else
+ diff = (18446744073709551615ULL - old_value) + new_value + 1;
+ } else {
+ diff = new_value - old_value;
+ }
+
+ return diff;
+} /* counter_t counter_diff */
+
+int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */
+ rate_to_value_state_t *state, int ds_type, cdtime_t t) {
+ gauge_t delta_gauge;
+ cdtime_t delta_t;
+
+ if (ds_type == DS_TYPE_GAUGE) {
+ state->last_value.gauge = rate;
+ state->last_time = t;
+
+ *ret_value = state->last_value;
+ return 0;
+ }
+
+ /* Counter and absolute can't handle negative rates. Reset "last time"
+ * to zero, so that the next valid rate will re-initialize the
+ * structure. */
+ if ((rate < 0.0) &&
+ ((ds_type == DS_TYPE_COUNTER) || (ds_type == DS_TYPE_ABSOLUTE))) {
+ memset(state, 0, sizeof(*state));
+ return EINVAL;
+ }
+
+ /* Another invalid state: The time is not increasing. */
+ if (t <= state->last_time) {
+ memset(state, 0, sizeof(*state));
+ return EINVAL;
+ }
+
+ delta_t = t - state->last_time;
+ delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual;
+
+ /* Previous value is invalid. */
+ if (state->last_time == 0) /* {{{ */
+ {
+ if (ds_type == DS_TYPE_DERIVE) {
+ state->last_value.derive = (derive_t)rate;
+ state->residual = rate - ((gauge_t)state->last_value.derive);
+ } else if (ds_type == DS_TYPE_COUNTER) {
+ state->last_value.counter = (counter_t)rate;
+ state->residual = rate - ((gauge_t)state->last_value.counter);
+ } else if (ds_type == DS_TYPE_ABSOLUTE) {
+ state->last_value.absolute = (absolute_t)rate;
+ state->residual = rate - ((gauge_t)state->last_value.absolute);
+ } else {
+ assert(23 == 42);
+ }
+
+ state->last_time = t;
+ return EAGAIN;
+ } /* }}} */
+
+ if (ds_type == DS_TYPE_DERIVE) {
+ derive_t delta_derive = (derive_t)delta_gauge;
+
+ state->last_value.derive += delta_derive;
+ state->residual = delta_gauge - ((gauge_t)delta_derive);
+ } else if (ds_type == DS_TYPE_COUNTER) {
+ counter_t delta_counter = (counter_t)delta_gauge;
+
+ state->last_value.counter += delta_counter;
+ state->residual = delta_gauge - ((gauge_t)delta_counter);
+ } else if (ds_type == DS_TYPE_ABSOLUTE) {
+ absolute_t delta_absolute = (absolute_t)delta_gauge;
+
+ state->last_value.absolute = delta_absolute;
+ state->residual = delta_gauge - ((gauge_t)delta_absolute);
+ } else {
+ assert(23 == 42);
+ }
+
+ state->last_time = t;
+ *ret_value = state->last_value;
+ return 0;
+} /* }}} value_t rate_to_value */
+
+int value_to_rate(gauge_t *ret_rate, /* {{{ */
+ value_t value, int ds_type, cdtime_t t,
+ value_to_rate_state_t *state) {
+ gauge_t interval;
+
+ /* Another invalid state: The time is not increasing. */
+ if (t <= state->last_time) {
+ memset(state, 0, sizeof(*state));
+ return EINVAL;
+ }
+
+ interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
+
+ /* Previous value is invalid. */
+ if (state->last_time == 0) {
+ state->last_value = value;
+ state->last_time = t;
+ return EAGAIN;
+ }
+
+ switch (ds_type) {
+ case DS_TYPE_DERIVE: {
+ derive_t diff = value.derive - state->last_value.derive;
+ *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
+ break;
+ }
+ case DS_TYPE_GAUGE: {
+ *ret_rate = value.gauge;
+ break;
+ }
+ case DS_TYPE_COUNTER: {
+ counter_t diff = counter_diff(state->last_value.counter, value.counter);
+ *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
+ break;
+ }
+ case DS_TYPE_ABSOLUTE: {
+ absolute_t diff = value.absolute;
+ *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
+ break;
+ }
+ default:
+ return EINVAL;
+ }
+
+ state->last_value = value;
+ state->last_time = t;
+ return 0;
+} /* }}} value_t rate_to_value */
+
+int service_name_to_port_number(const char *service_name) {
+ struct addrinfo *ai_list;
+ int status;
+ int service_number;
+
+ if (service_name == NULL)
+ return -1;
+
+ struct addrinfo ai_hints = {.ai_family = AF_UNSPEC};
+
+ status = getaddrinfo(/* node = */ NULL, service_name, &ai_hints, &ai_list);
+ if (status != 0) {
+ P_ERROR("service_name_to_port_number: getaddrinfo failed: %s",
+ gai_strerror(status));
+ return -1;
+ }
+
+ service_number = -1;
+ for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
+ ai_ptr = ai_ptr->ai_next) {
+ if (ai_ptr->ai_family == AF_INET) {
+ struct sockaddr_in *sa;
+
+ sa = (void *)ai_ptr->ai_addr;
+ service_number = (int)ntohs(sa->sin_port);
+ } else if (ai_ptr->ai_family == AF_INET6) {
+ struct sockaddr_in6 *sa;
+
+ sa = (void *)ai_ptr->ai_addr;
+ service_number = (int)ntohs(sa->sin6_port);
+ }
+
+ if ((service_number > 0) && (service_number <= 65535))
+ break;
+ }
+
+ freeaddrinfo(ai_list);
+
+ if ((service_number > 0) && (service_number <= 65535))
+ return service_number;
+ return -1;
+} /* int service_name_to_port_number */
+
+void set_sock_opts(int sockfd) /* {{{ */
+{
+ int status;
+ int socktype;
+
+ status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype,
+ &(socklen_t){sizeof(socktype)});
+ if (status != 0) {
+ P_WARNING("set_sock_opts: failed to determine socket type");
+ return;
+ }
+
+ if (socktype == SOCK_STREAM) {
+ status =
+ setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int));
+ if (status != 0)
+ P_WARNING("set_sock_opts: failed to set socket keepalive flag");
+
+#ifdef TCP_KEEPIDLE
+ int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1);
+ status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepidle,
+ sizeof(tcp_keepidle));
+ if (status != 0)
+ P_WARNING("set_sock_opts: failed to set socket tcp keepalive time");
+#endif
+
+#ifdef TCP_KEEPINTVL
+ int tcp_keepintvl =
+ ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1);
+ status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepintvl,
+ sizeof(tcp_keepintvl));
+ if (status != 0)
+ P_WARNING("set_sock_opts: failed to set socket tcp keepalive interval");
+#endif
+ }
+} /* }}} void set_sock_opts */
+
+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 */
+
+int strtogauge(const char *string, gauge_t *ret_value) /* {{{ */
+{
+ gauge_t tmp;
+ char *endptr = NULL;
+
+ if ((string == NULL) || (ret_value == NULL))
+ return EINVAL;
+
+ errno = 0;
+ endptr = NULL;
+ tmp = (gauge_t)strtod(string, &endptr);
+ if (errno != 0)
+ return errno;
+ else if ((endptr == NULL) || (*endptr != 0))
+ return EINVAL;
+
+ *ret_value = tmp;
+ return 0;
+} /* }}} int strtogauge */
+
+int strarray_add(char ***ret_array, size_t *ret_array_len,
+ char const *str) /* {{{ */
+{
+ char **array;
+ size_t array_len = *ret_array_len;
+
+ if (str == NULL)
+ return EINVAL;
+
+ array = realloc(*ret_array, (array_len + 1) * sizeof(*array));
+ if (array == NULL)
+ return ENOMEM;
+ *ret_array = array;
+
+ array[array_len] = strdup(str);
+ if (array[array_len] == NULL)
+ return ENOMEM;
+
+ array_len++;
+ *ret_array_len = array_len;
+ return 0;
+} /* }}} int strarray_add */
+
+void strarray_free(char **array, size_t array_len) /* {{{ */
+{
+ for (size_t i = 0; i < array_len; i++)
+ sfree(array[i]);
+ sfree(array);
+} /* }}} void strarray_free */
+
+#if HAVE_CAPABILITY
+int check_capability(int arg) /* {{{ */
+{
+ cap_value_t cap_value = (cap_value_t)arg;
+ cap_t cap;
+ cap_flag_value_t cap_flag_value;
+
+ if (!CAP_IS_SUPPORTED(cap_value))
+ return -1;
+
+ if (!(cap = cap_get_proc())) {
+ P_ERROR("check_capability: cap_get_proc failed.");
+ return -1;
+ }
+
+ if (cap_get_flag(cap, cap_value, CAP_EFFECTIVE, &cap_flag_value) < 0) {
+ P_ERROR("check_capability: cap_get_flag failed.");
+ cap_free(cap);
+ return -1;
+ }
+ cap_free(cap);
+
+ return cap_flag_value != CAP_SET;
+} /* }}} int check_capability */
+#else
+int check_capability(__attribute__((unused)) int arg) /* {{{ */
+{
+ P_WARNING("check_capability: unsupported capability implementation. "
+ "Some plugin(s) may require elevated privileges to work properly.");
+ return 0;
+} /* }}} int check_capability */
+#endif /* HAVE_CAPABILITY */
--- /dev/null
+/**
+ * collectd - src/common.h
+ * Copyright (C) 2005-2014 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Niki W. Waibel <niki.waibel@gmx.net>
+**/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "collectd.h"
+
+#include "plugin.h"
+
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#define sfree(ptr) \
+ do { \
+ free(ptr); \
+ (ptr) = NULL; \
+ } while (0)
+
+#define STATIC_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+#define IS_TRUE(s) \
+ ((strcasecmp("true", (s)) == 0) || (strcasecmp("yes", (s)) == 0) || \
+ (strcasecmp("on", (s)) == 0))
+#define IS_FALSE(s) \
+ ((strcasecmp("false", (s)) == 0) || (strcasecmp("no", (s)) == 0) || \
+ (strcasecmp("off", (s)) == 0))
+
+struct rate_to_value_state_s {
+ value_t last_value;
+ cdtime_t last_time;
+ gauge_t residual;
+};
+typedef struct rate_to_value_state_s rate_to_value_state_t;
+
+struct value_to_rate_state_s {
+ value_t last_value;
+ cdtime_t last_time;
+};
+typedef struct value_to_rate_state_s value_to_rate_state_t;
+
+char *sstrncpy(char *dest, const char *src, size_t n);
+
+__attribute__((format(printf, 1, 2))) char *ssnprintf_alloc(char const *format,
+ ...);
+
+char *sstrdup(const char *s);
+void *smalloc(size_t size);
+char *sstrerror(int errnum, char *buf, size_t buflen);
+
+#ifndef ERRBUF_SIZE
+#define ERRBUF_SIZE 256
+#endif
+
+#define STRERROR(e) sstrerror((e), (char[ERRBUF_SIZE]){0}, ERRBUF_SIZE)
+#define STRERRNO STRERROR(errno)
+
+/*
+ * NAME
+ * sread
+ *
+ * DESCRIPTION
+ * Reads exactly `n' bytes or fails. Syntax and other behavior is analogous
+ * to `read(2)'.
+ *
+ * PARAMETERS
+ * `fd' File descriptor to write to.
+ * `buf' Buffer that is to be written.
+ * `count' Number of bytes in the buffer.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred. `errno' is set in this
+ * case.
+ */
+int sread(int fd, void *buf, size_t count);
+
+/*
+ * NAME
+ * swrite
+ *
+ * DESCRIPTION
+ * Writes exactly `n' bytes or fails. Syntax and other behavior is analogous
+ * to `write(2)'.
+ *
+ * PARAMETERS
+ * `fd' File descriptor to write to.
+ * `buf' Buffer that is to be written.
+ * `count' Number of bytes in the buffer.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred. `errno' is set in this
+ * case.
+ */
+int swrite(int fd, const void *buf, size_t count);
+
+/*
+ * NAME
+ * strsplit
+ *
+ * DESCRIPTION
+ * Splits a string into parts and stores pointers to the parts in `fields'.
+ * The characters split at are: " ", "\t", "\r", and "\n".
+ *
+ * PARAMETERS
+ * `string' String to split. This string will be modified. `fields' will
+ * contain pointers to parts of this string, so free'ing it
+ * will destroy `fields' as well.
+ * `fields' Array of strings where pointers to the parts will be stored.
+ * `size' Number of elements in the array. No more than `size'
+ * pointers will be stored in `fields'.
+ *
+ * RETURN VALUE
+ * Returns the number of parts stored in `fields'.
+ */
+int strsplit(char *string, char **fields, size_t size);
+
+/*
+ * NAME
+ * strjoin
+ *
+ * DESCRIPTION
+ * Joins together several parts of a string using `sep' as a separator. This
+ * is equivalent to the Perl built-in `join'.
+ *
+ * PARAMETERS
+ * `dst' Buffer where the result is stored. Can be NULL if you need to
+ * determine the required buffer size only.
+ * `dst_len' Length of the destination buffer. No more than this many
+ * bytes will be written to the memory pointed to by `dst',
+ * including the trailing null-byte. Must be zero if dst is
+ * NULL.
+ * `fields' Array of strings to be joined.
+ * `fields_num' Number of elements in the `fields' array.
+ * `sep' String to be inserted between any two elements of `fields'.
+ * This string is neither prepended nor appended to the result.
+ * Instead of passing "" (empty string) one can pass NULL.
+ *
+ * RETURN VALUE
+ * Returns the number of characters in the resulting string, excluding a
+ * tailing null byte. If this value is greater than or equal to "dst_len", the
+ * result in "dst" is truncated (but still null terminated). On error a
+ * negative value is returned.
+ */
+int strjoin(char *dst, size_t dst_len, char **fields, size_t fields_num,
+ const char *sep);
+
+/*
+ * NAME
+ * escape_slashes
+ *
+ * DESCRIPTION
+ * Removes slashes ("/") from "buffer". If buffer contains a single slash,
+ * the result will be "root". Leading slashes are removed. All other slashes
+ * are replaced with underscores ("_").
+ * This function is used by plugin_dispatch_values() to escape all parts of
+ * the identifier.
+ *
+ * PARAMETERS
+ * `buffer' String to be escaped.
+ * `buffer_size' Size of the buffer. No more then this many bytes will be
+ * written to `buffer', including the trailing null-byte.
+ *
+ * RETURN VALUE
+ * Returns zero upon success and a value smaller than zero upon failure.
+ */
+int escape_slashes(char *buffer, size_t buffer_size);
+
+/**
+ * NAME
+ * escape_string
+ *
+ * DESCRIPTION
+ * escape_string quotes and escapes a string to be usable with collectd's
+ * plain text protocol. "simple" strings are left as they are, for example if
+ * buffer is 'simple' before the call, it will remain 'simple'. However, if
+ * buffer contains 'more "complex"' before the call, the returned buffer will
+ * contain '"more \"complex\""'.
+ *
+ * If the buffer is too small to contain the escaped string, the string will
+ * be truncated. However, leading and trailing double quotes, as well as an
+ * ending null byte are guaranteed.
+ *
+ * RETURN VALUE
+ * Returns zero on success, even if the string was truncated. Non-zero on
+ * failure.
+ */
+int escape_string(char *buffer, size_t buffer_size);
+
+/*
+ * NAME
+ * replace_special
+ *
+ * DESCRIPTION
+ * Replaces any special characters (anything that's not alpha-numeric or a
+ * dash) with an underscore.
+ *
+ * E.g. "foo$bar&" would become "foo_bar_".
+ *
+ * PARAMETERS
+ * `buffer' String to be handled.
+ * `buffer_size' Length of the string. The function returns after
+ * encountering a null-byte or reading this many bytes.
+ */
+void replace_special(char *buffer, size_t buffer_size);
+
+/*
+ * NAME
+ * strunescape
+ *
+ * DESCRIPTION
+ * Replaces any escaped characters in a string with the appropriate special
+ * characters. The following escaped characters are recognized:
+ *
+ * \t -> <tab>
+ * \n -> <newline>
+ * \r -> <carriage return>
+ *
+ * For all other escacped characters only the backslash will be removed.
+ *
+ * PARAMETERS
+ * `buf' String to be unescaped.
+ * `buf_len' Length of the string, including the terminating null-byte.
+ *
+ * RETURN VALUE
+ * Returns zero upon success, a value less than zero else.
+ */
+int strunescape(char *buf, size_t buf_len);
+
+/**
+ * Removed trailing newline characters (CR and LF) from buffer, which must be
+ * null terminated. Returns the length of the resulting string.
+ */
+__attribute__((nonnull(1))) size_t strstripnewline(char *buffer);
+
+/*
+ * NAME
+ * timeval_cmp
+ *
+ * DESCRIPTION
+ * Compare the two time values `tv0' and `tv1' and store the absolut value
+ * of the difference in the time value pointed to by `delta' if it does not
+ * equal NULL.
+ *
+ * RETURN VALUE
+ * Returns an integer less than, equal to, or greater than zero if `tv0' is
+ * less than, equal to, or greater than `tv1' respectively.
+ */
+int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta);
+
+/* make sure tv_usec stores less than a second */
+#define NORMALIZE_TIMEVAL(tv) \
+ do { \
+ (tv).tv_sec += (tv).tv_usec / 1000000; \
+ (tv).tv_usec = (tv).tv_usec % 1000000; \
+ } while (0)
+
+/* make sure tv_sec stores less than a second */
+#define NORMALIZE_TIMESPEC(tv) \
+ do { \
+ (tv).tv_sec += (tv).tv_nsec / 1000000000; \
+ (tv).tv_nsec = (tv).tv_nsec % 1000000000; \
+ } while (0)
+
+int check_create_dir(const char *file_orig);
+
+#ifdef HAVE_LIBKSTAT
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
+int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name);
+long long get_kstat_value(kstat_t *ksp, char *name);
+#endif
+
+#ifndef HAVE_HTONLL
+unsigned long long ntohll(unsigned long long n);
+unsigned long long htonll(unsigned long long n);
+#endif
+
+#if FP_LAYOUT_NEED_NOTHING
+#define ntohd(d) (d)
+#define htond(d) (d)
+#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
+double ntohd(double d);
+double htond(double d);
+#else
+#error \
+ "Don't know how to convert between host and network representation of doubles."
+#endif
+
+int format_name(char *ret, int ret_len, const char *hostname,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance);
+#define FORMAT_VL(ret, ret_len, vl) \
+ format_name(ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
+ (vl)->type, (vl)->type_instance)
+int format_values(char *ret, size_t ret_len, const data_set_t *ds,
+ const value_list_t *vl, bool store_rates);
+
+int parse_identifier(char *str, char **ret_host, char **ret_plugin,
+ char **ret_plugin_instance, char **ret_type,
+ char **ret_type_instance, char *default_host);
+int parse_identifier_vl(const char *str, value_list_t *vl);
+int parse_value(const char *value, value_t *ret_value, int ds_type);
+int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds);
+
+/* parse_value_file reads "path" and parses its content as an integer or
+ * floating point, depending on "ds_type". On success, the value is stored in
+ * "ret_value" and zero is returned. On failure, a non-zero value is returned.
+ */
+int parse_value_file(char const *path, value_t *ret_value, int ds_type);
+
+#if !HAVE_GETPWNAM_R
+struct passwd;
+int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
+ struct passwd **pwbufp);
+#endif
+
+int notification_init(notification_t *n, int severity, const char *message,
+ const char *host, const char *plugin,
+ const char *plugin_instance, const char *type,
+ const char *type_instance);
+#define NOTIFICATION_INIT_VL(n, vl) \
+ notification_init(n, NOTIF_FAILURE, NULL, (vl)->host, (vl)->plugin, \
+ (vl)->plugin_instance, (vl)->type, (vl)->type_instance)
+
+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, int hidden);
+/* Returns the number of bytes read or negative on error. */
+ssize_t read_file_contents(char const *filename, char *buf, size_t bufsize);
+
+counter_t counter_diff(counter_t old_value, counter_t new_value);
+
+/* Convert a rate back to a value_t. When converting to a derive_t, counter_t
+ * or absolute_t, take fractional residuals into account. This is important
+ * when scaling counters, for example.
+ * Returns zero on success. Returns EAGAIN when called for the first time; in
+ * this case the value_t is invalid and the next call should succeed. Other
+ * return values indicate an error. */
+int rate_to_value(value_t *ret_value, gauge_t rate,
+ rate_to_value_state_t *state, int ds_type, cdtime_t t);
+
+int value_to_rate(gauge_t *ret_rate, value_t value, int ds_type, cdtime_t t,
+ value_to_rate_state_t *state);
+
+/* Converts a service name (a string) to a port number
+ * (in the range [1-65535]). Returns less than zero on error. */
+int service_name_to_port_number(const char *service_name);
+
+/* Sets various, non-default, socket options */
+void set_sock_opts(int sockfd);
+
+/** Parse a string to a derive_t value. Returns zero on success or non-zero on
+ * failure. If failure is returned, ret_value is not touched. */
+int strtoderive(const char *string, derive_t *ret_value);
+
+/** Parse a string to a gauge_t value. Returns zero on success or non-zero on
+ * failure. If failure is returned, ret_value is not touched. */
+int strtogauge(const char *string, gauge_t *ret_value);
+
+int strarray_add(char ***ret_array, size_t *ret_array_len, char const *str);
+void strarray_free(char **array, size_t array_len);
+
+/** Check if the current process benefits from the capability passed in
+ * argument. Returns zero if it does, less than zero if it doesn't or on error.
+ * See capabilities(7) for the list of possible capabilities.
+ * */
+int check_capability(int arg);
+
+#endif /* COMMON_H */
--- /dev/null
+/**
+ * collectd - src/tests/test_common.c
+ * Copyright (C) 2013 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "testing.h"
+#include "utils/common/common.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
+
+#if HAVE_LIBKSTAT
+kstat_ctl_t *kc;
+#endif /* HAVE_LIBKSTAT */
+
+DEF_TEST(sstrncpy) {
+ char buffer[16] = "";
+ char *ptr = &buffer[4];
+ char *ret;
+
+ buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff;
+ buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff;
+
+ ret = sstrncpy(ptr, "foobar", 8);
+ OK(ret == ptr);
+ EXPECT_EQ_STR("foobar", ptr);
+ OK(buffer[3] == buffer[12]);
+
+ ret = sstrncpy(ptr, "abc", 8);
+ OK(ret == ptr);
+ EXPECT_EQ_STR("abc", ptr);
+ OK(buffer[3] == buffer[12]);
+
+ ret = sstrncpy(ptr, "collectd", 8);
+ OK(ret == ptr);
+ OK(ptr[7] == 0);
+ EXPECT_EQ_STR("collect", ptr);
+ OK(buffer[3] == buffer[12]);
+
+ return 0;
+}
+
+DEF_TEST(sstrdup) {
+ char *ptr;
+
+ ptr = sstrdup("collectd");
+ OK(ptr != NULL);
+ EXPECT_EQ_STR("collectd", ptr);
+
+ sfree(ptr);
+
+ ptr = sstrdup(NULL);
+ OK(ptr == NULL);
+
+ return 0;
+}
+
+DEF_TEST(strsplit) {
+ char buffer[32];
+ char *fields[8];
+ int status;
+
+ strncpy(buffer, "foo bar", sizeof(buffer));
+ status = strsplit(buffer, fields, 8);
+ OK(status == 2);
+ EXPECT_EQ_STR("foo", fields[0]);
+ EXPECT_EQ_STR("bar", fields[1]);
+
+ strncpy(buffer, "foo \t bar", sizeof(buffer));
+ status = strsplit(buffer, fields, 8);
+ OK(status == 2);
+ EXPECT_EQ_STR("foo", fields[0]);
+ EXPECT_EQ_STR("bar", fields[1]);
+
+ strncpy(buffer, "one two\tthree\rfour\nfive", sizeof(buffer));
+ status = strsplit(buffer, fields, 8);
+ OK(status == 5);
+ EXPECT_EQ_STR("one", fields[0]);
+ EXPECT_EQ_STR("two", fields[1]);
+ EXPECT_EQ_STR("three", fields[2]);
+ EXPECT_EQ_STR("four", fields[3]);
+ EXPECT_EQ_STR("five", fields[4]);
+
+ strncpy(buffer, "\twith trailing\n", sizeof(buffer));
+ status = strsplit(buffer, fields, 8);
+ OK(status == 2);
+ EXPECT_EQ_STR("with", fields[0]);
+ EXPECT_EQ_STR("trailing", fields[1]);
+
+ strncpy(buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof(buffer));
+ status = strsplit(buffer, fields, 8);
+ OK(status == 8);
+ EXPECT_EQ_STR("7", fields[6]);
+ EXPECT_EQ_STR("8", fields[7]);
+
+ strncpy(buffer, "single", sizeof(buffer));
+ status = strsplit(buffer, fields, 8);
+ OK(status == 1);
+ EXPECT_EQ_STR("single", fields[0]);
+
+ strncpy(buffer, "", sizeof(buffer));
+ status = strsplit(buffer, fields, 8);
+ OK(status == 0);
+
+ return 0;
+}
+
+DEF_TEST(strjoin) {
+ struct {
+ char **fields;
+ size_t fields_num;
+ char *separator;
+
+ int want_return;
+ char *want_buffer;
+ } cases[] = {
+ /* Normal case. */
+ {(char *[]){"foo", "bar"}, 2, "!", 7, "foo!bar"},
+ /* One field only. */
+ {(char *[]){"foo"}, 1, "!", 3, "foo"},
+ /* No fields at all. */
+ {NULL, 0, "!", 0, ""},
+ /* Longer separator. */
+ {(char *[]){"foo", "bar"}, 2, "rcht", 10, "foorchtbar"},
+ /* Empty separator. */
+ {(char *[]){"foo", "bar"}, 2, "", 6, "foobar"},
+ /* NULL separator. */
+ {(char *[]){"foo", "bar"}, 2, NULL, 6, "foobar"},
+ /* buffer not large enough -> string is truncated. */
+ {(char *[]){"aaaaaa", "bbbbbb", "c!"}, 3, "-", 16, "aaaaaa-bbbbbb-c"},
+ /* buffer not large enough -> last field fills buffer completely. */
+ {(char *[]){"aaaaaaa", "bbbbbbb", "!"}, 3, "-", 17, "aaaaaaa-bbbbbbb"},
+ /* buffer not large enough -> string does *not* end in separator. */
+ {(char *[]){"aaaa", "bbbb", "cccc", "!"}, 4, "-", 16, "aaaa-bbbb-cccc"},
+ /* buffer not large enough -> string does not end with partial
+ separator. */
+ {(char *[]){"aaaaaa", "bbbbbb", "!"}, 3, "+-", 17, "aaaaaa+-bbbbbb"},
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ char buffer[16];
+ int status;
+
+ memset(buffer, 0xFF, sizeof(buffer));
+ status = strjoin(buffer, sizeof(buffer), cases[i].fields,
+ cases[i].fields_num, cases[i].separator);
+ EXPECT_EQ_INT(cases[i].want_return, status);
+ EXPECT_EQ_STR(cases[i].want_buffer, buffer);
+
+ /* use (NULL, 0) to determine required buffer size. */
+ EXPECT_EQ_INT(cases[i].want_return,
+ strjoin(NULL, 0, cases[i].fields, cases[i].fields_num,
+ cases[i].separator));
+ }
+
+ return 0;
+}
+
+DEF_TEST(escape_slashes) {
+ struct {
+ char *str;
+ char *want;
+ } cases[] = {
+ {"foo/bar/baz", "foo_bar_baz"},
+ {"/like/a/path", "like_a_path"},
+ {"trailing/slash/", "trailing_slash_"},
+ {"foo//bar", "foo__bar"},
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ char buffer[32];
+
+ strncpy(buffer, cases[i].str, sizeof(buffer));
+ OK(escape_slashes(buffer, sizeof(buffer)) == 0);
+ EXPECT_EQ_STR(cases[i].want, buffer);
+ }
+
+ return 0;
+}
+
+DEF_TEST(escape_string) {
+ struct {
+ char *str;
+ char *want;
+ } cases[] = {
+ {"foobar", "foobar"},
+ {"f00bar", "f00bar"},
+ {"foo bar", "\"foo bar\""},
+ {"foo \"bar\"", "\"foo \\\"bar\\\"\""},
+ {"012345678901234", "012345678901234"},
+ {"012345 78901234", "\"012345 789012\""},
+ {"012345 78901\"34", "\"012345 78901\""},
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ char buffer[16];
+
+ strncpy(buffer, cases[i].str, sizeof(buffer));
+ OK(escape_string(buffer, sizeof(buffer)) == 0);
+ EXPECT_EQ_STR(cases[i].want, buffer);
+ }
+
+ return 0;
+}
+
+DEF_TEST(strunescape) {
+ char buffer[16];
+ int status;
+
+ strncpy(buffer, "foo\\tbar", sizeof(buffer));
+ status = strunescape(buffer, sizeof(buffer));
+ OK(status == 0);
+ EXPECT_EQ_STR("foo\tbar", buffer);
+
+ strncpy(buffer, "\\tfoo\\r\\n", sizeof(buffer));
+ status = strunescape(buffer, sizeof(buffer));
+ OK(status == 0);
+ EXPECT_EQ_STR("\tfoo\r\n", buffer);
+
+ strncpy(buffer, "With \\\"quotes\\\"", sizeof(buffer));
+ status = strunescape(buffer, sizeof(buffer));
+ OK(status == 0);
+ EXPECT_EQ_STR("With \"quotes\"", buffer);
+
+ /* Backslash before null byte */
+ strncpy(buffer, "\\tbackslash end\\", sizeof(buffer));
+ status = strunescape(buffer, sizeof(buffer));
+ OK(status != 0);
+ EXPECT_EQ_STR("\tbackslash end", buffer);
+ return 0;
+
+ /* Backslash at buffer end */
+ strncpy(buffer, "\\t3\\56", sizeof(buffer));
+ status = strunescape(buffer, 4);
+ OK(status != 0);
+ OK(buffer[0] == '\t');
+ OK(buffer[1] == '3');
+ OK(buffer[2] == 0);
+ OK(buffer[3] == 0);
+ OK(buffer[4] == '5');
+ OK(buffer[5] == '6');
+ OK(buffer[6] == '7');
+
+ return 0;
+}
+
+DEF_TEST(parse_values) {
+ struct {
+ char buffer[64];
+ int status;
+ gauge_t value;
+ } cases[] = {
+ {"1435044576:42", 0, 42.0}, {"1435044576:42:23", -1, NAN},
+ {"1435044576:U", 0, NAN}, {"N:12.3", 0, 12.3},
+ {"N:42.0:23", -1, NAN}, {"N:U", 0, NAN},
+ {"T:42.0", -1, NAN},
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ data_source_t dsrc = {
+ .name = "value", .type = DS_TYPE_GAUGE, .min = 0.0, .max = NAN,
+ };
+ data_set_t ds = {
+ .type = "example", .ds_num = 1, .ds = &dsrc,
+ };
+
+ value_t v = {
+ .gauge = NAN,
+ };
+ value_list_t vl = {
+ .values = &v,
+ .values_len = 1,
+ .time = 0,
+ .interval = 0,
+ .host = "example.com",
+ .plugin = "common_test",
+ .type = "example",
+ .meta = NULL,
+ };
+
+ int status = parse_values(cases[i].buffer, &vl, &ds);
+ EXPECT_EQ_INT(cases[i].status, status);
+ if (status != 0)
+ continue;
+
+ EXPECT_EQ_DOUBLE(cases[i].value, vl.values[0].gauge);
+ }
+
+ return 0;
+}
+
+DEF_TEST(value_to_rate) {
+ struct {
+ time_t t0;
+ time_t t1;
+ int ds_type;
+ value_t v0;
+ value_t v1;
+ gauge_t want;
+ } cases[] = {
+ {0, 10, DS_TYPE_DERIVE, {.derive = 0}, {.derive = 1000}, NAN},
+ {10, 20, DS_TYPE_DERIVE, {.derive = 1000}, {.derive = 2000}, 100.0},
+ {20, 30, DS_TYPE_DERIVE, {.derive = 2000}, {.derive = 1800}, -20.0},
+ {0, 10, DS_TYPE_COUNTER, {.counter = 0}, {.counter = 1000}, NAN},
+ {10, 20, DS_TYPE_COUNTER, {.counter = 1000}, {.counter = 5000}, 400.0},
+ /* 32bit wrap-around. */
+ {20,
+ 30,
+ DS_TYPE_COUNTER,
+ {.counter = 4294967238ULL},
+ {.counter = 42},
+ 10.0},
+ /* 64bit wrap-around. */
+ {30,
+ 40,
+ DS_TYPE_COUNTER,
+ {.counter = 18446744073709551558ULL},
+ {.counter = 42},
+ 10.0},
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ cdtime_t t0 = TIME_T_TO_CDTIME_T(cases[i].t0);
+ value_to_rate_state_t state = {
+ .last_value = cases[i].v0, .last_time = t0,
+ };
+ gauge_t got;
+
+ if (cases[i].t0 == 0) {
+ EXPECT_EQ_INT(EAGAIN,
+ value_to_rate(&got, cases[i].v1, cases[i].ds_type,
+ TIME_T_TO_CDTIME_T(cases[i].t1), &state));
+ continue;
+ }
+
+ EXPECT_EQ_INT(0, value_to_rate(&got, cases[i].v1, cases[i].ds_type,
+ TIME_T_TO_CDTIME_T(cases[i].t1), &state));
+ EXPECT_EQ_DOUBLE(cases[i].want, got);
+ }
+
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(sstrncpy);
+ RUN_TEST(sstrdup);
+ RUN_TEST(strsplit);
+ RUN_TEST(strjoin);
+ RUN_TEST(escape_slashes);
+ RUN_TEST(escape_string);
+ RUN_TEST(strunescape);
+ RUN_TEST(parse_values);
+ RUN_TEST(value_to_rate);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_config_cores.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#include "collectd.h"
+
+#include "utils/common/common.h"
+
+#include "utils/config_cores/config_cores.h"
+
+#define UTIL_NAME "utils_config_cores"
+
+#define MAX_SOCKETS 8
+#define MAX_SOCKET_CORES 64
+#define MAX_CORES (MAX_SOCKET_CORES * MAX_SOCKETS)
+
+static inline _Bool is_in_list(unsigned val, const unsigned *list, size_t len) {
+ for (size_t i = 0; i < len; i++)
+ if (list[i] == val)
+ return 1;
+ return 0;
+}
+
+static int str_to_uint(const char *s, unsigned *n) {
+ if (s == NULL || n == NULL)
+ return -EINVAL;
+ char *endptr = NULL;
+
+ *n = (unsigned)strtoul(s, &endptr, 0);
+ if (*s == '\0' || *endptr != '\0') {
+ ERROR(UTIL_NAME ": Failed to parse '%s' into unsigned number", s);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * NAME
+ * str_list_to_nums
+ *
+ * DESCRIPTION
+ * Converts string of characters representing list of numbers into array of
+ * numbers. Allowed formats are:
+ * 0,1,2,3
+ * 0-10,20-18
+ * 1,3,5-8,10,0x10-12
+ *
+ * Numbers can be in decimal or hexadecimal format.
+ *
+ * PARAMETERS
+ * `s' String representing list of unsigned numbers.
+ * `nums' Array to put converted numeric values into.
+ * `nums_len' Maximum number of elements that nums can accommodate.
+ *
+ * RETURN VALUE
+ * Number of elements placed into nums.
+ */
+static size_t str_list_to_nums(char *s, unsigned *nums, size_t nums_len) {
+ char *saveptr = NULL;
+ char *token;
+ size_t idx = 0;
+
+ while ((token = strtok_r(s, ",", &saveptr))) {
+ char *pos;
+ unsigned start, end = 0;
+ s = NULL;
+
+ while (isspace(*token))
+ token++;
+ if (*token == '\0')
+ continue;
+
+ pos = strchr(token, '-');
+ if (pos) {
+ *pos = '\0';
+ }
+
+ if (str_to_uint(token, &start))
+ return 0;
+
+ if (pos) {
+ if (str_to_uint(pos + 1, &end))
+ return 0;
+ } else {
+ end = start;
+ }
+
+ if (start > end) {
+ unsigned swap = start;
+ start = end;
+ end = swap;
+ }
+
+ for (unsigned i = start; i <= end; i++) {
+ if (is_in_list(i, nums, idx))
+ continue;
+ if (idx >= nums_len) {
+ WARNING(UTIL_NAME ": exceeded the cores number limit: %" PRIsz,
+ nums_len);
+ return idx;
+ }
+ nums[idx] = i;
+ idx++;
+ }
+ }
+ return idx;
+}
+
+/*
+ * NAME
+ * check_core_grouping
+ *
+ * DESCRIPTION
+ * Look for [...] brackets in *in string and if found copy the
+ * part between brackets into *out string and set grouped to 0.
+ * Otherwise grouped is set to 1 and input is copied without leading
+ * whitespaces.
+ *
+ * PARAMETERS
+ * `out' Output string to store result.
+ * `in' Input string to be parsed and copied.
+ * `out_size' Maximum number of elements that out can accommodate.
+ * `grouped' Set by function depending if cores should be grouped or not.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred.
+ */
+static int check_core_grouping(char *out, const char *in, size_t out_size,
+ _Bool *grouped) {
+ const char *start = in;
+ char *end;
+ while (isspace(*start))
+ ++start;
+ if (start[0] == '[') {
+ *grouped = 0;
+ ++start;
+ end = strchr(start, ']');
+ if (end == NULL) {
+ ERROR(UTIL_NAME ": Missing closing bracket ] in option %s.", in);
+ return -EINVAL;
+ }
+ if ((size_t)(end - start) >= out_size) {
+ ERROR(UTIL_NAME ": Out buffer is too small.");
+ return -EINVAL;
+ }
+ sstrncpy(out, start, end - start + 1);
+ DEBUG(UTIL_NAME ": Mask for individual (not aggregated) cores: %s", out);
+ } else {
+ *grouped = 1;
+ sstrncpy(out, start, out_size);
+ }
+ return 0;
+}
+
+int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl) {
+ if (ci == NULL || cgl == NULL)
+ return -EINVAL;
+ if (ci->values_num == 0 || ci->values_num > MAX_CORES)
+ return -EINVAL;
+ core_group_t cgroups[MAX_CORES] = {{0}};
+ size_t cg_idx = 0; /* index for cgroups array */
+ int ret = 0;
+
+ for (int i = 0; i < ci->values_num; i++) {
+ if (ci->values[i].type != OCONFIG_TYPE_STRING) {
+ WARNING(UTIL_NAME ": The %s option requires string arguments.", ci->key);
+ return -EINVAL;
+ }
+ }
+
+ if (ci->values_num == 1 && ci->values[0].value.string &&
+ strlen(ci->values[0].value.string) == 0)
+ return 0;
+
+ for (int i = 0; i < ci->values_num; i++) {
+ size_t n;
+ _Bool grouped = 1;
+ char str[DATA_MAX_NAME_LEN];
+ unsigned cores[MAX_CORES] = {0};
+
+ if (cg_idx >= STATIC_ARRAY_SIZE(cgroups)) {
+ ERROR(UTIL_NAME
+ ": Configuration exceeds maximum number of cores: %" PRIsz,
+ STATIC_ARRAY_SIZE(cgroups));
+ ret = -EINVAL;
+ goto parse_error;
+ }
+ if ((ci->values[i].value.string == NULL) ||
+ (strlen(ci->values[i].value.string) == 0)) {
+ ERROR(UTIL_NAME ": Failed to parse parameters for %s option.", ci->key);
+ ret = -EINVAL;
+ goto parse_error;
+ }
+
+ ret = check_core_grouping(str, ci->values[i].value.string, sizeof(str),
+ &grouped);
+ if (ret != 0) {
+ ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
+ ci->values[i].value.string);
+ goto parse_error;
+ }
+ n = str_list_to_nums(str, cores, STATIC_ARRAY_SIZE(cores));
+ if (n == 0) {
+ ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
+ ci->values[i].value.string);
+ ret = -EINVAL;
+ goto parse_error;
+ }
+
+ if (grouped) {
+ cgroups[cg_idx].desc = strdup(ci->values[i].value.string);
+ if (cgroups[cg_idx].desc == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate description.");
+ ret = -ENOMEM;
+ goto parse_error;
+ }
+
+ cgroups[cg_idx].cores = calloc(n, sizeof(*cgroups[cg_idx].cores));
+ if (cgroups[cg_idx].cores == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate cores for cgroup.");
+ ret = -ENOMEM;
+ goto parse_error;
+ }
+
+ for (size_t j = 0; j < n; j++)
+ cgroups[cg_idx].cores[j] = cores[j];
+
+ cgroups[cg_idx].num_cores = n;
+ cg_idx++;
+ } else {
+ for (size_t j = 0; j < n && cg_idx < STATIC_ARRAY_SIZE(cgroups); j++) {
+ char desc[DATA_MAX_NAME_LEN];
+ snprintf(desc, sizeof(desc), "%u", cores[j]);
+
+ cgroups[cg_idx].desc = strdup(desc);
+ if (cgroups[cg_idx].desc == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate desc for core %u.", cores[j]);
+ ret = -ENOMEM;
+ goto parse_error;
+ }
+
+ cgroups[cg_idx].cores = calloc(1, sizeof(*(cgroups[cg_idx].cores)));
+ if (cgroups[cg_idx].cores == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate cgroup for core %u.", cores[j]);
+ ret = -ENOMEM;
+ goto parse_error;
+ }
+ cgroups[cg_idx].num_cores = 1;
+ cgroups[cg_idx].cores[0] = cores[j];
+ cg_idx++;
+ }
+ }
+ }
+
+ cgl->cgroups = calloc(cg_idx, sizeof(*cgl->cgroups));
+ if (cgl->cgroups == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate core groups.");
+ ret = -ENOMEM;
+ goto parse_error;
+ }
+
+ cgl->num_cgroups = cg_idx;
+ for (size_t i = 0; i < cg_idx; i++)
+ cgl->cgroups[i] = cgroups[i];
+
+ return 0;
+
+parse_error:
+
+ cg_idx = 0;
+ while (cg_idx < STATIC_ARRAY_SIZE(cgroups) && cgroups[cg_idx].desc != NULL) {
+ sfree(cgroups[cg_idx].desc);
+ sfree(cgroups[cg_idx].cores);
+ cg_idx++;
+ }
+ return ret;
+}
+
+int config_cores_default(int num_cores, core_groups_list_t *cgl) {
+ if (cgl == NULL || num_cores < 0 || num_cores > MAX_CORES)
+ return -EINVAL;
+
+ cgl->cgroups = calloc(num_cores, sizeof(*(cgl->cgroups)));
+ if (cgl->cgroups == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate memory for core groups.");
+ return -ENOMEM;
+ }
+ cgl->num_cgroups = num_cores;
+
+ for (int i = 0; i < num_cores; i++) {
+ char desc[DATA_MAX_NAME_LEN];
+ snprintf(desc, sizeof(desc), "%d", i);
+
+ cgl->cgroups[i].cores = calloc(1, sizeof(*(cgl->cgroups[i].cores)));
+ if (cgl->cgroups[i].cores == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate default cores for cgroup %d.", i);
+ config_cores_cleanup(cgl);
+ return -ENOMEM;
+ }
+ cgl->cgroups[i].num_cores = 1;
+ cgl->cgroups[i].cores[0] = i;
+
+ cgl->cgroups[i].desc = strdup(desc);
+ if (cgl->cgroups[i].desc == NULL) {
+ ERROR(UTIL_NAME ": Failed to allocate description for cgroup %d.", i);
+ config_cores_cleanup(cgl);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+void config_cores_cleanup(core_groups_list_t *cgl) {
+ if (cgl == NULL)
+ return;
+ for (size_t i = 0; i < cgl->num_cgroups; i++) {
+ sfree(cgl->cgroups[i].desc);
+ sfree(cgl->cgroups[i].cores);
+ }
+ sfree(cgl->cgroups);
+ cgl->num_cgroups = 0;
+}
+
+int config_cores_cmp_cgroups(const core_group_t *cg_a,
+ const core_group_t *cg_b) {
+ size_t found = 0;
+
+ assert(cg_a != NULL);
+ assert(cg_b != NULL);
+
+ const size_t sz_a = cg_a->num_cores;
+ const size_t sz_b = cg_b->num_cores;
+ const unsigned *tab_a = cg_a->cores;
+ const unsigned *tab_b = cg_b->cores;
+
+ for (size_t i = 0; i < sz_a; i++)
+ if (is_in_list(tab_a[i], tab_b, sz_b))
+ found++;
+
+ /* if no cores are the same */
+ if (!found)
+ return 0;
+ /* if group contains same cores */
+ if (sz_a == sz_b && sz_b == found)
+ return 1;
+ /* if not all cores are the same */
+ return -1;
+}
--- /dev/null
+/**
+ * collectd - src/utils_config_cores.h
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#ifndef UTILS_CONFIG_CORES_H
+#define UTILS_CONFIG_CORES_H 1
+
+#include "configfile.h"
+
+#ifndef PRIsz
+#define PRIsz "zu"
+#endif /* PRIsz */
+
+struct core_group_s {
+ char *desc;
+ unsigned int *cores;
+ size_t num_cores;
+};
+typedef struct core_group_s core_group_t;
+
+struct core_groups_list_s {
+ core_group_t *cgroups;
+ size_t num_cgroups;
+};
+typedef struct core_groups_list_s core_groups_list_t;
+
+/*
+ * NAME
+ * config_cores_parse
+ *
+ * DESCRIPTION
+ * Convert strings from config item into list of core groups.
+ *
+ * PARAMETERS
+ * `ci' Pointer to config item.
+ * `cgl' Pointer to core groups list to be filled.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred.
+ *
+ * NOTES
+ * In case of an error, *cgl is not modified.
+ * Numbers can be in decimal or hexadecimal format.
+ * The memory allocated for *cgroups in list needs to be freed
+ * with config_cores_cleanup.
+ *
+ * EXAMPLES
+ * If config is "0-3" "[4-15]" it means that cores 0-3 are aggregated
+ * into one group and cores 4 to 15 are stored individualily in
+ * separate groups. Examples of allowed formats:
+ * "0,3,4" "10-15" - cores collected into two groups
+ * "0" "0x3" "7" - 3 cores, each in individual group
+ * "[32-63]" - 32 cores, each in individual group
+ *
+ * For empty string "" *cgl is not modified and zero is returned.
+ */
+int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl);
+
+/*
+ * NAME
+ * config_cores_default
+ *
+ * DESCRIPTION
+ * Set number of cores starting from zero into individual
+ * core groups in *cgl list.
+ *
+ * PARAMETERS
+ * `num_cores' Number of cores to be configured.
+ * `cgl' Pointer to core groups list.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred.
+ *
+ * NOTES
+ * The memory allocated for *cgroups in list needs to be freed
+ * with config_cores_cleanup. In case of error the memory is
+ * freed by the function itself.
+ */
+int config_cores_default(int num_cores, core_groups_list_t *cgl);
+
+/*
+ * NAME
+ * config_cores_cleanup
+ *
+ * DESCRIPTION
+ * Free the memory allocated for cgroups and set
+ * num_cgroups to zero.
+ *
+ * PARAMETERS
+ * `cgl' Pointer to core groups list.
+ */
+void config_cores_cleanup(core_groups_list_t *cgl);
+
+/*
+ * NAME
+ * config_cores_cmp_cgroups
+ *
+ * DESCRIPTION
+ * Function to compare cores in 2 core groups.
+ *
+ * PARAMETERS
+ * `cg_a' Pointer to core group a.
+ * `cg_b' Pointer to core group b.
+ *
+ * RETURN VALUE
+ * 1 if both groups contain the same cores
+ * 0 if none of their cores match
+ * -1 if some but not all cores match
+ */
+int config_cores_cmp_cgroups(const core_group_t *cg_a,
+ const core_group_t *cg_b);
+
+#endif /* UTILS_CONFIG_CORES_H */
--- /dev/null
+/**
+ * collectd - src/utils_config_cores_test.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/config_cores/config_cores.c" /* sic */
+
+oconfig_value_t test_cfg_values[] = {{{"0"}, OCONFIG_TYPE_STRING},
+ {{"1-2"}, OCONFIG_TYPE_STRING},
+ {{"[3-4]"}, OCONFIG_TYPE_STRING}};
+
+oconfig_item_t test_cfg = {
+ "Cores", test_cfg_values, STATIC_ARRAY_SIZE(test_cfg_values), NULL, NULL,
+ 0};
+
+static int compare_with_test_config(core_groups_list_t *cgl) {
+ if (cgl->num_cgroups == 4 && cgl->cgroups[0].num_cores == 1 &&
+ strcmp("0", cgl->cgroups[0].desc) == 0 && cgl->cgroups[0].cores[0] == 0 &&
+ cgl->cgroups[1].num_cores == 2 &&
+ strcmp("1-2", cgl->cgroups[1].desc) == 0 &&
+ cgl->cgroups[1].cores[0] == 1 && cgl->cgroups[1].cores[1] == 2 &&
+ cgl->cgroups[2].num_cores == 1 &&
+ strcmp("3", cgl->cgroups[2].desc) == 0 && cgl->cgroups[2].cores[0] == 3 &&
+ cgl->cgroups[3].num_cores == 1 &&
+ strcmp("4", cgl->cgroups[3].desc) == 0 && cgl->cgroups[3].cores[0] == 4)
+ return 0;
+
+ return -1;
+}
+
+DEF_TEST(string_to_uint) {
+ int ret = 0;
+ char *s = "13", *s1 = "0xd", *s2 = "g";
+ unsigned n = 0;
+
+ ret = str_to_uint(s, &n);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(13, n);
+
+ ret = str_to_uint(s1, &n);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(13, n);
+
+ ret = str_to_uint(s2, &n);
+ OK(ret < 0);
+
+ ret = str_to_uint(NULL, &n);
+ OK(ret < 0);
+ return 0;
+}
+
+DEF_TEST(cores_list_to_numbers) {
+ size_t n = 0;
+ unsigned nums[MAX_CORES];
+ char str[64] = "";
+
+ n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
+ EXPECT_EQ_INT(0, n);
+
+ strncpy(str, "1", STATIC_ARRAY_SIZE(str));
+ n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
+ EXPECT_EQ_INT(1, n);
+ EXPECT_EQ_INT(1, nums[0]);
+
+ strncpy(str, "0,2-3", STATIC_ARRAY_SIZE(str));
+ n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
+ EXPECT_EQ_INT(3, n);
+ EXPECT_EQ_INT(0, nums[0]);
+ EXPECT_EQ_INT(2, nums[1]);
+ EXPECT_EQ_INT(3, nums[2]);
+
+ strncpy(str, "11-0xa", STATIC_ARRAY_SIZE(str));
+ n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
+ EXPECT_EQ_INT(2, n);
+ EXPECT_EQ_INT(10, nums[0]);
+ EXPECT_EQ_INT(11, nums[1]);
+
+ snprintf(str, sizeof(str), "0-%d", (MAX_CORES - 1));
+ n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
+ EXPECT_EQ_INT(MAX_CORES, n);
+ EXPECT_EQ_INT(0, nums[0]);
+ EXPECT_EQ_INT(MAX_CORES - 1, nums[MAX_CORES - 1]);
+
+ /* Should return 0 for incorrect syntax. */
+ strncpy(str, "5g", STATIC_ARRAY_SIZE(str));
+ n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
+ EXPECT_EQ_INT(0, n);
+ return 0;
+}
+
+DEF_TEST(check_grouped_cores) {
+ int ret = 0;
+ _Bool grouped;
+ char src[64] = "[5-15]";
+ char dest[64];
+
+ ret = check_core_grouping(dest, src, sizeof(dest), &grouped);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(0, grouped);
+ EXPECT_EQ_STR("5-15", dest);
+
+ strncpy(src, " 5-15", STATIC_ARRAY_SIZE(src));
+ ret = check_core_grouping(dest, src, sizeof(dest), &grouped);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, grouped);
+ EXPECT_EQ_STR("5-15", dest);
+ return 0;
+}
+
+DEF_TEST(cores_option_parse) {
+ int ret = 0;
+ core_groups_list_t cgl = {0};
+
+ ret = config_cores_parse(&test_cfg, &cgl);
+ EXPECT_EQ_INT(0, ret);
+ CHECK_NOT_NULL(cgl.cgroups);
+ EXPECT_EQ_INT(0, compare_with_test_config(&cgl));
+
+ config_cores_cleanup(&cgl);
+ return 0;
+}
+
+DEF_TEST(cores_option_parse_fail) {
+ int ret = 0;
+ core_groups_list_t cgl = {0};
+ /* Wrong value, missing closing bracket ] */
+ oconfig_value_t values = {{"[0-15"}, OCONFIG_TYPE_STRING};
+ oconfig_item_t cfg = {"Cores", &values, 1, NULL, NULL, 0};
+
+ ret = config_cores_parse(&cfg, &cgl);
+ EXPECT_EQ_INT(-EINVAL, ret);
+ EXPECT_EQ_INT(0, cgl.num_cgroups);
+ OK(NULL == cgl.cgroups);
+ return 0;
+}
+
+DEF_TEST(cores_default_list) {
+ int ret = 0;
+ core_groups_list_t cgl = {0};
+
+ ret = config_cores_default(2, &cgl);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(2, cgl.num_cgroups);
+ CHECK_NOT_NULL(cgl.cgroups);
+
+ CHECK_NOT_NULL(cgl.cgroups[0].cores);
+ CHECK_NOT_NULL(cgl.cgroups[0].desc);
+ EXPECT_EQ_STR("0", cgl.cgroups[0].desc);
+ EXPECT_EQ_INT(1, cgl.cgroups[0].num_cores);
+ EXPECT_EQ_INT(0, cgl.cgroups[0].cores[0]);
+
+ CHECK_NOT_NULL(cgl.cgroups[1].cores);
+ CHECK_NOT_NULL(cgl.cgroups[1].desc);
+ EXPECT_EQ_STR("1", cgl.cgroups[1].desc);
+ EXPECT_EQ_INT(1, cgl.cgroups[1].num_cores);
+ EXPECT_EQ_INT(1, cgl.cgroups[1].cores[0]);
+
+ config_cores_cleanup(&cgl);
+ return 0;
+}
+
+DEF_TEST(cores_default_list_fail) {
+ int ret = 0;
+ core_groups_list_t cgl = {0};
+
+ ret = config_cores_default(-1, &cgl);
+ OK(ret < 0);
+ ret = config_cores_default(MAX_CORES + 1, &cgl);
+ OK(ret < 0);
+ ret = config_cores_default(1, NULL);
+ OK(ret < 0);
+ return 0;
+}
+
+DEF_TEST(cores_group_cleanup) {
+ core_groups_list_t cgl;
+ cgl.cgroups = calloc(1, sizeof(*cgl.cgroups));
+ CHECK_NOT_NULL(cgl.cgroups);
+ cgl.num_cgroups = 1;
+ cgl.cgroups[0].desc = strdup("1");
+ cgl.cgroups[0].cores = calloc(1, sizeof(*cgl.cgroups[0].cores));
+ CHECK_NOT_NULL(cgl.cgroups[0].cores);
+ cgl.cgroups[0].cores[0] = 1;
+ cgl.cgroups[0].num_cores = 1;
+
+ config_cores_cleanup(&cgl);
+ OK(NULL == cgl.cgroups);
+ EXPECT_EQ_INT(0, cgl.num_cgroups);
+ return 0;
+}
+
+DEF_TEST(cores_group_cmp) {
+ unsigned cores_mock[] = {0, 1, 2};
+ core_group_t group_mock = {"0,1,2", cores_mock, 3};
+ unsigned cores_mock_2[] = {2, 3};
+ core_group_t group_mock_2 = {"2,3", cores_mock_2, 2};
+
+ int ret = config_cores_cmp_cgroups(&group_mock, &group_mock);
+ EXPECT_EQ_INT(1, ret);
+
+ ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2);
+ EXPECT_EQ_INT(-1, ret);
+
+ cores_mock_2[0] = 4;
+ ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2);
+ EXPECT_EQ_INT(0, ret);
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(string_to_uint);
+ RUN_TEST(cores_list_to_numbers);
+ RUN_TEST(check_grouped_cores);
+
+ RUN_TEST(cores_group_cleanup);
+ RUN_TEST(cores_option_parse);
+ RUN_TEST(cores_option_parse_fail);
+ RUN_TEST(cores_default_list);
+ RUN_TEST(cores_default_list_fail);
+
+ RUN_TEST(cores_group_cmp);
+
+ END_TEST;
+}
--- /dev/null
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+uint32_t crc32_buffer(const unsigned char *, size_t);
+static unsigned int crc32_tab[] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL};
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+uint32_t crc32_buffer(const unsigned char *s, size_t len) {
+ uint32_t ret;
+
+ ret = 0;
+ for (size_t i = 0; i < len; i++)
+ ret = crc32_tab[(ret ^ s[i]) & 0xff] ^ (ret >> 8);
+ return ret;
+}
--- /dev/null
+/**
+ * collectd - src/utils_crc32.h
+ * Copyright (C) 2014 Pierre-Yves Ritschard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Pierre-Yves Ritschard <pyr at spootnik.org>
+ */
+
+#ifndef UTILS_CRC32_H
+#define UTILS_CRC32_H 1
+
+uint32_t crc32_buffer(const unsigned char *, size_t);
+
+#endif
--- /dev/null
+/**
+ * collectd - src/utils_curl_stats.c
+ * Copyright (C) 2015 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian Harl <sh@tokkee.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/common/common.h"
+#include "utils/curl_stats/curl_stats.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct curl_stats_s {
+ bool total_time;
+ bool namelookup_time;
+ bool connect_time;
+ bool pretransfer_time;
+ bool size_upload;
+ bool size_download;
+ bool speed_download;
+ bool speed_upload;
+ bool header_size;
+ bool request_size;
+ bool content_length_download;
+ bool content_length_upload;
+ bool starttransfer_time;
+ bool redirect_time;
+ bool redirect_count;
+ bool num_connects;
+ bool appconnect_time;
+};
+
+/*
+ * Private functions
+ */
+
+static int dispatch_gauge(CURL *curl, CURLINFO info, value_list_t *vl) {
+ CURLcode code;
+ value_t v;
+
+ code = curl_easy_getinfo(curl, info, &v.gauge);
+ if (code != CURLE_OK)
+ return -1;
+
+ vl->values = &v;
+ vl->values_len = 1;
+
+ return plugin_dispatch_values(vl);
+} /* dispatch_gauge */
+
+/* dispatch a speed, in bytes/second */
+static int dispatch_speed(CURL *curl, CURLINFO info, value_list_t *vl) {
+ CURLcode code;
+ value_t v;
+
+ code = curl_easy_getinfo(curl, info, &v.gauge);
+ if (code != CURLE_OK)
+ return -1;
+
+ v.gauge *= 8;
+
+ vl->values = &v;
+ vl->values_len = 1;
+
+ return plugin_dispatch_values(vl);
+} /* dispatch_speed */
+
+/* dispatch a size/count, reported as a long value */
+static int dispatch_size(CURL *curl, CURLINFO info, value_list_t *vl) {
+ CURLcode code;
+ value_t v;
+ long raw;
+
+ code = curl_easy_getinfo(curl, info, &raw);
+ if (code != CURLE_OK)
+ return -1;
+
+ v.gauge = (double)raw;
+
+ vl->values = &v;
+ vl->values_len = 1;
+
+ return plugin_dispatch_values(vl);
+} /* dispatch_size */
+
+static struct {
+ const char *name;
+ const char *config_key;
+ size_t offset;
+
+ int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
+ const char *type;
+ CURLINFO info;
+} field_specs[] = {
+#define SPEC(name, config_key, dispatcher, type, info) \
+ { #name, config_key, offsetof(curl_stats_t, name), dispatcher, type, info }
+
+ SPEC(total_time, "TotalTime", dispatch_gauge, "duration",
+ CURLINFO_TOTAL_TIME),
+ SPEC(namelookup_time, "NamelookupTime", dispatch_gauge, "duration",
+ CURLINFO_NAMELOOKUP_TIME),
+ SPEC(connect_time, "ConnectTime", dispatch_gauge, "duration",
+ CURLINFO_CONNECT_TIME),
+ SPEC(pretransfer_time, "PretransferTime", dispatch_gauge, "duration",
+ CURLINFO_PRETRANSFER_TIME),
+ SPEC(size_upload, "SizeUpload", dispatch_gauge, "bytes",
+ CURLINFO_SIZE_UPLOAD),
+ SPEC(size_download, "SizeDownload", dispatch_gauge, "bytes",
+ CURLINFO_SIZE_DOWNLOAD),
+ SPEC(speed_download, "SpeedDownload", dispatch_speed, "bitrate",
+ CURLINFO_SPEED_DOWNLOAD),
+ SPEC(speed_upload, "SpeedUpload", dispatch_speed, "bitrate",
+ CURLINFO_SPEED_UPLOAD),
+ SPEC(header_size, "HeaderSize", dispatch_size, "bytes",
+ CURLINFO_HEADER_SIZE),
+ SPEC(request_size, "RequestSize", dispatch_size, "bytes",
+ CURLINFO_REQUEST_SIZE),
+ SPEC(content_length_download, "ContentLengthDownload", dispatch_gauge,
+ "bytes", CURLINFO_CONTENT_LENGTH_DOWNLOAD),
+ SPEC(content_length_upload, "ContentLengthUpload", dispatch_gauge, "bytes",
+ CURLINFO_CONTENT_LENGTH_UPLOAD),
+ SPEC(starttransfer_time, "StarttransferTime", dispatch_gauge, "duration",
+ CURLINFO_STARTTRANSFER_TIME),
+ SPEC(redirect_time, "RedirectTime", dispatch_gauge, "duration",
+ CURLINFO_REDIRECT_TIME),
+ SPEC(redirect_count, "RedirectCount", dispatch_size, "count",
+ CURLINFO_REDIRECT_COUNT),
+ SPEC(num_connects, "NumConnects", dispatch_size, "count",
+ CURLINFO_NUM_CONNECTS),
+#ifdef HAVE_CURLINFO_APPCONNECT_TIME
+ SPEC(appconnect_time, "AppconnectTime", dispatch_gauge, "duration",
+ CURLINFO_APPCONNECT_TIME),
+#endif
+
+#undef SPEC
+};
+
+static void enable_field(curl_stats_t *s, size_t offset) {
+ *(bool *)((char *)s + offset) = true;
+} /* enable_field */
+
+static bool field_enabled(curl_stats_t *s, size_t offset) {
+ return *(bool *)((char *)s + offset);
+} /* field_enabled */
+
+/*
+ * Public API
+ */
+curl_stats_t *curl_stats_from_config(oconfig_item_t *ci) {
+ curl_stats_t *s;
+
+ if (ci == NULL)
+ return NULL;
+
+ s = calloc(1, sizeof(*s));
+ if (s == NULL)
+ return NULL;
+
+ for (int i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *c = ci->children + i;
+ size_t field;
+
+ bool enabled = 0;
+
+ for (field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) {
+ if (!strcasecmp(c->key, field_specs[field].config_key))
+ break;
+ if (!strcasecmp(c->key, field_specs[field].name))
+ break;
+ }
+ if (field >= STATIC_ARRAY_SIZE(field_specs)) {
+ ERROR("curl stats: Unknown field name %s", c->key);
+ free(s);
+ return NULL;
+ }
+
+ if (cf_util_get_boolean(c, &enabled) != 0) {
+ free(s);
+ return NULL;
+ }
+ if (enabled)
+ enable_field(s, field_specs[field].offset);
+ }
+
+ return s;
+} /* curl_stats_from_config */
+
+void curl_stats_destroy(curl_stats_t *s) {
+ if (s != NULL)
+ free(s);
+} /* curl_stats_destroy */
+
+int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname,
+ const char *plugin, const char *plugin_instance) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ if (s == NULL)
+ return 0;
+ if ((curl == NULL) || (plugin == NULL)) {
+ ERROR("curl stats: dispatch() called with missing arguments "
+ "(curl=%p; plugin=%s)",
+ curl, plugin == NULL ? "<NULL>" : plugin);
+ return -1;
+ }
+
+ if (hostname != NULL)
+ sstrncpy(vl.host, hostname, sizeof(vl.host));
+ sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
+ if (plugin_instance != NULL)
+ sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+
+ for (size_t field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) {
+ int status;
+
+ if (!field_enabled(s, field_specs[field].offset))
+ continue;
+
+ sstrncpy(vl.type, field_specs[field].type, sizeof(vl.type));
+ sstrncpy(vl.type_instance, field_specs[field].name,
+ sizeof(vl.type_instance));
+
+ vl.values = NULL;
+ vl.values_len = 0;
+ status = field_specs[field].dispatcher(curl, field_specs[field].info, &vl);
+ if (status < 0)
+ return status;
+ }
+
+ return 0;
+} /* curl_stats_dispatch */
--- /dev/null
+/**
+ * collectd - src/utils_curl_stats.h
+ * Copyright (C) 2015 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian Harl <sh@tokkee.org>
+ **/
+
+#ifndef UTILS_CURL_STATS_H
+#define UTILS_CURL_STATS_H 1
+
+#include "plugin.h"
+
+#include <curl/curl.h>
+
+struct curl_stats_s;
+typedef struct curl_stats_s curl_stats_t;
+
+/*
+ * curl_stats_from_config allocates and constructs a cURL statistics object
+ * from the specified configuration which is expected to be a single block of
+ * boolean options named after cURL information fields. The boolean value
+ * indicates whether to collect the respective information.
+ *
+ * See http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
+ */
+curl_stats_t *curl_stats_from_config(oconfig_item_t *ci);
+
+void curl_stats_destroy(curl_stats_t *s);
+
+/*
+ * curl_stats_dispatch dispatches performance values from the the specified
+ * cURL session to the daemon.
+ */
+int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname,
+ const char *plugin, const char *plugin_instance);
+
+#endif /* UTILS_CURL_STATS_H */
--- /dev/null
+/**
+ * collectd - src/utils_db_query.c
+ * Copyright (C) 2008,2009 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/db_query/db_query.h"
+
+/*
+ * Data types
+ */
+struct udb_result_s; /* {{{ */
+typedef struct udb_result_s udb_result_t;
+struct udb_result_s {
+ char *type;
+ char *instance_prefix;
+ char **instances;
+ size_t instances_num;
+ char **values;
+ size_t values_num;
+ char **metadata;
+ size_t metadata_num;
+
+ udb_result_t *next;
+}; /* }}} */
+
+struct udb_query_s /* {{{ */
+{
+ char *name;
+ char *statement;
+ void *user_data;
+ char *plugin_instance_from;
+
+ unsigned int min_version;
+ unsigned int max_version;
+
+ udb_result_t *results;
+}; /* }}} */
+
+struct udb_result_preparation_area_s /* {{{ */
+{
+ const data_set_t *ds;
+ size_t *instances_pos;
+ size_t *values_pos;
+ size_t *metadata_pos;
+ char **instances_buffer;
+ char **values_buffer;
+ char **metadata_buffer;
+ char *plugin_instance;
+
+ struct udb_result_preparation_area_s *next;
+}; /* }}} */
+typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
+
+struct udb_query_preparation_area_s /* {{{ */
+{
+ size_t column_num;
+ size_t plugin_instance_pos;
+ char *host;
+ char *plugin;
+ char *db_name;
+
+ udb_result_preparation_area_t *result_prep_areas;
+}; /* }}} */
+
+/*
+ * Config Private functions
+ */
+static int udb_config_add_string(char ***ret_array, /* {{{ */
+ size_t *ret_array_len, oconfig_item_t *ci) {
+ char **array;
+ size_t array_len;
+
+ if (ci->values_num < 1) {
+ P_WARNING("The `%s' config option "
+ "needs at least one argument.",
+ ci->key);
+ return -1;
+ }
+
+ for (int i = 0; i < ci->values_num; i++) {
+ if (ci->values[i].type != OCONFIG_TYPE_STRING) {
+ P_WARNING("Argument %i to the `%s' option "
+ "is not a string.",
+ i + 1, ci->key);
+ return -1;
+ }
+ }
+
+ array_len = *ret_array_len;
+ array = realloc(*ret_array, sizeof(char *) * (array_len + ci->values_num));
+ if (array == NULL) {
+ P_ERROR("udb_config_add_string: realloc failed.");
+ return -1;
+ }
+ *ret_array = array;
+
+ for (int i = 0; i < ci->values_num; i++) {
+ array[array_len] = strdup(ci->values[i].value.string);
+ if (array[array_len] == NULL) {
+ P_ERROR("udb_config_add_string: strdup failed.");
+ *ret_array_len = array_len;
+ return -1;
+ }
+ array_len++;
+ }
+
+ *ret_array_len = array_len;
+ return 0;
+} /* }}} int udb_config_add_string */
+
+static int udb_config_set_uint(unsigned int *ret_value, /* {{{ */
+ oconfig_item_t *ci) {
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
+ P_WARNING("The `%s' config option "
+ "needs exactly one numeric argument.",
+ ci->key);
+ return -1;
+ }
+
+ double tmp = ci->values[0].value.number;
+ if ((tmp < 0.0) || (tmp > ((double)UINT_MAX))) {
+ P_WARNING("The value given for the `%s` option is out of range.", ci->key);
+ return -ERANGE;
+ }
+
+ *ret_value = (unsigned int)(tmp + .5);
+ return 0;
+} /* }}} int udb_config_set_uint */
+
+/*
+ * Result private functions
+ */
+static int udb_result_submit(udb_result_t *r, /* {{{ */
+ udb_result_preparation_area_t *r_area,
+ udb_query_t const *q,
+ udb_query_preparation_area_t *q_area) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ assert(r != NULL);
+ assert(r_area->ds != NULL);
+ assert(((size_t)r_area->ds->ds_num) == r->values_num);
+ assert(r->values_num > 0);
+
+ vl.values = calloc(r->values_num, sizeof(*vl.values));
+ if (vl.values == NULL) {
+ P_ERROR("udb_result_submit: calloc failed.");
+ return -1;
+ }
+ vl.values_len = r_area->ds->ds_num;
+
+ for (size_t i = 0; i < r->values_num; i++) {
+ char *value_str = r_area->values_buffer[i];
+
+ if (0 != parse_value(value_str, &vl.values[i], r_area->ds->ds[i].type)) {
+ P_ERROR("udb_result_submit: Parsing `%s' as %s failed.", value_str,
+ DS_TYPE_TO_STRING(r_area->ds->ds[i].type));
+ errno = EINVAL;
+ free(vl.values);
+ return -1;
+ }
+ }
+
+ sstrncpy(vl.host, q_area->host, sizeof(vl.host));
+ sstrncpy(vl.plugin, q_area->plugin, sizeof(vl.plugin));
+ sstrncpy(vl.type, r->type, sizeof(vl.type));
+
+ /* Set vl.plugin_instance */
+ if (q->plugin_instance_from != NULL) {
+ sstrncpy(vl.plugin_instance, r_area->plugin_instance,
+ sizeof(vl.plugin_instance));
+ } else {
+ sstrncpy(vl.plugin_instance, q_area->db_name, sizeof(vl.plugin_instance));
+ }
+
+ /* Set vl.type_instance {{{ */
+ if (r->instances_num == 0) {
+ if (r->instance_prefix == NULL)
+ vl.type_instance[0] = 0;
+ else
+ sstrncpy(vl.type_instance, r->instance_prefix, sizeof(vl.type_instance));
+ } else /* if ((r->instances_num > 0) */
+ {
+ if (r->instance_prefix == NULL) {
+ int status = strjoin(vl.type_instance, sizeof(vl.type_instance),
+ r_area->instances_buffer, r->instances_num, "-");
+ if (status < 0) {
+ P_ERROR(
+ "udb_result_submit: creating type_instance failed with status %d.",
+ status);
+ return status;
+ }
+ } else {
+ char tmp[DATA_MAX_NAME_LEN];
+
+ int status = strjoin(tmp, sizeof(tmp), r_area->instances_buffer,
+ r->instances_num, "-");
+ if (status < 0) {
+ P_ERROR(
+ "udb_result_submit: creating type_instance failed with status %d.",
+ status);
+ return status;
+ }
+ tmp[sizeof(tmp) - 1] = '\0';
+
+ snprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s",
+ r->instance_prefix, tmp);
+ }
+ }
+ vl.type_instance[sizeof(vl.type_instance) - 1] = '\0';
+ /* }}} */
+
+ /* Annotate meta data. {{{ */
+ if (r->metadata_num > 0) {
+ vl.meta = meta_data_create();
+ if (vl.meta == NULL) {
+ P_ERROR("udb_result_submit: meta_data_create failed.");
+ free(vl.values);
+ return -ENOMEM;
+ }
+
+ for (size_t i = 0; i < r->metadata_num; i++) {
+ int status = meta_data_add_string(vl.meta, r->metadata[i],
+ r_area->metadata_buffer[i]);
+ if (status != 0) {
+ P_ERROR("udb_result_submit: meta_data_add_string failed.");
+ meta_data_destroy(vl.meta);
+ vl.meta = NULL;
+ free(vl.values);
+ return status;
+ }
+ }
+ }
+ /* }}} */
+
+ plugin_dispatch_values(&vl);
+
+ if (r->metadata_num > 0) {
+ meta_data_destroy(vl.meta);
+ vl.meta = NULL;
+ }
+ sfree(vl.values);
+ return 0;
+} /* }}} void udb_result_submit */
+
+static void udb_result_finish_result(udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area) {
+ if ((r == NULL) || (prep_area == NULL))
+ return;
+
+ prep_area->ds = NULL;
+ sfree(prep_area->instances_pos);
+ sfree(prep_area->values_pos);
+ sfree(prep_area->metadata_pos);
+ sfree(prep_area->instances_buffer);
+ sfree(prep_area->values_buffer);
+ sfree(prep_area->metadata_buffer);
+} /* }}} void udb_result_finish_result */
+
+static int udb_result_handle_result(udb_result_t *r, /* {{{ */
+ udb_query_preparation_area_t *q_area,
+ udb_result_preparation_area_t *r_area,
+ udb_query_t const *q,
+ char **column_values) {
+ assert(r && q_area && r_area);
+
+ for (size_t i = 0; i < r->instances_num; i++)
+ r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
+
+ for (size_t i = 0; i < r->values_num; i++)
+ r_area->values_buffer[i] = column_values[r_area->values_pos[i]];
+
+ for (size_t i = 0; i < r->metadata_num; i++)
+ r_area->metadata_buffer[i] = column_values[r_area->metadata_pos[i]];
+
+ if (q->plugin_instance_from)
+ r_area->plugin_instance = column_values[q_area->plugin_instance_pos];
+
+ return udb_result_submit(r, r_area, q, q_area);
+} /* }}} int udb_result_handle_result */
+
+static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area,
+ char **column_names, size_t column_num) {
+ if ((r == NULL) || (prep_area == NULL))
+ return -EINVAL;
+
+#if COLLECT_DEBUG
+ assert(prep_area->ds == NULL);
+ assert(prep_area->instances_pos == NULL);
+ assert(prep_area->values_pos == NULL);
+ assert(prep_area->metadata_pos == NULL);
+ assert(prep_area->instances_buffer == NULL);
+ assert(prep_area->values_buffer == NULL);
+ assert(prep_area->metadata_buffer == NULL);
+#endif
+
+#define BAIL_OUT(status) \
+ udb_result_finish_result(r, prep_area); \
+ return (status)
+
+ /* Read `ds' and check number of values {{{ */
+ prep_area->ds = plugin_get_ds(r->type);
+ if (prep_area->ds == NULL) {
+ P_ERROR("udb_result_prepare_result: Type `%s' is not "
+ "known by the daemon. See types.db(5) for details.",
+ r->type);
+ BAIL_OUT(-1);
+ }
+
+ if (prep_area->ds->ds_num != r->values_num) {
+ P_ERROR("udb_result_prepare_result: The type `%s' "
+ "requires exactly %" PRIsz
+ " value%s, but the configuration specifies %" PRIsz ".",
+ r->type, prep_area->ds->ds_num,
+ (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num);
+ BAIL_OUT(-1);
+ }
+ /* }}} */
+
+ /* Allocate r->instances_pos, r->values_pos, r->metadata_post,
+ * r->instances_buffer, r->values_buffer, and r->metadata_buffer {{{ */
+ if (r->instances_num > 0) {
+ prep_area->instances_pos =
+ calloc(r->instances_num, sizeof(*prep_area->instances_pos));
+ if (prep_area->instances_pos == NULL) {
+ P_ERROR("udb_result_prepare_result: calloc failed.");
+ BAIL_OUT(-ENOMEM);
+ }
+
+ prep_area->instances_buffer =
+ calloc(r->instances_num, sizeof(*prep_area->instances_buffer));
+ if (prep_area->instances_buffer == NULL) {
+ P_ERROR("udb_result_prepare_result: calloc failed.");
+ BAIL_OUT(-ENOMEM);
+ }
+ } /* if (r->instances_num > 0) */
+
+ prep_area->values_pos = calloc(r->values_num, sizeof(*prep_area->values_pos));
+ if (prep_area->values_pos == NULL) {
+ P_ERROR("udb_result_prepare_result: calloc failed.");
+ BAIL_OUT(-ENOMEM);
+ }
+
+ prep_area->values_buffer =
+ calloc(r->values_num, sizeof(*prep_area->values_buffer));
+ if (prep_area->values_buffer == NULL) {
+ P_ERROR("udb_result_prepare_result: calloc failed.");
+ BAIL_OUT(-ENOMEM);
+ }
+
+ prep_area->metadata_pos =
+ calloc(r->metadata_num, sizeof(*prep_area->metadata_pos));
+ if (prep_area->metadata_pos == NULL) {
+ P_ERROR("udb_result_prepare_result: calloc failed.");
+ BAIL_OUT(-ENOMEM);
+ }
+
+ prep_area->metadata_buffer =
+ calloc(r->metadata_num, sizeof(*prep_area->metadata_buffer));
+ if (prep_area->metadata_buffer == NULL) {
+ P_ERROR("udb_result_prepare_result: calloc failed.");
+ BAIL_OUT(-ENOMEM);
+ }
+
+ /* }}} */
+
+ /* Determine the position of the plugin instance column {{{ */
+ for (size_t i = 0; i < r->instances_num; i++) {
+ size_t j;
+
+ for (j = 0; j < column_num; j++) {
+ if (strcasecmp(r->instances[i], column_names[j]) == 0) {
+ prep_area->instances_pos[i] = j;
+ break;
+ }
+ }
+
+ if (j >= column_num) {
+ P_ERROR("udb_result_prepare_result: "
+ "Column `%s' could not be found.",
+ r->instances[i]);
+ BAIL_OUT(-ENOENT);
+ }
+ } /* }}} for (i = 0; i < r->instances_num; i++) */
+
+ /* Determine the position of the value columns {{{ */
+ for (size_t i = 0; i < r->values_num; i++) {
+ size_t j;
+
+ for (j = 0; j < column_num; j++) {
+ if (strcasecmp(r->values[i], column_names[j]) == 0) {
+ prep_area->values_pos[i] = j;
+ break;
+ }
+ }
+
+ if (j >= column_num) {
+ P_ERROR("udb_result_prepare_result: "
+ "Column `%s' could not be found.",
+ r->values[i]);
+ BAIL_OUT(-ENOENT);
+ }
+ } /* }}} for (i = 0; i < r->values_num; i++) */
+
+ /* Determine the position of the metadata columns {{{ */
+ for (size_t i = 0; i < r->metadata_num; i++) {
+ size_t j;
+
+ for (j = 0; j < column_num; j++) {
+ if (strcasecmp(r->metadata[i], column_names[j]) == 0) {
+ prep_area->metadata_pos[i] = j;
+ break;
+ }
+ }
+
+ if (j >= column_num) {
+ P_ERROR("udb_result_prepare_result: "
+ "Metadata column `%s' could not be found.",
+ r->values[i]);
+ BAIL_OUT(-ENOENT);
+ }
+ } /* }}} for (i = 0; i < r->metadata_num; i++) */
+
+#undef BAIL_OUT
+ return 0;
+} /* }}} int udb_result_prepare_result */
+
+static void udb_result_free(udb_result_t *r) /* {{{ */
+{
+ if (r == NULL)
+ return;
+
+ sfree(r->type);
+ sfree(r->instance_prefix);
+
+ for (size_t i = 0; i < r->instances_num; i++)
+ sfree(r->instances[i]);
+ sfree(r->instances);
+
+ for (size_t i = 0; i < r->values_num; i++)
+ sfree(r->values[i]);
+ sfree(r->values);
+
+ for (size_t i = 0; i < r->metadata_num; i++)
+ sfree(r->metadata[i]);
+ sfree(r->metadata);
+
+ udb_result_free(r->next);
+
+ sfree(r);
+} /* }}} void udb_result_free */
+
+static int udb_result_create(const char *query_name, /* {{{ */
+ udb_result_t **r_head, oconfig_item_t *ci) {
+ udb_result_t *r;
+ int status;
+
+ if (ci->values_num != 0) {
+ P_WARNING("The `Result' block doesn't accept "
+ "any arguments. Ignoring %i argument%s.",
+ ci->values_num, (ci->values_num == 1) ? "" : "s");
+ }
+
+ r = calloc(1, sizeof(*r));
+ if (r == NULL) {
+ P_ERROR("udb_result_create: calloc failed.");
+ return -1;
+ }
+ r->type = NULL;
+ r->instance_prefix = NULL;
+ r->instances = NULL;
+ r->values = NULL;
+ r->metadata = NULL;
+ r->next = NULL;
+
+ /* Fill the `udb_result_t' structure.. */
+ status = 0;
+ for (int 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, &r->type);
+ else if (strcasecmp("InstancePrefix", child->key) == 0)
+ status = cf_util_get_string(child, &r->instance_prefix);
+ else if (strcasecmp("InstancesFrom", child->key) == 0)
+ status = udb_config_add_string(&r->instances, &r->instances_num, child);
+ else if (strcasecmp("ValuesFrom", child->key) == 0)
+ status = udb_config_add_string(&r->values, &r->values_num, child);
+ else if (strcasecmp("MetadataFrom", child->key) == 0)
+ status = udb_config_add_string(&r->metadata, &r->metadata_num, child);
+ else {
+ P_WARNING("Query `%s': Option `%s' not allowed here.", query_name,
+ child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ /* Check that all necessary options have been given. */
+ while (status == 0) {
+ if (r->type == NULL) {
+ P_WARNING("udb_result_create: `Type' not given for "
+ "result in query `%s'",
+ query_name);
+ status = -1;
+ }
+ if (r->values == NULL) {
+ P_WARNING("udb_result_create: `ValuesFrom' not given for "
+ "result in query `%s'",
+ query_name);
+ status = -1;
+ }
+
+ break;
+ } /* while (status == 0) */
+
+ if (status != 0) {
+ udb_result_free(r);
+ return -1;
+ }
+
+ /* If all went well, add this result to the list of results. */
+ if (*r_head == NULL) {
+ *r_head = r;
+ } else {
+ udb_result_t *last;
+
+ last = *r_head;
+ while (last->next != NULL)
+ last = last->next;
+
+ last->next = r;
+ }
+
+ return 0;
+} /* }}} int udb_result_create */
+
+/*
+ * Query private functions
+ */
+static void udb_query_free_one(udb_query_t *q) /* {{{ */
+{
+ if (q == NULL)
+ return;
+
+ sfree(q->name);
+ sfree(q->statement);
+ sfree(q->plugin_instance_from);
+
+ udb_result_free(q->results);
+
+ sfree(q);
+} /* }}} void udb_query_free_one */
+
+/*
+ * Query public functions
+ */
+int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
+ size_t *ret_query_list_len, oconfig_item_t *ci,
+ udb_query_create_callback_t cb) {
+ udb_query_t **query_list;
+ size_t query_list_len;
+
+ udb_query_t *q;
+ int status;
+
+ if ((ret_query_list == NULL) || (ret_query_list_len == NULL))
+ return -EINVAL;
+ query_list = *ret_query_list;
+ query_list_len = *ret_query_list_len;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+ P_WARNING("udb_result_create: The `Query' block "
+ "needs exactly one string argument.");
+ return -1;
+ }
+
+ q = calloc(1, sizeof(*q));
+ if (q == NULL) {
+ P_ERROR("udb_query_create: calloc failed.");
+ return -1;
+ }
+ q->min_version = 0;
+ q->max_version = UINT_MAX;
+ q->statement = NULL;
+ q->results = NULL;
+ q->plugin_instance_from = NULL;
+
+ status = cf_util_get_string(ci, &q->name);
+ if (status != 0) {
+ sfree(q);
+ return status;
+ }
+
+ /* Fill the `udb_query_t' structure.. */
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Statement", child->key) == 0)
+ status = cf_util_get_string(child, &q->statement);
+ else if (strcasecmp("Result", child->key) == 0)
+ status = udb_result_create(q->name, &q->results, child);
+ else if (strcasecmp("MinVersion", child->key) == 0)
+ status = udb_config_set_uint(&q->min_version, child);
+ else if (strcasecmp("MaxVersion", child->key) == 0)
+ status = udb_config_set_uint(&q->max_version, child);
+ else if (strcasecmp("PluginInstanceFrom", child->key) == 0)
+ status = cf_util_get_string(child, &q->plugin_instance_from);
+
+ /* Call custom callbacks */
+ else if (cb != NULL) {
+ status = (*cb)(q, child);
+ if (status != 0) {
+ P_WARNING("The configuration callback failed "
+ "to handle `%s'.",
+ child->key);
+ }
+ } else {
+ P_WARNING("Query `%s': Option `%s' not allowed here.", q->name,
+ child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ /* Check that all necessary options have been given. */
+ if (status == 0) {
+ if (q->statement == NULL) {
+ P_WARNING("Query `%s': No `Statement' given.", q->name);
+ status = -1;
+ }
+ if (q->results == NULL) {
+ P_WARNING("Query `%s': No (valid) `Result' block given.", q->name);
+ status = -1;
+ }
+ } /* if (status == 0) */
+
+ /* If all went well, add this query to the list of queries within the
+ * database structure. */
+ if (status == 0) {
+ udb_query_t **temp;
+
+ temp = realloc(query_list, sizeof(*query_list) * (query_list_len + 1));
+ if (temp == NULL) {
+ P_ERROR("udb_query_create: realloc failed");
+ status = -1;
+ } else {
+ query_list = temp;
+ query_list[query_list_len] = q;
+ query_list_len++;
+ }
+ }
+
+ if (status != 0) {
+ udb_query_free_one(q);
+ return -1;
+ }
+
+ *ret_query_list = query_list;
+ *ret_query_list_len = query_list_len;
+
+ return 0;
+} /* }}} int udb_query_create */
+
+void udb_query_free(udb_query_t **query_list, size_t query_list_len) /* {{{ */
+{
+ if (query_list == NULL)
+ return;
+
+ for (size_t i = 0; i < query_list_len; i++)
+ udb_query_free_one(query_list[i]);
+
+ sfree(query_list);
+} /* }}} void udb_query_free */
+
+int udb_query_pick_from_list_by_name(const char *name, /* {{{ */
+ udb_query_t **src_list,
+ size_t src_list_len,
+ udb_query_t ***dst_list,
+ size_t *dst_list_len) {
+ int num_added;
+
+ if ((name == NULL) || (src_list == NULL) || (dst_list == NULL) ||
+ (dst_list_len == NULL)) {
+ P_ERROR("udb_query_pick_from_list_by_name: "
+ "Invalid argument.");
+ return -EINVAL;
+ }
+
+ num_added = 0;
+ for (size_t i = 0; i < src_list_len; i++) {
+ udb_query_t **tmp_list;
+ size_t tmp_list_len;
+
+ if (strcasecmp(name, src_list[i]->name) != 0)
+ continue;
+
+ tmp_list_len = *dst_list_len;
+ tmp_list = realloc(*dst_list, (tmp_list_len + 1) * sizeof(udb_query_t *));
+ if (tmp_list == NULL) {
+ P_ERROR("udb_query_pick_from_list_by_name: realloc failed.");
+ return -ENOMEM;
+ }
+
+ tmp_list[tmp_list_len] = src_list[i];
+ tmp_list_len++;
+
+ *dst_list = tmp_list;
+ *dst_list_len = tmp_list_len;
+
+ num_added++;
+ } /* for (i = 0; i < src_list_len; i++) */
+
+ if (num_added <= 0) {
+ P_ERROR("Cannot find query `%s'. Make sure the <Query> "
+ "block is above the database definition!",
+ name);
+ return -ENOENT;
+ } else {
+ DEBUG("Added %i versions of query `%s'.", num_added, name);
+ }
+
+ return 0;
+} /* }}} int udb_query_pick_from_list_by_name */
+
+int udb_query_pick_from_list(oconfig_item_t *ci, /* {{{ */
+ udb_query_t **src_list, size_t src_list_len,
+ udb_query_t ***dst_list, size_t *dst_list_len) {
+ const char *name;
+
+ if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) ||
+ (dst_list_len == NULL)) {
+ P_ERROR("udb_query_pick_from_list: "
+ "Invalid argument.");
+ return -EINVAL;
+ }
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+ P_ERROR("The `%s' config option "
+ "needs exactly one string argument.",
+ ci->key);
+ return -1;
+ }
+ name = ci->values[0].value.string;
+
+ return udb_query_pick_from_list_by_name(name, src_list, src_list_len,
+ dst_list, dst_list_len);
+} /* }}} int udb_query_pick_from_list */
+
+const char *udb_query_get_name(udb_query_t *q) /* {{{ */
+{
+ if (q == NULL)
+ return NULL;
+
+ return q->name;
+} /* }}} const char *udb_query_get_name */
+
+const char *udb_query_get_statement(udb_query_t *q) /* {{{ */
+{
+ if (q == NULL)
+ return NULL;
+
+ return q->statement;
+} /* }}} const char *udb_query_get_statement */
+
+void udb_query_set_user_data(udb_query_t *q, void *user_data) /* {{{ */
+{
+ if (q == NULL)
+ return;
+
+ q->user_data = user_data;
+} /* }}} void udb_query_set_user_data */
+
+void *udb_query_get_user_data(udb_query_t *q) /* {{{ */
+{
+ if (q == NULL)
+ return NULL;
+
+ return q->user_data;
+} /* }}} void *udb_query_get_user_data */
+
+int udb_query_check_version(udb_query_t *q, unsigned int version) /* {{{ */
+{
+ if (q == NULL)
+ return -EINVAL;
+
+ if ((version < q->min_version) || (version > q->max_version))
+ return 0;
+
+ return 1;
+} /* }}} int udb_query_check_version */
+
+void udb_query_finish_result(udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area) {
+ udb_result_preparation_area_t *r_area;
+ udb_result_t *r;
+
+ if ((q == NULL) || (prep_area == NULL))
+ return;
+
+ prep_area->column_num = 0;
+ sfree(prep_area->host);
+ sfree(prep_area->plugin);
+ sfree(prep_area->db_name);
+
+ for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
+ r = r->next, r_area = r_area->next) {
+ /* this may happen during error conditions of the caller */
+ if (r_area == NULL)
+ break;
+ udb_result_finish_result(r, r_area);
+ }
+} /* }}} void udb_query_finish_result */
+
+int udb_query_handle_result(udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area,
+ char **column_values) {
+ udb_result_preparation_area_t *r_area;
+ udb_result_t *r;
+ int success;
+ int status;
+
+ if ((q == NULL) || (prep_area == NULL))
+ return -EINVAL;
+
+ if ((prep_area->column_num < 1) || (prep_area->host == NULL) ||
+ (prep_area->plugin == NULL) || (prep_area->db_name == NULL)) {
+ P_ERROR("Query `%s': Query is not prepared; "
+ "can't handle result.",
+ q->name);
+ return -EINVAL;
+ }
+
+#if defined(COLLECT_DEBUG) && COLLECT_DEBUG /* {{{ */
+ do {
+ for (size_t i = 0; i < prep_area->column_num; i++) {
+ DEBUG("udb_query_handle_result (%s, %s): "
+ "column[%" PRIsz "] = %s;",
+ prep_area->db_name, q->name, i, column_values[i]);
+ }
+ } while (0);
+#endif /* }}} */
+
+ success = 0;
+ for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
+ r = r->next, r_area = r_area->next) {
+ status = udb_result_handle_result(r, prep_area, r_area, q, column_values);
+ if (status == 0)
+ success++;
+ }
+
+ if (success == 0) {
+ P_ERROR("udb_query_handle_result (%s, %s): "
+ "All results failed.",
+ prep_area->db_name, q->name);
+ return -1;
+ }
+
+ return 0;
+} /* }}} int udb_query_handle_result */
+
+int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area,
+ const char *host, const char *plugin,
+ const char *db_name, char **column_names,
+ size_t column_num) {
+ udb_result_preparation_area_t *r_area;
+ udb_result_t *r;
+ int status;
+
+ if ((q == NULL) || (prep_area == NULL))
+ return -EINVAL;
+
+#if COLLECT_DEBUG
+ assert(prep_area->column_num == 0);
+ assert(prep_area->host == NULL);
+ assert(prep_area->plugin == NULL);
+ assert(prep_area->db_name == NULL);
+#endif
+
+ prep_area->column_num = column_num;
+ prep_area->host = strdup(host);
+ prep_area->plugin = strdup(plugin);
+ prep_area->db_name = strdup(db_name);
+
+ if ((prep_area->host == NULL) || (prep_area->plugin == NULL) ||
+ (prep_area->db_name == NULL)) {
+ P_ERROR("Query `%s': Prepare failed: Out of memory.", q->name);
+ udb_query_finish_result(q, prep_area);
+ return -ENOMEM;
+ }
+
+#if defined(COLLECT_DEBUG) && COLLECT_DEBUG
+ do {
+ for (size_t i = 0; i < column_num; i++) {
+ DEBUG("udb_query_prepare_result: "
+ "query = %s; column[%" PRIsz "] = %s;",
+ q->name, i, column_names[i]);
+ }
+ } while (0);
+#endif
+
+ /* Determine the position of the PluginInstance column {{{ */
+ if (q->plugin_instance_from != NULL) {
+ size_t i;
+
+ for (i = 0; i < column_num; i++) {
+ if (strcasecmp(q->plugin_instance_from, column_names[i]) == 0) {
+ prep_area->plugin_instance_pos = i;
+ break;
+ }
+ }
+
+ if (i >= column_num) {
+ P_ERROR("udb_query_prepare_result: "
+ "Column `%s' from `PluginInstanceFrom' could not be found.",
+ q->plugin_instance_from);
+ udb_query_finish_result(q, prep_area);
+ return -ENOENT;
+ }
+ }
+ /* }}} */
+
+ for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
+ r = r->next, r_area = r_area->next) {
+ if (!r_area) {
+ P_ERROR("Query `%s': Invalid number of result "
+ "preparation areas.",
+ q->name);
+ udb_query_finish_result(q, prep_area);
+ return -EINVAL;
+ }
+
+ status = udb_result_prepare_result(r, r_area, column_names, column_num);
+ if (status != 0) {
+ udb_query_finish_result(q, prep_area);
+ return status;
+ }
+ }
+
+ return 0;
+} /* }}} int udb_query_prepare_result */
+
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area(udb_query_t *q) /* {{{ */
+{
+ udb_query_preparation_area_t *q_area;
+ udb_result_preparation_area_t **next_r_area;
+ udb_result_t *r;
+
+ q_area = calloc(1, sizeof(*q_area));
+ if (q_area == NULL)
+ return NULL;
+
+ next_r_area = &q_area->result_prep_areas;
+ for (r = q->results; r != NULL; r = r->next) {
+ udb_result_preparation_area_t *r_area;
+
+ r_area = calloc(1, sizeof(*r_area));
+ if (r_area == NULL) {
+ udb_result_preparation_area_t *a = q_area->result_prep_areas;
+
+ while (a != NULL) {
+ udb_result_preparation_area_t *next = a->next;
+ sfree(a);
+ a = next;
+ }
+
+ free(q_area);
+ return NULL;
+ }
+
+ *next_r_area = r_area;
+ next_r_area = &r_area->next;
+ }
+
+ return q_area;
+} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
+
+void udb_query_delete_preparation_area(
+ udb_query_preparation_area_t *q_area) /* {{{ */
+{
+ udb_result_preparation_area_t *r_area;
+
+ if (q_area == NULL)
+ return;
+
+ r_area = q_area->result_prep_areas;
+ while (r_area != NULL) {
+ udb_result_preparation_area_t *area = r_area;
+
+ r_area = r_area->next;
+
+ sfree(area->instances_pos);
+ sfree(area->values_pos);
+ sfree(area->instances_buffer);
+ sfree(area->values_buffer);
+ free(area);
+ }
+
+ sfree(q_area->host);
+ sfree(q_area->plugin);
+ sfree(q_area->db_name);
+
+ free(q_area);
+} /* }}} void udb_query_delete_preparation_area */
--- /dev/null
+/**
+ * collectd - src/utils_db_query.h
+ * Copyright (C) 2008,2009 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_DB_QUERY_H
+#define UTILS_DB_QUERY_H 1
+
+/*
+ * Data types
+ */
+struct udb_query_s;
+typedef struct udb_query_s udb_query_t;
+
+struct udb_query_preparation_area_s;
+typedef struct udb_query_preparation_area_s udb_query_preparation_area_t;
+
+typedef int (*udb_query_create_callback_t)(udb_query_t *q, oconfig_item_t *ci);
+
+/*
+ * Public functions
+ */
+int udb_query_create(udb_query_t ***ret_query_list, size_t *ret_query_list_len,
+ oconfig_item_t *ci, udb_query_create_callback_t cb);
+void udb_query_free(udb_query_t **query_list, size_t query_list_len);
+
+int udb_query_pick_from_list_by_name(const char *name, udb_query_t **src_list,
+ size_t src_list_len,
+ udb_query_t ***dst_list,
+ size_t *dst_list_len);
+int udb_query_pick_from_list(oconfig_item_t *ci, udb_query_t **src_list,
+ size_t src_list_len, udb_query_t ***dst_list,
+ size_t *dst_list_len);
+
+const char *udb_query_get_name(udb_query_t *q);
+const char *udb_query_get_statement(udb_query_t *q);
+
+void udb_query_set_user_data(udb_query_t *q, void *user_data);
+void *udb_query_get_user_data(udb_query_t *q);
+
+/*
+ * udb_query_check_version
+ *
+ * Returns 0 if the query is NOT suitable for `version' and >0 if the
+ * query IS suitable.
+ */
+int udb_query_check_version(udb_query_t *q, unsigned int version);
+
+int udb_query_prepare_result(udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area,
+ const char *host, const char *plugin,
+ const char *db_name, char **column_names,
+ size_t column_num);
+int udb_query_handle_result(udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area,
+ char **column_values);
+void udb_query_finish_result(udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area);
+
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area(udb_query_t *q);
+void udb_query_delete_preparation_area(udb_query_preparation_area_t *q_area);
+
+#endif /* UTILS_DB_QUERY_H */
--- /dev/null
+/**
+ * collectd - src/utils_deq.h
+ * Copyright(c) 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Andy Smith <ansmith@redhat.com>
+ */
+
+#ifndef utils_deq_h
+#define utils_deq_h 1
+
+#include <assert.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#define CT_ASSERT(exp) \
+ { assert(exp); }
+
+#define NEW(t) (t *)malloc(sizeof(t))
+#define NEW_ARRAY(t, n) (t *)malloc(sizeof(t) * (n))
+#define NEW_PTR_ARRAY(t, n) (t **)malloc(sizeof(t *) * (n))
+
+#define ZERO(p) memset(p, 0, sizeof(*p))
+
+#define DEQ_DECLARE(i, d) \
+ typedef struct { \
+ i *head; \
+ i *tail; \
+ i *scratch; \
+ size_t size; \
+ } d
+
+#define DEQ_LINKS_N(n, t) \
+ t *prev##n; \
+ t *next##n
+#define DEQ_LINKS(t) DEQ_LINKS_N(, t)
+#define DEQ_EMPTY \
+ { 0, 0, 0, 0 }
+
+#define DEQ_INIT(d) \
+ do { \
+ (d).head = 0; \
+ (d).tail = 0; \
+ (d).scratch = 0; \
+ (d).size = 0; \
+ } while (0)
+#define DEQ_IS_EMPTY(d) ((d).head == 0)
+#define DEQ_ITEM_INIT_N(n, i) \
+ do { \
+ (i)->next##n = 0; \
+ (i)->prev##n = 0; \
+ } while (0)
+#define DEQ_ITEM_INIT(i) DEQ_ITEM_INIT_N(, i)
+#define DEQ_HEAD(d) ((d).head)
+#define DEQ_TAIL(d) ((d).tail)
+#define DEQ_SIZE(d) ((d).size)
+#define DEQ_NEXT_N(n, i) (i)->next##n
+#define DEQ_NEXT(i) DEQ_NEXT_N(, i)
+#define DEQ_PREV_N(n, i) (i)->prev##n
+#define DEQ_PREV(i) DEQ_PREV_N(, i)
+#define DEQ_MOVE(d1, d2) \
+ do { \
+ d2 = d1; \
+ DEQ_INIT(d1); \
+ } while (0)
+/**
+ *@pre ptr points to first element of deq
+ *@post ptr points to first element of deq that passes test, or 0. Test should
+ *involve ptr.
+ */
+#define DEQ_FIND_N(n, ptr, test) \
+ while ((ptr) && !(test)) \
+ ptr = DEQ_NEXT_N(n, ptr);
+#define DEQ_FIND(ptr, test) DEQ_FIND_N(, ptr, test)
+
+#define DEQ_INSERT_HEAD_N(n, d, i) \
+ do { \
+ CT_ASSERT((i)->next##n == 0); \
+ CT_ASSERT((i)->prev##n == 0); \
+ if ((d).head) { \
+ (i)->next##n = (d).head; \
+ (d).head->prev##n = i; \
+ } else { \
+ (d).tail = i; \
+ (i)->next##n = 0; \
+ CT_ASSERT((d).size == 0); \
+ } \
+ (i)->prev##n = 0; \
+ (d).head = i; \
+ (d).size++; \
+ } while (0)
+#define DEQ_INSERT_HEAD(d, i) DEQ_INSERT_HEAD_N(, d, i)
+
+#define DEQ_INSERT_TAIL_N(n, d, i) \
+ do { \
+ CT_ASSERT((i)->next##n == 0); \
+ CT_ASSERT((i)->prev##n == 0); \
+ if ((d).tail) { \
+ (i)->prev##n = (d).tail; \
+ (d).tail->next##n = i; \
+ } else { \
+ (d).head = i; \
+ (i)->prev##n = 0; \
+ CT_ASSERT((d).size == 0); \
+ } \
+ (i)->next##n = 0; \
+ (d).tail = i; \
+ (d).size++; \
+ } while (0)
+#define DEQ_INSERT_TAIL(d, i) DEQ_INSERT_TAIL_N(, d, i)
+
+#define DEQ_REMOVE_HEAD_N(n, d) \
+ do { \
+ CT_ASSERT((d).head); \
+ if ((d).head) { \
+ (d).scratch = (d).head; \
+ (d).head = (d).head->next##n; \
+ if ((d).head == 0) { \
+ (d).tail = 0; \
+ CT_ASSERT((d).size == 1); \
+ } else \
+ (d).head->prev##n = 0; \
+ (d).size--; \
+ (d).scratch->next##n = 0; \
+ (d).scratch->prev##n = 0; \
+ } \
+ } while (0)
+#define DEQ_REMOVE_HEAD(d) DEQ_REMOVE_HEAD_N(, d)
+
+#define DEQ_REMOVE_TAIL_N(n, d) \
+ do { \
+ CT_ASSERT((d).tail); \
+ if ((d).tail) { \
+ (d).scratch = (d).tail; \
+ (d).tail = (d).tail->prev##n; \
+ if ((d).tail == 0) { \
+ (d).head = 0; \
+ CT_ASSERT((d).size == 1); \
+ } else \
+ (d).tail->next##n = 0; \
+ (d).size--; \
+ (d).scratch->next##n = 0; \
+ (d).scratch->prev##n = 0; \
+ } \
+ } while (0)
+#define DEQ_REMOVE_TAIL(d) DEQ_REMOVE_TAIL_N(, d)
+
+#define DEQ_INSERT_AFTER_N(n, d, i, a) \
+ do { \
+ CT_ASSERT((i)->next##n == 0); \
+ CT_ASSERT((i)->prev##n == 0); \
+ CT_ASSERT(a); \
+ if ((a)->next##n) \
+ (a)->next##n->prev##n = (i); \
+ else \
+ (d).tail = (i); \
+ (i)->next##n = (a)->next##n; \
+ (i)->prev##n = (a); \
+ (a)->next##n = (i); \
+ (d).size++; \
+ } while (0)
+#define DEQ_INSERT_AFTER(d, i, a) DEQ_INSERT_AFTER_N(, d, i, a)
+
+#define DEQ_REMOVE_N(n, d, i) \
+ do { \
+ if ((i)->next##n) \
+ (i)->next##n->prev##n = (i)->prev##n; \
+ else \
+ (d).tail = (i)->prev##n; \
+ if ((i)->prev##n) \
+ (i)->prev##n->next##n = (i)->next##n; \
+ else \
+ (d).head = (i)->next##n; \
+ CT_ASSERT((d).size > 0); \
+ (d).size--; \
+ (i)->next##n = 0; \
+ (i)->prev##n = 0; \
+ CT_ASSERT((d).size || (!(d).head && !(d).tail)); \
+ } while (0)
+#define DEQ_REMOVE(d, i) DEQ_REMOVE_N(, d, i)
+
+#define DEQ_APPEND_N(n, d1, d2) \
+ do { \
+ if (!(d1).head) \
+ (d1) = (d2); \
+ else if ((d2).head) { \
+ (d1).tail->next##n = (d2).head; \
+ (d2).head->prev##n = (d1).tail; \
+ (d1).tail = (d2).tail; \
+ (d1).size += (d2).size; \
+ } \
+ DEQ_INIT(d2); \
+ } while (0)
+#define DEQ_APPEND(d1, d2) DEQ_APPEND_N(, d1, d2)
+
+#endif
--- /dev/null
+/*
+ * collectd - src/utils_dns.c
+ * Copyright (C) 2006 Florian octo Forster
+ * Copyright (C) 2002 The Measurement Factory, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ * The Measurement Factory, Inc. <http://www.measurement-factory.com/>
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#if HAVE_NET_IF_ARP_H
+#include <net/if_arp.h>
+#endif
+#if HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#if HAVE_NET_PPP_DEFS_H
+#include <net/ppp_defs.h>
+#endif
+#if HAVE_NET_IF_PPP_H
+#include <net/if_ppp.h>
+#endif
+
+#if HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP6_H
+#include <netinet/ip6.h>
+#endif
+#if HAVE_NETINET_IF_ETHER_H
+#include <netinet/if_ether.h>
+#endif
+#if HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_NETINET_IP_VAR_H
+#include <netinet/ip_var.h>
+#endif
+#if HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#if HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#if HAVE_PCAP_H
+#include <pcap.h>
+#endif
+
+#define PCAP_SNAPLEN 1460
+#ifndef ETHER_HDR_LEN
+#define ETHER_ADDR_LEN 6
+#define ETHER_TYPE_LEN 2
+#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
+#endif
+#ifndef ETHERTYPE_8021Q
+#define ETHERTYPE_8021Q 0x8100
+#endif
+#ifndef ETHERTYPE_IPV6
+#define ETHERTYPE_IPV6 0x86DD
+#endif
+
+#ifndef PPP_ADDRESS_VAL
+#define PPP_ADDRESS_VAL 0xff /* The address byte value */
+#endif
+#ifndef PPP_CONTROL_VAL
+#define PPP_CONTROL_VAL 0x03 /* The control byte value */
+#endif
+
+#if HAVE_STRUCT_UDPHDR_UH_DPORT && HAVE_STRUCT_UDPHDR_UH_SPORT
+#define UDP_DEST uh_dport
+#define UDP_SRC uh_sport
+#elif HAVE_STRUCT_UDPHDR_DEST && HAVE_STRUCT_UDPHDR_SOURCE
+#define UDP_DEST dest
+#define UDP_SRC source
+#else
+#error "`struct udphdr' is unusable."
+#endif
+
+#if HAVE_NETINET_IP6_H && HAVE_STRUCT_IP6_EXT
+#define HAVE_IPV6 1
+#endif
+
+#include "utils/dns/dns.h"
+
+/*
+ * Type definitions
+ */
+struct ip_list_s {
+ struct in6_addr addr;
+ void *data;
+ struct ip_list_s *next;
+};
+typedef struct ip_list_s ip_list_t;
+
+typedef int(printer)(const char *, ...);
+
+/*
+ * flags/features for non-interactive mode
+ */
+
+#ifndef T_A6
+#define T_A6 38
+#endif
+#ifndef T_SRV
+#define T_SRV 33
+#endif
+
+/*
+ * Global variables
+ */
+
+#if HAVE_PCAP_H
+static pcap_t *pcap_obj;
+#endif
+
+static ip_list_t *IgnoreList;
+
+#if HAVE_PCAP_H
+static void (*Callback)(const rfc1035_header_t *);
+
+static int query_count_intvl;
+static int query_count_total;
+#ifdef __OpenBSD__
+static struct bpf_timeval last_ts;
+#else
+static struct timeval last_ts;
+#endif /* __OpenBSD__ */
+#endif /* HAVE_PCAP_H */
+
+static int cmp_in6_addr(const struct in6_addr *a, const struct in6_addr *b) {
+ int i;
+
+ assert(sizeof(struct in6_addr) == 16);
+
+ for (i = 0; i < 16; i++)
+ if (a->s6_addr[i] != b->s6_addr[i])
+ break;
+
+ if (i >= 16)
+ return 0;
+
+ return a->s6_addr[i] > b->s6_addr[i] ? 1 : -1;
+} /* int cmp_addrinfo */
+
+static inline int ignore_list_match(const struct in6_addr *addr) {
+ for (ip_list_t *ptr = IgnoreList; ptr != NULL; ptr = ptr->next)
+ if (cmp_in6_addr(addr, &ptr->addr) == 0)
+ return 1;
+ return 0;
+} /* int ignore_list_match */
+
+static void ignore_list_add(const struct in6_addr *addr) {
+ ip_list_t *new;
+
+ if (ignore_list_match(addr) != 0)
+ return;
+
+ new = malloc(sizeof(*new));
+ if (new == NULL) {
+ perror("malloc");
+ return;
+ }
+
+ memcpy(&new->addr, addr, sizeof(struct in6_addr));
+ new->next = IgnoreList;
+
+ IgnoreList = new;
+} /* void ignore_list_add */
+
+void ignore_list_add_name(const char *name) {
+ struct addrinfo *ai_list;
+ struct in6_addr addr;
+ int status;
+
+ status = getaddrinfo(name, NULL, NULL, &ai_list);
+ if (status != 0)
+ return;
+
+ for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
+ ai_ptr = ai_ptr->ai_next) {
+ if (ai_ptr->ai_family == AF_INET) {
+ memset(&addr, '\0', sizeof(addr));
+ addr.s6_addr[10] = 0xFF;
+ addr.s6_addr[11] = 0xFF;
+ memcpy(addr.s6_addr + 12,
+ &((struct sockaddr_in *)ai_ptr->ai_addr)->sin_addr, 4);
+
+ ignore_list_add(&addr);
+ } else {
+ ignore_list_add(&((struct sockaddr_in6 *)ai_ptr->ai_addr)->sin6_addr);
+ }
+ } /* for */
+
+ freeaddrinfo(ai_list);
+}
+
+#if HAVE_PCAP_H
+static void in6_addr_from_buffer(struct in6_addr *ia, const void *buf,
+ size_t buf_len, int family) {
+ memset(ia, 0, sizeof(struct in6_addr));
+ if ((AF_INET == family) && (sizeof(uint32_t) == buf_len)) {
+ ia->s6_addr[10] = 0xFF;
+ ia->s6_addr[11] = 0xFF;
+ memcpy(ia->s6_addr + 12, buf, buf_len);
+ } else if ((AF_INET6 == family) && (sizeof(struct in6_addr) == buf_len)) {
+ memcpy(ia, buf, buf_len);
+ }
+} /* void in6_addr_from_buffer */
+
+void dnstop_set_pcap_obj(pcap_t *po) { pcap_obj = po; }
+
+void dnstop_set_callback(void (*cb)(const rfc1035_header_t *)) {
+ Callback = cb;
+}
+
+#define RFC1035_MAXLABELSZ 63
+static int rfc1035NameUnpack(const char *buf, size_t sz, off_t *off, char *name,
+ size_t ns) {
+ off_t no = 0;
+ unsigned char c;
+ size_t len;
+ static int loop_detect;
+ if (loop_detect > 2)
+ return 4; /* compression loop */
+ if (ns == 0)
+ return 4; /* probably compression loop */
+ do {
+ if ((*off) >= ((off_t)sz))
+ break;
+ c = *(buf + (*off));
+ if (c > 191) {
+ /* blasted compression */
+ int rc;
+ unsigned short s;
+ off_t ptr;
+ memcpy(&s, buf + (*off), sizeof(s));
+ s = ntohs(s);
+ (*off) += sizeof(s);
+ /* Sanity check */
+ if ((*off) >= ((off_t)sz))
+ return 1; /* message too short */
+ ptr = s & 0x3FFF;
+ /* Make sure the pointer is inside this message */
+ if (ptr >= ((off_t)sz))
+ return 2; /* bad compression ptr */
+ if (ptr < DNS_MSG_HDR_SZ)
+ return 2; /* bad compression ptr */
+ loop_detect++;
+ rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
+ loop_detect--;
+ return rc;
+ } else if (c > RFC1035_MAXLABELSZ) {
+ /*
+ * "(The 10 and 01 combinations are reserved for future use.)"
+ */
+ return 3; /* reserved label/compression flags */
+ } else {
+ (*off)++;
+ len = (size_t)c;
+ if (len == 0)
+ break;
+ if (len > (ns - 1))
+ len = ns - 1;
+ if ((*off) + len > sz)
+ return 4; /* message is too short */
+ if (no + len + 1 > ns)
+ return 5; /* qname would overflow name buffer */
+ memcpy(name + no, buf + (*off), len);
+ (*off) += len;
+ no += len;
+ *(name + (no++)) = '.';
+ }
+ } while (c > 0);
+ if (no > 0)
+ *(name + no - 1) = '\0';
+ /* make sure we didn't allow someone to overflow the name buffer */
+ assert(no <= ((off_t)ns));
+ return 0;
+}
+
+static int handle_dns(const char *buf, int len) {
+ rfc1035_header_t qh;
+ uint16_t us;
+ off_t offset;
+ char *t;
+ int status;
+
+ /* The DNS header is 12 bytes long */
+ if (len < DNS_MSG_HDR_SZ)
+ return 0;
+
+ memcpy(&us, buf + 0, 2);
+ qh.id = ntohs(us);
+
+ memcpy(&us, buf + 2, 2);
+ us = ntohs(us);
+ qh.qr = (us >> 15) & 0x01;
+ qh.opcode = (us >> 11) & 0x0F;
+ qh.aa = (us >> 10) & 0x01;
+ qh.tc = (us >> 9) & 0x01;
+ qh.rd = (us >> 8) & 0x01;
+ qh.ra = (us >> 7) & 0x01;
+ qh.z = (us >> 6) & 0x01;
+ qh.ad = (us >> 5) & 0x01;
+ qh.cd = (us >> 4) & 0x01;
+ qh.rcode = us & 0x0F;
+
+ memcpy(&us, buf + 4, 2);
+ qh.qdcount = ntohs(us);
+
+ memcpy(&us, buf + 6, 2);
+ qh.ancount = ntohs(us);
+
+ memcpy(&us, buf + 8, 2);
+ qh.nscount = ntohs(us);
+
+ memcpy(&us, buf + 10, 2);
+ qh.arcount = ntohs(us);
+
+ offset = DNS_MSG_HDR_SZ;
+ memset(qh.qname, '\0', MAX_QNAME_SZ);
+ status = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
+ if (status != 0) {
+ INFO("utils_dns: handle_dns: rfc1035NameUnpack failed "
+ "with status %i.",
+ status);
+ return 0;
+ }
+ if ('\0' == qh.qname[0])
+ sstrncpy(qh.qname, ".", sizeof(qh.qname));
+ while ((t = strchr(qh.qname, '\n')))
+ *t = ' ';
+ while ((t = strchr(qh.qname, '\r')))
+ *t = ' ';
+ for (t = qh.qname; *t; t++)
+ *t = tolower((int)*t);
+
+ memcpy(&us, buf + offset, 2);
+ qh.qtype = ntohs(us);
+ memcpy(&us, buf + offset + 2, 2);
+ qh.qclass = ntohs(us);
+
+ qh.length = (uint16_t)len;
+
+ if (Callback != NULL)
+ Callback(&qh);
+
+ return 1;
+}
+
+static int handle_udp(const struct udphdr *udp, int len) {
+ char buf[PCAP_SNAPLEN];
+ if ((ntohs(udp->UDP_DEST) != 53) && (ntohs(udp->UDP_SRC) != 53))
+ return 0;
+ memcpy(buf, udp + 1, len - sizeof(*udp));
+ if (0 == handle_dns(buf, len - sizeof(*udp)))
+ return 0;
+ return 1;
+}
+
+#if HAVE_IPV6
+static int handle_ipv6(struct ip6_hdr *ipv6, int len) {
+ char buf[PCAP_SNAPLEN];
+ unsigned int offset;
+ int nexthdr;
+
+ struct in6_addr c_src_addr;
+ uint16_t payload_len;
+
+ if (0 > len)
+ return 0;
+
+ offset = sizeof(struct ip6_hdr);
+ nexthdr = ipv6->ip6_nxt;
+ c_src_addr = ipv6->ip6_src;
+ payload_len = ntohs(ipv6->ip6_plen);
+
+ if (ignore_list_match(&c_src_addr))
+ return 0;
+
+ /* Parse extension headers. This only handles the standard headers, as
+ * defined in RFC 2460, correctly. Fragments are discarded. */
+ while ((IPPROTO_ROUTING == nexthdr) /* routing header */
+ || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */
+ || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */
+ || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
+ || (IPPROTO_AH == nexthdr) /* destination options. */
+ || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */
+ {
+ struct ip6_ext ext_hdr;
+ uint16_t ext_hdr_len;
+
+ /* Catch broken packets */
+ if ((offset + sizeof(struct ip6_ext)) > (unsigned int)len)
+ return 0;
+
+ /* Cannot handle fragments. */
+ if (IPPROTO_FRAGMENT == nexthdr)
+ return 0;
+
+ memcpy(&ext_hdr, (char *)ipv6 + offset, sizeof(struct ip6_ext));
+ nexthdr = ext_hdr.ip6e_nxt;
+ ext_hdr_len = (8 * (ntohs(ext_hdr.ip6e_len) + 1));
+
+ /* This header is longer than the packets payload.. WTF? */
+ if (ext_hdr_len > payload_len)
+ return 0;
+
+ offset += ext_hdr_len;
+ payload_len -= ext_hdr_len;
+ } /* while */
+
+ /* Catch broken and empty packets */
+ if (((offset + payload_len) > (unsigned int)len) || (payload_len == 0) ||
+ (payload_len > PCAP_SNAPLEN))
+ return 0;
+
+ if (IPPROTO_UDP != nexthdr)
+ return 0;
+
+ memcpy(buf, (char *)ipv6 + offset, payload_len);
+ if (handle_udp((struct udphdr *)buf, payload_len) == 0)
+ return 0;
+
+ return 1; /* Success */
+} /* int handle_ipv6 */
+/* #endif HAVE_IPV6 */
+
+#else /* if !HAVE_IPV6 */
+static int handle_ipv6(__attribute__((unused)) void *pkg,
+ __attribute__((unused)) int len) {
+ return 0;
+}
+#endif /* !HAVE_IPV6 */
+
+static int handle_ip(const struct ip *ip, int len) {
+ char buf[PCAP_SNAPLEN];
+ int offset = ip->ip_hl << 2;
+ struct in6_addr c_src_addr;
+ struct in6_addr c_dst_addr;
+
+ if (ip->ip_v == 6)
+ return handle_ipv6((void *)ip, len);
+
+ in6_addr_from_buffer(&c_src_addr, &ip->ip_src.s_addr,
+ sizeof(ip->ip_src.s_addr), AF_INET);
+ in6_addr_from_buffer(&c_dst_addr, &ip->ip_dst.s_addr,
+ sizeof(ip->ip_dst.s_addr), AF_INET);
+ if (ignore_list_match(&c_src_addr))
+ return 0;
+ if (IPPROTO_UDP != ip->ip_p)
+ return 0;
+ memcpy(buf, ((char *)ip) + offset, len - offset);
+ if (0 == handle_udp((struct udphdr *)buf, len - offset))
+ return 0;
+ return 1;
+}
+
+#if HAVE_NET_IF_PPP_H
+static int handle_ppp(const u_char *pkt, int len) {
+ char buf[PCAP_SNAPLEN];
+ unsigned short us;
+ unsigned short proto;
+ if (len < 2)
+ return 0;
+ if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
+ pkt += 2; /* ACFC not used */
+ len -= 2;
+ }
+ if (len < 2)
+ return 0;
+ if (*pkt % 2) {
+ proto = *pkt; /* PFC is used */
+ pkt++;
+ len--;
+ } else {
+ memcpy(&us, pkt, sizeof(us));
+ proto = ntohs(us);
+ pkt += 2;
+ len -= 2;
+ }
+ if (ETHERTYPE_IP != proto && PPP_IP != proto)
+ return 0;
+ memcpy(buf, pkt, len);
+ return handle_ip((struct ip *)buf, len);
+}
+#endif /* HAVE_NET_IF_PPP_H */
+
+static int handle_null(const u_char *pkt, int len) {
+ unsigned int family;
+ memcpy(&family, pkt, sizeof(family));
+ if (AF_INET != family)
+ return 0;
+ return handle_ip((struct ip *)(pkt + 4), len - 4);
+}
+
+#ifdef DLT_LOOP
+static int handle_loop(const u_char *pkt, int len) {
+ unsigned int family;
+ memcpy(&family, pkt, sizeof(family));
+ if (AF_INET != ntohl(family))
+ return 0;
+ return handle_ip((struct ip *)(pkt + 4), len - 4);
+}
+
+#endif
+
+#ifdef DLT_RAW
+static int handle_raw(const u_char *pkt, int len) {
+ return handle_ip((struct ip *)pkt, len);
+}
+
+#endif
+
+static int handle_ether(const u_char *pkt, int len) {
+ char buf[PCAP_SNAPLEN];
+ struct ether_header *e = (void *)pkt;
+ unsigned short etype = ntohs(e->ether_type);
+ if (len < ETHER_HDR_LEN)
+ return 0;
+ pkt += ETHER_HDR_LEN;
+ len -= ETHER_HDR_LEN;
+ if (ETHERTYPE_8021Q == etype) {
+ etype = ntohs(*(unsigned short *)(pkt + 2));
+ pkt += 4;
+ len -= 4;
+ }
+ if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype))
+ return 0;
+ memcpy(buf, pkt, len);
+ if (ETHERTYPE_IPV6 == etype)
+ return handle_ipv6((void *)buf, len);
+ else
+ return handle_ip((struct ip *)buf, len);
+}
+
+#ifdef DLT_LINUX_SLL
+static int handle_linux_sll(const u_char *pkt, int len) {
+ struct sll_header {
+ uint16_t pkt_type;
+ uint16_t dev_type;
+ uint16_t addr_len;
+ uint8_t addr[8];
+ uint16_t proto_type;
+ } * hdr;
+ uint16_t etype;
+
+ if ((0 > len) || ((unsigned int)len < sizeof(struct sll_header)))
+ return 0;
+
+ hdr = (struct sll_header *)pkt;
+ pkt = (u_char *)(hdr + 1);
+ len -= sizeof(struct sll_header);
+
+ etype = ntohs(hdr->proto_type);
+
+ if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype))
+ return 0;
+
+ if (ETHERTYPE_IPV6 == etype)
+ return handle_ipv6((void *)pkt, len);
+ else
+ return handle_ip((struct ip *)pkt, len);
+}
+#endif /* DLT_LINUX_SLL */
+
+/* public function */
+void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr,
+ const u_char *pkt) {
+ int status;
+
+ if (hdr->caplen < ETHER_HDR_LEN)
+ return;
+
+ switch (pcap_datalink(pcap_obj)) {
+ case DLT_EN10MB:
+ status = handle_ether(pkt, hdr->caplen);
+ break;
+#if HAVE_NET_IF_PPP_H
+ case DLT_PPP:
+ status = handle_ppp(pkt, hdr->caplen);
+ break;
+#endif
+#ifdef DLT_LOOP
+ case DLT_LOOP:
+ status = handle_loop(pkt, hdr->caplen);
+ break;
+#endif
+#ifdef DLT_RAW
+ case DLT_RAW:
+ status = handle_raw(pkt, hdr->caplen);
+ break;
+#endif
+#ifdef DLT_LINUX_SLL
+ case DLT_LINUX_SLL:
+ status = handle_linux_sll(pkt, hdr->caplen);
+ break;
+#endif
+ case DLT_NULL:
+ status = handle_null(pkt, hdr->caplen);
+ break;
+
+ default:
+ ERROR("handle_pcap: unsupported data link type %d",
+ pcap_datalink(pcap_obj));
+ status = 0;
+ break;
+ } /* switch (pcap_datalink(pcap_obj)) */
+
+ if (0 == status)
+ return;
+
+ query_count_intvl++;
+ query_count_total++;
+ last_ts = hdr->ts;
+}
+#endif /* HAVE_PCAP_H */
+
+const char *qtype_str(int t) {
+ static char buf[32];
+ switch (t) {
+#if (defined(__NAMESER)) && (__NAMESER >= 19991001)
+ case ns_t_a:
+ return "A";
+ case ns_t_ns:
+ return "NS";
+ case ns_t_md:
+ return "MD";
+ case ns_t_mf:
+ return "MF";
+ case ns_t_cname:
+ return "CNAME";
+ case ns_t_soa:
+ return "SOA";
+ case ns_t_mb:
+ return "MB";
+ case ns_t_mg:
+ return "MG";
+ case ns_t_mr:
+ return "MR";
+ case ns_t_null:
+ return "NULL";
+ case ns_t_wks:
+ return "WKS";
+ case ns_t_ptr:
+ return "PTR";
+ case ns_t_hinfo:
+ return "HINFO";
+ case ns_t_minfo:
+ return "MINFO";
+ case ns_t_mx:
+ return "MX";
+ case ns_t_txt:
+ return "TXT";
+ case ns_t_rp:
+ return "RP";
+ case ns_t_afsdb:
+ return "AFSDB";
+ case ns_t_x25:
+ return "X25";
+ case ns_t_isdn:
+ return "ISDN";
+ case ns_t_rt:
+ return "RT";
+ case ns_t_nsap:
+ return "NSAP";
+ case ns_t_nsap_ptr:
+ return "NSAP-PTR";
+ case ns_t_sig:
+ return "SIG";
+ case ns_t_key:
+ return "KEY";
+ case ns_t_px:
+ return "PX";
+ case ns_t_gpos:
+ return "GPOS";
+ case ns_t_aaaa:
+ return "AAAA";
+ case ns_t_loc:
+ return "LOC";
+ case ns_t_nxt:
+ return "NXT";
+ case ns_t_eid:
+ return "EID";
+ case ns_t_nimloc:
+ return "NIMLOC";
+ case ns_t_srv:
+ return "SRV";
+ case ns_t_atma:
+ return "ATMA";
+ case ns_t_naptr:
+ return "NAPTR";
+ case ns_t_opt:
+ return "OPT";
+#if __NAMESER >= 19991006
+ case ns_t_kx:
+ return "KX";
+ case ns_t_cert:
+ return "CERT";
+ case ns_t_a6:
+ return "A6";
+ case ns_t_dname:
+ return "DNAME";
+ case ns_t_sink:
+ return "SINK";
+ case ns_t_tsig:
+ return "TSIG";
+#endif
+#if __NAMESER >= 20090302
+ case ns_t_apl:
+ return "APL";
+ case ns_t_ds:
+ return "DS";
+ case ns_t_sshfp:
+ return "SSHFP";
+ case ns_t_ipseckey:
+ return "IPSECKEY";
+ case ns_t_rrsig:
+ return "RRSIG";
+ case ns_t_nsec:
+ return "NSEC";
+ case ns_t_dnskey:
+ return "DNSKEY";
+ case ns_t_dhcid:
+ return "DHCID";
+ case ns_t_nsec3:
+ return "NSEC3";
+ case ns_t_nsec3param:
+ return "NSEC3PARAM";
+ case ns_t_hip:
+ return "HIP";
+ case ns_t_spf:
+ return "SPF";
+ case ns_t_ixfr:
+ return "IXFR";
+#endif
+ case ns_t_axfr:
+ return "AXFR";
+ case ns_t_mailb:
+ return "MAILB";
+ case ns_t_maila:
+ return "MAILA";
+ case ns_t_any:
+ return "ANY";
+#if __NAMESER >= 19991006
+ case ns_t_zxfr:
+ return "ZXFR";
+#endif
+#if __NAMESER >= 20090302
+ case ns_t_dlv:
+ return "DLV";
+#endif
+/* #endif __NAMESER >= 19991001 */
+#elif (defined(__BIND)) && (__BIND >= 19950621)
+ case T_A:
+ return "A"; /* 1 ... */
+ case T_NS:
+ return "NS";
+ case T_MD:
+ return "MD";
+ case T_MF:
+ return "MF";
+ case T_CNAME:
+ return "CNAME";
+ case T_SOA:
+ return "SOA";
+ case T_MB:
+ return "MB";
+ case T_MG:
+ return "MG";
+ case T_MR:
+ return "MR";
+ case T_NULL:
+ return "NULL";
+ case T_WKS:
+ return "WKS";
+ case T_PTR:
+ return "PTR";
+ case T_HINFO:
+ return "HINFO";
+ case T_MINFO:
+ return "MINFO";
+ case T_MX:
+ return "MX";
+ case T_TXT:
+ return "TXT";
+ case T_RP:
+ return "RP";
+ case T_AFSDB:
+ return "AFSDB";
+ case T_X25:
+ return "X25";
+ case T_ISDN:
+ return "ISDN";
+ case T_RT:
+ return "RT";
+ case T_NSAP:
+ return "NSAP";
+ case T_NSAP_PTR:
+ return "NSAP_PTR";
+ case T_SIG:
+ return "SIG";
+ case T_KEY:
+ return "KEY";
+ case T_PX:
+ return "PX";
+ case T_GPOS:
+ return "GPOS";
+ case T_AAAA:
+ return "AAAA";
+ case T_LOC:
+ return "LOC";
+ case T_NXT:
+ return "NXT";
+ case T_EID:
+ return "EID";
+ case T_NIMLOC:
+ return "NIMLOC";
+ case T_SRV:
+ return "SRV";
+ case T_ATMA:
+ return "ATMA";
+ case T_NAPTR:
+ return "NAPTR"; /* ... 35 */
+#if (__BIND >= 19960801)
+ case T_KX:
+ return "KX"; /* 36 ... */
+ case T_CERT:
+ return "CERT";
+ case T_A6:
+ return "A6";
+ case T_DNAME:
+ return "DNAME";
+ case T_SINK:
+ return "SINK";
+ case T_OPT:
+ return "OPT";
+ case T_APL:
+ return "APL";
+ case T_DS:
+ return "DS";
+ case T_SSHFP:
+ return "SSHFP";
+ case T_RRSIG:
+ return "RRSIG";
+ case T_NSEC:
+ return "NSEC";
+ case T_DNSKEY:
+ return "DNSKEY"; /* ... 48 */
+ case T_TKEY:
+ return "TKEY"; /* 249 */
+#endif /* __BIND >= 19960801 */
+ case T_TSIG:
+ return "TSIG"; /* 250 ... */
+ case T_IXFR:
+ return "IXFR";
+ case T_AXFR:
+ return "AXFR";
+ case T_MAILB:
+ return "MAILB";
+ case T_MAILA:
+ return "MAILA";
+ case T_ANY:
+ return "ANY"; /* ... 255 */
+#endif /* __BIND >= 19950621 */
+ default:
+ snprintf(buf, sizeof(buf), "#%i", t);
+ return buf;
+ } /* switch (t) */
+}
+
+const char *opcode_str(int o) {
+ static char buf[30];
+ switch (o) {
+ case 0:
+ return "Query";
+ case 1:
+ return "Iquery";
+ case 2:
+ return "Status";
+ case 4:
+ return "Notify";
+ case 5:
+ return "Update";
+ default:
+ snprintf(buf, sizeof(buf), "Opcode%d", o);
+ return buf;
+ }
+}
+
+const char *rcode_str(int rcode) {
+ static char buf[32];
+ switch (rcode) {
+#if (defined(__NAMESER)) && (__NAMESER >= 19991006)
+ case ns_r_noerror:
+ return "NOERROR";
+ case ns_r_formerr:
+ return "FORMERR";
+ case ns_r_servfail:
+ return "SERVFAIL";
+ case ns_r_nxdomain:
+ return "NXDOMAIN";
+ case ns_r_notimpl:
+ return "NOTIMPL";
+ case ns_r_refused:
+ return "REFUSED";
+ case ns_r_yxdomain:
+ return "YXDOMAIN";
+ case ns_r_yxrrset:
+ return "YXRRSET";
+ case ns_r_nxrrset:
+ return "NXRRSET";
+ case ns_r_notauth:
+ return "NOTAUTH";
+ case ns_r_notzone:
+ return "NOTZONE";
+ case ns_r_max:
+ return "MAX";
+ case ns_r_badsig:
+ return "BADSIG";
+ case ns_r_badkey:
+ return "BADKEY";
+ case ns_r_badtime:
+ return "BADTIME";
+/* #endif __NAMESER >= 19991006 */
+#elif (defined(__BIND)) && (__BIND >= 19950621)
+ case NOERROR:
+ return "NOERROR";
+ case FORMERR:
+ return "FORMERR";
+ case SERVFAIL:
+ return "SERVFAIL";
+ case NXDOMAIN:
+ return "NXDOMAIN";
+ case NOTIMP:
+ return "NOTIMP";
+ case REFUSED:
+ return "REFUSED";
+#if defined(YXDOMAIN) && defined(NXRRSET)
+ case YXDOMAIN:
+ return "YXDOMAIN";
+ case YXRRSET:
+ return "YXRRSET";
+ case NXRRSET:
+ return "NXRRSET";
+ case NOTAUTH:
+ return "NOTAUTH";
+ case NOTZONE:
+ return "NOTZONE";
+#endif /* RFC2136 rcodes */
+#endif /* __BIND >= 19950621 */
+ default:
+ snprintf(buf, sizeof(buf), "RCode%i", rcode);
+ return buf;
+ }
+} /* const char *rcode_str (int rcode) */
+
+#if 0
+static int
+main(int argc, char *argv[])
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ int x;
+ struct stat sb;
+ int readfile_state = 0;
+ struct bpf_program fp;
+
+ port53 = htons(53);
+ SubReport = Sources_report;
+ ignore_addr.s_addr = 0;
+ progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
+ srandom(time(NULL));
+ ResetCounters();
+
+ while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
+ switch (x) {
+ case 'a':
+ anon_flag = 1;
+ break;
+ case 's':
+ sld_flag = 1;
+ break;
+ case 't':
+ nld_flag = 1;
+ break;
+ case 'p':
+ promisc_flag = 0;
+ break;
+ case 'b':
+ bpf_program_str = strdup(optarg);
+ break;
+ case 'i':
+ ignore_addr.s_addr = inet_addr(optarg);
+ break;
+ case 'f':
+ set_filter(optarg);
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+ device = strdup(argv[0]);
+
+ if (0 == stat(device, &sb))
+ readfile_state = 1;
+ if (readfile_state) {
+ pcap_obj = pcap_open_offline(device, errbuf);
+ } else {
+ pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
+ }
+ if (NULL == pcap_obj) {
+ fprintf(stderr, "pcap_open_*: %s\n", errbuf);
+ exit(1);
+ }
+
+ if (0 == isatty(1)) {
+ if (0 == readfile_state) {
+ fprintf(stderr, "Non-interactive mode requires savefile argument\n");
+ exit(1);
+ }
+ interactive = 0;
+ print_func = printf;
+ }
+
+ memset(&fp, '\0', sizeof(fp));
+ x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0);
+ if (x < 0) {
+ fprintf(stderr, "pcap_compile failed\n");
+ exit(1);
+ }
+ x = pcap_setfilter(pcap_obj, &fp);
+ if (x < 0) {
+ fprintf(stderr, "pcap_setfilter failed\n");
+ exit(1);
+ }
+
+ /*
+ * non-blocking call added for Mac OS X bugfix. Sent by Max Horn.
+ * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
+ */
+ x = pcap_setnonblock(pcap_obj, 1, errbuf);
+ if (x < 0) {
+ fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
+ exit(1);
+ }
+
+ switch (pcap_datalink(pcap_obj)) {
+ case DLT_EN10MB:
+ handle_datalink = handle_ether;
+ break;
+#if HAVE_NET_IF_PPP_H
+ case DLT_PPP:
+ handle_datalink = handle_ppp;
+ break;
+#endif
+#ifdef DLT_LOOP
+ case DLT_LOOP:
+ handle_datalink = handle_loop;
+ break;
+#endif
+#ifdef DLT_RAW
+ case DLT_RAW:
+ handle_datalink = handle_raw;
+ break;
+#endif
+ case DLT_NULL:
+ handle_datalink = handle_null;
+ break;
+ default:
+ fprintf(stderr, "unsupported data link type %d\n",
+ pcap_datalink(pcap_obj));
+ return 1;
+ break;
+ }
+ if (interactive) {
+ init_curses();
+ while (0 == Quit) {
+ if (readfile_state < 2) {
+ /*
+ * On some OSes select() might return 0 even when
+ * there are packets to process. Thus, we always
+ * ignore its return value and just call pcap_dispatch()
+ * anyway.
+ */
+ if (0 == readfile_state) /* interactive */
+ pcap_select(pcap_obj, 1, 0);
+ x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL);
+ }
+ if (0 == x && 1 == readfile_state) {
+ /* block on keyboard until user quits */
+ readfile_state++;
+ nodelay(w, 0);
+ }
+ keyboard();
+ cron_pre();
+ report();
+ cron_post();
+ }
+ endwin(); /* klin, Thu Nov 28 08:56:51 2002 */
+ } else {
+ while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL))
+ (void) 0;
+ cron_pre();
+ Sources_report(); print_func("\n");
+ Destinatioreport(); print_func("\n");
+ Qtypes_report(); print_func("\n");
+ Opcodes_report(); print_func("\n");
+ Tld_report(); print_func("\n");
+ Sld_report(); print_func("\n");
+ Nld_report(); print_func("\n");
+ SldBySource_report();
+ }
+
+ pcap_close(pcap_obj);
+ return 0;
+} /* static int main(int argc, char *argv[]) */
+#endif
--- /dev/null
+/*
+ * collectd - src/utils_dns.h
+ * Copyright (C) 2006 Florian octo Forster
+ * Copyright (C) 2002 The Measurement Factory, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ * The Measurement Factory, Inc. <http://www.measurement-factory.com/>
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#ifndef COLLECTD_UTILS_DNS_H
+#define COLLECTD_UTILS_DNS_H 1
+
+#include "config.h"
+
+#include <arpa/nameser.h>
+#include <stdint.h>
+
+#if HAVE_PCAP_H
+#include <pcap.h>
+#endif
+
+#define DNS_MSG_HDR_SZ 12
+
+#define T_MAX 65536
+#define MAX_QNAME_SZ 512
+
+struct rfc1035_header_s {
+ uint16_t id;
+ unsigned int qr : 1;
+ unsigned int opcode : 4;
+ unsigned int aa : 1;
+ unsigned int tc : 1;
+ unsigned int rd : 1;
+ unsigned int ra : 1;
+ unsigned int z : 1;
+ unsigned int ad : 1;
+ unsigned int cd : 1;
+ unsigned int rcode : 4;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+ uint16_t qtype;
+ uint16_t qclass;
+ char qname[MAX_QNAME_SZ];
+ uint16_t length;
+};
+typedef struct rfc1035_header_s rfc1035_header_t;
+
+#if HAVE_PCAP_H
+void dnstop_set_pcap_obj(pcap_t *po);
+#endif
+void dnstop_set_callback(void (*cb)(const rfc1035_header_t *));
+
+void ignore_list_add_name(const char *name);
+#if HAVE_PCAP_H
+void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr,
+ const u_char *pkt);
+#endif
+
+const char *qtype_str(int t);
+const char *opcode_str(int o);
+const char *rcode_str(int r);
+
+#endif /* !COLLECTD_UTILS_DNS_H */
--- /dev/null
+/*
+ * collectd - src/utils_dpdk.c
+ * MIT License
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Maryam Tahhan <maryam.tahhan@intel.com>
+ * Harry van Haaren <harry.van.haaren@intel.com>
+ * Taras Chornyi <tarasx.chornyi@intel.com>
+ * Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ * Krzysztof Matczak <krzysztofx.matczak@intel.com>
+ */
+
+#include "collectd.h"
+
+#include <poll.h>
+#include <semaphore.h>
+#include <sys/mman.h>
+
+#include <rte_config.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+
+#include "utils/common/common.h"
+#include "utils/dpdk/dpdk.h"
+
+#if RTE_VERSION <= RTE_VERSION_NUM(18, 5, 0, 0)
+#define DPDK_DEFAULT_RTE_CONFIG "/var/run/.rte_config"
+#else
+#define DPDK_DEFAULT_RTE_CONFIG "/var/run/dpdk/rte/config"
+#endif
+#define DPDK_EAL_ARGC 10
+// Complete trace should fit into 1024 chars. Trace contain some headers
+// and text together with traced data from pipe. This is the reason why
+// we need to limit DPDK_MAX_BUFFER_SIZE value.
+#define DPDK_MAX_BUFFER_SIZE 896
+#define DPDK_CDM_DEFAULT_TIMEOUT 10000
+
+enum DPDK_HELPER_STATUS {
+ DPDK_HELPER_NOT_INITIALIZED = 0,
+ DPDK_HELPER_INITIALIZING,
+ DPDK_HELPER_WAITING_ON_PRIMARY,
+ DPDK_HELPER_INITIALIZING_EAL,
+ DPDK_HELPER_ALIVE_SENDING_EVENTS,
+ DPDK_HELPER_GRACEFUL_QUIT,
+};
+
+#define DPDK_HELPER_TRACE(_name) \
+ DEBUG("%s:%s:%d pid=%ld", _name, __FUNCTION__, __LINE__, (long)getpid())
+
+struct dpdk_helper_ctx_s {
+
+ dpdk_eal_config_t eal_config;
+ int eal_initialized;
+
+ size_t shm_size;
+ char shm_name[DATA_MAX_NAME_LEN];
+
+ sem_t sema_cmd_start;
+ sem_t sema_cmd_complete;
+ cdtime_t cmd_wait_time;
+
+ pid_t pid;
+ int pipes[2];
+ int status;
+
+ int cmd;
+ int cmd_result;
+
+ char priv_data[];
+};
+
+static int dpdk_shm_init(const char *name, size_t size, void **map);
+static void dpdk_shm_cleanup(const char *name, size_t size, void *map);
+
+static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc);
+static int dpdk_helper_worker(dpdk_helper_ctx_t *phc);
+static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc);
+static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid);
+static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc,
+ enum DPDK_HELPER_STATUS status);
+static int dpdk_helper_exit(dpdk_helper_ctx_t *phc,
+ enum DPDK_HELPER_STATUS status);
+static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc);
+static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc);
+static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status);
+
+static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc) {
+ if (phc == NULL)
+ return;
+
+ DPDK_HELPER_TRACE(phc->shm_name);
+
+ snprintf(phc->eal_config.coremask, DATA_MAX_NAME_LEN, "%s", "0xf");
+ snprintf(phc->eal_config.memory_channels, DATA_MAX_NAME_LEN, "%s", "1");
+ snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, "%s",
+ DPDK_DEFAULT_RTE_CONFIG);
+}
+
+int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) {
+ if (phc == NULL) {
+ ERROR("Invalid argument (phc)");
+ return -EINVAL;
+ }
+
+ DPDK_HELPER_TRACE(phc->shm_name);
+
+ if (ec == NULL) {
+ ERROR("Invalid argument (ec)");
+ return -EINVAL;
+ }
+
+ memcpy(&phc->eal_config, ec, sizeof(phc->eal_config));
+
+ return 0;
+}
+
+int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) {
+ if (phc == NULL) {
+ ERROR("Invalid argument (phc)");
+ return -EINVAL;
+ }
+
+ DPDK_HELPER_TRACE(phc->shm_name);
+
+ if (ec == NULL) {
+ ERROR("Invalid argument (ec)");
+ return -EINVAL;
+ }
+
+ memcpy(ec, &phc->eal_config, sizeof(*ec));
+
+ return 0;
+}
+
+int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci) {
+ DPDK_HELPER_TRACE(phc->shm_name);
+
+ if (phc == NULL) {
+ ERROR("Invalid argument (phc)");
+ return -EINVAL;
+ }
+
+ if (ci == NULL) {
+ ERROR("Invalid argument (ci)");
+ return -EINVAL;
+ }
+
+ int status = 0;
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Coremask", child->key) == 0) {
+ status = cf_util_get_string_buffer(child, phc->eal_config.coremask,
+ sizeof(phc->eal_config.coremask));
+ DEBUG("dpdk_common: EAL:Coremask %s", phc->eal_config.coremask);
+ } else if (strcasecmp("MemoryChannels", child->key) == 0) {
+ status =
+ cf_util_get_string_buffer(child, phc->eal_config.memory_channels,
+ sizeof(phc->eal_config.memory_channels));
+ DEBUG("dpdk_common: EAL:Memory Channels %s",
+ phc->eal_config.memory_channels);
+ } else if (strcasecmp("SocketMemory", child->key) == 0) {
+ status = cf_util_get_string_buffer(child, phc->eal_config.socket_memory,
+ sizeof(phc->eal_config.socket_memory));
+ DEBUG("dpdk_common: EAL:Socket memory %s", phc->eal_config.socket_memory);
+ } else if (strcasecmp("FilePrefix", child->key) == 0) {
+ char prefix[DATA_MAX_NAME_LEN];
+
+ status = cf_util_get_string_buffer(child, prefix, sizeof(prefix));
+ if (status == 0) {
+#if RTE_VERSION <= RTE_VERSION_NUM(18, 5, 0, 0)
+ snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN,
+ "/var/run/.%s_config", prefix);
+#else
+ snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN,
+ "/var/run/dpdk/%s/config", prefix);
+#endif
+ DEBUG("dpdk_common: EAL:File prefix %s", phc->eal_config.file_prefix);
+ }
+ } else if (strcasecmp("LogLevel", child->key) == 0) {
+ status = cf_util_get_string_buffer(child, phc->eal_config.log_level,
+ sizeof(phc->eal_config.log_level));
+ DEBUG("dpdk_common: EAL:LogLevel %s", phc->eal_config.log_level);
+ } else if (strcasecmp("RteDriverLibPath", child->key) == 0) {
+ status = cf_util_get_string_buffer(
+ child, phc->eal_config.rte_driver_lib_path,
+ sizeof(phc->eal_config.rte_driver_lib_path));
+ DEBUG("dpdk_common: EAL:RteDriverLibPath %s",
+ phc->eal_config.rte_driver_lib_path);
+ } else {
+ ERROR("dpdk_common: Invalid '%s' configuration option", child->key);
+ status = -EINVAL;
+ }
+
+ if (status != 0) {
+ ERROR("dpdk_common: Parsing EAL configuration failed");
+ break;
+ }
+ }
+
+ return status;
+}
+
+static int dpdk_shm_init(const char *name, size_t size, void **map) {
+ DPDK_HELPER_TRACE(name);
+
+ int fd = shm_open(name, O_CREAT | O_TRUNC | O_RDWR, 0666);
+ if (fd < 0) {
+ WARNING("dpdk_shm_init: Failed to open %s as SHM:%s", name, STRERRNO);
+ *map = NULL;
+ return -1;
+ }
+
+ int ret = ftruncate(fd, size);
+ if (ret != 0) {
+ WARNING("dpdk_shm_init: Failed to resize SHM:%s", STRERRNO);
+ close(fd);
+ *map = NULL;
+ dpdk_shm_cleanup(name, size, NULL);
+ return -1;
+ }
+
+ *map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (*map == MAP_FAILED) {
+ WARNING("dpdk_shm_init:Failed to mmap SHM:%s", STRERRNO);
+ close(fd);
+ *map = NULL;
+ dpdk_shm_cleanup(name, size, NULL);
+ return -1;
+ }
+ /* File descriptor no longer needed for shared memory operations */
+ close(fd);
+ memset(*map, 0, size);
+
+ return 0;
+}
+
+static void dpdk_shm_cleanup(const char *name, size_t size, void *map) {
+ DPDK_HELPER_TRACE(name);
+
+ /*
+ * Call shm_unlink first, as 'name' might be no longer accessible after munmap
+ */
+ if (shm_unlink(name))
+ ERROR("shm_unlink failure %s", STRERRNO);
+
+ if (map != NULL) {
+ if (munmap(map, size))
+ ERROR("munmap failure %s", STRERRNO);
+ }
+}
+
+void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc) {
+ if (phc)
+ return phc->priv_data;
+
+ return NULL;
+}
+
+int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc) {
+ if (phc == NULL) {
+ DPDK_CHILD_LOG("Invalid argument(phc)\n");
+ return -EINVAL;
+ }
+
+ return phc->shm_size - sizeof(dpdk_helper_ctx_t);
+}
+
+int dpdk_helper_init(const char *name, size_t data_size,
+ dpdk_helper_ctx_t **pphc) {
+ dpdk_helper_ctx_t *phc = NULL;
+ size_t shm_size = sizeof(dpdk_helper_ctx_t) + data_size;
+
+ if (pphc == NULL) {
+ ERROR("%s:Invalid argument(pphc)", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (name == NULL) {
+ ERROR("%s:Invalid argument(name)", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ DPDK_HELPER_TRACE(name);
+
+ /* Allocate dpdk_helper_ctx_t and
+ * initialize a POSIX SHared Memory (SHM) object.
+ */
+ int err = dpdk_shm_init(name, shm_size, (void **)&phc);
+ if (err != 0) {
+ return -errno;
+ }
+
+ err = sem_init(&phc->sema_cmd_start, 1, 0);
+ if (err != 0) {
+ ERROR("sema_cmd_start semaphore init failed: %s", STRERRNO);
+ int errno_m = errno;
+ dpdk_shm_cleanup(name, shm_size, (void *)phc);
+ return -errno_m;
+ }
+
+ err = sem_init(&phc->sema_cmd_complete, 1, 0);
+ if (err != 0) {
+ ERROR("sema_cmd_complete semaphore init failed: %s", STRERRNO);
+ sem_destroy(&phc->sema_cmd_start);
+ int errno_m = errno;
+ dpdk_shm_cleanup(name, shm_size, (void *)phc);
+ return -errno_m;
+ }
+
+ phc->shm_size = shm_size;
+ sstrncpy(phc->shm_name, name, sizeof(phc->shm_name));
+
+ dpdk_helper_config_default(phc);
+
+ *pphc = phc;
+
+ return 0;
+}
+
+void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) {
+ if (phc == NULL)
+ return;
+
+ DPDK_HELPER_TRACE(phc->shm_name);
+
+ close(phc->pipes[1]);
+
+ if (phc->status != DPDK_HELPER_NOT_INITIALIZED) {
+ dpdk_helper_exit_command(phc, DPDK_HELPER_GRACEFUL_QUIT);
+ }
+
+ sem_destroy(&phc->sema_cmd_start);
+ sem_destroy(&phc->sema_cmd_complete);
+ dpdk_shm_cleanup(phc->shm_name, phc->shm_size, (void *)phc);
+}
+
+static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) {
+ if (phc == NULL) {
+ ERROR("Invalid argument(phc)");
+ return -EINVAL;
+ }
+
+ DPDK_HELPER_TRACE(phc->shm_name);
+
+ phc->eal_initialized = 0;
+ phc->cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT);
+
+ /*
+ * Create a pipe for helper stdout back to collectd. This is necessary for
+ * logging EAL failures, as rte_eal_init() calls rte_panic().
+ */
+ if (phc->pipes[1]) {
+ DEBUG("dpdk_helper_spawn: collectd closing helper pipe %d", phc->pipes[1]);
+ } else {
+ DEBUG("dpdk_helper_spawn: collectd helper pipe %d, not closing",
+ phc->pipes[1]);
+ }
+
+ if (pipe(phc->pipes) != 0) {
+ DEBUG("dpdk_helper_spawn: Could not create helper pipe: %s", STRERRNO);
+ return -1;
+ }
+
+ int pipe0_flags = fcntl(phc->pipes[0], F_GETFL, 0);
+ int pipe1_flags = fcntl(phc->pipes[1], F_GETFL, 0);
+ if (pipe0_flags == -1 || pipe1_flags == -1) {
+ WARNING("dpdk_helper_spawn: error setting up pipe flags: %s", STRERRNO);
+ }
+ int pipe0_err = fcntl(phc->pipes[0], F_SETFL, pipe1_flags | O_NONBLOCK);
+ int pipe1_err = fcntl(phc->pipes[1], F_SETFL, pipe0_flags | O_NONBLOCK);
+ if (pipe0_err == -1 || pipe1_err == -1) {
+ WARNING("dpdk_helper_spawn: error setting up pipes: %s", STRERRNO);
+ }
+
+ pid_t pid = fork();
+ if (pid > 0) {
+ phc->pid = pid;
+ close(phc->pipes[1]);
+ DEBUG("%s:dpdk_helper_spawn: helper pid %lu", phc->shm_name,
+ (long)phc->pid);
+ } else if (pid == 0) {
+ /* Replace stdout with a pipe to collectd. */
+ close(phc->pipes[0]);
+ close(STDOUT_FILENO);
+ dup2(phc->pipes[1], STDOUT_FILENO);
+ DPDK_CHILD_TRACE(phc->shm_name);
+ dpdk_helper_worker(phc);
+ exit(0);
+ } else {
+ ERROR("dpdk_helper_start: Failed to fork helper process: %s", STRERRNO);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dpdk_helper_exit(dpdk_helper_ctx_t *phc,
+ enum DPDK_HELPER_STATUS status) {
+ DPDK_CHILD_LOG("%s:%s:%d %s\n", phc->shm_name, __FUNCTION__, __LINE__,
+ dpdk_helper_status_str(status));
+
+ close(phc->pipes[1]);
+
+ phc->status = status;
+
+ exit(0);
+
+ return 0;
+}
+
+static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc,
+ enum DPDK_HELPER_STATUS status) {
+ DPDK_HELPER_TRACE(phc->shm_name);
+
+ close(phc->pipes[1]);
+
+ if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) {
+ phc->status = status;
+ DEBUG("%s:%s:%d %s", phc->shm_name, __FUNCTION__, __LINE__,
+ dpdk_helper_status_str(status));
+
+ int ret = dpdk_helper_command(phc, DPDK_CMD_QUIT, NULL, 0);
+ if (ret != 0) {
+ DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__,
+ __LINE__, (long)phc->pid);
+
+ int err = kill(phc->pid, SIGKILL);
+ if (err) {
+ ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO);
+ }
+ }
+ } else {
+
+ DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__,
+ __LINE__, (long)phc->pid);
+
+ int err = kill(phc->pid, SIGKILL);
+ if (err) {
+ ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO);
+ }
+ }
+
+ return 0;
+}
+
+static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc) {
+ phc->status = DPDK_HELPER_INITIALIZING_EAL;
+ DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (start)\n",
+ phc->shm_name, __FUNCTION__, __LINE__);
+
+ char *argp[DPDK_EAL_ARGC * 2 + 1];
+ int argc = 0;
+
+ /* EAL config must be initialized */
+ assert(phc->eal_config.coremask[0] != 0);
+ assert(phc->eal_config.memory_channels[0] != 0);
+ assert(phc->eal_config.file_prefix[0] != 0);
+
+ argp[argc++] = "collectd-dpdk";
+
+ argp[argc++] = "-c";
+ argp[argc++] = phc->eal_config.coremask;
+
+ argp[argc++] = "-n";
+ argp[argc++] = phc->eal_config.memory_channels;
+
+ if (strcasecmp(phc->eal_config.socket_memory, "") != 0) {
+ argp[argc++] = "--socket-mem";
+ argp[argc++] = phc->eal_config.socket_memory;
+ }
+
+ if (strcasecmp(phc->eal_config.file_prefix, DPDK_DEFAULT_RTE_CONFIG) != 0) {
+ argp[argc++] = "--file-prefix";
+ argp[argc++] = phc->eal_config.file_prefix;
+ }
+
+ argp[argc++] = "--proc-type";
+ argp[argc++] = "secondary";
+
+ if (strcasecmp(phc->eal_config.log_level, "") != 0) {
+ argp[argc++] = "--log-level";
+ argp[argc++] = phc->eal_config.log_level;
+ }
+ if (strcasecmp(phc->eal_config.rte_driver_lib_path, "") != 0) {
+ argp[argc++] = "-d";
+ argp[argc++] = phc->eal_config.rte_driver_lib_path;
+ }
+
+ assert(argc <= (DPDK_EAL_ARGC * 2 + 1));
+
+ int ret = rte_eal_init(argc, argp);
+
+ if (ret < 0) {
+
+ phc->eal_initialized = 0;
+
+ DPDK_CHILD_LOG("dpdk_helper_eal_init: ERROR initializing EAL ret=%d\n",
+ ret);
+
+ printf("dpdk_helper_eal_init: EAL arguments: ");
+ for (int i = 0; i < argc; i++) {
+ printf("%s ", argp[i]);
+ }
+ printf("\n");
+
+ return ret;
+ }
+
+ phc->eal_initialized = 1;
+
+ DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (done)\n",
+ phc->shm_name, __FUNCTION__, __LINE__);
+
+ return 0;
+}
+
+static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid) {
+ DPDK_CHILD_TRACE(phc->shm_name);
+
+ struct timespec ts;
+ cdtime_t now = cdtime();
+ cdtime_t cmd_wait_time = MS_TO_CDTIME_T(1500) + phc->cmd_wait_time * 2;
+ ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time);
+
+ int ret = sem_timedwait(&phc->sema_cmd_start, &ts);
+ DPDK_CHILD_LOG("%s:%s:%d pid=%lu got sema_cmd_start (ret=%d, errno=%d)\n",
+ phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), ret,
+ errno);
+
+ if (phc->cmd == DPDK_CMD_QUIT) {
+ DPDK_CHILD_LOG("%s:%s:%d pid=%lu exiting\n", phc->shm_name, __FUNCTION__,
+ __LINE__, (long)getpid());
+ exit(0);
+ } else if (ret == -1 && errno == ETIMEDOUT) {
+ if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) {
+ DPDK_CHILD_LOG("%s:dpdk_helper_cmd_wait: sem timedwait()"
+ " timeout, did collectd terminate?\n",
+ phc->shm_name);
+ dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT);
+ }
+ }
+#if COLLECT_DEBUG
+ int val = 0;
+ if (sem_getvalue(&phc->sema_cmd_start, &val) == 0)
+ DPDK_CHILD_LOG("%s:%s:%d pid=%lu wait sema_cmd_start (value=%d)\n",
+ phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), val);
+#endif
+
+ /* Parent PID change means collectd died so quit the helper process. */
+ if (ppid != getppid()) {
+ DPDK_CHILD_LOG("dpdk_helper_cmd_wait: parent PID changed, quitting.\n");
+ dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT);
+ }
+
+ /* Checking for DPDK primary process. */
+ if (!rte_eal_primary_proc_alive(phc->eal_config.file_prefix)) {
+ if (phc->eal_initialized) {
+ DPDK_CHILD_LOG(
+ "%s:dpdk_helper_cmd_wait: no primary alive but EAL initialized:"
+ " quitting.\n",
+ phc->shm_name);
+ dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED);
+ }
+
+ phc->status = DPDK_HELPER_WAITING_ON_PRIMARY;
+ DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_WAITING_ON_PRIMARY\n", phc->shm_name,
+ __FUNCTION__, __LINE__);
+
+ return -1;
+ }
+
+ if (!phc->eal_initialized) {
+ int ret = dpdk_helper_eal_init(phc);
+ if (ret != 0) {
+ DPDK_CHILD_LOG("Error initializing EAL\n");
+ dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED);
+ }
+ phc->status = DPDK_HELPER_ALIVE_SENDING_EVENTS;
+ DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_ALIVE_SENDING_EVENTS\n", phc->shm_name,
+ __FUNCTION__, __LINE__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dpdk_helper_worker(dpdk_helper_ctx_t *phc) {
+ DPDK_CHILD_TRACE(phc->shm_name);
+
+ pid_t ppid = getppid();
+
+ while (1) {
+ if (dpdk_helper_cmd_wait(phc, ppid) == 0) {
+ DPDK_CHILD_LOG("%s:%s:%d DPDK command handle (cmd=%d, pid=%lu)\n",
+ phc->shm_name, __FUNCTION__, __LINE__, phc->cmd,
+ (long)getpid());
+ phc->cmd_result = dpdk_helper_command_handler(phc, phc->cmd);
+ } else {
+ phc->cmd_result = -1;
+ }
+
+ /* now kick collectd to get results */
+ int err = sem_post(&phc->sema_cmd_complete);
+ DPDK_CHILD_LOG("%s:%s:%d post sema_cmd_complete (pid=%lu)\n", phc->shm_name,
+ __FUNCTION__, __LINE__, (long)getpid());
+ if (err) {
+ DPDK_CHILD_LOG("dpdk_helper_worker: error posting sema_cmd_complete "
+ "semaphore (%s)\n",
+ STRERRNO);
+ }
+
+#if COLLECT_DEBUG
+ int val = 0;
+ if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0)
+ DPDK_CHILD_LOG("%s:%s:%d pid=%lu sema_cmd_complete (value=%d)\n",
+ phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(),
+ val);
+#endif
+
+ } /* while(1) */
+
+ return 0;
+}
+
+static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status) {
+ switch (status) {
+ case DPDK_HELPER_ALIVE_SENDING_EVENTS:
+ return "DPDK_HELPER_ALIVE_SENDING_EVENTS";
+ case DPDK_HELPER_WAITING_ON_PRIMARY:
+ return "DPDK_HELPER_WAITING_ON_PRIMARY";
+ case DPDK_HELPER_INITIALIZING:
+ return "DPDK_HELPER_INITIALIZING";
+ case DPDK_HELPER_INITIALIZING_EAL:
+ return "DPDK_HELPER_INITIALIZING_EAL";
+ case DPDK_HELPER_GRACEFUL_QUIT:
+ return "DPDK_HELPER_GRACEFUL_QUIT";
+ case DPDK_HELPER_NOT_INITIALIZED:
+ return "DPDK_HELPER_NOT_INITIALIZED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc) {
+ DEBUG("%s:%s:%d pid=%u %s", phc->shm_name, __FUNCTION__, __LINE__, getpid(),
+ dpdk_helper_status_str(phc->status));
+
+ if (phc->status == DPDK_HELPER_GRACEFUL_QUIT) {
+ return 0;
+ } else if (phc->status == DPDK_HELPER_NOT_INITIALIZED) {
+ phc->status = DPDK_HELPER_INITIALIZING;
+ DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__,
+ __LINE__);
+ int err = dpdk_helper_spawn(phc);
+ if (err) {
+ ERROR("dpdkstat: error spawning helper %s", STRERRNO);
+ }
+ return -1;
+ }
+
+ pid_t ws = waitpid(phc->pid, NULL, WNOHANG);
+ if (ws != 0) {
+ phc->status = DPDK_HELPER_INITIALIZING;
+ DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__,
+ __LINE__);
+ int err = dpdk_helper_spawn(phc);
+ if (err) {
+ ERROR("dpdkstat: error spawning helper %s", STRERRNO);
+ }
+ return -1;
+ }
+
+ if (phc->status == DPDK_HELPER_INITIALIZING_EAL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) {
+ char buf[DPDK_MAX_BUFFER_SIZE];
+ char out[DPDK_MAX_BUFFER_SIZE];
+
+ /* non blocking check on helper logging pipe */
+ struct pollfd fds = {
+ .fd = phc->pipes[0], .events = POLLIN,
+ };
+ int data_avail = poll(&fds, 1, 0);
+ DEBUG("%s:dpdk_helper_check_pipe: poll data_avail=%d", phc->shm_name,
+ data_avail);
+ if (data_avail < 0) {
+ if (errno != EINTR || errno != EAGAIN) {
+ ERROR("%s: poll(2) failed: %s", phc->shm_name, STRERRNO);
+ }
+ }
+ while (data_avail) {
+ int nbytes = read(phc->pipes[0], buf, (sizeof(buf) - 1));
+ DEBUG("%s:dpdk_helper_check_pipe: read nbytes=%d", phc->shm_name, nbytes);
+ if (nbytes <= 0)
+ break;
+ buf[nbytes] = '\0';
+ sstrncpy(out, buf, sizeof(out));
+ DEBUG("%s: helper process:\n%s", phc->shm_name, out);
+ }
+}
+
+int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result,
+ cdtime_t cmd_wait_time) {
+ if (phc == NULL) {
+ ERROR("Invalid argument(phc)");
+ return -EINVAL;
+ }
+
+ DEBUG("%s:%s:%d pid=%lu, cmd=%d", phc->shm_name, __FUNCTION__, __LINE__,
+ (long)getpid(), cmd);
+
+ phc->cmd_wait_time = cmd_wait_time;
+
+ int ret = dpdk_helper_status_check(phc);
+
+ dpdk_helper_check_pipe(phc);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ DEBUG("%s: DPDK command execute (cmd=%d)", phc->shm_name, cmd);
+
+ phc->cmd_result = 0;
+ phc->cmd = cmd;
+
+ /* kick helper to process command */
+ int err = sem_post(&phc->sema_cmd_start);
+ if (err) {
+ ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)",
+ STRERRNO);
+ }
+
+#if COLLECT_DEBUG
+ int val = 0;
+ if (sem_getvalue(&phc->sema_cmd_start, &val) == 0)
+ DEBUG("%s:dpdk_helper_command: post sema_cmd_start (value=%d)",
+ phc->shm_name, val);
+#endif
+
+ if (phc->cmd != DPDK_CMD_QUIT) {
+
+ /* wait for helper to complete processing */
+ struct timespec ts;
+ cdtime_t now = cdtime();
+
+ if (phc->status != DPDK_HELPER_ALIVE_SENDING_EVENTS) {
+ cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT);
+ }
+
+ ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time);
+ ret = sem_timedwait(&phc->sema_cmd_complete, &ts);
+ if (ret == -1 && errno == ETIMEDOUT) {
+ DPDK_HELPER_TRACE(phc->shm_name);
+ DEBUG("%s:sema_cmd_start: timeout in collectd thread: is a DPDK Primary "
+ "running?",
+ phc->shm_name);
+ return -ETIMEDOUT;
+ }
+
+#if COLLECT_DEBUG
+ val = 0;
+ if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0)
+ DEBUG("%s:dpdk_helper_command: wait sema_cmd_complete (value=%d)",
+ phc->shm_name, val);
+#endif
+
+ if (result) {
+ *result = phc->cmd_result;
+ }
+ }
+
+ dpdk_helper_check_pipe(phc);
+
+ DEBUG("%s: DPDK command complete (cmd=%d, result=%d)", phc->shm_name,
+ phc->cmd, phc->cmd_result);
+
+ return 0;
+}
+
+uint64_t strtoull_safe(const char *str, int *err) {
+ uint64_t val = 0;
+ char *endptr;
+ int res = 0;
+
+ val = strtoull(str, &endptr, 16);
+ if (*endptr) {
+ ERROR("%s Failed to parse the value %s, endptr=%c", __FUNCTION__, str,
+ *endptr);
+ res = -EINVAL;
+ }
+ if (err != NULL)
+ *err = res;
+ return val;
+}
+
+uint128_t str_to_uint128(const char *str, int len) {
+ uint128_t lcore_mask;
+ int err = 0;
+
+ memset(&lcore_mask, 0, sizeof(lcore_mask));
+
+ if (len <= 2 || strncmp(str, "0x", 2) != 0) {
+ ERROR("%s Value %s should be represened in hexadecimal format",
+ __FUNCTION__, str);
+ return lcore_mask;
+ }
+ /* If str is <= 64 bit long ('0x' + 16 chars = 18 chars) then
+ * conversion is straightforward. Otherwise str is splitted into 64b long
+ * blocks */
+ if (len <= 18) {
+ lcore_mask.low = strtoull_safe(str, &err);
+ if (err)
+ return lcore_mask;
+ } else {
+ char low_str[DATA_MAX_NAME_LEN];
+ char high_str[DATA_MAX_NAME_LEN * 2];
+
+ memset(high_str, 0, sizeof(high_str));
+ memset(low_str, 0, sizeof(low_str));
+
+ strncpy(high_str, str, len - 16);
+ strncpy(low_str, str + len - 16, 16);
+
+ lcore_mask.low = strtoull_safe(low_str, &err);
+ if (err)
+ return lcore_mask;
+
+ lcore_mask.high = strtoull_safe(high_str, &err);
+ if (err) {
+ lcore_mask.low = 0;
+ return lcore_mask;
+ }
+ }
+ return lcore_mask;
+}
+
+uint8_t dpdk_helper_eth_dev_count(void) {
+#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0)
+ uint8_t ports = rte_eth_dev_count();
+#else
+ uint8_t ports = rte_eth_dev_count_avail();
+#endif
+ if (ports == 0) {
+ ERROR(
+ "%s:%d: No DPDK ports available. Check bound devices to DPDK driver.\n",
+ __FUNCTION__, __LINE__);
+ return ports;
+ }
+
+ if (ports > RTE_MAX_ETHPORTS) {
+ ERROR("%s:%d: Number of DPDK ports (%u) is greater than "
+ "RTE_MAX_ETHPORTS=%d. Ignoring extra ports\n",
+ __FUNCTION__, __LINE__, ports, RTE_MAX_ETHPORTS);
+ ports = RTE_MAX_ETHPORTS;
+ }
+
+ return ports;
+}
--- /dev/null
+/*
+ * collectd - src/utils_dpdk.h
+ * MIT License
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Maryam Tahhan <maryam.tahhan@intel.com>
+ * Harry van Haaren <harry.van.haaren@intel.com>
+ * Taras Chornyi <tarasx.chornyi@intel.com>
+ * Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ * Krzysztof Matczak <krzysztofx.matczak@intel.com>
+ */
+
+#ifndef UTILS_DPDK_H
+#define UTILS_DPDK_H
+
+#include <rte_version.h>
+
+#define ERR_BUF_SIZE 1024
+
+enum DPDK_CMD {
+ DPDK_CMD_NONE = 0,
+ DPDK_CMD_QUIT,
+ DPDK_CMD_INIT,
+ DPDK_CMD_GET_STATS,
+ DPDK_CMD_GET_EVENTS,
+ __DPDK_CMD_LAST,
+};
+
+struct dpdk_eal_config_s {
+ char coremask[DATA_MAX_NAME_LEN];
+ char memory_channels[DATA_MAX_NAME_LEN];
+ char socket_memory[DATA_MAX_NAME_LEN];
+ char file_prefix[DATA_MAX_NAME_LEN];
+ char log_level[DATA_MAX_NAME_LEN];
+ char rte_driver_lib_path[PATH_MAX];
+};
+typedef struct dpdk_eal_config_s dpdk_eal_config_t;
+
+struct uint128_s {
+ u_int64_t high;
+ u_int64_t low;
+};
+typedef struct uint128_s uint128_t;
+
+typedef struct dpdk_helper_ctx_s dpdk_helper_ctx_t;
+
+int dpdk_helper_init(const char *name, size_t data_size,
+ dpdk_helper_ctx_t **pphc);
+void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc);
+int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci);
+int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec);
+int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec);
+int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result,
+ cdtime_t cmd_wait_time);
+void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc);
+int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc);
+uint8_t dpdk_helper_eth_dev_count(void);
+
+/* forward declaration of handler function that is called by helper from
+ * child process. not implemented in helper. must be provided by client. */
+int dpdk_helper_command_handler(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd);
+
+uint128_t str_to_uint128(const char *str, int len);
+
+/* logging functions that should be used in child process */
+#define DPDK_CHILD_LOG(...) fprintf(stdout, __VA_ARGS__)
+#define DPDK_CHILD_TRACE(_name) \
+ fprintf(stdout, "%s:%s:%d pid=%u\n", _name, __FUNCTION__, __LINE__, getpid())
+
+#endif /* UTILS_DPDK_H */
--- /dev/null
+/**
+ * collectd - src/utils_format_graphite.c
+ * Copyright (C) 2012 Thomas Meson
+ * Copyright (C) 2012 Florian octo Forster
+ *
+ * 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:
+ * Thomas Meson <zllak at hycik.org>
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/format_graphite/format_graphite.h"
+#include "utils_cache.h"
+
+#define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r"
+
+/* Utils functions to format data sets in graphite format.
+ * Largely taken from write_graphite.c as it remains the same formatting */
+
+static int gr_format_values(char *ret, size_t ret_len, int ds_num,
+ const data_set_t *ds, const value_list_t *vl,
+ gauge_t const *rates) {
+ size_t offset = 0;
+ int status;
+
+ assert(0 == strcmp(ds->type, vl->type));
+
+ memset(ret, 0, ret_len);
+
+#define BUFFER_ADD(...) \
+ do { \
+ status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \
+ if (status < 1) { \
+ return -1; \
+ } else if (((size_t)status) >= (ret_len - offset)) { \
+ return -1; \
+ } else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
+ BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge);
+ else if (rates != NULL)
+ BUFFER_ADD("%f", rates[ds_num]);
+ else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
+ BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
+ else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
+ BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
+ else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
+ BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
+ else {
+ P_ERROR("gr_format_values: Unknown data source type: %i",
+ ds->ds[ds_num].type);
+ return -1;
+ }
+
+#undef BUFFER_ADD
+
+ return 0;
+}
+
+static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
+ char escape_char, bool preserve_separator) {
+ memset(dst, 0, dst_len);
+
+ if (src == NULL)
+ return;
+
+ for (size_t i = 0; i < dst_len; i++) {
+ if (src[i] == 0) {
+ dst[i] = 0;
+ break;
+ }
+
+ if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) ||
+ iscntrl((int)src[i]))
+ dst[i] = escape_char;
+ else
+ dst[i] = src[i];
+ }
+}
+
+static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl,
+ char const *ds_name, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
+ char n_host[DATA_MAX_NAME_LEN];
+ char n_plugin[DATA_MAX_NAME_LEN];
+ char n_plugin_instance[DATA_MAX_NAME_LEN];
+ char n_type[DATA_MAX_NAME_LEN];
+ char n_type_instance[DATA_MAX_NAME_LEN];
+
+ char tmp_plugin[DATA_MAX_NAME_LEN + 8];
+ char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17];
+ char tmp_type[DATA_MAX_NAME_LEN + 6];
+ char tmp_type_instance[DATA_MAX_NAME_LEN + 15];
+ char tmp_metric[3 * DATA_MAX_NAME_LEN + 2];
+ char tmp_ds_name[DATA_MAX_NAME_LEN + 9];
+
+ if (prefix == NULL)
+ prefix = "";
+
+ if (postfix == NULL)
+ postfix = "";
+
+ gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
+ gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1);
+ gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+ sizeof(n_plugin_instance), escape_char, 1);
+ gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1);
+ gr_copy_escape_part(n_type_instance, vl->type_instance,
+ sizeof(n_type_instance), escape_char, 1);
+
+ snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin);
+
+ if (n_plugin_instance[0] != '\0')
+ snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance),
+ ";plugin_instance=%s", n_plugin_instance);
+ else
+ tmp_plugin_instance[0] = '\0';
+
+ if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0)
+ snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type);
+ else
+ tmp_type[0] = '\0';
+
+ if (n_type_instance[0] != '\0') {
+ if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) ||
+ strcmp(n_plugin_instance, n_type_instance) != 0)
+ snprintf(tmp_type_instance, sizeof(tmp_type_instance),
+ ";type_instance=%s", n_type_instance);
+ else
+ tmp_type_instance[0] = '\0';
+ } else
+ tmp_type_instance[0] = '\0';
+
+ /* Assert always_append_ds -> ds_name */
+ assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+ if (ds_name != NULL) {
+ snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name);
+
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name);
+ else
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type,
+ ds_name);
+ } else {
+ tmp_ds_name[0] = '\0';
+
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin);
+ else
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type);
+ }
+
+ snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric,
+ postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type,
+ tmp_type_instance, tmp_ds_name);
+
+ return 0;
+}
+
+static int gr_format_name(char *ret, int ret_len, value_list_t const *vl,
+ char const *ds_name, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
+ char n_host[DATA_MAX_NAME_LEN];
+ char n_plugin[DATA_MAX_NAME_LEN];
+ char n_plugin_instance[DATA_MAX_NAME_LEN];
+ char n_type[DATA_MAX_NAME_LEN];
+ char n_type_instance[DATA_MAX_NAME_LEN];
+
+ char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1];
+ char tmp_type[2 * DATA_MAX_NAME_LEN + 1];
+
+ if (prefix == NULL)
+ prefix = "";
+
+ if (postfix == NULL)
+ postfix = "";
+
+ bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR);
+
+ gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+ sizeof(n_plugin_instance), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_type_instance, vl->type_instance,
+ sizeof(n_type_instance), escape_char, preserve_separator);
+
+ if (n_plugin_instance[0] != '\0')
+ snprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin,
+ (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+ n_plugin_instance);
+ else
+ sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin));
+
+ if (n_type_instance[0] != '\0') {
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type));
+ else
+ snprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type,
+ (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+ n_type_instance);
+ } else
+ sstrncpy(tmp_type, n_type, sizeof(tmp_type));
+
+ /* Assert always_append_ds -> ds_name */
+ assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+ if (ds_name != NULL) {
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) &&
+ strcmp(tmp_plugin, tmp_type) == 0)
+ snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix,
+ tmp_plugin, ds_name);
+ else
+ snprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix,
+ tmp_plugin, tmp_type, ds_name);
+ } else
+ snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin,
+ tmp_type);
+
+ return 0;
+}
+
+static void escape_graphite_string(char *buffer, char escape_char) {
+ assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL);
+
+ for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0';
+ head += strcspn(head, GRAPHITE_FORBIDDEN))
+ *head = escape_char;
+}
+
+int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
+ value_list_t const *vl, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
+ int status = 0;
+ int buffer_pos = 0;
+
+ gauge_t *rates = NULL;
+ if (flags & GRAPHITE_STORE_RATES) {
+ rates = uc_get_rate(ds, vl);
+ if (rates == NULL) {
+ P_ERROR("format_graphite: error with uc_get_rate");
+ return -1;
+ }
+ }
+
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char const *ds_name = NULL;
+ char key[10 * DATA_MAX_NAME_LEN];
+ char values[512];
+ size_t message_len;
+ char message[1024];
+
+ if ((flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds->ds_num > 1))
+ ds_name = ds->ds[i].name;
+
+ /* Copy the identifier to `key' and escape it. */
+ if (flags & GRAPHITE_USE_TAGS) {
+ status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix,
+ postfix, escape_char, flags);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_name_tagged");
+ sfree(rates);
+ return status;
+ }
+ } else {
+ status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
+ escape_char, flags);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_name");
+ sfree(rates);
+ return status;
+ }
+ }
+
+ escape_graphite_string(key, escape_char);
+
+ /* Convert the values to an ASCII representation and put that into
+ * `values'. */
+ status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_values");
+ sfree(rates);
+ return status;
+ }
+
+ /* Compute the graphite command */
+ message_len =
+ (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values,
+ (unsigned int)CDTIME_T_TO_TIME_T(vl->time));
+ if (message_len >= sizeof(message)) {
+ P_ERROR("format_graphite: message buffer too small: "
+ "Need %" PRIsz " bytes.",
+ message_len + 1);
+ sfree(rates);
+ return -ENOMEM;
+ }
+
+ /* Append it in case we got multiple data set */
+ if ((buffer_pos + message_len) >= buffer_size) {
+ P_ERROR("format_graphite: target buffer too small");
+ sfree(rates);
+ return -ENOMEM;
+ }
+ memcpy((void *)(buffer + buffer_pos), message, message_len);
+ buffer_pos += message_len;
+ buffer[buffer_pos] = '\0';
+ }
+ sfree(rates);
+ return status;
+} /* int format_graphite */
--- /dev/null
+/**
+ * collectd - src/utils_format_graphite.h
+ * Copyright (C) 2012 Thomas Meson
+ *
+ * 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
+ *
+ * Author:
+ * Thomas Meson <zllak at hycik.org>
+ **/
+
+#ifndef UTILS_FORMAT_GRAPHITE_H
+#define UTILS_FORMAT_GRAPHITE_H 1
+
+#include "collectd.h"
+
+#include "plugin.h"
+
+#define GRAPHITE_STORE_RATES 0x01
+#define GRAPHITE_SEPARATE_INSTANCES 0x02
+#define GRAPHITE_ALWAYS_APPEND_DS 0x04
+#define GRAPHITE_DROP_DUPE_FIELDS 0x08
+#define GRAPHITE_PRESERVE_SEPARATOR 0x10
+#define GRAPHITE_USE_TAGS 0x20
+
+int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
+ const value_list_t *vl, const char *prefix,
+ const char *postfix, const char escape_char,
+ unsigned int flags);
+
+#endif /* UTILS_FORMAT_GRAPHITE_H */
--- /dev/null
+/**
+ * collectd - src/utils_format_graphite_test.c
+ * Copyright (C) 2016 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */
+#include "utils/format_graphite/format_graphite.h"
+
+static data_set_t ds_single = {
+ .type = "single",
+ .ds_num = 1,
+ .ds = &(data_source_t){"value", DS_TYPE_GAUGE, NAN, NAN},
+};
+
+/*
+static data_set_t ds_double = {
+ .type = "double",
+ .ds_num = 2,
+ .ds =
+ (data_source_t[]){
+ {"one", DS_TYPE_DERIVE, 0, NAN}, {"two", DS_TYPE_DERIVE, 0, NAN},
+ },
+};
+*/
+
+DEF_TEST(metric_name) {
+ struct {
+ const char *plugin_instance;
+ const char *type_instance;
+ const char *prefix;
+ const char *suffix;
+ unsigned int flags;
+ const char *want_name;
+ } cases[] = {
+ {
+ .want_name = "example@com.test.single",
+ },
+ /* plugin and type instances */
+ {
+ .plugin_instance = "foo",
+ .type_instance = "bar",
+ .want_name = "example@com.test-foo.single-bar",
+ },
+ {
+ .plugin_instance = NULL,
+ .type_instance = "bar",
+ .want_name = "example@com.test.single-bar",
+ },
+ {
+ .plugin_instance = "foo",
+ .type_instance = NULL,
+ .want_name = "example@com.test-foo.single",
+ },
+ /* special chars */
+ {
+ .plugin_instance = "foo (test)",
+ .type_instance = "test: \"hello\"",
+ .want_name = "example@com.test-foo@@test@.single-test@@@hello@",
+ },
+ /* flag GRAPHITE_SEPARATE_INSTANCES */
+ {
+ .plugin_instance = "foo",
+ .type_instance = "bar",
+ .flags = GRAPHITE_SEPARATE_INSTANCES,
+ .want_name = "example@com.test.foo.single.bar",
+ },
+ /* flag GRAPHITE_ALWAYS_APPEND_DS */
+ {
+ .plugin_instance = "foo",
+ .type_instance = "bar",
+ .flags = GRAPHITE_ALWAYS_APPEND_DS,
+ .want_name = "example@com.test-foo.single-bar.value",
+ },
+ /* flag GRAPHITE_PRESERVE_SEPARATOR */
+ {
+ .plugin_instance = "f.o.o",
+ .type_instance = "b.a.r",
+ .flags = 0,
+ .want_name = "example@com.test-f@o@o.single-b@a@r",
+ },
+ {
+ .plugin_instance = "f.o.o",
+ .type_instance = "b.a.r",
+ .flags = GRAPHITE_PRESERVE_SEPARATOR,
+ .want_name = "example.com.test-f.o.o.single-b.a.r",
+ },
+ /* prefix and suffix */
+ {
+ .prefix = "foo.",
+ .suffix = ".bar",
+ .want_name = "foo.example@com.bar.test.single",
+ },
+ {
+ .prefix = NULL,
+ .suffix = ".bar",
+ .want_name = "example@com.bar.test.single",
+ },
+ {
+ .prefix = "foo.",
+ .suffix = NULL,
+ .want_name = "foo.example@com.test.single",
+ },
+ /* flag GRAPHITE_USE_TAGS */
+ {.flags = GRAPHITE_USE_TAGS,
+ .want_name = "test.single;host=example.com;plugin=test;type=single"},
+ {.plugin_instance = "f.o.o",
+ .type_instance = "b.a.r",
+ .flags = GRAPHITE_USE_TAGS,
+ .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
+ "f.o.o;type=single;type_instance=b.a.r"},
+ {.flags = GRAPHITE_USE_TAGS ^ GRAPHITE_ALWAYS_APPEND_DS,
+ .want_name = "test.single.value;host=example.com;plugin=test;type="
+ "single;ds_name=value"},
+ {.plugin_instance = "foo",
+ .type_instance = "foo",
+ .flags = GRAPHITE_USE_TAGS ^ GRAPHITE_DROP_DUPE_FIELDS,
+ .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
+ "foo;type=single"},
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ value_list_t vl = {
+ .values = &(value_t){.gauge = 42},
+ .values_len = 1,
+ .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
+ .interval = TIME_T_TO_CDTIME_T_STATIC(10),
+ .host = "example.com",
+ .plugin = "test",
+ .type = "single",
+ };
+
+ char want[1024];
+ snprintf(want, sizeof(want), "%s 42 1480063672\r\n", cases[i].want_name);
+
+ if (cases[i].plugin_instance != NULL)
+ sstrncpy(vl.plugin_instance, cases[i].plugin_instance,
+ sizeof(vl.plugin_instance));
+ if (cases[i].type_instance != NULL)
+ sstrncpy(vl.type_instance, cases[i].type_instance,
+ sizeof(vl.type_instance));
+
+ char got[1024];
+ EXPECT_EQ_INT(0, format_graphite(got, sizeof(got), &ds_single, &vl,
+ cases[i].prefix, cases[i].suffix, '@',
+ cases[i].flags));
+ EXPECT_EQ_STR(want, got);
+ }
+
+ return 0;
+}
+
+DEF_TEST(null_termination) {
+ value_list_t vl = {
+ .values = &(value_t){.gauge = 1337},
+ .values_len = 1,
+ .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
+ .interval = TIME_T_TO_CDTIME_T_STATIC(10),
+ .host = "example.com",
+ .plugin = "test",
+ .type = "single",
+ };
+ char const *want = "example_com.test.single 1337 1480063672\r\n";
+
+ char buffer[128];
+ for (size_t i = 0; i < sizeof(buffer); i++)
+ buffer[i] = (char)i;
+
+ EXPECT_EQ_INT(0, format_graphite(buffer, sizeof(buffer), &ds_single, &vl,
+ NULL, NULL, '_', 0));
+ EXPECT_EQ_STR(want, buffer);
+ EXPECT_EQ_INT(0, buffer[strlen(want)]);
+ for (size_t i = strlen(want) + 1; i < sizeof(buffer); i++)
+ EXPECT_EQ_INT((int)i, (int)buffer[i]);
+
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(metric_name);
+ RUN_TEST(null_termination);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_format_json.c
+ * Copyright (C) 2009-2015 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/format_json/format_json.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils_cache.h"
+
+#if HAVE_LIBYAJL
+#include <yajl/yajl_common.h>
+#include <yajl/yajl_gen.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
+#define HAVE_YAJL_V2 1
+#endif
+#endif
+
+static int json_escape_string(char *buffer, size_t buffer_size, /* {{{ */
+ const char *string) {
+ size_t dst_pos;
+
+ if ((buffer == NULL) || (string == NULL))
+ return -EINVAL;
+
+ if (buffer_size < 3)
+ return -ENOMEM;
+
+ dst_pos = 0;
+
+#define BUFFER_ADD(c) \
+ do { \
+ if (dst_pos >= (buffer_size - 1)) { \
+ buffer[buffer_size - 1] = '\0'; \
+ return -ENOMEM; \
+ } \
+ buffer[dst_pos] = (c); \
+ dst_pos++; \
+ } while (0)
+
+ /* Escape special characters */
+ BUFFER_ADD('"');
+ for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) {
+ if ((string[src_pos] == '"') || (string[src_pos] == '\\')) {
+ BUFFER_ADD('\\');
+ BUFFER_ADD(string[src_pos]);
+ } else if (string[src_pos] <= 0x001F)
+ BUFFER_ADD('?');
+ else
+ BUFFER_ADD(string[src_pos]);
+ } /* for */
+ BUFFER_ADD('"');
+ buffer[dst_pos] = 0;
+
+#undef BUFFER_ADD
+
+ return 0;
+} /* }}} int json_escape_string */
+
+static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl,
+ int store_rates) {
+ size_t offset = 0;
+ gauge_t *rates = NULL;
+
+ memset(buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) \
+ do { \
+ int status; \
+ status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
+ if (status < 1) { \
+ sfree(rates); \
+ return -1; \
+ } else if (((size_t)status) >= (buffer_size - offset)) { \
+ sfree(rates); \
+ return -ENOMEM; \
+ } else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ BUFFER_ADD("[");
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ if (i > 0)
+ BUFFER_ADD(",");
+
+ if (ds->ds[i].type == DS_TYPE_GAUGE) {
+ if (isfinite(vl->values[i].gauge))
+ BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[i].gauge);
+ else
+ BUFFER_ADD("null");
+ } else if (store_rates) {
+ if (rates == NULL)
+ rates = uc_get_rate(ds, vl);
+ if (rates == NULL) {
+ WARNING("utils_format_json: uc_get_rate failed.");
+ sfree(rates);
+ return -1;
+ }
+
+ if (isfinite(rates[i]))
+ BUFFER_ADD(JSON_GAUGE_FORMAT, rates[i]);
+ else
+ BUFFER_ADD("null");
+ } else if (ds->ds[i].type == DS_TYPE_COUNTER)
+ BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[i].counter);
+ else if (ds->ds[i].type == DS_TYPE_DERIVE)
+ BUFFER_ADD("%" PRIi64, vl->values[i].derive);
+ else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+ BUFFER_ADD("%" PRIu64, vl->values[i].absolute);
+ else {
+ ERROR("format_json: Unknown data source type: %i", ds->ds[i].type);
+ sfree(rates);
+ return -1;
+ }
+ } /* for ds->ds_num */
+ BUFFER_ADD("]");
+
+#undef BUFFER_ADD
+
+ sfree(rates);
+ return 0;
+} /* }}} int values_to_json */
+
+static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds) {
+ size_t offset = 0;
+
+ memset(buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) \
+ do { \
+ int status; \
+ status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
+ if (status < 1) \
+ return -1; \
+ else if (((size_t)status) >= (buffer_size - offset)) \
+ return -ENOMEM; \
+ else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ BUFFER_ADD("[");
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ if (i > 0)
+ BUFFER_ADD(",");
+
+ BUFFER_ADD("\"%s\"", DS_TYPE_TO_STRING(ds->ds[i].type));
+ } /* for ds->ds_num */
+ BUFFER_ADD("]");
+
+#undef BUFFER_ADD
+
+ return 0;
+} /* }}} int dstypes_to_json */
+
+static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds) {
+ size_t offset = 0;
+
+ memset(buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) \
+ do { \
+ int status; \
+ status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
+ if (status < 1) \
+ return -1; \
+ else if (((size_t)status) >= (buffer_size - offset)) \
+ return -ENOMEM; \
+ else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ BUFFER_ADD("[");
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ if (i > 0)
+ BUFFER_ADD(",");
+
+ BUFFER_ADD("\"%s\"", ds->ds[i].name);
+ } /* for ds->ds_num */
+ BUFFER_ADD("]");
+
+#undef BUFFER_ADD
+
+ return 0;
+} /* }}} int dsnames_to_json */
+
+static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */
+ meta_data_t *meta, char **keys,
+ size_t keys_num) {
+ size_t offset = 0;
+ int status;
+
+ buffer[0] = 0;
+
+#define BUFFER_ADD(...) \
+ do { \
+ status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
+ if (status < 1) \
+ return -1; \
+ else if (((size_t)status) >= (buffer_size - offset)) \
+ return -ENOMEM; \
+ else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ for (size_t i = 0; i < keys_num; ++i) {
+ int type;
+ char *key = keys[i];
+
+ type = meta_data_type(meta, key);
+ if (type == MD_TYPE_STRING) {
+ char *value = NULL;
+ if (meta_data_get_string(meta, key, &value) == 0) {
+ char temp[512] = "";
+
+ status = json_escape_string(temp, sizeof(temp), value);
+ sfree(value);
+ if (status != 0)
+ return status;
+
+ BUFFER_ADD(",\"%s\":%s", key, temp);
+ }
+ } else if (type == MD_TYPE_SIGNED_INT) {
+ int64_t value = 0;
+ if (meta_data_get_signed_int(meta, key, &value) == 0)
+ BUFFER_ADD(",\"%s\":%" PRIi64, key, value);
+ } else if (type == MD_TYPE_UNSIGNED_INT) {
+ uint64_t value = 0;
+ if (meta_data_get_unsigned_int(meta, key, &value) == 0)
+ BUFFER_ADD(",\"%s\":%" PRIu64, key, value);
+ } else if (type == MD_TYPE_DOUBLE) {
+ double value = 0.0;
+ if (meta_data_get_double(meta, key, &value) == 0)
+ BUFFER_ADD(",\"%s\":%f", key, value);
+ } else if (type == MD_TYPE_BOOLEAN) {
+ bool value = false;
+ if (meta_data_get_boolean(meta, key, &value) == 0)
+ BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false");
+ }
+ } /* for (keys) */
+
+ if (offset == 0)
+ return ENOENT;
+
+ buffer[0] = '{'; /* replace leading ',' */
+ BUFFER_ADD("}");
+
+#undef BUFFER_ADD
+
+ return 0;
+} /* }}} int meta_data_keys_to_json */
+
+static int meta_data_to_json(char *buffer, size_t buffer_size, /* {{{ */
+ meta_data_t *meta) {
+ char **keys = NULL;
+ size_t keys_num;
+ int status;
+
+ if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL))
+ return EINVAL;
+
+ status = meta_data_toc(meta, &keys);
+ if (status <= 0)
+ return status;
+ keys_num = (size_t)status;
+
+ status = meta_data_keys_to_json(buffer, buffer_size, meta, keys, keys_num);
+
+ for (size_t i = 0; i < keys_num; ++i)
+ sfree(keys[i]);
+ sfree(keys);
+
+ return status;
+} /* }}} int meta_data_to_json */
+
+static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl,
+ int store_rates) {
+ char temp[512];
+ size_t offset = 0;
+ int status;
+
+ memset(buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) \
+ do { \
+ status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
+ if (status < 1) \
+ return -1; \
+ else if (((size_t)status) >= (buffer_size - offset)) \
+ return -ENOMEM; \
+ else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ /* All value lists have a leading comma. The first one will be replaced with
+ * a square bracket in `format_json_finalize'. */
+ BUFFER_ADD(",{");
+
+ status = values_to_json(temp, sizeof(temp), ds, vl, store_rates);
+ if (status != 0)
+ return status;
+ BUFFER_ADD("\"values\":%s", temp);
+
+ status = dstypes_to_json(temp, sizeof(temp), ds);
+ if (status != 0)
+ return status;
+ BUFFER_ADD(",\"dstypes\":%s", temp);
+
+ status = dsnames_to_json(temp, sizeof(temp), ds);
+ if (status != 0)
+ return status;
+ BUFFER_ADD(",\"dsnames\":%s", temp);
+
+ BUFFER_ADD(",\"time\":%.3f", CDTIME_T_TO_DOUBLE(vl->time));
+ BUFFER_ADD(",\"interval\":%.3f", CDTIME_T_TO_DOUBLE(vl->interval));
+
+#define BUFFER_ADD_KEYVAL(key, value) \
+ do { \
+ status = json_escape_string(temp, sizeof(temp), (value)); \
+ if (status != 0) \
+ return status; \
+ BUFFER_ADD(",\"%s\":%s", (key), temp); \
+ } while (0)
+
+ BUFFER_ADD_KEYVAL("host", vl->host);
+ BUFFER_ADD_KEYVAL("plugin", vl->plugin);
+ BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance);
+ BUFFER_ADD_KEYVAL("type", vl->type);
+ BUFFER_ADD_KEYVAL("type_instance", vl->type_instance);
+
+ if (vl->meta != NULL) {
+ char meta_buffer[buffer_size];
+ memset(meta_buffer, 0, sizeof(meta_buffer));
+ status = meta_data_to_json(meta_buffer, sizeof(meta_buffer), vl->meta);
+ if (status != 0)
+ return status;
+
+ BUFFER_ADD(",\"meta\":%s", meta_buffer);
+ } /* if (vl->meta != NULL) */
+
+ BUFFER_ADD("}");
+
+#undef BUFFER_ADD_KEYVAL
+#undef BUFFER_ADD
+
+ return 0;
+} /* }}} int value_list_to_json */
+
+static int format_json_value_list_nocheck(char *buffer, /* {{{ */
+ size_t *ret_buffer_fill,
+ size_t *ret_buffer_free,
+ const data_set_t *ds,
+ const value_list_t *vl,
+ int store_rates, size_t temp_size) {
+ char temp[temp_size];
+ int status;
+
+ status = value_list_to_json(temp, sizeof(temp), ds, vl, store_rates);
+ if (status != 0)
+ return status;
+ temp_size = strlen(temp);
+
+ memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1);
+ (*ret_buffer_fill) += temp_size;
+ (*ret_buffer_free) -= temp_size;
+
+ return 0;
+} /* }}} int format_json_value_list_nocheck */
+
+int format_json_initialize(char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free) {
+ size_t buffer_fill;
+ size_t buffer_free;
+
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
+ (ret_buffer_free == NULL))
+ return -EINVAL;
+
+ buffer_fill = *ret_buffer_fill;
+ buffer_free = *ret_buffer_free;
+
+ buffer_free = buffer_fill + buffer_free;
+ buffer_fill = 0;
+
+ if (buffer_free < 3)
+ return -ENOMEM;
+
+ memset(buffer, 0, buffer_free);
+ *ret_buffer_fill = buffer_fill;
+ *ret_buffer_free = buffer_free;
+
+ return 0;
+} /* }}} int format_json_initialize */
+
+int format_json_finalize(char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free) {
+ size_t pos;
+
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
+ (ret_buffer_free == NULL))
+ return -EINVAL;
+
+ if (*ret_buffer_free < 2)
+ return -ENOMEM;
+
+ /* Replace the leading comma added in `value_list_to_json' with a square
+ * bracket. */
+ if (buffer[0] != ',')
+ return -EINVAL;
+ buffer[0] = '[';
+
+ pos = *ret_buffer_fill;
+ buffer[pos] = ']';
+ buffer[pos + 1] = 0;
+
+ (*ret_buffer_fill)++;
+ (*ret_buffer_free)--;
+
+ return 0;
+} /* }}} int format_json_finalize */
+
+int format_json_value_list(char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free,
+ const data_set_t *ds, const value_list_t *vl,
+ int store_rates) {
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
+ (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
+ return -EINVAL;
+
+ if (*ret_buffer_free < 3)
+ return -ENOMEM;
+
+ return format_json_value_list_nocheck(buffer, ret_buffer_fill,
+ ret_buffer_free, ds, vl, store_rates,
+ (*ret_buffer_free) - 2);
+} /* }}} int format_json_value_list */
+
+#if HAVE_LIBYAJL
+static int json_add_string(yajl_gen g, char const *str) /* {{{ */
+{
+ if (str == NULL)
+ return (int)yajl_gen_null(g);
+
+ return (int)yajl_gen_string(g, (const unsigned char *)str,
+ (unsigned int)strlen(str));
+} /* }}} int json_add_string */
+
+#define JSON_ADD(g, str) \
+ do { \
+ yajl_gen_status status = json_add_string(g, str); \
+ if (status != yajl_gen_status_ok) { \
+ return -1; \
+ } \
+ } while (0)
+
+#define JSON_ADDF(g, format, ...) \
+ do { \
+ char *str = ssnprintf_alloc(format, __VA_ARGS__); \
+ yajl_gen_status status = json_add_string(g, str); \
+ free(str); \
+ if (status != yajl_gen_status_ok) { \
+ return -1; \
+ } \
+ } while (0)
+
+#define CHECK_SUCCESS(cmd) \
+ do { \
+ yajl_gen_status s = (cmd); \
+ if (s != yajl_gen_status_ok) { \
+ return (int)s; \
+ } \
+ } while (0)
+
+static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */
+{
+ if (meta == NULL)
+ return 0;
+
+ JSON_ADD(g, meta->name);
+ switch (meta->type) {
+ case NM_TYPE_STRING:
+ JSON_ADD(g, meta->nm_value.nm_string);
+ break;
+ case NM_TYPE_SIGNED_INT:
+ JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int);
+ break;
+ case NM_TYPE_UNSIGNED_INT:
+ JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int);
+ break;
+ case NM_TYPE_DOUBLE:
+ JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double);
+ break;
+ case NM_TYPE_BOOLEAN:
+ JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false");
+ break;
+ default:
+ ERROR("format_json_meta: unknown meta data type %d (name \"%s\")",
+ meta->type, meta->name);
+ CHECK_SUCCESS(yajl_gen_null(g));
+ }
+
+ return format_json_meta(g, meta->next);
+} /* }}} int format_json_meta */
+
+static int format_time(yajl_gen g, cdtime_t t) /* {{{ */
+{
+ char buffer[RFC3339NANO_SIZE] = "";
+
+ if (rfc3339nano(buffer, sizeof(buffer), t) != 0)
+ return -1;
+
+ JSON_ADD(g, buffer);
+ return 0;
+} /* }}} int format_time */
+
+static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */
+{
+ CHECK_SUCCESS(yajl_gen_array_open(g)); /* BEGIN array */
+ CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN alert */
+
+ /*
+ * labels
+ */
+ JSON_ADD(g, "labels");
+ CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN labels */
+
+ JSON_ADD(g, "alertname");
+ if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0)
+ JSON_ADDF(g, "collectd_%s", n->type);
+ else
+ JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type);
+
+ JSON_ADD(g, "instance");
+ JSON_ADD(g, n->host);
+
+ /* mangling of plugin instance and type instance into labels is copied from
+ * the Prometheus collectd exporter. */
+ if (strlen(n->plugin_instance) > 0) {
+ JSON_ADD(g, n->plugin);
+ JSON_ADD(g, n->plugin_instance);
+ }
+ if (strlen(n->type_instance) > 0) {
+ if (strlen(n->plugin_instance) > 0)
+ JSON_ADD(g, "type");
+ else
+ JSON_ADD(g, n->plugin);
+ JSON_ADD(g, n->type_instance);
+ }
+
+ JSON_ADD(g, "severity");
+ JSON_ADD(g,
+ (n->severity == NOTIF_FAILURE)
+ ? "FAILURE"
+ : (n->severity == NOTIF_WARNING)
+ ? "WARNING"
+ : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN");
+
+ JSON_ADD(g, "service");
+ JSON_ADD(g, "collectd");
+
+ CHECK_SUCCESS(yajl_gen_map_close(g)); /* END labels */
+
+ /*
+ * annotations
+ */
+ JSON_ADD(g, "annotations");
+ CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN annotations */
+
+ JSON_ADD(g, "summary");
+ JSON_ADD(g, n->message);
+
+ if (format_json_meta(g, n->meta) != 0) {
+ return -1;
+ }
+
+ CHECK_SUCCESS(yajl_gen_map_close(g)); /* END annotations */
+
+ JSON_ADD(g, "startsAt");
+ if (format_time(g, n->time) != 0) {
+ return -1;
+ }
+
+ CHECK_SUCCESS(yajl_gen_map_close(g)); /* END alert */
+ CHECK_SUCCESS(yajl_gen_array_close(g)); /* END array */
+
+ return 0;
+} /* }}} format_alert */
+
+/*
+ * Format (prometheus/alertmanager v1):
+ *
+ * [{
+ * "labels": {
+ * "alertname": "collectd_cpu",
+ * "instance": "host.example.com",
+ * "severity": "FAILURE",
+ * "service": "collectd",
+ * "cpu": "0",
+ * "type": "wait"
+ * },
+ * "annotations": {
+ * "summary": "...",
+ * // meta
+ * },
+ * "startsAt": <rfc3339 time>,
+ * "endsAt": <rfc3339 time>, // not used
+ * }]
+ */
+int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
+ notification_t const *n) {
+ yajl_gen g;
+ unsigned char const *out;
+#if HAVE_YAJL_V2
+ size_t unused_out_len;
+#else
+ unsigned int unused_out_len;
+#endif
+
+ if ((buffer == NULL) || (n == NULL))
+ return EINVAL;
+
+#if HAVE_YAJL_V2
+ g = yajl_gen_alloc(NULL);
+ if (g == NULL)
+ return -1;
+#if COLLECT_DEBUG
+ yajl_gen_config(g, yajl_gen_beautify, 1);
+ yajl_gen_config(g, yajl_gen_validate_utf8, 1);
+#endif
+
+#else /* !HAVE_YAJL_V2 */
+ yajl_gen_config conf = {0};
+#if COLLECT_DEBUG
+ conf.beautify = 1;
+ conf.indentString = " ";
+#endif
+ g = yajl_gen_alloc(&conf, NULL);
+ if (g == NULL)
+ return -1;
+#endif
+
+ if (format_alert(g, n) != 0) {
+ yajl_gen_clear(g);
+ yajl_gen_free(g);
+ return -1;
+ }
+
+ /* copy to output buffer */
+ if (yajl_gen_get_buf(g, &out, &unused_out_len) != yajl_gen_status_ok) {
+ yajl_gen_clear(g);
+ yajl_gen_free(g);
+ return -1;
+ }
+ sstrncpy(buffer, (void *)out, buffer_size);
+
+ yajl_gen_clear(g);
+ yajl_gen_free(g);
+ return 0;
+} /* }}} format_json_notification */
+#else
+int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
+ notification_t const *n) {
+ ERROR("format_json_notification: Not available (requires libyajl).");
+ return ENOTSUP;
+} /* }}} int format_json_notification */
+#endif
--- /dev/null
+/**
+ * collectd - src/utils_format_json.h
+ * Copyright (C) 2009 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_FORMAT_JSON_H
+#define UTILS_FORMAT_JSON_H 1
+
+#include "collectd.h"
+
+#include "plugin.h"
+
+#ifndef JSON_GAUGE_FORMAT
+#define JSON_GAUGE_FORMAT GAUGE_FORMAT
+#endif
+
+int format_json_initialize(char *buffer, size_t *ret_buffer_fill,
+ size_t *ret_buffer_free);
+int format_json_value_list(char *buffer, size_t *ret_buffer_fill,
+ size_t *ret_buffer_free, const data_set_t *ds,
+ const value_list_t *vl, int store_rates);
+int format_json_finalize(char *buffer, size_t *ret_buffer_fill,
+ size_t *ret_buffer_free);
+int format_json_notification(char *buffer, size_t buffer_size,
+ notification_t const *n);
+
+#endif /* UTILS_FORMAT_JSON_H */
--- /dev/null
+/**
+ * collectd - src/utils_format_json_test.c
+ * Copyright (C) 2015 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+/* Workaround for Solaris 10 defining label_t
+ * Issue #1301
+ */
+
+#include "config.h"
+#if KERNEL_SOLARIS
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200112L
+#endif
+#undef __EXTENSIONS__
+#endif
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */
+#include "utils/format_json/format_json.h"
+
+#include <yajl/yajl_common.h>
+#include <yajl/yajl_parse.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+#if YAJL_MAJOR > 1
+#define HAVE_YAJL_V2 1
+#endif
+
+typedef struct {
+ char const *key;
+ char const *value;
+} label_t;
+
+typedef struct {
+ label_t *expected_labels;
+ size_t expected_labels_num;
+
+ label_t *current_label;
+} test_case_t;
+
+#if HAVE_YAJL_V2
+static int test_map_key(void *ctx, unsigned char const *key, size_t key_len)
+#else
+static int test_map_key(void *ctx, unsigned char const *key,
+ unsigned int key_len)
+#endif
+{
+ test_case_t *c = ctx;
+ size_t i;
+
+ c->current_label = NULL;
+ for (i = 0; i < c->expected_labels_num; i++) {
+ label_t *l = c->expected_labels + i;
+ if ((strlen(l->key) == key_len) &&
+ (strncmp(l->key, (char const *)key, key_len) == 0)) {
+ c->current_label = l;
+ break;
+ }
+ }
+
+ return 1; /* continue */
+}
+
+static int expect_label(char const *name, char const *got, char const *want) {
+ bool ok = (strcmp(got, want) == 0);
+ char msg[1024];
+
+ if (ok)
+ snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\"", name, got);
+ else
+ snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\", want \"%s\"", name, got,
+ want);
+
+ OK1(ok, msg);
+ return 0;
+}
+
+#if HAVE_YAJL_V2
+static int test_string(void *ctx, unsigned char const *value, size_t value_len)
+#else
+static int test_string(void *ctx, unsigned char const *value,
+ unsigned int value_len)
+#endif
+{
+ test_case_t *c = ctx;
+
+ if (c->current_label != NULL) {
+ label_t *l = c->current_label;
+ char *got;
+ int status;
+
+ got = malloc(value_len + 1);
+ memmove(got, value, value_len);
+ got[value_len] = 0;
+
+ status = expect_label(l->key, got, l->value);
+
+ free(got);
+
+ if (status != 0)
+ return 0; /* abort */
+ }
+
+ return 1; /* continue */
+}
+
+static int expect_json_labels(char *json, label_t *labels, size_t labels_num) {
+ yajl_callbacks funcs = {
+ .yajl_string = test_string, .yajl_map_key = test_map_key,
+ };
+
+ test_case_t c = {labels, labels_num, NULL};
+
+ yajl_handle hndl;
+#if HAVE_YAJL_V2
+ CHECK_NOT_NULL(hndl = yajl_alloc(&funcs, /* alloc = */ NULL, &c));
+#else
+ CHECK_NOT_NULL(
+ hndl = yajl_alloc(&funcs, /* config = */ NULL, /* alloc = */ NULL, &c));
+#endif
+ OK(yajl_parse(hndl, (unsigned char *)json, strlen(json)) == yajl_status_ok);
+
+ yajl_free(hndl);
+ return 0;
+}
+
+DEF_TEST(notification) {
+ label_t labels[] = {
+ {"summary", "this is a message"},
+ {"alertname", "collectd_unit_test"},
+ {"instance", "example.com"},
+ {"service", "collectd"},
+ {"unit", "case"},
+ };
+
+ /* 1448284606.125 ^= 1555083754651779072 */
+ notification_t n = {NOTIF_WARNING,
+ 1555083754651779072ULL,
+ "this is a message",
+ "example.com",
+ "unit",
+ "",
+ "test",
+ "case",
+ NULL};
+
+ char got[1024];
+ CHECK_ZERO(format_json_notification(got, sizeof(got), &n));
+ // printf ("got = \"%s\";\n", got);
+
+ return expect_json_labels(got, labels, STATIC_ARRAY_SIZE(labels));
+}
+
+int main(void) {
+ RUN_TEST(notification);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_format_kairosdb.c
+ * Copyright (C) 2016 Aurelien beorn Rougemont
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Aurelien beorn Rougemont <beorn at gandi dot net>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/format_kairosdb/format_kairosdb.h"
+#include "utils_cache.h"
+
+/* This is the KAIROSDB format for write_http output
+ *
+ * Target format
+ * [
+ * {
+ * "name":"collectd.vmem"
+ * "datapoints":
+ * [
+ * [1453897164060, 97.000000]
+ * ],
+ * "tags":
+ * {
+ * "host": "fqdn.domain.tld",
+ * "plugin_instance": "vmpage_number",
+ * "type": "kernel_stack",
+ * "ds": "value"
+ * ""
+ * }
+ * }
+ * ]
+ */
+
+static int kairosdb_escape_string(char *buffer, size_t buffer_size, /* {{{ */
+ const char *string) {
+ size_t dst_pos;
+
+ if ((buffer == NULL) || (string == NULL))
+ return -EINVAL;
+
+ if (buffer_size < 3)
+ return -ENOMEM;
+
+ dst_pos = 0;
+
+#define BUFFER_ADD(c) \
+ do { \
+ if (dst_pos >= (buffer_size - 1)) { \
+ buffer[buffer_size - 1] = '\0'; \
+ return -ENOMEM; \
+ } \
+ buffer[dst_pos] = (c); \
+ dst_pos++; \
+ } while (0)
+
+ /* Escape special characters */
+ /* authorize -_. and alpha num but also escapes " */
+ BUFFER_ADD('"');
+ for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) {
+ if (isalnum(string[src_pos]) || 0x2d == string[src_pos] ||
+ 0x2e == string[src_pos] || 0x5f == string[src_pos])
+ BUFFER_ADD(tolower(string[src_pos]));
+ } /* for */
+ BUFFER_ADD('"');
+ buffer[dst_pos] = 0;
+
+#undef BUFFER_ADD
+
+ return 0;
+} /* }}} int kairosdb_escape_string */
+
+static int values_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl,
+ int store_rates, size_t ds_idx) {
+ size_t offset = 0;
+ gauge_t *rates = NULL;
+
+ memset(buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) \
+ do { \
+ int status; \
+ status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
+ if (status < 1) { \
+ sfree(rates); \
+ return -1; \
+ } else if (((size_t)status) >= (buffer_size - offset)) { \
+ sfree(rates); \
+ return -ENOMEM; \
+ } else \
+ offset += ((size_t)status); \
+ } while (0)
+
+ if (ds->ds[ds_idx].type == DS_TYPE_GAUGE) {
+ if (isfinite(vl->values[ds_idx].gauge)) {
+ BUFFER_ADD("[[");
+ BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
+ BUFFER_ADD(",");
+ BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[ds_idx].gauge);
+ } else {
+ DEBUG("utils_format_kairosdb: invalid vl->values[ds_idx].gauge for "
+ "%s|%s|%s|%s|%s",
+ vl->plugin, vl->plugin_instance, vl->type, vl->type_instance,
+ ds->ds[ds_idx].name);
+ return -1;
+ }
+ } else if (store_rates) {
+ if (rates == NULL)
+ rates = uc_get_rate(ds, vl);
+ if (rates == NULL) {
+ WARNING("utils_format_kairosdb: uc_get_rate failed for %s|%s|%s|%s|%s",
+ vl->plugin, vl->plugin_instance, vl->type, vl->type_instance,
+ ds->ds[ds_idx].name);
+
+ return -1;
+ }
+
+ if (isfinite(rates[ds_idx])) {
+ BUFFER_ADD("[[");
+ BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
+ BUFFER_ADD(",");
+ BUFFER_ADD(JSON_GAUGE_FORMAT, rates[ds_idx]);
+ } else {
+ WARNING("utils_format_kairosdb: invalid rates[ds_idx] for %s|%s|%s|%s|%s",
+ vl->plugin, vl->plugin_instance, vl->type, vl->type_instance,
+ ds->ds[ds_idx].name);
+ sfree(rates);
+ return -1;
+ }
+ } else if (ds->ds[ds_idx].type == DS_TYPE_COUNTER) {
+ BUFFER_ADD("[[");
+ BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
+ BUFFER_ADD(",");
+ BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_idx].counter);
+ } else if (ds->ds[ds_idx].type == DS_TYPE_DERIVE) {
+ BUFFER_ADD("[[");
+ BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
+ BUFFER_ADD(",");
+ BUFFER_ADD("%" PRIi64, vl->values[ds_idx].derive);
+ } else if (ds->ds[ds_idx].type == DS_TYPE_ABSOLUTE) {
+ BUFFER_ADD("[[");
+ BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
+ BUFFER_ADD(",");
+ BUFFER_ADD("%" PRIu64, vl->values[ds_idx].absolute);
+ } else {
+ ERROR("format_kairosdb: Unknown data source type: %i", ds->ds[ds_idx].type);
+ sfree(rates);
+ return -1;
+ }
+ BUFFER_ADD("]]");
+
+#undef BUFFER_ADD
+
+ DEBUG("format_kairosdb: values_to_kairosdb: buffer = %s;", buffer);
+ sfree(rates);
+ return 0;
+} /* }}} int values_to_kairosdb */
+
+static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl,
+ int store_rates,
+ char const *const *http_attrs,
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
+ char temp[512];
+ size_t offset = 0;
+ int status;
+
+ memset(buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) \
+ do { \
+ status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
+ if (status < 1) \
+ return -1; \
+ else if (((size_t)status) >= (buffer_size - offset)) \
+ return -ENOMEM; \
+ else \
+ offset += ((size_t)status); \
+ } while (0)
+
+#define BUFFER_ADD_KEYVAL(key, value) \
+ do { \
+ status = kairosdb_escape_string(temp, sizeof(temp), (value)); \
+ if (status != 0) \
+ return status; \
+ BUFFER_ADD(",\"%s\": %s", (key), temp); \
+ } while (0)
+
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ /* All value lists have a leading comma. The first one will be replaced with
+ * a square bracket in `format_kairosdb_finalize'. */
+ BUFFER_ADD(",{\"name\":\"");
+
+ if (metrics_prefix != NULL) {
+ BUFFER_ADD("%s.", metrics_prefix);
+ }
+
+ BUFFER_ADD("%s", vl->plugin);
+
+ status = values_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, i);
+ if (status != 0)
+ return status;
+
+ BUFFER_ADD("\", \"datapoints\": %s", temp);
+
+ /*
+ * Now adds meta data to metric as tags
+ */
+
+ memset(temp, 0, sizeof(temp));
+
+ if (data_ttl != 0)
+ BUFFER_ADD(", \"ttl\": %i", data_ttl);
+
+ BUFFER_ADD(", \"tags\":\{");
+
+ BUFFER_ADD("\"host\": \"%s\"", vl->host);
+ for (size_t j = 0; j < http_attrs_num; j += 2) {
+ BUFFER_ADD(", \"%s\":", http_attrs[j]);
+ BUFFER_ADD(" \"%s\"", http_attrs[j + 1]);
+ }
+
+ if (strlen(vl->plugin_instance))
+ BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance);
+ BUFFER_ADD_KEYVAL("type", vl->type);
+ if (strlen(vl->type_instance))
+ BUFFER_ADD_KEYVAL("type_instance", vl->type_instance);
+ if (ds->ds_num != 1)
+ BUFFER_ADD_KEYVAL("ds", ds->ds[i].name);
+ BUFFER_ADD("}}");
+ } /* for ds->ds_num */
+
+#undef BUFFER_ADD_KEYVAL
+#undef BUFFER_ADD
+
+ DEBUG("format_kairosdb: value_list_to_kairosdb: buffer = %s;", buffer);
+
+ return 0;
+} /* }}} int value_list_to_kairosdb */
+
+static int format_kairosdb_value_list_nocheck(
+ char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free, const data_set_t *ds,
+ const value_list_t *vl, int store_rates, size_t temp_size,
+ char const *const *http_attrs, size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
+ char temp[temp_size];
+ int status;
+
+ status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates,
+ http_attrs, http_attrs_num, data_ttl,
+ metrics_prefix);
+ if (status != 0)
+ return status;
+ temp_size = strlen(temp);
+
+ memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1);
+ (*ret_buffer_fill) += temp_size;
+ (*ret_buffer_free) -= temp_size;
+
+ return 0;
+} /* }}} int format_kairosdb_value_list_nocheck */
+
+int format_kairosdb_initialize(char *buffer, /* {{{ */
+ size_t *ret_buffer_fill,
+ size_t *ret_buffer_free) {
+ size_t buffer_fill;
+ size_t buffer_free;
+
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
+ (ret_buffer_free == NULL))
+ return -EINVAL;
+
+ buffer_fill = *ret_buffer_fill;
+ buffer_free = *ret_buffer_free;
+
+ buffer_free = buffer_fill + buffer_free;
+ buffer_fill = 0;
+
+ if (buffer_free < 3)
+ return -ENOMEM;
+
+ memset(buffer, 0, buffer_free);
+ *ret_buffer_fill = buffer_fill;
+ *ret_buffer_free = buffer_free;
+
+ return 0;
+} /* }}} int format_kairosdb_initialize */
+
+int format_kairosdb_finalize(char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free) {
+ size_t pos;
+
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
+ (ret_buffer_free == NULL))
+ return -EINVAL;
+
+ if (*ret_buffer_free < 2)
+ return -ENOMEM;
+
+ /* Replace the leading comma added in `value_list_to_kairosdb' with a square
+ * bracket. */
+ if (buffer[0] != ',')
+ return -EINVAL;
+ buffer[0] = '[';
+
+ pos = *ret_buffer_fill;
+ buffer[pos] = ']';
+ buffer[pos + 1] = 0;
+
+ (*ret_buffer_fill)++;
+ (*ret_buffer_free)--;
+
+ return 0;
+} /* }}} int format_kairosdb_finalize */
+
+int format_kairosdb_value_list(char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free,
+ const data_set_t *ds, const value_list_t *vl,
+ int store_rates, char const *const *http_attrs,
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
+ (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
+ return -EINVAL;
+
+ if (*ret_buffer_free < 3)
+ return -ENOMEM;
+
+ return format_kairosdb_value_list_nocheck(
+ buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates,
+ (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl,
+ metrics_prefix);
+} /* }}} int format_kairosdb_value_list */
--- /dev/null
+/**
+ * collectd - src/utils_format_kairosdb.h
+ * Copyright (C) 2016 Aurelien Rougemont
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Aurelien beorn Rougemont <beorn at gandi dot net>
+ **/
+
+#ifndef UTILS_FORMAT_KAIROSDB_H
+#define UTILS_FORMAT_KAIROSDB_H 1
+
+#include "collectd.h"
+
+#include "plugin.h"
+
+#ifndef JSON_GAUGE_FORMAT
+#define JSON_GAUGE_FORMAT GAUGE_FORMAT
+#endif
+
+int format_kairosdb_initialize(char *buffer, size_t *ret_buffer_fill,
+ size_t *ret_buffer_free);
+int format_kairosdb_value_list(char *buffer, size_t *ret_buffer_fill,
+ size_t *ret_buffer_free, const data_set_t *ds,
+ const value_list_t *vl, int store_rates,
+ char const *const *http_attrs,
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix);
+int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill,
+ size_t *ret_buffer_free);
+
+#endif /* UTILS_FORMAT_KAIROSDB_H */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/format_stackdriver/format_stackdriver.h"
+
+#include "plugin.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils_cache.h"
+#include "utils_time.h"
+
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_parse.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+
+struct sd_output_s {
+ sd_resource_t *res;
+ yajl_gen gen;
+ c_avl_tree_t *staged;
+ c_avl_tree_t *metric_descriptors;
+};
+
+struct sd_label_s {
+ char *key;
+ char *value;
+};
+typedef struct sd_label_s sd_label_t;
+
+struct sd_resource_s {
+ char *type;
+
+ sd_label_t *labels;
+ size_t labels_num;
+};
+
+static int json_string(yajl_gen gen, char const *s) /* {{{ */
+{
+ yajl_gen_status status =
+ yajl_gen_string(gen, (unsigned char const *)s, strlen(s));
+ if (status != yajl_gen_status_ok)
+ return (int)status;
+
+ return 0;
+} /* }}} int json_string */
+
+static int json_time(yajl_gen gen, cdtime_t t) {
+ char buffer[64];
+
+ size_t status = rfc3339(buffer, sizeof(buffer), t);
+ if (status != 0) {
+ return status;
+ }
+
+ return json_string(gen, buffer);
+} /* }}} int json_time */
+
+/* MonitoredResource
+ *
+ * {
+ * "type": "library.googleapis.com/book",
+ * "labels": {
+ * "/genre": "fiction",
+ * "/media": "paper"
+ * "/title": "The Old Man and the Sea"
+ * }
+ * }
+ */
+static int format_gcm_resource(yajl_gen gen, sd_resource_t *res) /* {{{ */
+{
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "type") || json_string(gen, res->type);
+ if (status != 0)
+ return status;
+
+ if (res->labels_num != 0) {
+ status = json_string(gen, "labels");
+ if (status != 0)
+ return status;
+
+ yajl_gen_map_open(gen);
+ for (size_t i = 0; i < res->labels_num; i++) {
+ status = json_string(gen, res->labels[i].key) ||
+ json_string(gen, res->labels[i].value);
+ if (status != 0)
+ return status;
+ }
+ yajl_gen_map_close(gen);
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_gcm_resource */
+
+/* TypedValue
+ *
+ * {
+ * // Union field, only one of the following:
+ * "int64Value": string,
+ * "doubleValue": number,
+ * }
+ */
+static int format_typed_value(yajl_gen gen, int ds_type, value_t v,
+ int64_t start_value) {
+ char integer[32];
+
+ yajl_gen_map_open(gen);
+
+ switch (ds_type) {
+ case DS_TYPE_GAUGE: {
+ int status = json_string(gen, "doubleValue");
+ if (status != 0)
+ return status;
+
+ status = (int)yajl_gen_double(gen, (double)v.gauge);
+ if (status != yajl_gen_status_ok)
+ return status;
+
+ yajl_gen_map_close(gen);
+ return 0;
+ }
+ case DS_TYPE_DERIVE: {
+ derive_t diff = v.derive - (derive_t)start_value;
+ snprintf(integer, sizeof(integer), "%" PRIi64, diff);
+ break;
+ }
+ case DS_TYPE_COUNTER: {
+ counter_t diff = counter_diff((counter_t)start_value, v.counter);
+ snprintf(integer, sizeof(integer), "%llu", diff);
+ break;
+ }
+ case DS_TYPE_ABSOLUTE: {
+ snprintf(integer, sizeof(integer), "%" PRIu64, v.absolute);
+ break;
+ }
+ default: {
+ ERROR("format_typed_value: unknown value type %d.", ds_type);
+ return EINVAL;
+ }
+ }
+
+ int status = json_string(gen, "int64Value") || json_string(gen, integer);
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_typed_value */
+
+/* MetricKind
+ *
+ * enum(
+ * "CUMULATIVE",
+ * "GAUGE"
+ * )
+*/
+static int format_metric_kind(yajl_gen gen, int ds_type) {
+ switch (ds_type) {
+ case DS_TYPE_GAUGE:
+ case DS_TYPE_ABSOLUTE:
+ return json_string(gen, "GAUGE");
+ case DS_TYPE_COUNTER:
+ case DS_TYPE_DERIVE:
+ return json_string(gen, "CUMULATIVE");
+ default:
+ ERROR("format_metric_kind: unknown value type %d.", ds_type);
+ return EINVAL;
+ }
+}
+
+/* ValueType
+ *
+ * enum(
+ * "DOUBLE",
+ * "INT64"
+ * )
+*/
+static int format_value_type(yajl_gen gen, int ds_type) {
+ return json_string(gen, (ds_type == DS_TYPE_GAUGE) ? "DOUBLE" : "INT64");
+}
+
+static int metric_type(char *buffer, size_t buffer_size, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ char const *ds_name = ds->ds[ds_index].name;
+
+#define GCM_PREFIX "custom.googleapis.com/collectd/"
+ if ((ds_index != 0) || strcmp("value", ds_name) != 0) {
+ snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s_%s", vl->plugin, vl->type,
+ ds_name);
+ } else {
+ snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s", vl->plugin, vl->type);
+ }
+
+ char const *whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789_/";
+ char *ptr = buffer + strlen(GCM_PREFIX);
+ size_t ok_len;
+ while ((ok_len = strspn(ptr, whitelist)) != strlen(ptr)) {
+ ptr[ok_len] = '_';
+ ptr += ok_len;
+ }
+
+ return 0;
+} /* }}} int metric_type */
+
+/* The metric type, including its DNS name prefix. The type is not URL-encoded.
+ * All user-defined custom metric types have the DNS name custom.googleapis.com.
+ * Metric types should use a natural hierarchical grouping. */
+static int format_metric_type(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, ds_index);
+
+ return json_string(gen, buffer);
+} /* }}} int format_metric_type */
+
+/* TimeInterval
+ *
+ * {
+ * "endTime": string,
+ * "startTime": string,
+ * }
+ */
+static int format_time_interval(yajl_gen gen, int ds_type,
+ value_list_t const *vl, cdtime_t start_time) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "endTime") || json_time(gen, vl->time);
+ if (status != 0)
+ return status;
+
+ if ((ds_type == DS_TYPE_DERIVE) || (ds_type == DS_TYPE_COUNTER)) {
+ int status = json_string(gen, "startTime") || json_time(gen, start_time);
+ if (status != 0)
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_time_interval */
+
+/* read_cumulative_state reads the start time and start value of cumulative
+ * (i.e. DERIVE or COUNTER) metrics from the cache. If a metric is seen for the
+ * first time, or when a DERIVE metric is reset, the start time is (re)set to
+ * vl->time. */
+static int read_cumulative_state(data_set_t const *ds, value_list_t const *vl,
+ int ds_index, cdtime_t *ret_start_time,
+ int64_t *ret_start_value) {
+ int ds_type = ds->ds[ds_index].type;
+ if ((ds_type != DS_TYPE_DERIVE) && (ds_type != DS_TYPE_COUNTER)) {
+ return 0;
+ }
+
+ char start_value_key[DATA_MAX_NAME_LEN];
+ snprintf(start_value_key, sizeof(start_value_key),
+ "stackdriver:start_value[%d]", ds_index);
+
+ int status =
+ uc_meta_data_get_signed_int(vl, start_value_key, ret_start_value);
+ if ((status == 0) && ((ds_type != DS_TYPE_DERIVE) ||
+ (*ret_start_value <= vl->values[ds_index].derive))) {
+ return uc_meta_data_get_unsigned_int(vl, "stackdriver:start_time",
+ ret_start_time);
+ }
+
+ if (ds_type == DS_TYPE_DERIVE) {
+ *ret_start_value = vl->values[ds_index].derive;
+ } else {
+ *ret_start_value = (int64_t)vl->values[ds_index].counter;
+ }
+ *ret_start_time = vl->time;
+
+ status = uc_meta_data_add_signed_int(vl, start_value_key, *ret_start_value);
+ if (status != 0) {
+ return status;
+ }
+ return uc_meta_data_add_unsigned_int(vl, "stackdriver:start_time",
+ *ret_start_time);
+} /* int read_cumulative_state */
+
+/* Point
+ *
+ * {
+ * "interval": {
+ * object(TimeInterval)
+ * },
+ * "value": {
+ * object(TypedValue)
+ * },
+ * }
+ */
+static int format_point(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index,
+ cdtime_t start_time, int64_t start_value) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int ds_type = ds->ds[ds_index].type;
+
+ int status =
+ json_string(gen, "interval") ||
+ format_time_interval(gen, ds_type, vl, start_time) ||
+ json_string(gen, "value") ||
+ format_typed_value(gen, ds_type, vl->values[ds_index], start_value);
+ if (status != 0)
+ return status;
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_point */
+
+/* Metric
+ *
+ * {
+ * "type": string,
+ * "labels": {
+ * string: string,
+ * ...
+ * },
+ * }
+ */
+static int format_metric(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "type") ||
+ format_metric_type(gen, ds, vl, ds_index) ||
+ json_string(gen, "labels");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_open(gen);
+ status = json_string(gen, "host") || json_string(gen, vl->host) ||
+ json_string(gen, "plugin_instance") ||
+ json_string(gen, vl->plugin_instance) ||
+ json_string(gen, "type_instance") ||
+ json_string(gen, vl->type_instance);
+ if (status != 0) {
+ return status;
+ }
+ yajl_gen_map_close(gen);
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_metric */
+
+/* TimeSeries
+ *
+ * {
+ * "metric": {
+ * object(Metric)
+ * },
+ * "resource": {
+ * object(MonitoredResource)
+ * },
+ * "metricKind": enum(MetricKind),
+ * "valueType": enum(ValueType),
+ * "points": [
+ * {
+ * object(Point)
+ * }
+ * ],
+ * }
+ */
+/* format_time_series formats a TimeSeries object. Returns EAGAIN when a
+ * cumulative metric is seen for the first time and cannot be sent to
+ * Stackdriver due to lack of state. */
+static int format_time_series(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index,
+ sd_resource_t *res) {
+ int ds_type = ds->ds[ds_index].type;
+
+ cdtime_t start_time = 0;
+ int64_t start_value = 0;
+ int status =
+ read_cumulative_state(ds, vl, ds_index, &start_time, &start_value);
+ if (status != 0) {
+ return status;
+ }
+ if (start_time == vl->time) {
+ /* for cumulative metrics, the interval must not be zero. */
+ return EAGAIN;
+ }
+
+ yajl_gen_map_open(gen);
+
+ status = json_string(gen, "metric") || format_metric(gen, ds, vl, ds_index) ||
+ json_string(gen, "resource") || format_gcm_resource(gen, res) ||
+ json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
+ json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
+ json_string(gen, "points");
+ if (status != 0)
+ return status;
+
+ yajl_gen_array_open(gen);
+
+ status = format_point(gen, ds, vl, ds_index, start_time, start_value);
+ if (status != 0)
+ return status;
+
+ yajl_gen_array_close(gen);
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_time_series */
+
+/* Request body
+ *
+ * {
+ * "timeSeries": [
+ * {
+ * object(TimeSeries)
+ * }
+ * ],
+ * }
+ */
+static int sd_output_initialize(sd_output_t *out) /* {{{ */
+{
+ yajl_gen_map_open(out->gen);
+
+ int status = json_string(out->gen, "timeSeries");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_array_open(out->gen);
+ return 0;
+} /* }}} int sd_output_initialize */
+
+static int sd_output_finalize(sd_output_t *out) /* {{{ */
+{
+ yajl_gen_array_close(out->gen);
+ yajl_gen_map_close(out->gen);
+
+ return 0;
+} /* }}} int sd_output_finalize */
+
+static void sd_output_reset_staged(sd_output_t *out) /* {{{ */
+{
+ void *key = NULL;
+
+ while (c_avl_pick(out->staged, &key, &(void *){NULL}) == 0)
+ sfree(key);
+} /* }}} void sd_output_reset_staged */
+
+sd_output_t *sd_output_create(sd_resource_t *res) /* {{{ */
+{
+ sd_output_t *out = calloc(1, sizeof(*out));
+ if (out == NULL)
+ return NULL;
+
+ out->res = res;
+
+ out->gen = yajl_gen_alloc(/* funcs = */ NULL);
+ if (out->gen == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ out->staged = c_avl_create((void *)strcmp);
+ if (out->staged == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ out->metric_descriptors = c_avl_create((void *)strcmp);
+ if (out->metric_descriptors == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ sd_output_initialize(out);
+
+ return out;
+} /* }}} sd_output_t *sd_output_create */
+
+void sd_output_destroy(sd_output_t *out) /* {{{ */
+{
+ if (out == NULL)
+ return;
+
+ if (out->metric_descriptors != NULL) {
+ void *key = NULL;
+ while (c_avl_pick(out->metric_descriptors, &key, &(void *){NULL}) == 0) {
+ sfree(key);
+ }
+ c_avl_destroy(out->metric_descriptors);
+ out->metric_descriptors = NULL;
+ }
+
+ if (out->staged != NULL) {
+ sd_output_reset_staged(out);
+ c_avl_destroy(out->staged);
+ out->staged = NULL;
+ }
+
+ if (out->gen != NULL) {
+ yajl_gen_free(out->gen);
+ out->gen = NULL;
+ }
+
+ if (out->res != NULL) {
+ sd_resource_destroy(out->res);
+ out->res = NULL;
+ }
+
+ sfree(out);
+} /* }}} void sd_output_destroy */
+
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) /* {{{ */
+{
+ /* first, check that we have all appropriate metric descriptors. */
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, i);
+
+ if (c_avl_get(out->metric_descriptors, buffer, NULL) != 0) {
+ return ENOENT;
+ }
+ }
+
+ char key[6 * DATA_MAX_NAME_LEN];
+ int status = FORMAT_VL(key, sizeof(key), vl);
+ if (status != 0) {
+ ERROR("sd_output_add: FORMAT_VL failed with status %d.", status);
+ return status;
+ }
+
+ if (c_avl_get(out->staged, key, NULL) == 0) {
+ return EEXIST;
+ }
+
+ _Bool staged = 0;
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ int status = format_time_series(out->gen, ds, vl, i, out->res);
+ if (status == EAGAIN) {
+ /* first instance of a cumulative metric */
+ continue;
+ }
+ if (status != 0) {
+ ERROR("sd_output_add: format_time_series failed with status %d.", status);
+ return status;
+ }
+ staged = 1;
+ }
+
+ if (staged) {
+ c_avl_insert(out->staged, strdup(key), NULL);
+ }
+
+ size_t json_buffer_size = 0;
+ yajl_gen_get_buf(out->gen, &(unsigned char const *){NULL}, &json_buffer_size);
+ if (json_buffer_size > 65535)
+ return ENOBUFS;
+
+ return 0;
+} /* }}} int sd_output_add */
+
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) {
+ /* {{{ */
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, i);
+
+ char *key = strdup(buffer);
+ int status = c_avl_insert(out->metric_descriptors, key, NULL);
+ if (status != 0) {
+ sfree(key);
+ return status;
+ }
+ }
+
+ return 0;
+} /* }}} int sd_output_register_metric */
+
+char *sd_output_reset(sd_output_t *out) /* {{{ */
+{
+ sd_output_finalize(out);
+
+ unsigned char const *json_buffer = NULL;
+ yajl_gen_get_buf(out->gen, &json_buffer, &(size_t){0});
+ char *ret = strdup((void const *)json_buffer);
+
+ sd_output_reset_staged(out);
+
+ yajl_gen_free(out->gen);
+ out->gen = yajl_gen_alloc(/* funcs = */ NULL);
+
+ sd_output_initialize(out);
+
+ return ret;
+} /* }}} char *sd_output_reset */
+
+sd_resource_t *sd_resource_create(char const *type) /* {{{ */
+{
+ sd_resource_t *res = malloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+ memset(res, 0, sizeof(*res));
+
+ res->type = strdup(type);
+ if (res->type == NULL) {
+ sfree(res);
+ return NULL;
+ }
+
+ res->labels = NULL;
+ res->labels_num = 0;
+
+ return res;
+} /* }}} sd_resource_t *sd_resource_create */
+
+void sd_resource_destroy(sd_resource_t *res) /* {{{ */
+{
+ if (res == NULL)
+ return;
+
+ for (size_t i = 0; i < res->labels_num; i++) {
+ sfree(res->labels[i].key);
+ sfree(res->labels[i].value);
+ }
+ sfree(res->labels);
+ sfree(res->type);
+ sfree(res);
+} /* }}} void sd_resource_destroy */
+
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value) /* {{{ */
+{
+ if ((res == NULL) || (key == NULL) || (value == NULL))
+ return EINVAL;
+
+ sd_label_t *l =
+ realloc(res->labels, sizeof(*res->labels) * (res->labels_num + 1));
+ if (l == NULL)
+ return ENOMEM;
+
+ res->labels = l;
+ l = res->labels + res->labels_num;
+
+ l->key = strdup(key);
+ l->value = strdup(value);
+ if ((l->key == NULL) || (l->value == NULL)) {
+ sfree(l->key);
+ sfree(l->value);
+ return ENOMEM;
+ }
+
+ res->labels_num++;
+ return 0;
+} /* }}} int sd_resource_add_label */
+
+/* LabelDescriptor
+ *
+ * {
+ * "key": string,
+ * "valueType": enum(ValueType),
+ * "description": string,
+ * }
+ */
+static int format_label_descriptor(yajl_gen gen, char const *key) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "key") || json_string(gen, key) ||
+ json_string(gen, "valueType") || json_string(gen, "STRING");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_label_descriptor */
+
+/* MetricDescriptor
+ *
+ * {
+ * "name": string,
+ * "type": string,
+ * "labels": [
+ * {
+ * object(LabelDescriptor)
+ * }
+ * ],
+ * "metricKind": enum(MetricKind),
+ * "valueType": enum(ValueType),
+ * "unit": string,
+ * "description": string,
+ * "displayName": string,
+ * }
+ */
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index) {
+ /* {{{ */
+ yajl_gen gen = yajl_gen_alloc(/* funcs = */ NULL);
+ if (gen == NULL) {
+ return ENOMEM;
+ }
+
+ int ds_type = ds->ds[ds_index].type;
+
+ yajl_gen_map_open(gen);
+
+ int status =
+ json_string(gen, "type") || format_metric_type(gen, ds, vl, ds_index) ||
+ json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
+ json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
+ json_string(gen, "labels");
+ if (status != 0) {
+ yajl_gen_free(gen);
+ return status;
+ }
+
+ char const *labels[] = {"host", "plugin_instance", "type_instance"};
+ yajl_gen_array_open(gen);
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(labels); i++) {
+ int status = format_label_descriptor(gen, labels[i]);
+ if (status != 0) {
+ yajl_gen_free(gen);
+ return status;
+ }
+ }
+
+ yajl_gen_array_close(gen);
+ yajl_gen_map_close(gen);
+
+ unsigned char const *tmp = NULL;
+ yajl_gen_get_buf(gen, &tmp, &(size_t){0});
+ sstrncpy(buffer, (void const *)tmp, buffer_size);
+
+ yajl_gen_free(gen);
+ return 0;
+} /* }}} int sd_format_metric_descriptor */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_FORMAT_STACKDRIVER_H
+#define UTILS_FORMAT_STACKDRIVER_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+/* sd_output_t is a buffer to which value_list_t* can be added and from which
+ * an appropriately formatted char* can be read. */
+struct sd_output_s;
+typedef struct sd_output_s sd_output_t;
+
+/* sd_resource_t represents a MonitoredResource. */
+struct sd_resource_s;
+typedef struct sd_resource_s sd_resource_t;
+
+sd_output_t *sd_output_create(sd_resource_t *res);
+
+/* sd_output_destroy frees all memory used by out, including the
+ * sd_resource_t* passed to sd_output_create. */
+void sd_output_destroy(sd_output_t *out);
+
+/* sd_output_add adds a value_list_t* to "out".
+ *
+ * Return values:
+ * - 0 Success
+ * - ENOBUFS Success, but the buffer should be flushed soon.
+ * - EEXIST The value list is already encoded in the buffer.
+ * Flush the buffer, then call sd_output_add again.
+ * - ENOENT First time we encounter this metric. Create a metric descriptor
+ * using the Stackdriver API and then call
+ * sd_output_register_metric.
+ */
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_register_metric adds the metric descriptor which vl maps to, to
+ * the list of known metric descriptors. */
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_reset resets the output and returns the previous content of the
+ * buffer. It is the caller's responsibility to call free() with the returned
+ * pointer. */
+char *sd_output_reset(sd_output_t *out);
+
+sd_resource_t *sd_resource_create(char const *type);
+void sd_resource_destroy(sd_resource_t *res);
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value);
+
+/* sd_format_metric_descriptor creates the payload for a
+ * projects.metricDescriptors.create() request. */
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index);
+
+#endif /* UTILS_FORMAT_STACKDRIVER_H */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver_test.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/format_stackdriver/format_stackdriver.h"
+
+DEF_TEST(sd_format_metric_descriptor) {
+ value_list_t vl = {
+ .host = "example.com", .plugin = "unit-test", .type = "example",
+ };
+ char got[1024];
+
+ data_set_t ds_single = {
+ .type = "example",
+ .ds_num = 1,
+ .ds =
+ &(data_source_t){
+ .name = "value", .type = DS_TYPE_GAUGE, .min = NAN, .max = NAN,
+ },
+ };
+ EXPECT_EQ_INT(
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_single, &vl, 0));
+ char const *want_single =
+ "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
+ "example\",\"metricKind\":\"GAUGE\",\"valueType\":\"DOUBLE\",\"labels\":["
+ "{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":\"plugin_"
+ "instance\",\"valueType\":\"STRING\"},{\"key\":\"type_instance\","
+ "\"valueType\":\"STRING\"}]}";
+ EXPECT_EQ_STR(want_single, got);
+
+ data_set_t ds_double = {
+ .type = "example",
+ .ds_num = 2,
+ .ds =
+ (data_source_t[]){
+ {.name = "one", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
+ {.name = "two", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
+ },
+ };
+ EXPECT_EQ_INT(
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_double, &vl, 0));
+ char const *want_double =
+ "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
+ "example_one\",\"metricKind\":\"CUMULATIVE\",\"valueType\":\"INT64\","
+ "\"labels\":[{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":"
+ "\"plugin_instance\",\"valueType\":\"STRING\"},{\"key\":\"type_"
+ "instance\",\"valueType\":\"STRING\"}]}";
+ EXPECT_EQ_STR(want_double, got);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ RUN_TEST(sd_format_metric_descriptor);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_gce.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/gce/gce.h"
+#include "utils/oauth/oauth.h"
+#include "utils_time.h"
+
+#include <curl/curl.h>
+
+#ifndef GCP_METADATA_PREFIX
+#define GCP_METADATA_PREFIX "http://metadata.google.internal/computeMetadata/v1"
+#endif
+#ifndef GCE_METADATA_HEADER
+#define GCE_METADATA_HEADER "Metadata-Flavor: Google"
+#endif
+
+#ifndef GCE_INSTANCE_ID_URL
+#define GCE_INSTANCE_ID_URL GCP_METADATA_PREFIX "/instance/id"
+#endif
+#ifndef GCE_PROJECT_NUM_URL
+#define GCE_PROJECT_NUM_URL GCP_METADATA_PREFIX "/project/numeric-project-id"
+#endif
+#ifndef GCE_PROJECT_ID_URL
+#define GCE_PROJECT_ID_URL GCP_METADATA_PREFIX "/project/project-id"
+#endif
+#ifndef GCE_ZONE_URL
+#define GCE_ZONE_URL GCP_METADATA_PREFIX "/instance/zone"
+#endif
+
+#ifndef GCE_DEFAULT_SERVICE_ACCOUNT
+#define GCE_DEFAULT_SERVICE_ACCOUNT "default"
+#endif
+
+#ifndef GCE_SCOPE_URL
+#define GCE_SCOPE_URL_FORMAT \
+ GCP_METADATA_PREFIX "/instance/service-accounts/%s/scopes"
+#endif
+#ifndef GCE_TOKEN_URL
+#define GCE_TOKEN_URL_FORMAT \
+ GCP_METADATA_PREFIX "/instance/service-accounts/%s/token"
+#endif
+
+struct blob_s {
+ char *data;
+ size_t size;
+};
+typedef struct blob_s blob_t;
+
+static int on_gce = -1;
+
+static char *token = NULL;
+static char *token_email = NULL;
+static cdtime_t token_valid_until = 0;
+static pthread_mutex_t token_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static size_t write_callback(void *contents, size_t size, size_t nmemb,
+ void *ud) /* {{{ */
+{
+ size_t realsize = size * nmemb;
+ blob_t *blob = ud;
+
+ if ((0x7FFFFFF0 < blob->size) || (0x7FFFFFF0 - blob->size < realsize)) {
+ ERROR("utils_gce: write_callback: integer overflow");
+ return 0;
+ }
+
+ blob->data = realloc(blob->data, blob->size + realsize + 1);
+ if (blob->data == NULL) {
+ /* out of memory! */
+ ERROR(
+ "utils_gce: write_callback: not enough memory (realloc returned NULL)");
+ return 0;
+ }
+
+ memcpy(blob->data + blob->size, contents, realsize);
+ blob->size += realsize;
+ blob->data[blob->size] = 0;
+
+ return realsize;
+} /* }}} size_t write_callback */
+
+/* read_url will issue a GET request for the given URL, setting the magic GCE
+ * metadata header in the process. On success, the response body is returned
+ * and it's the caller's responsibility to free it. On failure, an error is
+ * logged and NULL is returned. */
+static char *read_url(char const *url) /* {{{ */
+{
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ ERROR("utils_gce: curl_easy_init failed.");
+ return NULL;
+ }
+
+ struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
+
+ char curl_errbuf[CURL_ERROR_SIZE];
+ blob_t blob = {0};
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &blob);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+
+ int status = curl_easy_perform(curl);
+ if (status != CURLE_OK) {
+ ERROR("utils_gce: fetching %s failed: %s", url, curl_errbuf);
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return NULL;
+ }
+
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ ERROR("write_gcm plugin: fetching %s failed: HTTP error %ld", url,
+ http_code);
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return NULL;
+ }
+
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return blob.data;
+} /* }}} char *read_url */
+
+_Bool gce_check(void) /* {{{ */
+{
+ if (on_gce != -1)
+ return on_gce == 1;
+
+ DEBUG("utils_gce: Checking whether I'm running on GCE ...");
+
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ ERROR("utils_gce: curl_easy_init failed.");
+ return 0;
+ }
+
+ struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
+
+ char curl_errbuf[CURL_ERROR_SIZE];
+ blob_t blob = {NULL, 0};
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &blob);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, GCP_METADATA_PREFIX "/");
+
+ int status = curl_easy_perform(curl);
+ if ((status != CURLE_OK) || (blob.data == NULL) ||
+ (strstr(blob.data, "Metadata-Flavor: Google") == NULL)) {
+ DEBUG("utils_gce: ... no (%s)",
+ (status != CURLE_OK)
+ ? "curl_easy_perform failed"
+ : (blob.data == NULL) ? "blob.data == NULL"
+ : "Metadata-Flavor header not found");
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 0;
+ return 0;
+ }
+ sfree(blob.data);
+
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ DEBUG("utils_gce: ... no (HTTP status %ld)", http_code);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 0;
+ return 0;
+ }
+
+ DEBUG("utils_gce: ... yes");
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 1;
+ return 1;
+} /* }}} _Bool gce_check */
+
+char *gce_project_id(void) /* {{{ */
+{
+ return read_url(GCE_PROJECT_ID_URL);
+} /* }}} char *gce_project_id */
+
+char *gce_instance_id(void) /* {{{ */
+{
+ return read_url(GCE_INSTANCE_ID_URL);
+} /* }}} char *gce_instance_id */
+
+char *gce_zone(void) /* {{{ */
+{
+ return read_url(GCE_ZONE_URL);
+} /* }}} char *gce_instance_id */
+
+char *gce_scope(char const *email) /* {{{ */
+{
+ char url[1024];
+
+ snprintf(url, sizeof(url), GCE_SCOPE_URL_FORMAT,
+ (email != NULL) ? email : GCE_DEFAULT_SERVICE_ACCOUNT);
+
+ return read_url(url);
+} /* }}} char *gce_scope */
+
+int gce_access_token(char const *email, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ char url[1024];
+ char *json;
+ cdtime_t now = cdtime();
+
+ pthread_mutex_lock(&token_lock);
+
+ if (email == NULL)
+ email = GCE_DEFAULT_SERVICE_ACCOUNT;
+
+ if ((token_email != NULL) && (strcmp(email, token_email) == 0) &&
+ (token_valid_until > now)) {
+ sstrncpy(buffer, token, buffer_size);
+ pthread_mutex_unlock(&token_lock);
+ return 0;
+ }
+
+ snprintf(url, sizeof(url), GCE_TOKEN_URL_FORMAT, email);
+ json = read_url(url);
+ if (json == NULL) {
+ pthread_mutex_unlock(&token_lock);
+ return -1;
+ }
+
+ char tmp[256];
+ cdtime_t expires_in = 0;
+ int status = oauth_parse_json_token(json, tmp, sizeof(tmp), &expires_in);
+ sfree(json);
+ if (status != 0) {
+ pthread_mutex_unlock(&token_lock);
+ return status;
+ }
+
+ sfree(token);
+ token = strdup(tmp);
+
+ sfree(token_email);
+ token_email = strdup(email);
+
+ /* let tokens expire a bit early */
+ expires_in = (expires_in * 95) / 100;
+ token_valid_until = now + expires_in;
+
+ sstrncpy(buffer, token, buffer_size);
+ pthread_mutex_unlock(&token_lock);
+ return 0;
+} /* }}} char *gce_token */
--- /dev/null
+/**
+ * collectd - src/utils_gce.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_GCE_H
+#define UTILS_GCE_H 1
+
+/* gce_check returns 1 when running on Google Compute Engine (GCE) and 0
+ * otherwise. */
+_Bool gce_check(void);
+
+/* gce_project_id returns the project ID of the instance, as configured when
+ * creating the project.
+ * For example "example-project-a". */
+char *gce_project_id(void);
+
+/* gce_instance_id returns the unique ID of the GCE instance. */
+char *gce_instance_id(void);
+
+/* gce_zone returns the zone in which the GCE instance runs. */
+char *gce_zone(void);
+
+/* gce_scope returns the list of scopes for the given service account (or the
+ * default service account when NULL is passed). */
+char *gce_scope(char const *email);
+
+/* gce_access_token acquires an OAuth access token for the given service account
+ * (or
+ * the default service account when NULL is passed) and stores it in buffer.
+ * Access tokens are automatically cached and renewed when they expire. Returns
+ * zero on success, non-zero otherwise. */
+int gce_access_token(char const *email, char *buffer, size_t buffer_size);
+
+#endif
--- /dev/null
+/**
+ * collectd - src/utils_heap.c
+ * Copyright (C) 2009 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "utils/heap/heap.h"
+
+struct c_heap_s {
+ pthread_mutex_t lock;
+ int (*compare)(const void *, const void *);
+
+ void **list;
+ size_t list_len; /* # entries used */
+ size_t list_size; /* # entries allocated */
+};
+
+enum reheap_direction { DIR_UP, DIR_DOWN };
+
+static void reheap(c_heap_t *h, size_t root, enum reheap_direction dir) {
+ size_t left;
+ size_t right;
+ size_t min;
+ int status;
+
+ /* Calculate the positions of the children */
+ left = (2 * root) + 1;
+ if (left >= h->list_len)
+ left = 0;
+
+ right = (2 * root) + 2;
+ if (right >= h->list_len)
+ right = 0;
+
+ /* Check which one of the children is smaller. */
+ if ((left == 0) && (right == 0))
+ return;
+ else if (left == 0)
+ min = right;
+ else if (right == 0)
+ min = left;
+ else {
+ status = h->compare(h->list[left], h->list[right]);
+ if (status > 0)
+ min = right;
+ else
+ min = left;
+ }
+
+ status = h->compare(h->list[root], h->list[min]);
+ if (status <= 0) {
+ /* We didn't need to change anything, so the rest of the tree should be
+ * okay now. */
+ return;
+ } else /* if (status > 0) */
+ {
+ void *tmp;
+
+ tmp = h->list[root];
+ h->list[root] = h->list[min];
+ h->list[min] = tmp;
+ }
+
+ if ((dir == DIR_UP) && (root == 0))
+ return;
+
+ if (dir == DIR_UP)
+ reheap(h, (root - 1) / 2, dir);
+ else if (dir == DIR_DOWN)
+ reheap(h, min, dir);
+} /* void reheap */
+
+c_heap_t *c_heap_create(int (*compare)(const void *, const void *)) {
+ c_heap_t *h;
+
+ if (compare == NULL)
+ return NULL;
+
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ return NULL;
+
+ pthread_mutex_init(&h->lock, /* attr = */ NULL);
+ h->compare = compare;
+
+ h->list = NULL;
+ h->list_len = 0;
+ h->list_size = 0;
+
+ return h;
+} /* c_heap_t *c_heap_create */
+
+void c_heap_destroy(c_heap_t *h) {
+ if (h == NULL)
+ return;
+
+ h->list_len = 0;
+ h->list_size = 0;
+ free(h->list);
+ h->list = NULL;
+
+ pthread_mutex_destroy(&h->lock);
+
+ free(h);
+} /* void c_heap_destroy */
+
+int c_heap_insert(c_heap_t *h, void *ptr) {
+ size_t index;
+
+ if ((h == NULL) || (ptr == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&h->lock);
+
+ assert(h->list_len <= h->list_size);
+ if (h->list_len == h->list_size) {
+ void **tmp;
+
+ tmp = realloc(h->list, (h->list_size + 16) * sizeof(*h->list));
+ if (tmp == NULL) {
+ pthread_mutex_unlock(&h->lock);
+ return -ENOMEM;
+ }
+
+ h->list = tmp;
+ h->list_size += 16;
+ }
+
+ /* Insert the new node as a leaf. */
+ index = h->list_len;
+ h->list[index] = ptr;
+ h->list_len++;
+
+ /* Reorganize the heap from bottom up. */
+ reheap(h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
+
+ pthread_mutex_unlock(&h->lock);
+ return 0;
+} /* int c_heap_insert */
+
+void *c_heap_get_root(c_heap_t *h) {
+ void *ret = NULL;
+
+ if (h == NULL)
+ return NULL;
+
+ pthread_mutex_lock(&h->lock);
+
+ if (h->list_len == 0) {
+ pthread_mutex_unlock(&h->lock);
+ return NULL;
+ } else if (h->list_len == 1) {
+ ret = h->list[0];
+ h->list[0] = NULL;
+ h->list_len = 0;
+ } else /* if (h->list_len > 1) */
+ {
+ ret = h->list[0];
+ h->list[0] = h->list[h->list_len - 1];
+ h->list[h->list_len - 1] = NULL;
+ h->list_len--;
+
+ reheap(h, /* root = */ 0, DIR_DOWN);
+ }
+
+ /* free some memory */
+ if ((h->list_len + 32) < h->list_size) {
+ void **tmp;
+
+ tmp = realloc(h->list, (h->list_len + 16) * sizeof(*h->list));
+ if (tmp != NULL) {
+ h->list = tmp;
+ h->list_size = h->list_len + 16;
+ }
+ }
+
+ pthread_mutex_unlock(&h->lock);
+
+ return ret;
+} /* void *c_heap_get_root */
--- /dev/null
+/**
+ * collectd - src/utils_heap.h
+ * Copyright (C) 2009 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_HEAP_H
+#define UTILS_HEAP_H 1
+
+struct c_heap_s;
+typedef struct c_heap_s c_heap_t;
+
+/*
+ * NAME
+ * c_heap_create
+ *
+ * DESCRIPTION
+ * Allocates a new heap.
+ *
+ * PARAMETERS
+ * `compare' The function-pointer `compare' is used to compare two keys. It
+ * has to return less than zero if its first argument is smaller
+ * then the second argument, more than zero if the first argument
+ * is bigger than the second argument and zero if they are equal.
+ * If your keys are char-pointers, you can use the `strcmp'
+ * function from the libc here.
+ *
+ * RETURN VALUE
+ * A c_heap_t-pointer upon success or NULL upon failure.
+ */
+c_heap_t *c_heap_create(int (*compare)(const void *, const void *));
+
+/*
+ * NAME
+ * c_heap_destroy
+ *
+ * DESCRIPTION
+ * Deallocates a heap. Stored value- and key-pointer are lost, but of course
+ * not freed.
+ */
+void c_heap_destroy(c_heap_t *h);
+
+/*
+ * NAME
+ * c_heap_insert
+ *
+ * DESCRIPTION
+ * Stores the key-value-pair in the heap pointed to by `h'.
+ *
+ * PARAMETERS
+ * `h' Heap to store the data in.
+ * `ptr' Value to be stored. This is typically a pointer to a data
+ * structure. The data structure is of course *not* copied and may
+ * not be free'd before the pointer has been removed from the heap
+ * again.
+ *
+ * RETURN VALUE
+ * Zero upon success, non-zero otherwise. It's less than zero if an error
+ * occurred or greater than zero if the key is already stored in the tree.
+ */
+int c_heap_insert(c_heap_t *h, void *ptr);
+
+/*
+ * NAME
+ * c_heap_get_root
+ *
+ * DESCRIPTION
+ * Removes the value at the root of the heap and returns both, key and value.
+ *
+ * PARAMETERS
+ * `h' Heap to remove key-value-pair from.
+ *
+ * RETURN VALUE
+ * The pointer passed to `c_heap_insert' or NULL if there are no more
+ * elements in the heap (or an error occurred).
+ */
+void *c_heap_get_root(c_heap_t *h);
+
+#endif /* UTILS_HEAP_H */
--- /dev/null
+/**
+ * collectd - src/tests/test_utils_heap.c
+ * Copyright (C) 2013 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/heap/heap.h"
+
+static int compare(void const *v0, void const *v1) {
+ int const *i0 = v0;
+ int const *i1 = v1;
+
+ if ((*i0) < (*i1))
+ return -1;
+ else if ((*i0) > (*i1))
+ return 1;
+ else
+ return 0;
+}
+
+DEF_TEST(simple) {
+ int values[] = {9, 5, 6, 1, 3, 4, 0, 8, 2, 7};
+ c_heap_t *h;
+
+ CHECK_NOT_NULL(h = c_heap_create(compare));
+ for (int i = 0; i < 10; i++)
+ CHECK_ZERO(c_heap_insert(h, &values[i]));
+
+ for (int i = 0; i < 5; i++) {
+ int *ret = NULL;
+ CHECK_NOT_NULL(ret = c_heap_get_root(h));
+ OK(*ret == i);
+ }
+
+ CHECK_ZERO(c_heap_insert(h, &values[6] /* = 0 */));
+ CHECK_ZERO(c_heap_insert(h, &values[3] /* = 1 */));
+ CHECK_ZERO(c_heap_insert(h, &values[8] /* = 2 */));
+ CHECK_ZERO(c_heap_insert(h, &values[4] /* = 3 */));
+ CHECK_ZERO(c_heap_insert(h, &values[5] /* = 4 */));
+
+ for (int i = 0; i < 10; i++) {
+ int *ret = NULL;
+ CHECK_NOT_NULL(ret = c_heap_get_root(h));
+ OK(*ret == i);
+ }
+
+ c_heap_destroy(h);
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(simple);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_ignorelist.c
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ * Copyright (C) 2008 Florian Forster <octo at collectd.org>
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence 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:
+ * Lubos Stanek <lubek at users.sourceforge.net>
+ * Florian Forster <octo at collectd.org>
+ **/
+/**
+ * ignorelist handles plugin's list of configured collectable
+ * entries with global ignore action
+ **/
+/**
+ * Usage:
+ *
+ * Define plugin's global pointer variable of type ignorelist_t:
+ * ignorelist_t *myconfig_ignore;
+ * If you know the state of the global ignore (IgnoreSelected),
+ * allocate the variable with:
+ * myconfig_ignore = ignorelist_create (YourKnownIgnore);
+ * If you do not know the state of the global ignore,
+ * initialize the global variable and set the ignore flag later:
+ * myconfig_ignore = ignorelist_init ();
+ * Append single entries in your cf_register'ed callback function:
+ * ignorelist_add (myconfig_ignore, newentry);
+ * When you hit the IgnoreSelected config option,
+ * offer it to the list:
+ * ignorelist_ignore (myconfig_ignore, instantly_got_value_of_ignore);
+ * That is all for the ignorelist initialization.
+ * Later during read and write (plugin's registered functions) get
+ * the information whether this entry would be collected or not:
+ * if (ignorelist_match (myconfig_ignore, thisentry))
+ * return;
+ **/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
+
+/*
+ * private prototypes
+ */
+struct ignorelist_item_s {
+#if HAVE_REGEX_H
+ regex_t *rmatch; /* regular expression entry identification */
+#endif
+ char *smatch; /* string entry identification */
+ struct ignorelist_item_s *next;
+};
+typedef struct ignorelist_item_s ignorelist_item_t;
+
+struct ignorelist_s {
+ int ignore; /* ignore entries */
+ ignorelist_item_t *head; /* pointer to the first entry */
+};
+
+/* *** *** *** ********************************************* *** *** *** */
+/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */
+/* *** *** *** ********************************************* *** *** *** */
+
+static inline void ignorelist_append(ignorelist_t *il,
+ ignorelist_item_t *item) {
+ assert((il != NULL) && (item != NULL));
+
+ item->next = il->head;
+ il->head = item;
+}
+
+#if HAVE_REGEX_H
+static int ignorelist_append_regex(ignorelist_t *il, const char *re_str) {
+ regex_t *re;
+ ignorelist_item_t *entry;
+ int status;
+
+ re = calloc(1, sizeof(*re));
+ if (re == NULL) {
+ ERROR("ignorelist_append_regex: calloc failed.");
+ return ENOMEM;
+ }
+
+ status = regcomp(re, re_str, REG_EXTENDED);
+ if (status != 0) {
+ char errbuf[1024];
+ (void)regerror(status, re, errbuf, sizeof(errbuf));
+ ERROR("utils_ignorelist: regcomp failed: %s", errbuf);
+ ERROR("ignorelist_append_regex: Compiling regular expression \"%s\" "
+ "failed: %s",
+ re_str, errbuf);
+ sfree(re);
+ return status;
+ }
+
+ entry = calloc(1, sizeof(*entry));
+ if (entry == NULL) {
+ ERROR("ignorelist_append_regex: calloc failed.");
+ regfree(re);
+ sfree(re);
+ return ENOMEM;
+ }
+ entry->rmatch = re;
+
+ ignorelist_append(il, entry);
+ return 0;
+} /* int ignorelist_append_regex */
+#endif
+
+static int ignorelist_append_string(ignorelist_t *il, const char *entry) {
+ ignorelist_item_t *new;
+
+ /* create new entry */
+ if ((new = calloc(1, sizeof(*new))) == NULL) {
+ ERROR("cannot allocate new entry");
+ return 1;
+ }
+ new->smatch = sstrdup(entry);
+
+ /* append new entry */
+ ignorelist_append(il, new);
+
+ return 0;
+} /* int ignorelist_append_string(ignorelist_t *il, const char *entry) */
+
+#if HAVE_REGEX_H
+/*
+ * check list for entry regex match
+ * return 1 if found
+ */
+static int ignorelist_match_regex(ignorelist_item_t *item, const char *entry) {
+ assert((item != NULL) && (item->rmatch != NULL) && (entry != NULL) &&
+ (strlen(entry) > 0));
+
+ /* match regex */
+ if (regexec(item->rmatch, entry, 0, NULL, 0) == 0)
+ return 1;
+
+ return 0;
+} /* int ignorelist_match_regex (ignorelist_item_t *item, const char *entry) */
+#endif
+
+/*
+ * check list for entry string match
+ * return 1 if found
+ */
+static int ignorelist_match_string(ignorelist_item_t *item, const char *entry) {
+ assert((item != NULL) && (item->smatch != NULL) && (entry != NULL) &&
+ (strlen(entry) > 0));
+
+ if (strcmp(entry, item->smatch) == 0)
+ return 1;
+
+ return 0;
+} /* int ignorelist_match_string (ignorelist_item_t *item, const char *entry) */
+
+/* *** *** *** ******************************************** *** *** *** */
+/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */
+/* *** *** *** ******************************************** *** *** *** */
+
+/*
+ * create the ignorelist_t with known ignore state
+ * return pointer to ignorelist_t
+ */
+ignorelist_t *ignorelist_create(int invert) {
+ ignorelist_t *il;
+
+ il = calloc(1, sizeof(*il));
+ if (il == NULL)
+ return NULL;
+
+ /*
+ * ->ignore == 0 => collect
+ * ->ignore == 1 => ignore
+ */
+ il->ignore = invert ? 0 : 1;
+
+ return il;
+} /* ignorelist_t *ignorelist_create (int ignore) */
+
+/*
+ * free memory used by ignorelist_t
+ */
+void ignorelist_free(ignorelist_t *il) {
+ ignorelist_item_t *this;
+ ignorelist_item_t *next;
+
+ if (il == NULL)
+ return;
+
+ for (this = il->head; this != NULL; this = next) {
+ next = this->next;
+#if HAVE_REGEX_H
+ if (this->rmatch != NULL) {
+ regfree(this->rmatch);
+ sfree(this->rmatch);
+ this->rmatch = NULL;
+ }
+#endif
+ if (this->smatch != NULL) {
+ sfree(this->smatch);
+ this->smatch = NULL;
+ }
+ sfree(this);
+ }
+
+ sfree(il);
+} /* void ignorelist_destroy (ignorelist_t *il) */
+
+/*
+ * set ignore state of the ignorelist_t
+ */
+void ignorelist_set_invert(ignorelist_t *il, int invert) {
+ if (il == NULL) {
+ DEBUG("ignore call with ignorelist_t == NULL");
+ return;
+ }
+
+ il->ignore = invert ? 0 : 1;
+} /* void ignorelist_set_invert (ignorelist_t *il, int ignore) */
+
+/*
+ * append entry into ignorelist_t
+ * return 0 for success
+ */
+int ignorelist_add(ignorelist_t *il, const char *entry) {
+ size_t len;
+
+ if (il == NULL) {
+ DEBUG("add called with ignorelist_t == NULL");
+ return 1;
+ }
+
+ len = strlen(entry);
+
+ /* append nothing */
+ if (len == 0) {
+ DEBUG("not appending: empty entry");
+ return 1;
+ }
+
+#if HAVE_REGEX_H
+ /* regex string is enclosed in "/.../" */
+ if ((len > 2) && (entry[0] == '/') && entry[len - 1] == '/') {
+ char *copy;
+ int status;
+
+ /* skip leading slash */
+ copy = strdup(entry + 1);
+ if (copy == NULL)
+ return ENOMEM;
+
+ /* trim trailing slash */
+ copy[strlen(copy) - 1] = '\0';
+
+ status = ignorelist_append_regex(il, copy);
+ sfree(copy);
+ return status;
+ }
+#endif
+
+ return ignorelist_append_string(il, entry);
+} /* int ignorelist_add (ignorelist_t *il, const char *entry) */
+
+/*
+ * check list for entry
+ * return 1 for ignored entry
+ */
+int ignorelist_match(ignorelist_t *il, const char *entry) {
+ /* if no entries, collect all */
+ if ((il == NULL) || (il->head == NULL))
+ return 0;
+
+ if ((entry == NULL) || (strlen(entry) == 0))
+ return 0;
+
+ /* traverse list and check entries */
+ for (ignorelist_item_t *traverse = il->head; traverse != NULL;
+ traverse = traverse->next) {
+#if HAVE_REGEX_H
+ if (traverse->rmatch != NULL) {
+ if (ignorelist_match_regex(traverse, entry))
+ return il->ignore;
+ } else
+#endif
+ {
+ if (ignorelist_match_string(traverse, entry))
+ return il->ignore;
+ }
+ } /* for traverse */
+
+ return 1 - il->ignore;
+} /* int ignorelist_match (ignorelist_t *il, const char *entry) */
--- /dev/null
+/**
+ * collectd - src/utils_ignorelist.h
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence 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:
+ * Lubos Stanek <lubek at users.sourceforge.net>
+ **/
+/**
+ * ignorelist handles plugin's list of configured collectable
+ * entries with global ignore action
+ **/
+
+#ifndef UTILS_IGNORELIST_H
+#define UTILS_IGNORELIST_H 1
+
+#include "collectd.h"
+
+#if HAVE_REGEX_H
+#include <regex.h>
+#endif
+
+/* public prototypes */
+
+struct ignorelist_s;
+typedef struct ignorelist_s ignorelist_t;
+
+/*
+ * create the ignorelist_t with known ignore state
+ * return pointer to ignorelist_t
+ */
+ignorelist_t *ignorelist_create(int invert);
+
+/*
+ * free memory used by ignorelist_t
+ */
+void ignorelist_free(ignorelist_t *il);
+
+/*
+ * set ignore state of the ignorelist_t
+ */
+void ignorelist_set_invert(ignorelist_t *il, int invert);
+
+/*
+ * append entry to ignorelist_t
+ * returns zero on success, non-zero upon failure.
+ */
+int ignorelist_add(ignorelist_t *il, const char *entry);
+
+/*
+ * check list for entry
+ * return 1 for ignored entry
+ */
+int ignorelist_match(ignorelist_t *il, const char *entry);
+
+#endif /* UTILS_IGNORELIST_H */
--- /dev/null
+/**
+ * collectd - src/utils_latency.c
+ * Copyright (C) 2013 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/latency/latency.h"
+
+#include <limits.h>
+#include <math.h>
+
+#ifndef LLONG_MAX
+#define LLONG_MAX 9223372036854775807LL
+#endif
+
+#ifndef HISTOGRAM_DEFAULT_BIN_WIDTH
+/* 1048576 = 2^20 ^= 1/1024 s */
+#define HISTOGRAM_DEFAULT_BIN_WIDTH 1048576
+#endif
+
+struct latency_counter_s {
+ cdtime_t start_time;
+
+ cdtime_t sum;
+ size_t num;
+
+ cdtime_t min;
+ cdtime_t max;
+
+ cdtime_t bin_width;
+ int histogram[HISTOGRAM_NUM_BINS];
+};
+
+/*
+* Histogram represents the distribution of data, it has a list of "bins".
+* Each bin represents an interval and has a count (frequency) of
+* number of values fall within its interval.
+*
+* Histogram's range is determined by the number of bins and the bin width,
+* There are 1000 bins and all bins have the same width of default 1 millisecond.
+* When a value above this range is added, Histogram's range is increased by
+* increasing the bin width (note that number of bins remains always at 1000).
+* This operation of increasing bin width is little expensive as each bin need
+* to be visited to update its count. To reduce frequent change of bin width,
+* new bin width will be the next nearest power of 2. Example: 2, 4, 8, 16, 32,
+* 64, 128, 256, 512, 1024, 2048, 5086, ...
+*
+* So, if the required bin width is 300, then new bin width will be 512 as it is
+* the next nearest power of 2.
+*/
+static void change_bin_width(latency_counter_t *lc, cdtime_t latency) /* {{{ */
+{
+ /* This function is called because the new value is above histogram's range.
+ * First find the required bin width:
+ * requiredBinWidth = (value + 1) / numBins
+ * then get the next nearest power of 2
+ * newBinWidth = 2^(ceil(log2(requiredBinWidth)))
+ */
+ double required_bin_width =
+ ((double)(latency + 1)) / ((double)HISTOGRAM_NUM_BINS);
+ double required_bin_width_logbase2 = log(required_bin_width) / log(2.0);
+ cdtime_t new_bin_width =
+ (cdtime_t)(pow(2.0, ceil(required_bin_width_logbase2)) + .5);
+ cdtime_t old_bin_width = lc->bin_width;
+
+ lc->bin_width = new_bin_width;
+
+ /* bin_width has been increased, now iterate through all bins and move the
+ * old bin's count to new bin. */
+ if (lc->num > 0) // if the histogram has data then iterate else skip
+ {
+ double width_change_ratio =
+ ((double)old_bin_width) / ((double)new_bin_width);
+
+ for (size_t i = 0; i < HISTOGRAM_NUM_BINS; i++) {
+ size_t new_bin = (size_t)(((double)i) * width_change_ratio);
+ if (i == new_bin)
+ continue;
+ assert(new_bin < i);
+
+ lc->histogram[new_bin] += lc->histogram[i];
+ lc->histogram[i] = 0;
+ }
+ }
+
+ DEBUG("utils_latency: change_bin_width: latency = %.3f; "
+ "old_bin_width = %.3f; new_bin_width = %.3f;",
+ CDTIME_T_TO_DOUBLE(latency), CDTIME_T_TO_DOUBLE(old_bin_width),
+ CDTIME_T_TO_DOUBLE(new_bin_width));
+} /* }}} void change_bin_width */
+
+latency_counter_t *latency_counter_create(void) /* {{{ */
+{
+ latency_counter_t *lc;
+
+ lc = calloc(1, sizeof(*lc));
+ if (lc == NULL)
+ return NULL;
+
+ lc->bin_width = HISTOGRAM_DEFAULT_BIN_WIDTH;
+ latency_counter_reset(lc);
+ return lc;
+} /* }}} latency_counter_t *latency_counter_create */
+
+void latency_counter_destroy(latency_counter_t *lc) /* {{{ */
+{
+ sfree(lc);
+} /* }}} void latency_counter_destroy */
+
+void latency_counter_add(latency_counter_t *lc, cdtime_t latency) /* {{{ */
+{
+ cdtime_t bin;
+
+ if ((lc == NULL) || (latency == 0) || (latency > ((cdtime_t)LLONG_MAX)))
+ return;
+
+ lc->sum += latency;
+ lc->num++;
+
+ if ((lc->min == 0) && (lc->max == 0))
+ lc->min = lc->max = latency;
+ if (lc->min > latency)
+ lc->min = latency;
+ if (lc->max < latency)
+ lc->max = latency;
+
+ /* A latency of _exactly_ 1.0 ms is stored in the buffer 0, so
+ * subtract one from the cdtime_t value so that exactly 1.0 ms get sorted
+ * accordingly. */
+ bin = (latency - 1) / lc->bin_width;
+ if (bin >= HISTOGRAM_NUM_BINS) {
+ change_bin_width(lc, latency);
+ bin = (latency - 1) / lc->bin_width;
+ if (bin >= HISTOGRAM_NUM_BINS) {
+ P_ERROR("latency_counter_add: Invalid bin: %" PRIu64, bin);
+ return;
+ }
+ }
+ lc->histogram[bin]++;
+} /* }}} void latency_counter_add */
+
+void latency_counter_reset(latency_counter_t *lc) /* {{{ */
+{
+ if (lc == NULL)
+ return;
+
+ cdtime_t bin_width = lc->bin_width;
+ cdtime_t max_bin = (lc->max - 1) / lc->bin_width;
+
+/*
+ If max latency is REDUCE_THRESHOLD times less than histogram's range,
+ then cut it in half. REDUCE_THRESHOLD must be >= 2.
+ Value of 4 is selected to reduce frequent changes of bin width.
+*/
+#define REDUCE_THRESHOLD 4
+ if ((lc->num > 0) && (lc->bin_width >= HISTOGRAM_DEFAULT_BIN_WIDTH * 2) &&
+ (max_bin < HISTOGRAM_NUM_BINS / REDUCE_THRESHOLD)) {
+ /* new bin width will be the previous power of 2 */
+ bin_width = bin_width / 2;
+
+ DEBUG("utils_latency: latency_counter_reset: max_latency = %.3f; "
+ "max_bin = %" PRIu64 "; old_bin_width = %.3f; new_bin_width = %.3f;",
+ CDTIME_T_TO_DOUBLE(lc->max), max_bin,
+ CDTIME_T_TO_DOUBLE(lc->bin_width), CDTIME_T_TO_DOUBLE(bin_width));
+ }
+
+ memset(lc, 0, sizeof(*lc));
+
+ /* preserve bin width */
+ lc->bin_width = bin_width;
+ lc->start_time = cdtime();
+} /* }}} void latency_counter_reset */
+
+cdtime_t latency_counter_get_min(latency_counter_t *lc) /* {{{ */
+{
+ if (lc == NULL)
+ return 0;
+ return lc->min;
+} /* }}} cdtime_t latency_counter_get_min */
+
+cdtime_t latency_counter_get_max(latency_counter_t *lc) /* {{{ */
+{
+ if (lc == NULL)
+ return 0;
+ return lc->max;
+} /* }}} cdtime_t latency_counter_get_max */
+
+cdtime_t latency_counter_get_sum(latency_counter_t *lc) /* {{{ */
+{
+ if (lc == NULL)
+ return 0;
+ return lc->sum;
+} /* }}} cdtime_t latency_counter_get_sum */
+
+size_t latency_counter_get_num(latency_counter_t *lc) /* {{{ */
+{
+ if (lc == NULL)
+ return 0;
+ return lc->num;
+} /* }}} size_t latency_counter_get_num */
+
+cdtime_t latency_counter_get_average(latency_counter_t *lc) /* {{{ */
+{
+ double average;
+
+ if ((lc == NULL) || (lc->num == 0))
+ return 0;
+
+ average = CDTIME_T_TO_DOUBLE(lc->sum) / ((double)lc->num);
+ return DOUBLE_TO_CDTIME_T(average);
+} /* }}} cdtime_t latency_counter_get_average */
+
+cdtime_t latency_counter_get_percentile(latency_counter_t *lc, /* {{{ */
+ double percent) {
+ double percent_upper;
+ double percent_lower;
+ double p;
+ cdtime_t latency_lower;
+ cdtime_t latency_interpolated;
+ int sum;
+ size_t i;
+
+ if ((lc == NULL) || (lc->num == 0) || !((percent > 0.0) && (percent < 100.0)))
+ return 0;
+
+ /* Find index i so that at least "percent" events are within i+1 ms. */
+ percent_upper = 0.0;
+ percent_lower = 0.0;
+ sum = 0;
+ for (i = 0; i < HISTOGRAM_NUM_BINS; i++) {
+ percent_lower = percent_upper;
+ sum += lc->histogram[i];
+ if (sum == 0)
+ percent_upper = 0.0;
+ else
+ percent_upper = 100.0 * ((double)sum) / ((double)lc->num);
+
+ if (percent_upper >= percent)
+ break;
+ }
+
+ if (i >= HISTOGRAM_NUM_BINS)
+ return 0;
+
+ assert(percent_upper >= percent);
+ assert(percent_lower < percent);
+
+ if (i == 0)
+ return lc->bin_width;
+
+ latency_lower = ((cdtime_t)i) * lc->bin_width;
+ p = (percent - percent_lower) / (percent_upper - percent_lower);
+
+ latency_interpolated =
+ latency_lower + DOUBLE_TO_CDTIME_T(p * CDTIME_T_TO_DOUBLE(lc->bin_width));
+
+ DEBUG("latency_counter_get_percentile: latency_interpolated = %.3f",
+ CDTIME_T_TO_DOUBLE(latency_interpolated));
+ return latency_interpolated;
+} /* }}} cdtime_t latency_counter_get_percentile */
+
+double latency_counter_get_rate(const latency_counter_t *lc, /* {{{ */
+ cdtime_t lower, cdtime_t upper,
+ const cdtime_t now) {
+ if ((lc == NULL) || (lc->num == 0))
+ return NAN;
+
+ if (upper && (upper < lower))
+ return NAN;
+ if (lower == upper)
+ return 0;
+
+ /* Buckets have an exclusive lower bound and an inclusive upper bound. That
+ * means that the first bucket, index 0, represents (0-bin_width]. That means
+ * that latency==bin_width needs to result in bin=0, that's why we need to
+ * subtract one before dividing by bin_width. */
+ cdtime_t lower_bin = 0;
+ if (lower)
+ /* lower is *exclusive* => determine bucket for lower+1 */
+ lower_bin = ((lower + 1) - 1) / lc->bin_width;
+
+ /* lower is greater than the longest latency observed => rate is zero. */
+ if (lower_bin >= HISTOGRAM_NUM_BINS)
+ return 0;
+
+ cdtime_t upper_bin = HISTOGRAM_NUM_BINS - 1;
+ if (upper)
+ upper_bin = (upper - 1) / lc->bin_width;
+
+ if (upper_bin >= HISTOGRAM_NUM_BINS) {
+ upper_bin = HISTOGRAM_NUM_BINS - 1;
+ upper = 0;
+ }
+
+ double sum = 0;
+ for (size_t i = lower_bin; i <= upper_bin; i++)
+ sum += lc->histogram[i];
+
+ if (lower) {
+ /* Approximate ratio of requests in lower_bin, that fall between
+ * lower_bin_boundary and lower. This ratio is then subtracted from sum to
+ * increase accuracy. */
+ cdtime_t lower_bin_boundary = lower_bin * lc->bin_width;
+ assert(lower >= lower_bin_boundary);
+ double lower_ratio =
+ (double)(lower - lower_bin_boundary) / ((double)lc->bin_width);
+ sum -= lower_ratio * lc->histogram[lower_bin];
+ }
+
+ if (upper) {
+ /* As above: approximate ratio of requests in upper_bin, that fall between
+ * upper and upper_bin_boundary. */
+ cdtime_t upper_bin_boundary = (upper_bin + 1) * lc->bin_width;
+ assert(upper <= upper_bin_boundary);
+ double ratio = (double)(upper_bin_boundary - upper) / (double)lc->bin_width;
+ sum -= ratio * lc->histogram[upper_bin];
+ }
+
+ return sum / (CDTIME_T_TO_DOUBLE(now - lc->start_time));
+} /* }}} double latency_counter_get_rate */
--- /dev/null
+/**
+ * collectd - src/utils_latency.h
+ * Copyright (C) 2013 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+
+#include "utils_time.h"
+
+#ifndef HISTOGRAM_NUM_BINS
+#define HISTOGRAM_NUM_BINS 1000
+#endif
+
+struct latency_counter_s;
+typedef struct latency_counter_s latency_counter_t;
+
+latency_counter_t *latency_counter_create(void);
+void latency_counter_destroy(latency_counter_t *lc);
+
+void latency_counter_add(latency_counter_t *lc, cdtime_t latency);
+void latency_counter_reset(latency_counter_t *lc);
+
+cdtime_t latency_counter_get_min(latency_counter_t *lc);
+cdtime_t latency_counter_get_max(latency_counter_t *lc);
+cdtime_t latency_counter_get_sum(latency_counter_t *lc);
+size_t latency_counter_get_num(latency_counter_t *lc);
+cdtime_t latency_counter_get_average(latency_counter_t *lc);
+cdtime_t latency_counter_get_percentile(latency_counter_t *lc, double percent);
+
+/*
+ * NAME
+ * latency_counter_get_rate(counter,lower,upper,now)
+ *
+ * DESCRIPTION
+ * Calculates rate of latency values fall within requested interval.
+ * Interval specified as (lower,upper], i.e. the lower boundary is exclusive,
+ * the upper boundary is inclusive.
+ * When lower is zero, then the interval is (0, upper].
+ * When upper is zero, then the interval is (lower, infinity).
+ */
+double latency_counter_get_rate(const latency_counter_t *lc, cdtime_t lower,
+ cdtime_t upper, const cdtime_t now);
--- /dev/null
+/**
+ * collectd - src/utils_latency_config.c
+ * Copyright (C) 2013-2016 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#include "collectd.h"
+#include "utils/common/common.h"
+#include "utils/latency/latency_config.h"
+
+static int latency_config_add_percentile(latency_config_t *conf,
+ oconfig_item_t *ci) {
+ double percent;
+ int status = cf_util_get_double(ci, &percent);
+ if (status != 0)
+ return status;
+
+ if ((percent <= 0.0) || (percent >= 100)) {
+ P_ERROR("The value for \"%s\" must be between 0 and 100, "
+ "exclusively.",
+ ci->key);
+ return ERANGE;
+ }
+
+ double *tmp = realloc(conf->percentile,
+ sizeof(*conf->percentile) * (conf->percentile_num + 1));
+ if (tmp == NULL) {
+ P_ERROR("realloc failed.");
+ return ENOMEM;
+ }
+ conf->percentile = tmp;
+ conf->percentile[conf->percentile_num] = percent;
+ conf->percentile_num++;
+
+ return 0;
+} /* int latency_config_add_percentile */
+
+static int latency_config_add_bucket(latency_config_t *conf,
+ oconfig_item_t *ci) {
+ if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_NUMBER) ||
+ (ci->values[1].type != OCONFIG_TYPE_NUMBER)) {
+ P_ERROR("\"%s\" requires exactly two numeric arguments.", ci->key);
+ return EINVAL;
+ }
+
+ if (ci->values[1].value.number &&
+ ci->values[1].value.number <= ci->values[0].value.number) {
+ P_ERROR("MIN must be less than MAX in \"%s\".", ci->key);
+ return ERANGE;
+ }
+
+ if (ci->values[0].value.number < 0) {
+ P_ERROR("MIN must be greater then or equal to zero in \"%s\".", ci->key);
+ return ERANGE;
+ }
+
+ latency_bucket_t *tmp =
+ realloc(conf->buckets, sizeof(*conf->buckets) * (conf->buckets_num + 1));
+ if (tmp == NULL) {
+ P_ERROR("realloc failed.");
+ return ENOMEM;
+ }
+ conf->buckets = tmp;
+ conf->buckets[conf->buckets_num].lower_bound =
+ DOUBLE_TO_CDTIME_T(ci->values[0].value.number);
+ conf->buckets[conf->buckets_num].upper_bound =
+ DOUBLE_TO_CDTIME_T(ci->values[1].value.number);
+ conf->buckets_num++;
+
+ return 0;
+} /* int latency_config_add_bucket */
+
+int latency_config(latency_config_t *conf, oconfig_item_t *ci) {
+ int status = 0;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Percentile", child->key) == 0)
+ status = latency_config_add_percentile(conf, child);
+ else if (strcasecmp("Bucket", child->key) == 0)
+ status = latency_config_add_bucket(conf, child);
+ else if (strcasecmp("BucketType", child->key) == 0)
+ status = cf_util_get_string(child, &conf->bucket_type);
+ else
+ P_WARNING("\"%s\" is not a valid option within a \"%s\" block.",
+ child->key, ci->key);
+
+ if (status != 0)
+ return status;
+ }
+
+ if ((status == 0) && (conf->percentile_num == 0) &&
+ (conf->buckets_num == 0)) {
+ P_ERROR("The \"%s\" block must contain at least one "
+ "\"Percentile\" or \"Bucket\" option.",
+ ci->key);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int latency_config_copy(latency_config_t *dst, const latency_config_t src) {
+ *dst = (latency_config_t){
+ .percentile_num = src.percentile_num, .buckets_num = src.buckets_num,
+ };
+
+ dst->percentile = calloc(dst->percentile_num, sizeof(*dst->percentile));
+ dst->buckets = calloc(dst->buckets_num, sizeof(*dst->buckets));
+
+ if ((dst->percentile == NULL) || (dst->buckets == NULL)) {
+ latency_config_free(*dst);
+ return ENOMEM;
+ }
+
+ if (src.bucket_type != NULL) {
+ dst->bucket_type = strdup(src.bucket_type);
+ if (dst->bucket_type == NULL) {
+ latency_config_free(*dst);
+ return ENOMEM;
+ }
+ }
+
+ memmove(dst->percentile, src.percentile,
+ dst->percentile_num * sizeof(*dst->percentile));
+ memmove(dst->buckets, src.buckets, dst->buckets_num * sizeof(*dst->buckets));
+
+ return 0;
+} /* int latency_config_copy */
+
+void latency_config_free(latency_config_t conf) {
+ sfree(conf.percentile);
+ sfree(conf.buckets);
+ sfree(conf.bucket_type);
+} /* void latency_config_free */
--- /dev/null
+/**
+ * collectd - src/utils_latency_config.c
+ * Copyright (C) 2013-2016 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#ifndef UTILS_LATENCY_CONFIG_H
+#define UTILS_LATENCY_CONFIG_H 1
+
+#include "collectd.h"
+
+#include "liboconfig/oconfig.h"
+#include "utils_time.h"
+
+typedef struct {
+ cdtime_t lower_bound;
+ cdtime_t upper_bound;
+} latency_bucket_t;
+
+typedef struct {
+ double *percentile;
+ size_t percentile_num;
+
+ latency_bucket_t *buckets;
+ size_t buckets_num;
+ char *bucket_type;
+
+ /*
+ bool lower;
+ bool upper;
+ bool avg;
+ */
+} latency_config_t;
+
+int latency_config(latency_config_t *conf, oconfig_item_t *ci);
+
+int latency_config_copy(latency_config_t *dst, const latency_config_t src);
+
+void latency_config_free(latency_config_t conf);
+
+#endif /* UTILS_LATENCY_CONFIG_H */
--- /dev/null
+/**
+ * collectd - src/utils_latency_test.c
+ * Copyright (C) 2015 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#define DBL_PRECISION 1e-6
+
+#include "collectd.h"
+#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */
+
+#include "testing.h"
+#include "utils/latency/latency.h"
+#include "utils_time.h"
+
+DEF_TEST(simple) {
+ struct {
+ double val;
+ double min;
+ double max;
+ double sum;
+ double avg;
+ } cases[] = {
+ /* val min max sum avg */
+ {0.5, 0.5, 0.5, 0.5, 0.5}, {0.3, 0.3, 0.5, 0.8, 0.4},
+ {0.7, 0.3, 0.7, 1.5, 0.5}, {2.5, 0.3, 2.5, 4.0, 1.0},
+ {99, 0.3, 99, 103, 20.6},
+ /* { -1, 0.3, 99, 103, 20.6}, see issue #1139 */
+ };
+ latency_counter_t *l;
+
+ CHECK_NOT_NULL(l = latency_counter_create());
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ printf("# case %" PRIsz ": DOUBLE_TO_CDTIME_T(%g) = %" PRIu64 "\n", i,
+ cases[i].val, DOUBLE_TO_CDTIME_T(cases[i].val));
+ latency_counter_add(l, DOUBLE_TO_CDTIME_T(cases[i].val));
+
+ EXPECT_EQ_DOUBLE(cases[i].min,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_min(l)));
+ EXPECT_EQ_DOUBLE(cases[i].max,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_max(l)));
+ EXPECT_EQ_DOUBLE(cases[i].sum,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l)));
+ EXPECT_EQ_DOUBLE(cases[i].avg,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_average(l)));
+ }
+
+ latency_counter_destroy(l);
+ return 0;
+}
+
+DEF_TEST(percentile) {
+ latency_counter_t *l;
+
+ CHECK_NOT_NULL(l = latency_counter_create());
+
+ for (size_t i = 0; i < 100; i++) {
+ latency_counter_add(l, TIME_T_TO_CDTIME_T(((time_t)i) + 1));
+ }
+
+ EXPECT_EQ_DOUBLE(1.0, CDTIME_T_TO_DOUBLE(latency_counter_get_min(l)));
+ EXPECT_EQ_DOUBLE(100.0, CDTIME_T_TO_DOUBLE(latency_counter_get_max(l)));
+ EXPECT_EQ_DOUBLE(100.0 * 101.0 / 2.0,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l)));
+ EXPECT_EQ_DOUBLE(50.5, CDTIME_T_TO_DOUBLE(latency_counter_get_average(l)));
+
+ EXPECT_EQ_DOUBLE(50.0,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 50.0)));
+ EXPECT_EQ_DOUBLE(80.0,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 80.0)));
+ EXPECT_EQ_DOUBLE(95.0,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 95.0)));
+ EXPECT_EQ_DOUBLE(99.0,
+ CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 99.0)));
+
+ CHECK_ZERO(latency_counter_get_percentile(l, -1.0));
+ CHECK_ZERO(latency_counter_get_percentile(l, 101.0));
+
+ latency_counter_destroy(l);
+ return 0;
+}
+
+DEF_TEST(get_rate) {
+ /* We re-declare the struct here so we can inspect its content. */
+ struct {
+ cdtime_t start_time;
+ cdtime_t sum;
+ size_t num;
+ cdtime_t min;
+ cdtime_t max;
+ cdtime_t bin_width;
+ int histogram[HISTOGRAM_NUM_BINS];
+ } * peek;
+ latency_counter_t *l;
+
+ CHECK_NOT_NULL(l = latency_counter_create());
+ peek = (void *)l;
+
+ for (time_t i = 1; i <= 125; i++) {
+ latency_counter_add(l, TIME_T_TO_CDTIME_T(i));
+ }
+
+ /* We expect a bucket width of 125ms. */
+ EXPECT_EQ_UINT64(DOUBLE_TO_CDTIME_T(0.125), peek->bin_width);
+
+ struct {
+ size_t index;
+ int want;
+ } bucket_cases[] = {
+ {0, 0}, /* (0.000-0.125] */
+ {1, 0}, /* (0.125-0.250] */
+ {2, 0}, /* (0.250-0.375] */
+ {3, 0}, /* (0.375-0.500] */
+ {4, 0}, /* (0.500-0.625] */
+ {5, 0}, /* (0.625-0.750] */
+ {6, 0}, /* (0.750-0.875] */
+ {7, 1}, /* (0.875-1.000] */
+ {8, 0}, /* (1.000-1.125] */
+ {9, 0}, /* (1.125-1.250] */
+ {10, 0}, /* (1.250-1.375] */
+ {11, 0}, /* (1.375-1.500] */
+ {12, 0}, /* (1.500-1.625] */
+ {13, 0}, /* (1.625-1.750] */
+ {14, 0}, /* (1.750-1.875] */
+ {15, 1}, /* (1.875-2.000] */
+ {16, 0}, /* (2.000-2.125] */
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(bucket_cases); i++) {
+ size_t index = bucket_cases[i].index;
+ EXPECT_EQ_INT(bucket_cases[i].want, peek->histogram[index]);
+ }
+
+ struct {
+ cdtime_t lower_bound;
+ cdtime_t upper_bound;
+ double want;
+ } cases[] = {
+ {
+ // bucket 6 is zero
+ DOUBLE_TO_CDTIME_T_STATIC(0.750), DOUBLE_TO_CDTIME_T_STATIC(0.875),
+ 0.00,
+ },
+ {
+ // bucket 7 contains the t=1 update
+ DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(1.000),
+ 1.00,
+ },
+ {
+ // range: bucket 7 - bucket 15; contains the t=1 and t=2 updates
+ DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(2.000),
+ 2.00,
+ },
+ {
+ // lower bucket is only partially applied
+ DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
+ DOUBLE_TO_CDTIME_T_STATIC(2.000), 1.75,
+ },
+ {
+ // upper bucket is only partially applied
+ DOUBLE_TO_CDTIME_T_STATIC(0.875),
+ DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.75,
+ },
+ {
+ // both buckets are only partially applied
+ DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
+ DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.50,
+ },
+ {
+ // lower bound is unspecified
+ 0, DOUBLE_TO_CDTIME_T_STATIC(2.000), 2.00,
+ },
+ {
+ // upper bound is unspecified
+ DOUBLE_TO_CDTIME_T_STATIC(125.000 - 0.125), 0, 1.00,
+ },
+ {
+ // overflow test: upper >> longest latency
+ DOUBLE_TO_CDTIME_T_STATIC(1.000), DOUBLE_TO_CDTIME_T_STATIC(999999),
+ 124.00,
+ },
+ {
+ // overflow test: lower > longest latency
+ DOUBLE_TO_CDTIME_T_STATIC(130), 0, 0.00,
+ },
+ {
+ // lower > upper => error
+ DOUBLE_TO_CDTIME_T_STATIC(10), DOUBLE_TO_CDTIME_T_STATIC(9), NAN,
+ },
+ {
+ // lower == upper => zero
+ DOUBLE_TO_CDTIME_T_STATIC(9), DOUBLE_TO_CDTIME_T_STATIC(9), 0.00,
+ },
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ cdtime_t now = peek->start_time + TIME_T_TO_CDTIME_T(1);
+ EXPECT_EQ_DOUBLE(cases[i].want,
+ latency_counter_get_rate(l, cases[i].lower_bound,
+ cases[i].upper_bound, now));
+ }
+
+ latency_counter_destroy(l);
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(simple);
+ RUN_TEST(percentile);
+ RUN_TEST(get_rate);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_vl_lookup.c
+ * Copyright (C) 2012 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include <pthread.h>
+#include <regex.h>
+
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils/lookup/vl_lookup.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
+
+#if HAVE_LIBKSTAT
+kstat_ctl_t *kc;
+#endif /* HAVE_LIBKSTAT */
+
+#if BUILD_TEST
+#define sstrncpy strncpy
+#define plugin_log(s, ...) \
+ do { \
+ printf("[severity %i] ", s); \
+ printf(__VA_ARGS__); \
+ printf("\n"); \
+ } while (0)
+#endif
+
+/*
+ * Types
+ */
+struct part_match_s {
+ char str[DATA_MAX_NAME_LEN];
+ regex_t regex;
+ bool is_regex;
+};
+typedef struct part_match_s part_match_t;
+
+struct identifier_match_s {
+ part_match_t host;
+ part_match_t plugin;
+ part_match_t plugin_instance;
+ part_match_t type;
+ part_match_t type_instance;
+
+ unsigned int group_by;
+};
+typedef struct identifier_match_s identifier_match_t;
+
+struct lookup_s {
+ c_avl_tree_t *by_type_tree;
+
+ lookup_class_callback_t cb_user_class;
+ lookup_obj_callback_t cb_user_obj;
+ lookup_free_class_callback_t cb_free_class;
+ lookup_free_obj_callback_t cb_free_obj;
+};
+
+struct user_obj_s;
+typedef struct user_obj_s user_obj_t;
+struct user_obj_s {
+ void *user_obj;
+ lookup_identifier_t ident;
+
+ user_obj_t *next;
+};
+
+struct user_class_s {
+ pthread_mutex_t lock;
+ void *user_class;
+ identifier_match_t match;
+ user_obj_t *user_obj_list; /* list of user_obj */
+};
+typedef struct user_class_s user_class_t;
+
+struct user_class_list_s;
+typedef struct user_class_list_s user_class_list_t;
+struct user_class_list_s {
+ user_class_t entry;
+ user_class_list_t *next;
+};
+
+struct by_type_entry_s {
+ c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */
+ user_class_list_t *wildcard_plugin_list;
+};
+typedef struct by_type_entry_s by_type_entry_t;
+
+/*
+ * Private functions
+ */
+static bool lu_part_matches(part_match_t const *match, /* {{{ */
+ char const *str) {
+ if (match->is_regex) {
+ /* Short cut popular catch-all regex. */
+ if (strcmp(".*", match->str) == 0)
+ return true;
+
+ int status = regexec(&match->regex, str,
+ /* nmatch = */ 0, /* pmatch = */ NULL,
+ /* flags = */ 0);
+ if (status == 0)
+ return true;
+ else
+ return false;
+ } else if (strcmp(match->str, str) == 0)
+ return true;
+ else
+ return false;
+} /* }}} bool lu_part_matches */
+
+static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */
+ char const *ident_part) {
+ size_t len = strlen(ident_part);
+ int status;
+
+ if ((len < 3) || (ident_part[0] != '/') || (ident_part[len - 1] != '/')) {
+ sstrncpy(match_part->str, ident_part, sizeof(match_part->str));
+ match_part->is_regex = false;
+ return 0;
+ }
+
+ /* Copy string without the leading slash. */
+ sstrncpy(match_part->str, ident_part + 1, sizeof(match_part->str));
+ assert(sizeof(match_part->str) > len);
+ /* strip trailing slash */
+ match_part->str[len - 2] = 0;
+
+ status = regcomp(&match_part->regex, match_part->str,
+ /* flags = */ REG_EXTENDED);
+ if (status != 0) {
+ char errbuf[1024];
+ regerror(status, &match_part->regex, errbuf, sizeof(errbuf));
+ ERROR("utils_vl_lookup: Compiling regular expression \"%s\" failed: %s",
+ match_part->str, errbuf);
+ return EINVAL;
+ }
+ match_part->is_regex = true;
+
+ return 0;
+} /* }}} int lu_copy_ident_to_match_part */
+
+static int lu_copy_ident_to_match(identifier_match_t *match, /* {{{ */
+ lookup_identifier_t const *ident,
+ unsigned int group_by) {
+ memset(match, 0, sizeof(*match));
+
+ match->group_by = group_by;
+
+#define COPY_FIELD(field) \
+ do { \
+ int status = lu_copy_ident_to_match_part(&match->field, ident->field); \
+ if (status != 0) \
+ return status; \
+ } while (0)
+
+ COPY_FIELD(host);
+ COPY_FIELD(plugin);
+ COPY_FIELD(plugin_instance);
+ COPY_FIELD(type);
+ COPY_FIELD(type_instance);
+
+#undef COPY_FIELD
+
+ return 0;
+} /* }}} int lu_copy_ident_to_match */
+
+/* user_class->lock must be held when calling this function */
+static void *lu_create_user_obj(lookup_t *obj, /* {{{ */
+ data_set_t const *ds, value_list_t const *vl,
+ user_class_t *user_class) {
+ user_obj_t *user_obj;
+
+ user_obj = calloc(1, sizeof(*user_obj));
+ if (user_obj == NULL) {
+ ERROR("utils_vl_lookup: calloc failed.");
+ return NULL;
+ }
+ user_obj->next = NULL;
+
+ user_obj->user_obj = obj->cb_user_class(ds, vl, user_class->user_class);
+ if (user_obj->user_obj == NULL) {
+ sfree(user_obj);
+ WARNING("utils_vl_lookup: User-provided constructor failed.");
+ return NULL;
+ }
+
+#define COPY_FIELD(field, group_mask) \
+ do { \
+ if (user_class->match.field.is_regex && \
+ ((user_class->match.group_by & group_mask) == 0)) \
+ sstrncpy(user_obj->ident.field, "/.*/", sizeof(user_obj->ident.field)); \
+ else \
+ sstrncpy(user_obj->ident.field, vl->field, \
+ sizeof(user_obj->ident.field)); \
+ } while (0)
+
+ COPY_FIELD(host, LU_GROUP_BY_HOST);
+ COPY_FIELD(plugin, LU_GROUP_BY_PLUGIN);
+ COPY_FIELD(plugin_instance, LU_GROUP_BY_PLUGIN_INSTANCE);
+ COPY_FIELD(type, 0);
+ COPY_FIELD(type_instance, LU_GROUP_BY_TYPE_INSTANCE);
+
+#undef COPY_FIELD
+
+ if (user_class->user_obj_list == NULL) {
+ user_class->user_obj_list = user_obj;
+ } else {
+ user_obj_t *last = user_class->user_obj_list;
+ while (last->next != NULL)
+ last = last->next;
+ last->next = user_obj;
+ }
+
+ return user_obj;
+} /* }}} void *lu_create_user_obj */
+
+/* user_class->lock must be held when calling this function */
+static user_obj_t *lu_find_user_obj(user_class_t *user_class, /* {{{ */
+ value_list_t const *vl) {
+ user_obj_t *ptr;
+
+ for (ptr = user_class->user_obj_list; ptr != NULL; ptr = ptr->next) {
+ if (user_class->match.host.is_regex &&
+ (user_class->match.group_by & LU_GROUP_BY_HOST) &&
+ (strcmp(vl->host, ptr->ident.host) != 0))
+ continue;
+ if (user_class->match.plugin.is_regex &&
+ (user_class->match.group_by & LU_GROUP_BY_PLUGIN) &&
+ (strcmp(vl->plugin, ptr->ident.plugin) != 0))
+ continue;
+ if (user_class->match.plugin_instance.is_regex &&
+ (user_class->match.group_by & LU_GROUP_BY_PLUGIN_INSTANCE) &&
+ (strcmp(vl->plugin_instance, ptr->ident.plugin_instance) != 0))
+ continue;
+ if (user_class->match.type_instance.is_regex &&
+ (user_class->match.group_by & LU_GROUP_BY_TYPE_INSTANCE) &&
+ (strcmp(vl->type_instance, ptr->ident.type_instance) != 0))
+ continue;
+
+ return ptr;
+ }
+
+ return NULL;
+} /* }}} user_obj_t *lu_find_user_obj */
+
+static int lu_handle_user_class(lookup_t *obj, /* {{{ */
+ data_set_t const *ds, value_list_t const *vl,
+ user_class_t *user_class) {
+ user_obj_t *user_obj;
+ int status;
+
+ assert(strcmp(vl->type, user_class->match.type.str) == 0);
+ assert(user_class->match.plugin.is_regex ||
+ (strcmp(vl->plugin, user_class->match.plugin.str)) == 0);
+
+ if (!lu_part_matches(&user_class->match.type_instance, vl->type_instance) ||
+ !lu_part_matches(&user_class->match.plugin_instance,
+ vl->plugin_instance) ||
+ !lu_part_matches(&user_class->match.plugin, vl->plugin) ||
+ !lu_part_matches(&user_class->match.host, vl->host))
+ return 1;
+
+ pthread_mutex_lock(&user_class->lock);
+ user_obj = lu_find_user_obj(user_class, vl);
+ if (user_obj == NULL) {
+ /* call lookup_class_callback_t() and insert into the list of user objects.
+ */
+ user_obj = lu_create_user_obj(obj, ds, vl, user_class);
+ if (user_obj == NULL) {
+ pthread_mutex_unlock(&user_class->lock);
+ return -1;
+ }
+ }
+ pthread_mutex_unlock(&user_class->lock);
+
+ status = obj->cb_user_obj(ds, vl, user_class->user_class, user_obj->user_obj);
+ if (status != 0) {
+ ERROR("utils_vl_lookup: The user object callback failed with status %i.",
+ status);
+ /* Returning a negative value means: abort! */
+ if (status < 0)
+ return status;
+ else
+ return 1;
+ }
+
+ return 0;
+} /* }}} int lu_handle_user_class */
+
+static int lu_handle_user_class_list(lookup_t *obj, /* {{{ */
+ data_set_t const *ds,
+ value_list_t const *vl,
+ user_class_list_t *user_class_list) {
+ user_class_list_t *ptr;
+ int retval = 0;
+
+ for (ptr = user_class_list; ptr != NULL; ptr = ptr->next) {
+ int status;
+
+ status = lu_handle_user_class(obj, ds, vl, &ptr->entry);
+ if (status < 0)
+ return status;
+ else if (status == 0)
+ retval++;
+ }
+
+ return retval;
+} /* }}} int lu_handle_user_class_list */
+
+static by_type_entry_t *lu_search_by_type(lookup_t *obj, /* {{{ */
+ char const *type,
+ bool allocate_if_missing) {
+ by_type_entry_t *by_type;
+ char *type_copy;
+ int status;
+
+ status = c_avl_get(obj->by_type_tree, type, (void *)&by_type);
+ if (status == 0)
+ return by_type;
+
+ if (!allocate_if_missing)
+ return NULL;
+
+ type_copy = strdup(type);
+ if (type_copy == NULL) {
+ ERROR("utils_vl_lookup: strdup failed.");
+ return NULL;
+ }
+
+ by_type = calloc(1, sizeof(*by_type));
+ if (by_type == NULL) {
+ ERROR("utils_vl_lookup: calloc failed.");
+ sfree(type_copy);
+ return NULL;
+ }
+ by_type->wildcard_plugin_list = NULL;
+
+ by_type->by_plugin_tree =
+ c_avl_create((int (*)(const void *, const void *))strcmp);
+ if (by_type->by_plugin_tree == NULL) {
+ ERROR("utils_vl_lookup: c_avl_create failed.");
+ sfree(by_type);
+ sfree(type_copy);
+ return NULL;
+ }
+
+ status = c_avl_insert(obj->by_type_tree,
+ /* key = */ type_copy, /* value = */ by_type);
+ assert(status <= 0); /* >0 => entry exists => race condition. */
+ if (status != 0) {
+ ERROR("utils_vl_lookup: c_avl_insert failed.");
+ c_avl_destroy(by_type->by_plugin_tree);
+ sfree(by_type);
+ sfree(type_copy);
+ return NULL;
+ }
+
+ return by_type;
+} /* }}} by_type_entry_t *lu_search_by_type */
+
+static int lu_add_by_plugin(by_type_entry_t *by_type, /* {{{ */
+ user_class_list_t *user_class_list) {
+ user_class_list_t *ptr = NULL;
+ identifier_match_t const *match = &user_class_list->entry.match;
+
+ /* Lookup user_class_list from the per-plugin structure. If this is the first
+ * user_class to be added, the block returns immediately. Otherwise they will
+ * set "ptr" to non-NULL. */
+ if (match->plugin.is_regex) {
+ if (by_type->wildcard_plugin_list == NULL) {
+ by_type->wildcard_plugin_list = user_class_list;
+ return 0;
+ }
+
+ ptr = by_type->wildcard_plugin_list;
+ } /* if (plugin is wildcard) */
+ else /* (plugin is not wildcard) */
+ {
+ int status;
+
+ status =
+ c_avl_get(by_type->by_plugin_tree, match->plugin.str, (void *)&ptr);
+
+ if (status != 0) /* plugin not yet in tree */
+ {
+ char *plugin_copy = strdup(match->plugin.str);
+
+ if (plugin_copy == NULL) {
+ ERROR("utils_vl_lookup: strdup failed.");
+ sfree(user_class_list);
+ return ENOMEM;
+ }
+
+ status =
+ c_avl_insert(by_type->by_plugin_tree, plugin_copy, user_class_list);
+ if (status != 0) {
+ ERROR("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.",
+ plugin_copy, status);
+ sfree(plugin_copy);
+ sfree(user_class_list);
+ return status;
+ } else {
+ return 0;
+ }
+ } /* if (plugin not yet in tree) */
+ } /* if (plugin is not wildcard) */
+
+ assert(ptr != NULL);
+
+ while (ptr->next != NULL)
+ ptr = ptr->next;
+ ptr->next = user_class_list;
+
+ return 0;
+} /* }}} int lu_add_by_plugin */
+
+static void lu_destroy_user_obj(lookup_t *obj, /* {{{ */
+ user_obj_t *user_obj) {
+ while (user_obj != NULL) {
+ user_obj_t *next = user_obj->next;
+
+ if (obj->cb_free_obj != NULL)
+ obj->cb_free_obj(user_obj->user_obj);
+ user_obj->user_obj = NULL;
+
+ sfree(user_obj);
+ user_obj = next;
+ }
+} /* }}} void lu_destroy_user_obj */
+
+static void lu_destroy_user_class_list(lookup_t *obj, /* {{{ */
+ user_class_list_t *user_class_list) {
+ while (user_class_list != NULL) {
+ user_class_list_t *next = user_class_list->next;
+
+ if (obj->cb_free_class != NULL)
+ obj->cb_free_class(user_class_list->entry.user_class);
+ user_class_list->entry.user_class = NULL;
+
+#define CLEAR_FIELD(field) \
+ do { \
+ if (user_class_list->entry.match.field.is_regex) { \
+ regfree(&user_class_list->entry.match.field.regex); \
+ user_class_list->entry.match.field.is_regex = false; \
+ } \
+ } while (0)
+
+ CLEAR_FIELD(host);
+ CLEAR_FIELD(plugin);
+ CLEAR_FIELD(plugin_instance);
+ CLEAR_FIELD(type);
+ CLEAR_FIELD(type_instance);
+
+#undef CLEAR_FIELD
+
+ lu_destroy_user_obj(obj, user_class_list->entry.user_obj_list);
+ user_class_list->entry.user_obj_list = NULL;
+ pthread_mutex_destroy(&user_class_list->entry.lock);
+
+ sfree(user_class_list);
+ user_class_list = next;
+ }
+} /* }}} void lu_destroy_user_class_list */
+
+static void lu_destroy_by_type(lookup_t *obj, /* {{{ */
+ by_type_entry_t *by_type) {
+
+ while (42) {
+ char *plugin = NULL;
+ user_class_list_t *user_class_list = NULL;
+ int status;
+
+ status = c_avl_pick(by_type->by_plugin_tree, (void *)&plugin,
+ (void *)&user_class_list);
+ if (status != 0)
+ break;
+
+ DEBUG("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".",
+ plugin);
+ sfree(plugin);
+ lu_destroy_user_class_list(obj, user_class_list);
+ }
+
+ c_avl_destroy(by_type->by_plugin_tree);
+ by_type->by_plugin_tree = NULL;
+
+ lu_destroy_user_class_list(obj, by_type->wildcard_plugin_list);
+ by_type->wildcard_plugin_list = NULL;
+
+ sfree(by_type);
+} /* }}} int lu_destroy_by_type */
+
+/*
+ * Public functions
+ */
+lookup_t *lookup_create(lookup_class_callback_t cb_user_class, /* {{{ */
+ lookup_obj_callback_t cb_user_obj,
+ lookup_free_class_callback_t cb_free_class,
+ lookup_free_obj_callback_t cb_free_obj) {
+ lookup_t *obj = calloc(1, sizeof(*obj));
+ if (obj == NULL) {
+ ERROR("utils_vl_lookup: calloc failed.");
+ return NULL;
+ }
+
+ obj->by_type_tree = c_avl_create((int (*)(const void *, const void *))strcmp);
+ if (obj->by_type_tree == NULL) {
+ ERROR("utils_vl_lookup: c_avl_create failed.");
+ sfree(obj);
+ return NULL;
+ }
+
+ obj->cb_user_class = cb_user_class;
+ obj->cb_user_obj = cb_user_obj;
+ obj->cb_free_class = cb_free_class;
+ obj->cb_free_obj = cb_free_obj;
+
+ return obj;
+} /* }}} lookup_t *lookup_create */
+
+void lookup_destroy(lookup_t *obj) /* {{{ */
+{
+ int status;
+
+ if (obj == NULL)
+ return;
+
+ while (42) {
+ char *type = NULL;
+ by_type_entry_t *by_type = NULL;
+
+ status = c_avl_pick(obj->by_type_tree, (void *)&type, (void *)&by_type);
+ if (status != 0)
+ break;
+
+ DEBUG("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
+ sfree(type);
+ lu_destroy_by_type(obj, by_type);
+ }
+
+ c_avl_destroy(obj->by_type_tree);
+ obj->by_type_tree = NULL;
+
+ sfree(obj);
+} /* }}} void lookup_destroy */
+
+int lookup_add(lookup_t *obj, /* {{{ */
+ lookup_identifier_t const *ident, unsigned int group_by,
+ void *user_class) {
+ by_type_entry_t *by_type = NULL;
+ user_class_list_t *user_class_obj;
+
+ by_type = lu_search_by_type(obj, ident->type, /* allocate = */ true);
+ if (by_type == NULL)
+ return -1;
+
+ user_class_obj = calloc(1, sizeof(*user_class_obj));
+ if (user_class_obj == NULL) {
+ ERROR("utils_vl_lookup: calloc failed.");
+ return ENOMEM;
+ }
+ pthread_mutex_init(&user_class_obj->entry.lock, /* attr = */ NULL);
+ user_class_obj->entry.user_class = user_class;
+ lu_copy_ident_to_match(&user_class_obj->entry.match, ident, group_by);
+ user_class_obj->entry.user_obj_list = NULL;
+ user_class_obj->next = NULL;
+
+ return lu_add_by_plugin(by_type, user_class_obj);
+} /* }}} int lookup_add */
+
+/* returns the number of successful calls to the callback function */
+int lookup_search(lookup_t *obj, /* {{{ */
+ data_set_t const *ds, value_list_t const *vl) {
+ by_type_entry_t *by_type = NULL;
+ user_class_list_t *user_class_list = NULL;
+ int retval = 0;
+ int status;
+
+ if ((obj == NULL) || (ds == NULL) || (vl == NULL))
+ return -EINVAL;
+
+ by_type = lu_search_by_type(obj, vl->type, /* allocate = */ false);
+ if (by_type == NULL)
+ return 0;
+
+ status =
+ c_avl_get(by_type->by_plugin_tree, vl->plugin, (void *)&user_class_list);
+ if (status == 0) {
+ status = lu_handle_user_class_list(obj, ds, vl, user_class_list);
+ if (status < 0)
+ return status;
+ retval += status;
+ }
+
+ if (by_type->wildcard_plugin_list != NULL) {
+ status =
+ lu_handle_user_class_list(obj, ds, vl, by_type->wildcard_plugin_list);
+ if (status < 0)
+ return status;
+ retval += status;
+ }
+
+ return retval;
+} /* }}} lookup_search */
--- /dev/null
+/**
+ * collectd - src/utils_vl_lookup.h
+ * Copyright (C) 2012 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_VL_LOOKUP_H
+#define UTILS_VL_LOOKUP_H 1
+
+#include "plugin.h"
+
+/*
+ * Types
+ */
+struct lookup_s;
+typedef struct lookup_s lookup_t;
+
+/* Given a user_class, constructs a new user_obj. */
+typedef void *(*lookup_class_callback_t)(data_set_t const *ds,
+ value_list_t const *vl,
+ void *user_class);
+
+/* Given a user_class and a ds/vl combination, does stuff with the data.
+ * This is the main working horse of the module. */
+typedef int (*lookup_obj_callback_t)(data_set_t const *ds,
+ value_list_t const *vl, void *user_class,
+ void *user_obj);
+
+/* Used to free user_class pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_class_callback_t)(void *user_class);
+
+/* Used to free user_obj pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_obj_callback_t)(void *user_obj);
+
+struct lookup_identifier_s {
+ char host[DATA_MAX_NAME_LEN];
+ char plugin[DATA_MAX_NAME_LEN];
+ char plugin_instance[DATA_MAX_NAME_LEN];
+ char type[DATA_MAX_NAME_LEN];
+ char type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct lookup_identifier_s lookup_identifier_t;
+
+#define LU_GROUP_BY_HOST 0x01
+#define LU_GROUP_BY_PLUGIN 0x02
+#define LU_GROUP_BY_PLUGIN_INSTANCE 0x04
+/* #define LU_GROUP_BY_TYPE 0x00 */
+#define LU_GROUP_BY_TYPE_INSTANCE 0x10
+
+/*
+ * Functions
+ */
+__attribute__((nonnull(1, 2)))
+lookup_t *lookup_create(lookup_class_callback_t, lookup_obj_callback_t,
+ lookup_free_class_callback_t,
+ lookup_free_obj_callback_t);
+void lookup_destroy(lookup_t *obj);
+
+int lookup_add(lookup_t *obj, lookup_identifier_t const *ident,
+ unsigned int group_by, void *user_class);
+
+/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
+int lookup_search(lookup_t *obj, data_set_t const *ds, value_list_t const *vl);
+
+#endif /* UTILS_VL_LOOKUP_H */
--- /dev/null
+/**
+ * collectd - src/tests/test_utils_vl_lookup.c
+ * Copyright (C) 2012 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/lookup/vl_lookup.h"
+
+static bool expect_new_obj;
+static bool have_new_obj;
+
+static lookup_identifier_t last_class_ident;
+static lookup_identifier_t last_obj_ident;
+
+static data_source_t dsrc_test = {"value", DS_TYPE_DERIVE, 0.0, NAN};
+static data_set_t const ds_test = {"test", 1, &dsrc_test};
+
+static data_source_t dsrc_unknown = {"value", DS_TYPE_DERIVE, 0.0, NAN};
+static data_set_t const ds_unknown = {"unknown", 1, &dsrc_unknown};
+
+static int lookup_obj_callback(data_set_t const *ds, value_list_t const *vl,
+ void *user_class, void *user_obj) {
+ lookup_identifier_t *class = user_class;
+ lookup_identifier_t *obj = user_obj;
+
+ OK1(expect_new_obj == have_new_obj,
+ (expect_new_obj ? "New obj is created." : "Updating existing obj."));
+
+ memcpy(&last_class_ident, class, sizeof(last_class_ident));
+ memcpy(&last_obj_ident, obj, sizeof(last_obj_ident));
+
+ if (strcmp(obj->plugin_instance, "failure") == 0)
+ return -1;
+
+ return 0;
+}
+
+static void *lookup_class_callback(data_set_t const *ds, value_list_t const *vl,
+ void *user_class) {
+ lookup_identifier_t *class = user_class;
+ lookup_identifier_t *obj;
+
+ assert(expect_new_obj);
+
+ memcpy(&last_class_ident, class, sizeof(last_class_ident));
+
+ obj = malloc(sizeof(*obj));
+ strncpy(obj->host, vl->host, sizeof(obj->host));
+ strncpy(obj->plugin, vl->plugin, sizeof(obj->plugin));
+ strncpy(obj->plugin_instance, vl->plugin_instance,
+ sizeof(obj->plugin_instance));
+ strncpy(obj->type, vl->type, sizeof(obj->type));
+ strncpy(obj->type_instance, vl->type_instance, sizeof(obj->type_instance));
+
+ have_new_obj = true;
+
+ return (void *)obj;
+}
+
+static int checked_lookup_add(lookup_t *obj, /* {{{ */
+ char const *host, char const *plugin,
+ char const *plugin_instance, char const *type,
+ char const *type_instance,
+ unsigned int group_by) {
+ lookup_identifier_t ident = {{0}};
+ void *user_class;
+
+ strncpy(ident.host, host, sizeof(ident.host) - 1);
+ strncpy(ident.plugin, plugin, sizeof(ident.plugin) - 1);
+ strncpy(ident.plugin_instance, plugin_instance,
+ sizeof(ident.plugin_instance) - 1);
+ strncpy(ident.type, type, sizeof(ident.type) - 1);
+ strncpy(ident.type_instance, type_instance, sizeof(ident.type_instance) - 1);
+
+ user_class = malloc(sizeof(ident));
+ memmove(user_class, &ident, sizeof(ident));
+
+ OK(lookup_add(obj, &ident, group_by, user_class) == 0);
+ return 0;
+} /* }}} int checked_lookup_add */
+
+static int checked_lookup_search(lookup_t *obj, char const *host,
+ char const *plugin,
+ char const *plugin_instance, char const *type,
+ char const *type_instance, bool expect_new) {
+ int status;
+ value_list_t vl = VALUE_LIST_INIT;
+ data_set_t const *ds = &ds_unknown;
+
+ strncpy(vl.host, host, sizeof(vl.host) - 1);
+ strncpy(vl.plugin, plugin, sizeof(vl.plugin) - 1);
+ strncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance) - 1);
+ strncpy(vl.type, type, sizeof(vl.type) - 1);
+ strncpy(vl.type_instance, type_instance, sizeof(vl.type_instance) - 1);
+
+ if (strcmp(vl.type, "test") == 0)
+ ds = &ds_test;
+
+ expect_new_obj = expect_new;
+ have_new_obj = false;
+
+ status = lookup_search(obj, ds, &vl);
+ return status;
+}
+
+DEF_TEST(group_by_specific_host) {
+ lookup_t *obj;
+ CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
+ (void *)free, (void *)free));
+
+ checked_lookup_add(obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST);
+ checked_lookup_search(obj, "host0", "test", "", "test", "0",
+ /* expect new = */ 1);
+ checked_lookup_search(obj, "host0", "test", "", "test", "1",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "host1", "test", "", "test", "0",
+ /* expect new = */ 1);
+ checked_lookup_search(obj, "host1", "test", "", "test", "1",
+ /* expect new = */ 0);
+
+ lookup_destroy(obj);
+ return 0;
+}
+
+DEF_TEST(group_by_any_host) {
+ lookup_t *obj;
+ CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
+ (void *)free, (void *)free));
+
+ checked_lookup_add(obj, "/.*/", "/.*/", "/.*/", "test", "/.*/",
+ LU_GROUP_BY_HOST);
+ checked_lookup_search(obj, "host0", "plugin0", "", "test", "0",
+ /* expect new = */ 1);
+ checked_lookup_search(obj, "host0", "plugin0", "", "test", "1",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "host0", "plugin1", "", "test", "0",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "host0", "plugin1", "", "test", "1",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "host1", "plugin0", "", "test", "0",
+ /* expect new = */ 1);
+ checked_lookup_search(obj, "host1", "plugin0", "", "test", "1",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "host1", "plugin1", "", "test", "0",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "host1", "plugin1", "", "test", "1",
+ /* expect new = */ 0);
+
+ lookup_destroy(obj);
+ return 0;
+}
+
+DEF_TEST(multiple_lookups) {
+ lookup_t *obj;
+ int status;
+
+ CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
+ (void *)free, (void *)free));
+
+ checked_lookup_add(obj, "/.*/", "plugin0", "", "test", "/.*/",
+ LU_GROUP_BY_HOST);
+ checked_lookup_add(obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST);
+
+ status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "",
+ /* expect new = */ 0);
+ assert(status == 0);
+ status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "",
+ /* expect new = */ 1);
+ assert(status == 1);
+ status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "ti0",
+ /* expect new = */ 1);
+ assert(status == 1);
+ status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "ti0",
+ /* expect new = */ 0);
+ assert(status == 2);
+
+ lookup_destroy(obj);
+ return 0;
+}
+
+DEF_TEST(regex) {
+ lookup_t *obj;
+ CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
+ (void *)free, (void *)free));
+
+ checked_lookup_add(obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/",
+ LU_GROUP_BY_TYPE_INSTANCE);
+ checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "user",
+ /* expect new = */ 1);
+ checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "idle",
+ /* expect new = */ 1);
+ checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "user",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "idle",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "user",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "idle",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "user",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "idle",
+ /* expect new = */ 0);
+ checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "system",
+ /* expect new = */ 1);
+
+ lookup_destroy(obj);
+ return 0;
+}
+
+int main(int argc, char **argv) /* {{{ */
+{
+ RUN_TEST(group_by_specific_host);
+ RUN_TEST(group_by_any_host);
+ RUN_TEST(multiple_lookups);
+ RUN_TEST(regex);
+
+ END_TEST;
+} /* }}} int main */
--- /dev/null
+/**
+ * collectd - src/utils_match.c
+ * Copyright (C) 2008-2014 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/match/match.h"
+
+#include <regex.h>
+
+#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
+#define UTILS_MATCH_FLAGS_REGEX 0x04
+
+struct cu_match_s {
+ regex_t regex;
+ regex_t excluderegex;
+ int flags;
+
+ int (*callback)(const char *str, char *const *matches, size_t matches_num,
+ void *user_data);
+ void *user_data;
+ void (*free)(void *user_data);
+};
+
+/*
+ * Private functions
+ */
+static char *match_substr(const char *str, int begin, int end) {
+ char *ret;
+ size_t ret_len;
+
+ if ((begin < 0) || (end < 0) || (begin >= end))
+ return NULL;
+ if ((size_t)end > (strlen(str) + 1)) {
+ ERROR("utils_match: match_substr: `end' points after end of string.");
+ return NULL;
+ }
+
+ ret_len = end - begin;
+ ret = malloc(ret_len + 1);
+ if (ret == NULL) {
+ ERROR("utils_match: match_substr: malloc failed.");
+ return NULL;
+ }
+
+ sstrncpy(ret, str + begin, ret_len + 1);
+ return ret;
+} /* char *match_substr */
+
+static int default_callback(const char __attribute__((unused)) * str,
+ char *const *matches, size_t matches_num,
+ void *user_data) {
+ cu_match_value_t *data = (cu_match_value_t *)user_data;
+
+ if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) {
+ gauge_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) {
+ data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1;
+ data->values_num++;
+ return 0;
+ }
+
+ if (matches_num < 2)
+ return -1;
+
+ value = (gauge_t)strtod(matches[1], &endptr);
+ if (matches[1] == endptr)
+ return -1;
+
+ if (data->ds_type & UTILS_MATCH_CF_GAUGE_DIST) {
+ latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value));
+ data->values_num++;
+ return 0;
+ }
+
+ if ((data->values_num == 0) ||
+ (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST) ||
+ (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
+ data->value.gauge = value;
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) {
+ double f = ((double)data->values_num) / ((double)(data->values_num + 1));
+ data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) {
+ if (data->value.gauge > value)
+ data->value.gauge = value;
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) {
+ if (data->value.gauge < value)
+ data->value.gauge = value;
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) {
+ data->value.gauge += value;
+ } else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return -1;
+ }
+
+ data->values_num++;
+ } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) {
+ counter_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) {
+ data->value.counter++;
+ data->values_num++;
+ return 0;
+ }
+
+ if (matches_num < 2)
+ return -1;
+
+ value = (counter_t)strtoull(matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return -1;
+
+ if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
+ data->value.counter = value;
+ else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
+ data->value.counter += value;
+ else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return -1;
+ }
+
+ data->values_num++;
+ } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) {
+ derive_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) {
+ data->value.derive++;
+ data->values_num++;
+ return 0;
+ }
+
+ if (matches_num < 2)
+ return -1;
+
+ value = (derive_t)strtoll(matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return -1;
+
+ if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
+ data->value.derive = value;
+ else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
+ data->value.derive += value;
+ else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return -1;
+ }
+
+ data->values_num++;
+ } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) {
+ absolute_t value;
+ char *endptr = NULL;
+
+ if (matches_num < 2)
+ return -1;
+
+ value = (absolute_t)strtoull(matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return -1;
+
+ if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
+ data->value.absolute = value;
+ else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return -1;
+ }
+
+ data->values_num++;
+ } else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return -1;
+ }
+
+ return 0;
+} /* int default_callback */
+
+static void match_simple_free(void *data) {
+ cu_match_value_t *user_data = (cu_match_value_t *)data;
+ if (user_data->latency)
+ latency_counter_destroy(user_data->latency);
+
+ free(data);
+} /* void match_simple_free */
+
+/*
+ * Public functions
+ */
+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,
+ void (*free_user_data)(void *user_data)) {
+ cu_match_t *obj;
+ int status;
+
+ DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s",
+ regex, excluderegex);
+
+ obj = calloc(1, sizeof(*obj));
+ if (obj == NULL)
+ return NULL;
+
+ status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
+ if (status != 0) {
+ ERROR("Compiling the regular expression \"%s\" failed.", regex);
+ sfree(obj);
+ return NULL;
+ }
+ obj->flags |= UTILS_MATCH_FLAGS_REGEX;
+
+ 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;
+ obj->free = free_user_data;
+
+ return obj;
+} /* cu_match_t *match_create_callback */
+
+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;
+
+ user_data = calloc(1, sizeof(*user_data));
+ if (user_data == NULL)
+ return NULL;
+ user_data->ds_type = match_ds_type;
+
+ if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
+ (match_ds_type & UTILS_MATCH_CF_GAUGE_DIST)) {
+ user_data->latency = latency_counter_create();
+ if (user_data->latency == NULL) {
+ ERROR("match_create_simple(): latency_counter_create() failed.");
+ free(user_data);
+ return NULL;
+ }
+ }
+
+ obj = match_create_callback(regex, excluderegex, default_callback, user_data,
+ match_simple_free);
+ if (obj == NULL) {
+ if (user_data->latency)
+ latency_counter_destroy(user_data->latency);
+
+ sfree(user_data);
+ return NULL;
+ }
+ return obj;
+} /* cu_match_t *match_create_simple */
+
+void match_value_reset(cu_match_value_t *mv) {
+ if (mv == NULL)
+ return;
+
+ /* Reset GAUGE metrics only and except GAUGE_PERSIST. */
+ if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
+ !(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
+ mv->value.gauge = (mv->ds_type & UTILS_MATCH_CF_GAUGE_INC) ? 0 : NAN;
+ mv->values_num = 0;
+ }
+} /* }}} void match_value_reset */
+
+void match_destroy(cu_match_t *obj) {
+ if (obj == NULL)
+ return;
+
+ if (obj->flags & UTILS_MATCH_FLAGS_REGEX)
+ regfree(&obj->regex);
+ if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX)
+ regfree(&obj->excluderegex);
+ if ((obj->user_data != NULL) && (obj->free != NULL))
+ (*obj->free)(obj->user_data);
+
+ sfree(obj);
+} /* void match_destroy */
+
+int match_apply(cu_match_t *obj, const char *str) {
+ int status;
+ regmatch_t re_match[32];
+ char *matches[32] = {0};
+ size_t matches_num;
+
+ 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);
+
+ /* Regex did not match */
+ if (status != 0)
+ return 0;
+
+ for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches);
+ matches_num++) {
+ if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0))
+ break;
+
+ matches[matches_num] = match_substr(str, re_match[matches_num].rm_so,
+ re_match[matches_num].rm_eo);
+ if (matches[matches_num] == NULL) {
+ status = -1;
+ break;
+ }
+ }
+
+ if (status != 0) {
+ ERROR("utils_match: match_apply: match_substr failed.");
+ } else {
+ status = obj->callback(str, matches, matches_num, obj->user_data);
+ if (status != 0) {
+ ERROR("utils_match: match_apply: callback failed.");
+ }
+ }
+
+ for (size_t i = 0; i < matches_num; i++) {
+ sfree(matches[i]);
+ }
+
+ return status;
+} /* int match_apply */
+
+void *match_get_user_data(cu_match_t *obj) {
+ if (obj == NULL)
+ return NULL;
+ return obj->user_data;
+} /* void *match_get_user_data */
--- /dev/null
+/**
+ * collectd - src/utils_match.h
+ * Copyright (C) 2008-2014 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_MATCH_H
+#define UTILS_MATCH_H 1
+
+#include "plugin.h"
+#include "utils/latency/latency.h"
+
+/*
+ * Each type may have 12 sub-types
+ * 0x1000 = 1000000000000
+ * ^ <- Type bit
+ * ^^^^^^^^^^^^ <- Subtype bits
+ */
+#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000
+#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000
+#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000
+#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000
+
+#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
+#define UTILS_MATCH_CF_GAUGE_MIN 0x02
+#define UTILS_MATCH_CF_GAUGE_MAX 0x04
+#define UTILS_MATCH_CF_GAUGE_LAST 0x08
+#define UTILS_MATCH_CF_GAUGE_INC 0x10
+#define UTILS_MATCH_CF_GAUGE_ADD 0x20
+#define UTILS_MATCH_CF_GAUGE_PERSIST 0x40
+#define UTILS_MATCH_CF_GAUGE_DIST 0x80
+
+#define UTILS_MATCH_CF_COUNTER_SET 0x01
+#define UTILS_MATCH_CF_COUNTER_ADD 0x02
+#define UTILS_MATCH_CF_COUNTER_INC 0x04
+
+#define UTILS_MATCH_CF_DERIVE_SET 0x01
+#define UTILS_MATCH_CF_DERIVE_ADD 0x02
+#define UTILS_MATCH_CF_DERIVE_INC 0x04
+
+#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01
+#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02
+#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04
+
+/*
+ * Data types
+ */
+struct cu_match_s;
+typedef struct cu_match_s cu_match_t;
+
+struct cu_match_value_s {
+ int ds_type;
+ value_t value;
+ unsigned int values_num;
+ latency_counter_t *latency;
+};
+typedef struct cu_match_value_s cu_match_value_t;
+
+/*
+ * Prototypes
+ */
+/*
+ * NAME
+ * match_create_callback
+ *
+ * DESCRIPTION
+ * Creates a new `cu_match_t' object which will use the regular expression
+ * `regex' to match lines, see the `match_apply' method below. If the line
+ * matches, the callback passed in `callback' will be called along with the
+ * pointer `user_pointer'.
+ * The string that's passed to the callback depends on the regular expression:
+ * If the regular expression includes a sub-match, i. e. something like
+ * "value=([0-9][0-9]*)"
+ * then only the submatch (the part in the parenthesis) will be passed to the
+ * callback. If there is no submatch, then the entire string is passed to the
+ * callback.
+ * The optional `excluderegex' allows to exclude the line from the match, if
+ * the excluderegex matches.
+ * When `match_destroy' is called the `user_data' pointer is freed using
+ * the `free_user_data' callback - if it is not NULL.
+ */
+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, void (*free_user_data)(void *user_data));
+
+/*
+ * NAME
+ * match_create_simple
+ *
+ * DESCRIPTION
+ * Creates a new `cu_match_t' with a default callback. The user data for that
+ * default callback will be a `cu_match_value_t' structure, with
+ * `ds_type' copied to the structure. The default callback will handle the
+ * string as containing a number (see strtoll(3) and strtod(3)) and store that
+ * number in the `value' member. How that is done depends on `ds_type':
+ *
+ * UTILS_MATCH_DS_TYPE_GAUGE
+ * The function will search for a floating point number in the string and
+ * store it in value.gauge.
+ * UTILS_MATCH_DS_TYPE_COUNTER_SET
+ * The function will search for an integer in the string and store it in
+ * value.counter.
+ * UTILS_MATCH_DS_TYPE_COUNTER_ADD
+ * The function will search for an integer in the string and add it to the
+ * value in value.counter.
+ * UTILS_MATCH_DS_TYPE_COUNTER_INC
+ * The function will not search for anything in the string and increase
+ * value.counter by one.
+ */
+cu_match_t *match_create_simple(const char *regex, const char *excluderegex,
+ int ds_type);
+
+/*
+ * NAME
+ * match_value_reset
+ *
+ * DESCRIPTION
+ * Resets the internal state, if applicable. This function must be called
+ * after each iteration for "simple" matches, usually after dispatching the
+ * metrics.
+ */
+void match_value_reset(cu_match_value_t *mv);
+
+/*
+ * NAME
+ * match_destroy
+ *
+ * DESCRIPTION
+ * Destroys the object and frees all internal resources.
+ */
+void match_destroy(cu_match_t *obj);
+
+/*
+ * NAME
+ * match_apply
+ *
+ * DESCRIPTION
+ * Tries to match the string `str' with the regular expression of `obj'. If
+ * the string matches, calls the callback in `obj' with the (sub-)match.
+ *
+ * The user_data pointer passed to `match_create_callback' is NOT freed
+ * automatically. The `cu_match_value_t' structure allocated by
+ * `match_create_callback' is freed automatically.
+ */
+int match_apply(cu_match_t *obj, const char *str);
+
+/*
+ * NAME
+ * match_get_user_data
+ *
+ * DESCRIPTION
+ * Returns the pointer passed to `match_create_callback' or a pointer to the
+ * `cu_match_value_t' structure allocated by `match_create_simple'.
+ */
+void *match_get_user_data(cu_match_t *obj);
+
+#endif /* UTILS_MATCH_H */
--- /dev/null
+/**
+ * collectd - src/meta_data.c
+ * Copyright (C) 2008-2011 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/metadata/meta_data.h"
+
+#define MD_MAX_NONSTRING_CHARS 128
+
+/*
+ * Data types
+ */
+union meta_value_u {
+ char *mv_string;
+ int64_t mv_signed_int;
+ uint64_t mv_unsigned_int;
+ double mv_double;
+ bool mv_boolean;
+};
+typedef union meta_value_u meta_value_t;
+
+struct meta_entry_s;
+typedef struct meta_entry_s meta_entry_t;
+struct meta_entry_s {
+ char *key;
+ meta_value_t value;
+ int type;
+ meta_entry_t *next;
+};
+
+struct meta_data_s {
+ meta_entry_t *head;
+ pthread_mutex_t lock;
+};
+
+/*
+ * Private functions
+ */
+static char *md_strdup(const char *orig) /* {{{ */
+{
+ size_t sz;
+ char *dest;
+
+ if (orig == NULL)
+ return NULL;
+
+ sz = strlen(orig) + 1;
+ dest = malloc(sz);
+ if (dest == NULL)
+ return NULL;
+
+ memcpy(dest, orig, sz);
+
+ return dest;
+} /* }}} char *md_strdup */
+
+static meta_entry_t *md_entry_alloc(const char *key) /* {{{ */
+{
+ meta_entry_t *e;
+
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ ERROR("md_entry_alloc: calloc failed.");
+ return NULL;
+ }
+
+ e->key = md_strdup(key);
+ if (e->key == NULL) {
+ free(e);
+ ERROR("md_entry_alloc: md_strdup failed.");
+ return NULL;
+ }
+
+ e->type = 0;
+ e->next = NULL;
+
+ return e;
+} /* }}} meta_entry_t *md_entry_alloc */
+
+/* XXX: The lock on md must be held while calling this function! */
+static meta_entry_t *md_entry_clone_contents(const meta_entry_t *orig) /* {{{ */
+{
+ meta_entry_t *copy;
+
+ /* WARNINGS :
+ * - we do not check that orig != NULL here. You should have done it before.
+ * - we do not set copy->next. DO NOT FORGET TO SET copy->next IN YOUR
+ * FUNCTION
+ */
+
+ copy = md_entry_alloc(orig->key);
+ if (copy == NULL)
+ return NULL;
+ copy->type = orig->type;
+ if (copy->type == MD_TYPE_STRING)
+ copy->value.mv_string = strdup(orig->value.mv_string);
+ else
+ copy->value = orig->value;
+
+ return copy;
+} /* }}} meta_entry_t *md_entry_clone_contents */
+
+static meta_entry_t *md_entry_clone(const meta_entry_t *orig) /* {{{ */
+{
+ meta_entry_t *copy;
+
+ if (orig == NULL)
+ return NULL;
+
+ copy = md_entry_clone_contents(orig);
+
+ copy->next = md_entry_clone(orig->next);
+ return copy;
+} /* }}} meta_entry_t *md_entry_clone */
+
+static void md_entry_free(meta_entry_t *e) /* {{{ */
+{
+ if (e == NULL)
+ return;
+
+ free(e->key);
+
+ if (e->type == MD_TYPE_STRING)
+ free(e->value.mv_string);
+
+ if (e->next != NULL)
+ md_entry_free(e->next);
+
+ free(e);
+} /* }}} void md_entry_free */
+
+static int md_entry_insert(meta_data_t *md, meta_entry_t *e) /* {{{ */
+{
+ meta_entry_t *this;
+ meta_entry_t *prev;
+
+ if ((md == NULL) || (e == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ prev = NULL;
+ this = md->head;
+ while (this != NULL) {
+ if (strcasecmp(e->key, this->key) == 0)
+ break;
+
+ prev = this;
+ this = this->next;
+ }
+
+ if (this == NULL) {
+ /* This key does not exist yet. */
+ if (md->head == NULL)
+ md->head = e;
+ else {
+ assert(prev != NULL);
+ prev->next = e;
+ }
+
+ e->next = NULL;
+ } else /* (this != NULL) */
+ {
+ if (prev == NULL)
+ md->head = e;
+ else
+ prev->next = e;
+
+ e->next = this->next;
+ }
+
+ pthread_mutex_unlock(&md->lock);
+
+ if (this != NULL) {
+ this->next = NULL;
+ md_entry_free(this);
+ }
+
+ return 0;
+} /* }}} int md_entry_insert */
+
+/* XXX: The lock on md must be held while calling this function! */
+static int md_entry_insert_clone(meta_data_t *md, meta_entry_t *orig) /* {{{ */
+{
+ meta_entry_t *e;
+ meta_entry_t *this;
+ meta_entry_t *prev;
+
+ /* WARNINGS :
+ * - we do not check that md and e != NULL here. You should have done it
+ * before.
+ * - we do not use the lock. You should have set it before.
+ */
+
+ e = md_entry_clone_contents(orig);
+
+ prev = NULL;
+ this = md->head;
+ while (this != NULL) {
+ if (strcasecmp(e->key, this->key) == 0)
+ break;
+
+ prev = this;
+ this = this->next;
+ }
+
+ if (this == NULL) {
+ /* This key does not exist yet. */
+ if (md->head == NULL)
+ md->head = e;
+ else {
+ assert(prev != NULL);
+ prev->next = e;
+ }
+
+ e->next = NULL;
+ } else /* (this != NULL) */
+ {
+ if (prev == NULL)
+ md->head = e;
+ else
+ prev->next = e;
+
+ e->next = this->next;
+ }
+
+ if (this != NULL) {
+ this->next = NULL;
+ md_entry_free(this);
+ }
+
+ return 0;
+} /* }}} int md_entry_insert_clone */
+
+/* XXX: The lock on md must be held while calling this function! */
+static meta_entry_t *md_entry_lookup(meta_data_t *md, /* {{{ */
+ const char *key) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL))
+ return NULL;
+
+ for (e = md->head; e != NULL; e = e->next)
+ if (strcasecmp(key, e->key) == 0)
+ break;
+
+ return e;
+} /* }}} meta_entry_t *md_entry_lookup */
+
+/*
+ * Each value_list_t*, as it is going through the system, is handled by exactly
+ * one thread. Plugins which pass a value_list_t* to another thread, e.g. the
+ * rrdtool plugin, must create a copy first. The meta data within a
+ * value_list_t* is not thread safe and doesn't need to be.
+ *
+ * The meta data associated with cache entries are a different story. There, we
+ * need to ensure exclusive locking to prevent leaks and other funky business.
+ * This is ensured by the uc_meta_data_get_*() functions.
+ */
+
+/*
+ * Public functions
+ */
+meta_data_t *meta_data_create(void) /* {{{ */
+{
+ meta_data_t *md;
+
+ md = calloc(1, sizeof(*md));
+ if (md == NULL) {
+ ERROR("meta_data_create: calloc failed.");
+ return NULL;
+ }
+
+ pthread_mutex_init(&md->lock, /* attr = */ NULL);
+
+ return md;
+} /* }}} meta_data_t *meta_data_create */
+
+meta_data_t *meta_data_clone(meta_data_t *orig) /* {{{ */
+{
+ meta_data_t *copy;
+
+ if (orig == NULL)
+ return NULL;
+
+ copy = meta_data_create();
+ if (copy == NULL)
+ return NULL;
+
+ pthread_mutex_lock(&orig->lock);
+ copy->head = md_entry_clone(orig->head);
+ pthread_mutex_unlock(&orig->lock);
+
+ return copy;
+} /* }}} meta_data_t *meta_data_clone */
+
+int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig) /* {{{ */
+{
+ if (orig == NULL)
+ return 0;
+
+ if (*dest == NULL) {
+ *dest = meta_data_clone(orig);
+ return 0;
+ }
+
+ pthread_mutex_lock(&orig->lock);
+ for (meta_entry_t *e = orig->head; e != NULL; e = e->next) {
+ md_entry_insert_clone((*dest), e);
+ }
+ pthread_mutex_unlock(&orig->lock);
+
+ return 0;
+} /* }}} int meta_data_clone_merge */
+
+void meta_data_destroy(meta_data_t *md) /* {{{ */
+{
+ if (md == NULL)
+ return;
+
+ md_entry_free(md->head);
+ pthread_mutex_destroy(&md->lock);
+ free(md);
+} /* }}} void meta_data_destroy */
+
+int meta_data_exists(meta_data_t *md, const char *key) /* {{{ */
+{
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ for (meta_entry_t *e = md->head; e != NULL; e = e->next) {
+ if (strcasecmp(key, e->key) == 0) {
+ pthread_mutex_unlock(&md->lock);
+ return 1;
+ }
+ }
+
+ pthread_mutex_unlock(&md->lock);
+ return 0;
+} /* }}} int meta_data_exists */
+
+int meta_data_type(meta_data_t *md, const char *key) /* {{{ */
+{
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ for (meta_entry_t *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;
+
+ if ((md == NULL) || (toc == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ for (meta_entry_t *e = md->head; e != NULL; e = e->next)
+ ++count;
+
+ if (count == 0) {
+ pthread_mutex_unlock(&md->lock);
+ return count;
+ }
+
+ *toc = calloc(count, sizeof(**toc));
+ for (meta_entry_t *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;
+ meta_entry_t *prev;
+
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ prev = NULL;
+ this = md->head;
+ while (this != NULL) {
+ if (strcasecmp(key, this->key) == 0)
+ break;
+
+ prev = this;
+ this = this->next;
+ }
+
+ if (this == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ if (prev == NULL)
+ md->head = this->next;
+ else
+ prev->next = this->next;
+
+ pthread_mutex_unlock(&md->lock);
+
+ this->next = NULL;
+ md_entry_free(this);
+
+ return 0;
+} /* }}} int meta_data_delete */
+
+/*
+ * Add functions
+ */
+int meta_data_add_string(meta_data_t *md, /* {{{ */
+ const char *key, const char *value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ e = md_entry_alloc(key);
+ if (e == NULL)
+ return -ENOMEM;
+
+ e->value.mv_string = md_strdup(value);
+ if (e->value.mv_string == NULL) {
+ ERROR("meta_data_add_string: md_strdup failed.");
+ md_entry_free(e);
+ return -ENOMEM;
+ }
+ e->type = MD_TYPE_STRING;
+
+ return md_entry_insert(md, e);
+} /* }}} int meta_data_add_string */
+
+int meta_data_add_signed_int(meta_data_t *md, /* {{{ */
+ const char *key, int64_t value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ e = md_entry_alloc(key);
+ if (e == NULL)
+ return -ENOMEM;
+
+ e->value.mv_signed_int = value;
+ e->type = MD_TYPE_SIGNED_INT;
+
+ return md_entry_insert(md, e);
+} /* }}} int meta_data_add_signed_int */
+
+int meta_data_add_unsigned_int(meta_data_t *md, /* {{{ */
+ const char *key, uint64_t value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ e = md_entry_alloc(key);
+ if (e == NULL)
+ return -ENOMEM;
+
+ e->value.mv_unsigned_int = value;
+ e->type = MD_TYPE_UNSIGNED_INT;
+
+ return md_entry_insert(md, e);
+} /* }}} int meta_data_add_unsigned_int */
+
+int meta_data_add_double(meta_data_t *md, /* {{{ */
+ const char *key, double value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ e = md_entry_alloc(key);
+ if (e == NULL)
+ return -ENOMEM;
+
+ e->value.mv_double = value;
+ e->type = MD_TYPE_DOUBLE;
+
+ return md_entry_insert(md, e);
+} /* }}} int meta_data_add_double */
+
+int meta_data_add_boolean(meta_data_t *md, /* {{{ */
+ const char *key, bool value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ e = md_entry_alloc(key);
+ if (e == NULL)
+ return -ENOMEM;
+
+ e->value.mv_boolean = value;
+ e->type = MD_TYPE_BOOLEAN;
+
+ return md_entry_insert(md, e);
+} /* }}} int meta_data_add_boolean */
+
+/*
+ * Get functions
+ */
+int meta_data_get_string(meta_data_t *md, /* {{{ */
+ const char *key, char **value) {
+ meta_entry_t *e;
+ char *temp;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ e = md_entry_lookup(md, key);
+ if (e == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ if (e->type != MD_TYPE_STRING) {
+ ERROR("meta_data_get_string: Type mismatch for key `%s'", e->key);
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ temp = md_strdup(e->value.mv_string);
+ if (temp == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ ERROR("meta_data_get_string: md_strdup failed.");
+ return -ENOMEM;
+ }
+
+ pthread_mutex_unlock(&md->lock);
+
+ *value = temp;
+
+ return 0;
+} /* }}} int meta_data_get_string */
+
+int meta_data_get_signed_int(meta_data_t *md, /* {{{ */
+ const char *key, int64_t *value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ e = md_entry_lookup(md, key);
+ if (e == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ if (e->type != MD_TYPE_SIGNED_INT) {
+ ERROR("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ *value = e->value.mv_signed_int;
+
+ pthread_mutex_unlock(&md->lock);
+ return 0;
+} /* }}} int meta_data_get_signed_int */
+
+int meta_data_get_unsigned_int(meta_data_t *md, /* {{{ */
+ const char *key, uint64_t *value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ e = md_entry_lookup(md, key);
+ if (e == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ if (e->type != MD_TYPE_UNSIGNED_INT) {
+ ERROR("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key);
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ *value = e->value.mv_unsigned_int;
+
+ pthread_mutex_unlock(&md->lock);
+ return 0;
+} /* }}} int meta_data_get_unsigned_int */
+
+int meta_data_get_double(meta_data_t *md, /* {{{ */
+ const char *key, double *value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ e = md_entry_lookup(md, key);
+ if (e == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ if (e->type != MD_TYPE_DOUBLE) {
+ ERROR("meta_data_get_double: Type mismatch for key `%s'", e->key);
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ *value = e->value.mv_double;
+
+ pthread_mutex_unlock(&md->lock);
+ return 0;
+} /* }}} int meta_data_get_double */
+
+int meta_data_get_boolean(meta_data_t *md, /* {{{ */
+ const char *key, bool *value) {
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ e = md_entry_lookup(md, key);
+ if (e == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ if (e->type != MD_TYPE_BOOLEAN) {
+ ERROR("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ *value = e->value.mv_boolean;
+
+ pthread_mutex_unlock(&md->lock);
+ return 0;
+} /* }}} int meta_data_get_boolean */
+
+int meta_data_as_string(meta_data_t *md, /* {{{ */
+ const char *key, char **value) {
+ meta_entry_t *e;
+ const char *actual;
+ char buffer[MD_MAX_NONSTRING_CHARS]; /* For non-string types. */
+ char *temp;
+ int type;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock(&md->lock);
+
+ e = md_entry_lookup(md, key);
+ if (e == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return -ENOENT;
+ }
+
+ type = e->type;
+
+ switch (type) {
+ case MD_TYPE_STRING:
+ actual = e->value.mv_string;
+ break;
+ case MD_TYPE_SIGNED_INT:
+ snprintf(buffer, sizeof(buffer), "%" PRIi64, e->value.mv_signed_int);
+ actual = buffer;
+ break;
+ case MD_TYPE_UNSIGNED_INT:
+ snprintf(buffer, sizeof(buffer), "%" PRIu64, e->value.mv_unsigned_int);
+ actual = buffer;
+ break;
+ case MD_TYPE_DOUBLE:
+ snprintf(buffer, sizeof(buffer), GAUGE_FORMAT, e->value.mv_double);
+ actual = buffer;
+ break;
+ case MD_TYPE_BOOLEAN:
+ actual = e->value.mv_boolean ? "true" : "false";
+ break;
+ default:
+ pthread_mutex_unlock(&md->lock);
+ ERROR("meta_data_as_string: unknown type %d for key `%s'", type, key);
+ return -ENOENT;
+ }
+
+ pthread_mutex_unlock(&md->lock);
+
+ temp = md_strdup(actual);
+ if (temp == NULL) {
+ ERROR("meta_data_as_string: md_strdup failed for key `%s'.", key);
+ return -ENOMEM;
+ }
+
+ *value = temp;
+
+ return 0;
+} /* }}} int meta_data_as_string */
--- /dev/null
+/**
+ * collectd - src/meta_data.h
+ * Copyright (C) 2008-2011 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef META_DATA_H
+#define 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;
+
+meta_data_t *meta_data_create(void);
+meta_data_t *meta_data_clone(meta_data_t *orig);
+int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig);
+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, const char *key, const char *value);
+int meta_data_add_signed_int(meta_data_t *md, const char *key, int64_t value);
+int meta_data_add_unsigned_int(meta_data_t *md, const char *key,
+ uint64_t value);
+int meta_data_add_double(meta_data_t *md, const char *key, double value);
+int meta_data_add_boolean(meta_data_t *md, const char *key, bool value);
+
+int meta_data_get_string(meta_data_t *md, const char *key, char **value);
+int meta_data_get_signed_int(meta_data_t *md, const char *key, int64_t *value);
+int meta_data_get_unsigned_int(meta_data_t *md, const char *key,
+ uint64_t *value);
+int meta_data_get_double(meta_data_t *md, const char *key, double *value);
+int meta_data_get_boolean(meta_data_t *md, const char *key, bool *value);
+
+/* Returns the value as a string, regardless of the type. */
+int meta_data_as_string(meta_data_t *md, const char *key, char **value);
+
+#endif /* META_DATA_H */
--- /dev/null
+/**
+ * collectd - src/daemon/meta_data_test.c
+ * Copyright (C) 2015 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */
+
+#include "testing.h"
+#include "utils/metadata/meta_data.h"
+
+DEF_TEST(base) {
+ meta_data_t *m;
+
+ char *s;
+ int64_t si;
+ uint64_t ui;
+ double d;
+ bool b;
+
+ CHECK_NOT_NULL(m = meta_data_create());
+
+ /* all of these are absent */
+ OK(meta_data_get_string(m, "string", &s) != 0);
+ OK(meta_data_get_signed_int(m, "signed_int", &si) != 0);
+ OK(meta_data_get_unsigned_int(m, "unsigned_int", &ui) != 0);
+ OK(meta_data_get_double(m, "double", &d) != 0);
+ OK(meta_data_get_boolean(m, "boolean", &b) != 0);
+
+ /* populate structure */
+ CHECK_ZERO(meta_data_add_string(m, "string", "foobar"));
+ OK(meta_data_exists(m, "string"));
+ OK(meta_data_type(m, "string") == MD_TYPE_STRING);
+
+ CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", -1));
+ OK(meta_data_exists(m, "signed_int"));
+ OK(meta_data_type(m, "signed_int") == MD_TYPE_SIGNED_INT);
+
+ CHECK_ZERO(meta_data_add_unsigned_int(m, "unsigned_int", 1));
+ OK(meta_data_exists(m, "unsigned_int"));
+ OK(meta_data_type(m, "unsigned_int") == MD_TYPE_UNSIGNED_INT);
+
+ CHECK_ZERO(meta_data_add_double(m, "double", 47.11));
+ OK(meta_data_exists(m, "double"));
+ OK(meta_data_type(m, "double") == MD_TYPE_DOUBLE);
+
+ CHECK_ZERO(meta_data_add_boolean(m, "boolean", 1));
+ OK(meta_data_exists(m, "boolean"));
+ OK(meta_data_type(m, "boolean") == MD_TYPE_BOOLEAN);
+
+ /* retrieve and check all values */
+ CHECK_ZERO(meta_data_get_string(m, "string", &s));
+ EXPECT_EQ_STR("foobar", s);
+ sfree(s);
+
+ CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si));
+ EXPECT_EQ_INT(-1, (int)si);
+
+ CHECK_ZERO(meta_data_get_unsigned_int(m, "unsigned_int", &ui));
+ EXPECT_EQ_INT(1, (int)ui);
+
+ CHECK_ZERO(meta_data_get_double(m, "double", &d));
+ EXPECT_EQ_DOUBLE(47.11, d);
+
+ CHECK_ZERO(meta_data_get_boolean(m, "boolean", &b));
+ OK1(b, "b evaluates to true");
+
+ /* retrieving the wrong type always fails */
+ EXPECT_EQ_INT(-2, meta_data_get_boolean(m, "string", &b));
+ EXPECT_EQ_INT(-2, meta_data_get_string(m, "signed_int", &s));
+ EXPECT_EQ_INT(-2, meta_data_get_string(m, "unsigned_int", &s));
+ EXPECT_EQ_INT(-2, meta_data_get_string(m, "double", &s));
+ EXPECT_EQ_INT(-2, meta_data_get_string(m, "boolean", &s));
+
+ /* replace existing keys */
+ CHECK_ZERO(meta_data_add_signed_int(m, "string", 666));
+ OK(meta_data_type(m, "string") == MD_TYPE_SIGNED_INT);
+
+ CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", 666));
+ CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si));
+ EXPECT_EQ_INT(666, (int)si);
+
+ /* deleting keys */
+ CHECK_ZERO(meta_data_delete(m, "signed_int"));
+ EXPECT_EQ_INT(-2, meta_data_delete(m, "doesnt exist"));
+
+ meta_data_destroy(m);
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(base);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_mount.c
+ * Copyright (C) 2005,2006 Niki W. Waibel
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence 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
+ *
+ * Author:
+ * Niki W. Waibel <niki.waibel@gmx.net>
+**/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+
+#include "collectd.h"
+
+#include "utils/mount/mount.h"
+
+#if HAVE_XFS_XQM_H
+#include <xfs/xqm.h>
+#define XFS_SUPER_MAGIC_STR "XFSB"
+#define XFS_SUPER_MAGIC2_STR "BSFX"
+#endif
+
+#include "plugin.h" /* ERROR() macro */
+#include "utils/common/common.h" /* sstrncpy() et alii */
+
+#if HAVE_GETVFSSTAT
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+/* #endif HAVE_GETVFSSTAT */
+
+#elif HAVE_GETFSSTAT
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#if HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#endif /* HAVE_GETFSSTAT */
+
+#if HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#if HAVE_SYS_MNTTAB_H
+#include <sys/mnttab.h>
+#endif
+
+#if HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef COLLECTD_MNTTAB
+#undef COLLECTD_MNTTAB
+#endif
+
+#if defined(_PATH_MOUNTED) /* glibc */
+#define COLLECTD_MNTTAB _PATH_MOUNTED
+#elif defined(MNTTAB) /* Solaris */
+#define COLLECTD_MNTTAB MNTTAB
+#elif defined(MNT_MNTTAB)
+#define COLLECTD_MNTTAB MNT_MNTTAB
+#elif defined(MNTTABNAME)
+#define COLLECTD_MNTTAB MNTTABNAME
+#elif defined(KMTAB)
+#define COLLECTD_MNTTAB KMTAB
+#else
+#define COLLECTD_MNTTAB "/etc/mnttab"
+#endif
+
+/* *** *** *** ********************************************* *** *** *** */
+/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */
+/* *** *** *** ********************************************* *** *** *** */
+
+/* stolen from quota-3.13 (quota-tools) */
+
+#define PROC_PARTITIONS "/proc/partitions"
+#define DEVLABELDIR "/dev"
+#define UUID 1
+#define VOL 2
+
+static struct uuidCache_s {
+ struct uuidCache_s *next;
+ char uuid[16];
+ char *label;
+ char *device;
+} *uuidCache = NULL;
+
+#define EXT2_SUPER_MAGIC 0xEF53
+struct ext2_super_block {
+ unsigned char s_dummy1[56];
+ unsigned char s_magic[2];
+ unsigned char s_dummy2[46];
+ unsigned char s_uuid[16];
+ char s_volume_name[16];
+};
+#define ext2magic(s) \
+ ((unsigned int)s.s_magic[0] + (((unsigned int)s.s_magic[1]) << 8))
+
+#if HAVE_XFS_XQM_H
+struct xfs_super_block {
+ unsigned char s_magic[4];
+ unsigned char s_dummy[28];
+ unsigned char s_uuid[16];
+ unsigned char s_dummy2[60];
+ char s_fsname[12];
+};
+#endif /* HAVE_XFS_XQM_H */
+
+#define REISER_SUPER_MAGIC "ReIsEr2Fs"
+struct reiserfs_super_block {
+ unsigned char s_dummy1[52];
+ unsigned char s_magic[10];
+ unsigned char s_dummy2[22];
+ unsigned char s_uuid[16];
+ char s_volume_name[16];
+};
+
+/* for now, only ext2 and xfs are supported */
+static int get_label_uuid(const char *device, char **label, char *uuid) {
+ /* start with ext2 and xfs tests, taken from mount_guess_fstype */
+ /* should merge these later */
+ int fd, rv = 1;
+ size_t namesize;
+ struct ext2_super_block e2sb;
+#if HAVE_XFS_XQM_H
+ struct xfs_super_block xfsb;
+#endif
+ struct reiserfs_super_block reisersb;
+
+ fd = open(device, O_RDONLY);
+ if (fd == -1) {
+ return rv;
+ }
+
+ if (lseek(fd, 1024, SEEK_SET) == 1024 &&
+ read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb) &&
+ ext2magic(e2sb) == EXT2_SUPER_MAGIC) {
+ memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
+ namesize = sizeof(e2sb.s_volume_name);
+ *label = smalloc(namesize + 1);
+ sstrncpy(*label, e2sb.s_volume_name, namesize);
+ rv = 0;
+#if HAVE_XFS_XQM_H
+ } else if (lseek(fd, 0, SEEK_SET) == 0 &&
+ read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb) &&
+ (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC_STR, 4) == 0 ||
+ strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2_STR, 4) == 0)) {
+ memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid));
+ namesize = sizeof(xfsb.s_fsname);
+ *label = smalloc(namesize + 1);
+ sstrncpy(*label, xfsb.s_fsname, namesize);
+ rv = 0;
+#endif /* HAVE_XFS_XQM_H */
+ } else if (lseek(fd, 65536, SEEK_SET) == 65536 &&
+ read(fd, (char *)&reisersb, sizeof(reisersb)) ==
+ sizeof(reisersb) &&
+ !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) {
+ memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid));
+ namesize = sizeof(reisersb.s_volume_name);
+ *label = smalloc(namesize + 1);
+ sstrncpy(*label, reisersb.s_volume_name, namesize);
+ rv = 0;
+ }
+ close(fd);
+ return rv;
+}
+
+static void uuidcache_addentry(char *device, char *label, char *uuid) {
+ struct uuidCache_s *last;
+
+ if (!uuidCache) {
+ last = uuidCache = smalloc(sizeof(*uuidCache));
+ } else {
+ for (last = uuidCache; last->next; last = last->next)
+ ;
+ last->next = smalloc(sizeof(*uuidCache));
+ last = last->next;
+ }
+ last->next = NULL;
+ last->device = device;
+ last->label = label;
+ memcpy(last->uuid, uuid, sizeof(last->uuid));
+}
+
+static void uuidcache_init(void) {
+ char line[100];
+ char *s;
+ int ma, mi, sz;
+ static char ptname[100];
+ FILE *procpt;
+ char uuid[16], *label = NULL;
+ char device[110];
+ int handleOnFirst;
+
+ if (uuidCache) {
+ return;
+ }
+
+ procpt = fopen(PROC_PARTITIONS, "r");
+ if (procpt == NULL) {
+ return;
+ }
+
+ for (int firstPass = 1; firstPass >= 0; firstPass--) {
+ fseek(procpt, 0, SEEK_SET);
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf(line, " %d %d %d %[^\n ]", &ma, &mi, &sz, ptname) != 4) {
+ continue;
+ }
+
+ /* skip extended partitions (heuristic: size 1) */
+ if (sz == 1) {
+ continue;
+ }
+
+ /* look only at md devices on first pass */
+ handleOnFirst = !strncmp(ptname, "md", 2);
+ if (firstPass != handleOnFirst) {
+ continue;
+ }
+
+ /* skip entire disk (minor 0, 64, ... on ide;
+ 0, 16, ... on sd) */
+ /* heuristic: partition name ends in a digit */
+
+ for (s = ptname; *s; s++)
+ ;
+
+ if (isdigit((int)s[-1])) {
+ /*
+ * Note: this is a heuristic only - there is no reason
+ * why these devices should live in /dev.
+ * Perhaps this directory should be specifiable by option.
+ * One might for example have /devlabel with links to /dev
+ * for the devices that may be accessed in this way.
+ * (This is useful, if the cdrom on /dev/hdc must not
+ * be accessed.)
+ */
+ snprintf(device, sizeof(device), "%s/%s", DEVLABELDIR, ptname);
+ if (!get_label_uuid(device, &label, uuid)) {
+ uuidcache_addentry(sstrdup(device), label, uuid);
+ }
+ }
+ }
+ }
+ fclose(procpt);
+}
+
+static unsigned char fromhex(char c) {
+ if (isdigit((int)c)) {
+ return c - '0';
+ } else if (islower((int)c)) {
+ return c - 'a' + 10;
+ } else {
+ return c - 'A' + 10;
+ }
+}
+
+static char *get_spec_by_x(int n, const char *t) {
+ struct uuidCache_s *uc;
+
+ uuidcache_init();
+ uc = uuidCache;
+
+ while (uc) {
+ switch (n) {
+ case UUID:
+ if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
+ return sstrdup(uc->device);
+ }
+ break;
+ case VOL:
+ if (!strcmp(t, uc->label)) {
+ return sstrdup(uc->device);
+ }
+ break;
+ }
+ uc = uc->next;
+ }
+ return NULL;
+}
+
+static char *get_spec_by_uuid(const char *s) {
+ char uuid[16];
+
+ if (strlen(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' ||
+ s[23] != '-') {
+ goto bad_uuid;
+ }
+
+ for (int i = 0; i < 16; i++) {
+ if (*s == '-') {
+ s++;
+ }
+ if (!isxdigit((int)s[0]) || !isxdigit((int)s[1])) {
+ goto bad_uuid;
+ }
+ uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
+ s += 2;
+ }
+ return get_spec_by_x(UUID, uuid);
+
+bad_uuid:
+ DEBUG("utils_mount: Found an invalid UUID: %s", s);
+ return NULL;
+}
+
+static char *get_spec_by_volume_label(const char *s) {
+ return get_spec_by_x(VOL, s);
+}
+
+static char *get_device_name(const char *optstr) {
+ char *rc;
+
+ if (optstr == NULL) {
+ return NULL;
+ } else if (strncmp(optstr, "UUID=", 5) == 0) {
+ DEBUG("utils_mount: TODO: check UUID= code!");
+ rc = get_spec_by_uuid(optstr + 5);
+ } else if (strncmp(optstr, "LABEL=", 6) == 0) {
+ DEBUG("utils_mount: TODO: check LABEL= code!");
+ rc = get_spec_by_volume_label(optstr + 6);
+ } else {
+ rc = sstrdup(optstr);
+ }
+
+ if (!rc) {
+ DEBUG("utils_mount: Error checking device name: optstr = %s", optstr);
+ }
+ return rc;
+}
+
+/* What weird OS is this..? I can't find any info with google :/ -octo */
+#if HAVE_LISTMNTENT && 0
+static cu_mount_t *cu_mount_listmntent(void) {
+ cu_mount_t *last = *list;
+ struct mntent *mnt;
+
+ struct tabmntent *mntlist;
+ if (listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) {
+#if COLLECT_DEBUG
+ DEBUG("utils_mount: calling listmntent() failed: %s", STRERRNO);
+#endif /* COLLECT_DEBUG */
+ }
+
+ for (struct tabmntent *p = mntlist; p; p = p->next) {
+ char *loop = NULL, *device = NULL;
+
+ mnt = p->ment;
+ loop = cu_mount_getoptionvalue(mnt->mnt_opts, "loop=");
+ if (loop == NULL) { /* no loop= mount */
+ device = get_device_name(mnt->mnt_fsname);
+ if (device == NULL) {
+ DEBUG("utils_mount: can't get devicename for fs (%s) %s (%s)"
+ ": ignored",
+ mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname);
+ continue;
+ }
+ } else {
+ device = loop;
+ }
+ if (*list == NULL) {
+ *list = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
+ last = *list;
+ } else {
+ while (last->next != NULL) { /* is last really last? */
+ last = last->next;
+ }
+ last->next = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
+ last = last->next;
+ }
+ last->dir = sstrdup(mnt->mnt_dir);
+ last->spec_device = sstrdup(mnt->mnt_fsname);
+ last->device = device;
+ last->type = sstrdup(mnt->mnt_type);
+ last->options = sstrdup(mnt->mnt_opts);
+ last->next = NULL;
+ } /* for(p = mntlist; p; p = p->next) */
+
+ return last;
+} /* cu_mount_t *cu_mount_listmntent(void) */
+/* #endif HAVE_LISTMNTENT */
+
+/* 4.4BSD and Mac OS X (getfsstat) or NetBSD (getvfsstat) */
+#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT
+static cu_mount_t *cu_mount_getfsstat(void) {
+#if HAVE_GETFSSTAT
+#define STRUCT_STATFS struct statfs
+#define CMD_STATFS getfsstat
+#define FLAGS_STATFS MNT_NOWAIT
+/* #endif HAVE_GETFSSTAT */
+#elif HAVE_GETVFSSTAT
+#define STRUCT_STATFS struct statvfs
+#define CMD_STATFS getvfsstat
+#define FLAGS_STATFS ST_NOWAIT
+#endif /* HAVE_GETVFSSTAT */
+
+ int bufsize;
+ STRUCT_STATFS *buf;
+
+ int num;
+
+ cu_mount_t *first = NULL;
+ cu_mount_t *last = NULL;
+ cu_mount_t *new = NULL;
+
+ /* Get the number of mounted file systems */
+ if ((bufsize = CMD_STATFS(NULL, 0, FLAGS_STATFS)) < 1) {
+#if COLLECT_DEBUG
+ DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO);
+#endif /* COLLECT_DEBUG */
+ return NULL;
+ }
+
+ if ((buf = calloc(bufsize, sizeof(*buf))) == NULL)
+ return NULL;
+
+ /* The bufsize needs to be passed in bytes. Really. This is not in the
+ * manpage.. -octo */
+ if ((num = CMD_STATFS(buf, bufsize * sizeof(STRUCT_STATFS), FLAGS_STATFS)) <
+ 1) {
+#if COLLECT_DEBUG
+ DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO);
+#endif /* COLLECT_DEBUG */
+ free(buf);
+ return NULL;
+ }
+
+ for (int i = 0; i < num; i++) {
+ if ((new = calloc(1, sizeof(*new))) == NULL)
+ break;
+
+ /* Copy values from `struct mnttab' */
+ new->dir = sstrdup(buf[i].f_mntonname);
+ new->spec_device = sstrdup(buf[i].f_mntfromname);
+ new->type = sstrdup(buf[i].f_fstypename);
+ new->options = NULL;
+ new->device = get_device_name(new->options);
+ new->next = NULL;
+
+ /* Append to list */
+ if (first == NULL) {
+ first = new;
+ last = new;
+ } else {
+ last->next = new;
+ last = new;
+ }
+ }
+
+ free(buf);
+
+ return first;
+}
+/* #endif HAVE_GETVFSSTAT || HAVE_GETFSSTAT */
+
+/* Solaris (SunOS 10): int getmntent(FILE *fp, struct mnttab *mp); */
+#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT
+static cu_mount_t *cu_mount_gen_getmntent(void) {
+ struct mnttab mt;
+ FILE *fp;
+
+ cu_mount_t *first = NULL;
+ cu_mount_t *last = NULL;
+ cu_mount_t *new = NULL;
+
+ DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
+
+ if ((fp = fopen(COLLECTD_MNTTAB, "r")) == NULL) {
+ ERROR("fopen (%s): %s", COLLECTD_MNTTAB, STRERRNO);
+ return NULL;
+ }
+
+ while (getmntent(fp, &mt) == 0) {
+ if ((new = calloc(1, sizeof(*new))) == NULL)
+ break;
+
+ /* Copy values from `struct mnttab' */
+ new->dir = sstrdup(mt.mnt_mountp);
+ new->spec_device = sstrdup(mt.mnt_special);
+ new->type = sstrdup(mt.mnt_fstype);
+ new->options = sstrdup(mt.mnt_mntopts);
+ new->device = get_device_name(new->options);
+ new->next = NULL;
+
+ /* Append to list */
+ if (first == NULL) {
+ first = new;
+ last = new;
+ } else {
+ last->next = new;
+ last = new;
+ }
+ }
+
+ fclose(fp);
+
+ return first;
+} /* static cu_mount_t *cu_mount_gen_getmntent (void) */
+
+#elif HAVE_SEQ_GETMNTENT
+#warn "This version of `getmntent' hat not yet been implemented!"
+/* #endif HAVE_SEQ_GETMNTENT */
+
+#elif HAVE_GETMNTENT_R
+static cu_mount_t *cu_mount_getmntent(void) {
+ FILE *fp;
+ struct mntent me;
+ char mntbuf[1024];
+
+ cu_mount_t *first = NULL;
+ cu_mount_t *last = NULL;
+ cu_mount_t *new = NULL;
+
+ DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
+
+ if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) {
+ ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO);
+ return NULL;
+ }
+
+ while (getmntent_r(fp, &me, mntbuf, sizeof(mntbuf))) {
+ if ((new = calloc(1, sizeof(*new))) == NULL)
+ break;
+
+ /* Copy values from `struct mntent *' */
+ new->dir = sstrdup(me.mnt_dir);
+ new->spec_device = sstrdup(me.mnt_fsname);
+ new->type = sstrdup(me.mnt_type);
+ new->options = sstrdup(me.mnt_opts);
+ new->device = get_device_name(new->options);
+ new->next = NULL;
+
+ DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options "
+ "= %s, device = %s}",
+ new->dir, new->spec_device, new->type, new->options, new->device);
+
+ /* Append to list */
+ if (first == NULL) {
+ first = new;
+ last = new;
+ } else {
+ last->next = new;
+ last = new;
+ }
+ }
+
+ endmntent(fp);
+
+ DEBUG("utils_mount: return 0x%p", (void *)first);
+
+ return first;
+} /* HAVE_GETMNTENT_R */
+
+#elif HAVE_ONE_GETMNTENT
+static cu_mount_t *cu_mount_getmntent(void) {
+ FILE *fp;
+ struct mntent *me;
+
+ cu_mount_t *first = NULL;
+ cu_mount_t *last = NULL;
+ cu_mount_t *new = NULL;
+
+ DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
+
+ if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) {
+ ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO);
+ return NULL;
+ }
+
+ while ((me = getmntent(fp)) != NULL) {
+ if ((new = calloc(1, sizeof(*new))) == NULL)
+ break;
+
+ /* Copy values from `struct mntent *' */
+ new->dir = sstrdup(me->mnt_dir);
+ new->spec_device = sstrdup(me->mnt_fsname);
+ new->type = sstrdup(me->mnt_type);
+ new->options = sstrdup(me->mnt_opts);
+ new->device = get_device_name(new->options);
+ new->next = NULL;
+
+ DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options "
+ "= %s, device = %s}",
+ new->dir, new->spec_device, new->type, new->options, new->device);
+
+ /* Append to list */
+ if (first == NULL) {
+ first = new;
+ last = new;
+ } else {
+ last->next = new;
+ last = new;
+ }
+ }
+
+ endmntent(fp);
+
+ DEBUG("utils_mount: return 0x%p", (void *)first);
+
+ return first;
+}
+#endif /* HAVE_ONE_GETMNTENT */
+
+/* *** *** *** ******************************************** *** *** *** */
+/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */
+/* *** *** *** ******************************************** *** *** *** */
+
+cu_mount_t *cu_mount_getlist(cu_mount_t **list) {
+ cu_mount_t *new;
+ cu_mount_t *first = NULL;
+ cu_mount_t *last = NULL;
+
+ if (list == NULL)
+ return NULL;
+
+ if (*list != NULL) {
+ first = *list;
+ last = first;
+ while (last->next != NULL)
+ last = last->next;
+ }
+
+#if HAVE_LISTMNTENT && 0
+ new = cu_mount_listmntent();
+#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT
+ new = cu_mount_getfsstat();
+#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT
+ new = cu_mount_gen_getmntent();
+#elif HAVE_SEQ_GETMNTENT
+#error "This version of `getmntent' hat not yet been implemented!"
+#elif HAVE_GETMNTENT_R
+ new = cu_mount_getmntent();
+#elif HAVE_ONE_GETMNTENT
+ new = cu_mount_getmntent();
+#else
+#error "Could not determine how to find mountpoints."
+#endif
+
+ if (first != NULL) {
+ last->next = new;
+ } else {
+ first = new;
+ last = new;
+ *list = first;
+ }
+
+ while ((last != NULL) && (last->next != NULL))
+ last = last->next;
+
+ return last;
+} /* cu_mount_t *cu_mount_getlist(cu_mount_t **list) */
+
+void cu_mount_freelist(cu_mount_t *list) {
+ cu_mount_t *next;
+
+ for (cu_mount_t *this = list; this != NULL; this = next) {
+ next = this->next;
+
+ sfree(this->dir);
+ sfree(this->spec_device);
+ sfree(this->device);
+ sfree(this->type);
+ sfree(this->options);
+ sfree(this);
+ }
+} /* void cu_mount_freelist(cu_mount_t *list) */
+
+char *cu_mount_checkoption(char *line, const char *keyword, int full) {
+ char *line2, *l2, *p1, *p2;
+ size_t l;
+
+ if (line == NULL || keyword == NULL) {
+ return NULL;
+ }
+
+ if (full != 0) {
+ full = 1;
+ }
+
+ line2 = sstrdup(line);
+ l2 = line2;
+ while (*l2 != '\0') {
+ if (*l2 == ',') {
+ *l2 = '\0';
+ }
+ l2++;
+ }
+
+ l = strlen(keyword);
+ p1 = line - 1;
+ p2 = strchr(line, ',');
+ do {
+ if (strncmp(line2 + (p1 - line) + 1, keyword, l + full) == 0) {
+ free(line2);
+ return p1 + 1;
+ }
+ p1 = p2;
+ if (p1 != NULL) {
+ p2 = strchr(p1 + 1, ',');
+ }
+ } while (p1 != NULL);
+
+ free(line2);
+ return NULL;
+} /* char *cu_mount_checkoption(char *line, char *keyword, int full) */
+
+char *cu_mount_getoptionvalue(char *line, const char *keyword) {
+ char *r;
+
+ r = cu_mount_checkoption(line, keyword, 0);
+ if (r != NULL) {
+ char *p;
+ r += strlen(keyword);
+ p = strchr(r, ',');
+ if (p == NULL) {
+ return sstrdup(r);
+ } else {
+ char *m;
+ if ((p - r) == 1) {
+ return NULL;
+ }
+ m = smalloc(p - r + 1);
+ sstrncpy(m, r, p - r + 1);
+ return m;
+ }
+ }
+ return r;
+} /* char *cu_mount_getoptionvalue(char *line, const char *keyword) */
+
+int cu_mount_type(const char *type) {
+ if (strcmp(type, "ext3") == 0)
+ return CUMT_EXT3;
+ if (strcmp(type, "ext2") == 0)
+ return CUMT_EXT2;
+ if (strcmp(type, "ufs") == 0)
+ return CUMT_UFS;
+ if (strcmp(type, "vxfs") == 0)
+ return CUMT_VXFS;
+ if (strcmp(type, "zfs") == 0)
+ return CUMT_ZFS;
+ return CUMT_UNKNOWN;
+} /* int cu_mount_type(const char *type) */
--- /dev/null
+/**
+ * collectd - src/utils_mount.h
+ * Copyright (C) 2005,2006 Niki W. Waibel
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence 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
+ *
+ * Author:
+ * Niki W. Waibel <niki.waibel@gmx.net>
+**/
+
+/* See below for instructions how to use the public functions. */
+
+#ifndef COLLECTD_UTILS_MOUNT_H
+#define COLLECTD_UTILS_MOUNT_H 1
+
+#include <stdio.h>
+#if HAVE_FS_INFO_H
+#include <fs_info.h>
+#endif
+#if HAVE_FSHELP_H
+#include <fshelp.h>
+#endif
+#if HAVE_PATHS_H
+#include <paths.h>
+#endif
+#if HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#if HAVE_MNTTAB_H
+#include <mnttab.h>
+#endif
+#if HAVE_SYS_FSTYP_H
+#include <sys/fstyp.h>
+#endif
+#if HAVE_SYS_FS_TYPES_H
+#include <sys/fs_types.h>
+#endif
+#if HAVE_SYS_MNTENT_H
+#include <sys/mntent.h>
+#endif
+#if HAVE_SYS_MNTTAB_H
+#include <sys/mnttab.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#if HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+#if HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+#if HAVE_SYS_VFSTAB_H
+#include <sys/vfstab.h>
+#endif
+
+/* Collectd Utils Mount Type */
+#define CUMT_UNKNOWN (0)
+#define CUMT_EXT2 (1)
+#define CUMT_EXT3 (2)
+#define CUMT_XFS (3)
+#define CUMT_UFS (4)
+#define CUMT_VXFS (5)
+#define CUMT_ZFS (6)
+
+typedef struct _cu_mount_t cu_mount_t;
+struct _cu_mount_t {
+ char *dir; /* "/sys" or "/" */
+ char *spec_device; /* "LABEL=/" or "none" or "proc" or "/dev/hda1" */
+ char *device; /* "none" or "proc" or "/dev/hda1" */
+ char *type; /* "sysfs" or "ext3" */
+ char *options; /* "rw,noatime,commit=600,quota,grpquota" */
+ cu_mount_t *next;
+};
+
+cu_mount_t *cu_mount_getlist(cu_mount_t **list);
+/*
+ DESCRIPTION
+ The cu_mount_getlist() function creates a list
+ of all mountpoints.
+
+ If *list is NULL, a new list is created and *list is
+ set to point to the first entry.
+
+ If *list is not NULL, the list of mountpoints is appended
+ and *list is not changed.
+
+ RETURN VALUE
+ The cu_mount_getlist() function returns a pointer to
+ the last entry of the list, or NULL if an error has
+ occured.
+
+ NOTES
+ In case of an error, *list is not modified.
+*/
+
+void cu_mount_freelist(cu_mount_t *list);
+/*
+ DESCRIPTION
+ The cu_mount_freelist() function free()s all memory
+ allocated by *list and *list itself as well.
+*/
+
+char *cu_mount_checkoption(char *line, const char *keyword, int full);
+/*
+ DESCRIPTION
+ The cu_mount_checkoption() function is a replacement of
+ char *hasmntopt(const struct mntent *mnt, const char *opt).
+ In fact hasmntopt() just looks for the first occurrence of the
+ characters at opt in mnt->mnt_opts. cu_mount_checkoption()
+ checks for the *option* keyword in line, starting at the
+ first character of line or after a ','.
+
+ If full is not 0 then also the end of keyword has to match
+ either the end of line or a ',' after keyword.
+
+ RETURN VALUE
+ The cu_mount_checkoption() function returns a pointer into
+ string line if a match of keyword is found. If no match is
+ found cu_mount_checkoption() returns NULL.
+
+ NOTES
+ Do *not* try to free() the pointer which is returned! It is
+ just part of the string line.
+
+ full should be set to 0 when matching options like: rw, quota,
+ noatime. Set full to 1 when matching options like: loop=,
+ gid=, commit=.
+
+ EXAMPLES
+ If line is "rw,usrquota,grpquota", keyword is "quota", NULL
+ will be returned (independend of full).
+
+ If line is "rw,usrquota,grpquota", keyword is "usrquota",
+ a pointer to "usrquota,grpquota" is returned (independend
+ of full).
+
+ If line is "rw,loop=/dev/loop1,quota", keyword is "loop="
+ and full is 0, then a pointer to "loop=/dev/loop1,quota"
+ is returned. If full is not 0 then NULL is returned. But
+ maybe you might want to try cu_mount_getoptionvalue()...
+*/
+
+char *cu_mount_getoptionvalue(char *line, const char *keyword);
+/*
+ DESCRIPTION
+ The cu_mount_getoptionvalue() function can be used to grab
+ a VALUE out of a mount option (line) like:
+ loop=VALUE
+ whereas "loop=" is the keyword.
+
+ RETURN VALUE
+ If the cu_mount_getoptionvalue() function can find the option
+ keyword in line, then memory is allocated for the value of
+ that option and a pointer to that value is returned.
+
+ If the option keyword is not found, cu_mount_getoptionvalue()
+ returns NULL;
+
+ NOTES
+ Internally it calls cu_mount_checkoption(), then it
+ allocates memory for VALUE and returns a pointer to that
+ string. So *do not forget* to free() the memory returned
+ after use!!!
+*/
+
+int cu_mount_type(const char *type);
+/*
+ DESCRIPTION
+
+ RETURN VALUE
+*/
+
+#endif /* !COLLECTD_UTILS_MOUNT_H */
--- /dev/null
+/**
+ * collectd - src/tests/test_utils_mount.c
+ * Copyright (C) 2013 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/common/common.h"
+#include "utils/mount/mount.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
+
+#if HAVE_LIBKSTAT
+kstat_ctl_t *kc;
+#endif /* HAVE_LIBKSTAT */
+
+DEF_TEST(cu_mount_checkoption) {
+ char line_opts[] = "foo=one,bar=two,qux=three";
+ char *foo = strstr(line_opts, "foo");
+ char *bar = strstr(line_opts, "bar");
+ char *qux = strstr(line_opts, "qux");
+
+ char line_bool[] = "one,two,three";
+ char *one = strstr(line_bool, "one");
+ char *two = strstr(line_bool, "two");
+ char *three = strstr(line_bool, "three");
+
+ /* Normal operation */
+ OK(foo == cu_mount_checkoption(line_opts, "foo", 0));
+ OK(bar == cu_mount_checkoption(line_opts, "bar", 0));
+ OK(qux == cu_mount_checkoption(line_opts, "qux", 0));
+ OK(NULL == cu_mount_checkoption(line_opts, "unknown", 0));
+
+ OK(one == cu_mount_checkoption(line_bool, "one", 0));
+ OK(two == cu_mount_checkoption(line_bool, "two", 0));
+ OK(three == cu_mount_checkoption(line_bool, "three", 0));
+ OK(NULL == cu_mount_checkoption(line_bool, "four", 0));
+
+ /* Shorter and longer parts */
+ OK(foo == cu_mount_checkoption(line_opts, "fo", 0));
+ OK(bar == cu_mount_checkoption(line_opts, "bar=", 0));
+ OK(qux == cu_mount_checkoption(line_opts, "qux=thr", 0));
+
+ OK(one == cu_mount_checkoption(line_bool, "o", 0));
+ OK(two == cu_mount_checkoption(line_bool, "tw", 0));
+ OK(three == cu_mount_checkoption(line_bool, "thr", 0));
+
+ /* "full" flag */
+ OK(one == cu_mount_checkoption(line_bool, "one", 1));
+ OK(two == cu_mount_checkoption(line_bool, "two", 1));
+ OK(three == cu_mount_checkoption(line_bool, "three", 1));
+ OK(NULL == cu_mount_checkoption(line_bool, "four", 1));
+
+ OK(NULL == cu_mount_checkoption(line_bool, "o", 1));
+ OK(NULL == cu_mount_checkoption(line_bool, "tw", 1));
+ OK(NULL == cu_mount_checkoption(line_bool, "thr", 1));
+
+ return 0;
+}
+DEF_TEST(cu_mount_getoptionvalue) {
+ char line_opts[] = "foo=one,bar=two,qux=three";
+ char line_bool[] = "one,two,three";
+ char *v;
+
+ EXPECT_EQ_STR("one", v = cu_mount_getoptionvalue(line_opts, "foo="));
+ sfree(v);
+ EXPECT_EQ_STR("two", v = cu_mount_getoptionvalue(line_opts, "bar="));
+ sfree(v);
+ EXPECT_EQ_STR("three", v = cu_mount_getoptionvalue(line_opts, "qux="));
+ sfree(v);
+ OK(NULL == (v = cu_mount_getoptionvalue(line_opts, "unknown=")));
+ sfree(v);
+
+ EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "one"));
+ sfree(v);
+ EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "two"));
+ sfree(v);
+ EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "three"));
+ sfree(v);
+ OK(NULL == (v = cu_mount_getoptionvalue(line_bool, "four")));
+ sfree(v);
+
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(cu_mount_checkoption);
+ RUN_TEST(cu_mount_getoptionvalue);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_oauth.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/oauth/oauth.h"
+
+#include <curl/curl.h>
+
+#include <yajl/yajl_tree.h>
+#include <yajl/yajl_version.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/sha.h>
+
+/*
+ * Private variables
+ */
+#define GOOGLE_TOKEN_URL "https://accounts.google.com/o/oauth2/token"
+
+/* Max send buffer size, since there will be only one writer thread and
+ * monitoring api supports up to 100K bytes in one request, 64K is reasonable
+ */
+#define MAX_BUFFER_SIZE 65536
+#define MAX_ENCODE_SIZE 2048
+
+struct oauth_s {
+ char *url;
+ char *iss;
+ char *aud;
+ char *scope;
+
+ EVP_PKEY *key;
+
+ char *token;
+ cdtime_t valid_until;
+};
+
+struct memory_s {
+ char *memory;
+ size_t size;
+};
+typedef struct memory_s memory_t;
+
+#define OAUTH_GRANT_TYPE "urn:ietf:params:oauth:grant-type:jwt-bearer"
+#define OAUTH_EXPIRATION_TIME TIME_T_TO_CDTIME_T(3600)
+#define OAUTH_HEADER "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"
+
+static const char OAUTH_CLAIM_FORMAT[] = "{"
+ "\"iss\":\"%s\","
+ "\"scope\":\"%s\","
+ "\"aud\":\"%s\","
+ "\"exp\":%lu,"
+ "\"iat\":%lu"
+ "}";
+
+static size_t write_memory(void *contents, size_t size, size_t nmemb, /* {{{ */
+ void *userp) {
+ size_t realsize = size * nmemb;
+ memory_t *mem = (memory_t *)userp;
+ char *tmp;
+
+ if (0x7FFFFFF0 < mem->size || 0x7FFFFFF0 - mem->size < realsize) {
+ ERROR("integer overflow");
+ return 0;
+ }
+
+ tmp = (char *)realloc((void *)mem->memory, mem->size + realsize + 1);
+ if (tmp == NULL) {
+ /* out of memory! */
+ ERROR("write_memory: not enough memory (realloc returned NULL)");
+ return 0;
+ }
+ mem->memory = tmp;
+
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+
+ return realsize;
+} /* }}} size_t write_memory */
+
+/* Base64-encodes "s" and stores the result in buffer.
+ * Returns zero on success, non-zero otherwise. */
+static int base64_encode_n(char const *s, size_t s_size, /* {{{ */
+ char *buffer, size_t buffer_size) {
+ BIO *b64;
+ BUF_MEM *bptr;
+ int status;
+ size_t i;
+
+ /* Set up the memory-base64 chain */
+ b64 = BIO_new(BIO_f_base64());
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ b64 = BIO_push(b64, BIO_new(BIO_s_mem()));
+
+ /* Write data to the chain */
+ BIO_write(b64, (void const *)s, s_size);
+ status = BIO_flush(b64);
+ if (status != 1) {
+ ERROR("utils_oauth: base64_encode: BIO_flush() failed.");
+ BIO_free_all(b64);
+ return -1;
+ }
+
+ /* Never fails */
+ BIO_get_mem_ptr(b64, &bptr);
+
+ if (buffer_size <= bptr->length) {
+ ERROR("utils_oauth: base64_encode: Buffer too small.");
+ BIO_free_all(b64);
+ return -1;
+ }
+
+ /* Copy data to buffer. */
+ memcpy(buffer, bptr->data, bptr->length);
+ buffer[bptr->length] = 0;
+
+ /* replace + with -, / with _ and remove padding = at the end */
+ for (i = 0; i < bptr->length; i++) {
+ if (buffer[i] == '+') {
+ buffer[i] = '-';
+ } else if (buffer[i] == '/') {
+ buffer[i] = '_';
+ } else if (buffer[i] == '=') {
+ buffer[i] = 0;
+ }
+ }
+
+ BIO_free_all(b64);
+ return 0;
+} /* }}} int base64_encode_n */
+
+/* Base64-encodes "s" and stores the result in buffer.
+ * Returns zero on success, non-zero otherwise. */
+static int base64_encode(char const *s, /* {{{ */
+ char *buffer, size_t buffer_size) {
+ return base64_encode_n(s, strlen(s), buffer, buffer_size);
+} /* }}} int base64_encode */
+
+/* get_header returns the base64 encoded OAuth header. */
+static int get_header(char *buffer, size_t buffer_size) /* {{{ */
+{
+ char header[] = OAUTH_HEADER;
+
+ return base64_encode(header, buffer, buffer_size);
+} /* }}} int get_header */
+
+/* get_claim constructs an OAuth claim and returns it as base64 encoded string.
+ */
+static int get_claim(oauth_t *auth, char *buffer, size_t buffer_size) /* {{{ */
+{
+ char claim[buffer_size];
+ cdtime_t exp;
+ cdtime_t iat;
+ int status;
+
+ iat = cdtime();
+ exp = iat + OAUTH_EXPIRATION_TIME;
+
+ /* create the claim set */
+ status =
+ snprintf(claim, sizeof(claim), OAUTH_CLAIM_FORMAT, auth->iss, auth->scope,
+ auth->aud, (unsigned long)CDTIME_T_TO_TIME_T(exp),
+ (unsigned long)CDTIME_T_TO_TIME_T(iat));
+ if (status < 1)
+ return -1;
+ else if ((size_t)status >= sizeof(claim))
+ return ENOMEM;
+
+ DEBUG("utils_oauth: get_claim() = %s", claim);
+
+ return base64_encode(claim, buffer, buffer_size);
+} /* }}} int get_claim */
+
+/* get_signature signs header and claim with pkey and returns the signature in
+ * buffer. */
+static int get_signature(char *buffer, size_t buffer_size, /* {{{ */
+ char const *header, char const *claim,
+ EVP_PKEY *pkey) {
+ char payload[buffer_size];
+ size_t payload_len;
+ char signature[buffer_size];
+ unsigned int signature_size;
+ int status;
+
+ /* Make the string to sign */
+ payload_len = snprintf(payload, sizeof(payload), "%s.%s", header, claim);
+ if (payload_len < 1) {
+ return -1;
+ } else if (payload_len >= sizeof(payload)) {
+ return ENOMEM;
+ }
+
+ /* Create the signature */
+ signature_size = EVP_PKEY_size(pkey);
+ if (signature_size > sizeof(signature)) {
+ ERROR("utils_oauth: Signature is too large (%u bytes).", signature_size);
+ return -1;
+ }
+
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+
+ /* EVP_SignInit(3SSL) claims this is a void function, but in fact it returns
+ * an int. We're not going to rely on this, though. */
+ EVP_SignInit(ctx, EVP_sha256());
+
+ status = EVP_SignUpdate(ctx, payload, payload_len);
+ if (status != 1) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR("utils_oauth: EVP_SignUpdate failed: %s", errbuf);
+
+ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+
+ status =
+ EVP_SignFinal(ctx, (unsigned char *)signature, &signature_size, pkey);
+ if (status != 1) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR("utils_oauth: EVP_SignFinal failed: %s", errbuf);
+
+ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+
+ EVP_MD_CTX_free(ctx);
+
+ return base64_encode_n(signature, (size_t)signature_size, buffer,
+ buffer_size);
+} /* }}} int get_signature */
+
+static int get_assertion(oauth_t *auth, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ char header[buffer_size];
+ char claim[buffer_size];
+ char signature[buffer_size];
+ int status;
+
+ status = get_header(header, sizeof(header));
+ if (status != 0)
+ return -1;
+
+ status = get_claim(auth, claim, sizeof(claim));
+ if (status != 0)
+ return -1;
+
+ status =
+ get_signature(signature, sizeof(signature), header, claim, auth->key);
+ if (status != 0)
+ return -1;
+
+ status = snprintf(buffer, buffer_size, "%s.%s.%s", header, claim, signature);
+ if (status < 1)
+ return -1;
+ else if (status >= buffer_size)
+ return ENOMEM;
+
+ return 0;
+} /* }}} int get_assertion */
+
+int oauth_parse_json_token(char const *json, /* {{{ */
+ char *out_access_token, size_t access_token_size,
+ cdtime_t *expires_in) {
+ time_t expire_in_seconds = 0;
+ yajl_val root;
+ yajl_val token_val;
+ yajl_val expire_val;
+ char errbuf[1024];
+ const char *token_path[] = {"access_token", NULL};
+ const char *expire_path[] = {"expires_in", NULL};
+
+ root = yajl_tree_parse(json, errbuf, sizeof(errbuf));
+ if (root == NULL) {
+ ERROR("utils_oauth: oauth_parse_json_token: parse error %s", errbuf);
+ return -1;
+ }
+
+ token_val = yajl_tree_get(root, token_path, yajl_t_string);
+ if (token_val == NULL) {
+ ERROR("utils_oauth: oauth_parse_json_token: access token field not found");
+ yajl_tree_free(root);
+ return -1;
+ }
+ sstrncpy(out_access_token, YAJL_GET_STRING(token_val), access_token_size);
+
+ expire_val = yajl_tree_get(root, expire_path, yajl_t_number);
+ if (expire_val == NULL) {
+ ERROR("utils_oauth: oauth_parse_json_token: expire field found");
+ yajl_tree_free(root);
+ return -1;
+ }
+ expire_in_seconds = (time_t)YAJL_GET_INTEGER(expire_val);
+ DEBUG("oauth_parse_json_token: expires_in %lu",
+ (unsigned long)expire_in_seconds);
+
+ *expires_in = TIME_T_TO_CDTIME_T(expire_in_seconds);
+ yajl_tree_free(root);
+ return 0;
+} /* }}} int oauth_parse_json_token */
+
+static int new_token(oauth_t *auth) /* {{{ */
+{
+ CURL *curl;
+ char assertion[1024];
+ char post_data[1024];
+ memory_t data;
+ char access_token[256];
+ cdtime_t expires_in;
+ cdtime_t now;
+ char curl_errbuf[CURL_ERROR_SIZE];
+ int status = 0;
+
+ data.size = 0;
+ data.memory = NULL;
+
+ now = cdtime();
+
+ status = get_assertion(auth, assertion, sizeof(assertion));
+ if (status != 0) {
+ ERROR("utils_oauth: Failed to get token using service account %s.",
+ auth->iss);
+ return -1;
+ }
+
+ snprintf(post_data, sizeof(post_data), "grant_type=%s&assertion=%s",
+ OAUTH_GRANT_TYPE, assertion);
+
+ curl = curl_easy_init();
+ if (curl == NULL) {
+ ERROR("utils_oauth: curl_easy_init failed.");
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
+ curl_easy_setopt(curl, CURLOPT_URL, auth->url);
+
+ status = curl_easy_perform(curl);
+ if (status != CURLE_OK) {
+ ERROR("utils_oauth: curl_easy_perform failed with status %i: %s", status,
+ curl_errbuf);
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ } else {
+ long http_code = 0;
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ ERROR("utils_oauth: POST request to %s failed: HTTP error %ld", auth->url,
+ http_code);
+ if (data.memory != NULL)
+ INFO("utils_oauth: Server replied: %s", data.memory);
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ }
+ }
+
+ status = oauth_parse_json_token(data.memory, access_token,
+ sizeof(access_token), &expires_in);
+ if (status != 0) {
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ }
+
+ sfree(auth->token);
+ auth->token = strdup(access_token);
+ if (auth->token == NULL) {
+ ERROR("utils_oauth: strdup failed");
+ auth->valid_until = 0;
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+ return -1;
+ }
+
+ INFO("utils_oauth: OAuth2 access token is valid for %.3fs",
+ CDTIME_T_TO_DOUBLE(expires_in));
+ auth->valid_until = now + expires_in;
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return 0;
+} /* }}} int new_token */
+
+static int renew_token(oauth_t *auth) /* {{{ */
+{
+ /* Renew OAuth token 30 seconds *before* it expires. */
+ cdtime_t const slack = TIME_T_TO_CDTIME_T(30);
+
+ if (auth->valid_until > (cdtime() + slack))
+ return 0;
+
+ return new_token(auth);
+} /* }}} int renew_token */
+
+static oauth_t *oauth_create(char const *url, char const *iss,
+ char const *scope, char const *aud,
+ EVP_PKEY *key) /* {{{ */
+{
+ oauth_t *auth;
+
+ if ((url == NULL) || (iss == NULL) || (scope == NULL) || (aud == NULL) ||
+ (key == NULL))
+ return NULL;
+
+ auth = malloc(sizeof(*auth));
+ if (auth == NULL)
+ return NULL;
+ memset(auth, 0, sizeof(*auth));
+
+ auth->url = strdup(url);
+ auth->iss = strdup(iss);
+ auth->scope = strdup(scope);
+ auth->aud = strdup(aud);
+
+ if ((auth->url == NULL) || (auth->iss == NULL) || (auth->scope == NULL) ||
+ (auth->aud == NULL)) {
+ oauth_destroy(auth);
+ return NULL;
+ }
+
+ auth->key = key;
+
+ return auth;
+} /* }}} oauth_t *oauth_create */
+
+/*
+ * Public
+ */
+oauth_google_t oauth_create_google_json(char const *buffer, char const *scope) {
+ char errbuf[1024];
+ yajl_val root = yajl_tree_parse(buffer, errbuf, sizeof(errbuf));
+ if (root == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: parse error %s", errbuf);
+ return (oauth_google_t){NULL};
+ }
+
+ yajl_val field_project =
+ yajl_tree_get(root, (char const *[]){"project_id", NULL}, yajl_t_string);
+ if (field_project == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: project_id field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+ char const *project_id = YAJL_GET_STRING(field_project);
+
+ yajl_val field_iss = yajl_tree_get(
+ root, (char const *[]){"client_email", NULL}, yajl_t_string);
+ if (field_iss == NULL) {
+ ERROR(
+ "utils_oauth: oauth_create_google_json: client_email field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ yajl_val field_token_uri =
+ yajl_tree_get(root, (char const *[]){"token_uri", NULL}, yajl_t_string);
+ char const *token_uri = (field_token_uri != NULL)
+ ? YAJL_GET_STRING(field_token_uri)
+ : GOOGLE_TOKEN_URL;
+
+ yajl_val field_priv_key =
+ yajl_tree_get(root, (char const *[]){"private_key", NULL}, yajl_t_string);
+ if (field_priv_key == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: private_key field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ BIO *bp = BIO_new_mem_buf(YAJL_GET_STRING(field_priv_key), -1);
+ EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR(
+ "utils_oauth: oauth_create_google_json: parsing private key failed: %s",
+ errbuf);
+ BIO_free(bp);
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ BIO_free(bp);
+
+ oauth_t *oauth = oauth_create(token_uri, YAJL_GET_STRING(field_iss), scope,
+ token_uri, pkey);
+ if (oauth == NULL) {
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ oauth_google_t ret = {
+ .project_id = strdup(project_id), .oauth = oauth,
+ };
+
+ yajl_tree_free(root);
+ return ret;
+} /* oauth_google_t oauth_create_google_json */
+
+oauth_google_t oauth_create_google_file(char const *path,
+ char const *scope) { /* {{{ */
+ int fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (oauth_google_t){NULL};
+
+ struct stat st = {0};
+ if (fstat(fd, &st) != 0) {
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+
+ size_t buf_size = (size_t)st.st_size;
+ char *buf = calloc(1, buf_size + 1);
+ if (buf == NULL) {
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+
+ if (sread(fd, buf, buf_size) != 0) {
+ free(buf);
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+ close(fd);
+ buf[buf_size] = 0;
+
+ oauth_google_t ret = oauth_create_google_json(buf, scope);
+
+ free(buf);
+ return ret;
+} /* }}} oauth_google_t oauth_create_google_file */
+
+/* oauth_create_google_default checks for JSON credentials in well-known
+ * positions, similar to gcloud and other tools. */
+oauth_google_t oauth_create_google_default(char const *scope) {
+ char const *app_creds;
+ if ((app_creds = getenv("GOOGLE_APPLICATION_CREDENTIALS")) != NULL) {
+ oauth_google_t ret = oauth_create_google_file(app_creds, scope);
+ if (ret.oauth == NULL) {
+ ERROR("The environment variable GOOGLE_APPLICATION_CREDENTIALS is set to "
+ "\"%s\" but that file could not be read.",
+ app_creds);
+ } else {
+ return ret;
+ }
+ }
+
+ char const *home;
+ if ((home = getenv("HOME")) != NULL) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path),
+ "%s/.config/gcloud/application_default_credentials.json", home);
+
+ oauth_google_t ret = oauth_create_google_file(path, scope);
+ if (ret.oauth != NULL) {
+ return ret;
+ }
+ }
+
+ return (oauth_google_t){NULL};
+} /* }}} oauth_google_t oauth_create_google_default */
+
+void oauth_destroy(oauth_t *auth) /* {{{ */
+{
+ if (auth == NULL)
+ return;
+
+ sfree(auth->url);
+ sfree(auth->iss);
+ sfree(auth->scope);
+ sfree(auth->aud);
+
+ if (auth->key != NULL) {
+ EVP_PKEY_free(auth->key);
+ auth->key = NULL;
+ }
+
+ sfree(auth);
+} /* }}} void oauth_destroy */
+
+int oauth_access_token(oauth_t *auth, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ int status;
+
+ if (auth == NULL)
+ return EINVAL;
+
+ status = renew_token(auth);
+ if (status != 0)
+ return status;
+ assert(auth->token != NULL);
+
+ sstrncpy(buffer, auth->token, buffer_size);
+ return 0;
+} /* }}} int oauth_access_token */
--- /dev/null
+/**
+ * collectd - src/utils_oauth.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_OAUTH_H
+#define UTILS_OAUTH_H
+
+#include "collectd.h"
+#include "utils_time.h"
+
+#ifndef GOOGLE_OAUTH_URL
+#define GOOGLE_OAUTH_URL "https://www.googleapis.com/oauth2/v3/token"
+#endif
+
+struct oauth_s;
+typedef struct oauth_s oauth_t;
+
+int oauth_parse_json_token(char const *json, char *out_access_token,
+ size_t access_token_size, cdtime_t *expires_in);
+
+typedef struct {
+ char *project_id;
+ oauth_t *oauth;
+} oauth_google_t;
+
+/* oauth_create_google_json creates an OAuth object from JSON encoded
+ * credentials. */
+oauth_google_t oauth_create_google_json(char const *json, char const *scope);
+
+/* oauth_create_google_file reads path, which contains JSON encoded service
+ * account credentials, and returns an OAuth object. */
+oauth_google_t oauth_create_google_file(char const *path, char const *scope);
+
+/* oauth_create_google_default looks for service account credentials in a couple
+ * of well-known places and returns an OAuth object if found. The well known
+ * locations are:
+ *
+ * - ${GOOGLE_APPLICATION_CREDENTIALS}
+ * - ${HOME}/.config/gcloud/application_default_credentials.json
+ */
+oauth_google_t oauth_create_google_default(char const *scope);
+
+/* oauth_destroy frees all resources associated with an OAuth object. */
+void oauth_destroy(oauth_t *auth);
+
+int oauth_access_token(oauth_t *auth, char *buffer, size_t buffer_size);
+
+#endif
--- /dev/null
+/**
+ * collectd - src/tests/utils_oauth_test.c
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at google.com>
+ **/
+
+#include "testing.h"
+#include "utils/oauth/oauth.h"
+
+struct {
+ char *json;
+ int status;
+ char *access_token;
+ cdtime_t expires_in;
+} cases[] = {
+ {
+ "{\"access_token\":\"MaeC6kaePhie1ree\",\"expires_in\":3600}",
+ /* status = */ 0, "MaeC6kaePhie1ree", TIME_T_TO_CDTIME_T_STATIC(3600),
+ },
+ {
+ "{\"token_type\":\"Bearer\",\"expires_in\":1800,\"access_token\":"
+ "\"aeThiebee2gushuY\"}",
+ /* status = */ 0, "aeThiebee2gushuY", TIME_T_TO_CDTIME_T_STATIC(1800),
+ },
+ {
+ "{\"ignored_key\":\"uaph5aewaeghi1Ge\",\"expires_in\":3600}",
+ /* status = */ -1, NULL, 0,
+ },
+ {
+ /* expires_in missing */
+ "{\"access_token\":\"shaephohbie9Ahch\"}",
+ /* status = */ -1, NULL, 0,
+ },
+};
+
+DEF_TEST(simple) /* {{{ */
+{
+ size_t i;
+ _Bool success = 1;
+
+ for (i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
+ char buffer[1024];
+ cdtime_t expires_in;
+
+ EXPECT_EQ_INT(cases[i].status,
+ oauth_parse_json_token(cases[i].json, buffer, sizeof(buffer),
+ &expires_in));
+ if (cases[i].status != 0)
+ continue;
+
+ EXPECT_EQ_STR(cases[i].access_token, buffer);
+ EXPECT_EQ_UINT64(cases[i].expires_in, expires_in);
+ }
+
+ return success ? 0 : -1;
+} /* }}} simple */
+
+DEF_TEST(oauth_create_google_json) {
+ char const *in =
+ "{\"type\": \"service_account\","
+ "\"project_id\":\"collectd.org:unit-test\","
+ "\"private_key_id\": \"ed7b4eb6c1b61a7bedab5bcafff374f7fc820698\","
+ "\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n"
+ "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNvS71Lr2WIEqx\\n"
+ "U766iJGORVVib0FnHhOf/0FEI4Hw+tF11vP3LZj0AyQFIi/h2l2EDXOr43C6Gt+K\\n"
+ "0stsyaWvRNzeQa+dUFY5A/ZEtdvYVPq7KudML5Hs9DNmWFlM/iIfQyIUJ+vHv7fe\\n"
+ "pJGgu4ZgSkNWehmWj3qiRzIvYxKvDIQizqPZNlTh+33KQcT2x+ErkuB3snQu8hSK\\n"
+ "HAg2sCvORqKGOvN9F4bAqXt5T0NVjGy4YXeuif1p/Np/GH6Ys1p+etgGwvIimXIv\\n"
+ "jFL9K/ZtrTOcFdy4R5bwrj2piCZa2T5H6fupVp2tVgIuS53r2fEaBMLD97oAvwZ3\\n"
+ "9XPxG1NLAgMBAAECggEACgHroKcrN1FkdgyzSIKFG1evCBCOV17kqHyI5wYXzNTT\\n"
+ "zyNrZDjBFGQkt+U0/AucTznnnahSCZNuD+QiBgLRqYgJevwp99Z6YzVDS438Xsuq\\n"
+ "Ezmf3O+sGEu78Pys11cTP38LT3yuS4iSqo9Jus5JrTG05dDJoYO4J4rxW3xlDRj8\\n"
+ "lQUimXI+S9skaSusf0oErDrjuQG9dxmhnGcSEX+rIe9G0UygTNuI0KKGJ8jmnPz5\\n"
+ "OS+sM8qrKcnjrvENFWKLb11HlliHkh6dILoO5rvf5DR+XGKM7BFAsdWg6oI7SFGh\\n"
+ "S6zGZ0jUR7QAugrjbTlDOCnAuZ+Mbc/4yHZ3u5PlcQKBgQDuvH1ds1YmmbOllOK5\\n"
+ "JtkdjCUUyH1bgkMrmcg/KkRARPRHQvfAioZsC6d0fa6jq0kTW/3Zu14IsVXgM8xK\\n"
+ "fuNSp8LdY+NCtJnfvdLaChgAwZaQLX4qgV0qYw8iLv5ifa4ZY0qaZioJCzkv57y1\\n"
+ "KkavYvITboO7aUSa441Zko9c+wKBgQDcndg0QpWH6JMz/FkCf/KDyW/cUODfKXhP\\n"
+ "5p9eTcVlfDL2sAb2RzVhvKZcuWXVwnfaDP0oBj2/SBLGx0idUb+VHdM/IGiLroyK\\n"
+ "pAHpNM//dowiGL1qPPOLXrzF/vn+w4t2Dqggfcqu52SzRiyaxUtSMnNyyyU19cO+\\n"
+ "pb7wAS5x8QKBgCW7WL0UeQtEw6Xp8CN/RlVrLvkn7tglsGQVvBZvobXesBULOokN\\n"
+ "28z70o2Qx6dKjRQoN+jPuj75eC8lQKaNg3Qu25eOD/8c+CzqnYakjcKg1iEXb5dc\\n"
+ "NtNaMKwgbUg3wOp2TPY2K3KeeX1ezO59LgrOQqBbmSpnqtYoHNEJXus9AoGAWl/y\\n"
+ "9J2eIdm9i5tBX0vIrgHz5/3d0K1tUtX3zSrwxT0Wp4W+pF7RWGNuhyePtvx+Gn4d\\n"
+ "qqq72sMMpg93CLM3Vz+rjP2atjXf7t92xPDUkCMhDsqxtXaYkixSCo4EHUA/vjIM\\n"
+ "35qIUBQMZYBGv3Q5AcgXERx09uDhuhSt3iWtwBECgYAHFnCh8fKsJbQrVN10tU/h\\n"
+ "ofVx0KZkUpBz8eNQPuxt4aY+LyWsKVKtnduw2WdumuOY66cUN1lsi8Bz/cq1dhPt\\n"
+ "Oc2S7pqjbu2Q1Oqx+/yr6jqsvKaSxHmcpbWQBsGn6UaWZgYZcAtQBcqDAp7pylwj\\n"
+ "tejRh0NB8d81H5Dli1Qfzw==\\n"
+ "-----END PRIVATE KEY-----\\n\","
+ "\"client_email\":\"example-sacct@unit-test.iam.gserviceaccount.com\", "
+ "\"client_id\": \"109958449193027604084\","
+ "\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\","
+ "\"token_uri\":\"https://accounts.google.com/o/oauth2/token\","
+ "\"auth_provider_x509_cert_url\":"
+ "\"https://www.googleapis.com/oauth2/v1/certs\","
+ "\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/"
+ "metadata/x509/example-sacct%40ssc-serv-dev.iam.gserviceaccount.com\"}";
+
+ oauth_google_t ret =
+ oauth_create_google_json(in, "https://collectd.org/example.scope");
+
+ EXPECT_EQ_STR("collectd.org:unit-test", ret.project_id);
+
+ CHECK_NOT_NULL(ret.oauth);
+ struct {
+ char *url;
+ char *iss;
+ char *aud;
+ char *scope;
+ } *obj = (void *)ret.oauth;
+
+ EXPECT_EQ_STR("https://accounts.google.com/o/oauth2/token", obj->url);
+ EXPECT_EQ_STR("example-sacct@unit-test.iam.gserviceaccount.com", obj->iss);
+ EXPECT_EQ_STR("https://collectd.org/example.scope", obj->scope);
+
+ free(ret.project_id);
+ oauth_destroy(ret.oauth);
+
+ return 0;
+}
+
+int main(int argc, char **argv) /* {{{ */
+{
+ RUN_TEST(simple);
+ RUN_TEST(oauth_create_google_json);
+
+ END_TEST;
+} /* }}} int main */
--- /dev/null
+/**
+ * collectd - src/utils_ovs.c
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ *of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to
+ *do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ *all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
+ **/
+
+/* clang-format off */
+/*
+ * OVS DB API internal architecture diagram
+ * +------------------------------------------------------------------------------+
+ * |OVS plugin |OVS utils |
+ * | | +------------------------+ |
+ * | | | echo handler | JSON request/ |
+ * | | +--+ (ovs_db_table_echo_cb) +<---+---------+ update event/ |
+ * | | | | | | | result |
+ * | | | +------------------------+ | | |
+ * | | | | +----+---+--------+ |
+ * | +----------+ | | +------------------------+ | | | | |
+ * | | update | | | | update handler | | | YAJL | JSON | |
+ * | | callback +<-------+(ovs_db_table_update_cp)+<---+ | parser | reader | |
+ * | +----------+ | | | | | | | | |
+ * | | | +------------------------+ | +--------+---+----+ |
+ * | | | | ^ |
+ * | +----------+ | | +------------------------+ | | |
+ * | | result | | | | result handler | | | |
+ * | | callback +<-------+ (ovs_db_result_cb) +<---+ JSON raw | |
+ * | +----------+ | | | | data | |
+ * | | | +------------------------+ | |
+ * | | | | |
+ * | | | +------------------+ +------------+----+ |
+ * | +----------+ | | |thread| | |thread| | |
+ * | | init | | | | | reconnect | | |
+ * | | callback +<---------+ EVENT WORKER +<------------+ POLL WORKER | |
+ * | +----------+ | | +------------------+ +--------+--------+ |
+ * | | | ^ |
+ * +----------------+-------------------------------------------------------------+
+ * | |
+ * JSON|echo reply raw|data
+ * v v
+ * +-------------------+----------------------------------------------+-----------+
+ * | TCP/UNIX socket |
+ * +-------------------------------------------------------------------------------
+ */
+/* clang-format on */
+
+/* collectd headers */
+#include "collectd.h"
+
+#include "utils/common/common.h"
+
+/* private headers */
+#include "utils/ovs/ovs.h"
+
+/* system libraries */
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_POLL_H
+#include <poll.h>
+#endif
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include <semaphore.h>
+
+#define OVS_ERROR(fmt, ...) \
+ do { \
+ ERROR("ovs_utils: " fmt, ##__VA_ARGS__); \
+ } while (0)
+#define OVS_DEBUG(fmt, ...) \
+ do { \
+ DEBUG("%s:%d:%s(): " fmt, __FILE__, __LINE__, __FUNCTION__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define OVS_DB_POLL_TIMEOUT 1 /* poll receive timeout (sec) */
+#define OVS_DB_POLL_READ_BLOCK_SIZE 512 /* read block size (bytes) */
+#define OVS_DB_DEFAULT_DB_NAME "Open_vSwitch"
+
+#define OVS_DB_EVENT_NONE 0
+#define OVS_DB_EVENT_TIMEOUT 5 /* event thread timeout (sec) */
+#define OVS_DB_EVENT_TERMINATE 1
+#define OVS_DB_EVENT_CONN_ESTABLISHED 2
+#define OVS_DB_EVENT_CONN_TERMINATED 3
+
+#define OVS_DB_POLL_STATE_RUNNING 1
+#define OVS_DB_POLL_STATE_EXITING 2
+
+#define OVS_DB_SEND_REQ_TIMEOUT 5 /* send request timeout (sec) */
+
+#define OVS_YAJL_CALL(func, ...) \
+ do { \
+ yajl_gen_ret = yajl_gen_status_ok; \
+ if ((yajl_gen_ret = func(__VA_ARGS__)) != yajl_gen_status_ok) \
+ goto yajl_gen_failure; \
+ } while (0)
+#define OVS_YAJL_ERROR_BUFFER_SIZE 1024
+#define OVS_ERROR_BUFF_SIZE 512
+#define OVS_UID_STR_SIZE 17 /* 64-bit HEX string len + '\0' */
+
+/* JSON reader internal data */
+struct ovs_json_reader_s {
+ char *buff_ptr;
+ size_t buff_size;
+ size_t buff_offset;
+ size_t json_offset;
+};
+typedef struct ovs_json_reader_s ovs_json_reader_t;
+
+/* Result callback declaration */
+struct ovs_result_cb_s {
+ sem_t sync;
+ ovs_db_result_cb_t call;
+};
+typedef struct ovs_result_cb_s ovs_result_cb_t;
+
+/* Table callback declaration */
+struct ovs_table_cb_s {
+ ovs_db_table_cb_t call;
+};
+typedef struct ovs_table_cb_s ovs_table_cb_t;
+
+/* Callback declaration */
+struct ovs_callback_s {
+ uint64_t uid;
+ union {
+ ovs_result_cb_t result;
+ ovs_table_cb_t table;
+ };
+ struct ovs_callback_s *next;
+ struct ovs_callback_s *prev;
+};
+typedef struct ovs_callback_s ovs_callback_t;
+
+/* Event thread data declaration */
+struct ovs_event_thread_s {
+ pthread_t tid;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int value;
+};
+typedef struct ovs_event_thread_s ovs_event_thread_t;
+
+/* Poll thread data declaration */
+struct ovs_poll_thread_s {
+ pthread_t tid;
+ pthread_mutex_t mutex;
+ int state;
+};
+typedef struct ovs_poll_thread_s ovs_poll_thread_t;
+
+/* OVS DB internal data declaration */
+struct ovs_db_s {
+ ovs_poll_thread_t poll_thread;
+ ovs_event_thread_t event_thread;
+ pthread_mutex_t mutex;
+ ovs_callback_t *remote_cb;
+ ovs_db_callback_t cb;
+ char service[OVS_DB_ADDR_SERVICE_SIZE];
+ char node[OVS_DB_ADDR_NODE_SIZE];
+ char unix_path[OVS_DB_ADDR_NODE_SIZE];
+ int sock;
+};
+
+/* Global variables */
+static uint64_t ovs_uid;
+static pthread_mutex_t ovs_uid_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Post an event to event thread.
+ * Possible events are:
+ * OVS_DB_EVENT_TERMINATE
+ * OVS_DB_EVENT_CONN_ESTABLISHED
+ * OVS_DB_EVENT_CONN_TERMINATED
+ */
+static void ovs_db_event_post(ovs_db_t *pdb, int event) {
+ pthread_mutex_lock(&pdb->event_thread.mutex);
+ pdb->event_thread.value = event;
+ pthread_mutex_unlock(&pdb->event_thread.mutex);
+ pthread_cond_signal(&pdb->event_thread.cond);
+}
+
+/* Check if POLL thread is still running. Returns
+ * 1 if running otherwise 0 is returned */
+static bool ovs_db_poll_is_running(ovs_db_t *pdb) {
+ int state = 0;
+ pthread_mutex_lock(&pdb->poll_thread.mutex);
+ state = pdb->poll_thread.state;
+ pthread_mutex_unlock(&pdb->poll_thread.mutex);
+ return state == OVS_DB_POLL_STATE_RUNNING;
+}
+
+/* Generate unique identifier (UID). It is used by OVS DB API
+ * to set "id" field for any OVS DB JSON request. */
+static uint64_t ovs_uid_generate() {
+ uint64_t new_uid;
+ pthread_mutex_lock(&ovs_uid_mutex);
+ new_uid = ++ovs_uid;
+ pthread_mutex_unlock(&ovs_uid_mutex);
+ return new_uid;
+}
+
+/*
+ * Callback API. These function are used to store
+ * registered callbacks in OVS DB API.
+ */
+
+/* Add new callback into OVS DB object */
+static void ovs_db_callback_add(ovs_db_t *pdb, ovs_callback_t *new_cb) {
+ pthread_mutex_lock(&pdb->mutex);
+ if (pdb->remote_cb)
+ pdb->remote_cb->prev = new_cb;
+ new_cb->next = pdb->remote_cb;
+ new_cb->prev = NULL;
+ pdb->remote_cb = new_cb;
+ pthread_mutex_unlock(&pdb->mutex);
+}
+
+/* Remove callback from OVS DB object */
+static void ovs_db_callback_remove(ovs_db_t *pdb, ovs_callback_t *del_cb) {
+ pthread_mutex_lock(&pdb->mutex);
+ ovs_callback_t *pre_cb = del_cb->prev;
+ ovs_callback_t *next_cb = del_cb->next;
+
+ if (next_cb)
+ next_cb->prev = del_cb->prev;
+
+ if (pre_cb)
+ pre_cb->next = del_cb->next;
+ else
+ pdb->remote_cb = del_cb->next;
+
+ free(del_cb);
+ pthread_mutex_unlock(&pdb->mutex);
+}
+
+/* Remove all callbacks form OVS DB object */
+static void ovs_db_callback_remove_all(ovs_db_t *pdb) {
+ pthread_mutex_lock(&pdb->mutex);
+ while (pdb->remote_cb != NULL) {
+ ovs_callback_t *del_cb = pdb->remote_cb;
+ pdb->remote_cb = del_cb->next;
+ sfree(del_cb);
+ }
+ pthread_mutex_unlock(&pdb->mutex);
+}
+
+/* Get/find callback in OVS DB object by UID. Returns pointer
+ * to requested callback otherwise NULL is returned.
+ *
+ * IMPORTANT NOTE:
+ * The OVS DB mutex MUST be locked by the caller
+ * to make sure that returned callback is still valid.
+ */
+static ovs_callback_t *ovs_db_callback_get(ovs_db_t *pdb, uint64_t uid) {
+ for (ovs_callback_t *cb = pdb->remote_cb; cb != NULL; cb = cb->next)
+ if (cb->uid == uid)
+ return cb;
+ return NULL;
+}
+
+/* Send all requested data to the socket. Returns 0 if
+ * ALL request data has been sent otherwise negative value
+ * is returned */
+static int ovs_db_data_send(const ovs_db_t *pdb, const char *data, size_t len) {
+ ssize_t nbytes = 0;
+ size_t rem = len;
+ size_t off = 0;
+
+ while (rem > 0) {
+ if ((nbytes = send(pdb->sock, data + off, rem, 0)) <= 0)
+ return -1;
+ rem -= (size_t)nbytes;
+ off += (size_t)nbytes;
+ }
+ return 0;
+}
+
+/*
+ * YAJL (Yet Another JSON Library) helper functions
+ * Documentation (https://lloyd.github.io/yajl/)
+ */
+
+/* Add null-terminated string into YAJL generator handle (JSON object).
+ * Similar function to yajl_gen_string() but takes null-terminated string
+ * instead of string and its length.
+ *
+ * jgen - YAJL generator handle allocated by yajl_gen_alloc()
+ * string - Null-terminated string
+ */
+static yajl_gen_status ovs_yajl_gen_tstring(yajl_gen hander,
+ const char *string) {
+ return yajl_gen_string(hander, (const unsigned char *)string, strlen(string));
+}
+
+/* Add YAJL value into YAJL generator handle (JSON object)
+ *
+ * jgen - YAJL generator handle allocated by yajl_gen_alloc()
+ * jval - YAJL value usually returned by yajl_tree_get()
+ */
+static yajl_gen_status ovs_yajl_gen_val(yajl_gen jgen, yajl_val jval) {
+ size_t array_len = 0;
+ yajl_val *jvalues = NULL;
+ yajl_val jobj_value = NULL;
+ const char *obj_key = NULL;
+ size_t obj_len = 0;
+ yajl_gen_status yajl_gen_ret = yajl_gen_status_ok;
+
+ if (jval == NULL)
+ return yajl_gen_generation_complete;
+
+ if (YAJL_IS_STRING(jval))
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, YAJL_GET_STRING(jval));
+ else if (YAJL_IS_DOUBLE(jval))
+ OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_DOUBLE(jval));
+ else if (YAJL_IS_INTEGER(jval))
+ OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_INTEGER(jval));
+ else if (YAJL_IS_TRUE(jval))
+ OVS_YAJL_CALL(yajl_gen_bool, jgen, 1);
+ else if (YAJL_IS_FALSE(jval))
+ OVS_YAJL_CALL(yajl_gen_bool, jgen, 0);
+ else if (YAJL_IS_NULL(jval))
+ OVS_YAJL_CALL(yajl_gen_null, jgen);
+ else if (YAJL_IS_ARRAY(jval)) {
+ /* create new array and add all elements into the array */
+ array_len = YAJL_GET_ARRAY(jval)->len;
+ jvalues = YAJL_GET_ARRAY(jval)->values;
+ OVS_YAJL_CALL(yajl_gen_array_open, jgen);
+ for (size_t i = 0; i < array_len; i++)
+ OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jvalues[i]);
+ OVS_YAJL_CALL(yajl_gen_array_close, jgen);
+ } else if (YAJL_IS_OBJECT(jval)) {
+ /* create new object and add all elements into the object */
+ OVS_YAJL_CALL(yajl_gen_map_open, jgen);
+ obj_len = YAJL_GET_OBJECT(jval)->len;
+ for (size_t i = 0; i < obj_len; i++) {
+ obj_key = YAJL_GET_OBJECT(jval)->keys[i];
+ jobj_value = YAJL_GET_OBJECT(jval)->values[i];
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, obj_key);
+ OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jobj_value);
+ }
+ OVS_YAJL_CALL(yajl_gen_map_close, jgen);
+ } else {
+ OVS_ERROR("%s() unsupported value type %d (skip)", __FUNCTION__,
+ (int)(jval)->type);
+ goto yajl_gen_failure;
+ }
+ return yajl_gen_status_ok;
+
+yajl_gen_failure:
+ OVS_ERROR("%s() error to generate value", __FUNCTION__);
+ return yajl_gen_ret;
+}
+
+/* OVS DB echo request handler. When OVS DB sends
+ * "echo" request to the client, client should generate
+ * "echo" replay with the same content received in the
+ * request */
+static int ovs_db_table_echo_cb(const ovs_db_t *pdb, yajl_val jnode) {
+ yajl_val jparams;
+ yajl_val jid;
+ yajl_gen jgen;
+ size_t resp_len = 0;
+ const char *resp = NULL;
+ const char *params_path[] = {"params", NULL};
+ const char *id_path[] = {"id", NULL};
+ yajl_gen_status yajl_gen_ret;
+
+ if ((jgen = yajl_gen_alloc(NULL)) == NULL)
+ return -1;
+
+ /* check & get request attributes */
+ if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL ||
+ ((jid = yajl_tree_get(jnode, id_path, yajl_t_any)) == NULL)) {
+ OVS_ERROR("parse echo request failed");
+ goto yajl_gen_failure;
+ }
+
+ /* generate JSON echo response */
+ OVS_YAJL_CALL(yajl_gen_map_open, jgen);
+
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "result");
+ OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams);
+
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "error");
+ OVS_YAJL_CALL(yajl_gen_null, jgen);
+
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id");
+ OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jid);
+
+ OVS_YAJL_CALL(yajl_gen_map_close, jgen);
+ OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&resp,
+ &resp_len);
+
+ /* send the response */
+ OVS_DEBUG("response: %s", resp);
+ if (ovs_db_data_send(pdb, resp, resp_len) < 0) {
+ OVS_ERROR("send echo reply failed");
+ goto yajl_gen_failure;
+ }
+ /* clean up and return success */
+ yajl_gen_clear(jgen);
+ return 0;
+
+yajl_gen_failure:
+ /* release memory */
+ yajl_gen_clear(jgen);
+ return -1;
+}
+
+/* Get OVS DB registered callback by YAJL val. The YAJL
+ * value should be YAJL string (UID). Returns NULL if
+ * callback hasn't been found. See also ovs_db_callback_get()
+ * description for addition info.
+ */
+static ovs_callback_t *ovs_db_table_callback_get(ovs_db_t *pdb, yajl_val jid) {
+ char *endptr = NULL;
+ const char *suid = NULL;
+ uint64_t uid;
+
+ if (jid && YAJL_IS_STRING(jid)) {
+ suid = YAJL_GET_STRING(jid);
+ uid = (uint64_t)strtoul(suid, &endptr, 16);
+ if (*endptr == '\0' && uid)
+ return ovs_db_callback_get(pdb, uid);
+ }
+
+ return NULL;
+}
+
+/* OVS DB table update event handler.
+ * This callback is called by POLL thread if OVS DB
+ * table update callback is received from the DB
+ * server. Once registered callback found, it's called
+ * by this handler. */
+static int ovs_db_table_update_cb(ovs_db_t *pdb, yajl_val jnode) {
+ ovs_callback_t *cb = NULL;
+ yajl_val jvalue;
+ yajl_val jparams;
+ yajl_val jtable_updates;
+ const char *params_path[] = {"params", NULL};
+ const char *id_path[] = {"id", NULL};
+
+ /* check & get request attributes */
+ if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL ||
+ (yajl_tree_get(jnode, id_path, yajl_t_null) == NULL)) {
+ OVS_ERROR("invalid OVS DB request received");
+ return -1;
+ }
+
+ /* check array length: [<json-value>, <table-updates>] */
+ if ((YAJL_GET_ARRAY(jparams) == NULL) ||
+ (YAJL_GET_ARRAY(jparams)->len != 2)) {
+ OVS_ERROR("invalid OVS DB request received");
+ return -1;
+ }
+
+ jvalue = YAJL_GET_ARRAY(jparams)->values[0];
+ jtable_updates = YAJL_GET_ARRAY(jparams)->values[1];
+ if ((!YAJL_IS_OBJECT(jtable_updates)) || (!YAJL_IS_STRING(jvalue))) {
+ OVS_ERROR("invalid OVS DB request id or table update received");
+ return -1;
+ }
+
+ /* find registered callback based on <json-value> */
+ pthread_mutex_lock(&pdb->mutex);
+ cb = ovs_db_table_callback_get(pdb, jvalue);
+ if (cb == NULL || cb->table.call == NULL) {
+ OVS_ERROR("No OVS DB table update callback found");
+ pthread_mutex_unlock(&pdb->mutex);
+ return -1;
+ }
+
+ /* call registered callback */
+ cb->table.call(jtable_updates);
+ pthread_mutex_unlock(&pdb->mutex);
+ return 0;
+}
+
+/* OVS DB result request handler.
+ * This callback is called by POLL thread if OVS DB
+ * result reply is received from the DB server.
+ * Once registered callback found, it's called
+ * by this handler. */
+static int ovs_db_result_cb(ovs_db_t *pdb, yajl_val jnode) {
+ ovs_callback_t *cb = NULL;
+ yajl_val jresult;
+ yajl_val jerror;
+ yajl_val jid;
+ const char *result_path[] = {"result", NULL};
+ const char *error_path[] = {"error", NULL};
+ const char *id_path[] = {"id", NULL};
+
+ jresult = yajl_tree_get(jnode, result_path, yajl_t_any);
+ jerror = yajl_tree_get(jnode, error_path, yajl_t_any);
+ jid = yajl_tree_get(jnode, id_path, yajl_t_string);
+
+ /* check & get result attributes */
+ if (!jresult || !jerror || !jid)
+ return -1;
+
+ /* try to find registered callback */
+ pthread_mutex_lock(&pdb->mutex);
+ cb = ovs_db_table_callback_get(pdb, jid);
+ if (cb != NULL && cb->result.call != NULL) {
+ /* call registered callback */
+ cb->result.call(jresult, jerror);
+ /* unlock owner of the reply */
+ sem_post(&cb->result.sync);
+ }
+
+ pthread_mutex_unlock(&pdb->mutex);
+ return 0;
+}
+
+/* Handle JSON data (one request) and call
+ * appropriate event OVS DB handler. Currently,
+ * update callback 'ovs_db_table_update_cb' and
+ * result callback 'ovs_db_result_cb' is supported.
+ */
+static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data,
+ size_t len) {
+ const char *method = NULL;
+ char yajl_errbuf[OVS_YAJL_ERROR_BUFFER_SIZE];
+ const char *method_path[] = {"method", NULL};
+ const char *result_path[] = {"result", NULL};
+ char *sjson = NULL;
+ yajl_val jnode, jval;
+
+ /* duplicate the data to make null-terminated string
+ * required for yajl_tree_parse() */
+ if ((sjson = calloc(1, len + 1)) == NULL)
+ return -1;
+
+ sstrncpy(sjson, data, len + 1);
+ OVS_DEBUG("[len=%" PRIsz "] %s", len, sjson);
+
+ /* parse json data */
+ jnode = yajl_tree_parse(sjson, yajl_errbuf, sizeof(yajl_errbuf));
+ if (jnode == NULL) {
+ OVS_ERROR("yajl_tree_parse() %s", yajl_errbuf);
+ sfree(sjson);
+ return -1;
+ }
+
+ /* get method name */
+ if ((jval = yajl_tree_get(jnode, method_path, yajl_t_string)) != NULL) {
+ if ((method = YAJL_GET_STRING(jval)) == NULL) {
+ yajl_tree_free(jnode);
+ sfree(sjson);
+ return -1;
+ }
+ if (strcmp("echo", method) == 0) {
+ /* echo request from the server */
+ if (ovs_db_table_echo_cb(pdb, jnode) < 0)
+ OVS_ERROR("handle echo request failed");
+ } else if (strcmp("update", method) == 0) {
+ /* update notification */
+ if (ovs_db_table_update_cb(pdb, jnode) < 0)
+ OVS_ERROR("handle update notification failed");
+ }
+ } else if ((jval = yajl_tree_get(jnode, result_path, yajl_t_any)) != NULL) {
+ /* result notification */
+ if (ovs_db_result_cb(pdb, jnode) < 0)
+ OVS_ERROR("handle result reply failed");
+ } else
+ OVS_ERROR("connot find method or result failed");
+
+ /* release memory */
+ yajl_tree_free(jnode);
+ sfree(sjson);
+ return 0;
+}
+
+/*
+ * JSON reader implementation.
+ *
+ * This module process raw JSON data (byte stream) and
+ * returns fully-fledged JSON data which can be processed
+ * (parsed) by YAJL later.
+ */
+
+/* Allocate JSON reader instance */
+static ovs_json_reader_t *ovs_json_reader_alloc() {
+ ovs_json_reader_t *jreader = calloc(1, sizeof(*jreader));
+ if (jreader == NULL)
+ return NULL;
+
+ return jreader;
+}
+
+/* Push raw data into into the JSON reader for processing */
+static int ovs_json_reader_push_data(ovs_json_reader_t *jreader,
+ const char *data, size_t data_len) {
+ char *new_buff = NULL;
+ size_t available = jreader->buff_size - jreader->buff_offset;
+
+ /* check/update required memory space */
+ if (available < data_len) {
+ OVS_DEBUG("Reallocate buffer [size=%d, available=%d required=%d]",
+ (int)jreader->buff_size, (int)available, (int)data_len);
+
+ /* allocate new chunk of memory */
+ new_buff = realloc(jreader->buff_ptr, (jreader->buff_size + data_len));
+ if (new_buff == NULL)
+ return -1;
+
+ /* point to new allocated memory */
+ jreader->buff_ptr = new_buff;
+ jreader->buff_size += data_len;
+ }
+
+ /* store input data */
+ memcpy(jreader->buff_ptr + jreader->buff_offset, data, data_len);
+ jreader->buff_offset += data_len;
+ return 0;
+}
+
+/* Pop one fully-fledged JSON if already exists. Returns 0 if
+ * completed JSON already exists otherwise negative value is
+ * returned */
+static int ovs_json_reader_pop(ovs_json_reader_t *jreader,
+ const char **json_ptr, size_t *json_len_ptr) {
+ size_t nbraces = 0;
+ size_t json_len = 0;
+ char *json = NULL;
+
+ /* search open/close brace */
+ for (size_t i = jreader->json_offset; i < jreader->buff_offset; i++) {
+ if (jreader->buff_ptr[i] == '{') {
+ nbraces++;
+ } else if (jreader->buff_ptr[i] == '}')
+ if (nbraces)
+ if (!(--nbraces)) {
+ /* JSON data */
+ *json_ptr = jreader->buff_ptr + jreader->json_offset;
+ *json_len_ptr = json_len + 1;
+ jreader->json_offset = i + 1;
+ return 0;
+ }
+
+ /* increase JSON data length */
+ if (nbraces)
+ json_len++;
+ }
+
+ if (jreader->json_offset) {
+ if (jreader->json_offset < jreader->buff_offset) {
+ /* shift data to the beginning of the buffer
+ * and zero rest of the buffer data */
+ json = &jreader->buff_ptr[jreader->json_offset];
+ json_len = jreader->buff_offset - jreader->json_offset;
+ for (size_t i = 0; i < jreader->buff_size; i++)
+ jreader->buff_ptr[i] = ((i < json_len) ? (json[i]) : (0));
+ jreader->buff_offset = json_len;
+ } else
+ /* reset the buffer */
+ jreader->buff_offset = 0;
+
+ /* data is at the beginning of the buffer */
+ jreader->json_offset = 0;
+ }
+
+ return -1;
+}
+
+/* Reset JSON reader. It is useful when start processing
+ * new raw data. E.g.: in case of lost stream connection.
+ */
+static void ovs_json_reader_reset(ovs_json_reader_t *jreader) {
+ if (jreader) {
+ jreader->buff_offset = 0;
+ jreader->json_offset = 0;
+ }
+}
+
+/* Release internal data allocated for JSON reader */
+static void ovs_json_reader_free(ovs_json_reader_t *jreader) {
+ if (jreader) {
+ free(jreader->buff_ptr);
+ free(jreader);
+ }
+}
+
+/* Reconnect to OVS DB and call the OVS DB post connection init callback
+ * if connection has been established.
+ */
+static void ovs_db_reconnect(ovs_db_t *pdb) {
+ const char *node_info = pdb->node;
+ struct addrinfo *result;
+
+ if (pdb->unix_path[0] != '\0') {
+ /* use UNIX socket instead of INET address */
+ node_info = pdb->unix_path;
+
+ struct sockaddr_un *sa_unix = calloc(1, sizeof(*sa_unix));
+ if (sa_unix == NULL)
+ return;
+
+ result = calloc(1, sizeof(*result));
+ if (result == NULL) {
+ free(sa_unix);
+ return;
+ }
+
+ result->ai_family = AF_UNIX;
+ result->ai_socktype = SOCK_STREAM;
+ result->ai_addrlen = sizeof(*sa_unix);
+ result->ai_addr = (struct sockaddr *)sa_unix;
+ sa_unix->sun_family = result->ai_family;
+ sstrncpy(sa_unix->sun_path, pdb->unix_path, sizeof(sa_unix->sun_path));
+ } else {
+ /* inet socket address */
+ struct addrinfo hints;
+
+ /* setup criteria for selecting the socket address */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* get socket addresses */
+ int ret = getaddrinfo(pdb->node, pdb->service, &hints, &result);
+ if (ret != 0) {
+ OVS_ERROR("getaddrinfo(): %s", gai_strerror(ret));
+ return;
+ }
+ }
+ /* try to connect to the server */
+ for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) {
+ int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (sock < 0) {
+ OVS_DEBUG("socket(): %s", STRERRNO);
+ continue;
+ }
+ if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) {
+ close(sock);
+ OVS_DEBUG("connect(): %s [family=%d]", STRERRNO, rp->ai_family);
+ } else {
+ /* send notification to event thread */
+ pdb->sock = sock;
+ ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_ESTABLISHED);
+ break;
+ }
+ }
+
+ if (pdb->sock < 0)
+ OVS_ERROR("connect to \"%s\" failed", node_info);
+
+ freeaddrinfo(result);
+}
+
+/* POLL worker thread.
+ * It listens on OVS DB connection for incoming
+ * requests/reply/events etc. Also, it reconnects to OVS DB
+ * if connection has been lost.
+ */
+static void *ovs_poll_worker(void *arg) {
+ ovs_db_t *pdb = (ovs_db_t *)arg; /* pointer to OVS DB */
+ ovs_json_reader_t *jreader = NULL;
+ struct pollfd poll_fd = {
+ .fd = pdb->sock, .events = POLLIN | POLLPRI, .revents = 0,
+ };
+
+ /* create JSON reader instance */
+ if ((jreader = ovs_json_reader_alloc()) == NULL) {
+ OVS_ERROR("initialize json reader failed");
+ return NULL;
+ }
+
+ /* poll data */
+ while (ovs_db_poll_is_running(pdb)) {
+ poll_fd.fd = pdb->sock;
+ int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000);
+ if (poll_ret < 0) {
+ OVS_ERROR("poll(): %s", STRERRNO);
+ break;
+ } else if (poll_ret == 0) {
+ OVS_DEBUG("poll(): timeout");
+ if (pdb->sock < 0)
+ /* invalid fd, so try to reconnect */
+ ovs_db_reconnect(pdb);
+ continue;
+ }
+ if (poll_fd.revents & POLLNVAL) {
+ /* invalid file descriptor, clean-up */
+ ovs_db_callback_remove_all(pdb);
+ ovs_json_reader_reset(jreader);
+ /* setting poll FD to -1 tells poll() call to ignore this FD.
+ * In that case poll() call will return timeout all the time */
+ pdb->sock = (-1);
+ } else if ((poll_fd.revents & POLLERR) || (poll_fd.revents & POLLHUP)) {
+ /* connection is broken */
+ close(poll_fd.fd);
+ ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED);
+ OVS_ERROR("poll() peer closed its end of the channel");
+ } else if ((poll_fd.revents & POLLIN) || (poll_fd.revents & POLLPRI)) {
+ /* read incoming data */
+ char buff[OVS_DB_POLL_READ_BLOCK_SIZE];
+ ssize_t nbytes = recv(poll_fd.fd, buff, sizeof(buff), 0);
+ if (nbytes < 0) {
+ OVS_ERROR("recv(): %s", STRERRNO);
+ /* read error? Try to reconnect */
+ close(poll_fd.fd);
+ continue;
+ } else if (nbytes == 0) {
+ close(poll_fd.fd);
+ ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED);
+ OVS_ERROR("recv() peer has performed an orderly shutdown");
+ continue;
+ }
+ /* read incoming data */
+ size_t json_len = 0;
+ const char *json = NULL;
+ OVS_DEBUG("recv(): received %zd bytes of data", nbytes);
+ ovs_json_reader_push_data(jreader, buff, nbytes);
+ while (!ovs_json_reader_pop(jreader, &json, &json_len))
+ /* process JSON data */
+ ovs_db_json_data_process(pdb, json, json_len);
+ }
+ }
+
+ OVS_DEBUG("poll thread has been completed");
+ ovs_json_reader_free(jreader);
+ return NULL;
+}
+
+/* EVENT worker thread.
+ * Perform task based on incoming events. This
+ * task can be done asynchronously which allows to
+ * handle OVS DB callback like 'init_cb'.
+ */
+static void *ovs_event_worker(void *arg) {
+ ovs_db_t *pdb = (ovs_db_t *)arg;
+
+ while (pdb->event_thread.value != OVS_DB_EVENT_TERMINATE) {
+ /* wait for an event */
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += (OVS_DB_EVENT_TIMEOUT);
+ int ret = pthread_cond_timedwait(&pdb->event_thread.cond,
+ &pdb->event_thread.mutex, &ts);
+ if (!ret || ret == ETIMEDOUT) {
+ /* handle the event */
+ OVS_DEBUG("handle event %d", pdb->event_thread.value);
+ switch (pdb->event_thread.value) {
+ case OVS_DB_EVENT_CONN_ESTABLISHED:
+ if (pdb->cb.post_conn_init)
+ pdb->cb.post_conn_init(pdb);
+ /* reset event */
+ pdb->event_thread.value = OVS_DB_EVENT_NONE;
+ break;
+ case OVS_DB_EVENT_CONN_TERMINATED:
+ if (pdb->cb.post_conn_terminate)
+ pdb->cb.post_conn_terminate();
+ /* reset event */
+ pdb->event_thread.value = OVS_DB_EVENT_NONE;
+ break;
+ case OVS_DB_EVENT_NONE:
+ /* wait timeout */
+ OVS_DEBUG("no event received (timeout)");
+ break;
+ default:
+ OVS_DEBUG("unknown event received");
+ break;
+ }
+ } else {
+ /* unexpected error */
+ OVS_ERROR("pthread_cond_timedwait() failed");
+ break;
+ }
+ }
+
+ OVS_DEBUG("event thread has been completed");
+ return NULL;
+}
+
+/* Initialize EVENT thread */
+static int ovs_db_event_thread_init(ovs_db_t *pdb) {
+ pdb->event_thread.tid = (pthread_t){0};
+ /* init event thread condition variable */
+ if (pthread_cond_init(&pdb->event_thread.cond, NULL)) {
+ return -1;
+ }
+ /* init event thread mutex */
+ if (pthread_mutex_init(&pdb->event_thread.mutex, NULL)) {
+ pthread_cond_destroy(&pdb->event_thread.cond);
+ return -1;
+ }
+ /* Hold the event thread mutex. It ensures that no events
+ * will be lost while thread is still starting. Once event
+ * thread is started and ready to accept events, it will release
+ * the mutex */
+ if (pthread_mutex_lock(&pdb->event_thread.mutex)) {
+ pthread_mutex_destroy(&pdb->event_thread.mutex);
+ pthread_cond_destroy(&pdb->event_thread.cond);
+ return -1;
+ }
+ /* start event thread */
+ pthread_t tid;
+ if (plugin_thread_create(&tid, NULL, ovs_event_worker, pdb,
+ "utils_ovs:event") != 0) {
+ pthread_mutex_unlock(&pdb->event_thread.mutex);
+ pthread_mutex_destroy(&pdb->event_thread.mutex);
+ pthread_cond_destroy(&pdb->event_thread.cond);
+ return -1;
+ }
+ pdb->event_thread.tid = tid;
+ return 0;
+}
+
+/* Terminate EVENT thread */
+static int ovs_db_event_thread_terminate(ovs_db_t *pdb) {
+ if (pthread_equal(pdb->event_thread.tid, (pthread_t){0})) {
+ /* already terminated */
+ return 0;
+ }
+ ovs_db_event_post(pdb, OVS_DB_EVENT_TERMINATE);
+ if (pthread_join(pdb->event_thread.tid, NULL) != 0)
+ return -1;
+ /* Event thread always holds the thread mutex when
+ * performs some task (handles event) and releases it when
+ * while sleeping. Thus, if event thread exits, the mutex
+ * remains locked */
+ pdb->event_thread.tid = (pthread_t){0};
+ pthread_mutex_unlock(&pdb->event_thread.mutex);
+ return 0;
+}
+
+/* Destroy EVENT thread private data */
+static void ovs_db_event_thread_data_destroy(ovs_db_t *pdb) {
+ /* destroy mutex */
+ pthread_mutex_destroy(&pdb->event_thread.mutex);
+ pthread_cond_destroy(&pdb->event_thread.cond);
+}
+
+/* Initialize POLL thread */
+static int ovs_db_poll_thread_init(ovs_db_t *pdb) {
+ pdb->poll_thread.tid = (pthread_t){0};
+ /* init event thread mutex */
+ if (pthread_mutex_init(&pdb->poll_thread.mutex, NULL)) {
+ return -1;
+ }
+ /* start poll thread */
+ pthread_t tid;
+ pdb->poll_thread.state = OVS_DB_POLL_STATE_RUNNING;
+ if (plugin_thread_create(&tid, NULL, ovs_poll_worker, pdb,
+ "utils_ovs:poll") != 0) {
+ pthread_mutex_destroy(&pdb->poll_thread.mutex);
+ return -1;
+ }
+ pdb->poll_thread.tid = tid;
+ return 0;
+}
+
+/* Destroy POLL thread */
+/* XXX: Must hold pdb->mutex when calling! */
+static int ovs_db_poll_thread_destroy(ovs_db_t *pdb) {
+ if (pthread_equal(pdb->poll_thread.tid, (pthread_t){0})) {
+ /* already destroyed */
+ return 0;
+ }
+ /* change thread state */
+ pthread_mutex_lock(&pdb->poll_thread.mutex);
+ pdb->poll_thread.state = OVS_DB_POLL_STATE_EXITING;
+ pthread_mutex_unlock(&pdb->poll_thread.mutex);
+ /* join the thread */
+ if (pthread_join(pdb->poll_thread.tid, NULL) != 0)
+ return -1;
+ pthread_mutex_destroy(&pdb->poll_thread.mutex);
+ pdb->poll_thread.tid = (pthread_t){0};
+ return 0;
+}
+
+/*
+ * Public OVS DB API implementation
+ */
+
+ovs_db_t *ovs_db_init(const char *node, const char *service,
+ const char *unix_path, ovs_db_callback_t *cb) {
+ int ret;
+
+ /* sanity check */
+ if (node == NULL || service == NULL || unix_path == NULL)
+ return NULL;
+
+ /* allocate db data & fill it */
+ ovs_db_t *pdb = calloc(1, sizeof(*pdb));
+ if (pdb == NULL)
+ return NULL;
+ pdb->sock = -1;
+
+ /* store the OVS DB address */
+ sstrncpy(pdb->node, node, sizeof(pdb->node));
+ sstrncpy(pdb->service, service, sizeof(pdb->service));
+ sstrncpy(pdb->unix_path, unix_path, sizeof(pdb->unix_path));
+
+ /* setup OVS DB callbacks */
+ if (cb)
+ pdb->cb = *cb;
+
+ /* init OVS DB mutex attributes */
+ pthread_mutexattr_t mutex_attr;
+ if (pthread_mutexattr_init(&mutex_attr)) {
+ OVS_ERROR("OVS DB mutex attribute init failed");
+ sfree(pdb);
+ return NULL;
+ }
+ /* set OVS DB mutex as recursive */
+ if (pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE)) {
+ OVS_ERROR("Failed to set OVS DB mutex as recursive");
+ pthread_mutexattr_destroy(&mutex_attr);
+ sfree(pdb);
+ return NULL;
+ }
+ /* init OVS DB mutex */
+ if (pthread_mutex_init(&pdb->mutex, &mutex_attr)) {
+ OVS_ERROR("OVS DB mutex init failed");
+ pthread_mutexattr_destroy(&mutex_attr);
+ sfree(pdb);
+ return NULL;
+ }
+ /* destroy mutex attributes */
+ pthread_mutexattr_destroy(&mutex_attr);
+
+ /* init event thread */
+ if (ovs_db_event_thread_init(pdb) < 0) {
+ ret = ovs_db_destroy(pdb);
+ if (ret > 0)
+ goto failure;
+ else
+ return NULL;
+ }
+
+ /* init polling thread */
+ if (ovs_db_poll_thread_init(pdb) < 0) {
+ ret = ovs_db_destroy(pdb);
+ if (ret > 0) {
+ ovs_db_event_thread_data_destroy(pdb);
+ goto failure;
+ } else {
+ return NULL;
+ }
+ }
+ return pdb;
+
+failure:
+ pthread_mutex_destroy(&pdb->mutex);
+ sfree(pdb);
+ return NULL;
+}
+
+int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params,
+ ovs_db_result_cb_t cb) {
+ int ret = 0;
+ yajl_gen_status yajl_gen_ret;
+ yajl_val jparams;
+ yajl_gen jgen;
+ ovs_callback_t *new_cb = NULL;
+ uint64_t uid;
+ char uid_buff[OVS_UID_STR_SIZE];
+ const char *req = NULL;
+ size_t req_len = 0;
+ struct timespec ts;
+
+ /* sanity check */
+ if (!pdb || !method || !params)
+ return -1;
+
+ if ((jgen = yajl_gen_alloc(NULL)) == NULL)
+ return -1;
+
+ /* try to parse params */
+ if ((jparams = yajl_tree_parse(params, NULL, 0)) == NULL) {
+ OVS_ERROR("params is not a JSON string");
+ yajl_gen_clear(jgen);
+ return -1;
+ }
+
+ /* generate method field */
+ OVS_YAJL_CALL(yajl_gen_map_open, jgen);
+
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "method");
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, method);
+
+ /* generate params field */
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "params");
+ OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams);
+ yajl_tree_free(jparams);
+
+ /* generate id field */
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id");
+ uid = ovs_uid_generate();
+ snprintf(uid_buff, sizeof(uid_buff), "%" PRIX64, uid);
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_buff);
+
+ OVS_YAJL_CALL(yajl_gen_map_close, jgen);
+
+ if (cb) {
+ /* register result callback */
+ if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL)
+ goto yajl_gen_failure;
+
+ /* add new callback to front */
+ sem_init(&new_cb->result.sync, 0, 0);
+ new_cb->result.call = cb;
+ new_cb->uid = uid;
+ ovs_db_callback_add(pdb, new_cb);
+ }
+
+ /* send the request */
+ OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&req, &req_len);
+ OVS_DEBUG("%s", req);
+ if (!ovs_db_data_send(pdb, req, req_len)) {
+ if (cb) {
+ /* wait for result */
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += OVS_DB_SEND_REQ_TIMEOUT;
+ if (sem_timedwait(&new_cb->result.sync, &ts) < 0) {
+ OVS_ERROR("%s() no replay received within %d sec", __FUNCTION__,
+ OVS_DB_SEND_REQ_TIMEOUT);
+ ret = (-1);
+ }
+ }
+ } else {
+ OVS_ERROR("ovs_db_data_send() failed");
+ ret = (-1);
+ }
+
+yajl_gen_failure:
+ if (new_cb) {
+ /* destroy callback */
+ sem_destroy(&new_cb->result.sync);
+ ovs_db_callback_remove(pdb, new_cb);
+ }
+
+ /* release memory */
+ yajl_gen_clear(jgen);
+ return (yajl_gen_ret != yajl_gen_status_ok) ? (-1) : ret;
+}
+
+int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name,
+ const char **tb_column,
+ ovs_db_table_cb_t update_cb,
+ ovs_db_result_cb_t result_cb, unsigned int flags) {
+ yajl_gen jgen;
+ yajl_gen_status yajl_gen_ret;
+ ovs_callback_t *new_cb = NULL;
+ char uid_str[OVS_UID_STR_SIZE];
+ char *params;
+ size_t params_len;
+ int ovs_db_ret = 0;
+
+ /* sanity check */
+ if (pdb == NULL || tb_name == NULL || update_cb == NULL)
+ return -1;
+
+ /* allocate new update callback */
+ if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL)
+ return -1;
+
+ /* init YAJL generator */
+ if ((jgen = yajl_gen_alloc(NULL)) == NULL) {
+ sfree(new_cb);
+ return -1;
+ }
+
+ /* add new callback to front */
+ new_cb->table.call = update_cb;
+ new_cb->uid = ovs_uid_generate();
+ ovs_db_callback_add(pdb, new_cb);
+
+ /* make update notification request
+ * [<db-name>, <json-value>, <monitor-requests>] */
+ OVS_YAJL_CALL(yajl_gen_array_open, jgen);
+ {
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME);
+
+ /* uid string <json-value> */
+ snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid);
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str);
+
+ /* <monitor-requests> */
+ OVS_YAJL_CALL(yajl_gen_map_open, jgen);
+ {
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, tb_name);
+ OVS_YAJL_CALL(yajl_gen_array_open, jgen);
+ {
+ /* <monitor-request> */
+ OVS_YAJL_CALL(yajl_gen_map_open, jgen);
+ {
+ if (tb_column) {
+ /* columns within the table to be monitored */
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "columns");
+ OVS_YAJL_CALL(yajl_gen_array_open, jgen);
+ for (; *tb_column; tb_column++)
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, *tb_column);
+ OVS_YAJL_CALL(yajl_gen_array_close, jgen);
+ }
+ /* specify select option */
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "select");
+ {
+ OVS_YAJL_CALL(yajl_gen_map_open, jgen);
+ {
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "initial");
+ OVS_YAJL_CALL(yajl_gen_bool, jgen,
+ flags & OVS_DB_TABLE_CB_FLAG_INITIAL);
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "insert");
+ OVS_YAJL_CALL(yajl_gen_bool, jgen,
+ flags & OVS_DB_TABLE_CB_FLAG_INSERT);
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "delete");
+ OVS_YAJL_CALL(yajl_gen_bool, jgen,
+ flags & OVS_DB_TABLE_CB_FLAG_DELETE);
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "modify");
+ OVS_YAJL_CALL(yajl_gen_bool, jgen,
+ flags & OVS_DB_TABLE_CB_FLAG_MODIFY);
+ }
+ OVS_YAJL_CALL(yajl_gen_map_close, jgen);
+ }
+ }
+ OVS_YAJL_CALL(yajl_gen_map_close, jgen);
+ }
+ OVS_YAJL_CALL(yajl_gen_array_close, jgen);
+ }
+ OVS_YAJL_CALL(yajl_gen_map_close, jgen);
+ }
+ OVS_YAJL_CALL(yajl_gen_array_close, jgen);
+
+ /* make a request to subscribe to given table */
+ OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)¶ms,
+ ¶ms_len);
+ if (ovs_db_send_request(pdb, "monitor", params, result_cb) < 0) {
+ OVS_ERROR("Failed to subscribe to \"%s\" table", tb_name);
+ ovs_db_ret = (-1);
+ }
+
+yajl_gen_failure:
+ /* release memory */
+ yajl_gen_clear(jgen);
+ return ovs_db_ret;
+}
+
+int ovs_db_destroy(ovs_db_t *pdb) {
+ int ovs_db_ret = 0;
+ int ret = 0;
+
+ /* sanity check */
+ if (pdb == NULL)
+ return -1;
+
+ /* stop event thread */
+ if (ovs_db_event_thread_terminate(pdb) < 0) {
+ OVS_ERROR("stop event thread failed");
+ ovs_db_ret = -1;
+ }
+
+ /* try to lock the structure before releasing */
+ if ((ret = pthread_mutex_lock(&pdb->mutex))) {
+ OVS_ERROR("pthread_mutex_lock() DB mutex lock failed (%d)", ret);
+ return ret;
+ }
+
+ /* stop poll thread and destroy thread's private data */
+ if (ovs_db_poll_thread_destroy(pdb) < 0) {
+ OVS_ERROR("destroy poll thread failed");
+ ovs_db_ret = -1;
+ }
+
+ /* destroy event thread private data */
+ ovs_db_event_thread_data_destroy(pdb);
+
+ pthread_mutex_unlock(&pdb->mutex);
+
+ /* unsubscribe callbacks */
+ ovs_db_callback_remove_all(pdb);
+
+ /* close connection */
+ if (pdb->sock >= 0)
+ close(pdb->sock);
+
+ /* release DB handler */
+ pthread_mutex_destroy(&pdb->mutex);
+ sfree(pdb);
+ return ovs_db_ret;
+}
+
+/*
+ * Public OVS utils API implementation
+ */
+
+/* Get YAJL value by key from YAJL dictionary
+ *
+ * EXAMPLE:
+ * {
+ * "key_a" : <YAJL return value>
+ * "key_b" : <YAJL return value>
+ * }
+ */
+yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key) {
+ const char *obj_key = NULL;
+
+ /* check params */
+ if (!YAJL_IS_OBJECT(jval) || (key == NULL))
+ return NULL;
+
+ /* find a value by key */
+ for (size_t i = 0; i < YAJL_GET_OBJECT(jval)->len; i++) {
+ obj_key = YAJL_GET_OBJECT(jval)->keys[i];
+ if (strcmp(obj_key, key) == 0)
+ return YAJL_GET_OBJECT(jval)->values[i];
+ }
+
+ return NULL;
+}
+
+/* Get OVS DB map value by given map key
+ *
+ * FROM RFC7047:
+ *
+ * <pair>
+ * A 2-element JSON array that represents a pair within a database
+ * map. The first element is an <atom> that represents the key, and
+ * the second element is an <atom> that represents the value.
+ *
+ * <map>
+ * A 2-element JSON array that represents a database map value. The
+ * first element of the array must be the string "map", and the
+ * second element must be an array of zero or more <pair>s giving the
+ * values in the map. All of the <pair>s must have the same key and
+ * value types.
+ *
+ * EXAMPLE:
+ * [
+ * "map", [
+ * [ "key_a", <YAJL value>], [ "key_b", <YAJL value>], ...
+ * ]
+ * ]
+ */
+yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key) {
+ size_t map_len = 0;
+ size_t array_len = 0;
+ yajl_val *map_values = NULL;
+ yajl_val *array_values = NULL;
+ const char *str_val = NULL;
+
+ /* check YAJL array */
+ if (!YAJL_IS_ARRAY(jval) || (key == NULL))
+ return NULL;
+
+ /* check a database map value (2-element, first one should be a string */
+ array_len = YAJL_GET_ARRAY(jval)->len;
+ array_values = YAJL_GET_ARRAY(jval)->values;
+ if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0])) ||
+ (!YAJL_IS_ARRAY(array_values[1])))
+ return NULL;
+
+ /* check first element of the array */
+ str_val = YAJL_GET_STRING(array_values[0]);
+ if (str_val == NULL || strcmp("map", str_val) != 0)
+ return NULL;
+
+ /* try to find map value by map key */
+ if (YAJL_GET_ARRAY(array_values[1]) == NULL)
+ return NULL;
+
+ map_len = YAJL_GET_ARRAY(array_values[1])->len;
+ map_values = YAJL_GET_ARRAY(array_values[1])->values;
+ for (size_t i = 0; i < map_len; i++) {
+ /* check YAJL array */
+ if (!YAJL_IS_ARRAY(map_values[i]) || YAJL_GET_ARRAY(map_values[i]) == NULL)
+ break;
+
+ /* check a database pair value (2-element, first one represents a key
+ * and it should be a string in our case */
+ array_len = YAJL_GET_ARRAY(map_values[i])->len;
+ array_values = YAJL_GET_ARRAY(map_values[i])->values;
+ if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0])))
+ break;
+
+ /* return map value if given key equals map key */
+ str_val = YAJL_GET_STRING(array_values[0]);
+ if (str_val != NULL && strcmp(key, str_val) == 0)
+ return array_values[1];
+ }
+ return NULL;
+}
--- /dev/null
+/**
+ * collectd - src/utils_ovs.h
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
+ *
+ * Description:
+ * The OVS util module provides the following features:
+ * - Implements the OVS DB communication transport specified
+ * by RFC7047:
+ * * Connect/disconnect to OVS DB;
+ * * Recovery mechanism in case of OVS DB connection lost;
+ * * Subscription mechanism to OVS DB table update events
+ * (insert/modify/delete);
+ * * Send custom JSON request to OVS DB (poll table data, etc.)
+ * * Handling of echo request from OVS DB server to verify the
+ * liveness of the connection.
+ * - Provides YAJL helpers functions.
+ *
+ * OVS DB API User Guide:
+ * All OVS DB function/structure names begins from 'ovs_db_*' prefix. To
+ * start using OVS DB API, client (plugin) should initialize the OVS DB
+ * object (`ovs_db_t') by calling `ovs_db_init' function. It initializes
+ * internal data and creates two main workers (threads). The result of the
+ * function is a pointer to new OVS DB object which can be used by other
+ * OVS DB API later and must be released by `ovs_db_destroy' function if
+ * the object isn't needed anymore.
+ * Once OVS DB API is initialized, the `init_cb' callback is called if
+ * the connection to OVS DB has been established. This callback is called
+ * every time the OVS DB is reconnected. So, if the client registers table
+ * update event callbacks or does any other OVS DB setup that can be lost
+ * after OVS DB reconnecting, it should be done in `init_cb' callback.
+ * The `ovs_db_table_cb_register` function is used to register OVS DB
+ * table update event callback and receive the table update notification
+ * when requested event occurs (registered callback is called). See
+ * function API for more info.
+ * To send custom JSON-RPC request to OVS DB, the `ovs_db_send_request'
+ * function is used. Please note, that connection to OVS DB should be
+ * established otherwise the function will return error.
+ * To verify the liveness of established connection, the OVS DB server
+ * sends echo request to the client with a given interval. The OVS utils
+ * takes care about this request and handles it properly.
+ **/
+
+#ifndef UTILS_OVS_H
+#define UTILS_OVS_H
+
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_tree.h>
+
+/* Forward declaration */
+typedef struct ovs_db_s ovs_db_t;
+
+/* OVS DB callback type declaration */
+typedef void (*ovs_db_table_cb_t)(yajl_val jupdates);
+typedef void (*ovs_db_result_cb_t)(yajl_val jresult, yajl_val jerror);
+
+/* OVS DB structures */
+struct ovs_db_callback_s {
+ /*
+ * This callback is called when OVS DB connection
+ * has been established and ready to use. Client
+ * can use this callback to configure OVS DB, e.g.
+ * to subscribe to table update notification or poll
+ * some OVS DB data. This field can be NULL.
+ */
+ void (*post_conn_init)(ovs_db_t *pdb);
+ /*
+ * This callback is called when OVS DB connection
+ * has been lost. This field can be NULL.
+ */
+ void (*post_conn_terminate)(void);
+};
+typedef struct ovs_db_callback_s ovs_db_callback_t;
+
+/* OVS DB defines */
+#define OVS_DB_ADDR_NODE_SIZE 256
+#define OVS_DB_ADDR_SERVICE_SIZE 128
+#define OVS_DB_ADDR_UNIX_SIZE 108
+
+/* OVS DB prototypes */
+
+/*
+ * NAME
+ * ovs_db_init
+ *
+ * DESCRIPTION
+ * Initialize OVS DB internal data. The `ovs_db_destroy' function
+ * shall destroy the returned object.
+ *
+ * PARAMETERS
+ * `node' OVS DB Address.
+ * `service' OVS DB service name.
+ * `unix' OVS DB unix socket path.
+ * `cb' OVS DB callbacks.
+ *
+ * RETURN VALUE
+ * New ovs_db_t object upon success or NULL if an error occurred.
+ */
+ovs_db_t *ovs_db_init(const char *node, const char *service,
+ const char *unix_path, ovs_db_callback_t *cb);
+
+/*
+ * NAME
+ * ovs_db_destroy
+ *
+ * DESCRIPTION
+ * Destroy OVS DB object referenced by `pdb'.
+ *
+ * PARAMETERS
+ * `pdb' Pointer to OVS DB object.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred.
+ */
+int ovs_db_destroy(ovs_db_t *pdb);
+
+/*
+ * NAME
+ * ovs_db_send_request
+ *
+ * DESCRIPTION
+ * Send JSON request to OVS DB server.
+ *
+ * PARAMETERS
+ * `pdb' Pointer to OVS DB object.
+ * `method' Request method name.
+ * `params' Method params to be sent (JSON value as a string).
+ * `cb' Result callback of the request. If NULL, the request
+ * is sent asynchronously.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred.
+ */
+int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params,
+ ovs_db_result_cb_t cb);
+
+/* callback types */
+#define OVS_DB_TABLE_CB_FLAG_INITIAL 0x01U
+#define OVS_DB_TABLE_CB_FLAG_INSERT 0x02U
+#define OVS_DB_TABLE_CB_FLAG_DELETE 0x04U
+#define OVS_DB_TABLE_CB_FLAG_MODIFY 0x08U
+#define OVS_DB_TABLE_CB_FLAG_ALL 0x0FU
+
+/*
+ * NAME
+ * ovs_db_table_cb_register
+ *
+ * DESCRIPTION
+ * Subscribe a callback on OVS DB table event. It allows to
+ * receive notifications (`update_cb' callback is called) of
+ * changes to requested table.
+ *
+ * PARAMETERS
+ * `pdb' Pointer to OVS DB object.
+ * `tb_name' OVS DB Table name to be monitored.
+ * `tb_column' OVS DB Table columns to be monitored. Last
+ * element in the array should be NULL.
+ * `update_cb' Callback function that is called when
+ * requested table columns are changed.
+ * `cb' Result callback of the request. If NULL, the call
+ * becomes asynchronous.
+ * Useful, if OVS_DB_TABLE_CB_FLAG_INITIAL is set.
+ * `flags' Bit mask of:
+ * OVS_DB_TABLE_CB_FLAG_INITIAL Receive initial values in
+ * result callback.
+ * OVS_DB_TABLE_CB_FLAG_INSERT Receive table insert events.
+ * OVS_DB_TABLE_CB_FLAG_DELETE Receive table remove events.
+ * OVS_DB_TABLE_CB_FLAG_MODIFY Receive table update events.
+ * OVS_DB_TABLE_CB_FLAG_ALL Receive all events.
+ *
+ * RETURN VALUE
+ * Zero upon success or non-zero if an error occurred.
+ */
+int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name,
+ const char **tb_column,
+ ovs_db_table_cb_t update_cb,
+ ovs_db_result_cb_t result_cb, unsigned int flags);
+
+/*
+ * OVS utils API
+ */
+
+/*
+ * NAME
+ * ovs_utils_get_value_by_key
+ *
+ * DESCRIPTION
+ * Get YAJL value by object name.
+ *
+ * PARAMETERS
+ * `jval' YAJL object value.
+ * `key' Object key name.
+ *
+ * RETURN VALUE
+ * YAJL value upon success or NULL if key not found.
+ */
+yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key);
+
+/*
+ * NAME
+ * ovs_utils_get_map_value
+ *
+ * DESCRIPTION
+ * Get OVS DB map value by given map key (rfc7047, "Notation" section).
+ *
+ * PARAMETERS
+ * `jval' A 2-element YAJL array that represents a OVS DB map value.
+ * `key' OVS DB map key name.
+ *
+ * RETURN VALUE
+ * YAJL value upon success or NULL if key not found.
+ */
+yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key);
+
+#endif
--- /dev/null
+/**
+ * collectd - src/utils_rrdcreate.c
+ * Copyright (C) 2006-2013 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/common/common.h"
+#include "utils/rrdcreate/rrdcreate.h"
+
+#include <pthread.h>
+#include <rrd.h>
+
+struct srrd_create_args_s {
+ char *filename;
+ unsigned long pdp_step;
+ time_t last_up;
+ int argc;
+ char **argv;
+};
+typedef struct srrd_create_args_s srrd_create_args_t;
+
+struct async_create_file_s;
+typedef struct async_create_file_s async_create_file_t;
+struct async_create_file_s {
+ char *filename;
+ async_create_file_t *next;
+};
+
+/*
+ * Private variables
+ */
+static int rra_timespans[] = {3600, 86400, 604800, 2678400, 31622400};
+static int rra_timespans_num = STATIC_ARRAY_SIZE(rra_timespans);
+
+static const char *const rra_types[] = {"AVERAGE", "MIN", "MAX"};
+static int rra_types_num = STATIC_ARRAY_SIZE(rra_types);
+
+#if !defined(HAVE_THREADSAFE_LIBRRD)
+static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static async_create_file_t *async_creation_list;
+static pthread_mutex_t async_creation_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Private functions
+ */
+static void rra_free(int rra_num, char **rra_def) /* {{{ */
+{
+ for (int i = 0; i < rra_num; i++) {
+ sfree(rra_def[i]);
+ }
+ sfree(rra_def);
+} /* }}} void rra_free */
+
+static void srrd_create_args_destroy(srrd_create_args_t *args) {
+ if (args == NULL)
+ return;
+
+ sfree(args->filename);
+ if (args->argv != NULL) {
+ for (int i = 0; i < args->argc; i++)
+ sfree(args->argv[i]);
+ sfree(args->argv);
+ }
+ sfree(args);
+} /* void srrd_create_args_destroy */
+
+static srrd_create_args_t *srrd_create_args_create(const char *filename,
+ unsigned long pdp_step,
+ time_t last_up, int argc,
+ const char **argv) {
+ srrd_create_args_t *args;
+
+ args = calloc(1, sizeof(*args));
+ if (args == NULL) {
+ P_ERROR("srrd_create_args_create: calloc failed.");
+ return NULL;
+ }
+ args->filename = NULL;
+ args->pdp_step = pdp_step;
+ args->last_up = last_up;
+ args->argv = NULL;
+
+ args->filename = strdup(filename);
+ if (args->filename == NULL) {
+ P_ERROR("srrd_create_args_create: strdup failed.");
+ srrd_create_args_destroy(args);
+ return NULL;
+ }
+
+ args->argv = calloc(argc + 1, sizeof(*args->argv));
+ if (args->argv == NULL) {
+ P_ERROR("srrd_create_args_create: calloc failed.");
+ srrd_create_args_destroy(args);
+ return NULL;
+ }
+
+ for (args->argc = 0; args->argc < argc; args->argc++) {
+ args->argv[args->argc] = strdup(argv[args->argc]);
+ if (args->argv[args->argc] == NULL) {
+ P_ERROR("srrd_create_args_create: strdup failed.");
+ srrd_create_args_destroy(args);
+ return NULL;
+ }
+ }
+ assert(args->argc == argc);
+ args->argv[args->argc] = NULL;
+
+ return args;
+} /* srrd_create_args_t *srrd_create_args_create */
+
+/* * * * * * * * * *
+ * WARNING: Magic *
+ * * * * * * * * * */
+static int rra_get(char ***ret, const value_list_t *vl, /* {{{ */
+ const rrdcreate_config_t *cfg) {
+ char **rra_def;
+ int rra_num;
+
+ int *rts;
+ int rts_num;
+
+ int rra_max;
+
+ int cdp_num;
+ int cdp_len;
+
+ /* The stepsize we use here: If it is user-set, use it. If not, use the
+ * interval of the value-list. */
+ int ss;
+
+ if (cfg->rrarows <= 0) {
+ *ret = NULL;
+ return -1;
+ }
+
+ if ((cfg->xff < 0) || (cfg->xff >= 1.0)) {
+ *ret = NULL;
+ return -1;
+ }
+
+ if (cfg->stepsize > 0)
+ ss = cfg->stepsize;
+ else
+ ss = (int)CDTIME_T_TO_TIME_T(vl->interval);
+ if (ss <= 0) {
+ *ret = NULL;
+ return -1;
+ }
+
+ /* Use the configured timespans or fall back to the built-in defaults */
+ if (cfg->timespans_num != 0) {
+ rts = cfg->timespans;
+ rts_num = cfg->timespans_num;
+ } else {
+ rts = rra_timespans;
+ rts_num = rra_timespans_num;
+ }
+
+ rra_max = rts_num * rra_types_num;
+ assert(rra_max > 0);
+
+ if ((rra_def = calloc(rra_max + 1, sizeof(*rra_def))) == NULL)
+ return -1;
+ rra_num = 0;
+
+ cdp_len = 0;
+ for (int i = 0; i < rts_num; i++) {
+ int span = rts[i];
+
+ if ((span / ss) < cfg->rrarows)
+ span = ss * cfg->rrarows;
+
+ if (cdp_len == 0)
+ cdp_len = 1;
+ else
+ cdp_len = (int)floor(((double)span) / ((double)(cfg->rrarows * ss)));
+
+ cdp_num = (int)ceil(((double)span) / ((double)(cdp_len * ss)));
+
+ for (int j = 0; j < rra_types_num; j++) {
+ char buffer[128];
+ int status;
+
+ if (rra_num >= rra_max)
+ break;
+
+ status = snprintf(buffer, sizeof(buffer), "RRA:%s:%.10f:%u:%u",
+ rra_types[j], cfg->xff, cdp_len, cdp_num);
+
+ if ((status < 0) || ((size_t)status >= sizeof(buffer))) {
+ P_ERROR("rra_get: Buffer would have been truncated.");
+ continue;
+ }
+
+ rra_def[rra_num++] = sstrdup(buffer);
+ }
+ }
+
+ if (rra_num <= 0) {
+ sfree(rra_def);
+ return 0;
+ }
+
+ *ret = rra_def;
+ return rra_num;
+} /* }}} int rra_get */
+
+static void ds_free(int ds_num, char **ds_def) /* {{{ */
+{
+ for (int i = 0; i < ds_num; i++)
+ if (ds_def[i] != NULL)
+ free(ds_def[i]);
+ free(ds_def);
+} /* }}} void ds_free */
+
+static int ds_get(char ***ret, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl,
+ const rrdcreate_config_t *cfg) {
+ char **ds_def;
+ size_t ds_num;
+
+ char min[32];
+ char max[32];
+ char buffer[128];
+
+ assert(ds->ds_num > 0);
+
+ ds_def = calloc(ds->ds_num, sizeof(*ds_def));
+ if (ds_def == NULL) {
+ P_ERROR("ds_get: calloc failed: %s", STRERRNO);
+ return -1;
+ }
+
+ for (ds_num = 0; ds_num < ds->ds_num; ds_num++) {
+ data_source_t *d = ds->ds + ds_num;
+ const char *type;
+ int status;
+
+ ds_def[ds_num] = NULL;
+
+ if (d->type == DS_TYPE_COUNTER)
+ type = "COUNTER";
+ else if (d->type == DS_TYPE_GAUGE)
+ type = "GAUGE";
+ else if (d->type == DS_TYPE_DERIVE)
+ type = "DERIVE";
+ else if (d->type == DS_TYPE_ABSOLUTE)
+ type = "ABSOLUTE";
+ else {
+ P_ERROR("ds_get: Unknown DS type: %i", d->type);
+ break;
+ }
+
+ if (isnan(d->min)) {
+ sstrncpy(min, "U", sizeof(min));
+ } else
+ snprintf(min, sizeof(min), "%f", d->min);
+
+ if (isnan(d->max)) {
+ sstrncpy(max, "U", sizeof(max));
+ } else
+ snprintf(max, sizeof(max), "%f", d->max);
+
+ status = snprintf(
+ buffer, sizeof(buffer), "DS:%s:%s:%i:%s:%s", d->name, type,
+ (cfg->heartbeat > 0) ? cfg->heartbeat
+ : (int)CDTIME_T_TO_TIME_T(2 * vl->interval),
+ min, max);
+ if ((status < 1) || ((size_t)status >= sizeof(buffer)))
+ break;
+
+ ds_def[ds_num] = sstrdup(buffer);
+ } /* for ds_num = 0 .. ds->ds_num */
+
+ if (ds_num != ds->ds_num) {
+ ds_free(ds_num, ds_def);
+ return -1;
+ }
+
+ if (ds_num == 0) {
+ sfree(ds_def);
+ return 0;
+ }
+
+ *ret = ds_def;
+ return ds_num;
+} /* }}} int ds_get */
+
+#if HAVE_THREADSAFE_LIBRRD
+static int srrd_create(const char *filename, /* {{{ */
+ unsigned long pdp_step, time_t last_up, int argc,
+ const char **argv) {
+ int status;
+ char *filename_copy;
+
+ if ((filename == NULL) || (argv == NULL))
+ return -EINVAL;
+
+ /* Some versions of librrd don't have the `const' qualifier for the first
+ * argument, so we have to copy the pointer here to avoid warnings. It sucks,
+ * but what else can we do? :( -octo */
+ filename_copy = strdup(filename);
+ if (filename_copy == NULL) {
+ ERROR("srrd_create: strdup failed.");
+ return -ENOMEM;
+ }
+
+ optind = 0; /* bug in librrd? */
+ rrd_clear_error();
+
+ status = rrd_create_r(filename_copy, pdp_step, last_up, argc, (void *)argv);
+
+ if (status != 0) {
+ P_WARNING("srrd_create: rrd_create_r (%s) failed: %s", filename,
+ rrd_get_error());
+ }
+
+ sfree(filename_copy);
+
+ return status;
+} /* }}} int srrd_create */
+/* #endif HAVE_THREADSAFE_LIBRRD */
+
+#else /* !HAVE_THREADSAFE_LIBRRD */
+static int srrd_create(const char *filename, /* {{{ */
+ unsigned long pdp_step, time_t last_up, int argc,
+ const char **argv) {
+ int status;
+
+ int new_argc;
+ char **new_argv;
+
+ char pdp_step_str[16];
+ char last_up_str[16];
+
+ new_argc = 6 + argc;
+ new_argv = malloc((new_argc + 1) * sizeof(*new_argv));
+ if (new_argv == NULL) {
+ P_ERROR("srrd_create: malloc failed.");
+ return -1;
+ }
+
+ if (last_up == 0)
+ last_up = time(NULL) - 10;
+
+ snprintf(pdp_step_str, sizeof(pdp_step_str), "%lu", pdp_step);
+ snprintf(last_up_str, sizeof(last_up_str), "%lu", (unsigned long)last_up);
+
+ new_argv[0] = "create";
+ new_argv[1] = (void *)filename;
+ new_argv[2] = "-s";
+ new_argv[3] = pdp_step_str;
+ new_argv[4] = "-b";
+ new_argv[5] = last_up_str;
+
+ memcpy(new_argv + 6, argv, argc * sizeof(char *));
+ new_argv[new_argc] = NULL;
+
+ pthread_mutex_lock(&librrd_lock);
+ optind = 0; /* bug in librrd? */
+ rrd_clear_error();
+
+ status = rrd_create(new_argc, new_argv);
+ pthread_mutex_unlock(&librrd_lock);
+
+ if (status != 0) {
+ P_WARNING("srrd_create: rrd_create (%s) failed: %s", filename,
+ rrd_get_error());
+ }
+
+ sfree(new_argv);
+
+ return status;
+} /* }}} int srrd_create */
+#endif /* !HAVE_THREADSAFE_LIBRRD */
+
+static int lock_file(char const *filename) /* {{{ */
+{
+ async_create_file_t *ptr;
+ struct stat sb;
+ int status;
+
+ pthread_mutex_lock(&async_creation_lock);
+
+ for (ptr = async_creation_list; ptr != NULL; ptr = ptr->next)
+ if (strcmp(filename, ptr->filename) == 0)
+ break;
+
+ if (ptr != NULL) {
+ pthread_mutex_unlock(&async_creation_lock);
+ return EEXIST;
+ }
+
+ status = stat(filename, &sb);
+ if ((status == 0) || (errno != ENOENT)) {
+ pthread_mutex_unlock(&async_creation_lock);
+ return EEXIST;
+ }
+
+ ptr = malloc(sizeof(*ptr));
+ if (ptr == NULL) {
+ pthread_mutex_unlock(&async_creation_lock);
+ return ENOMEM;
+ }
+
+ ptr->filename = strdup(filename);
+ if (ptr->filename == NULL) {
+ pthread_mutex_unlock(&async_creation_lock);
+ sfree(ptr);
+ return ENOMEM;
+ }
+
+ ptr->next = async_creation_list;
+ async_creation_list = ptr;
+
+ pthread_mutex_unlock(&async_creation_lock);
+
+ return 0;
+} /* }}} int lock_file */
+
+static int unlock_file(char const *filename) /* {{{ */
+{
+ async_create_file_t *this;
+ async_create_file_t *prev;
+
+ pthread_mutex_lock(&async_creation_lock);
+
+ prev = NULL;
+ for (this = async_creation_list; this != NULL; this = this->next) {
+ if (strcmp(filename, this->filename) == 0)
+ break;
+ prev = this;
+ }
+
+ if (this == NULL) {
+ pthread_mutex_unlock(&async_creation_lock);
+ return ENOENT;
+ }
+
+ if (prev == NULL) {
+ assert(this == async_creation_list);
+ async_creation_list = this->next;
+ } else {
+ assert(this == prev->next);
+ prev->next = this->next;
+ }
+ this->next = NULL;
+
+ pthread_mutex_unlock(&async_creation_lock);
+
+ sfree(this->filename);
+ sfree(this);
+
+ return 0;
+} /* }}} int unlock_file */
+
+static void *srrd_create_thread(void *targs) /* {{{ */
+{
+ srrd_create_args_t *args = targs;
+ char tmpfile[PATH_MAX];
+ int status;
+
+ status = lock_file(args->filename);
+ if (status != 0) {
+ if (status == EEXIST)
+ P_NOTICE("srrd_create_thread: File \"%s\" is already being created.",
+ args->filename);
+ else
+ P_ERROR("srrd_create_thread: Unable to lock file \"%s\".",
+ args->filename);
+ srrd_create_args_destroy(args);
+ return 0;
+ }
+
+ snprintf(tmpfile, sizeof(tmpfile), "%s.async", args->filename);
+
+ status = srrd_create(tmpfile, args->pdp_step, args->last_up, args->argc,
+ (void *)args->argv);
+ if (status != 0) {
+ P_WARNING("srrd_create_thread: srrd_create (%s) returned status %i.",
+ args->filename, status);
+ unlink(tmpfile);
+ unlock_file(args->filename);
+ srrd_create_args_destroy(args);
+ return 0;
+ }
+
+ status = rename(tmpfile, args->filename);
+ if (status != 0) {
+ P_ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile,
+ args->filename, STRERRNO);
+ unlink(tmpfile);
+ unlock_file(args->filename);
+ srrd_create_args_destroy(args);
+ return 0;
+ }
+
+ DEBUG("srrd_create_thread: Successfully created RRD file \"%s\".",
+ args->filename);
+
+ unlock_file(args->filename);
+ srrd_create_args_destroy(args);
+
+ return 0;
+} /* }}} void *srrd_create_thread */
+
+static int srrd_create_async(const char *filename, /* {{{ */
+ unsigned long pdp_step, time_t last_up, int argc,
+ const char **argv) {
+ srrd_create_args_t *args;
+ pthread_t thread;
+ pthread_attr_t attr;
+ int status;
+
+ DEBUG("srrd_create_async: Creating \"%s\" in the background.", filename);
+
+ args = srrd_create_args_create(filename, pdp_step, last_up, argc, argv);
+ if (args == NULL)
+ return -1;
+
+ status = pthread_attr_init(&attr);
+ if (status != 0) {
+ srrd_create_args_destroy(args);
+ return -1;
+ }
+
+ status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (status != 0) {
+ pthread_attr_destroy(&attr);
+ srrd_create_args_destroy(args);
+ return -1;
+ }
+
+ status = pthread_create(&thread, &attr, srrd_create_thread, args);
+ if (status != 0) {
+ P_ERROR("srrd_create_async: pthread_create failed: %s", STRERROR(status));
+ pthread_attr_destroy(&attr);
+ srrd_create_args_destroy(args);
+ return status;
+ }
+
+ pthread_attr_destroy(&attr);
+ /* args is freed in srrd_create_thread(). */
+ return 0;
+} /* }}} int srrd_create_async */
+
+/*
+ * Public functions
+ */
+int cu_rrd_create_file(const char *filename, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl,
+ const rrdcreate_config_t *cfg) {
+ char **argv;
+ int argc;
+ char **rra_def = NULL;
+ int rra_num;
+ char **ds_def = NULL;
+ int ds_num;
+ int status = 0;
+ time_t last_up;
+ unsigned long stepsize;
+
+ if (check_create_dir(filename))
+ return -1;
+
+ if ((rra_num = rra_get(&rra_def, vl, cfg)) < 1) {
+ P_ERROR("cu_rrd_create_file failed: Could not calculate RRAs");
+ return -1;
+ }
+
+ if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) {
+ P_ERROR("cu_rrd_create_file failed: Could not calculate DSes");
+ rra_free(rra_num, rra_def);
+ return -1;
+ }
+
+ argc = ds_num + rra_num;
+
+ if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) {
+ P_ERROR("cu_rrd_create_file failed: %s", STRERRNO);
+ rra_free(rra_num, rra_def);
+ ds_free(ds_num, ds_def);
+ return -1;
+ }
+
+ memcpy(argv, ds_def, ds_num * sizeof(char *));
+ memcpy(argv + ds_num, rra_def, rra_num * sizeof(char *));
+ argv[ds_num + rra_num] = NULL;
+
+ last_up = CDTIME_T_TO_TIME_T(vl->time);
+ if (last_up <= 0)
+ last_up = time(NULL);
+ last_up -= 1;
+
+ if (cfg->stepsize > 0)
+ stepsize = cfg->stepsize;
+ else
+ stepsize = (unsigned long)CDTIME_T_TO_TIME_T(vl->interval);
+
+ if (cfg->async) {
+ status = srrd_create_async(filename, stepsize, last_up, argc,
+ (const char **)argv);
+ if (status != 0)
+ P_WARNING("cu_rrd_create_file: srrd_create_async (%s) "
+ "returned status %i.",
+ filename, status);
+ } else /* synchronous */
+ {
+ status = lock_file(filename);
+ if (status != 0) {
+ if (status == EEXIST)
+ P_NOTICE("cu_rrd_create_file: File \"%s\" is already being created.",
+ filename);
+ else
+ P_ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename);
+ } else {
+ status =
+ srrd_create(filename, stepsize, last_up, argc, (const char **)argv);
+
+ if (status != 0) {
+ P_WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.",
+ filename, status);
+ } else {
+ DEBUG("cu_rrd_create_file: Successfully created RRD file \"%s\".",
+ filename);
+ }
+ unlock_file(filename);
+ }
+ }
+
+ free(argv);
+ ds_free(ds_num, ds_def);
+ rra_free(rra_num, rra_def);
+
+ return status;
+} /* }}} int cu_rrd_create_file */
--- /dev/null
+/**
+ * collectd - src/utils_rrdcreate.h
+ * Copyright (C) 2008-2013 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_RRDCREATE_H
+#define UTILS_RRDCREATE_H 1
+
+#include "plugin.h"
+
+#include <stddef.h>
+
+struct rrdcreate_config_s {
+ unsigned long stepsize;
+ int heartbeat;
+ int rrarows;
+ double xff;
+
+ int *timespans;
+ size_t timespans_num;
+
+ char **consolidation_functions;
+ size_t consolidation_functions_num;
+
+ bool async;
+};
+typedef struct rrdcreate_config_s rrdcreate_config_t;
+
+int cu_rrd_create_file(const char *filename, const data_set_t *ds,
+ const value_list_t *vl, const rrdcreate_config_t *cfg);
+
+#endif /* UTILS_RRDCREATE_H */
--- /dev/null
+/**
+ * collectd - src/utils_tail.c
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * Copyright (C) 2008 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * Description:
+ * Encapsulates useful code for plugins which must watch for appends to
+ * the end of a file.
+ **/
+
+#include "collectd.h"
+
+#include "utils/common/common.h"
+#include "utils/tail/tail.h"
+
+struct cu_tail_s {
+ char *file;
+ FILE *fh;
+ struct stat stat;
+};
+
+static int cu_tail_reopen(cu_tail_t *obj) {
+ int seek_end = 0;
+ struct stat stat_buf = {0};
+
+ int status = stat(obj->file, &stat_buf);
+ if (status != 0) {
+ P_ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO);
+ return -1;
+ }
+
+ /* The file is already open.. */
+ if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) {
+ /* Seek to the beginning if file was truncated */
+ if (stat_buf.st_size < obj->stat.st_size) {
+ P_INFO("utils_tail: File `%s' was truncated.", obj->file);
+ status = fseek(obj->fh, 0, SEEK_SET);
+ if (status != 0) {
+ P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
+ fclose(obj->fh);
+ obj->fh = NULL;
+ return -1;
+ }
+ }
+ memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
+ return 1;
+ }
+
+ /* Seek to the end if we re-open the same file again or the file opened
+ * is the first at all or the first after an error */
+ if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
+ seek_end = 1;
+
+ FILE *fh = fopen(obj->file, "r");
+ if (fh == NULL) {
+ P_ERROR("utils_tail: fopen (%s) failed: %s", obj->file, STRERRNO);
+ return -1;
+ }
+
+ if (seek_end != 0) {
+ status = fseek(fh, 0, SEEK_END);
+ if (status != 0) {
+ P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
+ fclose(fh);
+ return -1;
+ }
+ }
+
+ if (obj->fh != NULL)
+ fclose(obj->fh);
+ obj->fh = fh;
+ memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
+
+ return 0;
+} /* int cu_tail_reopen */
+
+cu_tail_t *cu_tail_create(const char *file) {
+ cu_tail_t *obj;
+
+ obj = calloc(1, sizeof(*obj));
+ if (obj == NULL)
+ return NULL;
+
+ obj->file = strdup(file);
+ if (obj->file == NULL) {
+ free(obj);
+ return NULL;
+ }
+
+ obj->fh = NULL;
+
+ return obj;
+} /* cu_tail_t *cu_tail_create */
+
+int cu_tail_destroy(cu_tail_t *obj) {
+ if (obj->fh != NULL)
+ fclose(obj->fh);
+ free(obj->file);
+ free(obj);
+
+ return 0;
+} /* int cu_tail_destroy */
+
+int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) {
+ int status;
+
+ if (buflen < 1) {
+ ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen);
+ return -1;
+ }
+
+ if (obj->fh == NULL) {
+ status = cu_tail_reopen(obj);
+ if (status < 0)
+ return status;
+ }
+ assert(obj->fh != NULL);
+
+ /* Try to read from the filehandle. If that succeeds, everything appears to
+ * be fine and we can return. */
+ clearerr(obj->fh);
+ if (fgets(buf, buflen, obj->fh) != NULL) {
+ buf[buflen - 1] = '\0';
+ return 0;
+ }
+
+ /* Check if we encountered an error */
+ if (ferror(obj->fh) != 0) {
+ /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
+ fclose(obj->fh);
+ obj->fh = NULL;
+ }
+ /* else: eof -> check if the file was moved away and reopen the new file if
+ * so.. */
+
+ status = cu_tail_reopen(obj);
+ /* error -> return with error */
+ if (status < 0)
+ return status;
+ /* file end reached and file not reopened -> nothing more to read */
+ else if (status > 0) {
+ buf[0] = 0;
+ return 0;
+ }
+
+ /* If we get here: file was re-opened and there may be more to read.. Let's
+ * try again. */
+ if (fgets(buf, buflen, obj->fh) != NULL) {
+ buf[buflen - 1] = '\0';
+ return 0;
+ }
+
+ if (ferror(obj->fh) != 0) {
+ WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file,
+ STRERRNO);
+ fclose(obj->fh);
+ obj->fh = NULL;
+ return -1;
+ }
+
+ /* EOf, well, apparently the new file is empty.. */
+ buf[0] = 0;
+ return 0;
+} /* int cu_tail_readline */
+
+int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+ void *data) {
+ int status;
+
+ while (42) {
+ size_t len;
+
+ status = cu_tail_readline(obj, buf, buflen);
+ if (status != 0) {
+ ERROR("utils_tail: cu_tail_read: cu_tail_readline "
+ "failed.");
+ break;
+ }
+
+ /* check for EOF */
+ if (buf[0] == 0)
+ break;
+
+ len = strlen(buf);
+ while (len > 0) {
+ if (buf[len - 1] != '\n')
+ break;
+ buf[len - 1] = '\0';
+ len--;
+ }
+
+ status = callback(data, buf, buflen);
+ if (status != 0) {
+ ERROR("utils_tail: cu_tail_read: callback returned "
+ "status %i.",
+ status);
+ break;
+ }
+ }
+
+ return status;
+} /* int cu_tail_read */
--- /dev/null
+/**
+ * collectd - src/utils_tail.h
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ * Luke Heberling <lukeh at c-ware.com>
+ *
+ * DESCRIPTION
+ * Facilitates reading information that is appended to a file, taking into
+ * account that the file may be rotated and a new file created under the
+ * same name.
+ **/
+
+#ifndef UTILS_TAIL_H
+#define UTILS_TAIL_H 1
+
+struct cu_tail_s;
+typedef struct cu_tail_s cu_tail_t;
+
+typedef int tailfunc_t(void *data, char *buf, int buflen);
+
+/*
+ * NAME
+ * cu_tail_create
+ *
+ * DESCRIPTION
+ * Allocates a new tail object..
+ *
+ * PARAMETERS
+ * `file' The name of the file to be tailed.
+ */
+cu_tail_t *cu_tail_create(const char *file);
+
+/*
+ * cu_tail_destroy
+ *
+ * Takes a tail object returned by `cu_tail_create' and destroys it, freeing
+ * all internal memory.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_destroy(cu_tail_t *obj);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until `buflen' characters are read, a newline
+ * character is read, or an eof condition is encountered. `buf' is
+ * always null-terminated on successful return and isn't touched when non-zero
+ * is returned.
+ *
+ * You can check if the EOF condition is reached by looking at the buffer: If
+ * the length of the string stored in the buffer is zero, EOF occurred.
+ * Otherwise at least the newline character will be in the buffer.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until eof condition or an error is encountered.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+ void *data);
+
+#endif /* UTILS_TAIL_H */
--- /dev/null
+/**
+ * collectd - src/utils_taskstats.c
+ * Copyright (C) 2017 Florian octo Forster
+ *
+ * ISC License (ISC)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "utils/taskstats/taskstats.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils_time.h"
+
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+#include <linux/taskstats.h>
+
+struct ts_s {
+ struct mnl_socket *nl;
+ pid_t pid;
+ uint32_t seq;
+ uint16_t genl_id_taskstats;
+ unsigned int port_id;
+};
+
+/* nlmsg_errno returns the errno encoded in nlh or zero if not an error. */
+static int nlmsg_errno(struct nlmsghdr *nlh, size_t sz) {
+ if (!mnl_nlmsg_ok(nlh, (int)sz)) {
+ ERROR("utils_taskstats: mnl_nlmsg_ok failed.");
+ return EPROTO;
+ }
+
+ if (nlh->nlmsg_type != NLMSG_ERROR) {
+ return 0;
+ }
+
+ struct nlmsgerr *nlerr = mnl_nlmsg_get_payload(nlh);
+ /* (struct nlmsgerr).error holds a negative errno. */
+ return nlerr->error * (-1);
+}
+
+static int get_taskstats_attr_cb(const struct nlattr *attr, void *data) {
+ struct taskstats *ret_taskstats = data;
+
+ uint16_t type = mnl_attr_get_type(attr);
+ switch (type) {
+ case TASKSTATS_TYPE_STATS:
+ if (mnl_attr_get_payload_len(attr) != sizeof(*ret_taskstats)) {
+ ERROR("utils_taskstats: mnl_attr_get_payload_len(attr) = %" PRIu32
+ ", want %zu",
+ mnl_attr_get_payload_len(attr), sizeof(*ret_taskstats));
+ return MNL_CB_ERROR;
+ }
+ struct taskstats *ts = mnl_attr_get_payload(attr);
+ memmove(ret_taskstats, ts, sizeof(*ret_taskstats));
+ return MNL_CB_OK;
+
+ case TASKSTATS_TYPE_AGGR_PID: /* fall through */
+ case TASKSTATS_TYPE_AGGR_TGID:
+ return mnl_attr_parse_nested(attr, get_taskstats_attr_cb, ret_taskstats);
+
+ case TASKSTATS_TYPE_PID: /* fall through */
+ case TASKSTATS_TYPE_TGID:
+ /* ignore */
+ return MNL_CB_OK;
+
+ default:
+ DEBUG("utils_taskstats: unknown attribute %" PRIu16
+ ", want one of TASKSTATS_TYPE_AGGR_PID/TGID, TASKSTATS_TYPE_STATS",
+ type);
+ }
+ return MNL_CB_OK;
+}
+
+static int get_taskstats_msg_cb(const struct nlmsghdr *nlh, void *data) {
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_taskstats_attr_cb,
+ data);
+}
+
+static int get_taskstats(ts_t *ts, uint32_t tgid,
+ struct taskstats *ret_taskstats) {
+ char buffer[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t seq = ts->seq++;
+
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
+ *nlh = (struct nlmsghdr){
+ .nlmsg_len = nlh->nlmsg_len,
+ .nlmsg_type = ts->genl_id_taskstats,
+ .nlmsg_flags = NLM_F_REQUEST,
+ .nlmsg_seq = seq,
+ .nlmsg_pid = ts->pid,
+ };
+
+ struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
+ *genh = (struct genlmsghdr){
+ .cmd = TASKSTATS_CMD_GET,
+ .version = TASKSTATS_GENL_VERSION, // or TASKSTATS_VERSION?
+ };
+
+ // mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_PID, tgid);
+ mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_TGID, tgid);
+
+ if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
+ int status = errno;
+ ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
+ return status;
+ }
+
+ int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
+ if (status < 0) {
+ status = errno;
+ ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
+ return status;
+ } else if (status == 0) {
+ ERROR("utils_taskstats: mnl_socket_recvfrom() = 0");
+ return ECONNABORTED;
+ }
+ size_t buffer_size = (size_t)status;
+
+ if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
+ ERROR("utils_taskstats: TASKSTATS_CMD_GET(TASKSTATS_CMD_ATTR_TGID = "
+ "%" PRIu32 ") = %s",
+ (uint32_t)tgid, STRERROR(status));
+ return status;
+ }
+
+ status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
+ get_taskstats_msg_cb, ret_taskstats);
+ if (status < MNL_CB_STOP) {
+ ERROR("utils_taskstats: Parsing message failed.");
+ return EPROTO;
+ }
+
+ return 0;
+}
+
+static int get_family_id_attr_cb(const struct nlattr *attr, void *data) {
+ uint16_t type = mnl_attr_get_type(attr);
+ if (type != CTRL_ATTR_FAMILY_ID) {
+ return MNL_CB_OK;
+ }
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ ERROR("mnl_attr_validate() = %s", STRERRNO);
+ return MNL_CB_ERROR;
+ }
+
+ uint16_t *ret_family_id = data;
+ *ret_family_id = mnl_attr_get_u16(attr);
+ return MNL_CB_STOP;
+}
+
+static int get_family_id_msg_cb(const struct nlmsghdr *nlh, void *data) {
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb,
+ data);
+}
+
+/* get_family_id initializes ts->genl_id_taskstats. Returns 0 on success and
+ * an error code otherwise. */
+static int get_family_id(ts_t *ts) {
+ char buffer[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t seq = ts->seq++;
+
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
+ *nlh = (struct nlmsghdr){
+ .nlmsg_len = nlh->nlmsg_len,
+ .nlmsg_type = GENL_ID_CTRL,
+ .nlmsg_flags = NLM_F_REQUEST,
+ .nlmsg_seq = seq,
+ .nlmsg_pid = ts->pid,
+ };
+
+ struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
+ *genh = (struct genlmsghdr){
+ .cmd = CTRL_CMD_GETFAMILY, .version = 0x01,
+ };
+
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TASKSTATS_GENL_NAME);
+
+ assert(genh->cmd == CTRL_CMD_GETFAMILY);
+ assert(genh->version == TASKSTATS_GENL_VERSION);
+
+ if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
+ int status = errno;
+ ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
+ return status;
+ }
+
+ ts->genl_id_taskstats = 0;
+ while (42) {
+ int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
+ if (status < 0) {
+ status = errno;
+ ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
+ return status;
+ } else if (status == 0) {
+ break;
+ }
+ size_t buffer_size = (size_t)status;
+
+ if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
+ ERROR("utils_taskstats: CTRL_CMD_GETFAMILY(\"%s\"): %s",
+ TASKSTATS_GENL_NAME, STRERROR(status));
+ return status;
+ }
+
+ status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
+ get_family_id_msg_cb, &ts->genl_id_taskstats);
+ if (status < MNL_CB_STOP) {
+ ERROR("utils_taskstats: Parsing message failed.");
+ return EPROTO;
+ } else if (status == MNL_CB_STOP) {
+ break;
+ }
+ }
+
+ if (ts->genl_id_taskstats == 0) {
+ ERROR("utils_taskstats: Netlink communication succeeded, but "
+ "genl_id_taskstats is still zero.");
+ return ENOENT;
+ }
+
+ return 0;
+}
+
+void ts_destroy(ts_t *ts) {
+ if (ts == NULL) {
+ return;
+ }
+
+ if (ts->nl != NULL) {
+ mnl_socket_close(ts->nl);
+ ts->nl = NULL;
+ }
+
+ sfree(ts);
+}
+
+ts_t *ts_create(void) {
+ ts_t *ts = calloc(1, sizeof(*ts));
+ if (ts == NULL) {
+ ERROR("utils_taskstats: calloc failed: %s", STRERRNO);
+ return NULL;
+ }
+
+ if ((ts->nl = mnl_socket_open(NETLINK_GENERIC)) == NULL) {
+ ERROR("utils_taskstats: mnl_socket_open(NETLINK_GENERIC) = %s", STRERRNO);
+ ts_destroy(ts);
+ return NULL;
+ }
+
+ if (mnl_socket_bind(ts->nl, 0, MNL_SOCKET_AUTOPID) != 0) {
+ ERROR("utils_taskstats: mnl_socket_bind() = %s", STRERRNO);
+ ts_destroy(ts);
+ return NULL;
+ }
+
+ ts->pid = getpid();
+ ts->port_id = mnl_socket_get_portid(ts->nl);
+
+ int status = get_family_id(ts);
+ if (status != 0) {
+ ERROR("utils_taskstats: get_family_id() = %s", STRERROR(status));
+ ts_destroy(ts);
+ return NULL;
+ }
+
+ return ts;
+}
+
+int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out) {
+ if ((ts == NULL) || (out == NULL)) {
+ return EINVAL;
+ }
+
+ struct taskstats raw = {0};
+
+ int status = get_taskstats(ts, tgid, &raw);
+ if (status != 0) {
+ return status;
+ }
+
+ *out = (ts_delay_t){
+ .cpu_ns = raw.cpu_delay_total,
+ .blkio_ns = raw.blkio_delay_total,
+ .swapin_ns = raw.swapin_delay_total,
+ .freepages_ns = raw.freepages_delay_total,
+ };
+ return 0;
+}
--- /dev/null
+/**
+ * collectd - src/utils_taskstats.h
+ * Copyright (C) 2017 Florian octo Forster
+ *
+ * ISC License (ISC)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#ifndef UTILS_TASKSTATS_H
+#define UTILS_TASKSTATS_H 1
+
+#include "collectd.h"
+
+#include "utils_time.h"
+
+struct ts_s;
+typedef struct ts_s ts_t;
+
+typedef struct {
+ uint64_t cpu_ns;
+ uint64_t blkio_ns;
+ uint64_t swapin_ns;
+ uint64_t freepages_ns;
+} ts_delay_t;
+
+ts_t *ts_create(void);
+void ts_destroy(ts_t *);
+
+/* ts_delay_by_tgid returns Linux delay accounting information for the task
+ * identified by tgid. Returns zero on success and an errno otherwise. */
+int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out);
+
+#endif /* UTILS_TASKSTATS_H */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_flush.c
- * Copyright (C) 2008, 2016 Sebastian Harl
- * Copyright (C) 2008 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sebastian "tokkee" Harl <sh at tokkee.org>
- * Florian "octo" Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_cmd_flush.h"
-
-cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush,
- const cmd_options_t *opts,
- cmd_error_handler_t *err) {
-
- if ((ret_flush == NULL) || (opts == NULL)) {
- errno = EINVAL;
- cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_flush.");
- return CMD_ERROR;
- }
-
- for (size_t i = 0; i < argc; i++) {
- char *opt_key;
- char *opt_value;
- int status;
-
- opt_key = NULL;
- opt_value = NULL;
- status = cmd_parse_option(argv[i], &opt_key, &opt_value, err);
- if (status != 0) {
- if (status == CMD_NO_OPTION)
- cmd_error(CMD_PARSE_ERROR, err, "Invalid option string `%s'.", argv[i]);
- cmd_destroy_flush(ret_flush);
- return CMD_PARSE_ERROR;
- }
-
- if (strcasecmp("plugin", opt_key) == 0) {
- strarray_add(&ret_flush->plugins, &ret_flush->plugins_num, opt_value);
- } else if (strcasecmp("identifier", opt_key) == 0) {
- identifier_t *id =
- realloc(ret_flush->identifiers,
- (ret_flush->identifiers_num + 1) * sizeof(*id));
- if (id == NULL) {
- cmd_error(CMD_ERROR, err, "realloc failed.");
- cmd_destroy_flush(ret_flush);
- return CMD_ERROR;
- }
-
- ret_flush->identifiers = id;
- id = ret_flush->identifiers + ret_flush->identifiers_num;
- ret_flush->identifiers_num++;
- if (parse_identifier(opt_value, &id->host, &id->plugin,
- &id->plugin_instance, &id->type, &id->type_instance,
- opts->identifier_default_host) != 0) {
- cmd_error(CMD_PARSE_ERROR, err, "Invalid identifier `%s'.", opt_value);
- cmd_destroy_flush(ret_flush);
- return CMD_PARSE_ERROR;
- }
- } else if (strcasecmp("timeout", opt_key) == 0) {
- char *endptr;
-
- errno = 0;
- endptr = NULL;
- ret_flush->timeout = strtod(opt_value, &endptr);
-
- if ((endptr == opt_value) || (errno != 0) ||
- (!isfinite(ret_flush->timeout))) {
- cmd_error(CMD_PARSE_ERROR, err,
- "Invalid value for option `timeout': %s", opt_value);
- cmd_destroy_flush(ret_flush);
- return CMD_PARSE_ERROR;
- } else if (ret_flush->timeout < 0.0) {
- ret_flush->timeout = 0.0;
- }
- } else {
- cmd_error(CMD_PARSE_ERROR, err, "Cannot parse option `%s'.", opt_key);
- cmd_destroy_flush(ret_flush);
- return CMD_PARSE_ERROR;
- }
- }
-
- return CMD_OK;
-} /* cmd_status_t cmd_parse_flush */
-
-cmd_status_t cmd_handle_flush(FILE *fh, char *buffer) {
- cmd_error_handler_t err = {cmd_error_fh, fh};
- cmd_t cmd;
-
- int success = 0;
- int error = 0;
- int status;
-
- if ((fh == NULL) || (buffer == NULL))
- return -1;
-
- DEBUG("utils_cmd_flush: cmd_handle_flush (fh = %p, buffer = %s);", (void *)fh,
- buffer);
-
- if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
- return status;
- if (cmd.type != CMD_FLUSH) {
- cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
- CMD_TO_STRING(cmd.type));
- cmd_destroy(&cmd);
- return CMD_UNKNOWN_COMMAND;
- }
-
- for (size_t i = 0; (i == 0) || (i < cmd.cmd.flush.plugins_num); i++) {
- char *plugin = NULL;
-
- if (cmd.cmd.flush.plugins_num != 0)
- plugin = cmd.cmd.flush.plugins[i];
-
- for (size_t j = 0; (j == 0) || (j < cmd.cmd.flush.identifiers_num); j++) {
- char *identifier = NULL;
- char buf[1024];
-
- if (cmd.cmd.flush.identifiers_num != 0) {
- identifier_t *id = cmd.cmd.flush.identifiers + j;
- if (format_name(buf, sizeof(buf), id->host, id->plugin,
- id->plugin_instance, id->type,
- id->type_instance) != 0) {
- error++;
- continue;
- }
- identifier = buf;
- }
-
- if (plugin_flush(plugin, DOUBLE_TO_CDTIME_T(cmd.cmd.flush.timeout),
- identifier) == 0)
- success++;
- else
- error++;
- }
- }
-
- cmd_error(CMD_OK, &err, "Done: %i successful, %i errors", success, error);
-
- cmd_destroy(&cmd);
- return 0;
-#undef PRINT_TO_SOCK
-} /* cmd_status_t cmd_handle_flush */
-
-void cmd_destroy_flush(cmd_flush_t *flush) {
- if (flush == NULL)
- return;
-
- strarray_free(flush->plugins, flush->plugins_num);
- flush->plugins = NULL;
- flush->plugins_num = 0;
-
- sfree(flush->identifiers);
- flush->identifiers_num = 0;
-} /* void cmd_destroy_flush */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_flush.h
- * Copyright (C) 2008, 2016 Sebastian Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sebastian "tokkee" Harl <sh at tokkee.org>
- **/
-
-#ifndef UTILS_CMD_FLUSH_H
-#define UTILS_CMD_FLUSH_H 1
-
-#include "utils_cmds.h"
-
-#include <stdio.h>
-
-cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush,
- const cmd_options_t *opts,
- cmd_error_handler_t *err);
-
-cmd_status_t cmd_handle_flush(FILE *fh, char *buffer);
-
-void cmd_destroy_flush(cmd_flush_t *flush);
-
-#endif /* UTILS_CMD_FLUSH_H */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_getthreshold.c
- * Copyright (C) 2008,2009 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_avltree.h"
-#include "utils_cmd_getthreshold.h"
-#include "utils_parse_option.h" /* for `parse_string' */
-#include "utils_threshold.h"
-
-#define print_to_socket(fh, ...) \
- if (fprintf(fh, __VA_ARGS__) < 0) { \
- WARNING("handle_getthreshold: failed to write to socket #%i: %s", \
- fileno(fh), STRERRNO); \
- return -1; \
- }
-
-int handle_getthreshold(FILE *fh, char *buffer) {
- char *command;
- char *identifier;
- char *identifier_copy;
-
- char *host;
- char *plugin;
- char *plugin_instance;
- char *type;
- char *type_instance;
-
- threshold_t threshold;
-
- int status;
- size_t i;
-
- if ((fh == NULL) || (buffer == NULL))
- return -1;
-
- DEBUG("utils_cmd_getthreshold: handle_getthreshold (fh = %p, buffer = %s);",
- (void *)fh, buffer);
-
- command = NULL;
- status = parse_string(&buffer, &command);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse command.\n");
- return -1;
- }
- assert(command != NULL);
-
- if (strcasecmp("GETTHRESHOLD", command) != 0) {
- print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
- return -1;
- }
-
- identifier = NULL;
- status = parse_string(&buffer, &identifier);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse identifier.\n");
- return -1;
- }
- assert(identifier != NULL);
-
- if (*buffer != 0) {
- print_to_socket(fh, "-1 Garbage after end of command: %s\n", buffer);
- return -1;
- }
-
- /* parse_identifier() modifies its first argument,
- * returning pointers into it */
- identifier_copy = sstrdup(identifier);
-
- status = parse_identifier(identifier_copy, &host, &plugin, &plugin_instance,
- &type, &type_instance,
- /* default_host = */ NULL);
- if (status != 0) {
- DEBUG("handle_getthreshold: Cannot parse identifier `%s'.", identifier);
- print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier);
- sfree(identifier_copy);
- return -1;
- }
-
- value_list_t vl = {.values = NULL};
- sstrncpy(vl.host, host, sizeof(vl.host));
- sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
- if (plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
- sstrncpy(vl.type, type, sizeof(vl.type));
- if (type_instance != NULL)
- sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
- sfree(identifier_copy);
-
- status = ut_search_threshold(&vl, &threshold);
- if (status == ENOENT) {
- print_to_socket(fh, "-1 No threshold found for identifier %s\n",
- identifier);
- return 0;
- } else if (status != 0) {
- print_to_socket(fh, "-1 Error while looking up threshold: %i\n", status);
- return -1;
- }
-
- /* Lets count the number of lines we'll return. */
- i = 0;
- if (threshold.host[0] != 0)
- i++;
- if (threshold.plugin[0] != 0)
- i++;
- if (threshold.plugin_instance[0] != 0)
- i++;
- if (threshold.type[0] != 0)
- i++;
- if (threshold.type_instance[0] != 0)
- i++;
- if (threshold.data_source[0] != 0)
- i++;
- if (!isnan(threshold.warning_min))
- i++;
- if (!isnan(threshold.warning_max))
- i++;
- if (!isnan(threshold.failure_min))
- i++;
- if (!isnan(threshold.failure_max))
- i++;
- if (threshold.hysteresis > 0.0)
- i++;
- if (threshold.hits > 1)
- i++;
-
- /* Print the response */
- print_to_socket(fh, "%" PRIsz " Threshold found\n", i);
-
- if (threshold.host[0] != 0)
- print_to_socket(fh, "Host: %s\n", threshold.host);
- if (threshold.plugin[0] != 0)
- print_to_socket(fh, "Plugin: %s\n", threshold.plugin);
- if (threshold.plugin_instance[0] != 0)
- print_to_socket(fh, "Plugin Instance: %s\n", threshold.plugin_instance);
- if (threshold.type[0] != 0)
- print_to_socket(fh, "Type: %s\n", threshold.type);
- if (threshold.type_instance[0] != 0)
- print_to_socket(fh, "Type Instance: %s\n", threshold.type_instance);
- if (threshold.data_source[0] != 0)
- print_to_socket(fh, "Data Source: %s\n", threshold.data_source);
- if (!isnan(threshold.warning_min))
- print_to_socket(fh, "Warning Min: %g\n", threshold.warning_min);
- if (!isnan(threshold.warning_max))
- print_to_socket(fh, "Warning Max: %g\n", threshold.warning_max);
- if (!isnan(threshold.failure_min))
- print_to_socket(fh, "Failure Min: %g\n", threshold.failure_min);
- if (!isnan(threshold.failure_max))
- print_to_socket(fh, "Failure Max: %g\n", threshold.failure_max);
- if (threshold.hysteresis > 0.0)
- print_to_socket(fh, "Hysteresis: %g\n", threshold.hysteresis);
- if (threshold.hits > 1)
- print_to_socket(fh, "Hits: %i\n", threshold.hits);
-
- return 0;
-} /* int handle_getthreshold */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_getthreshold.h
- * Copyright (C) 2009 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_GETTHRESHOLD_H
-#define UTILS_CMD_GETTHRESHOLD_H 1
-
-#include <stdio.h>
-
-int handle_getthreshold(FILE *fh, char *buffer);
-
-#endif /* UTILS_CMD_GETTHRESHOLD_H */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_getval.c
- * Copyright (C) 2008 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_cache.h"
-#include "utils_cmd_getval.h"
-#include "utils_parse_option.h"
-
-cmd_status_t cmd_parse_getval(size_t argc, char **argv,
- cmd_getval_t *ret_getval,
- const cmd_options_t *opts,
- cmd_error_handler_t *err) {
- char *identifier_copy;
- int status;
-
- if ((ret_getval == NULL) || (opts == NULL)) {
- errno = EINVAL;
- cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_getval.");
- return CMD_ERROR;
- }
-
- if (argc != 1) {
- if (argc == 0)
- cmd_error(CMD_PARSE_ERROR, err, "Missing identifier.");
- else
- cmd_error(CMD_PARSE_ERROR, err, "Garbage after identifier: `%s'.",
- argv[1]);
- return CMD_PARSE_ERROR;
- }
-
- /* parse_identifier() modifies its first argument,
- * returning pointers into it */
- identifier_copy = sstrdup(argv[0]);
-
- status = parse_identifier(
- argv[0], &ret_getval->identifier.host, &ret_getval->identifier.plugin,
- &ret_getval->identifier.plugin_instance, &ret_getval->identifier.type,
- &ret_getval->identifier.type_instance, opts->identifier_default_host);
- if (status != 0) {
- DEBUG("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy);
- cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
- identifier_copy);
- sfree(identifier_copy);
- return CMD_PARSE_ERROR;
- }
-
- ret_getval->raw_identifier = identifier_copy;
- return CMD_OK;
-} /* cmd_status_t cmd_parse_getval */
-
-#define print_to_socket(fh, ...) \
- do { \
- if (fprintf(fh, __VA_ARGS__) < 0) { \
- WARNING("cmd_handle_getval: failed to write to socket #%i: %s", \
- fileno(fh), STRERRNO); \
- return -1; \
- } \
- fflush(fh); \
- } while (0)
-
-cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) {
- cmd_error_handler_t err = {cmd_error_fh, fh};
- cmd_status_t status;
- cmd_t cmd;
-
- gauge_t *values;
- size_t values_num;
-
- const data_set_t *ds;
-
- if ((fh == NULL) || (buffer == NULL))
- return -1;
-
- DEBUG("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);",
- (void *)fh, buffer);
-
- if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
- return status;
- if (cmd.type != CMD_GETVAL) {
- cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
- CMD_TO_STRING(cmd.type));
- cmd_destroy(&cmd);
- return CMD_UNKNOWN_COMMAND;
- }
-
- ds = plugin_get_ds(cmd.cmd.getval.identifier.type);
- if (ds == NULL) {
- DEBUG("cmd_handle_getval: plugin_get_ds (%s) == NULL;",
- cmd.cmd.getval.identifier.type);
- cmd_error(CMD_ERROR, &err, "Type `%s' is unknown.\n",
- cmd.cmd.getval.identifier.type);
- cmd_destroy(&cmd);
- return -1;
- }
-
- values = NULL;
- values_num = 0;
- status =
- uc_get_rate_by_name(cmd.cmd.getval.raw_identifier, &values, &values_num);
- if (status != 0) {
- cmd_error(CMD_ERROR, &err, "No such value.");
- cmd_destroy(&cmd);
- return CMD_ERROR;
- }
-
- if (ds->ds_num != values_num) {
- ERROR("ds[%s]->ds_num = %" PRIsz ", "
- "but uc_get_rate_by_name returned %" PRIsz " values.",
- ds->type, ds->ds_num, values_num);
- cmd_error(CMD_ERROR, &err, "Error reading value from cache.");
- sfree(values);
- cmd_destroy(&cmd);
- return CMD_ERROR;
- }
-
- print_to_socket(fh, "%" PRIsz " Value%s found\n", values_num,
- (values_num == 1) ? "" : "s");
- for (size_t i = 0; i < values_num; i++) {
- print_to_socket(fh, "%s=", ds->ds[i].name);
- if (isnan(values[i])) {
- print_to_socket(fh, "NaN\n");
- } else {
- print_to_socket(fh, "%12e\n", values[i]);
- }
- }
-
- sfree(values);
- cmd_destroy(&cmd);
-
- return CMD_OK;
-} /* cmd_status_t cmd_handle_getval */
-
-void cmd_destroy_getval(cmd_getval_t *getval) {
- if (getval == NULL)
- return;
-
- sfree(getval->raw_identifier);
-} /* void cmd_destroy_getval */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_getval.h
- * Copyright (C) 2008 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_GETVAL_H
-#define UTILS_CMD_GETVAL_H 1
-
-#include <stdio.h>
-
-#include "utils_cmds.h"
-
-cmd_status_t cmd_parse_getval(size_t argc, char **argv,
- cmd_getval_t *ret_getval,
- const cmd_options_t *opts,
- cmd_error_handler_t *err);
-
-cmd_status_t cmd_handle_getval(FILE *fh, char *buffer);
-
-void cmd_destroy_getval(cmd_getval_t *getval);
-
-#endif /* UTILS_CMD_GETVAL_H */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_listval.c
- * Copyright (C) 2008 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_cache.h"
-#include "utils_cmd_listval.h"
-#include "utils_parse_option.h"
-
-cmd_status_t cmd_parse_listval(size_t argc, char **argv,
- const cmd_options_t *opts
- __attribute__((unused)),
- cmd_error_handler_t *err) {
- if (argc != 0) {
- cmd_error(CMD_PARSE_ERROR, err, "Garbage after end of command: `%s'.",
- argv[0]);
- return CMD_PARSE_ERROR;
- }
-
- return CMD_OK;
-} /* cmd_status_t cmd_parse_listval */
-
-#define free_everything_and_return(status) \
- do { \
- for (size_t j = 0; j < number; j++) { \
- sfree(names[j]); \
- names[j] = NULL; \
- } \
- sfree(names); \
- sfree(times); \
- return status; \
- } while (0)
-
-#define print_to_socket(fh, ...) \
- do { \
- if (fprintf(fh, __VA_ARGS__) < 0) { \
- WARNING("handle_listval: failed to write to socket #%i: %s", fileno(fh), \
- STRERRNO); \
- free_everything_and_return(CMD_ERROR); \
- } \
- fflush(fh); \
- } while (0)
-
-cmd_status_t cmd_handle_listval(FILE *fh, char *buffer) {
- cmd_error_handler_t err = {cmd_error_fh, fh};
- cmd_status_t status;
- cmd_t cmd;
-
- char **names = NULL;
- cdtime_t *times = NULL;
- size_t number = 0;
-
- DEBUG("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);", (void *)fh,
- buffer);
-
- if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
- return status;
- if (cmd.type != CMD_LISTVAL) {
- cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
- CMD_TO_STRING(cmd.type));
- free_everything_and_return(CMD_UNKNOWN_COMMAND);
- }
-
- status = uc_get_names(&names, ×, &number);
- if (status != 0) {
- DEBUG("command listval: uc_get_names failed with status %i", status);
- cmd_error(CMD_ERROR, &err, "uc_get_names failed.");
- free_everything_and_return(CMD_ERROR);
- }
-
- print_to_socket(fh, "%i Value%s found\n", (int)number,
- (number == 1) ? "" : "s");
- for (size_t i = 0; i < number; i++)
- print_to_socket(fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE(times[i]), names[i]);
-
- free_everything_and_return(CMD_OK);
-} /* cmd_status_t cmd_handle_listval */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_listval.h
- * Copyright (C) 2008 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_LISTVAL_H
-#define UTILS_CMD_LISTVAL_H 1
-
-#include <stdio.h>
-
-#include "utils_cmds.h"
-
-cmd_status_t cmd_parse_listval(size_t argc, char **argv,
- const cmd_options_t *opts,
- cmd_error_handler_t *err);
-
-cmd_status_t cmd_handle_listval(FILE *fh, char *buffer);
-
-#endif /* UTILS_CMD_LISTVAL_H */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_putnotif.c
- * Copyright (C) 2008 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_cmd_putnotif.h"
-#include "utils_parse_option.h"
-
-#define print_to_socket(fh, ...) \
- do { \
- if (fprintf(fh, __VA_ARGS__) < 0) { \
- WARNING("handle_putnotif: failed to write to socket #%i: %s", \
- fileno(fh), STRERRNO); \
- return -1; \
- } \
- fflush(fh); \
- } while (0)
-
-static int set_option_severity(notification_t *n, const char *value) {
- if (strcasecmp(value, "Failure") == 0)
- n->severity = NOTIF_FAILURE;
- else if (strcasecmp(value, "Warning") == 0)
- n->severity = NOTIF_WARNING;
- else if (strcasecmp(value, "Okay") == 0)
- n->severity = NOTIF_OKAY;
- else
- return -1;
-
- return 0;
-} /* int set_option_severity */
-
-static int set_option_time(notification_t *n, const char *value) {
- char *endptr = NULL;
- double tmp;
-
- errno = 0;
- tmp = strtod(value, &endptr);
- if ((errno != 0) /* Overflow */
- || (endptr == value) /* Invalid string */
- || (endptr == NULL) /* This should not happen */
- || (*endptr != 0)) /* Trailing chars */
- return -1;
-
- n->time = DOUBLE_TO_CDTIME_T(tmp);
-
- return 0;
-} /* int set_option_time */
-
-static int set_option(notification_t *n, const char *option,
- const char *value) {
- if ((n == NULL) || (option == NULL) || (value == NULL))
- return -1;
-
- DEBUG("utils_cmd_putnotif: set_option (option = %s, value = %s);", option,
- value);
-
- /* Add a meta option in the form: <type>:<key> */
- if (option[0] != '\0' && option[1] == ':') {
- /* Refuse empty key */
- if (option[2] == '\0')
- return 1;
-
- if (option[0] == 's')
- return plugin_notification_meta_add_string(n, option + 2, value);
- else
- return 1;
- }
-
- if (strcasecmp("severity", option) == 0)
- return set_option_severity(n, value);
- else if (strcasecmp("time", option) == 0)
- return set_option_time(n, value);
- else if (strcasecmp("message", option) == 0)
- sstrncpy(n->message, value, sizeof(n->message));
- else if (strcasecmp("host", option) == 0)
- sstrncpy(n->host, value, sizeof(n->host));
- else if (strcasecmp("plugin", option) == 0)
- sstrncpy(n->plugin, value, sizeof(n->plugin));
- else if (strcasecmp("plugin_instance", option) == 0)
- sstrncpy(n->plugin_instance, value, sizeof(n->plugin_instance));
- else if (strcasecmp("type", option) == 0)
- sstrncpy(n->type, value, sizeof(n->type));
- else if (strcasecmp("type_instance", option) == 0)
- sstrncpy(n->type_instance, value, sizeof(n->type_instance));
- else
- return 1;
-
- return 0;
-} /* int set_option */
-
-int handle_putnotif(FILE *fh, char *buffer) {
- char *command;
- notification_t n = {0};
- int status;
-
- if ((fh == NULL) || (buffer == NULL))
- return -1;
-
- DEBUG("utils_cmd_putnotif: handle_putnotif (fh = %p, buffer = %s);",
- (void *)fh, buffer);
-
- command = NULL;
- status = parse_string(&buffer, &command);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse command.\n");
- return -1;
- }
- assert(command != NULL);
-
- if (strcasecmp("PUTNOTIF", command) != 0) {
- print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
- return -1;
- }
-
- status = 0;
- while (*buffer != 0) {
- char *key;
- char *value;
-
- status = parse_option(&buffer, &key, &value);
- if (status != 0) {
- print_to_socket(fh, "-1 Malformed option.\n");
- break;
- }
-
- status = set_option(&n, key, value);
- if (status != 0) {
- print_to_socket(fh, "-1 Error parsing option `%s'\n", key);
- break;
- }
- } /* for (i) */
-
- /* Check for required fields and complain if anything is missing. */
- if ((status == 0) && (n.severity == 0)) {
- print_to_socket(fh, "-1 Option `severity' missing.\n");
- status = -1;
- }
- if ((status == 0) && (n.time == 0)) {
- print_to_socket(fh, "-1 Option `time' missing.\n");
- status = -1;
- }
- if ((status == 0) && (strlen(n.message) == 0)) {
- print_to_socket(fh, "-1 No message or message of length 0 given.\n");
- status = -1;
- }
-
- /* If status is still zero the notification is fine and we can finally
- * dispatch it. */
- if (status == 0) {
- plugin_dispatch_notification(&n);
- print_to_socket(fh, "0 Success\n");
- }
-
- return 0;
-} /* int handle_putnotif */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_putnotif.h
- * Copyright (C) 2008 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_PUTNOTIF_H
-#define UTILS_CMD_PUTNOTIF_H 1
-
-#include <stdio.h>
-
-int handle_putnotif(FILE *fh, char *buffer);
-
-#endif /* UTILS_CMD_PUTNOTIF_H */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_putval.c
- * Copyright (C) 2007-2009 Florian octo Forster
- * Copyright (C) 2016 Sebastian tokkee Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- * Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "utils_cmd_putval.h"
-
-/*
- * private helper functions
- */
-
-static int set_option(value_list_t *vl, const char *key, const char *value) {
- if ((vl == NULL) || (key == NULL) || (value == NULL))
- return -1;
-
- if (strcasecmp("interval", key) == 0) {
- double tmp;
- char *endptr;
-
- endptr = NULL;
- errno = 0;
- tmp = strtod(value, &endptr);
-
- if ((errno == 0) && (endptr != NULL) && (endptr != value) && (tmp > 0.0))
- vl->interval = DOUBLE_TO_CDTIME_T(tmp);
- } else
- return 1;
-
- return 0;
-} /* int set_option */
-
-/*
- * public API
- */
-
-cmd_status_t cmd_parse_putval(size_t argc, char **argv,
- cmd_putval_t *ret_putval,
- const cmd_options_t *opts,
- cmd_error_handler_t *err) {
- cmd_status_t result;
-
- char *identifier;
- char *hostname;
- char *plugin;
- char *plugin_instance;
- char *type;
- char *type_instance;
- int status;
-
- char *identifier_copy;
-
- const data_set_t *ds;
- value_list_t vl = VALUE_LIST_INIT;
-
- if ((ret_putval == NULL) || (opts == NULL)) {
- errno = EINVAL;
- cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_putval.");
- return CMD_ERROR;
- }
-
- if (argc < 2) {
- cmd_error(CMD_PARSE_ERROR, err, "Missing identifier and/or value-list.");
- return CMD_PARSE_ERROR;
- }
-
- identifier = argv[0];
-
- /* parse_identifier() modifies its first argument, returning pointers into
- * it; retain the old value for later. */
- identifier_copy = sstrdup(identifier);
-
- status =
- parse_identifier(identifier, &hostname, &plugin, &plugin_instance, &type,
- &type_instance, opts->identifier_default_host);
- if (status != 0) {
- DEBUG("cmd_handle_putval: Cannot parse identifier `%s'.", identifier_copy);
- cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
- identifier_copy);
- sfree(identifier_copy);
- return CMD_PARSE_ERROR;
- }
-
- if ((strlen(hostname) >= sizeof(vl.host)) ||
- (strlen(plugin) >= sizeof(vl.plugin)) ||
- ((plugin_instance != NULL) &&
- (strlen(plugin_instance) >= sizeof(vl.plugin_instance))) ||
- ((type_instance != NULL) &&
- (strlen(type_instance) >= sizeof(vl.type_instance)))) {
- cmd_error(CMD_PARSE_ERROR, err, "Identifier too long.");
- sfree(identifier_copy);
- return CMD_PARSE_ERROR;
- }
-
- sstrncpy(vl.host, hostname, sizeof(vl.host));
- sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
- sstrncpy(vl.type, type, sizeof(vl.type));
- if (plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
- if (type_instance != NULL)
- sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
-
- ds = plugin_get_ds(type);
- if (ds == NULL) {
- cmd_error(CMD_PARSE_ERROR, err, "1 Type `%s' isn't defined.", type);
- sfree(identifier_copy);
- return CMD_PARSE_ERROR;
- }
-
- hostname = NULL;
- plugin = NULL;
- plugin_instance = NULL;
- type = NULL;
- type_instance = NULL;
-
- ret_putval->raw_identifier = identifier_copy;
- if (ret_putval->raw_identifier == NULL) {
- cmd_error(CMD_ERROR, err, "malloc failed.");
- cmd_destroy_putval(ret_putval);
- sfree(vl.values);
- return CMD_ERROR;
- }
-
- /* All the remaining fields are part of the option list. */
- result = CMD_OK;
- for (size_t i = 1; i < argc; ++i) {
- value_list_t *tmp;
-
- char *key = NULL;
- char *value = NULL;
-
- status = cmd_parse_option(argv[i], &key, &value, err);
- if (status == CMD_OK) {
- assert(key != NULL);
- assert(value != NULL);
- set_option(&vl, key, value);
- continue;
- } else if (status != CMD_NO_OPTION) {
- /* parse_option failed, buffer has been modified.
- * => we need to abort */
- result = status;
- break;
- }
- /* else: cmd_parse_option did not find an option; treat this as a
- * value list. */
-
- vl.values_len = ds->ds_num;
- vl.values = calloc(vl.values_len, sizeof(*vl.values));
- if (vl.values == NULL) {
- cmd_error(CMD_ERROR, err, "malloc failed.");
- result = CMD_ERROR;
- break;
- }
-
- status = parse_values(argv[i], &vl, ds);
- if (status != 0) {
- cmd_error(CMD_PARSE_ERROR, err, "Parsing the values string failed.");
- result = CMD_PARSE_ERROR;
- vl.values_len = 0;
- sfree(vl.values);
- break;
- }
-
- tmp = realloc(ret_putval->vl,
- (ret_putval->vl_num + 1) * sizeof(*ret_putval->vl));
- if (tmp == NULL) {
- cmd_error(CMD_ERROR, err, "realloc failed.");
- cmd_destroy_putval(ret_putval);
- result = CMD_ERROR;
- vl.values_len = 0;
- sfree(vl.values);
- break;
- }
-
- ret_putval->vl = tmp;
- ret_putval->vl_num++;
- memcpy(&ret_putval->vl[ret_putval->vl_num - 1], &vl, sizeof(vl));
-
- /* pointer is now owned by ret_putval->vl[] */
- vl.values_len = 0;
- vl.values = NULL;
- } /* while (*buffer != 0) */
- /* Done parsing the options. */
-
- if (result != CMD_OK)
- cmd_destroy_putval(ret_putval);
-
- return result;
-} /* cmd_status_t cmd_parse_putval */
-
-void cmd_destroy_putval(cmd_putval_t *putval) {
- if (putval == NULL)
- return;
-
- sfree(putval->raw_identifier);
-
- for (size_t i = 0; i < putval->vl_num; ++i) {
- sfree(putval->vl[i].values);
- meta_data_destroy(putval->vl[i].meta);
- putval->vl[i].meta = NULL;
- }
- sfree(putval->vl);
- putval->vl = NULL;
- putval->vl_num = 0;
-} /* void cmd_destroy_putval */
-
-cmd_status_t cmd_handle_putval(FILE *fh, char *buffer) {
- cmd_error_handler_t err = {cmd_error_fh, fh};
- cmd_t cmd;
-
- int status;
-
- DEBUG("utils_cmd_putval: cmd_handle_putval (fh = %p, buffer = %s);",
- (void *)fh, buffer);
-
- if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
- return status;
- if (cmd.type != CMD_PUTVAL) {
- cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
- CMD_TO_STRING(cmd.type));
- cmd_destroy(&cmd);
- return CMD_UNKNOWN_COMMAND;
- }
-
- for (size_t i = 0; i < cmd.cmd.putval.vl_num; ++i)
- plugin_dispatch_values(&cmd.cmd.putval.vl[i]);
-
- if (fh != stdout)
- cmd_error(CMD_OK, &err, "Success: %i %s been dispatched.",
- (int)cmd.cmd.putval.vl_num,
- (cmd.cmd.putval.vl_num == 1) ? "value has" : "values have");
-
- cmd_destroy(&cmd);
- return CMD_OK;
-} /* int cmd_handle_putval */
-
-int cmd_create_putval(char *ret, size_t ret_len, /* {{{ */
- const data_set_t *ds, const value_list_t *vl) {
- char buffer_ident[6 * DATA_MAX_NAME_LEN];
- char buffer_values[1024];
- int status;
-
- status = FORMAT_VL(buffer_ident, sizeof(buffer_ident), vl);
- if (status != 0)
- return status;
- escape_string(buffer_ident, sizeof(buffer_ident));
-
- status = format_values(buffer_values, sizeof(buffer_values), ds, vl,
- /* store rates = */ false);
- if (status != 0)
- return status;
- escape_string(buffer_values, sizeof(buffer_values));
-
- snprintf(ret, ret_len, "PUTVAL %s interval=%.3f %s", buffer_ident,
- (vl->interval > 0) ? CDTIME_T_TO_DOUBLE(vl->interval)
- : CDTIME_T_TO_DOUBLE(plugin_get_interval()),
- buffer_values);
-
- return 0;
-} /* }}} int cmd_create_putval */
+++ /dev/null
-/**
- * collectd - src/utils_cmd_putval.h
- * Copyright (C) 2007 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_PUTVAL_H
-#define UTILS_CMD_PUTVAL_H 1
-
-#include "plugin.h"
-#include "utils_cmds.h"
-
-#include <stdio.h>
-
-cmd_status_t cmd_parse_putval(size_t argc, char **argv,
- cmd_putval_t *ret_putval,
- const cmd_options_t *opts,
- cmd_error_handler_t *err);
-
-cmd_status_t cmd_handle_putval(FILE *fh, char *buffer);
-
-void cmd_destroy_putval(cmd_putval_t *putval);
-
-int cmd_create_putval(char *ret, size_t ret_len, const data_set_t *ds,
- const value_list_t *vl);
-
-#endif /* UTILS_CMD_PUTVAL_H */
+++ /dev/null
-/**
- * collectd - src/utils_cmds.c
- * Copyright (C) 2008 Florian Forster
- * Copyright (C) 2016 Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- * Sebastian 'tokkee' Harl <sh at tokkee.org>
- **/
-
-#include "daemon/common.h"
-#include "utils_cmd_flush.h"
-#include "utils_cmd_getval.h"
-#include "utils_cmd_listval.h"
-#include "utils_cmd_putval.h"
-#include "utils_cmds.h"
-#include "utils_parse_option.h"
-
-#include <stdbool.h>
-#include <string.h>
-
-static cmd_options_t default_options = {
- /* identifier_default_host = */ NULL,
-};
-
-/*
- * private helper functions
- */
-
-static cmd_status_t cmd_split(char *buffer, size_t *ret_len, char ***ret_fields,
- cmd_error_handler_t *err) {
- char *field;
- bool in_field, in_quotes;
-
- size_t estimate, len;
- char **fields;
-
- estimate = 0;
- in_field = false;
- for (char *string = buffer; *string != '\0'; ++string) {
- /* Make a quick worst-case estimate of the number of fields by
- * counting spaces and ignoring quotation marks. */
- if (!isspace((int)*string)) {
- if (!in_field) {
- estimate++;
- in_field = true;
- }
- } else {
- in_field = false;
- }
- }
-
- /* fields will be NULL-terminated */
- fields = malloc((estimate + 1) * sizeof(*fields));
- if (fields == NULL) {
- cmd_error(CMD_ERROR, err, "malloc failed.");
- return CMD_ERROR;
- }
-
-#define END_FIELD() \
- do { \
- *field = '\0'; \
- field = NULL; \
- in_field = false; \
- } while (0)
-#define NEW_FIELD() \
- do { \
- field = string; \
- in_field = true; \
- assert(len < estimate); \
- fields[len] = field; \
- field++; \
- len++; \
- } while (0)
-
- len = 0;
- field = NULL;
- in_field = false;
- in_quotes = false;
- for (char *string = buffer; *string != '\0'; string++) {
- if (isspace((int)string[0])) {
- if (!in_quotes) {
- if (in_field)
- END_FIELD();
-
- /* skip space */
- continue;
- }
- } else if (string[0] == '"') {
- /* Note: Two consecutive quoted fields not separated by space are
- * treated as different fields. This is the collectd 5.x behavior
- * around splitting fields. */
-
- if (in_quotes) {
- /* end of quoted field */
- if (!in_field) /* empty quoted string */
- NEW_FIELD();
- END_FIELD();
- in_quotes = false;
- continue;
- }
-
- in_quotes = true;
- /* if (! in_field): add new field on next iteration
- * else: quoted string following an unquoted string (one field)
- * in either case: skip quotation mark */
- continue;
- } else if ((string[0] == '\\') && in_quotes) {
- /* Outside of quotes, a backslash is a regular character (mostly
- * for backward compatibility). */
-
- if (string[1] == '\0') {
- free(fields);
- cmd_error(CMD_PARSE_ERROR, err, "Backslash at end of string.");
- return CMD_PARSE_ERROR;
- }
-
- /* un-escape the next character; skip backslash */
- string++;
- }
-
- if (!in_field)
- NEW_FIELD();
- else {
- *field = string[0];
- field++;
- }
- }
-
- if (in_quotes) {
- free(fields);
- cmd_error(CMD_PARSE_ERROR, err, "Unterminated quoted string.");
- return CMD_PARSE_ERROR;
- }
-
-#undef NEW_FIELD
-#undef END_FIELD
-
- fields[len] = NULL;
- if (ret_len != NULL)
- *ret_len = len;
- if (ret_fields != NULL)
- *ret_fields = fields;
- else
- free(fields);
- return CMD_OK;
-} /* int cmd_split */
-
-/*
- * public API
- */
-
-void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
- const char *format, ...) {
- va_list ap;
-
- if ((err == NULL) || (err->cb == NULL))
- return;
-
- va_start(ap, format);
- err->cb(err->ud, status, format, ap);
- va_end(ap);
-} /* void cmd_error */
-
-cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
- const cmd_options_t *opts, cmd_error_handler_t *err) {
- char *command = NULL;
- cmd_status_t status;
-
- if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL)) {
- errno = EINVAL;
- cmd_error(CMD_ERROR, err, "Missing command.");
- return CMD_ERROR;
- }
-
- if (opts == NULL)
- opts = &default_options;
-
- memset(ret_cmd, 0, sizeof(*ret_cmd));
- command = argv[0];
- if (strcasecmp("FLUSH", command) == 0) {
- ret_cmd->type = CMD_FLUSH;
- status =
- cmd_parse_flush(argc - 1, argv + 1, &ret_cmd->cmd.flush, opts, err);
- } else if (strcasecmp("GETVAL", command) == 0) {
- ret_cmd->type = CMD_GETVAL;
- status =
- cmd_parse_getval(argc - 1, argv + 1, &ret_cmd->cmd.getval, opts, err);
- } else if (strcasecmp("LISTVAL", command) == 0) {
- ret_cmd->type = CMD_LISTVAL;
- status = cmd_parse_listval(argc - 1, argv + 1, opts, err);
- } else if (strcasecmp("PUTVAL", command) == 0) {
- ret_cmd->type = CMD_PUTVAL;
- status =
- cmd_parse_putval(argc - 1, argv + 1, &ret_cmd->cmd.putval, opts, err);
- } else {
- ret_cmd->type = CMD_UNKNOWN;
- cmd_error(CMD_UNKNOWN_COMMAND, err, "Unknown command `%s'.", command);
- return CMD_UNKNOWN_COMMAND;
- }
-
- if (status != CMD_OK)
- ret_cmd->type = CMD_UNKNOWN;
- return status;
-} /* cmd_status_t cmd_parsev */
-
-cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts,
- cmd_error_handler_t *err) {
- char **fields = NULL;
- size_t fields_num = 0;
- cmd_status_t status;
-
- if ((status = cmd_split(buffer, &fields_num, &fields, err)) != CMD_OK)
- return status;
-
- status = cmd_parsev(fields_num, fields, ret_cmd, opts, err);
- free(fields);
- return status;
-} /* cmd_status_t cmd_parse */
-
-void cmd_destroy(cmd_t *cmd) {
- if (cmd == NULL)
- return;
-
- switch (cmd->type) {
- case CMD_UNKNOWN:
- /* nothing to do */
- break;
- case CMD_FLUSH:
- cmd_destroy_flush(&cmd->cmd.flush);
- break;
- case CMD_GETVAL:
- cmd_destroy_getval(&cmd->cmd.getval);
- break;
- case CMD_LISTVAL:
- break;
- case CMD_PUTVAL:
- cmd_destroy_putval(&cmd->cmd.putval);
- break;
- }
-} /* void cmd_destroy */
-
-cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value,
- cmd_error_handler_t *err) {
- char *key, *value;
-
- if (field == NULL) {
- errno = EINVAL;
- cmd_error(CMD_ERROR, err, "Invalid argument to cmd_parse_option.");
- return CMD_ERROR;
- }
- key = value = field;
-
- /* Look for the equal sign. */
- while (isalnum((int)value[0]) || (value[0] == '_') || (value[0] == ':'))
- value++;
- if ((value[0] != '=') || (value == key)) {
- /* Whether this is a fatal error is up to the caller. */
- return CMD_NO_OPTION;
- }
- *value = '\0';
- value++;
-
- if (ret_key != NULL)
- *ret_key = key;
- if (ret_value != NULL)
- *ret_value = value;
-
- return CMD_OK;
-} /* cmd_status_t cmd_parse_option */
-
-void cmd_error_fh(void *ud, cmd_status_t status, const char *format,
- va_list ap) {
- FILE *fh = ud;
- int code = -1;
- char buf[1024];
-
- if (status == CMD_OK)
- code = 0;
-
- vsnprintf(buf, sizeof(buf), format, ap);
- buf[sizeof(buf) - 1] = '\0';
- if (fprintf(fh, "%i %s\n", code, buf) < 0) {
- WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh),
- STRERRNO);
- return;
- }
-
- fflush(fh);
-} /* void cmd_error_fh */
+++ /dev/null
-/**
- * collectd - src/utils_cmds.h
- * Copyright (C) 2016 Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sebastian 'tokkee' Harl <sh at tokkee.org>
- **/
-
-#ifndef UTILS_CMDS_H
-#define UTILS_CMDS_H 1
-
-#include "plugin.h"
-
-#include <stdarg.h>
-
-typedef enum {
- CMD_UNKNOWN = 0,
- CMD_FLUSH = 1,
- CMD_GETVAL = 2,
- CMD_LISTVAL = 3,
- CMD_PUTVAL = 4,
-} cmd_type_t;
-#define CMD_TO_STRING(type) \
- ((type) == CMD_FLUSH) \
- ? "FLUSH" \
- : ((type) == CMD_GETVAL) \
- ? "GETVAL" \
- : ((type) == CMD_LISTVAL) \
- ? "LISTVAL" \
- : ((type) == CMD_PUTVAL) ? "PUTVAL" : "UNKNOWN"
-
-typedef struct {
- double timeout;
-
- char **plugins;
- size_t plugins_num;
- identifier_t *identifiers;
- size_t identifiers_num;
-} cmd_flush_t;
-
-typedef struct {
- char *raw_identifier;
- identifier_t identifier;
-} cmd_getval_t;
-
-typedef struct {
- /* The raw identifier as provided by the user. */
- char *raw_identifier;
-
- /* An array of the fully parsed identifier and all value lists, and their
- * options as provided by the user. */
- value_list_t *vl;
- size_t vl_num;
-} cmd_putval_t;
-
-/*
- * NAME
- * cmd_t
- *
- * DESCRIPTION
- * The representation of a fully parsed command.
- */
-typedef struct {
- cmd_type_t type;
- union {
- cmd_flush_t flush;
- cmd_getval_t getval;
- cmd_putval_t putval;
- } cmd;
-} cmd_t;
-
-/*
- * NAME
- * cmd_options_t
- *
- * DESCRIPTIONS
- * Optional settings for tuning the parser behavior.
- */
-typedef struct {
- /* identifier_default_host: If non-NULL, the hostname is optional and will
- * default to the specified value. */
- char *identifier_default_host;
-} cmd_options_t;
-
-/*
- * NAME
- * cmd_status_t
- *
- * DESCRIPTION
- * Status codes describing the parse result.
- */
-typedef enum {
- CMD_OK = 0,
- CMD_ERROR = -1,
- CMD_PARSE_ERROR = -2,
- CMD_UNKNOWN_COMMAND = -3,
-
- /* Not necessarily fatal errors. */
- CMD_NO_OPTION = 1,
-} cmd_status_t;
-
-/*
- * NAME
- * cmd_error_handler_t
- *
- * DESCRIPTION
- * An error handler describes a callback to be invoked when the parser
- * encounters an error. The user data pointer will be passed to the callback
- * as the first argument.
- */
-typedef struct {
- void (*cb)(void *, cmd_status_t, const char *, va_list);
- void *ud;
-} cmd_error_handler_t;
-
-/*
- * NAME:
- * cmd_error
- *
- * DESCRIPTION
- * Reports an error via the specified error handler (if set).
- */
-void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
- const char *format, ...);
-
-/*
- * NAME
- * cmd_parse
- *
- * DESCRIPTION
- * Parse a command string and populate a command object.
- *
- * PARAMETERS
- * `buffer' The command string to be parsed.
- * `ret_cmd' The parse result will be stored at this location.
- * `opts' Parser options. If NULL, defaults will be used.
- * `err' An optional error handler to invoke on error.
- *
- * RETURN VALUE
- * CMD_OK on success or the respective error code otherwise.
- */
-cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts,
- cmd_error_handler_t *err);
-
-cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
- const cmd_options_t *opts, cmd_error_handler_t *err);
-
-void cmd_destroy(cmd_t *cmd);
-
-/*
- * NAME
- * cmd_parse_option
- *
- * DESCRIPTION
- * Parses a command option which must be of the form:
- * name=value with \ and spaces
- *
- * PARAMETERS
- * `field' The parsed input field with any quotes removed and special
- * characters unescaped.
- * `ret_key' The parsed key will be stored at this location.
- * `ret_value' The parsed value will be stored at this location.
- *
- * RETURN VALUE
- * CMD_OK on success or an error code otherwise.
- * CMD_NO_OPTION if `field' does not represent an option at all (missing
- * equal sign).
- */
-cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value,
- cmd_error_handler_t *err);
-
-/*
- * NAME
- * cmd_error_fh
- *
- * DESCRIPTION
- * An error callback writing the message to an open file handle using the
- * format expected by the unixsock or exec plugins.
- *
- * PARAMETERS
- * `ud' Error handler user-data pointer. This must be an open
- * file-handle (FILE *).
- * `status' The error status code.
- * `format' Printf-style format string.
- * `ap' Variable argument list providing the arguments for the format
- * string.
- */
-void cmd_error_fh(void *ud, cmd_status_t status, const char *format,
- va_list ap);
-
-#endif /* UTILS_CMDS_H */
+++ /dev/null
-/**
- * collectd - src/tests/utils_cmds_test.c
- * Copyright (C) 2016 Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sebastian 'tokkee' Harl <sh at tokkee.org>
- **/
-
-#include "common.h"
-#include "testing.h"
-#include "utils_cmds.h"
-
-static void error_cb(void *ud, cmd_status_t status, const char *format,
- va_list ap) {
- if (status == CMD_OK)
- return;
-
- printf("ERROR[%d]: ", status);
- vprintf(format, ap);
- printf("\n");
- fflush(stdout);
-} /* void error_cb */
-
-static cmd_options_t default_host_opts = {
- /* identifier_default_host = */ "dummy-host",
-};
-
-static struct {
- char *input;
- cmd_options_t *opts;
- cmd_status_t expected_status;
- cmd_type_t expected_type;
-} parse_data[] = {
- /* Valid FLUSH commands. */
- {
- "FLUSH", NULL, CMD_OK, CMD_FLUSH,
- },
- {
- "FLUSH identifier=myhost/magic/MAGIC", NULL, CMD_OK, CMD_FLUSH,
- },
- {
- "FLUSH identifier=magic/MAGIC", &default_host_opts, CMD_OK, CMD_FLUSH,
- },
- {
- "FLUSH timeout=123 plugin=\"A\"", NULL, CMD_OK, CMD_FLUSH,
- },
- /* Invalid FLUSH commands. */
- {
- /* Missing hostname; no default. */
- "FLUSH identifier=magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- /* Missing 'identifier' key. */
- "FLUSH myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- /* Invalid timeout. */
- "FLUSH timeout=A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- /* Invalid identifier. */
- "FLUSH identifier=invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- /* Invalid option. */
- "FLUSH invalid=option", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
-
- /* Valid GETVAL commands. */
- {
- "GETVAL myhost/magic/MAGIC", NULL, CMD_OK, CMD_GETVAL,
- },
- {
- "GETVAL magic/MAGIC", &default_host_opts, CMD_OK, CMD_GETVAL,
- },
-
- /* Invalid GETVAL commands. */
- {
- "GETVAL magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "GETVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "GETVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
-
- /* Valid LISTVAL commands. */
- {
- "LISTVAL", NULL, CMD_OK, CMD_LISTVAL,
- },
-
- /* Invalid LISTVAL commands. */
- {
- "LISTVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
-
- /* Valid PUTVAL commands. */
- {
- "PUTVAL magic/MAGIC N:42", &default_host_opts, CMD_OK, CMD_PUTVAL,
- },
- {
- "PUTVAL myhost/magic/MAGIC N:42", NULL, CMD_OK, CMD_PUTVAL,
- },
- {
- "PUTVAL myhost/magic/MAGIC 1234:42", NULL, CMD_OK, CMD_PUTVAL,
- },
- {
- "PUTVAL myhost/magic/MAGIC 1234:42 2345:23", NULL, CMD_OK, CMD_PUTVAL,
- },
- {
- "PUTVAL myhost/magic/MAGIC interval=2 1234:42", NULL, CMD_OK,
- CMD_PUTVAL,
- },
- {
- "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23", NULL,
- CMD_OK, CMD_PUTVAL,
- },
-
- /* Invalid PUTVAL commands. */
- {
- "PUTVAL magic/MAGIC N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "PUTVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "PUTVAL invalid N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "PUTVAL myhost/magic/MAGIC A:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "PUTVAL myhost/magic/MAGIC 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "PUTVAL myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "PUTVAL 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
- },
- {
- "PUTVAL myhost/magic/UNKNOWN 1234:42", NULL, CMD_PARSE_ERROR,
- CMD_UNKNOWN,
- },
- /*
- * As of collectd 5.x, PUTVAL accepts invalid options.
- {
- "PUTVAL myhost/magic/MAGIC invalid=2 1234:42",
- NULL,
- CMD_PARSE_ERROR,
- CMD_UNKNOWN,
- },
- */
-
- /* Invalid commands. */
- {
- "INVALID", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN,
- },
- {
- "INVALID interval=2", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN,
- },
-};
-
-DEF_TEST(parse) {
- cmd_error_handler_t err = {error_cb, NULL};
- int test_result = 0;
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(parse_data); i++) {
- char *input = strdup(parse_data[i].input);
-
- char description[1024];
- cmd_status_t status;
- cmd_t cmd;
-
- bool result;
-
- memset(&cmd, 0, sizeof(cmd));
-
- status = cmd_parse(input, &cmd, parse_data[i].opts, &err);
- snprintf(description, sizeof(description), "cmd_parse (\"%s\", opts=%p) = "
- "%d (type=%d [%s]); want %d "
- "(type=%d [%s])",
- parse_data[i].input, parse_data[i].opts, status, cmd.type,
- CMD_TO_STRING(cmd.type), parse_data[i].expected_status,
- parse_data[i].expected_type,
- CMD_TO_STRING(parse_data[i].expected_type));
- result = (status == parse_data[i].expected_status) &&
- (cmd.type == parse_data[i].expected_type);
- LOG(result, description);
-
- /* Run all tests before failing. */
- if (!result)
- test_result = -1;
-
- cmd_destroy(&cmd);
- free(input);
- }
-
- return test_result;
-}
-
-int main(int argc, char **argv) {
- RUN_TEST(parse);
- END_TEST;
-}
+++ /dev/null
-/**
- * collectd - src/utils_config_cores.c
- *
- * Copyright(c) 2018 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-
-#include "utils_config_cores.h"
-
-#define UTIL_NAME "utils_config_cores"
-
-#define MAX_SOCKETS 8
-#define MAX_SOCKET_CORES 64
-#define MAX_CORES (MAX_SOCKET_CORES * MAX_SOCKETS)
-
-static inline _Bool is_in_list(unsigned val, const unsigned *list, size_t len) {
- for (size_t i = 0; i < len; i++)
- if (list[i] == val)
- return 1;
- return 0;
-}
-
-static int str_to_uint(const char *s, unsigned *n) {
- if (s == NULL || n == NULL)
- return -EINVAL;
- char *endptr = NULL;
-
- *n = (unsigned)strtoul(s, &endptr, 0);
- if (*s == '\0' || *endptr != '\0') {
- ERROR(UTIL_NAME ": Failed to parse '%s' into unsigned number", s);
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * NAME
- * str_list_to_nums
- *
- * DESCRIPTION
- * Converts string of characters representing list of numbers into array of
- * numbers. Allowed formats are:
- * 0,1,2,3
- * 0-10,20-18
- * 1,3,5-8,10,0x10-12
- *
- * Numbers can be in decimal or hexadecimal format.
- *
- * PARAMETERS
- * `s' String representing list of unsigned numbers.
- * `nums' Array to put converted numeric values into.
- * `nums_len' Maximum number of elements that nums can accommodate.
- *
- * RETURN VALUE
- * Number of elements placed into nums.
- */
-static size_t str_list_to_nums(char *s, unsigned *nums, size_t nums_len) {
- char *saveptr = NULL;
- char *token;
- size_t idx = 0;
-
- while ((token = strtok_r(s, ",", &saveptr))) {
- char *pos;
- unsigned start, end = 0;
- s = NULL;
-
- while (isspace(*token))
- token++;
- if (*token == '\0')
- continue;
-
- pos = strchr(token, '-');
- if (pos) {
- *pos = '\0';
- }
-
- if (str_to_uint(token, &start))
- return 0;
-
- if (pos) {
- if (str_to_uint(pos + 1, &end))
- return 0;
- } else {
- end = start;
- }
-
- if (start > end) {
- unsigned swap = start;
- start = end;
- end = swap;
- }
-
- for (unsigned i = start; i <= end; i++) {
- if (is_in_list(i, nums, idx))
- continue;
- if (idx >= nums_len) {
- WARNING(UTIL_NAME ": exceeded the cores number limit: %" PRIsz,
- nums_len);
- return idx;
- }
- nums[idx] = i;
- idx++;
- }
- }
- return idx;
-}
-
-/*
- * NAME
- * check_core_grouping
- *
- * DESCRIPTION
- * Look for [...] brackets in *in string and if found copy the
- * part between brackets into *out string and set grouped to 0.
- * Otherwise grouped is set to 1 and input is copied without leading
- * whitespaces.
- *
- * PARAMETERS
- * `out' Output string to store result.
- * `in' Input string to be parsed and copied.
- * `out_size' Maximum number of elements that out can accommodate.
- * `grouped' Set by function depending if cores should be grouped or not.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred.
- */
-static int check_core_grouping(char *out, const char *in, size_t out_size,
- _Bool *grouped) {
- const char *start = in;
- char *end;
- while (isspace(*start))
- ++start;
- if (start[0] == '[') {
- *grouped = 0;
- ++start;
- end = strchr(start, ']');
- if (end == NULL) {
- ERROR(UTIL_NAME ": Missing closing bracket ] in option %s.", in);
- return -EINVAL;
- }
- if ((size_t)(end - start) >= out_size) {
- ERROR(UTIL_NAME ": Out buffer is too small.");
- return -EINVAL;
- }
- sstrncpy(out, start, end - start + 1);
- DEBUG(UTIL_NAME ": Mask for individual (not aggregated) cores: %s", out);
- } else {
- *grouped = 1;
- sstrncpy(out, start, out_size);
- }
- return 0;
-}
-
-int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl) {
- if (ci == NULL || cgl == NULL)
- return -EINVAL;
- if (ci->values_num == 0 || ci->values_num > MAX_CORES)
- return -EINVAL;
- core_group_t cgroups[MAX_CORES] = {{0}};
- size_t cg_idx = 0; /* index for cgroups array */
- int ret = 0;
-
- for (int i = 0; i < ci->values_num; i++) {
- if (ci->values[i].type != OCONFIG_TYPE_STRING) {
- WARNING(UTIL_NAME ": The %s option requires string arguments.", ci->key);
- return -EINVAL;
- }
- }
-
- if (ci->values_num == 1 && ci->values[0].value.string &&
- strlen(ci->values[0].value.string) == 0)
- return 0;
-
- for (int i = 0; i < ci->values_num; i++) {
- size_t n;
- _Bool grouped = 1;
- char str[DATA_MAX_NAME_LEN];
- unsigned cores[MAX_CORES] = {0};
-
- if (cg_idx >= STATIC_ARRAY_SIZE(cgroups)) {
- ERROR(UTIL_NAME
- ": Configuration exceeds maximum number of cores: %" PRIsz,
- STATIC_ARRAY_SIZE(cgroups));
- ret = -EINVAL;
- goto parse_error;
- }
- if ((ci->values[i].value.string == NULL) ||
- (strlen(ci->values[i].value.string) == 0)) {
- ERROR(UTIL_NAME ": Failed to parse parameters for %s option.", ci->key);
- ret = -EINVAL;
- goto parse_error;
- }
-
- ret = check_core_grouping(str, ci->values[i].value.string, sizeof(str),
- &grouped);
- if (ret != 0) {
- ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
- ci->values[i].value.string);
- goto parse_error;
- }
- n = str_list_to_nums(str, cores, STATIC_ARRAY_SIZE(cores));
- if (n == 0) {
- ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
- ci->values[i].value.string);
- ret = -EINVAL;
- goto parse_error;
- }
-
- if (grouped) {
- cgroups[cg_idx].desc = strdup(ci->values[i].value.string);
- if (cgroups[cg_idx].desc == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate description.");
- ret = -ENOMEM;
- goto parse_error;
- }
-
- cgroups[cg_idx].cores = calloc(n, sizeof(*cgroups[cg_idx].cores));
- if (cgroups[cg_idx].cores == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate cores for cgroup.");
- ret = -ENOMEM;
- goto parse_error;
- }
-
- for (size_t j = 0; j < n; j++)
- cgroups[cg_idx].cores[j] = cores[j];
-
- cgroups[cg_idx].num_cores = n;
- cg_idx++;
- } else {
- for (size_t j = 0; j < n && cg_idx < STATIC_ARRAY_SIZE(cgroups); j++) {
- char desc[DATA_MAX_NAME_LEN];
- snprintf(desc, sizeof(desc), "%u", cores[j]);
-
- cgroups[cg_idx].desc = strdup(desc);
- if (cgroups[cg_idx].desc == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate desc for core %u.", cores[j]);
- ret = -ENOMEM;
- goto parse_error;
- }
-
- cgroups[cg_idx].cores = calloc(1, sizeof(*(cgroups[cg_idx].cores)));
- if (cgroups[cg_idx].cores == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate cgroup for core %u.", cores[j]);
- ret = -ENOMEM;
- goto parse_error;
- }
- cgroups[cg_idx].num_cores = 1;
- cgroups[cg_idx].cores[0] = cores[j];
- cg_idx++;
- }
- }
- }
-
- cgl->cgroups = calloc(cg_idx, sizeof(*cgl->cgroups));
- if (cgl->cgroups == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate core groups.");
- ret = -ENOMEM;
- goto parse_error;
- }
-
- cgl->num_cgroups = cg_idx;
- for (size_t i = 0; i < cg_idx; i++)
- cgl->cgroups[i] = cgroups[i];
-
- return 0;
-
-parse_error:
-
- cg_idx = 0;
- while (cg_idx < STATIC_ARRAY_SIZE(cgroups) && cgroups[cg_idx].desc != NULL) {
- sfree(cgroups[cg_idx].desc);
- sfree(cgroups[cg_idx].cores);
- cg_idx++;
- }
- return ret;
-}
-
-int config_cores_default(int num_cores, core_groups_list_t *cgl) {
- if (cgl == NULL || num_cores < 0 || num_cores > MAX_CORES)
- return -EINVAL;
-
- cgl->cgroups = calloc(num_cores, sizeof(*(cgl->cgroups)));
- if (cgl->cgroups == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate memory for core groups.");
- return -ENOMEM;
- }
- cgl->num_cgroups = num_cores;
-
- for (int i = 0; i < num_cores; i++) {
- char desc[DATA_MAX_NAME_LEN];
- snprintf(desc, sizeof(desc), "%d", i);
-
- cgl->cgroups[i].cores = calloc(1, sizeof(*(cgl->cgroups[i].cores)));
- if (cgl->cgroups[i].cores == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate default cores for cgroup %d.", i);
- config_cores_cleanup(cgl);
- return -ENOMEM;
- }
- cgl->cgroups[i].num_cores = 1;
- cgl->cgroups[i].cores[0] = i;
-
- cgl->cgroups[i].desc = strdup(desc);
- if (cgl->cgroups[i].desc == NULL) {
- ERROR(UTIL_NAME ": Failed to allocate description for cgroup %d.", i);
- config_cores_cleanup(cgl);
- return -ENOMEM;
- }
- }
- return 0;
-}
-
-void config_cores_cleanup(core_groups_list_t *cgl) {
- if (cgl == NULL)
- return;
- for (size_t i = 0; i < cgl->num_cgroups; i++) {
- sfree(cgl->cgroups[i].desc);
- sfree(cgl->cgroups[i].cores);
- }
- sfree(cgl->cgroups);
- cgl->num_cgroups = 0;
-}
-
-int config_cores_cmp_cgroups(const core_group_t *cg_a,
- const core_group_t *cg_b) {
- size_t found = 0;
-
- assert(cg_a != NULL);
- assert(cg_b != NULL);
-
- const size_t sz_a = cg_a->num_cores;
- const size_t sz_b = cg_b->num_cores;
- const unsigned *tab_a = cg_a->cores;
- const unsigned *tab_b = cg_b->cores;
-
- for (size_t i = 0; i < sz_a; i++)
- if (is_in_list(tab_a[i], tab_b, sz_b))
- found++;
-
- /* if no cores are the same */
- if (!found)
- return 0;
- /* if group contains same cores */
- if (sz_a == sz_b && sz_b == found)
- return 1;
- /* if not all cores are the same */
- return -1;
-}
+++ /dev/null
-/**
- * collectd - src/utils_config_cores.h
- *
- * Copyright(c) 2018 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
- **/
-
-#ifndef UTILS_CONFIG_CORES_H
-#define UTILS_CONFIG_CORES_H 1
-
-#include "configfile.h"
-
-#ifndef PRIsz
-#define PRIsz "zu"
-#endif /* PRIsz */
-
-struct core_group_s {
- char *desc;
- unsigned int *cores;
- size_t num_cores;
-};
-typedef struct core_group_s core_group_t;
-
-struct core_groups_list_s {
- core_group_t *cgroups;
- size_t num_cgroups;
-};
-typedef struct core_groups_list_s core_groups_list_t;
-
-/*
- * NAME
- * config_cores_parse
- *
- * DESCRIPTION
- * Convert strings from config item into list of core groups.
- *
- * PARAMETERS
- * `ci' Pointer to config item.
- * `cgl' Pointer to core groups list to be filled.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred.
- *
- * NOTES
- * In case of an error, *cgl is not modified.
- * Numbers can be in decimal or hexadecimal format.
- * The memory allocated for *cgroups in list needs to be freed
- * with config_cores_cleanup.
- *
- * EXAMPLES
- * If config is "0-3" "[4-15]" it means that cores 0-3 are aggregated
- * into one group and cores 4 to 15 are stored individualily in
- * separate groups. Examples of allowed formats:
- * "0,3,4" "10-15" - cores collected into two groups
- * "0" "0x3" "7" - 3 cores, each in individual group
- * "[32-63]" - 32 cores, each in individual group
- *
- * For empty string "" *cgl is not modified and zero is returned.
- */
-int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl);
-
-/*
- * NAME
- * config_cores_default
- *
- * DESCRIPTION
- * Set number of cores starting from zero into individual
- * core groups in *cgl list.
- *
- * PARAMETERS
- * `num_cores' Number of cores to be configured.
- * `cgl' Pointer to core groups list.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred.
- *
- * NOTES
- * The memory allocated for *cgroups in list needs to be freed
- * with config_cores_cleanup. In case of error the memory is
- * freed by the function itself.
- */
-int config_cores_default(int num_cores, core_groups_list_t *cgl);
-
-/*
- * NAME
- * config_cores_cleanup
- *
- * DESCRIPTION
- * Free the memory allocated for cgroups and set
- * num_cgroups to zero.
- *
- * PARAMETERS
- * `cgl' Pointer to core groups list.
- */
-void config_cores_cleanup(core_groups_list_t *cgl);
-
-/*
- * NAME
- * config_cores_cmp_cgroups
- *
- * DESCRIPTION
- * Function to compare cores in 2 core groups.
- *
- * PARAMETERS
- * `cg_a' Pointer to core group a.
- * `cg_b' Pointer to core group b.
- *
- * RETURN VALUE
- * 1 if both groups contain the same cores
- * 0 if none of their cores match
- * -1 if some but not all cores match
- */
-int config_cores_cmp_cgroups(const core_group_t *cg_a,
- const core_group_t *cg_b);
-
-#endif /* UTILS_CONFIG_CORES_H */
+++ /dev/null
-/**
- * collectd - src/utils_config_cores_test.c
- *
- * Copyright(c) 2018 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
- **/
-
-#include "collectd.h"
-
-#include "testing.h"
-#include "utils_config_cores.c" /* sic */
-
-oconfig_value_t test_cfg_values[] = {{{"0"}, OCONFIG_TYPE_STRING},
- {{"1-2"}, OCONFIG_TYPE_STRING},
- {{"[3-4]"}, OCONFIG_TYPE_STRING}};
-
-oconfig_item_t test_cfg = {
- "Cores", test_cfg_values, STATIC_ARRAY_SIZE(test_cfg_values), NULL, NULL,
- 0};
-
-static int compare_with_test_config(core_groups_list_t *cgl) {
- if (cgl->num_cgroups == 4 && cgl->cgroups[0].num_cores == 1 &&
- strcmp("0", cgl->cgroups[0].desc) == 0 && cgl->cgroups[0].cores[0] == 0 &&
- cgl->cgroups[1].num_cores == 2 &&
- strcmp("1-2", cgl->cgroups[1].desc) == 0 &&
- cgl->cgroups[1].cores[0] == 1 && cgl->cgroups[1].cores[1] == 2 &&
- cgl->cgroups[2].num_cores == 1 &&
- strcmp("3", cgl->cgroups[2].desc) == 0 && cgl->cgroups[2].cores[0] == 3 &&
- cgl->cgroups[3].num_cores == 1 &&
- strcmp("4", cgl->cgroups[3].desc) == 0 && cgl->cgroups[3].cores[0] == 4)
- return 0;
-
- return -1;
-}
-
-DEF_TEST(string_to_uint) {
- int ret = 0;
- char *s = "13", *s1 = "0xd", *s2 = "g";
- unsigned n = 0;
-
- ret = str_to_uint(s, &n);
- EXPECT_EQ_INT(0, ret);
- EXPECT_EQ_INT(13, n);
-
- ret = str_to_uint(s1, &n);
- EXPECT_EQ_INT(0, ret);
- EXPECT_EQ_INT(13, n);
-
- ret = str_to_uint(s2, &n);
- OK(ret < 0);
-
- ret = str_to_uint(NULL, &n);
- OK(ret < 0);
- return 0;
-}
-
-DEF_TEST(cores_list_to_numbers) {
- size_t n = 0;
- unsigned nums[MAX_CORES];
- char str[64] = "";
-
- n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
- EXPECT_EQ_INT(0, n);
-
- strncpy(str, "1", STATIC_ARRAY_SIZE(str));
- n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
- EXPECT_EQ_INT(1, n);
- EXPECT_EQ_INT(1, nums[0]);
-
- strncpy(str, "0,2-3", STATIC_ARRAY_SIZE(str));
- n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
- EXPECT_EQ_INT(3, n);
- EXPECT_EQ_INT(0, nums[0]);
- EXPECT_EQ_INT(2, nums[1]);
- EXPECT_EQ_INT(3, nums[2]);
-
- strncpy(str, "11-0xa", STATIC_ARRAY_SIZE(str));
- n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
- EXPECT_EQ_INT(2, n);
- EXPECT_EQ_INT(10, nums[0]);
- EXPECT_EQ_INT(11, nums[1]);
-
- snprintf(str, sizeof(str), "0-%d", (MAX_CORES - 1));
- n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
- EXPECT_EQ_INT(MAX_CORES, n);
- EXPECT_EQ_INT(0, nums[0]);
- EXPECT_EQ_INT(MAX_CORES - 1, nums[MAX_CORES - 1]);
-
- /* Should return 0 for incorrect syntax. */
- strncpy(str, "5g", STATIC_ARRAY_SIZE(str));
- n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums));
- EXPECT_EQ_INT(0, n);
- return 0;
-}
-
-DEF_TEST(check_grouped_cores) {
- int ret = 0;
- _Bool grouped;
- char src[64] = "[5-15]";
- char dest[64];
-
- ret = check_core_grouping(dest, src, sizeof(dest), &grouped);
- EXPECT_EQ_INT(0, ret);
- EXPECT_EQ_INT(0, grouped);
- EXPECT_EQ_STR("5-15", dest);
-
- strncpy(src, " 5-15", STATIC_ARRAY_SIZE(src));
- ret = check_core_grouping(dest, src, sizeof(dest), &grouped);
- EXPECT_EQ_INT(0, ret);
- EXPECT_EQ_INT(1, grouped);
- EXPECT_EQ_STR("5-15", dest);
- return 0;
-}
-
-DEF_TEST(cores_option_parse) {
- int ret = 0;
- core_groups_list_t cgl = {0};
-
- ret = config_cores_parse(&test_cfg, &cgl);
- EXPECT_EQ_INT(0, ret);
- CHECK_NOT_NULL(cgl.cgroups);
- EXPECT_EQ_INT(0, compare_with_test_config(&cgl));
-
- config_cores_cleanup(&cgl);
- return 0;
-}
-
-DEF_TEST(cores_option_parse_fail) {
- int ret = 0;
- core_groups_list_t cgl = {0};
- /* Wrong value, missing closing bracket ] */
- oconfig_value_t values = {{"[0-15"}, OCONFIG_TYPE_STRING};
- oconfig_item_t cfg = {"Cores", &values, 1, NULL, NULL, 0};
-
- ret = config_cores_parse(&cfg, &cgl);
- EXPECT_EQ_INT(-EINVAL, ret);
- EXPECT_EQ_INT(0, cgl.num_cgroups);
- OK(NULL == cgl.cgroups);
- return 0;
-}
-
-DEF_TEST(cores_default_list) {
- int ret = 0;
- core_groups_list_t cgl = {0};
-
- ret = config_cores_default(2, &cgl);
- EXPECT_EQ_INT(0, ret);
- EXPECT_EQ_INT(2, cgl.num_cgroups);
- CHECK_NOT_NULL(cgl.cgroups);
-
- CHECK_NOT_NULL(cgl.cgroups[0].cores);
- CHECK_NOT_NULL(cgl.cgroups[0].desc);
- EXPECT_EQ_STR("0", cgl.cgroups[0].desc);
- EXPECT_EQ_INT(1, cgl.cgroups[0].num_cores);
- EXPECT_EQ_INT(0, cgl.cgroups[0].cores[0]);
-
- CHECK_NOT_NULL(cgl.cgroups[1].cores);
- CHECK_NOT_NULL(cgl.cgroups[1].desc);
- EXPECT_EQ_STR("1", cgl.cgroups[1].desc);
- EXPECT_EQ_INT(1, cgl.cgroups[1].num_cores);
- EXPECT_EQ_INT(1, cgl.cgroups[1].cores[0]);
-
- config_cores_cleanup(&cgl);
- return 0;
-}
-
-DEF_TEST(cores_default_list_fail) {
- int ret = 0;
- core_groups_list_t cgl = {0};
-
- ret = config_cores_default(-1, &cgl);
- OK(ret < 0);
- ret = config_cores_default(MAX_CORES + 1, &cgl);
- OK(ret < 0);
- ret = config_cores_default(1, NULL);
- OK(ret < 0);
- return 0;
-}
-
-DEF_TEST(cores_group_cleanup) {
- core_groups_list_t cgl;
- cgl.cgroups = calloc(1, sizeof(*cgl.cgroups));
- CHECK_NOT_NULL(cgl.cgroups);
- cgl.num_cgroups = 1;
- cgl.cgroups[0].desc = strdup("1");
- cgl.cgroups[0].cores = calloc(1, sizeof(*cgl.cgroups[0].cores));
- CHECK_NOT_NULL(cgl.cgroups[0].cores);
- cgl.cgroups[0].cores[0] = 1;
- cgl.cgroups[0].num_cores = 1;
-
- config_cores_cleanup(&cgl);
- OK(NULL == cgl.cgroups);
- EXPECT_EQ_INT(0, cgl.num_cgroups);
- return 0;
-}
-
-DEF_TEST(cores_group_cmp) {
- unsigned cores_mock[] = {0, 1, 2};
- core_group_t group_mock = {"0,1,2", cores_mock, 3};
- unsigned cores_mock_2[] = {2, 3};
- core_group_t group_mock_2 = {"2,3", cores_mock_2, 2};
-
- int ret = config_cores_cmp_cgroups(&group_mock, &group_mock);
- EXPECT_EQ_INT(1, ret);
-
- ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2);
- EXPECT_EQ_INT(-1, ret);
-
- cores_mock_2[0] = 4;
- ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2);
- EXPECT_EQ_INT(0, ret);
- return 0;
-}
-
-int main(void) {
- RUN_TEST(string_to_uint);
- RUN_TEST(cores_list_to_numbers);
- RUN_TEST(check_grouped_cores);
-
- RUN_TEST(cores_group_cleanup);
- RUN_TEST(cores_option_parse);
- RUN_TEST(cores_option_parse_fail);
- RUN_TEST(cores_default_list);
- RUN_TEST(cores_default_list_fail);
-
- RUN_TEST(cores_group_cmp);
-
- END_TEST;
-}
+++ /dev/null
-/*
- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
- * code or tables extracted from it, as desired without restriction.
- *
- * First, the polynomial itself and its table of feedback terms. The
- * polynomial is
- * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- * Note that we take it "backwards" and put the highest-order term in
- * the lowest-order bit. The X^32 term is "implied"; the LSB is the
- * X^31 term, etc. The X^0 term (usually shown as "+1") results in
- * the MSB being 1
- *
- * Note that the usual hardware shift register implementation, which
- * is what we're using (we're merely optimizing it by doing eight-bit
- * chunks at a time) shifts bits into the lowest-order term. In our
- * implementation, that means shifting towards the right. Why do we
- * do it this way? Because the calculated CRC must be transmitted in
- * order from highest-order term to lowest-order term. UARTs transmit
- * characters in order from LSB to MSB. By storing the CRC this way
- * we hand it to the UART in the order low-byte to high-byte; the UART
- * sends each low-bit to hight-bit; and the result is transmission bit
- * by bit from highest- to lowest-order term without requiring any bit
- * shuffling on our part. Reception works similarly
- *
- * The feedback terms table consists of 256, 32-bit entries. Notes
- *
- * The table can be generated at runtime if desired; code to do so
- * is shown later. It might not be obvious, but the feedback
- * terms simply represent the results of eight shift/xor opera
- * tions for all combinations of data and CRC register values
- *
- * The values must be right-shifted by eight bits by the "updcrc
- * logic; the shift must be unsigned (bring in zeroes). On some
- * hardware you could probably optimize the shift in assembler by
- * using byte-swap instructions
- * polynomial $edb88320
- */
-
-#include <stddef.h>
-#include <stdint.h>
-
-uint32_t crc32_buffer(const unsigned char *, size_t);
-static unsigned int crc32_tab[] = {
- 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
- 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
- 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
- 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
- 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
- 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
- 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
- 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
- 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
- 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
- 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
- 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
- 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
- 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
- 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
- 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
- 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
- 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
- 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
- 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
- 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
- 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
- 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
- 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
- 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
- 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
- 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
- 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
- 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
- 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
- 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
- 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
- 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
- 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
- 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
- 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
- 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
- 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
- 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
- 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
- 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
- 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
- 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
- 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
- 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
- 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
- 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
- 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
- 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
- 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
- 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
- 0x2d02ef8dL};
-
-/* Return a 32-bit CRC of the contents of the buffer. */
-
-uint32_t crc32_buffer(const unsigned char *s, size_t len) {
- uint32_t ret;
-
- ret = 0;
- for (size_t i = 0; i < len; i++)
- ret = crc32_tab[(ret ^ s[i]) & 0xff] ^ (ret >> 8);
- return ret;
-}
+++ /dev/null
-/**
- * collectd - src/utils_crc32.h
- * Copyright (C) 2014 Pierre-Yves Ritschard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Pierre-Yves Ritschard <pyr at spootnik.org>
- */
-
-#ifndef UTILS_CRC32_H
-#define UTILS_CRC32_H 1
-
-uint32_t crc32_buffer(const unsigned char *, size_t);
-
-#endif
+++ /dev/null
-/**
- * collectd - src/utils_curl_stats.c
- * Copyright (C) 2015 Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sebastian Harl <sh@tokkee.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "utils_curl_stats.h"
-
-#include <stdbool.h>
-#include <stddef.h>
-
-struct curl_stats_s {
- bool total_time;
- bool namelookup_time;
- bool connect_time;
- bool pretransfer_time;
- bool size_upload;
- bool size_download;
- bool speed_download;
- bool speed_upload;
- bool header_size;
- bool request_size;
- bool content_length_download;
- bool content_length_upload;
- bool starttransfer_time;
- bool redirect_time;
- bool redirect_count;
- bool num_connects;
- bool appconnect_time;
-};
-
-/*
- * Private functions
- */
-
-static int dispatch_gauge(CURL *curl, CURLINFO info, value_list_t *vl) {
- CURLcode code;
- value_t v;
-
- code = curl_easy_getinfo(curl, info, &v.gauge);
- if (code != CURLE_OK)
- return -1;
-
- vl->values = &v;
- vl->values_len = 1;
-
- return plugin_dispatch_values(vl);
-} /* dispatch_gauge */
-
-/* dispatch a speed, in bytes/second */
-static int dispatch_speed(CURL *curl, CURLINFO info, value_list_t *vl) {
- CURLcode code;
- value_t v;
-
- code = curl_easy_getinfo(curl, info, &v.gauge);
- if (code != CURLE_OK)
- return -1;
-
- v.gauge *= 8;
-
- vl->values = &v;
- vl->values_len = 1;
-
- return plugin_dispatch_values(vl);
-} /* dispatch_speed */
-
-/* dispatch a size/count, reported as a long value */
-static int dispatch_size(CURL *curl, CURLINFO info, value_list_t *vl) {
- CURLcode code;
- value_t v;
- long raw;
-
- code = curl_easy_getinfo(curl, info, &raw);
- if (code != CURLE_OK)
- return -1;
-
- v.gauge = (double)raw;
-
- vl->values = &v;
- vl->values_len = 1;
-
- return plugin_dispatch_values(vl);
-} /* dispatch_size */
-
-static struct {
- const char *name;
- const char *config_key;
- size_t offset;
-
- int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
- const char *type;
- CURLINFO info;
-} field_specs[] = {
-#define SPEC(name, config_key, dispatcher, type, info) \
- { #name, config_key, offsetof(curl_stats_t, name), dispatcher, type, info }
-
- SPEC(total_time, "TotalTime", dispatch_gauge, "duration",
- CURLINFO_TOTAL_TIME),
- SPEC(namelookup_time, "NamelookupTime", dispatch_gauge, "duration",
- CURLINFO_NAMELOOKUP_TIME),
- SPEC(connect_time, "ConnectTime", dispatch_gauge, "duration",
- CURLINFO_CONNECT_TIME),
- SPEC(pretransfer_time, "PretransferTime", dispatch_gauge, "duration",
- CURLINFO_PRETRANSFER_TIME),
- SPEC(size_upload, "SizeUpload", dispatch_gauge, "bytes",
- CURLINFO_SIZE_UPLOAD),
- SPEC(size_download, "SizeDownload", dispatch_gauge, "bytes",
- CURLINFO_SIZE_DOWNLOAD),
- SPEC(speed_download, "SpeedDownload", dispatch_speed, "bitrate",
- CURLINFO_SPEED_DOWNLOAD),
- SPEC(speed_upload, "SpeedUpload", dispatch_speed, "bitrate",
- CURLINFO_SPEED_UPLOAD),
- SPEC(header_size, "HeaderSize", dispatch_size, "bytes",
- CURLINFO_HEADER_SIZE),
- SPEC(request_size, "RequestSize", dispatch_size, "bytes",
- CURLINFO_REQUEST_SIZE),
- SPEC(content_length_download, "ContentLengthDownload", dispatch_gauge,
- "bytes", CURLINFO_CONTENT_LENGTH_DOWNLOAD),
- SPEC(content_length_upload, "ContentLengthUpload", dispatch_gauge, "bytes",
- CURLINFO_CONTENT_LENGTH_UPLOAD),
- SPEC(starttransfer_time, "StarttransferTime", dispatch_gauge, "duration",
- CURLINFO_STARTTRANSFER_TIME),
- SPEC(redirect_time, "RedirectTime", dispatch_gauge, "duration",
- CURLINFO_REDIRECT_TIME),
- SPEC(redirect_count, "RedirectCount", dispatch_size, "count",
- CURLINFO_REDIRECT_COUNT),
- SPEC(num_connects, "NumConnects", dispatch_size, "count",
- CURLINFO_NUM_CONNECTS),
-#ifdef HAVE_CURLINFO_APPCONNECT_TIME
- SPEC(appconnect_time, "AppconnectTime", dispatch_gauge, "duration",
- CURLINFO_APPCONNECT_TIME),
-#endif
-
-#undef SPEC
-};
-
-static void enable_field(curl_stats_t *s, size_t offset) {
- *(bool *)((char *)s + offset) = true;
-} /* enable_field */
-
-static bool field_enabled(curl_stats_t *s, size_t offset) {
- return *(bool *)((char *)s + offset);
-} /* field_enabled */
-
-/*
- * Public API
- */
-curl_stats_t *curl_stats_from_config(oconfig_item_t *ci) {
- curl_stats_t *s;
-
- if (ci == NULL)
- return NULL;
-
- s = calloc(1, sizeof(*s));
- if (s == NULL)
- return NULL;
-
- for (int i = 0; i < ci->children_num; ++i) {
- oconfig_item_t *c = ci->children + i;
- size_t field;
-
- bool enabled = 0;
-
- for (field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) {
- if (!strcasecmp(c->key, field_specs[field].config_key))
- break;
- if (!strcasecmp(c->key, field_specs[field].name))
- break;
- }
- if (field >= STATIC_ARRAY_SIZE(field_specs)) {
- ERROR("curl stats: Unknown field name %s", c->key);
- free(s);
- return NULL;
- }
-
- if (cf_util_get_boolean(c, &enabled) != 0) {
- free(s);
- return NULL;
- }
- if (enabled)
- enable_field(s, field_specs[field].offset);
- }
-
- return s;
-} /* curl_stats_from_config */
-
-void curl_stats_destroy(curl_stats_t *s) {
- if (s != NULL)
- free(s);
-} /* curl_stats_destroy */
-
-int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname,
- const char *plugin, const char *plugin_instance) {
- value_list_t vl = VALUE_LIST_INIT;
-
- if (s == NULL)
- return 0;
- if ((curl == NULL) || (plugin == NULL)) {
- ERROR("curl stats: dispatch() called with missing arguments "
- "(curl=%p; plugin=%s)",
- curl, plugin == NULL ? "<NULL>" : plugin);
- return -1;
- }
-
- if (hostname != NULL)
- sstrncpy(vl.host, hostname, sizeof(vl.host));
- sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
- if (plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
-
- for (size_t field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) {
- int status;
-
- if (!field_enabled(s, field_specs[field].offset))
- continue;
-
- sstrncpy(vl.type, field_specs[field].type, sizeof(vl.type));
- sstrncpy(vl.type_instance, field_specs[field].name,
- sizeof(vl.type_instance));
-
- vl.values = NULL;
- vl.values_len = 0;
- status = field_specs[field].dispatcher(curl, field_specs[field].info, &vl);
- if (status < 0)
- return status;
- }
-
- return 0;
-} /* curl_stats_dispatch */
+++ /dev/null
-/**
- * collectd - src/utils_curl_stats.h
- * Copyright (C) 2015 Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sebastian Harl <sh@tokkee.org>
- **/
-
-#ifndef UTILS_CURL_STATS_H
-#define UTILS_CURL_STATS_H 1
-
-#include "plugin.h"
-
-#include <curl/curl.h>
-
-struct curl_stats_s;
-typedef struct curl_stats_s curl_stats_t;
-
-/*
- * curl_stats_from_config allocates and constructs a cURL statistics object
- * from the specified configuration which is expected to be a single block of
- * boolean options named after cURL information fields. The boolean value
- * indicates whether to collect the respective information.
- *
- * See http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
- */
-curl_stats_t *curl_stats_from_config(oconfig_item_t *ci);
-
-void curl_stats_destroy(curl_stats_t *s);
-
-/*
- * curl_stats_dispatch dispatches performance values from the the specified
- * cURL session to the daemon.
- */
-int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname,
- const char *plugin, const char *plugin_instance);
-
-#endif /* UTILS_CURL_STATS_H */
+++ /dev/null
-/**
- * collectd - src/utils_db_query.c
- * Copyright (C) 2008,2009 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_db_query.h"
-
-/*
- * Data types
- */
-struct udb_result_s; /* {{{ */
-typedef struct udb_result_s udb_result_t;
-struct udb_result_s {
- char *type;
- char *instance_prefix;
- char **instances;
- size_t instances_num;
- char **values;
- size_t values_num;
- char **metadata;
- size_t metadata_num;
-
- udb_result_t *next;
-}; /* }}} */
-
-struct udb_query_s /* {{{ */
-{
- char *name;
- char *statement;
- void *user_data;
- char *plugin_instance_from;
-
- unsigned int min_version;
- unsigned int max_version;
-
- udb_result_t *results;
-}; /* }}} */
-
-struct udb_result_preparation_area_s /* {{{ */
-{
- const data_set_t *ds;
- size_t *instances_pos;
- size_t *values_pos;
- size_t *metadata_pos;
- char **instances_buffer;
- char **values_buffer;
- char **metadata_buffer;
- char *plugin_instance;
-
- struct udb_result_preparation_area_s *next;
-}; /* }}} */
-typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
-
-struct udb_query_preparation_area_s /* {{{ */
-{
- size_t column_num;
- size_t plugin_instance_pos;
- char *host;
- char *plugin;
- char *db_name;
-
- udb_result_preparation_area_t *result_prep_areas;
-}; /* }}} */
-
-/*
- * Config Private functions
- */
-static int udb_config_add_string(char ***ret_array, /* {{{ */
- size_t *ret_array_len, oconfig_item_t *ci) {
- char **array;
- size_t array_len;
-
- if (ci->values_num < 1) {
- P_WARNING("The `%s' config option "
- "needs at least one argument.",
- ci->key);
- return -1;
- }
-
- for (int i = 0; i < ci->values_num; i++) {
- if (ci->values[i].type != OCONFIG_TYPE_STRING) {
- P_WARNING("Argument %i to the `%s' option "
- "is not a string.",
- i + 1, ci->key);
- return -1;
- }
- }
-
- array_len = *ret_array_len;
- array = realloc(*ret_array, sizeof(char *) * (array_len + ci->values_num));
- if (array == NULL) {
- P_ERROR("udb_config_add_string: realloc failed.");
- return -1;
- }
- *ret_array = array;
-
- for (int i = 0; i < ci->values_num; i++) {
- array[array_len] = strdup(ci->values[i].value.string);
- if (array[array_len] == NULL) {
- P_ERROR("udb_config_add_string: strdup failed.");
- *ret_array_len = array_len;
- return -1;
- }
- array_len++;
- }
-
- *ret_array_len = array_len;
- return 0;
-} /* }}} int udb_config_add_string */
-
-static int udb_config_set_uint(unsigned int *ret_value, /* {{{ */
- oconfig_item_t *ci) {
-
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
- P_WARNING("The `%s' config option "
- "needs exactly one numeric argument.",
- ci->key);
- return -1;
- }
-
- double tmp = ci->values[0].value.number;
- if ((tmp < 0.0) || (tmp > ((double)UINT_MAX))) {
- P_WARNING("The value given for the `%s` option is out of range.", ci->key);
- return -ERANGE;
- }
-
- *ret_value = (unsigned int)(tmp + .5);
- return 0;
-} /* }}} int udb_config_set_uint */
-
-/*
- * Result private functions
- */
-static int udb_result_submit(udb_result_t *r, /* {{{ */
- udb_result_preparation_area_t *r_area,
- udb_query_t const *q,
- udb_query_preparation_area_t *q_area) {
- value_list_t vl = VALUE_LIST_INIT;
-
- assert(r != NULL);
- assert(r_area->ds != NULL);
- assert(((size_t)r_area->ds->ds_num) == r->values_num);
- assert(r->values_num > 0);
-
- vl.values = calloc(r->values_num, sizeof(*vl.values));
- if (vl.values == NULL) {
- P_ERROR("udb_result_submit: calloc failed.");
- return -1;
- }
- vl.values_len = r_area->ds->ds_num;
-
- for (size_t i = 0; i < r->values_num; i++) {
- char *value_str = r_area->values_buffer[i];
-
- if (0 != parse_value(value_str, &vl.values[i], r_area->ds->ds[i].type)) {
- P_ERROR("udb_result_submit: Parsing `%s' as %s failed.", value_str,
- DS_TYPE_TO_STRING(r_area->ds->ds[i].type));
- errno = EINVAL;
- free(vl.values);
- return -1;
- }
- }
-
- sstrncpy(vl.host, q_area->host, sizeof(vl.host));
- sstrncpy(vl.plugin, q_area->plugin, sizeof(vl.plugin));
- sstrncpy(vl.type, r->type, sizeof(vl.type));
-
- /* Set vl.plugin_instance */
- if (q->plugin_instance_from != NULL) {
- sstrncpy(vl.plugin_instance, r_area->plugin_instance,
- sizeof(vl.plugin_instance));
- } else {
- sstrncpy(vl.plugin_instance, q_area->db_name, sizeof(vl.plugin_instance));
- }
-
- /* Set vl.type_instance {{{ */
- if (r->instances_num == 0) {
- if (r->instance_prefix == NULL)
- vl.type_instance[0] = 0;
- else
- sstrncpy(vl.type_instance, r->instance_prefix, sizeof(vl.type_instance));
- } else /* if ((r->instances_num > 0) */
- {
- if (r->instance_prefix == NULL) {
- int status = strjoin(vl.type_instance, sizeof(vl.type_instance),
- r_area->instances_buffer, r->instances_num, "-");
- if (status < 0) {
- P_ERROR(
- "udb_result_submit: creating type_instance failed with status %d.",
- status);
- return status;
- }
- } else {
- char tmp[DATA_MAX_NAME_LEN];
-
- int status = strjoin(tmp, sizeof(tmp), r_area->instances_buffer,
- r->instances_num, "-");
- if (status < 0) {
- P_ERROR(
- "udb_result_submit: creating type_instance failed with status %d.",
- status);
- return status;
- }
- tmp[sizeof(tmp) - 1] = '\0';
-
- snprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s",
- r->instance_prefix, tmp);
- }
- }
- vl.type_instance[sizeof(vl.type_instance) - 1] = '\0';
- /* }}} */
-
- /* Annotate meta data. {{{ */
- if (r->metadata_num > 0) {
- vl.meta = meta_data_create();
- if (vl.meta == NULL) {
- P_ERROR("udb_result_submit: meta_data_create failed.");
- free(vl.values);
- return -ENOMEM;
- }
-
- for (size_t i = 0; i < r->metadata_num; i++) {
- int status = meta_data_add_string(vl.meta, r->metadata[i],
- r_area->metadata_buffer[i]);
- if (status != 0) {
- P_ERROR("udb_result_submit: meta_data_add_string failed.");
- meta_data_destroy(vl.meta);
- vl.meta = NULL;
- free(vl.values);
- return status;
- }
- }
- }
- /* }}} */
-
- plugin_dispatch_values(&vl);
-
- if (r->metadata_num > 0) {
- meta_data_destroy(vl.meta);
- vl.meta = NULL;
- }
- sfree(vl.values);
- return 0;
-} /* }}} void udb_result_submit */
-
-static void udb_result_finish_result(udb_result_t const *r, /* {{{ */
- udb_result_preparation_area_t *prep_area) {
- if ((r == NULL) || (prep_area == NULL))
- return;
-
- prep_area->ds = NULL;
- sfree(prep_area->instances_pos);
- sfree(prep_area->values_pos);
- sfree(prep_area->metadata_pos);
- sfree(prep_area->instances_buffer);
- sfree(prep_area->values_buffer);
- sfree(prep_area->metadata_buffer);
-} /* }}} void udb_result_finish_result */
-
-static int udb_result_handle_result(udb_result_t *r, /* {{{ */
- udb_query_preparation_area_t *q_area,
- udb_result_preparation_area_t *r_area,
- udb_query_t const *q,
- char **column_values) {
- assert(r && q_area && r_area);
-
- for (size_t i = 0; i < r->instances_num; i++)
- r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
-
- for (size_t i = 0; i < r->values_num; i++)
- r_area->values_buffer[i] = column_values[r_area->values_pos[i]];
-
- for (size_t i = 0; i < r->metadata_num; i++)
- r_area->metadata_buffer[i] = column_values[r_area->metadata_pos[i]];
-
- if (q->plugin_instance_from)
- r_area->plugin_instance = column_values[q_area->plugin_instance_pos];
-
- return udb_result_submit(r, r_area, q, q_area);
-} /* }}} int udb_result_handle_result */
-
-static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */
- udb_result_preparation_area_t *prep_area,
- char **column_names, size_t column_num) {
- if ((r == NULL) || (prep_area == NULL))
- return -EINVAL;
-
-#if COLLECT_DEBUG
- assert(prep_area->ds == NULL);
- assert(prep_area->instances_pos == NULL);
- assert(prep_area->values_pos == NULL);
- assert(prep_area->metadata_pos == NULL);
- assert(prep_area->instances_buffer == NULL);
- assert(prep_area->values_buffer == NULL);
- assert(prep_area->metadata_buffer == NULL);
-#endif
-
-#define BAIL_OUT(status) \
- udb_result_finish_result(r, prep_area); \
- return (status)
-
- /* Read `ds' and check number of values {{{ */
- prep_area->ds = plugin_get_ds(r->type);
- if (prep_area->ds == NULL) {
- P_ERROR("udb_result_prepare_result: Type `%s' is not "
- "known by the daemon. See types.db(5) for details.",
- r->type);
- BAIL_OUT(-1);
- }
-
- if (prep_area->ds->ds_num != r->values_num) {
- P_ERROR("udb_result_prepare_result: The type `%s' "
- "requires exactly %" PRIsz
- " value%s, but the configuration specifies %" PRIsz ".",
- r->type, prep_area->ds->ds_num,
- (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num);
- BAIL_OUT(-1);
- }
- /* }}} */
-
- /* Allocate r->instances_pos, r->values_pos, r->metadata_post,
- * r->instances_buffer, r->values_buffer, and r->metadata_buffer {{{ */
- if (r->instances_num > 0) {
- prep_area->instances_pos =
- calloc(r->instances_num, sizeof(*prep_area->instances_pos));
- if (prep_area->instances_pos == NULL) {
- P_ERROR("udb_result_prepare_result: calloc failed.");
- BAIL_OUT(-ENOMEM);
- }
-
- prep_area->instances_buffer =
- calloc(r->instances_num, sizeof(*prep_area->instances_buffer));
- if (prep_area->instances_buffer == NULL) {
- P_ERROR("udb_result_prepare_result: calloc failed.");
- BAIL_OUT(-ENOMEM);
- }
- } /* if (r->instances_num > 0) */
-
- prep_area->values_pos = calloc(r->values_num, sizeof(*prep_area->values_pos));
- if (prep_area->values_pos == NULL) {
- P_ERROR("udb_result_prepare_result: calloc failed.");
- BAIL_OUT(-ENOMEM);
- }
-
- prep_area->values_buffer =
- calloc(r->values_num, sizeof(*prep_area->values_buffer));
- if (prep_area->values_buffer == NULL) {
- P_ERROR("udb_result_prepare_result: calloc failed.");
- BAIL_OUT(-ENOMEM);
- }
-
- prep_area->metadata_pos =
- calloc(r->metadata_num, sizeof(*prep_area->metadata_pos));
- if (prep_area->metadata_pos == NULL) {
- P_ERROR("udb_result_prepare_result: calloc failed.");
- BAIL_OUT(-ENOMEM);
- }
-
- prep_area->metadata_buffer =
- calloc(r->metadata_num, sizeof(*prep_area->metadata_buffer));
- if (prep_area->metadata_buffer == NULL) {
- P_ERROR("udb_result_prepare_result: calloc failed.");
- BAIL_OUT(-ENOMEM);
- }
-
- /* }}} */
-
- /* Determine the position of the plugin instance column {{{ */
- for (size_t i = 0; i < r->instances_num; i++) {
- size_t j;
-
- for (j = 0; j < column_num; j++) {
- if (strcasecmp(r->instances[i], column_names[j]) == 0) {
- prep_area->instances_pos[i] = j;
- break;
- }
- }
-
- if (j >= column_num) {
- P_ERROR("udb_result_prepare_result: "
- "Column `%s' could not be found.",
- r->instances[i]);
- BAIL_OUT(-ENOENT);
- }
- } /* }}} for (i = 0; i < r->instances_num; i++) */
-
- /* Determine the position of the value columns {{{ */
- for (size_t i = 0; i < r->values_num; i++) {
- size_t j;
-
- for (j = 0; j < column_num; j++) {
- if (strcasecmp(r->values[i], column_names[j]) == 0) {
- prep_area->values_pos[i] = j;
- break;
- }
- }
-
- if (j >= column_num) {
- P_ERROR("udb_result_prepare_result: "
- "Column `%s' could not be found.",
- r->values[i]);
- BAIL_OUT(-ENOENT);
- }
- } /* }}} for (i = 0; i < r->values_num; i++) */
-
- /* Determine the position of the metadata columns {{{ */
- for (size_t i = 0; i < r->metadata_num; i++) {
- size_t j;
-
- for (j = 0; j < column_num; j++) {
- if (strcasecmp(r->metadata[i], column_names[j]) == 0) {
- prep_area->metadata_pos[i] = j;
- break;
- }
- }
-
- if (j >= column_num) {
- P_ERROR("udb_result_prepare_result: "
- "Metadata column `%s' could not be found.",
- r->values[i]);
- BAIL_OUT(-ENOENT);
- }
- } /* }}} for (i = 0; i < r->metadata_num; i++) */
-
-#undef BAIL_OUT
- return 0;
-} /* }}} int udb_result_prepare_result */
-
-static void udb_result_free(udb_result_t *r) /* {{{ */
-{
- if (r == NULL)
- return;
-
- sfree(r->type);
- sfree(r->instance_prefix);
-
- for (size_t i = 0; i < r->instances_num; i++)
- sfree(r->instances[i]);
- sfree(r->instances);
-
- for (size_t i = 0; i < r->values_num; i++)
- sfree(r->values[i]);
- sfree(r->values);
-
- for (size_t i = 0; i < r->metadata_num; i++)
- sfree(r->metadata[i]);
- sfree(r->metadata);
-
- udb_result_free(r->next);
-
- sfree(r);
-} /* }}} void udb_result_free */
-
-static int udb_result_create(const char *query_name, /* {{{ */
- udb_result_t **r_head, oconfig_item_t *ci) {
- udb_result_t *r;
- int status;
-
- if (ci->values_num != 0) {
- P_WARNING("The `Result' block doesn't accept "
- "any arguments. Ignoring %i argument%s.",
- ci->values_num, (ci->values_num == 1) ? "" : "s");
- }
-
- r = calloc(1, sizeof(*r));
- if (r == NULL) {
- P_ERROR("udb_result_create: calloc failed.");
- return -1;
- }
- r->type = NULL;
- r->instance_prefix = NULL;
- r->instances = NULL;
- r->values = NULL;
- r->metadata = NULL;
- r->next = NULL;
-
- /* Fill the `udb_result_t' structure.. */
- status = 0;
- for (int 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, &r->type);
- else if (strcasecmp("InstancePrefix", child->key) == 0)
- status = cf_util_get_string(child, &r->instance_prefix);
- else if (strcasecmp("InstancesFrom", child->key) == 0)
- status = udb_config_add_string(&r->instances, &r->instances_num, child);
- else if (strcasecmp("ValuesFrom", child->key) == 0)
- status = udb_config_add_string(&r->values, &r->values_num, child);
- else if (strcasecmp("MetadataFrom", child->key) == 0)
- status = udb_config_add_string(&r->metadata, &r->metadata_num, child);
- else {
- P_WARNING("Query `%s': Option `%s' not allowed here.", query_name,
- child->key);
- status = -1;
- }
-
- if (status != 0)
- break;
- }
-
- /* Check that all necessary options have been given. */
- while (status == 0) {
- if (r->type == NULL) {
- P_WARNING("udb_result_create: `Type' not given for "
- "result in query `%s'",
- query_name);
- status = -1;
- }
- if (r->values == NULL) {
- P_WARNING("udb_result_create: `ValuesFrom' not given for "
- "result in query `%s'",
- query_name);
- status = -1;
- }
-
- break;
- } /* while (status == 0) */
-
- if (status != 0) {
- udb_result_free(r);
- return -1;
- }
-
- /* If all went well, add this result to the list of results. */
- if (*r_head == NULL) {
- *r_head = r;
- } else {
- udb_result_t *last;
-
- last = *r_head;
- while (last->next != NULL)
- last = last->next;
-
- last->next = r;
- }
-
- return 0;
-} /* }}} int udb_result_create */
-
-/*
- * Query private functions
- */
-static void udb_query_free_one(udb_query_t *q) /* {{{ */
-{
- if (q == NULL)
- return;
-
- sfree(q->name);
- sfree(q->statement);
- sfree(q->plugin_instance_from);
-
- udb_result_free(q->results);
-
- sfree(q);
-} /* }}} void udb_query_free_one */
-
-/*
- * Query public functions
- */
-int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
- size_t *ret_query_list_len, oconfig_item_t *ci,
- udb_query_create_callback_t cb) {
- udb_query_t **query_list;
- size_t query_list_len;
-
- udb_query_t *q;
- int status;
-
- if ((ret_query_list == NULL) || (ret_query_list_len == NULL))
- return -EINVAL;
- query_list = *ret_query_list;
- query_list_len = *ret_query_list_len;
-
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
- P_WARNING("udb_result_create: The `Query' block "
- "needs exactly one string argument.");
- return -1;
- }
-
- q = calloc(1, sizeof(*q));
- if (q == NULL) {
- P_ERROR("udb_query_create: calloc failed.");
- return -1;
- }
- q->min_version = 0;
- q->max_version = UINT_MAX;
- q->statement = NULL;
- q->results = NULL;
- q->plugin_instance_from = NULL;
-
- status = cf_util_get_string(ci, &q->name);
- if (status != 0) {
- sfree(q);
- return status;
- }
-
- /* Fill the `udb_query_t' structure.. */
- for (int i = 0; i < ci->children_num; i++) {
- oconfig_item_t *child = ci->children + i;
-
- if (strcasecmp("Statement", child->key) == 0)
- status = cf_util_get_string(child, &q->statement);
- else if (strcasecmp("Result", child->key) == 0)
- status = udb_result_create(q->name, &q->results, child);
- else if (strcasecmp("MinVersion", child->key) == 0)
- status = udb_config_set_uint(&q->min_version, child);
- else if (strcasecmp("MaxVersion", child->key) == 0)
- status = udb_config_set_uint(&q->max_version, child);
- else if (strcasecmp("PluginInstanceFrom", child->key) == 0)
- status = cf_util_get_string(child, &q->plugin_instance_from);
-
- /* Call custom callbacks */
- else if (cb != NULL) {
- status = (*cb)(q, child);
- if (status != 0) {
- P_WARNING("The configuration callback failed "
- "to handle `%s'.",
- child->key);
- }
- } else {
- P_WARNING("Query `%s': Option `%s' not allowed here.", q->name,
- child->key);
- status = -1;
- }
-
- if (status != 0)
- break;
- }
-
- /* Check that all necessary options have been given. */
- if (status == 0) {
- if (q->statement == NULL) {
- P_WARNING("Query `%s': No `Statement' given.", q->name);
- status = -1;
- }
- if (q->results == NULL) {
- P_WARNING("Query `%s': No (valid) `Result' block given.", q->name);
- status = -1;
- }
- } /* if (status == 0) */
-
- /* If all went well, add this query to the list of queries within the
- * database structure. */
- if (status == 0) {
- udb_query_t **temp;
-
- temp = realloc(query_list, sizeof(*query_list) * (query_list_len + 1));
- if (temp == NULL) {
- P_ERROR("udb_query_create: realloc failed");
- status = -1;
- } else {
- query_list = temp;
- query_list[query_list_len] = q;
- query_list_len++;
- }
- }
-
- if (status != 0) {
- udb_query_free_one(q);
- return -1;
- }
-
- *ret_query_list = query_list;
- *ret_query_list_len = query_list_len;
-
- return 0;
-} /* }}} int udb_query_create */
-
-void udb_query_free(udb_query_t **query_list, size_t query_list_len) /* {{{ */
-{
- if (query_list == NULL)
- return;
-
- for (size_t i = 0; i < query_list_len; i++)
- udb_query_free_one(query_list[i]);
-
- sfree(query_list);
-} /* }}} void udb_query_free */
-
-int udb_query_pick_from_list_by_name(const char *name, /* {{{ */
- udb_query_t **src_list,
- size_t src_list_len,
- udb_query_t ***dst_list,
- size_t *dst_list_len) {
- int num_added;
-
- if ((name == NULL) || (src_list == NULL) || (dst_list == NULL) ||
- (dst_list_len == NULL)) {
- P_ERROR("udb_query_pick_from_list_by_name: "
- "Invalid argument.");
- return -EINVAL;
- }
-
- num_added = 0;
- for (size_t i = 0; i < src_list_len; i++) {
- udb_query_t **tmp_list;
- size_t tmp_list_len;
-
- if (strcasecmp(name, src_list[i]->name) != 0)
- continue;
-
- tmp_list_len = *dst_list_len;
- tmp_list = realloc(*dst_list, (tmp_list_len + 1) * sizeof(udb_query_t *));
- if (tmp_list == NULL) {
- P_ERROR("udb_query_pick_from_list_by_name: realloc failed.");
- return -ENOMEM;
- }
-
- tmp_list[tmp_list_len] = src_list[i];
- tmp_list_len++;
-
- *dst_list = tmp_list;
- *dst_list_len = tmp_list_len;
-
- num_added++;
- } /* for (i = 0; i < src_list_len; i++) */
-
- if (num_added <= 0) {
- P_ERROR("Cannot find query `%s'. Make sure the <Query> "
- "block is above the database definition!",
- name);
- return -ENOENT;
- } else {
- DEBUG("Added %i versions of query `%s'.", num_added, name);
- }
-
- return 0;
-} /* }}} int udb_query_pick_from_list_by_name */
-
-int udb_query_pick_from_list(oconfig_item_t *ci, /* {{{ */
- udb_query_t **src_list, size_t src_list_len,
- udb_query_t ***dst_list, size_t *dst_list_len) {
- const char *name;
-
- if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) ||
- (dst_list_len == NULL)) {
- P_ERROR("udb_query_pick_from_list: "
- "Invalid argument.");
- return -EINVAL;
- }
-
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
- P_ERROR("The `%s' config option "
- "needs exactly one string argument.",
- ci->key);
- return -1;
- }
- name = ci->values[0].value.string;
-
- return udb_query_pick_from_list_by_name(name, src_list, src_list_len,
- dst_list, dst_list_len);
-} /* }}} int udb_query_pick_from_list */
-
-const char *udb_query_get_name(udb_query_t *q) /* {{{ */
-{
- if (q == NULL)
- return NULL;
-
- return q->name;
-} /* }}} const char *udb_query_get_name */
-
-const char *udb_query_get_statement(udb_query_t *q) /* {{{ */
-{
- if (q == NULL)
- return NULL;
-
- return q->statement;
-} /* }}} const char *udb_query_get_statement */
-
-void udb_query_set_user_data(udb_query_t *q, void *user_data) /* {{{ */
-{
- if (q == NULL)
- return;
-
- q->user_data = user_data;
-} /* }}} void udb_query_set_user_data */
-
-void *udb_query_get_user_data(udb_query_t *q) /* {{{ */
-{
- if (q == NULL)
- return NULL;
-
- return q->user_data;
-} /* }}} void *udb_query_get_user_data */
-
-int udb_query_check_version(udb_query_t *q, unsigned int version) /* {{{ */
-{
- if (q == NULL)
- return -EINVAL;
-
- if ((version < q->min_version) || (version > q->max_version))
- return 0;
-
- return 1;
-} /* }}} int udb_query_check_version */
-
-void udb_query_finish_result(udb_query_t const *q, /* {{{ */
- udb_query_preparation_area_t *prep_area) {
- udb_result_preparation_area_t *r_area;
- udb_result_t *r;
-
- if ((q == NULL) || (prep_area == NULL))
- return;
-
- prep_area->column_num = 0;
- sfree(prep_area->host);
- sfree(prep_area->plugin);
- sfree(prep_area->db_name);
-
- for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
- r = r->next, r_area = r_area->next) {
- /* this may happen during error conditions of the caller */
- if (r_area == NULL)
- break;
- udb_result_finish_result(r, r_area);
- }
-} /* }}} void udb_query_finish_result */
-
-int udb_query_handle_result(udb_query_t const *q, /* {{{ */
- udb_query_preparation_area_t *prep_area,
- char **column_values) {
- udb_result_preparation_area_t *r_area;
- udb_result_t *r;
- int success;
- int status;
-
- if ((q == NULL) || (prep_area == NULL))
- return -EINVAL;
-
- if ((prep_area->column_num < 1) || (prep_area->host == NULL) ||
- (prep_area->plugin == NULL) || (prep_area->db_name == NULL)) {
- P_ERROR("Query `%s': Query is not prepared; "
- "can't handle result.",
- q->name);
- return -EINVAL;
- }
-
-#if defined(COLLECT_DEBUG) && COLLECT_DEBUG /* {{{ */
- do {
- for (size_t i = 0; i < prep_area->column_num; i++) {
- DEBUG("udb_query_handle_result (%s, %s): "
- "column[%" PRIsz "] = %s;",
- prep_area->db_name, q->name, i, column_values[i]);
- }
- } while (0);
-#endif /* }}} */
-
- success = 0;
- for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
- r = r->next, r_area = r_area->next) {
- status = udb_result_handle_result(r, prep_area, r_area, q, column_values);
- if (status == 0)
- success++;
- }
-
- if (success == 0) {
- P_ERROR("udb_query_handle_result (%s, %s): "
- "All results failed.",
- prep_area->db_name, q->name);
- return -1;
- }
-
- return 0;
-} /* }}} int udb_query_handle_result */
-
-int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
- udb_query_preparation_area_t *prep_area,
- const char *host, const char *plugin,
- const char *db_name, char **column_names,
- size_t column_num) {
- udb_result_preparation_area_t *r_area;
- udb_result_t *r;
- int status;
-
- if ((q == NULL) || (prep_area == NULL))
- return -EINVAL;
-
-#if COLLECT_DEBUG
- assert(prep_area->column_num == 0);
- assert(prep_area->host == NULL);
- assert(prep_area->plugin == NULL);
- assert(prep_area->db_name == NULL);
-#endif
-
- prep_area->column_num = column_num;
- prep_area->host = strdup(host);
- prep_area->plugin = strdup(plugin);
- prep_area->db_name = strdup(db_name);
-
- if ((prep_area->host == NULL) || (prep_area->plugin == NULL) ||
- (prep_area->db_name == NULL)) {
- P_ERROR("Query `%s': Prepare failed: Out of memory.", q->name);
- udb_query_finish_result(q, prep_area);
- return -ENOMEM;
- }
-
-#if defined(COLLECT_DEBUG) && COLLECT_DEBUG
- do {
- for (size_t i = 0; i < column_num; i++) {
- DEBUG("udb_query_prepare_result: "
- "query = %s; column[%" PRIsz "] = %s;",
- q->name, i, column_names[i]);
- }
- } while (0);
-#endif
-
- /* Determine the position of the PluginInstance column {{{ */
- if (q->plugin_instance_from != NULL) {
- size_t i;
-
- for (i = 0; i < column_num; i++) {
- if (strcasecmp(q->plugin_instance_from, column_names[i]) == 0) {
- prep_area->plugin_instance_pos = i;
- break;
- }
- }
-
- if (i >= column_num) {
- P_ERROR("udb_query_prepare_result: "
- "Column `%s' from `PluginInstanceFrom' could not be found.",
- q->plugin_instance_from);
- udb_query_finish_result(q, prep_area);
- return -ENOENT;
- }
- }
- /* }}} */
-
- for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
- r = r->next, r_area = r_area->next) {
- if (!r_area) {
- P_ERROR("Query `%s': Invalid number of result "
- "preparation areas.",
- q->name);
- udb_query_finish_result(q, prep_area);
- return -EINVAL;
- }
-
- status = udb_result_prepare_result(r, r_area, column_names, column_num);
- if (status != 0) {
- udb_query_finish_result(q, prep_area);
- return status;
- }
- }
-
- return 0;
-} /* }}} int udb_query_prepare_result */
-
-udb_query_preparation_area_t *
-udb_query_allocate_preparation_area(udb_query_t *q) /* {{{ */
-{
- udb_query_preparation_area_t *q_area;
- udb_result_preparation_area_t **next_r_area;
- udb_result_t *r;
-
- q_area = calloc(1, sizeof(*q_area));
- if (q_area == NULL)
- return NULL;
-
- next_r_area = &q_area->result_prep_areas;
- for (r = q->results; r != NULL; r = r->next) {
- udb_result_preparation_area_t *r_area;
-
- r_area = calloc(1, sizeof(*r_area));
- if (r_area == NULL) {
- udb_result_preparation_area_t *a = q_area->result_prep_areas;
-
- while (a != NULL) {
- udb_result_preparation_area_t *next = a->next;
- sfree(a);
- a = next;
- }
-
- free(q_area);
- return NULL;
- }
-
- *next_r_area = r_area;
- next_r_area = &r_area->next;
- }
-
- return q_area;
-} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
-
-void udb_query_delete_preparation_area(
- udb_query_preparation_area_t *q_area) /* {{{ */
-{
- udb_result_preparation_area_t *r_area;
-
- if (q_area == NULL)
- return;
-
- r_area = q_area->result_prep_areas;
- while (r_area != NULL) {
- udb_result_preparation_area_t *area = r_area;
-
- r_area = r_area->next;
-
- sfree(area->instances_pos);
- sfree(area->values_pos);
- sfree(area->instances_buffer);
- sfree(area->values_buffer);
- free(area);
- }
-
- sfree(q_area->host);
- sfree(q_area->plugin);
- sfree(q_area->db_name);
-
- free(q_area);
-} /* }}} void udb_query_delete_preparation_area */
+++ /dev/null
-/**
- * collectd - src/utils_db_query.h
- * Copyright (C) 2008,2009 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_DB_QUERY_H
-#define UTILS_DB_QUERY_H 1
-
-/*
- * Data types
- */
-struct udb_query_s;
-typedef struct udb_query_s udb_query_t;
-
-struct udb_query_preparation_area_s;
-typedef struct udb_query_preparation_area_s udb_query_preparation_area_t;
-
-typedef int (*udb_query_create_callback_t)(udb_query_t *q, oconfig_item_t *ci);
-
-/*
- * Public functions
- */
-int udb_query_create(udb_query_t ***ret_query_list, size_t *ret_query_list_len,
- oconfig_item_t *ci, udb_query_create_callback_t cb);
-void udb_query_free(udb_query_t **query_list, size_t query_list_len);
-
-int udb_query_pick_from_list_by_name(const char *name, udb_query_t **src_list,
- size_t src_list_len,
- udb_query_t ***dst_list,
- size_t *dst_list_len);
-int udb_query_pick_from_list(oconfig_item_t *ci, udb_query_t **src_list,
- size_t src_list_len, udb_query_t ***dst_list,
- size_t *dst_list_len);
-
-const char *udb_query_get_name(udb_query_t *q);
-const char *udb_query_get_statement(udb_query_t *q);
-
-void udb_query_set_user_data(udb_query_t *q, void *user_data);
-void *udb_query_get_user_data(udb_query_t *q);
-
-/*
- * udb_query_check_version
- *
- * Returns 0 if the query is NOT suitable for `version' and >0 if the
- * query IS suitable.
- */
-int udb_query_check_version(udb_query_t *q, unsigned int version);
-
-int udb_query_prepare_result(udb_query_t const *q,
- udb_query_preparation_area_t *prep_area,
- const char *host, const char *plugin,
- const char *db_name, char **column_names,
- size_t column_num);
-int udb_query_handle_result(udb_query_t const *q,
- udb_query_preparation_area_t *prep_area,
- char **column_values);
-void udb_query_finish_result(udb_query_t const *q,
- udb_query_preparation_area_t *prep_area);
-
-udb_query_preparation_area_t *
-udb_query_allocate_preparation_area(udb_query_t *q);
-void udb_query_delete_preparation_area(udb_query_preparation_area_t *q_area);
-
-#endif /* UTILS_DB_QUERY_H */
+++ /dev/null
-/**
- * collectd - src/utils_deq.h
- * Copyright(c) 2017 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Andy Smith <ansmith@redhat.com>
- */
-
-#ifndef utils_deq_h
-#define utils_deq_h 1
-
-#include <assert.h>
-#include <memory.h>
-#include <stdlib.h>
-
-#define CT_ASSERT(exp) \
- { assert(exp); }
-
-#define NEW(t) (t *)malloc(sizeof(t))
-#define NEW_ARRAY(t, n) (t *)malloc(sizeof(t) * (n))
-#define NEW_PTR_ARRAY(t, n) (t **)malloc(sizeof(t *) * (n))
-
-#define ZERO(p) memset(p, 0, sizeof(*p))
-
-#define DEQ_DECLARE(i, d) \
- typedef struct { \
- i *head; \
- i *tail; \
- i *scratch; \
- size_t size; \
- } d
-
-#define DEQ_LINKS_N(n, t) \
- t *prev##n; \
- t *next##n
-#define DEQ_LINKS(t) DEQ_LINKS_N(, t)
-#define DEQ_EMPTY \
- { 0, 0, 0, 0 }
-
-#define DEQ_INIT(d) \
- do { \
- (d).head = 0; \
- (d).tail = 0; \
- (d).scratch = 0; \
- (d).size = 0; \
- } while (0)
-#define DEQ_IS_EMPTY(d) ((d).head == 0)
-#define DEQ_ITEM_INIT_N(n, i) \
- do { \
- (i)->next##n = 0; \
- (i)->prev##n = 0; \
- } while (0)
-#define DEQ_ITEM_INIT(i) DEQ_ITEM_INIT_N(, i)
-#define DEQ_HEAD(d) ((d).head)
-#define DEQ_TAIL(d) ((d).tail)
-#define DEQ_SIZE(d) ((d).size)
-#define DEQ_NEXT_N(n, i) (i)->next##n
-#define DEQ_NEXT(i) DEQ_NEXT_N(, i)
-#define DEQ_PREV_N(n, i) (i)->prev##n
-#define DEQ_PREV(i) DEQ_PREV_N(, i)
-#define DEQ_MOVE(d1, d2) \
- do { \
- d2 = d1; \
- DEQ_INIT(d1); \
- } while (0)
-/**
- *@pre ptr points to first element of deq
- *@post ptr points to first element of deq that passes test, or 0. Test should
- *involve ptr.
- */
-#define DEQ_FIND_N(n, ptr, test) \
- while ((ptr) && !(test)) \
- ptr = DEQ_NEXT_N(n, ptr);
-#define DEQ_FIND(ptr, test) DEQ_FIND_N(, ptr, test)
-
-#define DEQ_INSERT_HEAD_N(n, d, i) \
- do { \
- CT_ASSERT((i)->next##n == 0); \
- CT_ASSERT((i)->prev##n == 0); \
- if ((d).head) { \
- (i)->next##n = (d).head; \
- (d).head->prev##n = i; \
- } else { \
- (d).tail = i; \
- (i)->next##n = 0; \
- CT_ASSERT((d).size == 0); \
- } \
- (i)->prev##n = 0; \
- (d).head = i; \
- (d).size++; \
- } while (0)
-#define DEQ_INSERT_HEAD(d, i) DEQ_INSERT_HEAD_N(, d, i)
-
-#define DEQ_INSERT_TAIL_N(n, d, i) \
- do { \
- CT_ASSERT((i)->next##n == 0); \
- CT_ASSERT((i)->prev##n == 0); \
- if ((d).tail) { \
- (i)->prev##n = (d).tail; \
- (d).tail->next##n = i; \
- } else { \
- (d).head = i; \
- (i)->prev##n = 0; \
- CT_ASSERT((d).size == 0); \
- } \
- (i)->next##n = 0; \
- (d).tail = i; \
- (d).size++; \
- } while (0)
-#define DEQ_INSERT_TAIL(d, i) DEQ_INSERT_TAIL_N(, d, i)
-
-#define DEQ_REMOVE_HEAD_N(n, d) \
- do { \
- CT_ASSERT((d).head); \
- if ((d).head) { \
- (d).scratch = (d).head; \
- (d).head = (d).head->next##n; \
- if ((d).head == 0) { \
- (d).tail = 0; \
- CT_ASSERT((d).size == 1); \
- } else \
- (d).head->prev##n = 0; \
- (d).size--; \
- (d).scratch->next##n = 0; \
- (d).scratch->prev##n = 0; \
- } \
- } while (0)
-#define DEQ_REMOVE_HEAD(d) DEQ_REMOVE_HEAD_N(, d)
-
-#define DEQ_REMOVE_TAIL_N(n, d) \
- do { \
- CT_ASSERT((d).tail); \
- if ((d).tail) { \
- (d).scratch = (d).tail; \
- (d).tail = (d).tail->prev##n; \
- if ((d).tail == 0) { \
- (d).head = 0; \
- CT_ASSERT((d).size == 1); \
- } else \
- (d).tail->next##n = 0; \
- (d).size--; \
- (d).scratch->next##n = 0; \
- (d).scratch->prev##n = 0; \
- } \
- } while (0)
-#define DEQ_REMOVE_TAIL(d) DEQ_REMOVE_TAIL_N(, d)
-
-#define DEQ_INSERT_AFTER_N(n, d, i, a) \
- do { \
- CT_ASSERT((i)->next##n == 0); \
- CT_ASSERT((i)->prev##n == 0); \
- CT_ASSERT(a); \
- if ((a)->next##n) \
- (a)->next##n->prev##n = (i); \
- else \
- (d).tail = (i); \
- (i)->next##n = (a)->next##n; \
- (i)->prev##n = (a); \
- (a)->next##n = (i); \
- (d).size++; \
- } while (0)
-#define DEQ_INSERT_AFTER(d, i, a) DEQ_INSERT_AFTER_N(, d, i, a)
-
-#define DEQ_REMOVE_N(n, d, i) \
- do { \
- if ((i)->next##n) \
- (i)->next##n->prev##n = (i)->prev##n; \
- else \
- (d).tail = (i)->prev##n; \
- if ((i)->prev##n) \
- (i)->prev##n->next##n = (i)->next##n; \
- else \
- (d).head = (i)->next##n; \
- CT_ASSERT((d).size > 0); \
- (d).size--; \
- (i)->next##n = 0; \
- (i)->prev##n = 0; \
- CT_ASSERT((d).size || (!(d).head && !(d).tail)); \
- } while (0)
-#define DEQ_REMOVE(d, i) DEQ_REMOVE_N(, d, i)
-
-#define DEQ_APPEND_N(n, d1, d2) \
- do { \
- if (!(d1).head) \
- (d1) = (d2); \
- else if ((d2).head) { \
- (d1).tail->next##n = (d2).head; \
- (d2).head->prev##n = (d1).tail; \
- (d1).tail = (d2).tail; \
- (d1).size += (d2).size; \
- } \
- DEQ_INIT(d2); \
- } while (0)
-#define DEQ_APPEND(d1, d2) DEQ_APPEND_N(, d1, d2)
-
-#endif
+++ /dev/null
-/*
- * collectd - src/utils_dns.c
- * Copyright (C) 2006 Florian octo Forster
- * Copyright (C) 2002 The Measurement Factory, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of The Measurement Factory nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Authors:
- * The Measurement Factory, Inc. <http://www.measurement-factory.com/>
- * Florian octo Forster <octo at collectd.org>
- */
-
-#define _DEFAULT_SOURCE
-#define _BSD_SOURCE
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#if HAVE_NET_IF_ARP_H
-#include <net/if_arp.h>
-#endif
-#if HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#if HAVE_NET_PPP_DEFS_H
-#include <net/ppp_defs.h>
-#endif
-#if HAVE_NET_IF_PPP_H
-#include <net/if_ppp.h>
-#endif
-
-#if HAVE_NETINET_IN_SYSTM_H
-#include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP6_H
-#include <netinet/ip6.h>
-#endif
-#if HAVE_NETINET_IF_ETHER_H
-#include <netinet/if_ether.h>
-#endif
-#if HAVE_NETINET_IP_H
-#include <netinet/ip.h>
-#endif
-#ifdef HAVE_NETINET_IP_VAR_H
-#include <netinet/ip_var.h>
-#endif
-#if HAVE_NETINET_UDP_H
-#include <netinet/udp.h>
-#endif
-
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#if HAVE_ARPA_NAMESER_H
-#include <arpa/nameser.h>
-#endif
-#if HAVE_ARPA_NAMESER_COMPAT_H
-#include <arpa/nameser_compat.h>
-#endif
-
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#if HAVE_PCAP_H
-#include <pcap.h>
-#endif
-
-#define PCAP_SNAPLEN 1460
-#ifndef ETHER_HDR_LEN
-#define ETHER_ADDR_LEN 6
-#define ETHER_TYPE_LEN 2
-#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
-#endif
-#ifndef ETHERTYPE_8021Q
-#define ETHERTYPE_8021Q 0x8100
-#endif
-#ifndef ETHERTYPE_IPV6
-#define ETHERTYPE_IPV6 0x86DD
-#endif
-
-#ifndef PPP_ADDRESS_VAL
-#define PPP_ADDRESS_VAL 0xff /* The address byte value */
-#endif
-#ifndef PPP_CONTROL_VAL
-#define PPP_CONTROL_VAL 0x03 /* The control byte value */
-#endif
-
-#if HAVE_STRUCT_UDPHDR_UH_DPORT && HAVE_STRUCT_UDPHDR_UH_SPORT
-#define UDP_DEST uh_dport
-#define UDP_SRC uh_sport
-#elif HAVE_STRUCT_UDPHDR_DEST && HAVE_STRUCT_UDPHDR_SOURCE
-#define UDP_DEST dest
-#define UDP_SRC source
-#else
-#error "`struct udphdr' is unusable."
-#endif
-
-#if HAVE_NETINET_IP6_H && HAVE_STRUCT_IP6_EXT
-#define HAVE_IPV6 1
-#endif
-
-#include "utils_dns.h"
-
-/*
- * Type definitions
- */
-struct ip_list_s {
- struct in6_addr addr;
- void *data;
- struct ip_list_s *next;
-};
-typedef struct ip_list_s ip_list_t;
-
-typedef int(printer)(const char *, ...);
-
-/*
- * flags/features for non-interactive mode
- */
-
-#ifndef T_A6
-#define T_A6 38
-#endif
-#ifndef T_SRV
-#define T_SRV 33
-#endif
-
-/*
- * Global variables
- */
-
-#if HAVE_PCAP_H
-static pcap_t *pcap_obj;
-#endif
-
-static ip_list_t *IgnoreList;
-
-#if HAVE_PCAP_H
-static void (*Callback)(const rfc1035_header_t *);
-
-static int query_count_intvl;
-static int query_count_total;
-#ifdef __OpenBSD__
-static struct bpf_timeval last_ts;
-#else
-static struct timeval last_ts;
-#endif /* __OpenBSD__ */
-#endif /* HAVE_PCAP_H */
-
-static int cmp_in6_addr(const struct in6_addr *a, const struct in6_addr *b) {
- int i;
-
- assert(sizeof(struct in6_addr) == 16);
-
- for (i = 0; i < 16; i++)
- if (a->s6_addr[i] != b->s6_addr[i])
- break;
-
- if (i >= 16)
- return 0;
-
- return a->s6_addr[i] > b->s6_addr[i] ? 1 : -1;
-} /* int cmp_addrinfo */
-
-static inline int ignore_list_match(const struct in6_addr *addr) {
- for (ip_list_t *ptr = IgnoreList; ptr != NULL; ptr = ptr->next)
- if (cmp_in6_addr(addr, &ptr->addr) == 0)
- return 1;
- return 0;
-} /* int ignore_list_match */
-
-static void ignore_list_add(const struct in6_addr *addr) {
- ip_list_t *new;
-
- if (ignore_list_match(addr) != 0)
- return;
-
- new = malloc(sizeof(*new));
- if (new == NULL) {
- perror("malloc");
- return;
- }
-
- memcpy(&new->addr, addr, sizeof(struct in6_addr));
- new->next = IgnoreList;
-
- IgnoreList = new;
-} /* void ignore_list_add */
-
-void ignore_list_add_name(const char *name) {
- struct addrinfo *ai_list;
- struct in6_addr addr;
- int status;
-
- status = getaddrinfo(name, NULL, NULL, &ai_list);
- if (status != 0)
- return;
-
- for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
- ai_ptr = ai_ptr->ai_next) {
- if (ai_ptr->ai_family == AF_INET) {
- memset(&addr, '\0', sizeof(addr));
- addr.s6_addr[10] = 0xFF;
- addr.s6_addr[11] = 0xFF;
- memcpy(addr.s6_addr + 12,
- &((struct sockaddr_in *)ai_ptr->ai_addr)->sin_addr, 4);
-
- ignore_list_add(&addr);
- } else {
- ignore_list_add(&((struct sockaddr_in6 *)ai_ptr->ai_addr)->sin6_addr);
- }
- } /* for */
-
- freeaddrinfo(ai_list);
-}
-
-#if HAVE_PCAP_H
-static void in6_addr_from_buffer(struct in6_addr *ia, const void *buf,
- size_t buf_len, int family) {
- memset(ia, 0, sizeof(struct in6_addr));
- if ((AF_INET == family) && (sizeof(uint32_t) == buf_len)) {
- ia->s6_addr[10] = 0xFF;
- ia->s6_addr[11] = 0xFF;
- memcpy(ia->s6_addr + 12, buf, buf_len);
- } else if ((AF_INET6 == family) && (sizeof(struct in6_addr) == buf_len)) {
- memcpy(ia, buf, buf_len);
- }
-} /* void in6_addr_from_buffer */
-
-void dnstop_set_pcap_obj(pcap_t *po) { pcap_obj = po; }
-
-void dnstop_set_callback(void (*cb)(const rfc1035_header_t *)) {
- Callback = cb;
-}
-
-#define RFC1035_MAXLABELSZ 63
-static int rfc1035NameUnpack(const char *buf, size_t sz, off_t *off, char *name,
- size_t ns) {
- off_t no = 0;
- unsigned char c;
- size_t len;
- static int loop_detect;
- if (loop_detect > 2)
- return 4; /* compression loop */
- if (ns == 0)
- return 4; /* probably compression loop */
- do {
- if ((*off) >= ((off_t)sz))
- break;
- c = *(buf + (*off));
- if (c > 191) {
- /* blasted compression */
- int rc;
- unsigned short s;
- off_t ptr;
- memcpy(&s, buf + (*off), sizeof(s));
- s = ntohs(s);
- (*off) += sizeof(s);
- /* Sanity check */
- if ((*off) >= ((off_t)sz))
- return 1; /* message too short */
- ptr = s & 0x3FFF;
- /* Make sure the pointer is inside this message */
- if (ptr >= ((off_t)sz))
- return 2; /* bad compression ptr */
- if (ptr < DNS_MSG_HDR_SZ)
- return 2; /* bad compression ptr */
- loop_detect++;
- rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
- loop_detect--;
- return rc;
- } else if (c > RFC1035_MAXLABELSZ) {
- /*
- * "(The 10 and 01 combinations are reserved for future use.)"
- */
- return 3; /* reserved label/compression flags */
- } else {
- (*off)++;
- len = (size_t)c;
- if (len == 0)
- break;
- if (len > (ns - 1))
- len = ns - 1;
- if ((*off) + len > sz)
- return 4; /* message is too short */
- if (no + len + 1 > ns)
- return 5; /* qname would overflow name buffer */
- memcpy(name + no, buf + (*off), len);
- (*off) += len;
- no += len;
- *(name + (no++)) = '.';
- }
- } while (c > 0);
- if (no > 0)
- *(name + no - 1) = '\0';
- /* make sure we didn't allow someone to overflow the name buffer */
- assert(no <= ((off_t)ns));
- return 0;
-}
-
-static int handle_dns(const char *buf, int len) {
- rfc1035_header_t qh;
- uint16_t us;
- off_t offset;
- char *t;
- int status;
-
- /* The DNS header is 12 bytes long */
- if (len < DNS_MSG_HDR_SZ)
- return 0;
-
- memcpy(&us, buf + 0, 2);
- qh.id = ntohs(us);
-
- memcpy(&us, buf + 2, 2);
- us = ntohs(us);
- qh.qr = (us >> 15) & 0x01;
- qh.opcode = (us >> 11) & 0x0F;
- qh.aa = (us >> 10) & 0x01;
- qh.tc = (us >> 9) & 0x01;
- qh.rd = (us >> 8) & 0x01;
- qh.ra = (us >> 7) & 0x01;
- qh.z = (us >> 6) & 0x01;
- qh.ad = (us >> 5) & 0x01;
- qh.cd = (us >> 4) & 0x01;
- qh.rcode = us & 0x0F;
-
- memcpy(&us, buf + 4, 2);
- qh.qdcount = ntohs(us);
-
- memcpy(&us, buf + 6, 2);
- qh.ancount = ntohs(us);
-
- memcpy(&us, buf + 8, 2);
- qh.nscount = ntohs(us);
-
- memcpy(&us, buf + 10, 2);
- qh.arcount = ntohs(us);
-
- offset = DNS_MSG_HDR_SZ;
- memset(qh.qname, '\0', MAX_QNAME_SZ);
- status = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
- if (status != 0) {
- INFO("utils_dns: handle_dns: rfc1035NameUnpack failed "
- "with status %i.",
- status);
- return 0;
- }
- if ('\0' == qh.qname[0])
- sstrncpy(qh.qname, ".", sizeof(qh.qname));
- while ((t = strchr(qh.qname, '\n')))
- *t = ' ';
- while ((t = strchr(qh.qname, '\r')))
- *t = ' ';
- for (t = qh.qname; *t; t++)
- *t = tolower((int)*t);
-
- memcpy(&us, buf + offset, 2);
- qh.qtype = ntohs(us);
- memcpy(&us, buf + offset + 2, 2);
- qh.qclass = ntohs(us);
-
- qh.length = (uint16_t)len;
-
- if (Callback != NULL)
- Callback(&qh);
-
- return 1;
-}
-
-static int handle_udp(const struct udphdr *udp, int len) {
- char buf[PCAP_SNAPLEN];
- if ((ntohs(udp->UDP_DEST) != 53) && (ntohs(udp->UDP_SRC) != 53))
- return 0;
- memcpy(buf, udp + 1, len - sizeof(*udp));
- if (0 == handle_dns(buf, len - sizeof(*udp)))
- return 0;
- return 1;
-}
-
-#if HAVE_IPV6
-static int handle_ipv6(struct ip6_hdr *ipv6, int len) {
- char buf[PCAP_SNAPLEN];
- unsigned int offset;
- int nexthdr;
-
- struct in6_addr c_src_addr;
- uint16_t payload_len;
-
- if (0 > len)
- return 0;
-
- offset = sizeof(struct ip6_hdr);
- nexthdr = ipv6->ip6_nxt;
- c_src_addr = ipv6->ip6_src;
- payload_len = ntohs(ipv6->ip6_plen);
-
- if (ignore_list_match(&c_src_addr))
- return 0;
-
- /* Parse extension headers. This only handles the standard headers, as
- * defined in RFC 2460, correctly. Fragments are discarded. */
- while ((IPPROTO_ROUTING == nexthdr) /* routing header */
- || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */
- || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */
- || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
- || (IPPROTO_AH == nexthdr) /* destination options. */
- || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */
- {
- struct ip6_ext ext_hdr;
- uint16_t ext_hdr_len;
-
- /* Catch broken packets */
- if ((offset + sizeof(struct ip6_ext)) > (unsigned int)len)
- return 0;
-
- /* Cannot handle fragments. */
- if (IPPROTO_FRAGMENT == nexthdr)
- return 0;
-
- memcpy(&ext_hdr, (char *)ipv6 + offset, sizeof(struct ip6_ext));
- nexthdr = ext_hdr.ip6e_nxt;
- ext_hdr_len = (8 * (ntohs(ext_hdr.ip6e_len) + 1));
-
- /* This header is longer than the packets payload.. WTF? */
- if (ext_hdr_len > payload_len)
- return 0;
-
- offset += ext_hdr_len;
- payload_len -= ext_hdr_len;
- } /* while */
-
- /* Catch broken and empty packets */
- if (((offset + payload_len) > (unsigned int)len) || (payload_len == 0) ||
- (payload_len > PCAP_SNAPLEN))
- return 0;
-
- if (IPPROTO_UDP != nexthdr)
- return 0;
-
- memcpy(buf, (char *)ipv6 + offset, payload_len);
- if (handle_udp((struct udphdr *)buf, payload_len) == 0)
- return 0;
-
- return 1; /* Success */
-} /* int handle_ipv6 */
-/* #endif HAVE_IPV6 */
-
-#else /* if !HAVE_IPV6 */
-static int handle_ipv6(__attribute__((unused)) void *pkg,
- __attribute__((unused)) int len) {
- return 0;
-}
-#endif /* !HAVE_IPV6 */
-
-static int handle_ip(const struct ip *ip, int len) {
- char buf[PCAP_SNAPLEN];
- int offset = ip->ip_hl << 2;
- struct in6_addr c_src_addr;
- struct in6_addr c_dst_addr;
-
- if (ip->ip_v == 6)
- return handle_ipv6((void *)ip, len);
-
- in6_addr_from_buffer(&c_src_addr, &ip->ip_src.s_addr,
- sizeof(ip->ip_src.s_addr), AF_INET);
- in6_addr_from_buffer(&c_dst_addr, &ip->ip_dst.s_addr,
- sizeof(ip->ip_dst.s_addr), AF_INET);
- if (ignore_list_match(&c_src_addr))
- return 0;
- if (IPPROTO_UDP != ip->ip_p)
- return 0;
- memcpy(buf, ((char *)ip) + offset, len - offset);
- if (0 == handle_udp((struct udphdr *)buf, len - offset))
- return 0;
- return 1;
-}
-
-#if HAVE_NET_IF_PPP_H
-static int handle_ppp(const u_char *pkt, int len) {
- char buf[PCAP_SNAPLEN];
- unsigned short us;
- unsigned short proto;
- if (len < 2)
- return 0;
- if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
- pkt += 2; /* ACFC not used */
- len -= 2;
- }
- if (len < 2)
- return 0;
- if (*pkt % 2) {
- proto = *pkt; /* PFC is used */
- pkt++;
- len--;
- } else {
- memcpy(&us, pkt, sizeof(us));
- proto = ntohs(us);
- pkt += 2;
- len -= 2;
- }
- if (ETHERTYPE_IP != proto && PPP_IP != proto)
- return 0;
- memcpy(buf, pkt, len);
- return handle_ip((struct ip *)buf, len);
-}
-#endif /* HAVE_NET_IF_PPP_H */
-
-static int handle_null(const u_char *pkt, int len) {
- unsigned int family;
- memcpy(&family, pkt, sizeof(family));
- if (AF_INET != family)
- return 0;
- return handle_ip((struct ip *)(pkt + 4), len - 4);
-}
-
-#ifdef DLT_LOOP
-static int handle_loop(const u_char *pkt, int len) {
- unsigned int family;
- memcpy(&family, pkt, sizeof(family));
- if (AF_INET != ntohl(family))
- return 0;
- return handle_ip((struct ip *)(pkt + 4), len - 4);
-}
-
-#endif
-
-#ifdef DLT_RAW
-static int handle_raw(const u_char *pkt, int len) {
- return handle_ip((struct ip *)pkt, len);
-}
-
-#endif
-
-static int handle_ether(const u_char *pkt, int len) {
- char buf[PCAP_SNAPLEN];
- struct ether_header *e = (void *)pkt;
- unsigned short etype = ntohs(e->ether_type);
- if (len < ETHER_HDR_LEN)
- return 0;
- pkt += ETHER_HDR_LEN;
- len -= ETHER_HDR_LEN;
- if (ETHERTYPE_8021Q == etype) {
- etype = ntohs(*(unsigned short *)(pkt + 2));
- pkt += 4;
- len -= 4;
- }
- if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype))
- return 0;
- memcpy(buf, pkt, len);
- if (ETHERTYPE_IPV6 == etype)
- return handle_ipv6((void *)buf, len);
- else
- return handle_ip((struct ip *)buf, len);
-}
-
-#ifdef DLT_LINUX_SLL
-static int handle_linux_sll(const u_char *pkt, int len) {
- struct sll_header {
- uint16_t pkt_type;
- uint16_t dev_type;
- uint16_t addr_len;
- uint8_t addr[8];
- uint16_t proto_type;
- } * hdr;
- uint16_t etype;
-
- if ((0 > len) || ((unsigned int)len < sizeof(struct sll_header)))
- return 0;
-
- hdr = (struct sll_header *)pkt;
- pkt = (u_char *)(hdr + 1);
- len -= sizeof(struct sll_header);
-
- etype = ntohs(hdr->proto_type);
-
- if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype))
- return 0;
-
- if (ETHERTYPE_IPV6 == etype)
- return handle_ipv6((void *)pkt, len);
- else
- return handle_ip((struct ip *)pkt, len);
-}
-#endif /* DLT_LINUX_SLL */
-
-/* public function */
-void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr,
- const u_char *pkt) {
- int status;
-
- if (hdr->caplen < ETHER_HDR_LEN)
- return;
-
- switch (pcap_datalink(pcap_obj)) {
- case DLT_EN10MB:
- status = handle_ether(pkt, hdr->caplen);
- break;
-#if HAVE_NET_IF_PPP_H
- case DLT_PPP:
- status = handle_ppp(pkt, hdr->caplen);
- break;
-#endif
-#ifdef DLT_LOOP
- case DLT_LOOP:
- status = handle_loop(pkt, hdr->caplen);
- break;
-#endif
-#ifdef DLT_RAW
- case DLT_RAW:
- status = handle_raw(pkt, hdr->caplen);
- break;
-#endif
-#ifdef DLT_LINUX_SLL
- case DLT_LINUX_SLL:
- status = handle_linux_sll(pkt, hdr->caplen);
- break;
-#endif
- case DLT_NULL:
- status = handle_null(pkt, hdr->caplen);
- break;
-
- default:
- ERROR("handle_pcap: unsupported data link type %d",
- pcap_datalink(pcap_obj));
- status = 0;
- break;
- } /* switch (pcap_datalink(pcap_obj)) */
-
- if (0 == status)
- return;
-
- query_count_intvl++;
- query_count_total++;
- last_ts = hdr->ts;
-}
-#endif /* HAVE_PCAP_H */
-
-const char *qtype_str(int t) {
- static char buf[32];
- switch (t) {
-#if (defined(__NAMESER)) && (__NAMESER >= 19991001)
- case ns_t_a:
- return "A";
- case ns_t_ns:
- return "NS";
- case ns_t_md:
- return "MD";
- case ns_t_mf:
- return "MF";
- case ns_t_cname:
- return "CNAME";
- case ns_t_soa:
- return "SOA";
- case ns_t_mb:
- return "MB";
- case ns_t_mg:
- return "MG";
- case ns_t_mr:
- return "MR";
- case ns_t_null:
- return "NULL";
- case ns_t_wks:
- return "WKS";
- case ns_t_ptr:
- return "PTR";
- case ns_t_hinfo:
- return "HINFO";
- case ns_t_minfo:
- return "MINFO";
- case ns_t_mx:
- return "MX";
- case ns_t_txt:
- return "TXT";
- case ns_t_rp:
- return "RP";
- case ns_t_afsdb:
- return "AFSDB";
- case ns_t_x25:
- return "X25";
- case ns_t_isdn:
- return "ISDN";
- case ns_t_rt:
- return "RT";
- case ns_t_nsap:
- return "NSAP";
- case ns_t_nsap_ptr:
- return "NSAP-PTR";
- case ns_t_sig:
- return "SIG";
- case ns_t_key:
- return "KEY";
- case ns_t_px:
- return "PX";
- case ns_t_gpos:
- return "GPOS";
- case ns_t_aaaa:
- return "AAAA";
- case ns_t_loc:
- return "LOC";
- case ns_t_nxt:
- return "NXT";
- case ns_t_eid:
- return "EID";
- case ns_t_nimloc:
- return "NIMLOC";
- case ns_t_srv:
- return "SRV";
- case ns_t_atma:
- return "ATMA";
- case ns_t_naptr:
- return "NAPTR";
- case ns_t_opt:
- return "OPT";
-#if __NAMESER >= 19991006
- case ns_t_kx:
- return "KX";
- case ns_t_cert:
- return "CERT";
- case ns_t_a6:
- return "A6";
- case ns_t_dname:
- return "DNAME";
- case ns_t_sink:
- return "SINK";
- case ns_t_tsig:
- return "TSIG";
-#endif
-#if __NAMESER >= 20090302
- case ns_t_apl:
- return "APL";
- case ns_t_ds:
- return "DS";
- case ns_t_sshfp:
- return "SSHFP";
- case ns_t_ipseckey:
- return "IPSECKEY";
- case ns_t_rrsig:
- return "RRSIG";
- case ns_t_nsec:
- return "NSEC";
- case ns_t_dnskey:
- return "DNSKEY";
- case ns_t_dhcid:
- return "DHCID";
- case ns_t_nsec3:
- return "NSEC3";
- case ns_t_nsec3param:
- return "NSEC3PARAM";
- case ns_t_hip:
- return "HIP";
- case ns_t_spf:
- return "SPF";
- case ns_t_ixfr:
- return "IXFR";
-#endif
- case ns_t_axfr:
- return "AXFR";
- case ns_t_mailb:
- return "MAILB";
- case ns_t_maila:
- return "MAILA";
- case ns_t_any:
- return "ANY";
-#if __NAMESER >= 19991006
- case ns_t_zxfr:
- return "ZXFR";
-#endif
-#if __NAMESER >= 20090302
- case ns_t_dlv:
- return "DLV";
-#endif
-/* #endif __NAMESER >= 19991001 */
-#elif (defined(__BIND)) && (__BIND >= 19950621)
- case T_A:
- return "A"; /* 1 ... */
- case T_NS:
- return "NS";
- case T_MD:
- return "MD";
- case T_MF:
- return "MF";
- case T_CNAME:
- return "CNAME";
- case T_SOA:
- return "SOA";
- case T_MB:
- return "MB";
- case T_MG:
- return "MG";
- case T_MR:
- return "MR";
- case T_NULL:
- return "NULL";
- case T_WKS:
- return "WKS";
- case T_PTR:
- return "PTR";
- case T_HINFO:
- return "HINFO";
- case T_MINFO:
- return "MINFO";
- case T_MX:
- return "MX";
- case T_TXT:
- return "TXT";
- case T_RP:
- return "RP";
- case T_AFSDB:
- return "AFSDB";
- case T_X25:
- return "X25";
- case T_ISDN:
- return "ISDN";
- case T_RT:
- return "RT";
- case T_NSAP:
- return "NSAP";
- case T_NSAP_PTR:
- return "NSAP_PTR";
- case T_SIG:
- return "SIG";
- case T_KEY:
- return "KEY";
- case T_PX:
- return "PX";
- case T_GPOS:
- return "GPOS";
- case T_AAAA:
- return "AAAA";
- case T_LOC:
- return "LOC";
- case T_NXT:
- return "NXT";
- case T_EID:
- return "EID";
- case T_NIMLOC:
- return "NIMLOC";
- case T_SRV:
- return "SRV";
- case T_ATMA:
- return "ATMA";
- case T_NAPTR:
- return "NAPTR"; /* ... 35 */
-#if (__BIND >= 19960801)
- case T_KX:
- return "KX"; /* 36 ... */
- case T_CERT:
- return "CERT";
- case T_A6:
- return "A6";
- case T_DNAME:
- return "DNAME";
- case T_SINK:
- return "SINK";
- case T_OPT:
- return "OPT";
- case T_APL:
- return "APL";
- case T_DS:
- return "DS";
- case T_SSHFP:
- return "SSHFP";
- case T_RRSIG:
- return "RRSIG";
- case T_NSEC:
- return "NSEC";
- case T_DNSKEY:
- return "DNSKEY"; /* ... 48 */
- case T_TKEY:
- return "TKEY"; /* 249 */
-#endif /* __BIND >= 19960801 */
- case T_TSIG:
- return "TSIG"; /* 250 ... */
- case T_IXFR:
- return "IXFR";
- case T_AXFR:
- return "AXFR";
- case T_MAILB:
- return "MAILB";
- case T_MAILA:
- return "MAILA";
- case T_ANY:
- return "ANY"; /* ... 255 */
-#endif /* __BIND >= 19950621 */
- default:
- snprintf(buf, sizeof(buf), "#%i", t);
- return buf;
- } /* switch (t) */
-}
-
-const char *opcode_str(int o) {
- static char buf[30];
- switch (o) {
- case 0:
- return "Query";
- case 1:
- return "Iquery";
- case 2:
- return "Status";
- case 4:
- return "Notify";
- case 5:
- return "Update";
- default:
- snprintf(buf, sizeof(buf), "Opcode%d", o);
- return buf;
- }
-}
-
-const char *rcode_str(int rcode) {
- static char buf[32];
- switch (rcode) {
-#if (defined(__NAMESER)) && (__NAMESER >= 19991006)
- case ns_r_noerror:
- return "NOERROR";
- case ns_r_formerr:
- return "FORMERR";
- case ns_r_servfail:
- return "SERVFAIL";
- case ns_r_nxdomain:
- return "NXDOMAIN";
- case ns_r_notimpl:
- return "NOTIMPL";
- case ns_r_refused:
- return "REFUSED";
- case ns_r_yxdomain:
- return "YXDOMAIN";
- case ns_r_yxrrset:
- return "YXRRSET";
- case ns_r_nxrrset:
- return "NXRRSET";
- case ns_r_notauth:
- return "NOTAUTH";
- case ns_r_notzone:
- return "NOTZONE";
- case ns_r_max:
- return "MAX";
- case ns_r_badsig:
- return "BADSIG";
- case ns_r_badkey:
- return "BADKEY";
- case ns_r_badtime:
- return "BADTIME";
-/* #endif __NAMESER >= 19991006 */
-#elif (defined(__BIND)) && (__BIND >= 19950621)
- case NOERROR:
- return "NOERROR";
- case FORMERR:
- return "FORMERR";
- case SERVFAIL:
- return "SERVFAIL";
- case NXDOMAIN:
- return "NXDOMAIN";
- case NOTIMP:
- return "NOTIMP";
- case REFUSED:
- return "REFUSED";
-#if defined(YXDOMAIN) && defined(NXRRSET)
- case YXDOMAIN:
- return "YXDOMAIN";
- case YXRRSET:
- return "YXRRSET";
- case NXRRSET:
- return "NXRRSET";
- case NOTAUTH:
- return "NOTAUTH";
- case NOTZONE:
- return "NOTZONE";
-#endif /* RFC2136 rcodes */
-#endif /* __BIND >= 19950621 */
- default:
- snprintf(buf, sizeof(buf), "RCode%i", rcode);
- return buf;
- }
-} /* const char *rcode_str (int rcode) */
-
-#if 0
-static int
-main(int argc, char *argv[])
-{
- char errbuf[PCAP_ERRBUF_SIZE];
- int x;
- struct stat sb;
- int readfile_state = 0;
- struct bpf_program fp;
-
- port53 = htons(53);
- SubReport = Sources_report;
- ignore_addr.s_addr = 0;
- progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
- srandom(time(NULL));
- ResetCounters();
-
- while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
- switch (x) {
- case 'a':
- anon_flag = 1;
- break;
- case 's':
- sld_flag = 1;
- break;
- case 't':
- nld_flag = 1;
- break;
- case 'p':
- promisc_flag = 0;
- break;
- case 'b':
- bpf_program_str = strdup(optarg);
- break;
- case 'i':
- ignore_addr.s_addr = inet_addr(optarg);
- break;
- case 'f':
- set_filter(optarg);
- break;
- default:
- usage();
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc < 1)
- usage();
- device = strdup(argv[0]);
-
- if (0 == stat(device, &sb))
- readfile_state = 1;
- if (readfile_state) {
- pcap_obj = pcap_open_offline(device, errbuf);
- } else {
- pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
- }
- if (NULL == pcap_obj) {
- fprintf(stderr, "pcap_open_*: %s\n", errbuf);
- exit(1);
- }
-
- if (0 == isatty(1)) {
- if (0 == readfile_state) {
- fprintf(stderr, "Non-interactive mode requires savefile argument\n");
- exit(1);
- }
- interactive = 0;
- print_func = printf;
- }
-
- memset(&fp, '\0', sizeof(fp));
- x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0);
- if (x < 0) {
- fprintf(stderr, "pcap_compile failed\n");
- exit(1);
- }
- x = pcap_setfilter(pcap_obj, &fp);
- if (x < 0) {
- fprintf(stderr, "pcap_setfilter failed\n");
- exit(1);
- }
-
- /*
- * non-blocking call added for Mac OS X bugfix. Sent by Max Horn.
- * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
- */
- x = pcap_setnonblock(pcap_obj, 1, errbuf);
- if (x < 0) {
- fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
- exit(1);
- }
-
- switch (pcap_datalink(pcap_obj)) {
- case DLT_EN10MB:
- handle_datalink = handle_ether;
- break;
-#if HAVE_NET_IF_PPP_H
- case DLT_PPP:
- handle_datalink = handle_ppp;
- break;
-#endif
-#ifdef DLT_LOOP
- case DLT_LOOP:
- handle_datalink = handle_loop;
- break;
-#endif
-#ifdef DLT_RAW
- case DLT_RAW:
- handle_datalink = handle_raw;
- break;
-#endif
- case DLT_NULL:
- handle_datalink = handle_null;
- break;
- default:
- fprintf(stderr, "unsupported data link type %d\n",
- pcap_datalink(pcap_obj));
- return 1;
- break;
- }
- if (interactive) {
- init_curses();
- while (0 == Quit) {
- if (readfile_state < 2) {
- /*
- * On some OSes select() might return 0 even when
- * there are packets to process. Thus, we always
- * ignore its return value and just call pcap_dispatch()
- * anyway.
- */
- if (0 == readfile_state) /* interactive */
- pcap_select(pcap_obj, 1, 0);
- x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL);
- }
- if (0 == x && 1 == readfile_state) {
- /* block on keyboard until user quits */
- readfile_state++;
- nodelay(w, 0);
- }
- keyboard();
- cron_pre();
- report();
- cron_post();
- }
- endwin(); /* klin, Thu Nov 28 08:56:51 2002 */
- } else {
- while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL))
- (void) 0;
- cron_pre();
- Sources_report(); print_func("\n");
- Destinatioreport(); print_func("\n");
- Qtypes_report(); print_func("\n");
- Opcodes_report(); print_func("\n");
- Tld_report(); print_func("\n");
- Sld_report(); print_func("\n");
- Nld_report(); print_func("\n");
- SldBySource_report();
- }
-
- pcap_close(pcap_obj);
- return 0;
-} /* static int main(int argc, char *argv[]) */
-#endif
+++ /dev/null
-/*
- * collectd - src/utils_dns.h
- * Copyright (C) 2006 Florian octo Forster
- * Copyright (C) 2002 The Measurement Factory, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of The Measurement Factory nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Authors:
- * The Measurement Factory, Inc. <http://www.measurement-factory.com/>
- * Florian octo Forster <octo at collectd.org>
- */
-
-#ifndef COLLECTD_UTILS_DNS_H
-#define COLLECTD_UTILS_DNS_H 1
-
-#include "config.h"
-
-#include <arpa/nameser.h>
-#include <stdint.h>
-
-#if HAVE_PCAP_H
-#include <pcap.h>
-#endif
-
-#define DNS_MSG_HDR_SZ 12
-
-#define T_MAX 65536
-#define MAX_QNAME_SZ 512
-
-struct rfc1035_header_s {
- uint16_t id;
- unsigned int qr : 1;
- unsigned int opcode : 4;
- unsigned int aa : 1;
- unsigned int tc : 1;
- unsigned int rd : 1;
- unsigned int ra : 1;
- unsigned int z : 1;
- unsigned int ad : 1;
- unsigned int cd : 1;
- unsigned int rcode : 4;
- uint16_t qdcount;
- uint16_t ancount;
- uint16_t nscount;
- uint16_t arcount;
- uint16_t qtype;
- uint16_t qclass;
- char qname[MAX_QNAME_SZ];
- uint16_t length;
-};
-typedef struct rfc1035_header_s rfc1035_header_t;
-
-#if HAVE_PCAP_H
-void dnstop_set_pcap_obj(pcap_t *po);
-#endif
-void dnstop_set_callback(void (*cb)(const rfc1035_header_t *));
-
-void ignore_list_add_name(const char *name);
-#if HAVE_PCAP_H
-void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr,
- const u_char *pkt);
-#endif
-
-const char *qtype_str(int t);
-const char *opcode_str(int o);
-const char *rcode_str(int r);
-
-#endif /* !COLLECTD_UTILS_DNS_H */
+++ /dev/null
-/*
- * collectd - src/utils_dpdk.c
- * MIT License
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- * Maryam Tahhan <maryam.tahhan@intel.com>
- * Harry van Haaren <harry.van.haaren@intel.com>
- * Taras Chornyi <tarasx.chornyi@intel.com>
- * Serhiy Pshyk <serhiyx.pshyk@intel.com>
- * Krzysztof Matczak <krzysztofx.matczak@intel.com>
- */
-
-#include "collectd.h"
-
-#include <poll.h>
-#include <semaphore.h>
-#include <sys/mman.h>
-
-#include <rte_config.h>
-#include <rte_eal.h>
-#include <rte_ethdev.h>
-
-#include "common.h"
-#include "utils_dpdk.h"
-
-#if RTE_VERSION <= RTE_VERSION_NUM(18, 5, 0, 0)
-#define DPDK_DEFAULT_RTE_CONFIG "/var/run/.rte_config"
-#else
-#define DPDK_DEFAULT_RTE_CONFIG "/var/run/dpdk/rte/config"
-#endif
-#define DPDK_EAL_ARGC 10
-// Complete trace should fit into 1024 chars. Trace contain some headers
-// and text together with traced data from pipe. This is the reason why
-// we need to limit DPDK_MAX_BUFFER_SIZE value.
-#define DPDK_MAX_BUFFER_SIZE 896
-#define DPDK_CDM_DEFAULT_TIMEOUT 10000
-
-enum DPDK_HELPER_STATUS {
- DPDK_HELPER_NOT_INITIALIZED = 0,
- DPDK_HELPER_INITIALIZING,
- DPDK_HELPER_WAITING_ON_PRIMARY,
- DPDK_HELPER_INITIALIZING_EAL,
- DPDK_HELPER_ALIVE_SENDING_EVENTS,
- DPDK_HELPER_GRACEFUL_QUIT,
-};
-
-#define DPDK_HELPER_TRACE(_name) \
- DEBUG("%s:%s:%d pid=%ld", _name, __FUNCTION__, __LINE__, (long)getpid())
-
-struct dpdk_helper_ctx_s {
-
- dpdk_eal_config_t eal_config;
- int eal_initialized;
-
- size_t shm_size;
- char shm_name[DATA_MAX_NAME_LEN];
-
- sem_t sema_cmd_start;
- sem_t sema_cmd_complete;
- cdtime_t cmd_wait_time;
-
- pid_t pid;
- int pipes[2];
- int status;
-
- int cmd;
- int cmd_result;
-
- char priv_data[];
-};
-
-static int dpdk_shm_init(const char *name, size_t size, void **map);
-static void dpdk_shm_cleanup(const char *name, size_t size, void *map);
-
-static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc);
-static int dpdk_helper_worker(dpdk_helper_ctx_t *phc);
-static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc);
-static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid);
-static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc,
- enum DPDK_HELPER_STATUS status);
-static int dpdk_helper_exit(dpdk_helper_ctx_t *phc,
- enum DPDK_HELPER_STATUS status);
-static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc);
-static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc);
-static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status);
-
-static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc) {
- if (phc == NULL)
- return;
-
- DPDK_HELPER_TRACE(phc->shm_name);
-
- snprintf(phc->eal_config.coremask, DATA_MAX_NAME_LEN, "%s", "0xf");
- snprintf(phc->eal_config.memory_channels, DATA_MAX_NAME_LEN, "%s", "1");
- snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, "%s",
- DPDK_DEFAULT_RTE_CONFIG);
-}
-
-int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) {
- if (phc == NULL) {
- ERROR("Invalid argument (phc)");
- return -EINVAL;
- }
-
- DPDK_HELPER_TRACE(phc->shm_name);
-
- if (ec == NULL) {
- ERROR("Invalid argument (ec)");
- return -EINVAL;
- }
-
- memcpy(&phc->eal_config, ec, sizeof(phc->eal_config));
-
- return 0;
-}
-
-int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) {
- if (phc == NULL) {
- ERROR("Invalid argument (phc)");
- return -EINVAL;
- }
-
- DPDK_HELPER_TRACE(phc->shm_name);
-
- if (ec == NULL) {
- ERROR("Invalid argument (ec)");
- return -EINVAL;
- }
-
- memcpy(ec, &phc->eal_config, sizeof(*ec));
-
- return 0;
-}
-
-int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci) {
- DPDK_HELPER_TRACE(phc->shm_name);
-
- if (phc == NULL) {
- ERROR("Invalid argument (phc)");
- return -EINVAL;
- }
-
- if (ci == NULL) {
- ERROR("Invalid argument (ci)");
- return -EINVAL;
- }
-
- int status = 0;
- for (int i = 0; i < ci->children_num; i++) {
- oconfig_item_t *child = ci->children + i;
-
- if (strcasecmp("Coremask", child->key) == 0) {
- status = cf_util_get_string_buffer(child, phc->eal_config.coremask,
- sizeof(phc->eal_config.coremask));
- DEBUG("dpdk_common: EAL:Coremask %s", phc->eal_config.coremask);
- } else if (strcasecmp("MemoryChannels", child->key) == 0) {
- status =
- cf_util_get_string_buffer(child, phc->eal_config.memory_channels,
- sizeof(phc->eal_config.memory_channels));
- DEBUG("dpdk_common: EAL:Memory Channels %s",
- phc->eal_config.memory_channels);
- } else if (strcasecmp("SocketMemory", child->key) == 0) {
- status = cf_util_get_string_buffer(child, phc->eal_config.socket_memory,
- sizeof(phc->eal_config.socket_memory));
- DEBUG("dpdk_common: EAL:Socket memory %s", phc->eal_config.socket_memory);
- } else if (strcasecmp("FilePrefix", child->key) == 0) {
- char prefix[DATA_MAX_NAME_LEN];
-
- status = cf_util_get_string_buffer(child, prefix, sizeof(prefix));
- if (status == 0) {
-#if RTE_VERSION <= RTE_VERSION_NUM(18, 5, 0, 0)
- snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN,
- "/var/run/.%s_config", prefix);
-#else
- snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN,
- "/var/run/dpdk/%s/config", prefix);
-#endif
- DEBUG("dpdk_common: EAL:File prefix %s", phc->eal_config.file_prefix);
- }
- } else if (strcasecmp("LogLevel", child->key) == 0) {
- status = cf_util_get_string_buffer(child, phc->eal_config.log_level,
- sizeof(phc->eal_config.log_level));
- DEBUG("dpdk_common: EAL:LogLevel %s", phc->eal_config.log_level);
- } else if (strcasecmp("RteDriverLibPath", child->key) == 0) {
- status = cf_util_get_string_buffer(
- child, phc->eal_config.rte_driver_lib_path,
- sizeof(phc->eal_config.rte_driver_lib_path));
- DEBUG("dpdk_common: EAL:RteDriverLibPath %s",
- phc->eal_config.rte_driver_lib_path);
- } else {
- ERROR("dpdk_common: Invalid '%s' configuration option", child->key);
- status = -EINVAL;
- }
-
- if (status != 0) {
- ERROR("dpdk_common: Parsing EAL configuration failed");
- break;
- }
- }
-
- return status;
-}
-
-static int dpdk_shm_init(const char *name, size_t size, void **map) {
- DPDK_HELPER_TRACE(name);
-
- int fd = shm_open(name, O_CREAT | O_TRUNC | O_RDWR, 0666);
- if (fd < 0) {
- WARNING("dpdk_shm_init: Failed to open %s as SHM:%s", name, STRERRNO);
- *map = NULL;
- return -1;
- }
-
- int ret = ftruncate(fd, size);
- if (ret != 0) {
- WARNING("dpdk_shm_init: Failed to resize SHM:%s", STRERRNO);
- close(fd);
- *map = NULL;
- dpdk_shm_cleanup(name, size, NULL);
- return -1;
- }
-
- *map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (*map == MAP_FAILED) {
- WARNING("dpdk_shm_init:Failed to mmap SHM:%s", STRERRNO);
- close(fd);
- *map = NULL;
- dpdk_shm_cleanup(name, size, NULL);
- return -1;
- }
- /* File descriptor no longer needed for shared memory operations */
- close(fd);
- memset(*map, 0, size);
-
- return 0;
-}
-
-static void dpdk_shm_cleanup(const char *name, size_t size, void *map) {
- DPDK_HELPER_TRACE(name);
-
- /*
- * Call shm_unlink first, as 'name' might be no longer accessible after munmap
- */
- if (shm_unlink(name))
- ERROR("shm_unlink failure %s", STRERRNO);
-
- if (map != NULL) {
- if (munmap(map, size))
- ERROR("munmap failure %s", STRERRNO);
- }
-}
-
-void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc) {
- if (phc)
- return phc->priv_data;
-
- return NULL;
-}
-
-int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc) {
- if (phc == NULL) {
- DPDK_CHILD_LOG("Invalid argument(phc)\n");
- return -EINVAL;
- }
-
- return phc->shm_size - sizeof(dpdk_helper_ctx_t);
-}
-
-int dpdk_helper_init(const char *name, size_t data_size,
- dpdk_helper_ctx_t **pphc) {
- dpdk_helper_ctx_t *phc = NULL;
- size_t shm_size = sizeof(dpdk_helper_ctx_t) + data_size;
-
- if (pphc == NULL) {
- ERROR("%s:Invalid argument(pphc)", __FUNCTION__);
- return -EINVAL;
- }
-
- if (name == NULL) {
- ERROR("%s:Invalid argument(name)", __FUNCTION__);
- return -EINVAL;
- }
-
- DPDK_HELPER_TRACE(name);
-
- /* Allocate dpdk_helper_ctx_t and
- * initialize a POSIX SHared Memory (SHM) object.
- */
- int err = dpdk_shm_init(name, shm_size, (void **)&phc);
- if (err != 0) {
- return -errno;
- }
-
- err = sem_init(&phc->sema_cmd_start, 1, 0);
- if (err != 0) {
- ERROR("sema_cmd_start semaphore init failed: %s", STRERRNO);
- int errno_m = errno;
- dpdk_shm_cleanup(name, shm_size, (void *)phc);
- return -errno_m;
- }
-
- err = sem_init(&phc->sema_cmd_complete, 1, 0);
- if (err != 0) {
- ERROR("sema_cmd_complete semaphore init failed: %s", STRERRNO);
- sem_destroy(&phc->sema_cmd_start);
- int errno_m = errno;
- dpdk_shm_cleanup(name, shm_size, (void *)phc);
- return -errno_m;
- }
-
- phc->shm_size = shm_size;
- sstrncpy(phc->shm_name, name, sizeof(phc->shm_name));
-
- dpdk_helper_config_default(phc);
-
- *pphc = phc;
-
- return 0;
-}
-
-void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) {
- if (phc == NULL)
- return;
-
- DPDK_HELPER_TRACE(phc->shm_name);
-
- close(phc->pipes[1]);
-
- if (phc->status != DPDK_HELPER_NOT_INITIALIZED) {
- dpdk_helper_exit_command(phc, DPDK_HELPER_GRACEFUL_QUIT);
- }
-
- sem_destroy(&phc->sema_cmd_start);
- sem_destroy(&phc->sema_cmd_complete);
- dpdk_shm_cleanup(phc->shm_name, phc->shm_size, (void *)phc);
-}
-
-static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) {
- if (phc == NULL) {
- ERROR("Invalid argument(phc)");
- return -EINVAL;
- }
-
- DPDK_HELPER_TRACE(phc->shm_name);
-
- phc->eal_initialized = 0;
- phc->cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT);
-
- /*
- * Create a pipe for helper stdout back to collectd. This is necessary for
- * logging EAL failures, as rte_eal_init() calls rte_panic().
- */
- if (phc->pipes[1]) {
- DEBUG("dpdk_helper_spawn: collectd closing helper pipe %d", phc->pipes[1]);
- } else {
- DEBUG("dpdk_helper_spawn: collectd helper pipe %d, not closing",
- phc->pipes[1]);
- }
-
- if (pipe(phc->pipes) != 0) {
- DEBUG("dpdk_helper_spawn: Could not create helper pipe: %s", STRERRNO);
- return -1;
- }
-
- int pipe0_flags = fcntl(phc->pipes[0], F_GETFL, 0);
- int pipe1_flags = fcntl(phc->pipes[1], F_GETFL, 0);
- if (pipe0_flags == -1 || pipe1_flags == -1) {
- WARNING("dpdk_helper_spawn: error setting up pipe flags: %s", STRERRNO);
- }
- int pipe0_err = fcntl(phc->pipes[0], F_SETFL, pipe1_flags | O_NONBLOCK);
- int pipe1_err = fcntl(phc->pipes[1], F_SETFL, pipe0_flags | O_NONBLOCK);
- if (pipe0_err == -1 || pipe1_err == -1) {
- WARNING("dpdk_helper_spawn: error setting up pipes: %s", STRERRNO);
- }
-
- pid_t pid = fork();
- if (pid > 0) {
- phc->pid = pid;
- close(phc->pipes[1]);
- DEBUG("%s:dpdk_helper_spawn: helper pid %lu", phc->shm_name,
- (long)phc->pid);
- } else if (pid == 0) {
- /* Replace stdout with a pipe to collectd. */
- close(phc->pipes[0]);
- close(STDOUT_FILENO);
- dup2(phc->pipes[1], STDOUT_FILENO);
- DPDK_CHILD_TRACE(phc->shm_name);
- dpdk_helper_worker(phc);
- exit(0);
- } else {
- ERROR("dpdk_helper_start: Failed to fork helper process: %s", STRERRNO);
- return -1;
- }
-
- return 0;
-}
-
-static int dpdk_helper_exit(dpdk_helper_ctx_t *phc,
- enum DPDK_HELPER_STATUS status) {
- DPDK_CHILD_LOG("%s:%s:%d %s\n", phc->shm_name, __FUNCTION__, __LINE__,
- dpdk_helper_status_str(status));
-
- close(phc->pipes[1]);
-
- phc->status = status;
-
- exit(0);
-
- return 0;
-}
-
-static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc,
- enum DPDK_HELPER_STATUS status) {
- DPDK_HELPER_TRACE(phc->shm_name);
-
- close(phc->pipes[1]);
-
- if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) {
- phc->status = status;
- DEBUG("%s:%s:%d %s", phc->shm_name, __FUNCTION__, __LINE__,
- dpdk_helper_status_str(status));
-
- int ret = dpdk_helper_command(phc, DPDK_CMD_QUIT, NULL, 0);
- if (ret != 0) {
- DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__,
- __LINE__, (long)phc->pid);
-
- int err = kill(phc->pid, SIGKILL);
- if (err) {
- ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO);
- }
- }
- } else {
-
- DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__,
- __LINE__, (long)phc->pid);
-
- int err = kill(phc->pid, SIGKILL);
- if (err) {
- ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO);
- }
- }
-
- return 0;
-}
-
-static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc) {
- phc->status = DPDK_HELPER_INITIALIZING_EAL;
- DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (start)\n",
- phc->shm_name, __FUNCTION__, __LINE__);
-
- char *argp[DPDK_EAL_ARGC * 2 + 1];
- int argc = 0;
-
- /* EAL config must be initialized */
- assert(phc->eal_config.coremask[0] != 0);
- assert(phc->eal_config.memory_channels[0] != 0);
- assert(phc->eal_config.file_prefix[0] != 0);
-
- argp[argc++] = "collectd-dpdk";
-
- argp[argc++] = "-c";
- argp[argc++] = phc->eal_config.coremask;
-
- argp[argc++] = "-n";
- argp[argc++] = phc->eal_config.memory_channels;
-
- if (strcasecmp(phc->eal_config.socket_memory, "") != 0) {
- argp[argc++] = "--socket-mem";
- argp[argc++] = phc->eal_config.socket_memory;
- }
-
- if (strcasecmp(phc->eal_config.file_prefix, DPDK_DEFAULT_RTE_CONFIG) != 0) {
- argp[argc++] = "--file-prefix";
- argp[argc++] = phc->eal_config.file_prefix;
- }
-
- argp[argc++] = "--proc-type";
- argp[argc++] = "secondary";
-
- if (strcasecmp(phc->eal_config.log_level, "") != 0) {
- argp[argc++] = "--log-level";
- argp[argc++] = phc->eal_config.log_level;
- }
- if (strcasecmp(phc->eal_config.rte_driver_lib_path, "") != 0) {
- argp[argc++] = "-d";
- argp[argc++] = phc->eal_config.rte_driver_lib_path;
- }
-
- assert(argc <= (DPDK_EAL_ARGC * 2 + 1));
-
- int ret = rte_eal_init(argc, argp);
-
- if (ret < 0) {
-
- phc->eal_initialized = 0;
-
- DPDK_CHILD_LOG("dpdk_helper_eal_init: ERROR initializing EAL ret=%d\n",
- ret);
-
- printf("dpdk_helper_eal_init: EAL arguments: ");
- for (int i = 0; i < argc; i++) {
- printf("%s ", argp[i]);
- }
- printf("\n");
-
- return ret;
- }
-
- phc->eal_initialized = 1;
-
- DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (done)\n",
- phc->shm_name, __FUNCTION__, __LINE__);
-
- return 0;
-}
-
-static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid) {
- DPDK_CHILD_TRACE(phc->shm_name);
-
- struct timespec ts;
- cdtime_t now = cdtime();
- cdtime_t cmd_wait_time = MS_TO_CDTIME_T(1500) + phc->cmd_wait_time * 2;
- ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time);
-
- int ret = sem_timedwait(&phc->sema_cmd_start, &ts);
- DPDK_CHILD_LOG("%s:%s:%d pid=%lu got sema_cmd_start (ret=%d, errno=%d)\n",
- phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), ret,
- errno);
-
- if (phc->cmd == DPDK_CMD_QUIT) {
- DPDK_CHILD_LOG("%s:%s:%d pid=%lu exiting\n", phc->shm_name, __FUNCTION__,
- __LINE__, (long)getpid());
- exit(0);
- } else if (ret == -1 && errno == ETIMEDOUT) {
- if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) {
- DPDK_CHILD_LOG("%s:dpdk_helper_cmd_wait: sem timedwait()"
- " timeout, did collectd terminate?\n",
- phc->shm_name);
- dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT);
- }
- }
-#if COLLECT_DEBUG
- int val = 0;
- if (sem_getvalue(&phc->sema_cmd_start, &val) == 0)
- DPDK_CHILD_LOG("%s:%s:%d pid=%lu wait sema_cmd_start (value=%d)\n",
- phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), val);
-#endif
-
- /* Parent PID change means collectd died so quit the helper process. */
- if (ppid != getppid()) {
- DPDK_CHILD_LOG("dpdk_helper_cmd_wait: parent PID changed, quitting.\n");
- dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT);
- }
-
- /* Checking for DPDK primary process. */
- if (!rte_eal_primary_proc_alive(phc->eal_config.file_prefix)) {
- if (phc->eal_initialized) {
- DPDK_CHILD_LOG(
- "%s:dpdk_helper_cmd_wait: no primary alive but EAL initialized:"
- " quitting.\n",
- phc->shm_name);
- dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED);
- }
-
- phc->status = DPDK_HELPER_WAITING_ON_PRIMARY;
- DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_WAITING_ON_PRIMARY\n", phc->shm_name,
- __FUNCTION__, __LINE__);
-
- return -1;
- }
-
- if (!phc->eal_initialized) {
- int ret = dpdk_helper_eal_init(phc);
- if (ret != 0) {
- DPDK_CHILD_LOG("Error initializing EAL\n");
- dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED);
- }
- phc->status = DPDK_HELPER_ALIVE_SENDING_EVENTS;
- DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_ALIVE_SENDING_EVENTS\n", phc->shm_name,
- __FUNCTION__, __LINE__);
- return -1;
- }
-
- return 0;
-}
-
-static int dpdk_helper_worker(dpdk_helper_ctx_t *phc) {
- DPDK_CHILD_TRACE(phc->shm_name);
-
- pid_t ppid = getppid();
-
- while (1) {
- if (dpdk_helper_cmd_wait(phc, ppid) == 0) {
- DPDK_CHILD_LOG("%s:%s:%d DPDK command handle (cmd=%d, pid=%lu)\n",
- phc->shm_name, __FUNCTION__, __LINE__, phc->cmd,
- (long)getpid());
- phc->cmd_result = dpdk_helper_command_handler(phc, phc->cmd);
- } else {
- phc->cmd_result = -1;
- }
-
- /* now kick collectd to get results */
- int err = sem_post(&phc->sema_cmd_complete);
- DPDK_CHILD_LOG("%s:%s:%d post sema_cmd_complete (pid=%lu)\n", phc->shm_name,
- __FUNCTION__, __LINE__, (long)getpid());
- if (err) {
- DPDK_CHILD_LOG("dpdk_helper_worker: error posting sema_cmd_complete "
- "semaphore (%s)\n",
- STRERRNO);
- }
-
-#if COLLECT_DEBUG
- int val = 0;
- if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0)
- DPDK_CHILD_LOG("%s:%s:%d pid=%lu sema_cmd_complete (value=%d)\n",
- phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(),
- val);
-#endif
-
- } /* while(1) */
-
- return 0;
-}
-
-static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status) {
- switch (status) {
- case DPDK_HELPER_ALIVE_SENDING_EVENTS:
- return "DPDK_HELPER_ALIVE_SENDING_EVENTS";
- case DPDK_HELPER_WAITING_ON_PRIMARY:
- return "DPDK_HELPER_WAITING_ON_PRIMARY";
- case DPDK_HELPER_INITIALIZING:
- return "DPDK_HELPER_INITIALIZING";
- case DPDK_HELPER_INITIALIZING_EAL:
- return "DPDK_HELPER_INITIALIZING_EAL";
- case DPDK_HELPER_GRACEFUL_QUIT:
- return "DPDK_HELPER_GRACEFUL_QUIT";
- case DPDK_HELPER_NOT_INITIALIZED:
- return "DPDK_HELPER_NOT_INITIALIZED";
- default:
- return "UNKNOWN";
- }
-}
-
-static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc) {
- DEBUG("%s:%s:%d pid=%u %s", phc->shm_name, __FUNCTION__, __LINE__, getpid(),
- dpdk_helper_status_str(phc->status));
-
- if (phc->status == DPDK_HELPER_GRACEFUL_QUIT) {
- return 0;
- } else if (phc->status == DPDK_HELPER_NOT_INITIALIZED) {
- phc->status = DPDK_HELPER_INITIALIZING;
- DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__,
- __LINE__);
- int err = dpdk_helper_spawn(phc);
- if (err) {
- ERROR("dpdkstat: error spawning helper %s", STRERRNO);
- }
- return -1;
- }
-
- pid_t ws = waitpid(phc->pid, NULL, WNOHANG);
- if (ws != 0) {
- phc->status = DPDK_HELPER_INITIALIZING;
- DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__,
- __LINE__);
- int err = dpdk_helper_spawn(phc);
- if (err) {
- ERROR("dpdkstat: error spawning helper %s", STRERRNO);
- }
- return -1;
- }
-
- if (phc->status == DPDK_HELPER_INITIALIZING_EAL) {
- return -1;
- }
-
- return 0;
-}
-
-static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) {
- char buf[DPDK_MAX_BUFFER_SIZE];
- char out[DPDK_MAX_BUFFER_SIZE];
-
- /* non blocking check on helper logging pipe */
- struct pollfd fds = {
- .fd = phc->pipes[0], .events = POLLIN,
- };
- int data_avail = poll(&fds, 1, 0);
- DEBUG("%s:dpdk_helper_check_pipe: poll data_avail=%d", phc->shm_name,
- data_avail);
- if (data_avail < 0) {
- if (errno != EINTR || errno != EAGAIN) {
- ERROR("%s: poll(2) failed: %s", phc->shm_name, STRERRNO);
- }
- }
- while (data_avail) {
- int nbytes = read(phc->pipes[0], buf, (sizeof(buf) - 1));
- DEBUG("%s:dpdk_helper_check_pipe: read nbytes=%d", phc->shm_name, nbytes);
- if (nbytes <= 0)
- break;
- buf[nbytes] = '\0';
- sstrncpy(out, buf, sizeof(out));
- DEBUG("%s: helper process:\n%s", phc->shm_name, out);
- }
-}
-
-int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result,
- cdtime_t cmd_wait_time) {
- if (phc == NULL) {
- ERROR("Invalid argument(phc)");
- return -EINVAL;
- }
-
- DEBUG("%s:%s:%d pid=%lu, cmd=%d", phc->shm_name, __FUNCTION__, __LINE__,
- (long)getpid(), cmd);
-
- phc->cmd_wait_time = cmd_wait_time;
-
- int ret = dpdk_helper_status_check(phc);
-
- dpdk_helper_check_pipe(phc);
-
- if (ret != 0) {
- return ret;
- }
-
- DEBUG("%s: DPDK command execute (cmd=%d)", phc->shm_name, cmd);
-
- phc->cmd_result = 0;
- phc->cmd = cmd;
-
- /* kick helper to process command */
- int err = sem_post(&phc->sema_cmd_start);
- if (err) {
- ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)",
- STRERRNO);
- }
-
-#if COLLECT_DEBUG
- int val = 0;
- if (sem_getvalue(&phc->sema_cmd_start, &val) == 0)
- DEBUG("%s:dpdk_helper_command: post sema_cmd_start (value=%d)",
- phc->shm_name, val);
-#endif
-
- if (phc->cmd != DPDK_CMD_QUIT) {
-
- /* wait for helper to complete processing */
- struct timespec ts;
- cdtime_t now = cdtime();
-
- if (phc->status != DPDK_HELPER_ALIVE_SENDING_EVENTS) {
- cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT);
- }
-
- ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time);
- ret = sem_timedwait(&phc->sema_cmd_complete, &ts);
- if (ret == -1 && errno == ETIMEDOUT) {
- DPDK_HELPER_TRACE(phc->shm_name);
- DEBUG("%s:sema_cmd_start: timeout in collectd thread: is a DPDK Primary "
- "running?",
- phc->shm_name);
- return -ETIMEDOUT;
- }
-
-#if COLLECT_DEBUG
- val = 0;
- if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0)
- DEBUG("%s:dpdk_helper_command: wait sema_cmd_complete (value=%d)",
- phc->shm_name, val);
-#endif
-
- if (result) {
- *result = phc->cmd_result;
- }
- }
-
- dpdk_helper_check_pipe(phc);
-
- DEBUG("%s: DPDK command complete (cmd=%d, result=%d)", phc->shm_name,
- phc->cmd, phc->cmd_result);
-
- return 0;
-}
-
-uint64_t strtoull_safe(const char *str, int *err) {
- uint64_t val = 0;
- char *endptr;
- int res = 0;
-
- val = strtoull(str, &endptr, 16);
- if (*endptr) {
- ERROR("%s Failed to parse the value %s, endptr=%c", __FUNCTION__, str,
- *endptr);
- res = -EINVAL;
- }
- if (err != NULL)
- *err = res;
- return val;
-}
-
-uint128_t str_to_uint128(const char *str, int len) {
- uint128_t lcore_mask;
- int err = 0;
-
- memset(&lcore_mask, 0, sizeof(lcore_mask));
-
- if (len <= 2 || strncmp(str, "0x", 2) != 0) {
- ERROR("%s Value %s should be represened in hexadecimal format",
- __FUNCTION__, str);
- return lcore_mask;
- }
- /* If str is <= 64 bit long ('0x' + 16 chars = 18 chars) then
- * conversion is straightforward. Otherwise str is splitted into 64b long
- * blocks */
- if (len <= 18) {
- lcore_mask.low = strtoull_safe(str, &err);
- if (err)
- return lcore_mask;
- } else {
- char low_str[DATA_MAX_NAME_LEN];
- char high_str[DATA_MAX_NAME_LEN * 2];
-
- memset(high_str, 0, sizeof(high_str));
- memset(low_str, 0, sizeof(low_str));
-
- strncpy(high_str, str, len - 16);
- strncpy(low_str, str + len - 16, 16);
-
- lcore_mask.low = strtoull_safe(low_str, &err);
- if (err)
- return lcore_mask;
-
- lcore_mask.high = strtoull_safe(high_str, &err);
- if (err) {
- lcore_mask.low = 0;
- return lcore_mask;
- }
- }
- return lcore_mask;
-}
-
-uint8_t dpdk_helper_eth_dev_count(void) {
-#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0)
- uint8_t ports = rte_eth_dev_count();
-#else
- uint8_t ports = rte_eth_dev_count_avail();
-#endif
- if (ports == 0) {
- ERROR(
- "%s:%d: No DPDK ports available. Check bound devices to DPDK driver.\n",
- __FUNCTION__, __LINE__);
- return ports;
- }
-
- if (ports > RTE_MAX_ETHPORTS) {
- ERROR("%s:%d: Number of DPDK ports (%u) is greater than "
- "RTE_MAX_ETHPORTS=%d. Ignoring extra ports\n",
- __FUNCTION__, __LINE__, ports, RTE_MAX_ETHPORTS);
- ports = RTE_MAX_ETHPORTS;
- }
-
- return ports;
-}
+++ /dev/null
-/*
- * collectd - src/utils_dpdk.h
- * MIT License
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- * Maryam Tahhan <maryam.tahhan@intel.com>
- * Harry van Haaren <harry.van.haaren@intel.com>
- * Taras Chornyi <tarasx.chornyi@intel.com>
- * Serhiy Pshyk <serhiyx.pshyk@intel.com>
- * Krzysztof Matczak <krzysztofx.matczak@intel.com>
- */
-
-#ifndef UTILS_DPDK_H
-#define UTILS_DPDK_H
-
-#include <rte_version.h>
-
-#define ERR_BUF_SIZE 1024
-
-enum DPDK_CMD {
- DPDK_CMD_NONE = 0,
- DPDK_CMD_QUIT,
- DPDK_CMD_INIT,
- DPDK_CMD_GET_STATS,
- DPDK_CMD_GET_EVENTS,
- __DPDK_CMD_LAST,
-};
-
-struct dpdk_eal_config_s {
- char coremask[DATA_MAX_NAME_LEN];
- char memory_channels[DATA_MAX_NAME_LEN];
- char socket_memory[DATA_MAX_NAME_LEN];
- char file_prefix[DATA_MAX_NAME_LEN];
- char log_level[DATA_MAX_NAME_LEN];
- char rte_driver_lib_path[PATH_MAX];
-};
-typedef struct dpdk_eal_config_s dpdk_eal_config_t;
-
-struct uint128_s {
- u_int64_t high;
- u_int64_t low;
-};
-typedef struct uint128_s uint128_t;
-
-typedef struct dpdk_helper_ctx_s dpdk_helper_ctx_t;
-
-int dpdk_helper_init(const char *name, size_t data_size,
- dpdk_helper_ctx_t **pphc);
-void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc);
-int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci);
-int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec);
-int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec);
-int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result,
- cdtime_t cmd_wait_time);
-void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc);
-int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc);
-uint8_t dpdk_helper_eth_dev_count(void);
-
-/* forward declaration of handler function that is called by helper from
- * child process. not implemented in helper. must be provided by client. */
-int dpdk_helper_command_handler(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd);
-
-uint128_t str_to_uint128(const char *str, int len);
-
-/* logging functions that should be used in child process */
-#define DPDK_CHILD_LOG(...) fprintf(stdout, __VA_ARGS__)
-#define DPDK_CHILD_TRACE(_name) \
- fprintf(stdout, "%s:%s:%d pid=%u\n", _name, __FUNCTION__, __LINE__, getpid())
-
-#endif /* UTILS_DPDK_H */
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
#include "utils_fbhash.h"
struct fbhash_s {
+++ /dev/null
-/**
- * collectd - src/utils_format_graphite.c
- * Copyright (C) 2012 Thomas Meson
- * Copyright (C) 2012 Florian octo Forster
- *
- * 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:
- * Thomas Meson <zllak at hycik.org>
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_cache.h"
-#include "utils_format_graphite.h"
-
-#define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r"
-
-/* Utils functions to format data sets in graphite format.
- * Largely taken from write_graphite.c as it remains the same formatting */
-
-static int gr_format_values(char *ret, size_t ret_len, int ds_num,
- const data_set_t *ds, const value_list_t *vl,
- gauge_t const *rates) {
- size_t offset = 0;
- int status;
-
- assert(0 == strcmp(ds->type, vl->type));
-
- memset(ret, 0, ret_len);
-
-#define BUFFER_ADD(...) \
- do { \
- status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \
- if (status < 1) { \
- return -1; \
- } else if (((size_t)status) >= (ret_len - offset)) { \
- return -1; \
- } else \
- offset += ((size_t)status); \
- } while (0)
-
- if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
- BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge);
- else if (rates != NULL)
- BUFFER_ADD("%f", rates[ds_num]);
- else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
- BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
- else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
- BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
- else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
- BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
- else {
- P_ERROR("gr_format_values: Unknown data source type: %i",
- ds->ds[ds_num].type);
- return -1;
- }
-
-#undef BUFFER_ADD
-
- return 0;
-}
-
-static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
- char escape_char, bool preserve_separator) {
- memset(dst, 0, dst_len);
-
- if (src == NULL)
- return;
-
- for (size_t i = 0; i < dst_len; i++) {
- if (src[i] == 0) {
- dst[i] = 0;
- break;
- }
-
- if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) ||
- iscntrl((int)src[i]))
- dst[i] = escape_char;
- else
- dst[i] = src[i];
- }
-}
-
-static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl,
- char const *ds_name, char const *prefix,
- char const *postfix, char const escape_char,
- unsigned int flags) {
- char n_host[DATA_MAX_NAME_LEN];
- char n_plugin[DATA_MAX_NAME_LEN];
- char n_plugin_instance[DATA_MAX_NAME_LEN];
- char n_type[DATA_MAX_NAME_LEN];
- char n_type_instance[DATA_MAX_NAME_LEN];
-
- char tmp_plugin[DATA_MAX_NAME_LEN + 8];
- char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17];
- char tmp_type[DATA_MAX_NAME_LEN + 6];
- char tmp_type_instance[DATA_MAX_NAME_LEN + 15];
- char tmp_metric[3 * DATA_MAX_NAME_LEN + 2];
- char tmp_ds_name[DATA_MAX_NAME_LEN + 9];
-
- if (prefix == NULL)
- prefix = "";
-
- if (postfix == NULL)
- postfix = "";
-
- gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
- gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1);
- gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
- sizeof(n_plugin_instance), escape_char, 1);
- gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1);
- gr_copy_escape_part(n_type_instance, vl->type_instance,
- sizeof(n_type_instance), escape_char, 1);
-
- snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin);
-
- if (n_plugin_instance[0] != '\0')
- snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance),
- ";plugin_instance=%s", n_plugin_instance);
- else
- tmp_plugin_instance[0] = '\0';
-
- if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0)
- snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type);
- else
- tmp_type[0] = '\0';
-
- if (n_type_instance[0] != '\0') {
- if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) ||
- strcmp(n_plugin_instance, n_type_instance) != 0)
- snprintf(tmp_type_instance, sizeof(tmp_type_instance),
- ";type_instance=%s", n_type_instance);
- else
- tmp_type_instance[0] = '\0';
- } else
- tmp_type_instance[0] = '\0';
-
- /* Assert always_append_ds -> ds_name */
- assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
- if (ds_name != NULL) {
- snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name);
-
- if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
- snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name);
- else
- snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type,
- ds_name);
- } else {
- tmp_ds_name[0] = '\0';
-
- if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
- snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin);
- else
- snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type);
- }
-
- snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric,
- postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type,
- tmp_type_instance, tmp_ds_name);
-
- return 0;
-}
-
-static int gr_format_name(char *ret, int ret_len, value_list_t const *vl,
- char const *ds_name, char const *prefix,
- char const *postfix, char const escape_char,
- unsigned int flags) {
- char n_host[DATA_MAX_NAME_LEN];
- char n_plugin[DATA_MAX_NAME_LEN];
- char n_plugin_instance[DATA_MAX_NAME_LEN];
- char n_type[DATA_MAX_NAME_LEN];
- char n_type_instance[DATA_MAX_NAME_LEN];
-
- char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1];
- char tmp_type[2 * DATA_MAX_NAME_LEN + 1];
-
- if (prefix == NULL)
- prefix = "";
-
- if (postfix == NULL)
- postfix = "";
-
- bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR);
-
- gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char,
- preserve_separator);
- gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char,
- preserve_separator);
- gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
- sizeof(n_plugin_instance), escape_char,
- preserve_separator);
- gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char,
- preserve_separator);
- gr_copy_escape_part(n_type_instance, vl->type_instance,
- sizeof(n_type_instance), escape_char, preserve_separator);
-
- if (n_plugin_instance[0] != '\0')
- snprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin,
- (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
- n_plugin_instance);
- else
- sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin));
-
- if (n_type_instance[0] != '\0') {
- if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
- sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type));
- else
- snprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type,
- (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
- n_type_instance);
- } else
- sstrncpy(tmp_type, n_type, sizeof(tmp_type));
-
- /* Assert always_append_ds -> ds_name */
- assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
- if (ds_name != NULL) {
- if ((flags & GRAPHITE_DROP_DUPE_FIELDS) &&
- strcmp(tmp_plugin, tmp_type) == 0)
- snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix,
- tmp_plugin, ds_name);
- else
- snprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix,
- tmp_plugin, tmp_type, ds_name);
- } else
- snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin,
- tmp_type);
-
- return 0;
-}
-
-static void escape_graphite_string(char *buffer, char escape_char) {
- assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL);
-
- for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0';
- head += strcspn(head, GRAPHITE_FORBIDDEN))
- *head = escape_char;
-}
-
-int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
- value_list_t const *vl, char const *prefix,
- char const *postfix, char const escape_char,
- unsigned int flags) {
- int status = 0;
- int buffer_pos = 0;
-
- gauge_t *rates = NULL;
- if (flags & GRAPHITE_STORE_RATES) {
- rates = uc_get_rate(ds, vl);
- if (rates == NULL) {
- P_ERROR("format_graphite: error with uc_get_rate");
- return -1;
- }
- }
-
- for (size_t i = 0; i < ds->ds_num; i++) {
- char const *ds_name = NULL;
- char key[10 * DATA_MAX_NAME_LEN];
- char values[512];
- size_t message_len;
- char message[1024];
-
- if ((flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds->ds_num > 1))
- ds_name = ds->ds[i].name;
-
- /* Copy the identifier to `key' and escape it. */
- if (flags & GRAPHITE_USE_TAGS) {
- status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix,
- postfix, escape_char, flags);
- if (status != 0) {
- P_ERROR("format_graphite: error with gr_format_name_tagged");
- sfree(rates);
- return status;
- }
- } else {
- status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
- escape_char, flags);
- if (status != 0) {
- P_ERROR("format_graphite: error with gr_format_name");
- sfree(rates);
- return status;
- }
- }
-
- escape_graphite_string(key, escape_char);
-
- /* Convert the values to an ASCII representation and put that into
- * `values'. */
- status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
- if (status != 0) {
- P_ERROR("format_graphite: error with gr_format_values");
- sfree(rates);
- return status;
- }
-
- /* Compute the graphite command */
- message_len =
- (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values,
- (unsigned int)CDTIME_T_TO_TIME_T(vl->time));
- if (message_len >= sizeof(message)) {
- P_ERROR("format_graphite: message buffer too small: "
- "Need %" PRIsz " bytes.",
- message_len + 1);
- sfree(rates);
- return -ENOMEM;
- }
-
- /* Append it in case we got multiple data set */
- if ((buffer_pos + message_len) >= buffer_size) {
- P_ERROR("format_graphite: target buffer too small");
- sfree(rates);
- return -ENOMEM;
- }
- memcpy((void *)(buffer + buffer_pos), message, message_len);
- buffer_pos += message_len;
- buffer[buffer_pos] = '\0';
- }
- sfree(rates);
- return status;
-} /* int format_graphite */
+++ /dev/null
-/**
- * collectd - src/utils_format_graphite.h
- * Copyright (C) 2012 Thomas Meson
- *
- * 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
- *
- * Author:
- * Thomas Meson <zllak at hycik.org>
- **/
-
-#ifndef UTILS_FORMAT_GRAPHITE_H
-#define UTILS_FORMAT_GRAPHITE_H 1
-
-#include "collectd.h"
-
-#include "plugin.h"
-
-#define GRAPHITE_STORE_RATES 0x01
-#define GRAPHITE_SEPARATE_INSTANCES 0x02
-#define GRAPHITE_ALWAYS_APPEND_DS 0x04
-#define GRAPHITE_DROP_DUPE_FIELDS 0x08
-#define GRAPHITE_PRESERVE_SEPARATOR 0x10
-#define GRAPHITE_USE_TAGS 0x20
-
-int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
- const value_list_t *vl, const char *prefix,
- const char *postfix, const char escape_char,
- unsigned int flags);
-
-#endif /* UTILS_FORMAT_GRAPHITE_H */
+++ /dev/null
-/**
- * collectd - src/utils_format_graphite_test.c
- * Copyright (C) 2016 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#include "collectd.h"
-
-#include "common.h" /* for STATIC_ARRAY_SIZE */
-#include "testing.h"
-#include "utils_format_graphite.h"
-
-static data_set_t ds_single = {
- .type = "single",
- .ds_num = 1,
- .ds = &(data_source_t){"value", DS_TYPE_GAUGE, NAN, NAN},
-};
-
-/*
-static data_set_t ds_double = {
- .type = "double",
- .ds_num = 2,
- .ds =
- (data_source_t[]){
- {"one", DS_TYPE_DERIVE, 0, NAN}, {"two", DS_TYPE_DERIVE, 0, NAN},
- },
-};
-*/
-
-DEF_TEST(metric_name) {
- struct {
- const char *plugin_instance;
- const char *type_instance;
- const char *prefix;
- const char *suffix;
- unsigned int flags;
- const char *want_name;
- } cases[] = {
- {
- .want_name = "example@com.test.single",
- },
- /* plugin and type instances */
- {
- .plugin_instance = "foo",
- .type_instance = "bar",
- .want_name = "example@com.test-foo.single-bar",
- },
- {
- .plugin_instance = NULL,
- .type_instance = "bar",
- .want_name = "example@com.test.single-bar",
- },
- {
- .plugin_instance = "foo",
- .type_instance = NULL,
- .want_name = "example@com.test-foo.single",
- },
- /* special chars */
- {
- .plugin_instance = "foo (test)",
- .type_instance = "test: \"hello\"",
- .want_name = "example@com.test-foo@@test@.single-test@@@hello@",
- },
- /* flag GRAPHITE_SEPARATE_INSTANCES */
- {
- .plugin_instance = "foo",
- .type_instance = "bar",
- .flags = GRAPHITE_SEPARATE_INSTANCES,
- .want_name = "example@com.test.foo.single.bar",
- },
- /* flag GRAPHITE_ALWAYS_APPEND_DS */
- {
- .plugin_instance = "foo",
- .type_instance = "bar",
- .flags = GRAPHITE_ALWAYS_APPEND_DS,
- .want_name = "example@com.test-foo.single-bar.value",
- },
- /* flag GRAPHITE_PRESERVE_SEPARATOR */
- {
- .plugin_instance = "f.o.o",
- .type_instance = "b.a.r",
- .flags = 0,
- .want_name = "example@com.test-f@o@o.single-b@a@r",
- },
- {
- .plugin_instance = "f.o.o",
- .type_instance = "b.a.r",
- .flags = GRAPHITE_PRESERVE_SEPARATOR,
- .want_name = "example.com.test-f.o.o.single-b.a.r",
- },
- /* prefix and suffix */
- {
- .prefix = "foo.",
- .suffix = ".bar",
- .want_name = "foo.example@com.bar.test.single",
- },
- {
- .prefix = NULL,
- .suffix = ".bar",
- .want_name = "example@com.bar.test.single",
- },
- {
- .prefix = "foo.",
- .suffix = NULL,
- .want_name = "foo.example@com.test.single",
- },
- /* flag GRAPHITE_USE_TAGS */
- {.flags = GRAPHITE_USE_TAGS,
- .want_name = "test.single;host=example.com;plugin=test;type=single"},
- {.plugin_instance = "f.o.o",
- .type_instance = "b.a.r",
- .flags = GRAPHITE_USE_TAGS,
- .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
- "f.o.o;type=single;type_instance=b.a.r"},
- {.flags = GRAPHITE_USE_TAGS ^ GRAPHITE_ALWAYS_APPEND_DS,
- .want_name = "test.single.value;host=example.com;plugin=test;type="
- "single;ds_name=value"},
- {.plugin_instance = "foo",
- .type_instance = "foo",
- .flags = GRAPHITE_USE_TAGS ^ GRAPHITE_DROP_DUPE_FIELDS,
- .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
- "foo;type=single"},
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- value_list_t vl = {
- .values = &(value_t){.gauge = 42},
- .values_len = 1,
- .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
- .interval = TIME_T_TO_CDTIME_T_STATIC(10),
- .host = "example.com",
- .plugin = "test",
- .type = "single",
- };
-
- char want[1024];
- snprintf(want, sizeof(want), "%s 42 1480063672\r\n", cases[i].want_name);
-
- if (cases[i].plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, cases[i].plugin_instance,
- sizeof(vl.plugin_instance));
- if (cases[i].type_instance != NULL)
- sstrncpy(vl.type_instance, cases[i].type_instance,
- sizeof(vl.type_instance));
-
- char got[1024];
- EXPECT_EQ_INT(0, format_graphite(got, sizeof(got), &ds_single, &vl,
- cases[i].prefix, cases[i].suffix, '@',
- cases[i].flags));
- EXPECT_EQ_STR(want, got);
- }
-
- return 0;
-}
-
-DEF_TEST(null_termination) {
- value_list_t vl = {
- .values = &(value_t){.gauge = 1337},
- .values_len = 1,
- .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
- .interval = TIME_T_TO_CDTIME_T_STATIC(10),
- .host = "example.com",
- .plugin = "test",
- .type = "single",
- };
- char const *want = "example_com.test.single 1337 1480063672\r\n";
-
- char buffer[128];
- for (size_t i = 0; i < sizeof(buffer); i++)
- buffer[i] = (char)i;
-
- EXPECT_EQ_INT(0, format_graphite(buffer, sizeof(buffer), &ds_single, &vl,
- NULL, NULL, '_', 0));
- EXPECT_EQ_STR(want, buffer);
- EXPECT_EQ_INT(0, buffer[strlen(want)]);
- for (size_t i = strlen(want) + 1; i < sizeof(buffer); i++)
- EXPECT_EQ_INT((int)i, (int)buffer[i]);
-
- return 0;
-}
-
-int main(void) {
- RUN_TEST(metric_name);
- RUN_TEST(null_termination);
-
- END_TEST;
-}
+++ /dev/null
-/**
- * collectd - src/utils_format_json.c
- * Copyright (C) 2009-2015 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "utils_format_json.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_cache.h"
-
-#if HAVE_LIBYAJL
-#include <yajl/yajl_common.h>
-#include <yajl/yajl_gen.h>
-#if HAVE_YAJL_YAJL_VERSION_H
-#include <yajl/yajl_version.h>
-#endif
-#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
-#define HAVE_YAJL_V2 1
-#endif
-#endif
-
-static int json_escape_string(char *buffer, size_t buffer_size, /* {{{ */
- const char *string) {
- size_t dst_pos;
-
- if ((buffer == NULL) || (string == NULL))
- return -EINVAL;
-
- if (buffer_size < 3)
- return -ENOMEM;
-
- dst_pos = 0;
-
-#define BUFFER_ADD(c) \
- do { \
- if (dst_pos >= (buffer_size - 1)) { \
- buffer[buffer_size - 1] = '\0'; \
- return -ENOMEM; \
- } \
- buffer[dst_pos] = (c); \
- dst_pos++; \
- } while (0)
-
- /* Escape special characters */
- BUFFER_ADD('"');
- for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) {
- if ((string[src_pos] == '"') || (string[src_pos] == '\\')) {
- BUFFER_ADD('\\');
- BUFFER_ADD(string[src_pos]);
- } else if (string[src_pos] <= 0x001F)
- BUFFER_ADD('?');
- else
- BUFFER_ADD(string[src_pos]);
- } /* for */
- BUFFER_ADD('"');
- buffer[dst_pos] = 0;
-
-#undef BUFFER_ADD
-
- return 0;
-} /* }}} int json_escape_string */
-
-static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds, const value_list_t *vl,
- int store_rates) {
- size_t offset = 0;
- gauge_t *rates = NULL;
-
- memset(buffer, 0, buffer_size);
-
-#define BUFFER_ADD(...) \
- do { \
- int status; \
- status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
- if (status < 1) { \
- sfree(rates); \
- return -1; \
- } else if (((size_t)status) >= (buffer_size - offset)) { \
- sfree(rates); \
- return -ENOMEM; \
- } else \
- offset += ((size_t)status); \
- } while (0)
-
- BUFFER_ADD("[");
- for (size_t i = 0; i < ds->ds_num; i++) {
- if (i > 0)
- BUFFER_ADD(",");
-
- if (ds->ds[i].type == DS_TYPE_GAUGE) {
- if (isfinite(vl->values[i].gauge))
- BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[i].gauge);
- else
- BUFFER_ADD("null");
- } else if (store_rates) {
- if (rates == NULL)
- rates = uc_get_rate(ds, vl);
- if (rates == NULL) {
- WARNING("utils_format_json: uc_get_rate failed.");
- sfree(rates);
- return -1;
- }
-
- if (isfinite(rates[i]))
- BUFFER_ADD(JSON_GAUGE_FORMAT, rates[i]);
- else
- BUFFER_ADD("null");
- } else if (ds->ds[i].type == DS_TYPE_COUNTER)
- BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[i].counter);
- else if (ds->ds[i].type == DS_TYPE_DERIVE)
- BUFFER_ADD("%" PRIi64, vl->values[i].derive);
- else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
- BUFFER_ADD("%" PRIu64, vl->values[i].absolute);
- else {
- ERROR("format_json: Unknown data source type: %i", ds->ds[i].type);
- sfree(rates);
- return -1;
- }
- } /* for ds->ds_num */
- BUFFER_ADD("]");
-
-#undef BUFFER_ADD
-
- sfree(rates);
- return 0;
-} /* }}} int values_to_json */
-
-static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds) {
- size_t offset = 0;
-
- memset(buffer, 0, buffer_size);
-
-#define BUFFER_ADD(...) \
- do { \
- int status; \
- status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
- if (status < 1) \
- return -1; \
- else if (((size_t)status) >= (buffer_size - offset)) \
- return -ENOMEM; \
- else \
- offset += ((size_t)status); \
- } while (0)
-
- BUFFER_ADD("[");
- for (size_t i = 0; i < ds->ds_num; i++) {
- if (i > 0)
- BUFFER_ADD(",");
-
- BUFFER_ADD("\"%s\"", DS_TYPE_TO_STRING(ds->ds[i].type));
- } /* for ds->ds_num */
- BUFFER_ADD("]");
-
-#undef BUFFER_ADD
-
- return 0;
-} /* }}} int dstypes_to_json */
-
-static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds) {
- size_t offset = 0;
-
- memset(buffer, 0, buffer_size);
-
-#define BUFFER_ADD(...) \
- do { \
- int status; \
- status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
- if (status < 1) \
- return -1; \
- else if (((size_t)status) >= (buffer_size - offset)) \
- return -ENOMEM; \
- else \
- offset += ((size_t)status); \
- } while (0)
-
- BUFFER_ADD("[");
- for (size_t i = 0; i < ds->ds_num; i++) {
- if (i > 0)
- BUFFER_ADD(",");
-
- BUFFER_ADD("\"%s\"", ds->ds[i].name);
- } /* for ds->ds_num */
- BUFFER_ADD("]");
-
-#undef BUFFER_ADD
-
- return 0;
-} /* }}} int dsnames_to_json */
-
-static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */
- meta_data_t *meta, char **keys,
- size_t keys_num) {
- size_t offset = 0;
- int status;
-
- buffer[0] = 0;
-
-#define BUFFER_ADD(...) \
- do { \
- status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
- if (status < 1) \
- return -1; \
- else if (((size_t)status) >= (buffer_size - offset)) \
- return -ENOMEM; \
- else \
- offset += ((size_t)status); \
- } while (0)
-
- for (size_t i = 0; i < keys_num; ++i) {
- int type;
- char *key = keys[i];
-
- type = meta_data_type(meta, key);
- if (type == MD_TYPE_STRING) {
- char *value = NULL;
- if (meta_data_get_string(meta, key, &value) == 0) {
- char temp[512] = "";
-
- status = json_escape_string(temp, sizeof(temp), value);
- sfree(value);
- if (status != 0)
- return status;
-
- BUFFER_ADD(",\"%s\":%s", key, temp);
- }
- } else if (type == MD_TYPE_SIGNED_INT) {
- int64_t value = 0;
- if (meta_data_get_signed_int(meta, key, &value) == 0)
- BUFFER_ADD(",\"%s\":%" PRIi64, key, value);
- } else if (type == MD_TYPE_UNSIGNED_INT) {
- uint64_t value = 0;
- if (meta_data_get_unsigned_int(meta, key, &value) == 0)
- BUFFER_ADD(",\"%s\":%" PRIu64, key, value);
- } else if (type == MD_TYPE_DOUBLE) {
- double value = 0.0;
- if (meta_data_get_double(meta, key, &value) == 0)
- BUFFER_ADD(",\"%s\":%f", key, value);
- } else if (type == MD_TYPE_BOOLEAN) {
- bool value = false;
- if (meta_data_get_boolean(meta, key, &value) == 0)
- BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false");
- }
- } /* for (keys) */
-
- if (offset == 0)
- return ENOENT;
-
- buffer[0] = '{'; /* replace leading ',' */
- BUFFER_ADD("}");
-
-#undef BUFFER_ADD
-
- return 0;
-} /* }}} int meta_data_keys_to_json */
-
-static int meta_data_to_json(char *buffer, size_t buffer_size, /* {{{ */
- meta_data_t *meta) {
- char **keys = NULL;
- size_t keys_num;
- int status;
-
- if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL))
- return EINVAL;
-
- status = meta_data_toc(meta, &keys);
- if (status <= 0)
- return status;
- keys_num = (size_t)status;
-
- status = meta_data_keys_to_json(buffer, buffer_size, meta, keys, keys_num);
-
- for (size_t i = 0; i < keys_num; ++i)
- sfree(keys[i]);
- sfree(keys);
-
- return status;
-} /* }}} int meta_data_to_json */
-
-static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds, const value_list_t *vl,
- int store_rates) {
- char temp[512];
- size_t offset = 0;
- int status;
-
- memset(buffer, 0, buffer_size);
-
-#define BUFFER_ADD(...) \
- do { \
- status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
- if (status < 1) \
- return -1; \
- else if (((size_t)status) >= (buffer_size - offset)) \
- return -ENOMEM; \
- else \
- offset += ((size_t)status); \
- } while (0)
-
- /* All value lists have a leading comma. The first one will be replaced with
- * a square bracket in `format_json_finalize'. */
- BUFFER_ADD(",{");
-
- status = values_to_json(temp, sizeof(temp), ds, vl, store_rates);
- if (status != 0)
- return status;
- BUFFER_ADD("\"values\":%s", temp);
-
- status = dstypes_to_json(temp, sizeof(temp), ds);
- if (status != 0)
- return status;
- BUFFER_ADD(",\"dstypes\":%s", temp);
-
- status = dsnames_to_json(temp, sizeof(temp), ds);
- if (status != 0)
- return status;
- BUFFER_ADD(",\"dsnames\":%s", temp);
-
- BUFFER_ADD(",\"time\":%.3f", CDTIME_T_TO_DOUBLE(vl->time));
- BUFFER_ADD(",\"interval\":%.3f", CDTIME_T_TO_DOUBLE(vl->interval));
-
-#define BUFFER_ADD_KEYVAL(key, value) \
- do { \
- status = json_escape_string(temp, sizeof(temp), (value)); \
- if (status != 0) \
- return status; \
- BUFFER_ADD(",\"%s\":%s", (key), temp); \
- } while (0)
-
- BUFFER_ADD_KEYVAL("host", vl->host);
- BUFFER_ADD_KEYVAL("plugin", vl->plugin);
- BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance);
- BUFFER_ADD_KEYVAL("type", vl->type);
- BUFFER_ADD_KEYVAL("type_instance", vl->type_instance);
-
- if (vl->meta != NULL) {
- char meta_buffer[buffer_size];
- memset(meta_buffer, 0, sizeof(meta_buffer));
- status = meta_data_to_json(meta_buffer, sizeof(meta_buffer), vl->meta);
- if (status != 0)
- return status;
-
- BUFFER_ADD(",\"meta\":%s", meta_buffer);
- } /* if (vl->meta != NULL) */
-
- BUFFER_ADD("}");
-
-#undef BUFFER_ADD_KEYVAL
-#undef BUFFER_ADD
-
- return 0;
-} /* }}} int value_list_to_json */
-
-static int format_json_value_list_nocheck(char *buffer, /* {{{ */
- size_t *ret_buffer_fill,
- size_t *ret_buffer_free,
- const data_set_t *ds,
- const value_list_t *vl,
- int store_rates, size_t temp_size) {
- char temp[temp_size];
- int status;
-
- status = value_list_to_json(temp, sizeof(temp), ds, vl, store_rates);
- if (status != 0)
- return status;
- temp_size = strlen(temp);
-
- memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1);
- (*ret_buffer_fill) += temp_size;
- (*ret_buffer_free) -= temp_size;
-
- return 0;
-} /* }}} int format_json_value_list_nocheck */
-
-int format_json_initialize(char *buffer, /* {{{ */
- size_t *ret_buffer_fill, size_t *ret_buffer_free) {
- size_t buffer_fill;
- size_t buffer_free;
-
- if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
- (ret_buffer_free == NULL))
- return -EINVAL;
-
- buffer_fill = *ret_buffer_fill;
- buffer_free = *ret_buffer_free;
-
- buffer_free = buffer_fill + buffer_free;
- buffer_fill = 0;
-
- if (buffer_free < 3)
- return -ENOMEM;
-
- memset(buffer, 0, buffer_free);
- *ret_buffer_fill = buffer_fill;
- *ret_buffer_free = buffer_free;
-
- return 0;
-} /* }}} int format_json_initialize */
-
-int format_json_finalize(char *buffer, /* {{{ */
- size_t *ret_buffer_fill, size_t *ret_buffer_free) {
- size_t pos;
-
- if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
- (ret_buffer_free == NULL))
- return -EINVAL;
-
- if (*ret_buffer_free < 2)
- return -ENOMEM;
-
- /* Replace the leading comma added in `value_list_to_json' with a square
- * bracket. */
- if (buffer[0] != ',')
- return -EINVAL;
- buffer[0] = '[';
-
- pos = *ret_buffer_fill;
- buffer[pos] = ']';
- buffer[pos + 1] = 0;
-
- (*ret_buffer_fill)++;
- (*ret_buffer_free)--;
-
- return 0;
-} /* }}} int format_json_finalize */
-
-int format_json_value_list(char *buffer, /* {{{ */
- size_t *ret_buffer_fill, size_t *ret_buffer_free,
- const data_set_t *ds, const value_list_t *vl,
- int store_rates) {
- if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
- (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
- return -EINVAL;
-
- if (*ret_buffer_free < 3)
- return -ENOMEM;
-
- return format_json_value_list_nocheck(buffer, ret_buffer_fill,
- ret_buffer_free, ds, vl, store_rates,
- (*ret_buffer_free) - 2);
-} /* }}} int format_json_value_list */
-
-#if HAVE_LIBYAJL
-static int json_add_string(yajl_gen g, char const *str) /* {{{ */
-{
- if (str == NULL)
- return (int)yajl_gen_null(g);
-
- return (int)yajl_gen_string(g, (const unsigned char *)str,
- (unsigned int)strlen(str));
-} /* }}} int json_add_string */
-
-#define JSON_ADD(g, str) \
- do { \
- yajl_gen_status status = json_add_string(g, str); \
- if (status != yajl_gen_status_ok) { \
- return -1; \
- } \
- } while (0)
-
-#define JSON_ADDF(g, format, ...) \
- do { \
- char *str = ssnprintf_alloc(format, __VA_ARGS__); \
- yajl_gen_status status = json_add_string(g, str); \
- free(str); \
- if (status != yajl_gen_status_ok) { \
- return -1; \
- } \
- } while (0)
-
-#define CHECK_SUCCESS(cmd) \
- do { \
- yajl_gen_status s = (cmd); \
- if (s != yajl_gen_status_ok) { \
- return (int)s; \
- } \
- } while (0)
-
-static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */
-{
- if (meta == NULL)
- return 0;
-
- JSON_ADD(g, meta->name);
- switch (meta->type) {
- case NM_TYPE_STRING:
- JSON_ADD(g, meta->nm_value.nm_string);
- break;
- case NM_TYPE_SIGNED_INT:
- JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int);
- break;
- case NM_TYPE_UNSIGNED_INT:
- JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int);
- break;
- case NM_TYPE_DOUBLE:
- JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double);
- break;
- case NM_TYPE_BOOLEAN:
- JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false");
- break;
- default:
- ERROR("format_json_meta: unknown meta data type %d (name \"%s\")",
- meta->type, meta->name);
- CHECK_SUCCESS(yajl_gen_null(g));
- }
-
- return format_json_meta(g, meta->next);
-} /* }}} int format_json_meta */
-
-static int format_time(yajl_gen g, cdtime_t t) /* {{{ */
-{
- char buffer[RFC3339NANO_SIZE] = "";
-
- if (rfc3339nano(buffer, sizeof(buffer), t) != 0)
- return -1;
-
- JSON_ADD(g, buffer);
- return 0;
-} /* }}} int format_time */
-
-static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */
-{
- CHECK_SUCCESS(yajl_gen_array_open(g)); /* BEGIN array */
- CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN alert */
-
- /*
- * labels
- */
- JSON_ADD(g, "labels");
- CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN labels */
-
- JSON_ADD(g, "alertname");
- if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0)
- JSON_ADDF(g, "collectd_%s", n->type);
- else
- JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type);
-
- JSON_ADD(g, "instance");
- JSON_ADD(g, n->host);
-
- /* mangling of plugin instance and type instance into labels is copied from
- * the Prometheus collectd exporter. */
- if (strlen(n->plugin_instance) > 0) {
- JSON_ADD(g, n->plugin);
- JSON_ADD(g, n->plugin_instance);
- }
- if (strlen(n->type_instance) > 0) {
- if (strlen(n->plugin_instance) > 0)
- JSON_ADD(g, "type");
- else
- JSON_ADD(g, n->plugin);
- JSON_ADD(g, n->type_instance);
- }
-
- JSON_ADD(g, "severity");
- JSON_ADD(g,
- (n->severity == NOTIF_FAILURE)
- ? "FAILURE"
- : (n->severity == NOTIF_WARNING)
- ? "WARNING"
- : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN");
-
- JSON_ADD(g, "service");
- JSON_ADD(g, "collectd");
-
- CHECK_SUCCESS(yajl_gen_map_close(g)); /* END labels */
-
- /*
- * annotations
- */
- JSON_ADD(g, "annotations");
- CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN annotations */
-
- JSON_ADD(g, "summary");
- JSON_ADD(g, n->message);
-
- if (format_json_meta(g, n->meta) != 0) {
- return -1;
- }
-
- CHECK_SUCCESS(yajl_gen_map_close(g)); /* END annotations */
-
- JSON_ADD(g, "startsAt");
- if (format_time(g, n->time) != 0) {
- return -1;
- }
-
- CHECK_SUCCESS(yajl_gen_map_close(g)); /* END alert */
- CHECK_SUCCESS(yajl_gen_array_close(g)); /* END array */
-
- return 0;
-} /* }}} format_alert */
-
-/*
- * Format (prometheus/alertmanager v1):
- *
- * [{
- * "labels": {
- * "alertname": "collectd_cpu",
- * "instance": "host.example.com",
- * "severity": "FAILURE",
- * "service": "collectd",
- * "cpu": "0",
- * "type": "wait"
- * },
- * "annotations": {
- * "summary": "...",
- * // meta
- * },
- * "startsAt": <rfc3339 time>,
- * "endsAt": <rfc3339 time>, // not used
- * }]
- */
-int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
- notification_t const *n) {
- yajl_gen g;
- unsigned char const *out;
-#if HAVE_YAJL_V2
- size_t unused_out_len;
-#else
- unsigned int unused_out_len;
-#endif
-
- if ((buffer == NULL) || (n == NULL))
- return EINVAL;
-
-#if HAVE_YAJL_V2
- g = yajl_gen_alloc(NULL);
- if (g == NULL)
- return -1;
-#if COLLECT_DEBUG
- yajl_gen_config(g, yajl_gen_beautify, 1);
- yajl_gen_config(g, yajl_gen_validate_utf8, 1);
-#endif
-
-#else /* !HAVE_YAJL_V2 */
- yajl_gen_config conf = {0};
-#if COLLECT_DEBUG
- conf.beautify = 1;
- conf.indentString = " ";
-#endif
- g = yajl_gen_alloc(&conf, NULL);
- if (g == NULL)
- return -1;
-#endif
-
- if (format_alert(g, n) != 0) {
- yajl_gen_clear(g);
- yajl_gen_free(g);
- return -1;
- }
-
- /* copy to output buffer */
- if (yajl_gen_get_buf(g, &out, &unused_out_len) != yajl_gen_status_ok) {
- yajl_gen_clear(g);
- yajl_gen_free(g);
- return -1;
- }
- sstrncpy(buffer, (void *)out, buffer_size);
-
- yajl_gen_clear(g);
- yajl_gen_free(g);
- return 0;
-} /* }}} format_json_notification */
-#else
-int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
- notification_t const *n) {
- ERROR("format_json_notification: Not available (requires libyajl).");
- return ENOTSUP;
-} /* }}} int format_json_notification */
-#endif
+++ /dev/null
-/**
- * collectd - src/utils_format_json.h
- * Copyright (C) 2009 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_FORMAT_JSON_H
-#define UTILS_FORMAT_JSON_H 1
-
-#include "collectd.h"
-
-#include "plugin.h"
-
-#ifndef JSON_GAUGE_FORMAT
-#define JSON_GAUGE_FORMAT GAUGE_FORMAT
-#endif
-
-int format_json_initialize(char *buffer, size_t *ret_buffer_fill,
- size_t *ret_buffer_free);
-int format_json_value_list(char *buffer, size_t *ret_buffer_fill,
- size_t *ret_buffer_free, const data_set_t *ds,
- const value_list_t *vl, int store_rates);
-int format_json_finalize(char *buffer, size_t *ret_buffer_fill,
- size_t *ret_buffer_free);
-int format_json_notification(char *buffer, size_t buffer_size,
- notification_t const *n);
-
-#endif /* UTILS_FORMAT_JSON_H */
+++ /dev/null
-/**
- * collectd - src/utils_format_json_test.c
- * Copyright (C) 2015 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-/* Workaround for Solaris 10 defining label_t
- * Issue #1301
- */
-
-#include "config.h"
-#if KERNEL_SOLARIS
-#ifndef _POSIX_C_SOURCE
-#define _POSIX_C_SOURCE 200112L
-#endif
-#undef __EXTENSIONS__
-#endif
-
-#include "collectd.h"
-
-#include "common.h" /* for STATIC_ARRAY_SIZE */
-#include "testing.h"
-#include "utils_format_json.h"
-
-#include <yajl/yajl_common.h>
-#include <yajl/yajl_parse.h>
-#if HAVE_YAJL_YAJL_VERSION_H
-#include <yajl/yajl_version.h>
-#endif
-#if YAJL_MAJOR > 1
-#define HAVE_YAJL_V2 1
-#endif
-
-typedef struct {
- char const *key;
- char const *value;
-} label_t;
-
-typedef struct {
- label_t *expected_labels;
- size_t expected_labels_num;
-
- label_t *current_label;
-} test_case_t;
-
-#if HAVE_YAJL_V2
-static int test_map_key(void *ctx, unsigned char const *key, size_t key_len)
-#else
-static int test_map_key(void *ctx, unsigned char const *key,
- unsigned int key_len)
-#endif
-{
- test_case_t *c = ctx;
- size_t i;
-
- c->current_label = NULL;
- for (i = 0; i < c->expected_labels_num; i++) {
- label_t *l = c->expected_labels + i;
- if ((strlen(l->key) == key_len) &&
- (strncmp(l->key, (char const *)key, key_len) == 0)) {
- c->current_label = l;
- break;
- }
- }
-
- return 1; /* continue */
-}
-
-static int expect_label(char const *name, char const *got, char const *want) {
- bool ok = (strcmp(got, want) == 0);
- char msg[1024];
-
- if (ok)
- snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\"", name, got);
- else
- snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\", want \"%s\"", name, got,
- want);
-
- OK1(ok, msg);
- return 0;
-}
-
-#if HAVE_YAJL_V2
-static int test_string(void *ctx, unsigned char const *value, size_t value_len)
-#else
-static int test_string(void *ctx, unsigned char const *value,
- unsigned int value_len)
-#endif
-{
- test_case_t *c = ctx;
-
- if (c->current_label != NULL) {
- label_t *l = c->current_label;
- char *got;
- int status;
-
- got = malloc(value_len + 1);
- memmove(got, value, value_len);
- got[value_len] = 0;
-
- status = expect_label(l->key, got, l->value);
-
- free(got);
-
- if (status != 0)
- return 0; /* abort */
- }
-
- return 1; /* continue */
-}
-
-static int expect_json_labels(char *json, label_t *labels, size_t labels_num) {
- yajl_callbacks funcs = {
- .yajl_string = test_string, .yajl_map_key = test_map_key,
- };
-
- test_case_t c = {labels, labels_num, NULL};
-
- yajl_handle hndl;
-#if HAVE_YAJL_V2
- CHECK_NOT_NULL(hndl = yajl_alloc(&funcs, /* alloc = */ NULL, &c));
-#else
- CHECK_NOT_NULL(
- hndl = yajl_alloc(&funcs, /* config = */ NULL, /* alloc = */ NULL, &c));
-#endif
- OK(yajl_parse(hndl, (unsigned char *)json, strlen(json)) == yajl_status_ok);
-
- yajl_free(hndl);
- return 0;
-}
-
-DEF_TEST(notification) {
- label_t labels[] = {
- {"summary", "this is a message"},
- {"alertname", "collectd_unit_test"},
- {"instance", "example.com"},
- {"service", "collectd"},
- {"unit", "case"},
- };
-
- /* 1448284606.125 ^= 1555083754651779072 */
- notification_t n = {NOTIF_WARNING,
- 1555083754651779072ULL,
- "this is a message",
- "example.com",
- "unit",
- "",
- "test",
- "case",
- NULL};
-
- char got[1024];
- CHECK_ZERO(format_json_notification(got, sizeof(got), &n));
- // printf ("got = \"%s\";\n", got);
-
- return expect_json_labels(got, labels, STATIC_ARRAY_SIZE(labels));
-}
-
-int main(void) {
- RUN_TEST(notification);
-
- END_TEST;
-}
+++ /dev/null
-/**
- * collectd - src/utils_format_kairosdb.c
- * Copyright (C) 2016 Aurelien beorn Rougemont
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Aurelien beorn Rougemont <beorn at gandi dot net>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_cache.h"
-#include "utils_format_kairosdb.h"
-
-/* This is the KAIROSDB format for write_http output
- *
- * Target format
- * [
- * {
- * "name":"collectd.vmem"
- * "datapoints":
- * [
- * [1453897164060, 97.000000]
- * ],
- * "tags":
- * {
- * "host": "fqdn.domain.tld",
- * "plugin_instance": "vmpage_number",
- * "type": "kernel_stack",
- * "ds": "value"
- * ""
- * }
- * }
- * ]
- */
-
-static int kairosdb_escape_string(char *buffer, size_t buffer_size, /* {{{ */
- const char *string) {
- size_t dst_pos;
-
- if ((buffer == NULL) || (string == NULL))
- return -EINVAL;
-
- if (buffer_size < 3)
- return -ENOMEM;
-
- dst_pos = 0;
-
-#define BUFFER_ADD(c) \
- do { \
- if (dst_pos >= (buffer_size - 1)) { \
- buffer[buffer_size - 1] = '\0'; \
- return -ENOMEM; \
- } \
- buffer[dst_pos] = (c); \
- dst_pos++; \
- } while (0)
-
- /* Escape special characters */
- /* authorize -_. and alpha num but also escapes " */
- BUFFER_ADD('"');
- for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) {
- if (isalnum(string[src_pos]) || 0x2d == string[src_pos] ||
- 0x2e == string[src_pos] || 0x5f == string[src_pos])
- BUFFER_ADD(tolower(string[src_pos]));
- } /* for */
- BUFFER_ADD('"');
- buffer[dst_pos] = 0;
-
-#undef BUFFER_ADD
-
- return 0;
-} /* }}} int kairosdb_escape_string */
-
-static int values_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds, const value_list_t *vl,
- int store_rates, size_t ds_idx) {
- size_t offset = 0;
- gauge_t *rates = NULL;
-
- memset(buffer, 0, buffer_size);
-
-#define BUFFER_ADD(...) \
- do { \
- int status; \
- status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
- if (status < 1) { \
- sfree(rates); \
- return -1; \
- } else if (((size_t)status) >= (buffer_size - offset)) { \
- sfree(rates); \
- return -ENOMEM; \
- } else \
- offset += ((size_t)status); \
- } while (0)
-
- if (ds->ds[ds_idx].type == DS_TYPE_GAUGE) {
- if (isfinite(vl->values[ds_idx].gauge)) {
- BUFFER_ADD("[[");
- BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
- BUFFER_ADD(",");
- BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[ds_idx].gauge);
- } else {
- DEBUG("utils_format_kairosdb: invalid vl->values[ds_idx].gauge for "
- "%s|%s|%s|%s|%s",
- vl->plugin, vl->plugin_instance, vl->type, vl->type_instance,
- ds->ds[ds_idx].name);
- return -1;
- }
- } else if (store_rates) {
- if (rates == NULL)
- rates = uc_get_rate(ds, vl);
- if (rates == NULL) {
- WARNING("utils_format_kairosdb: uc_get_rate failed for %s|%s|%s|%s|%s",
- vl->plugin, vl->plugin_instance, vl->type, vl->type_instance,
- ds->ds[ds_idx].name);
-
- return -1;
- }
-
- if (isfinite(rates[ds_idx])) {
- BUFFER_ADD("[[");
- BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
- BUFFER_ADD(",");
- BUFFER_ADD(JSON_GAUGE_FORMAT, rates[ds_idx]);
- } else {
- WARNING("utils_format_kairosdb: invalid rates[ds_idx] for %s|%s|%s|%s|%s",
- vl->plugin, vl->plugin_instance, vl->type, vl->type_instance,
- ds->ds[ds_idx].name);
- sfree(rates);
- return -1;
- }
- } else if (ds->ds[ds_idx].type == DS_TYPE_COUNTER) {
- BUFFER_ADD("[[");
- BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
- BUFFER_ADD(",");
- BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_idx].counter);
- } else if (ds->ds[ds_idx].type == DS_TYPE_DERIVE) {
- BUFFER_ADD("[[");
- BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
- BUFFER_ADD(",");
- BUFFER_ADD("%" PRIi64, vl->values[ds_idx].derive);
- } else if (ds->ds[ds_idx].type == DS_TYPE_ABSOLUTE) {
- BUFFER_ADD("[[");
- BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time));
- BUFFER_ADD(",");
- BUFFER_ADD("%" PRIu64, vl->values[ds_idx].absolute);
- } else {
- ERROR("format_kairosdb: Unknown data source type: %i", ds->ds[ds_idx].type);
- sfree(rates);
- return -1;
- }
- BUFFER_ADD("]]");
-
-#undef BUFFER_ADD
-
- DEBUG("format_kairosdb: values_to_kairosdb: buffer = %s;", buffer);
- sfree(rates);
- return 0;
-} /* }}} int values_to_kairosdb */
-
-static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds, const value_list_t *vl,
- int store_rates,
- char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl,
- char const *metrics_prefix) {
- char temp[512];
- size_t offset = 0;
- int status;
-
- memset(buffer, 0, buffer_size);
-
-#define BUFFER_ADD(...) \
- do { \
- status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \
- if (status < 1) \
- return -1; \
- else if (((size_t)status) >= (buffer_size - offset)) \
- return -ENOMEM; \
- else \
- offset += ((size_t)status); \
- } while (0)
-
-#define BUFFER_ADD_KEYVAL(key, value) \
- do { \
- status = kairosdb_escape_string(temp, sizeof(temp), (value)); \
- if (status != 0) \
- return status; \
- BUFFER_ADD(",\"%s\": %s", (key), temp); \
- } while (0)
-
- for (size_t i = 0; i < ds->ds_num; i++) {
- /* All value lists have a leading comma. The first one will be replaced with
- * a square bracket in `format_kairosdb_finalize'. */
- BUFFER_ADD(",{\"name\":\"");
-
- if (metrics_prefix != NULL) {
- BUFFER_ADD("%s.", metrics_prefix);
- }
-
- BUFFER_ADD("%s", vl->plugin);
-
- status = values_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, i);
- if (status != 0)
- return status;
-
- BUFFER_ADD("\", \"datapoints\": %s", temp);
-
- /*
- * Now adds meta data to metric as tags
- */
-
- memset(temp, 0, sizeof(temp));
-
- if (data_ttl != 0)
- BUFFER_ADD(", \"ttl\": %i", data_ttl);
-
- BUFFER_ADD(", \"tags\":\{");
-
- BUFFER_ADD("\"host\": \"%s\"", vl->host);
- for (size_t j = 0; j < http_attrs_num; j += 2) {
- BUFFER_ADD(", \"%s\":", http_attrs[j]);
- BUFFER_ADD(" \"%s\"", http_attrs[j + 1]);
- }
-
- if (strlen(vl->plugin_instance))
- BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance);
- BUFFER_ADD_KEYVAL("type", vl->type);
- if (strlen(vl->type_instance))
- BUFFER_ADD_KEYVAL("type_instance", vl->type_instance);
- if (ds->ds_num != 1)
- BUFFER_ADD_KEYVAL("ds", ds->ds[i].name);
- BUFFER_ADD("}}");
- } /* for ds->ds_num */
-
-#undef BUFFER_ADD_KEYVAL
-#undef BUFFER_ADD
-
- DEBUG("format_kairosdb: value_list_to_kairosdb: buffer = %s;", buffer);
-
- return 0;
-} /* }}} int value_list_to_kairosdb */
-
-static int format_kairosdb_value_list_nocheck(
- char *buffer, /* {{{ */
- size_t *ret_buffer_fill, size_t *ret_buffer_free, const data_set_t *ds,
- const value_list_t *vl, int store_rates, size_t temp_size,
- char const *const *http_attrs, size_t http_attrs_num, int data_ttl,
- char const *metrics_prefix) {
- char temp[temp_size];
- int status;
-
- status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates,
- http_attrs, http_attrs_num, data_ttl,
- metrics_prefix);
- if (status != 0)
- return status;
- temp_size = strlen(temp);
-
- memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1);
- (*ret_buffer_fill) += temp_size;
- (*ret_buffer_free) -= temp_size;
-
- return 0;
-} /* }}} int format_kairosdb_value_list_nocheck */
-
-int format_kairosdb_initialize(char *buffer, /* {{{ */
- size_t *ret_buffer_fill,
- size_t *ret_buffer_free) {
- size_t buffer_fill;
- size_t buffer_free;
-
- if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
- (ret_buffer_free == NULL))
- return -EINVAL;
-
- buffer_fill = *ret_buffer_fill;
- buffer_free = *ret_buffer_free;
-
- buffer_free = buffer_fill + buffer_free;
- buffer_fill = 0;
-
- if (buffer_free < 3)
- return -ENOMEM;
-
- memset(buffer, 0, buffer_free);
- *ret_buffer_fill = buffer_fill;
- *ret_buffer_free = buffer_free;
-
- return 0;
-} /* }}} int format_kairosdb_initialize */
-
-int format_kairosdb_finalize(char *buffer, /* {{{ */
- size_t *ret_buffer_fill, size_t *ret_buffer_free) {
- size_t pos;
-
- if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
- (ret_buffer_free == NULL))
- return -EINVAL;
-
- if (*ret_buffer_free < 2)
- return -ENOMEM;
-
- /* Replace the leading comma added in `value_list_to_kairosdb' with a square
- * bracket. */
- if (buffer[0] != ',')
- return -EINVAL;
- buffer[0] = '[';
-
- pos = *ret_buffer_fill;
- buffer[pos] = ']';
- buffer[pos + 1] = 0;
-
- (*ret_buffer_fill)++;
- (*ret_buffer_free)--;
-
- return 0;
-} /* }}} int format_kairosdb_finalize */
-
-int format_kairosdb_value_list(char *buffer, /* {{{ */
- size_t *ret_buffer_fill, size_t *ret_buffer_free,
- const data_set_t *ds, const value_list_t *vl,
- int store_rates, char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl,
- char const *metrics_prefix) {
- if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
- (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
- return -EINVAL;
-
- if (*ret_buffer_free < 3)
- return -ENOMEM;
-
- return format_kairosdb_value_list_nocheck(
- buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates,
- (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl,
- metrics_prefix);
-} /* }}} int format_kairosdb_value_list */
+++ /dev/null
-/**
- * collectd - src/utils_format_kairosdb.h
- * Copyright (C) 2016 Aurelien Rougemont
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Aurelien beorn Rougemont <beorn at gandi dot net>
- **/
-
-#ifndef UTILS_FORMAT_KAIROSDB_H
-#define UTILS_FORMAT_KAIROSDB_H 1
-
-#include "collectd.h"
-
-#include "plugin.h"
-
-#ifndef JSON_GAUGE_FORMAT
-#define JSON_GAUGE_FORMAT GAUGE_FORMAT
-#endif
-
-int format_kairosdb_initialize(char *buffer, size_t *ret_buffer_fill,
- size_t *ret_buffer_free);
-int format_kairosdb_value_list(char *buffer, size_t *ret_buffer_fill,
- size_t *ret_buffer_free, const data_set_t *ds,
- const value_list_t *vl, int store_rates,
- char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl,
- char const *metrics_prefix);
-int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill,
- size_t *ret_buffer_free);
-
-#endif /* UTILS_FORMAT_KAIROSDB_H */
+++ /dev/null
-/**
- * collectd - src/utils_format_stackdriver.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "utils_format_stackdriver.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_avltree.h"
-#include "utils_cache.h"
-#include "utils_time.h"
-
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_parse.h>
-#if HAVE_YAJL_YAJL_VERSION_H
-#include <yajl/yajl_version.h>
-#endif
-
-struct sd_output_s {
- sd_resource_t *res;
- yajl_gen gen;
- c_avl_tree_t *staged;
- c_avl_tree_t *metric_descriptors;
-};
-
-struct sd_label_s {
- char *key;
- char *value;
-};
-typedef struct sd_label_s sd_label_t;
-
-struct sd_resource_s {
- char *type;
-
- sd_label_t *labels;
- size_t labels_num;
-};
-
-static int json_string(yajl_gen gen, char const *s) /* {{{ */
-{
- yajl_gen_status status =
- yajl_gen_string(gen, (unsigned char const *)s, strlen(s));
- if (status != yajl_gen_status_ok)
- return (int)status;
-
- return 0;
-} /* }}} int json_string */
-
-static int json_time(yajl_gen gen, cdtime_t t) {
- char buffer[64];
-
- size_t status = rfc3339(buffer, sizeof(buffer), t);
- if (status != 0) {
- return status;
- }
-
- return json_string(gen, buffer);
-} /* }}} int json_time */
-
-/* MonitoredResource
- *
- * {
- * "type": "library.googleapis.com/book",
- * "labels": {
- * "/genre": "fiction",
- * "/media": "paper"
- * "/title": "The Old Man and the Sea"
- * }
- * }
- */
-static int format_gcm_resource(yajl_gen gen, sd_resource_t *res) /* {{{ */
-{
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "type") || json_string(gen, res->type);
- if (status != 0)
- return status;
-
- if (res->labels_num != 0) {
- status = json_string(gen, "labels");
- if (status != 0)
- return status;
-
- yajl_gen_map_open(gen);
- for (size_t i = 0; i < res->labels_num; i++) {
- status = json_string(gen, res->labels[i].key) ||
- json_string(gen, res->labels[i].value);
- if (status != 0)
- return status;
- }
- yajl_gen_map_close(gen);
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_gcm_resource */
-
-/* TypedValue
- *
- * {
- * // Union field, only one of the following:
- * "int64Value": string,
- * "doubleValue": number,
- * }
- */
-static int format_typed_value(yajl_gen gen, int ds_type, value_t v,
- int64_t start_value) {
- char integer[32];
-
- yajl_gen_map_open(gen);
-
- switch (ds_type) {
- case DS_TYPE_GAUGE: {
- int status = json_string(gen, "doubleValue");
- if (status != 0)
- return status;
-
- status = (int)yajl_gen_double(gen, (double)v.gauge);
- if (status != yajl_gen_status_ok)
- return status;
-
- yajl_gen_map_close(gen);
- return 0;
- }
- case DS_TYPE_DERIVE: {
- derive_t diff = v.derive - (derive_t)start_value;
- snprintf(integer, sizeof(integer), "%" PRIi64, diff);
- break;
- }
- case DS_TYPE_COUNTER: {
- counter_t diff = counter_diff((counter_t)start_value, v.counter);
- snprintf(integer, sizeof(integer), "%llu", diff);
- break;
- }
- case DS_TYPE_ABSOLUTE: {
- snprintf(integer, sizeof(integer), "%" PRIu64, v.absolute);
- break;
- }
- default: {
- ERROR("format_typed_value: unknown value type %d.", ds_type);
- return EINVAL;
- }
- }
-
- int status = json_string(gen, "int64Value") || json_string(gen, integer);
- if (status != 0) {
- return status;
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_typed_value */
-
-/* MetricKind
- *
- * enum(
- * "CUMULATIVE",
- * "GAUGE"
- * )
-*/
-static int format_metric_kind(yajl_gen gen, int ds_type) {
- switch (ds_type) {
- case DS_TYPE_GAUGE:
- case DS_TYPE_ABSOLUTE:
- return json_string(gen, "GAUGE");
- case DS_TYPE_COUNTER:
- case DS_TYPE_DERIVE:
- return json_string(gen, "CUMULATIVE");
- default:
- ERROR("format_metric_kind: unknown value type %d.", ds_type);
- return EINVAL;
- }
-}
-
-/* ValueType
- *
- * enum(
- * "DOUBLE",
- * "INT64"
- * )
-*/
-static int format_value_type(yajl_gen gen, int ds_type) {
- return json_string(gen, (ds_type == DS_TYPE_GAUGE) ? "DOUBLE" : "INT64");
-}
-
-static int metric_type(char *buffer, size_t buffer_size, data_set_t const *ds,
- value_list_t const *vl, int ds_index) {
- /* {{{ */
- char const *ds_name = ds->ds[ds_index].name;
-
-#define GCM_PREFIX "custom.googleapis.com/collectd/"
- if ((ds_index != 0) || strcmp("value", ds_name) != 0) {
- snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s_%s", vl->plugin, vl->type,
- ds_name);
- } else {
- snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s", vl->plugin, vl->type);
- }
-
- char const *whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789_/";
- char *ptr = buffer + strlen(GCM_PREFIX);
- size_t ok_len;
- while ((ok_len = strspn(ptr, whitelist)) != strlen(ptr)) {
- ptr[ok_len] = '_';
- ptr += ok_len;
- }
-
- return 0;
-} /* }}} int metric_type */
-
-/* The metric type, including its DNS name prefix. The type is not URL-encoded.
- * All user-defined custom metric types have the DNS name custom.googleapis.com.
- * Metric types should use a natural hierarchical grouping. */
-static int format_metric_type(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index) {
- /* {{{ */
- char buffer[4 * DATA_MAX_NAME_LEN];
- metric_type(buffer, sizeof(buffer), ds, vl, ds_index);
-
- return json_string(gen, buffer);
-} /* }}} int format_metric_type */
-
-/* TimeInterval
- *
- * {
- * "endTime": string,
- * "startTime": string,
- * }
- */
-static int format_time_interval(yajl_gen gen, int ds_type,
- value_list_t const *vl, cdtime_t start_time) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "endTime") || json_time(gen, vl->time);
- if (status != 0)
- return status;
-
- if ((ds_type == DS_TYPE_DERIVE) || (ds_type == DS_TYPE_COUNTER)) {
- int status = json_string(gen, "startTime") || json_time(gen, start_time);
- if (status != 0)
- return status;
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_time_interval */
-
-/* read_cumulative_state reads the start time and start value of cumulative
- * (i.e. DERIVE or COUNTER) metrics from the cache. If a metric is seen for the
- * first time, or when a DERIVE metric is reset, the start time is (re)set to
- * vl->time. */
-static int read_cumulative_state(data_set_t const *ds, value_list_t const *vl,
- int ds_index, cdtime_t *ret_start_time,
- int64_t *ret_start_value) {
- int ds_type = ds->ds[ds_index].type;
- if ((ds_type != DS_TYPE_DERIVE) && (ds_type != DS_TYPE_COUNTER)) {
- return 0;
- }
-
- char start_value_key[DATA_MAX_NAME_LEN];
- snprintf(start_value_key, sizeof(start_value_key),
- "stackdriver:start_value[%d]", ds_index);
-
- int status =
- uc_meta_data_get_signed_int(vl, start_value_key, ret_start_value);
- if ((status == 0) && ((ds_type != DS_TYPE_DERIVE) ||
- (*ret_start_value <= vl->values[ds_index].derive))) {
- return uc_meta_data_get_unsigned_int(vl, "stackdriver:start_time",
- ret_start_time);
- }
-
- if (ds_type == DS_TYPE_DERIVE) {
- *ret_start_value = vl->values[ds_index].derive;
- } else {
- *ret_start_value = (int64_t)vl->values[ds_index].counter;
- }
- *ret_start_time = vl->time;
-
- status = uc_meta_data_add_signed_int(vl, start_value_key, *ret_start_value);
- if (status != 0) {
- return status;
- }
- return uc_meta_data_add_unsigned_int(vl, "stackdriver:start_time",
- *ret_start_time);
-} /* int read_cumulative_state */
-
-/* Point
- *
- * {
- * "interval": {
- * object(TimeInterval)
- * },
- * "value": {
- * object(TypedValue)
- * },
- * }
- */
-static int format_point(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index,
- cdtime_t start_time, int64_t start_value) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int ds_type = ds->ds[ds_index].type;
-
- int status =
- json_string(gen, "interval") ||
- format_time_interval(gen, ds_type, vl, start_time) ||
- json_string(gen, "value") ||
- format_typed_value(gen, ds_type, vl->values[ds_index], start_value);
- if (status != 0)
- return status;
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_point */
-
-/* Metric
- *
- * {
- * "type": string,
- * "labels": {
- * string: string,
- * ...
- * },
- * }
- */
-static int format_metric(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "type") ||
- format_metric_type(gen, ds, vl, ds_index) ||
- json_string(gen, "labels");
- if (status != 0) {
- return status;
- }
-
- yajl_gen_map_open(gen);
- status = json_string(gen, "host") || json_string(gen, vl->host) ||
- json_string(gen, "plugin_instance") ||
- json_string(gen, vl->plugin_instance) ||
- json_string(gen, "type_instance") ||
- json_string(gen, vl->type_instance);
- if (status != 0) {
- return status;
- }
- yajl_gen_map_close(gen);
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_metric */
-
-/* TimeSeries
- *
- * {
- * "metric": {
- * object(Metric)
- * },
- * "resource": {
- * object(MonitoredResource)
- * },
- * "metricKind": enum(MetricKind),
- * "valueType": enum(ValueType),
- * "points": [
- * {
- * object(Point)
- * }
- * ],
- * }
- */
-/* format_time_series formats a TimeSeries object. Returns EAGAIN when a
- * cumulative metric is seen for the first time and cannot be sent to
- * Stackdriver due to lack of state. */
-static int format_time_series(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index,
- sd_resource_t *res) {
- int ds_type = ds->ds[ds_index].type;
-
- cdtime_t start_time = 0;
- int64_t start_value = 0;
- int status =
- read_cumulative_state(ds, vl, ds_index, &start_time, &start_value);
- if (status != 0) {
- return status;
- }
- if (start_time == vl->time) {
- /* for cumulative metrics, the interval must not be zero. */
- return EAGAIN;
- }
-
- yajl_gen_map_open(gen);
-
- status = json_string(gen, "metric") || format_metric(gen, ds, vl, ds_index) ||
- json_string(gen, "resource") || format_gcm_resource(gen, res) ||
- json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
- json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
- json_string(gen, "points");
- if (status != 0)
- return status;
-
- yajl_gen_array_open(gen);
-
- status = format_point(gen, ds, vl, ds_index, start_time, start_value);
- if (status != 0)
- return status;
-
- yajl_gen_array_close(gen);
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_time_series */
-
-/* Request body
- *
- * {
- * "timeSeries": [
- * {
- * object(TimeSeries)
- * }
- * ],
- * }
- */
-static int sd_output_initialize(sd_output_t *out) /* {{{ */
-{
- yajl_gen_map_open(out->gen);
-
- int status = json_string(out->gen, "timeSeries");
- if (status != 0) {
- return status;
- }
-
- yajl_gen_array_open(out->gen);
- return 0;
-} /* }}} int sd_output_initialize */
-
-static int sd_output_finalize(sd_output_t *out) /* {{{ */
-{
- yajl_gen_array_close(out->gen);
- yajl_gen_map_close(out->gen);
-
- return 0;
-} /* }}} int sd_output_finalize */
-
-static void sd_output_reset_staged(sd_output_t *out) /* {{{ */
-{
- void *key = NULL;
-
- while (c_avl_pick(out->staged, &key, &(void *){NULL}) == 0)
- sfree(key);
-} /* }}} void sd_output_reset_staged */
-
-sd_output_t *sd_output_create(sd_resource_t *res) /* {{{ */
-{
- sd_output_t *out = calloc(1, sizeof(*out));
- if (out == NULL)
- return NULL;
-
- out->res = res;
-
- out->gen = yajl_gen_alloc(/* funcs = */ NULL);
- if (out->gen == NULL) {
- sd_output_destroy(out);
- return NULL;
- }
-
- out->staged = c_avl_create((void *)strcmp);
- if (out->staged == NULL) {
- sd_output_destroy(out);
- return NULL;
- }
-
- out->metric_descriptors = c_avl_create((void *)strcmp);
- if (out->metric_descriptors == NULL) {
- sd_output_destroy(out);
- return NULL;
- }
-
- sd_output_initialize(out);
-
- return out;
-} /* }}} sd_output_t *sd_output_create */
-
-void sd_output_destroy(sd_output_t *out) /* {{{ */
-{
- if (out == NULL)
- return;
-
- if (out->metric_descriptors != NULL) {
- void *key = NULL;
- while (c_avl_pick(out->metric_descriptors, &key, &(void *){NULL}) == 0) {
- sfree(key);
- }
- c_avl_destroy(out->metric_descriptors);
- out->metric_descriptors = NULL;
- }
-
- if (out->staged != NULL) {
- sd_output_reset_staged(out);
- c_avl_destroy(out->staged);
- out->staged = NULL;
- }
-
- if (out->gen != NULL) {
- yajl_gen_free(out->gen);
- out->gen = NULL;
- }
-
- if (out->res != NULL) {
- sd_resource_destroy(out->res);
- out->res = NULL;
- }
-
- sfree(out);
-} /* }}} void sd_output_destroy */
-
-int sd_output_add(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl) /* {{{ */
-{
- /* first, check that we have all appropriate metric descriptors. */
- for (size_t i = 0; i < ds->ds_num; i++) {
- char buffer[4 * DATA_MAX_NAME_LEN];
- metric_type(buffer, sizeof(buffer), ds, vl, i);
-
- if (c_avl_get(out->metric_descriptors, buffer, NULL) != 0) {
- return ENOENT;
- }
- }
-
- char key[6 * DATA_MAX_NAME_LEN];
- int status = FORMAT_VL(key, sizeof(key), vl);
- if (status != 0) {
- ERROR("sd_output_add: FORMAT_VL failed with status %d.", status);
- return status;
- }
-
- if (c_avl_get(out->staged, key, NULL) == 0) {
- return EEXIST;
- }
-
- _Bool staged = 0;
- for (size_t i = 0; i < ds->ds_num; i++) {
- int status = format_time_series(out->gen, ds, vl, i, out->res);
- if (status == EAGAIN) {
- /* first instance of a cumulative metric */
- continue;
- }
- if (status != 0) {
- ERROR("sd_output_add: format_time_series failed with status %d.", status);
- return status;
- }
- staged = 1;
- }
-
- if (staged) {
- c_avl_insert(out->staged, strdup(key), NULL);
- }
-
- size_t json_buffer_size = 0;
- yajl_gen_get_buf(out->gen, &(unsigned char const *){NULL}, &json_buffer_size);
- if (json_buffer_size > 65535)
- return ENOBUFS;
-
- return 0;
-} /* }}} int sd_output_add */
-
-int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl) {
- /* {{{ */
- for (size_t i = 0; i < ds->ds_num; i++) {
- char buffer[4 * DATA_MAX_NAME_LEN];
- metric_type(buffer, sizeof(buffer), ds, vl, i);
-
- char *key = strdup(buffer);
- int status = c_avl_insert(out->metric_descriptors, key, NULL);
- if (status != 0) {
- sfree(key);
- return status;
- }
- }
-
- return 0;
-} /* }}} int sd_output_register_metric */
-
-char *sd_output_reset(sd_output_t *out) /* {{{ */
-{
- sd_output_finalize(out);
-
- unsigned char const *json_buffer = NULL;
- yajl_gen_get_buf(out->gen, &json_buffer, &(size_t){0});
- char *ret = strdup((void const *)json_buffer);
-
- sd_output_reset_staged(out);
-
- yajl_gen_free(out->gen);
- out->gen = yajl_gen_alloc(/* funcs = */ NULL);
-
- sd_output_initialize(out);
-
- return ret;
-} /* }}} char *sd_output_reset */
-
-sd_resource_t *sd_resource_create(char const *type) /* {{{ */
-{
- sd_resource_t *res = malloc(sizeof(*res));
- if (res == NULL)
- return NULL;
- memset(res, 0, sizeof(*res));
-
- res->type = strdup(type);
- if (res->type == NULL) {
- sfree(res);
- return NULL;
- }
-
- res->labels = NULL;
- res->labels_num = 0;
-
- return res;
-} /* }}} sd_resource_t *sd_resource_create */
-
-void sd_resource_destroy(sd_resource_t *res) /* {{{ */
-{
- if (res == NULL)
- return;
-
- for (size_t i = 0; i < res->labels_num; i++) {
- sfree(res->labels[i].key);
- sfree(res->labels[i].value);
- }
- sfree(res->labels);
- sfree(res->type);
- sfree(res);
-} /* }}} void sd_resource_destroy */
-
-int sd_resource_add_label(sd_resource_t *res, char const *key,
- char const *value) /* {{{ */
-{
- if ((res == NULL) || (key == NULL) || (value == NULL))
- return EINVAL;
-
- sd_label_t *l =
- realloc(res->labels, sizeof(*res->labels) * (res->labels_num + 1));
- if (l == NULL)
- return ENOMEM;
-
- res->labels = l;
- l = res->labels + res->labels_num;
-
- l->key = strdup(key);
- l->value = strdup(value);
- if ((l->key == NULL) || (l->value == NULL)) {
- sfree(l->key);
- sfree(l->value);
- return ENOMEM;
- }
-
- res->labels_num++;
- return 0;
-} /* }}} int sd_resource_add_label */
-
-/* LabelDescriptor
- *
- * {
- * "key": string,
- * "valueType": enum(ValueType),
- * "description": string,
- * }
- */
-static int format_label_descriptor(yajl_gen gen, char const *key) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "key") || json_string(gen, key) ||
- json_string(gen, "valueType") || json_string(gen, "STRING");
- if (status != 0) {
- return status;
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_label_descriptor */
-
-/* MetricDescriptor
- *
- * {
- * "name": string,
- * "type": string,
- * "labels": [
- * {
- * object(LabelDescriptor)
- * }
- * ],
- * "metricKind": enum(MetricKind),
- * "valueType": enum(ValueType),
- * "unit": string,
- * "description": string,
- * "displayName": string,
- * }
- */
-int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
- data_set_t const *ds, value_list_t const *vl,
- int ds_index) {
- /* {{{ */
- yajl_gen gen = yajl_gen_alloc(/* funcs = */ NULL);
- if (gen == NULL) {
- return ENOMEM;
- }
-
- int ds_type = ds->ds[ds_index].type;
-
- yajl_gen_map_open(gen);
-
- int status =
- json_string(gen, "type") || format_metric_type(gen, ds, vl, ds_index) ||
- json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
- json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
- json_string(gen, "labels");
- if (status != 0) {
- yajl_gen_free(gen);
- return status;
- }
-
- char const *labels[] = {"host", "plugin_instance", "type_instance"};
- yajl_gen_array_open(gen);
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(labels); i++) {
- int status = format_label_descriptor(gen, labels[i]);
- if (status != 0) {
- yajl_gen_free(gen);
- return status;
- }
- }
-
- yajl_gen_array_close(gen);
- yajl_gen_map_close(gen);
-
- unsigned char const *tmp = NULL;
- yajl_gen_get_buf(gen, &tmp, &(size_t){0});
- sstrncpy(buffer, (void const *)tmp, buffer_size);
-
- yajl_gen_free(gen);
- return 0;
-} /* }}} int sd_format_metric_descriptor */
+++ /dev/null
-/**
- * collectd - src/utils_format_stackdriver.h
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_FORMAT_STACKDRIVER_H
-#define UTILS_FORMAT_STACKDRIVER_H 1
-
-#include "collectd.h"
-#include "plugin.h"
-
-/* sd_output_t is a buffer to which value_list_t* can be added and from which
- * an appropriately formatted char* can be read. */
-struct sd_output_s;
-typedef struct sd_output_s sd_output_t;
-
-/* sd_resource_t represents a MonitoredResource. */
-struct sd_resource_s;
-typedef struct sd_resource_s sd_resource_t;
-
-sd_output_t *sd_output_create(sd_resource_t *res);
-
-/* sd_output_destroy frees all memory used by out, including the
- * sd_resource_t* passed to sd_output_create. */
-void sd_output_destroy(sd_output_t *out);
-
-/* sd_output_add adds a value_list_t* to "out".
- *
- * Return values:
- * - 0 Success
- * - ENOBUFS Success, but the buffer should be flushed soon.
- * - EEXIST The value list is already encoded in the buffer.
- * Flush the buffer, then call sd_output_add again.
- * - ENOENT First time we encounter this metric. Create a metric descriptor
- * using the Stackdriver API and then call
- * sd_output_register_metric.
- */
-int sd_output_add(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl);
-
-/* sd_output_register_metric adds the metric descriptor which vl maps to, to
- * the list of known metric descriptors. */
-int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl);
-
-/* sd_output_reset resets the output and returns the previous content of the
- * buffer. It is the caller's responsibility to call free() with the returned
- * pointer. */
-char *sd_output_reset(sd_output_t *out);
-
-sd_resource_t *sd_resource_create(char const *type);
-void sd_resource_destroy(sd_resource_t *res);
-int sd_resource_add_label(sd_resource_t *res, char const *key,
- char const *value);
-
-/* sd_format_metric_descriptor creates the payload for a
- * projects.metricDescriptors.create() request. */
-int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
- data_set_t const *ds, value_list_t const *vl,
- int ds_index);
-
-#endif /* UTILS_FORMAT_STACKDRIVER_H */
+++ /dev/null
-/**
- * collectd - src/utils_format_stackdriver_test.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "testing.h"
-#include "utils_format_stackdriver.h"
-
-DEF_TEST(sd_format_metric_descriptor) {
- value_list_t vl = {
- .host = "example.com", .plugin = "unit-test", .type = "example",
- };
- char got[1024];
-
- data_set_t ds_single = {
- .type = "example",
- .ds_num = 1,
- .ds =
- &(data_source_t){
- .name = "value", .type = DS_TYPE_GAUGE, .min = NAN, .max = NAN,
- },
- };
- EXPECT_EQ_INT(
- 0, sd_format_metric_descriptor(got, sizeof(got), &ds_single, &vl, 0));
- char const *want_single =
- "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
- "example\",\"metricKind\":\"GAUGE\",\"valueType\":\"DOUBLE\",\"labels\":["
- "{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":\"plugin_"
- "instance\",\"valueType\":\"STRING\"},{\"key\":\"type_instance\","
- "\"valueType\":\"STRING\"}]}";
- EXPECT_EQ_STR(want_single, got);
-
- data_set_t ds_double = {
- .type = "example",
- .ds_num = 2,
- .ds =
- (data_source_t[]){
- {.name = "one", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
- {.name = "two", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
- },
- };
- EXPECT_EQ_INT(
- 0, sd_format_metric_descriptor(got, sizeof(got), &ds_double, &vl, 0));
- char const *want_double =
- "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
- "example_one\",\"metricKind\":\"CUMULATIVE\",\"valueType\":\"INT64\","
- "\"labels\":[{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":"
- "\"plugin_instance\",\"valueType\":\"STRING\"},{\"key\":\"type_"
- "instance\",\"valueType\":\"STRING\"}]}";
- EXPECT_EQ_STR(want_double, got);
- return 0;
-}
-
-int main(int argc, char **argv) {
- RUN_TEST(sd_format_metric_descriptor);
-
- END_TEST;
-}
+++ /dev/null
-/**
- * collectd - src/utils_gce.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_gce.h"
-#include "utils_oauth.h"
-#include "utils_time.h"
-
-#include <curl/curl.h>
-
-#ifndef GCP_METADATA_PREFIX
-#define GCP_METADATA_PREFIX "http://metadata.google.internal/computeMetadata/v1"
-#endif
-#ifndef GCE_METADATA_HEADER
-#define GCE_METADATA_HEADER "Metadata-Flavor: Google"
-#endif
-
-#ifndef GCE_INSTANCE_ID_URL
-#define GCE_INSTANCE_ID_URL GCP_METADATA_PREFIX "/instance/id"
-#endif
-#ifndef GCE_PROJECT_NUM_URL
-#define GCE_PROJECT_NUM_URL GCP_METADATA_PREFIX "/project/numeric-project-id"
-#endif
-#ifndef GCE_PROJECT_ID_URL
-#define GCE_PROJECT_ID_URL GCP_METADATA_PREFIX "/project/project-id"
-#endif
-#ifndef GCE_ZONE_URL
-#define GCE_ZONE_URL GCP_METADATA_PREFIX "/instance/zone"
-#endif
-
-#ifndef GCE_DEFAULT_SERVICE_ACCOUNT
-#define GCE_DEFAULT_SERVICE_ACCOUNT "default"
-#endif
-
-#ifndef GCE_SCOPE_URL
-#define GCE_SCOPE_URL_FORMAT \
- GCP_METADATA_PREFIX "/instance/service-accounts/%s/scopes"
-#endif
-#ifndef GCE_TOKEN_URL
-#define GCE_TOKEN_URL_FORMAT \
- GCP_METADATA_PREFIX "/instance/service-accounts/%s/token"
-#endif
-
-struct blob_s {
- char *data;
- size_t size;
-};
-typedef struct blob_s blob_t;
-
-static int on_gce = -1;
-
-static char *token = NULL;
-static char *token_email = NULL;
-static cdtime_t token_valid_until = 0;
-static pthread_mutex_t token_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static size_t write_callback(void *contents, size_t size, size_t nmemb,
- void *ud) /* {{{ */
-{
- size_t realsize = size * nmemb;
- blob_t *blob = ud;
-
- if ((0x7FFFFFF0 < blob->size) || (0x7FFFFFF0 - blob->size < realsize)) {
- ERROR("utils_gce: write_callback: integer overflow");
- return 0;
- }
-
- blob->data = realloc(blob->data, blob->size + realsize + 1);
- if (blob->data == NULL) {
- /* out of memory! */
- ERROR(
- "utils_gce: write_callback: not enough memory (realloc returned NULL)");
- return 0;
- }
-
- memcpy(blob->data + blob->size, contents, realsize);
- blob->size += realsize;
- blob->data[blob->size] = 0;
-
- return realsize;
-} /* }}} size_t write_callback */
-
-/* read_url will issue a GET request for the given URL, setting the magic GCE
- * metadata header in the process. On success, the response body is returned
- * and it's the caller's responsibility to free it. On failure, an error is
- * logged and NULL is returned. */
-static char *read_url(char const *url) /* {{{ */
-{
- CURL *curl = curl_easy_init();
- if (!curl) {
- ERROR("utils_gce: curl_easy_init failed.");
- return NULL;
- }
-
- struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
-
- char curl_errbuf[CURL_ERROR_SIZE];
- blob_t blob = {0};
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &blob);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_URL, url);
-
- int status = curl_easy_perform(curl);
- if (status != CURLE_OK) {
- ERROR("utils_gce: fetching %s failed: %s", url, curl_errbuf);
- sfree(blob.data);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- return NULL;
- }
-
- long http_code = 0;
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
- if ((http_code < 200) || (http_code >= 300)) {
- ERROR("write_gcm plugin: fetching %s failed: HTTP error %ld", url,
- http_code);
- sfree(blob.data);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- return NULL;
- }
-
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- return blob.data;
-} /* }}} char *read_url */
-
-_Bool gce_check(void) /* {{{ */
-{
- if (on_gce != -1)
- return on_gce == 1;
-
- DEBUG("utils_gce: Checking whether I'm running on GCE ...");
-
- CURL *curl = curl_easy_init();
- if (!curl) {
- ERROR("utils_gce: curl_easy_init failed.");
- return 0;
- }
-
- struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
-
- char curl_errbuf[CURL_ERROR_SIZE];
- blob_t blob = {NULL, 0};
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_callback);
- curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &blob);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_URL, GCP_METADATA_PREFIX "/");
-
- int status = curl_easy_perform(curl);
- if ((status != CURLE_OK) || (blob.data == NULL) ||
- (strstr(blob.data, "Metadata-Flavor: Google") == NULL)) {
- DEBUG("utils_gce: ... no (%s)",
- (status != CURLE_OK)
- ? "curl_easy_perform failed"
- : (blob.data == NULL) ? "blob.data == NULL"
- : "Metadata-Flavor header not found");
- sfree(blob.data);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- on_gce = 0;
- return 0;
- }
- sfree(blob.data);
-
- long http_code = 0;
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
- if ((http_code < 200) || (http_code >= 300)) {
- DEBUG("utils_gce: ... no (HTTP status %ld)", http_code);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- on_gce = 0;
- return 0;
- }
-
- DEBUG("utils_gce: ... yes");
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- on_gce = 1;
- return 1;
-} /* }}} _Bool gce_check */
-
-char *gce_project_id(void) /* {{{ */
-{
- return read_url(GCE_PROJECT_ID_URL);
-} /* }}} char *gce_project_id */
-
-char *gce_instance_id(void) /* {{{ */
-{
- return read_url(GCE_INSTANCE_ID_URL);
-} /* }}} char *gce_instance_id */
-
-char *gce_zone(void) /* {{{ */
-{
- return read_url(GCE_ZONE_URL);
-} /* }}} char *gce_instance_id */
-
-char *gce_scope(char const *email) /* {{{ */
-{
- char url[1024];
-
- snprintf(url, sizeof(url), GCE_SCOPE_URL_FORMAT,
- (email != NULL) ? email : GCE_DEFAULT_SERVICE_ACCOUNT);
-
- return read_url(url);
-} /* }}} char *gce_scope */
-
-int gce_access_token(char const *email, char *buffer,
- size_t buffer_size) /* {{{ */
-{
- char url[1024];
- char *json;
- cdtime_t now = cdtime();
-
- pthread_mutex_lock(&token_lock);
-
- if (email == NULL)
- email = GCE_DEFAULT_SERVICE_ACCOUNT;
-
- if ((token_email != NULL) && (strcmp(email, token_email) == 0) &&
- (token_valid_until > now)) {
- sstrncpy(buffer, token, buffer_size);
- pthread_mutex_unlock(&token_lock);
- return 0;
- }
-
- snprintf(url, sizeof(url), GCE_TOKEN_URL_FORMAT, email);
- json = read_url(url);
- if (json == NULL) {
- pthread_mutex_unlock(&token_lock);
- return -1;
- }
-
- char tmp[256];
- cdtime_t expires_in = 0;
- int status = oauth_parse_json_token(json, tmp, sizeof(tmp), &expires_in);
- sfree(json);
- if (status != 0) {
- pthread_mutex_unlock(&token_lock);
- return status;
- }
-
- sfree(token);
- token = strdup(tmp);
-
- sfree(token_email);
- token_email = strdup(email);
-
- /* let tokens expire a bit early */
- expires_in = (expires_in * 95) / 100;
- token_valid_until = now + expires_in;
-
- sstrncpy(buffer, token, buffer_size);
- pthread_mutex_unlock(&token_lock);
- return 0;
-} /* }}} char *gce_token */
+++ /dev/null
-/**
- * collectd - src/utils_gce.h
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_GCE_H
-#define UTILS_GCE_H 1
-
-/* gce_check returns 1 when running on Google Compute Engine (GCE) and 0
- * otherwise. */
-_Bool gce_check(void);
-
-/* gce_project_id returns the project ID of the instance, as configured when
- * creating the project.
- * For example "example-project-a". */
-char *gce_project_id(void);
-
-/* gce_instance_id returns the unique ID of the GCE instance. */
-char *gce_instance_id(void);
-
-/* gce_zone returns the zone in which the GCE instance runs. */
-char *gce_zone(void);
-
-/* gce_scope returns the list of scopes for the given service account (or the
- * default service account when NULL is passed). */
-char *gce_scope(char const *email);
-
-/* gce_access_token acquires an OAuth access token for the given service account
- * (or
- * the default service account when NULL is passed) and stores it in buffer.
- * Access tokens are automatically cached and renewed when they expire. Returns
- * zero on success, non-zero otherwise. */
-int gce_access_token(char const *email, char *buffer, size_t buffer_size);
-
-#endif
+++ /dev/null
-/**
- * collectd - src/utils_ignorelist.c
- * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
- * Copyright (C) 2008 Florian Forster <octo at collectd.org>
- *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
- *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence 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:
- * Lubos Stanek <lubek at users.sourceforge.net>
- * Florian Forster <octo at collectd.org>
- **/
-/**
- * ignorelist handles plugin's list of configured collectable
- * entries with global ignore action
- **/
-/**
- * Usage:
- *
- * Define plugin's global pointer variable of type ignorelist_t:
- * ignorelist_t *myconfig_ignore;
- * If you know the state of the global ignore (IgnoreSelected),
- * allocate the variable with:
- * myconfig_ignore = ignorelist_create (YourKnownIgnore);
- * If you do not know the state of the global ignore,
- * initialize the global variable and set the ignore flag later:
- * myconfig_ignore = ignorelist_init ();
- * Append single entries in your cf_register'ed callback function:
- * ignorelist_add (myconfig_ignore, newentry);
- * When you hit the IgnoreSelected config option,
- * offer it to the list:
- * ignorelist_ignore (myconfig_ignore, instantly_got_value_of_ignore);
- * That is all for the ignorelist initialization.
- * Later during read and write (plugin's registered functions) get
- * the information whether this entry would be collected or not:
- * if (ignorelist_match (myconfig_ignore, thisentry))
- * return;
- **/
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_ignorelist.h"
-
-/*
- * private prototypes
- */
-struct ignorelist_item_s {
-#if HAVE_REGEX_H
- regex_t *rmatch; /* regular expression entry identification */
-#endif
- char *smatch; /* string entry identification */
- struct ignorelist_item_s *next;
-};
-typedef struct ignorelist_item_s ignorelist_item_t;
-
-struct ignorelist_s {
- int ignore; /* ignore entries */
- ignorelist_item_t *head; /* pointer to the first entry */
-};
-
-/* *** *** *** ********************************************* *** *** *** */
-/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */
-/* *** *** *** ********************************************* *** *** *** */
-
-static inline void ignorelist_append(ignorelist_t *il,
- ignorelist_item_t *item) {
- assert((il != NULL) && (item != NULL));
-
- item->next = il->head;
- il->head = item;
-}
-
-#if HAVE_REGEX_H
-static int ignorelist_append_regex(ignorelist_t *il, const char *re_str) {
- regex_t *re;
- ignorelist_item_t *entry;
- int status;
-
- re = calloc(1, sizeof(*re));
- if (re == NULL) {
- ERROR("ignorelist_append_regex: calloc failed.");
- return ENOMEM;
- }
-
- status = regcomp(re, re_str, REG_EXTENDED);
- if (status != 0) {
- char errbuf[1024];
- (void)regerror(status, re, errbuf, sizeof(errbuf));
- ERROR("utils_ignorelist: regcomp failed: %s", errbuf);
- ERROR("ignorelist_append_regex: Compiling regular expression \"%s\" "
- "failed: %s",
- re_str, errbuf);
- sfree(re);
- return status;
- }
-
- entry = calloc(1, sizeof(*entry));
- if (entry == NULL) {
- ERROR("ignorelist_append_regex: calloc failed.");
- regfree(re);
- sfree(re);
- return ENOMEM;
- }
- entry->rmatch = re;
-
- ignorelist_append(il, entry);
- return 0;
-} /* int ignorelist_append_regex */
-#endif
-
-static int ignorelist_append_string(ignorelist_t *il, const char *entry) {
- ignorelist_item_t *new;
-
- /* create new entry */
- if ((new = calloc(1, sizeof(*new))) == NULL) {
- ERROR("cannot allocate new entry");
- return 1;
- }
- new->smatch = sstrdup(entry);
-
- /* append new entry */
- ignorelist_append(il, new);
-
- return 0;
-} /* int ignorelist_append_string(ignorelist_t *il, const char *entry) */
-
-#if HAVE_REGEX_H
-/*
- * check list for entry regex match
- * return 1 if found
- */
-static int ignorelist_match_regex(ignorelist_item_t *item, const char *entry) {
- assert((item != NULL) && (item->rmatch != NULL) && (entry != NULL) &&
- (strlen(entry) > 0));
-
- /* match regex */
- if (regexec(item->rmatch, entry, 0, NULL, 0) == 0)
- return 1;
-
- return 0;
-} /* int ignorelist_match_regex (ignorelist_item_t *item, const char *entry) */
-#endif
-
-/*
- * check list for entry string match
- * return 1 if found
- */
-static int ignorelist_match_string(ignorelist_item_t *item, const char *entry) {
- assert((item != NULL) && (item->smatch != NULL) && (entry != NULL) &&
- (strlen(entry) > 0));
-
- if (strcmp(entry, item->smatch) == 0)
- return 1;
-
- return 0;
-} /* int ignorelist_match_string (ignorelist_item_t *item, const char *entry) */
-
-/* *** *** *** ******************************************** *** *** *** */
-/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */
-/* *** *** *** ******************************************** *** *** *** */
-
-/*
- * create the ignorelist_t with known ignore state
- * return pointer to ignorelist_t
- */
-ignorelist_t *ignorelist_create(int invert) {
- ignorelist_t *il;
-
- il = calloc(1, sizeof(*il));
- if (il == NULL)
- return NULL;
-
- /*
- * ->ignore == 0 => collect
- * ->ignore == 1 => ignore
- */
- il->ignore = invert ? 0 : 1;
-
- return il;
-} /* ignorelist_t *ignorelist_create (int ignore) */
-
-/*
- * free memory used by ignorelist_t
- */
-void ignorelist_free(ignorelist_t *il) {
- ignorelist_item_t *this;
- ignorelist_item_t *next;
-
- if (il == NULL)
- return;
-
- for (this = il->head; this != NULL; this = next) {
- next = this->next;
-#if HAVE_REGEX_H
- if (this->rmatch != NULL) {
- regfree(this->rmatch);
- sfree(this->rmatch);
- this->rmatch = NULL;
- }
-#endif
- if (this->smatch != NULL) {
- sfree(this->smatch);
- this->smatch = NULL;
- }
- sfree(this);
- }
-
- sfree(il);
-} /* void ignorelist_destroy (ignorelist_t *il) */
-
-/*
- * set ignore state of the ignorelist_t
- */
-void ignorelist_set_invert(ignorelist_t *il, int invert) {
- if (il == NULL) {
- DEBUG("ignore call with ignorelist_t == NULL");
- return;
- }
-
- il->ignore = invert ? 0 : 1;
-} /* void ignorelist_set_invert (ignorelist_t *il, int ignore) */
-
-/*
- * append entry into ignorelist_t
- * return 0 for success
- */
-int ignorelist_add(ignorelist_t *il, const char *entry) {
- size_t len;
-
- if (il == NULL) {
- DEBUG("add called with ignorelist_t == NULL");
- return 1;
- }
-
- len = strlen(entry);
-
- /* append nothing */
- if (len == 0) {
- DEBUG("not appending: empty entry");
- return 1;
- }
-
-#if HAVE_REGEX_H
- /* regex string is enclosed in "/.../" */
- if ((len > 2) && (entry[0] == '/') && entry[len - 1] == '/') {
- char *copy;
- int status;
-
- /* skip leading slash */
- copy = strdup(entry + 1);
- if (copy == NULL)
- return ENOMEM;
-
- /* trim trailing slash */
- copy[strlen(copy) - 1] = '\0';
-
- status = ignorelist_append_regex(il, copy);
- sfree(copy);
- return status;
- }
-#endif
-
- return ignorelist_append_string(il, entry);
-} /* int ignorelist_add (ignorelist_t *il, const char *entry) */
-
-/*
- * check list for entry
- * return 1 for ignored entry
- */
-int ignorelist_match(ignorelist_t *il, const char *entry) {
- /* if no entries, collect all */
- if ((il == NULL) || (il->head == NULL))
- return 0;
-
- if ((entry == NULL) || (strlen(entry) == 0))
- return 0;
-
- /* traverse list and check entries */
- for (ignorelist_item_t *traverse = il->head; traverse != NULL;
- traverse = traverse->next) {
-#if HAVE_REGEX_H
- if (traverse->rmatch != NULL) {
- if (ignorelist_match_regex(traverse, entry))
- return il->ignore;
- } else
-#endif
- {
- if (ignorelist_match_string(traverse, entry))
- return il->ignore;
- }
- } /* for traverse */
-
- return 1 - il->ignore;
-} /* int ignorelist_match (ignorelist_t *il, const char *entry) */
+++ /dev/null
-/**
- * collectd - src/utils_ignorelist.h
- * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
- *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
- *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence 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:
- * Lubos Stanek <lubek at users.sourceforge.net>
- **/
-/**
- * ignorelist handles plugin's list of configured collectable
- * entries with global ignore action
- **/
-
-#ifndef UTILS_IGNORELIST_H
-#define UTILS_IGNORELIST_H 1
-
-#include "collectd.h"
-
-#if HAVE_REGEX_H
-#include <regex.h>
-#endif
-
-/* public prototypes */
-
-struct ignorelist_s;
-typedef struct ignorelist_s ignorelist_t;
-
-/*
- * create the ignorelist_t with known ignore state
- * return pointer to ignorelist_t
- */
-ignorelist_t *ignorelist_create(int invert);
-
-/*
- * free memory used by ignorelist_t
- */
-void ignorelist_free(ignorelist_t *il);
-
-/*
- * set ignore state of the ignorelist_t
- */
-void ignorelist_set_invert(ignorelist_t *il, int invert);
-
-/*
- * append entry to ignorelist_t
- * returns zero on success, non-zero upon failure.
- */
-int ignorelist_add(ignorelist_t *il, const char *entry);
-
-/*
- * check list for entry
- * return 1 for ignored entry
- */
-int ignorelist_match(ignorelist_t *il, const char *entry);
-
-#endif /* UTILS_IGNORELIST_H */
+++ /dev/null
-/**
- * collectd - src/utils_latency.c
- * Copyright (C) 2013 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian Forster <ff at octo.it>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_latency.h"
-
-#include <limits.h>
-#include <math.h>
-
-#ifndef LLONG_MAX
-#define LLONG_MAX 9223372036854775807LL
-#endif
-
-#ifndef HISTOGRAM_DEFAULT_BIN_WIDTH
-/* 1048576 = 2^20 ^= 1/1024 s */
-#define HISTOGRAM_DEFAULT_BIN_WIDTH 1048576
-#endif
-
-struct latency_counter_s {
- cdtime_t start_time;
-
- cdtime_t sum;
- size_t num;
-
- cdtime_t min;
- cdtime_t max;
-
- cdtime_t bin_width;
- int histogram[HISTOGRAM_NUM_BINS];
-};
-
-/*
-* Histogram represents the distribution of data, it has a list of "bins".
-* Each bin represents an interval and has a count (frequency) of
-* number of values fall within its interval.
-*
-* Histogram's range is determined by the number of bins and the bin width,
-* There are 1000 bins and all bins have the same width of default 1 millisecond.
-* When a value above this range is added, Histogram's range is increased by
-* increasing the bin width (note that number of bins remains always at 1000).
-* This operation of increasing bin width is little expensive as each bin need
-* to be visited to update its count. To reduce frequent change of bin width,
-* new bin width will be the next nearest power of 2. Example: 2, 4, 8, 16, 32,
-* 64, 128, 256, 512, 1024, 2048, 5086, ...
-*
-* So, if the required bin width is 300, then new bin width will be 512 as it is
-* the next nearest power of 2.
-*/
-static void change_bin_width(latency_counter_t *lc, cdtime_t latency) /* {{{ */
-{
- /* This function is called because the new value is above histogram's range.
- * First find the required bin width:
- * requiredBinWidth = (value + 1) / numBins
- * then get the next nearest power of 2
- * newBinWidth = 2^(ceil(log2(requiredBinWidth)))
- */
- double required_bin_width =
- ((double)(latency + 1)) / ((double)HISTOGRAM_NUM_BINS);
- double required_bin_width_logbase2 = log(required_bin_width) / log(2.0);
- cdtime_t new_bin_width =
- (cdtime_t)(pow(2.0, ceil(required_bin_width_logbase2)) + .5);
- cdtime_t old_bin_width = lc->bin_width;
-
- lc->bin_width = new_bin_width;
-
- /* bin_width has been increased, now iterate through all bins and move the
- * old bin's count to new bin. */
- if (lc->num > 0) // if the histogram has data then iterate else skip
- {
- double width_change_ratio =
- ((double)old_bin_width) / ((double)new_bin_width);
-
- for (size_t i = 0; i < HISTOGRAM_NUM_BINS; i++) {
- size_t new_bin = (size_t)(((double)i) * width_change_ratio);
- if (i == new_bin)
- continue;
- assert(new_bin < i);
-
- lc->histogram[new_bin] += lc->histogram[i];
- lc->histogram[i] = 0;
- }
- }
-
- DEBUG("utils_latency: change_bin_width: latency = %.3f; "
- "old_bin_width = %.3f; new_bin_width = %.3f;",
- CDTIME_T_TO_DOUBLE(latency), CDTIME_T_TO_DOUBLE(old_bin_width),
- CDTIME_T_TO_DOUBLE(new_bin_width));
-} /* }}} void change_bin_width */
-
-latency_counter_t *latency_counter_create(void) /* {{{ */
-{
- latency_counter_t *lc;
-
- lc = calloc(1, sizeof(*lc));
- if (lc == NULL)
- return NULL;
-
- lc->bin_width = HISTOGRAM_DEFAULT_BIN_WIDTH;
- latency_counter_reset(lc);
- return lc;
-} /* }}} latency_counter_t *latency_counter_create */
-
-void latency_counter_destroy(latency_counter_t *lc) /* {{{ */
-{
- sfree(lc);
-} /* }}} void latency_counter_destroy */
-
-void latency_counter_add(latency_counter_t *lc, cdtime_t latency) /* {{{ */
-{
- cdtime_t bin;
-
- if ((lc == NULL) || (latency == 0) || (latency > ((cdtime_t)LLONG_MAX)))
- return;
-
- lc->sum += latency;
- lc->num++;
-
- if ((lc->min == 0) && (lc->max == 0))
- lc->min = lc->max = latency;
- if (lc->min > latency)
- lc->min = latency;
- if (lc->max < latency)
- lc->max = latency;
-
- /* A latency of _exactly_ 1.0 ms is stored in the buffer 0, so
- * subtract one from the cdtime_t value so that exactly 1.0 ms get sorted
- * accordingly. */
- bin = (latency - 1) / lc->bin_width;
- if (bin >= HISTOGRAM_NUM_BINS) {
- change_bin_width(lc, latency);
- bin = (latency - 1) / lc->bin_width;
- if (bin >= HISTOGRAM_NUM_BINS) {
- P_ERROR("latency_counter_add: Invalid bin: %" PRIu64, bin);
- return;
- }
- }
- lc->histogram[bin]++;
-} /* }}} void latency_counter_add */
-
-void latency_counter_reset(latency_counter_t *lc) /* {{{ */
-{
- if (lc == NULL)
- return;
-
- cdtime_t bin_width = lc->bin_width;
- cdtime_t max_bin = (lc->max - 1) / lc->bin_width;
-
-/*
- If max latency is REDUCE_THRESHOLD times less than histogram's range,
- then cut it in half. REDUCE_THRESHOLD must be >= 2.
- Value of 4 is selected to reduce frequent changes of bin width.
-*/
-#define REDUCE_THRESHOLD 4
- if ((lc->num > 0) && (lc->bin_width >= HISTOGRAM_DEFAULT_BIN_WIDTH * 2) &&
- (max_bin < HISTOGRAM_NUM_BINS / REDUCE_THRESHOLD)) {
- /* new bin width will be the previous power of 2 */
- bin_width = bin_width / 2;
-
- DEBUG("utils_latency: latency_counter_reset: max_latency = %.3f; "
- "max_bin = %" PRIu64 "; old_bin_width = %.3f; new_bin_width = %.3f;",
- CDTIME_T_TO_DOUBLE(lc->max), max_bin,
- CDTIME_T_TO_DOUBLE(lc->bin_width), CDTIME_T_TO_DOUBLE(bin_width));
- }
-
- memset(lc, 0, sizeof(*lc));
-
- /* preserve bin width */
- lc->bin_width = bin_width;
- lc->start_time = cdtime();
-} /* }}} void latency_counter_reset */
-
-cdtime_t latency_counter_get_min(latency_counter_t *lc) /* {{{ */
-{
- if (lc == NULL)
- return 0;
- return lc->min;
-} /* }}} cdtime_t latency_counter_get_min */
-
-cdtime_t latency_counter_get_max(latency_counter_t *lc) /* {{{ */
-{
- if (lc == NULL)
- return 0;
- return lc->max;
-} /* }}} cdtime_t latency_counter_get_max */
-
-cdtime_t latency_counter_get_sum(latency_counter_t *lc) /* {{{ */
-{
- if (lc == NULL)
- return 0;
- return lc->sum;
-} /* }}} cdtime_t latency_counter_get_sum */
-
-size_t latency_counter_get_num(latency_counter_t *lc) /* {{{ */
-{
- if (lc == NULL)
- return 0;
- return lc->num;
-} /* }}} size_t latency_counter_get_num */
-
-cdtime_t latency_counter_get_average(latency_counter_t *lc) /* {{{ */
-{
- double average;
-
- if ((lc == NULL) || (lc->num == 0))
- return 0;
-
- average = CDTIME_T_TO_DOUBLE(lc->sum) / ((double)lc->num);
- return DOUBLE_TO_CDTIME_T(average);
-} /* }}} cdtime_t latency_counter_get_average */
-
-cdtime_t latency_counter_get_percentile(latency_counter_t *lc, /* {{{ */
- double percent) {
- double percent_upper;
- double percent_lower;
- double p;
- cdtime_t latency_lower;
- cdtime_t latency_interpolated;
- int sum;
- size_t i;
-
- if ((lc == NULL) || (lc->num == 0) || !((percent > 0.0) && (percent < 100.0)))
- return 0;
-
- /* Find index i so that at least "percent" events are within i+1 ms. */
- percent_upper = 0.0;
- percent_lower = 0.0;
- sum = 0;
- for (i = 0; i < HISTOGRAM_NUM_BINS; i++) {
- percent_lower = percent_upper;
- sum += lc->histogram[i];
- if (sum == 0)
- percent_upper = 0.0;
- else
- percent_upper = 100.0 * ((double)sum) / ((double)lc->num);
-
- if (percent_upper >= percent)
- break;
- }
-
- if (i >= HISTOGRAM_NUM_BINS)
- return 0;
-
- assert(percent_upper >= percent);
- assert(percent_lower < percent);
-
- if (i == 0)
- return lc->bin_width;
-
- latency_lower = ((cdtime_t)i) * lc->bin_width;
- p = (percent - percent_lower) / (percent_upper - percent_lower);
-
- latency_interpolated =
- latency_lower + DOUBLE_TO_CDTIME_T(p * CDTIME_T_TO_DOUBLE(lc->bin_width));
-
- DEBUG("latency_counter_get_percentile: latency_interpolated = %.3f",
- CDTIME_T_TO_DOUBLE(latency_interpolated));
- return latency_interpolated;
-} /* }}} cdtime_t latency_counter_get_percentile */
-
-double latency_counter_get_rate(const latency_counter_t *lc, /* {{{ */
- cdtime_t lower, cdtime_t upper,
- const cdtime_t now) {
- if ((lc == NULL) || (lc->num == 0))
- return NAN;
-
- if (upper && (upper < lower))
- return NAN;
- if (lower == upper)
- return 0;
-
- /* Buckets have an exclusive lower bound and an inclusive upper bound. That
- * means that the first bucket, index 0, represents (0-bin_width]. That means
- * that latency==bin_width needs to result in bin=0, that's why we need to
- * subtract one before dividing by bin_width. */
- cdtime_t lower_bin = 0;
- if (lower)
- /* lower is *exclusive* => determine bucket for lower+1 */
- lower_bin = ((lower + 1) - 1) / lc->bin_width;
-
- /* lower is greater than the longest latency observed => rate is zero. */
- if (lower_bin >= HISTOGRAM_NUM_BINS)
- return 0;
-
- cdtime_t upper_bin = HISTOGRAM_NUM_BINS - 1;
- if (upper)
- upper_bin = (upper - 1) / lc->bin_width;
-
- if (upper_bin >= HISTOGRAM_NUM_BINS) {
- upper_bin = HISTOGRAM_NUM_BINS - 1;
- upper = 0;
- }
-
- double sum = 0;
- for (size_t i = lower_bin; i <= upper_bin; i++)
- sum += lc->histogram[i];
-
- if (lower) {
- /* Approximate ratio of requests in lower_bin, that fall between
- * lower_bin_boundary and lower. This ratio is then subtracted from sum to
- * increase accuracy. */
- cdtime_t lower_bin_boundary = lower_bin * lc->bin_width;
- assert(lower >= lower_bin_boundary);
- double lower_ratio =
- (double)(lower - lower_bin_boundary) / ((double)lc->bin_width);
- sum -= lower_ratio * lc->histogram[lower_bin];
- }
-
- if (upper) {
- /* As above: approximate ratio of requests in upper_bin, that fall between
- * upper and upper_bin_boundary. */
- cdtime_t upper_bin_boundary = (upper_bin + 1) * lc->bin_width;
- assert(upper <= upper_bin_boundary);
- double ratio = (double)(upper_bin_boundary - upper) / (double)lc->bin_width;
- sum -= ratio * lc->histogram[upper_bin];
- }
-
- return sum / (CDTIME_T_TO_DOUBLE(now - lc->start_time));
-} /* }}} double latency_counter_get_rate */
+++ /dev/null
-/**
- * collectd - src/utils_latency.h
- * Copyright (C) 2013 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian Forster <ff at octo.it>
- **/
-
-#include "collectd.h"
-
-#include "utils_time.h"
-
-#ifndef HISTOGRAM_NUM_BINS
-#define HISTOGRAM_NUM_BINS 1000
-#endif
-
-struct latency_counter_s;
-typedef struct latency_counter_s latency_counter_t;
-
-latency_counter_t *latency_counter_create(void);
-void latency_counter_destroy(latency_counter_t *lc);
-
-void latency_counter_add(latency_counter_t *lc, cdtime_t latency);
-void latency_counter_reset(latency_counter_t *lc);
-
-cdtime_t latency_counter_get_min(latency_counter_t *lc);
-cdtime_t latency_counter_get_max(latency_counter_t *lc);
-cdtime_t latency_counter_get_sum(latency_counter_t *lc);
-size_t latency_counter_get_num(latency_counter_t *lc);
-cdtime_t latency_counter_get_average(latency_counter_t *lc);
-cdtime_t latency_counter_get_percentile(latency_counter_t *lc, double percent);
-
-/*
- * NAME
- * latency_counter_get_rate(counter,lower,upper,now)
- *
- * DESCRIPTION
- * Calculates rate of latency values fall within requested interval.
- * Interval specified as (lower,upper], i.e. the lower boundary is exclusive,
- * the upper boundary is inclusive.
- * When lower is zero, then the interval is (0, upper].
- * When upper is zero, then the interval is (lower, infinity).
- */
-double latency_counter_get_rate(const latency_counter_t *lc, cdtime_t lower,
- cdtime_t upper, const cdtime_t now);
+++ /dev/null
-/**
- * collectd - src/utils_latency_config.c
- * Copyright (C) 2013-2016 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- * Pavel Rochnyack <pavel2000 at ngs.ru>
- */
-
-#include "collectd.h"
-#include "common.h"
-#include "utils_latency_config.h"
-
-static int latency_config_add_percentile(latency_config_t *conf,
- oconfig_item_t *ci) {
- double percent;
- int status = cf_util_get_double(ci, &percent);
- if (status != 0)
- return status;
-
- if ((percent <= 0.0) || (percent >= 100)) {
- P_ERROR("The value for \"%s\" must be between 0 and 100, "
- "exclusively.",
- ci->key);
- return ERANGE;
- }
-
- double *tmp = realloc(conf->percentile,
- sizeof(*conf->percentile) * (conf->percentile_num + 1));
- if (tmp == NULL) {
- P_ERROR("realloc failed.");
- return ENOMEM;
- }
- conf->percentile = tmp;
- conf->percentile[conf->percentile_num] = percent;
- conf->percentile_num++;
-
- return 0;
-} /* int latency_config_add_percentile */
-
-static int latency_config_add_bucket(latency_config_t *conf,
- oconfig_item_t *ci) {
- if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_NUMBER) ||
- (ci->values[1].type != OCONFIG_TYPE_NUMBER)) {
- P_ERROR("\"%s\" requires exactly two numeric arguments.", ci->key);
- return EINVAL;
- }
-
- if (ci->values[1].value.number &&
- ci->values[1].value.number <= ci->values[0].value.number) {
- P_ERROR("MIN must be less than MAX in \"%s\".", ci->key);
- return ERANGE;
- }
-
- if (ci->values[0].value.number < 0) {
- P_ERROR("MIN must be greater then or equal to zero in \"%s\".", ci->key);
- return ERANGE;
- }
-
- latency_bucket_t *tmp =
- realloc(conf->buckets, sizeof(*conf->buckets) * (conf->buckets_num + 1));
- if (tmp == NULL) {
- P_ERROR("realloc failed.");
- return ENOMEM;
- }
- conf->buckets = tmp;
- conf->buckets[conf->buckets_num].lower_bound =
- DOUBLE_TO_CDTIME_T(ci->values[0].value.number);
- conf->buckets[conf->buckets_num].upper_bound =
- DOUBLE_TO_CDTIME_T(ci->values[1].value.number);
- conf->buckets_num++;
-
- return 0;
-} /* int latency_config_add_bucket */
-
-int latency_config(latency_config_t *conf, oconfig_item_t *ci) {
- int status = 0;
-
- for (int i = 0; i < ci->children_num; i++) {
- oconfig_item_t *child = ci->children + i;
-
- if (strcasecmp("Percentile", child->key) == 0)
- status = latency_config_add_percentile(conf, child);
- else if (strcasecmp("Bucket", child->key) == 0)
- status = latency_config_add_bucket(conf, child);
- else if (strcasecmp("BucketType", child->key) == 0)
- status = cf_util_get_string(child, &conf->bucket_type);
- else
- P_WARNING("\"%s\" is not a valid option within a \"%s\" block.",
- child->key, ci->key);
-
- if (status != 0)
- return status;
- }
-
- if ((status == 0) && (conf->percentile_num == 0) &&
- (conf->buckets_num == 0)) {
- P_ERROR("The \"%s\" block must contain at least one "
- "\"Percentile\" or \"Bucket\" option.",
- ci->key);
- return EINVAL;
- }
-
- return 0;
-}
-
-int latency_config_copy(latency_config_t *dst, const latency_config_t src) {
- *dst = (latency_config_t){
- .percentile_num = src.percentile_num, .buckets_num = src.buckets_num,
- };
-
- dst->percentile = calloc(dst->percentile_num, sizeof(*dst->percentile));
- dst->buckets = calloc(dst->buckets_num, sizeof(*dst->buckets));
-
- if ((dst->percentile == NULL) || (dst->buckets == NULL)) {
- latency_config_free(*dst);
- return ENOMEM;
- }
-
- if (src.bucket_type != NULL) {
- dst->bucket_type = strdup(src.bucket_type);
- if (dst->bucket_type == NULL) {
- latency_config_free(*dst);
- return ENOMEM;
- }
- }
-
- memmove(dst->percentile, src.percentile,
- dst->percentile_num * sizeof(*dst->percentile));
- memmove(dst->buckets, src.buckets, dst->buckets_num * sizeof(*dst->buckets));
-
- return 0;
-} /* int latency_config_copy */
-
-void latency_config_free(latency_config_t conf) {
- sfree(conf.percentile);
- sfree(conf.buckets);
- sfree(conf.bucket_type);
-} /* void latency_config_free */
+++ /dev/null
-/**
- * collectd - src/utils_latency_config.c
- * Copyright (C) 2013-2016 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- * Pavel Rochnyack <pavel2000 at ngs.ru>
- */
-
-#ifndef UTILS_LATENCY_CONFIG_H
-#define UTILS_LATENCY_CONFIG_H 1
-
-#include "collectd.h"
-
-#include "liboconfig/oconfig.h"
-#include "utils_time.h"
-
-typedef struct {
- cdtime_t lower_bound;
- cdtime_t upper_bound;
-} latency_bucket_t;
-
-typedef struct {
- double *percentile;
- size_t percentile_num;
-
- latency_bucket_t *buckets;
- size_t buckets_num;
- char *bucket_type;
-
- /*
- bool lower;
- bool upper;
- bool avg;
- */
-} latency_config_t;
-
-int latency_config(latency_config_t *conf, oconfig_item_t *ci);
-
-int latency_config_copy(latency_config_t *dst, const latency_config_t src);
-
-void latency_config_free(latency_config_t conf);
-
-#endif /* UTILS_LATENCY_CONFIG_H */
+++ /dev/null
-/**
- * collectd - src/utils_latency_test.c
- * Copyright (C) 2015 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#define DBL_PRECISION 1e-6
-
-#include "collectd.h"
-#include "common.h" /* for STATIC_ARRAY_SIZE */
-
-#include "testing.h"
-#include "utils_latency.h"
-#include "utils_time.h"
-
-DEF_TEST(simple) {
- struct {
- double val;
- double min;
- double max;
- double sum;
- double avg;
- } cases[] = {
- /* val min max sum avg */
- {0.5, 0.5, 0.5, 0.5, 0.5}, {0.3, 0.3, 0.5, 0.8, 0.4},
- {0.7, 0.3, 0.7, 1.5, 0.5}, {2.5, 0.3, 2.5, 4.0, 1.0},
- {99, 0.3, 99, 103, 20.6},
- /* { -1, 0.3, 99, 103, 20.6}, see issue #1139 */
- };
- latency_counter_t *l;
-
- CHECK_NOT_NULL(l = latency_counter_create());
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- printf("# case %" PRIsz ": DOUBLE_TO_CDTIME_T(%g) = %" PRIu64 "\n", i,
- cases[i].val, DOUBLE_TO_CDTIME_T(cases[i].val));
- latency_counter_add(l, DOUBLE_TO_CDTIME_T(cases[i].val));
-
- EXPECT_EQ_DOUBLE(cases[i].min,
- CDTIME_T_TO_DOUBLE(latency_counter_get_min(l)));
- EXPECT_EQ_DOUBLE(cases[i].max,
- CDTIME_T_TO_DOUBLE(latency_counter_get_max(l)));
- EXPECT_EQ_DOUBLE(cases[i].sum,
- CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l)));
- EXPECT_EQ_DOUBLE(cases[i].avg,
- CDTIME_T_TO_DOUBLE(latency_counter_get_average(l)));
- }
-
- latency_counter_destroy(l);
- return 0;
-}
-
-DEF_TEST(percentile) {
- latency_counter_t *l;
-
- CHECK_NOT_NULL(l = latency_counter_create());
-
- for (size_t i = 0; i < 100; i++) {
- latency_counter_add(l, TIME_T_TO_CDTIME_T(((time_t)i) + 1));
- }
-
- EXPECT_EQ_DOUBLE(1.0, CDTIME_T_TO_DOUBLE(latency_counter_get_min(l)));
- EXPECT_EQ_DOUBLE(100.0, CDTIME_T_TO_DOUBLE(latency_counter_get_max(l)));
- EXPECT_EQ_DOUBLE(100.0 * 101.0 / 2.0,
- CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l)));
- EXPECT_EQ_DOUBLE(50.5, CDTIME_T_TO_DOUBLE(latency_counter_get_average(l)));
-
- EXPECT_EQ_DOUBLE(50.0,
- CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 50.0)));
- EXPECT_EQ_DOUBLE(80.0,
- CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 80.0)));
- EXPECT_EQ_DOUBLE(95.0,
- CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 95.0)));
- EXPECT_EQ_DOUBLE(99.0,
- CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 99.0)));
-
- CHECK_ZERO(latency_counter_get_percentile(l, -1.0));
- CHECK_ZERO(latency_counter_get_percentile(l, 101.0));
-
- latency_counter_destroy(l);
- return 0;
-}
-
-DEF_TEST(get_rate) {
- /* We re-declare the struct here so we can inspect its content. */
- struct {
- cdtime_t start_time;
- cdtime_t sum;
- size_t num;
- cdtime_t min;
- cdtime_t max;
- cdtime_t bin_width;
- int histogram[HISTOGRAM_NUM_BINS];
- } * peek;
- latency_counter_t *l;
-
- CHECK_NOT_NULL(l = latency_counter_create());
- peek = (void *)l;
-
- for (time_t i = 1; i <= 125; i++) {
- latency_counter_add(l, TIME_T_TO_CDTIME_T(i));
- }
-
- /* We expect a bucket width of 125ms. */
- EXPECT_EQ_UINT64(DOUBLE_TO_CDTIME_T(0.125), peek->bin_width);
-
- struct {
- size_t index;
- int want;
- } bucket_cases[] = {
- {0, 0}, /* (0.000-0.125] */
- {1, 0}, /* (0.125-0.250] */
- {2, 0}, /* (0.250-0.375] */
- {3, 0}, /* (0.375-0.500] */
- {4, 0}, /* (0.500-0.625] */
- {5, 0}, /* (0.625-0.750] */
- {6, 0}, /* (0.750-0.875] */
- {7, 1}, /* (0.875-1.000] */
- {8, 0}, /* (1.000-1.125] */
- {9, 0}, /* (1.125-1.250] */
- {10, 0}, /* (1.250-1.375] */
- {11, 0}, /* (1.375-1.500] */
- {12, 0}, /* (1.500-1.625] */
- {13, 0}, /* (1.625-1.750] */
- {14, 0}, /* (1.750-1.875] */
- {15, 1}, /* (1.875-2.000] */
- {16, 0}, /* (2.000-2.125] */
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(bucket_cases); i++) {
- size_t index = bucket_cases[i].index;
- EXPECT_EQ_INT(bucket_cases[i].want, peek->histogram[index]);
- }
-
- struct {
- cdtime_t lower_bound;
- cdtime_t upper_bound;
- double want;
- } cases[] = {
- {
- // bucket 6 is zero
- DOUBLE_TO_CDTIME_T_STATIC(0.750), DOUBLE_TO_CDTIME_T_STATIC(0.875),
- 0.00,
- },
- {
- // bucket 7 contains the t=1 update
- DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(1.000),
- 1.00,
- },
- {
- // range: bucket 7 - bucket 15; contains the t=1 and t=2 updates
- DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(2.000),
- 2.00,
- },
- {
- // lower bucket is only partially applied
- DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
- DOUBLE_TO_CDTIME_T_STATIC(2.000), 1.75,
- },
- {
- // upper bucket is only partially applied
- DOUBLE_TO_CDTIME_T_STATIC(0.875),
- DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.75,
- },
- {
- // both buckets are only partially applied
- DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
- DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.50,
- },
- {
- // lower bound is unspecified
- 0, DOUBLE_TO_CDTIME_T_STATIC(2.000), 2.00,
- },
- {
- // upper bound is unspecified
- DOUBLE_TO_CDTIME_T_STATIC(125.000 - 0.125), 0, 1.00,
- },
- {
- // overflow test: upper >> longest latency
- DOUBLE_TO_CDTIME_T_STATIC(1.000), DOUBLE_TO_CDTIME_T_STATIC(999999),
- 124.00,
- },
- {
- // overflow test: lower > longest latency
- DOUBLE_TO_CDTIME_T_STATIC(130), 0, 0.00,
- },
- {
- // lower > upper => error
- DOUBLE_TO_CDTIME_T_STATIC(10), DOUBLE_TO_CDTIME_T_STATIC(9), NAN,
- },
- {
- // lower == upper => zero
- DOUBLE_TO_CDTIME_T_STATIC(9), DOUBLE_TO_CDTIME_T_STATIC(9), 0.00,
- },
- };
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- cdtime_t now = peek->start_time + TIME_T_TO_CDTIME_T(1);
- EXPECT_EQ_DOUBLE(cases[i].want,
- latency_counter_get_rate(l, cases[i].lower_bound,
- cases[i].upper_bound, now));
- }
-
- latency_counter_destroy(l);
- return 0;
-}
-
-int main(void) {
- RUN_TEST(simple);
- RUN_TEST(percentile);
- RUN_TEST(get_rate);
-
- END_TEST;
-}
* Florian Forster <octo at collectd.org>
**/
-#include "common.h"
+#include "utils/common/common.h"
#include "utils_lua.h"
static int ltoc_values(lua_State *L, /* {{{ */
+++ /dev/null
-/**
- * collectd - src/utils_match.c
- * Copyright (C) 2008-2014 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_match.h"
-
-#include <regex.h>
-
-#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
-#define UTILS_MATCH_FLAGS_REGEX 0x04
-
-struct cu_match_s {
- regex_t regex;
- regex_t excluderegex;
- int flags;
-
- int (*callback)(const char *str, char *const *matches, size_t matches_num,
- void *user_data);
- void *user_data;
- void (*free)(void *user_data);
-};
-
-/*
- * Private functions
- */
-static char *match_substr(const char *str, int begin, int end) {
- char *ret;
- size_t ret_len;
-
- if ((begin < 0) || (end < 0) || (begin >= end))
- return NULL;
- if ((size_t)end > (strlen(str) + 1)) {
- ERROR("utils_match: match_substr: `end' points after end of string.");
- return NULL;
- }
-
- ret_len = end - begin;
- ret = malloc(ret_len + 1);
- if (ret == NULL) {
- ERROR("utils_match: match_substr: malloc failed.");
- return NULL;
- }
-
- sstrncpy(ret, str + begin, ret_len + 1);
- return ret;
-} /* char *match_substr */
-
-static int default_callback(const char __attribute__((unused)) * str,
- char *const *matches, size_t matches_num,
- void *user_data) {
- cu_match_value_t *data = (cu_match_value_t *)user_data;
-
- if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) {
- gauge_t value;
- char *endptr = NULL;
-
- if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) {
- data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1;
- data->values_num++;
- return 0;
- }
-
- if (matches_num < 2)
- return -1;
-
- value = (gauge_t)strtod(matches[1], &endptr);
- if (matches[1] == endptr)
- return -1;
-
- if (data->ds_type & UTILS_MATCH_CF_GAUGE_DIST) {
- latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value));
- data->values_num++;
- return 0;
- }
-
- if ((data->values_num == 0) ||
- (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST) ||
- (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
- data->value.gauge = value;
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) {
- double f = ((double)data->values_num) / ((double)(data->values_num + 1));
- data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) {
- if (data->value.gauge > value)
- data->value.gauge = value;
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) {
- if (data->value.gauge < value)
- data->value.gauge = value;
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) {
- data->value.gauge += value;
- } else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return -1;
- }
-
- data->values_num++;
- } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) {
- counter_t value;
- char *endptr = NULL;
-
- if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) {
- data->value.counter++;
- data->values_num++;
- return 0;
- }
-
- if (matches_num < 2)
- return -1;
-
- value = (counter_t)strtoull(matches[1], &endptr, 0);
- if (matches[1] == endptr)
- return -1;
-
- if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
- data->value.counter = value;
- else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
- data->value.counter += value;
- else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return -1;
- }
-
- data->values_num++;
- } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) {
- derive_t value;
- char *endptr = NULL;
-
- if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) {
- data->value.derive++;
- data->values_num++;
- return 0;
- }
-
- if (matches_num < 2)
- return -1;
-
- value = (derive_t)strtoll(matches[1], &endptr, 0);
- if (matches[1] == endptr)
- return -1;
-
- if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
- data->value.derive = value;
- else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
- data->value.derive += value;
- else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return -1;
- }
-
- data->values_num++;
- } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) {
- absolute_t value;
- char *endptr = NULL;
-
- if (matches_num < 2)
- return -1;
-
- value = (absolute_t)strtoull(matches[1], &endptr, 0);
- if (matches[1] == endptr)
- return -1;
-
- if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
- data->value.absolute = value;
- else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return -1;
- }
-
- data->values_num++;
- } else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return -1;
- }
-
- return 0;
-} /* int default_callback */
-
-static void match_simple_free(void *data) {
- cu_match_value_t *user_data = (cu_match_value_t *)data;
- if (user_data->latency)
- latency_counter_destroy(user_data->latency);
-
- free(data);
-} /* void match_simple_free */
-
-/*
- * Public functions
- */
-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,
- void (*free_user_data)(void *user_data)) {
- cu_match_t *obj;
- int status;
-
- DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s",
- regex, excluderegex);
-
- obj = calloc(1, sizeof(*obj));
- if (obj == NULL)
- return NULL;
-
- status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
- if (status != 0) {
- ERROR("Compiling the regular expression \"%s\" failed.", regex);
- sfree(obj);
- return NULL;
- }
- obj->flags |= UTILS_MATCH_FLAGS_REGEX;
-
- 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;
- obj->free = free_user_data;
-
- return obj;
-} /* cu_match_t *match_create_callback */
-
-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;
-
- user_data = calloc(1, sizeof(*user_data));
- if (user_data == NULL)
- return NULL;
- user_data->ds_type = match_ds_type;
-
- if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
- (match_ds_type & UTILS_MATCH_CF_GAUGE_DIST)) {
- user_data->latency = latency_counter_create();
- if (user_data->latency == NULL) {
- ERROR("match_create_simple(): latency_counter_create() failed.");
- free(user_data);
- return NULL;
- }
- }
-
- obj = match_create_callback(regex, excluderegex, default_callback, user_data,
- match_simple_free);
- if (obj == NULL) {
- if (user_data->latency)
- latency_counter_destroy(user_data->latency);
-
- sfree(user_data);
- return NULL;
- }
- return obj;
-} /* cu_match_t *match_create_simple */
-
-void match_value_reset(cu_match_value_t *mv) {
- if (mv == NULL)
- return;
-
- /* Reset GAUGE metrics only and except GAUGE_PERSIST. */
- if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
- !(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
- mv->value.gauge = (mv->ds_type & UTILS_MATCH_CF_GAUGE_INC) ? 0 : NAN;
- mv->values_num = 0;
- }
-} /* }}} void match_value_reset */
-
-void match_destroy(cu_match_t *obj) {
- if (obj == NULL)
- return;
-
- if (obj->flags & UTILS_MATCH_FLAGS_REGEX)
- regfree(&obj->regex);
- if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX)
- regfree(&obj->excluderegex);
- if ((obj->user_data != NULL) && (obj->free != NULL))
- (*obj->free)(obj->user_data);
-
- sfree(obj);
-} /* void match_destroy */
-
-int match_apply(cu_match_t *obj, const char *str) {
- int status;
- regmatch_t re_match[32];
- char *matches[32] = {0};
- size_t matches_num;
-
- 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);
-
- /* Regex did not match */
- if (status != 0)
- return 0;
-
- for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches);
- matches_num++) {
- if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0))
- break;
-
- matches[matches_num] = match_substr(str, re_match[matches_num].rm_so,
- re_match[matches_num].rm_eo);
- if (matches[matches_num] == NULL) {
- status = -1;
- break;
- }
- }
-
- if (status != 0) {
- ERROR("utils_match: match_apply: match_substr failed.");
- } else {
- status = obj->callback(str, matches, matches_num, obj->user_data);
- if (status != 0) {
- ERROR("utils_match: match_apply: callback failed.");
- }
- }
-
- for (size_t i = 0; i < matches_num; i++) {
- sfree(matches[i]);
- }
-
- return status;
-} /* int match_apply */
-
-void *match_get_user_data(cu_match_t *obj) {
- if (obj == NULL)
- return NULL;
- return obj->user_data;
-} /* void *match_get_user_data */
+++ /dev/null
-/**
- * collectd - src/utils_match.h
- * Copyright (C) 2008-2014 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_MATCH_H
-#define UTILS_MATCH_H 1
-
-#include "plugin.h"
-#include "utils_latency.h"
-
-/*
- * Each type may have 12 sub-types
- * 0x1000 = 1000000000000
- * ^ <- Type bit
- * ^^^^^^^^^^^^ <- Subtype bits
- */
-#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000
-#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000
-#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000
-#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000
-
-#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
-#define UTILS_MATCH_CF_GAUGE_MIN 0x02
-#define UTILS_MATCH_CF_GAUGE_MAX 0x04
-#define UTILS_MATCH_CF_GAUGE_LAST 0x08
-#define UTILS_MATCH_CF_GAUGE_INC 0x10
-#define UTILS_MATCH_CF_GAUGE_ADD 0x20
-#define UTILS_MATCH_CF_GAUGE_PERSIST 0x40
-#define UTILS_MATCH_CF_GAUGE_DIST 0x80
-
-#define UTILS_MATCH_CF_COUNTER_SET 0x01
-#define UTILS_MATCH_CF_COUNTER_ADD 0x02
-#define UTILS_MATCH_CF_COUNTER_INC 0x04
-
-#define UTILS_MATCH_CF_DERIVE_SET 0x01
-#define UTILS_MATCH_CF_DERIVE_ADD 0x02
-#define UTILS_MATCH_CF_DERIVE_INC 0x04
-
-#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01
-#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02
-#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04
-
-/*
- * Data types
- */
-struct cu_match_s;
-typedef struct cu_match_s cu_match_t;
-
-struct cu_match_value_s {
- int ds_type;
- value_t value;
- unsigned int values_num;
- latency_counter_t *latency;
-};
-typedef struct cu_match_value_s cu_match_value_t;
-
-/*
- * Prototypes
- */
-/*
- * NAME
- * match_create_callback
- *
- * DESCRIPTION
- * Creates a new `cu_match_t' object which will use the regular expression
- * `regex' to match lines, see the `match_apply' method below. If the line
- * matches, the callback passed in `callback' will be called along with the
- * pointer `user_pointer'.
- * The string that's passed to the callback depends on the regular expression:
- * If the regular expression includes a sub-match, i. e. something like
- * "value=([0-9][0-9]*)"
- * then only the submatch (the part in the parenthesis) will be passed to the
- * callback. If there is no submatch, then the entire string is passed to the
- * callback.
- * The optional `excluderegex' allows to exclude the line from the match, if
- * the excluderegex matches.
- * When `match_destroy' is called the `user_data' pointer is freed using
- * the `free_user_data' callback - if it is not NULL.
- */
-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, void (*free_user_data)(void *user_data));
-
-/*
- * NAME
- * match_create_simple
- *
- * DESCRIPTION
- * Creates a new `cu_match_t' with a default callback. The user data for that
- * default callback will be a `cu_match_value_t' structure, with
- * `ds_type' copied to the structure. The default callback will handle the
- * string as containing a number (see strtoll(3) and strtod(3)) and store that
- * number in the `value' member. How that is done depends on `ds_type':
- *
- * UTILS_MATCH_DS_TYPE_GAUGE
- * The function will search for a floating point number in the string and
- * store it in value.gauge.
- * UTILS_MATCH_DS_TYPE_COUNTER_SET
- * The function will search for an integer in the string and store it in
- * value.counter.
- * UTILS_MATCH_DS_TYPE_COUNTER_ADD
- * The function will search for an integer in the string and add it to the
- * value in value.counter.
- * UTILS_MATCH_DS_TYPE_COUNTER_INC
- * The function will not search for anything in the string and increase
- * value.counter by one.
- */
-cu_match_t *match_create_simple(const char *regex, const char *excluderegex,
- int ds_type);
-
-/*
- * NAME
- * match_value_reset
- *
- * DESCRIPTION
- * Resets the internal state, if applicable. This function must be called
- * after each iteration for "simple" matches, usually after dispatching the
- * metrics.
- */
-void match_value_reset(cu_match_value_t *mv);
-
-/*
- * NAME
- * match_destroy
- *
- * DESCRIPTION
- * Destroys the object and frees all internal resources.
- */
-void match_destroy(cu_match_t *obj);
-
-/*
- * NAME
- * match_apply
- *
- * DESCRIPTION
- * Tries to match the string `str' with the regular expression of `obj'. If
- * the string matches, calls the callback in `obj' with the (sub-)match.
- *
- * The user_data pointer passed to `match_create_callback' is NOT freed
- * automatically. The `cu_match_value_t' structure allocated by
- * `match_create_callback' is freed automatically.
- */
-int match_apply(cu_match_t *obj, const char *str);
-
-/*
- * NAME
- * match_get_user_data
- *
- * DESCRIPTION
- * Returns the pointer passed to `match_create_callback' or a pointer to the
- * `cu_match_value_t' structure allocated by `match_create_simple'.
- */
-void *match_get_user_data(cu_match_t *obj);
-
-#endif /* UTILS_MATCH_H */
+++ /dev/null
-/**
- * collectd - src/utils_mount.c
- * Copyright (C) 2005,2006 Niki W. Waibel
- *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
- *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence 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
- *
- * Author:
- * Niki W. Waibel <niki.waibel@gmx.net>
-**/
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define _GNU_SOURCE
-
-#include "collectd.h"
-
-#include "utils_mount.h"
-
-#if HAVE_XFS_XQM_H
-#include <xfs/xqm.h>
-#define XFS_SUPER_MAGIC_STR "XFSB"
-#define XFS_SUPER_MAGIC2_STR "BSFX"
-#endif
-
-#include "common.h" /* sstrncpy() et alii */
-#include "plugin.h" /* ERROR() macro */
-
-#if HAVE_GETVFSSTAT
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#if HAVE_SYS_STATVFS_H
-#include <sys/statvfs.h>
-#endif
-/* #endif HAVE_GETVFSSTAT */
-
-#elif HAVE_GETFSSTAT
-#if HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#if HAVE_SYS_UCRED_H
-#include <sys/ucred.h>
-#endif
-#if HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
-#endif /* HAVE_GETFSSTAT */
-
-#if HAVE_MNTENT_H
-#include <mntent.h>
-#endif
-#if HAVE_SYS_MNTTAB_H
-#include <sys/mnttab.h>
-#endif
-
-#if HAVE_PATHS_H
-#include <paths.h>
-#endif
-
-#ifdef COLLECTD_MNTTAB
-#undef COLLECTD_MNTTAB
-#endif
-
-#if defined(_PATH_MOUNTED) /* glibc */
-#define COLLECTD_MNTTAB _PATH_MOUNTED
-#elif defined(MNTTAB) /* Solaris */
-#define COLLECTD_MNTTAB MNTTAB
-#elif defined(MNT_MNTTAB)
-#define COLLECTD_MNTTAB MNT_MNTTAB
-#elif defined(MNTTABNAME)
-#define COLLECTD_MNTTAB MNTTABNAME
-#elif defined(KMTAB)
-#define COLLECTD_MNTTAB KMTAB
-#else
-#define COLLECTD_MNTTAB "/etc/mnttab"
-#endif
-
-/* *** *** *** ********************************************* *** *** *** */
-/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */
-/* *** *** *** ********************************************* *** *** *** */
-
-/* stolen from quota-3.13 (quota-tools) */
-
-#define PROC_PARTITIONS "/proc/partitions"
-#define DEVLABELDIR "/dev"
-#define UUID 1
-#define VOL 2
-
-static struct uuidCache_s {
- struct uuidCache_s *next;
- char uuid[16];
- char *label;
- char *device;
-} *uuidCache = NULL;
-
-#define EXT2_SUPER_MAGIC 0xEF53
-struct ext2_super_block {
- unsigned char s_dummy1[56];
- unsigned char s_magic[2];
- unsigned char s_dummy2[46];
- unsigned char s_uuid[16];
- char s_volume_name[16];
-};
-#define ext2magic(s) \
- ((unsigned int)s.s_magic[0] + (((unsigned int)s.s_magic[1]) << 8))
-
-#if HAVE_XFS_XQM_H
-struct xfs_super_block {
- unsigned char s_magic[4];
- unsigned char s_dummy[28];
- unsigned char s_uuid[16];
- unsigned char s_dummy2[60];
- char s_fsname[12];
-};
-#endif /* HAVE_XFS_XQM_H */
-
-#define REISER_SUPER_MAGIC "ReIsEr2Fs"
-struct reiserfs_super_block {
- unsigned char s_dummy1[52];
- unsigned char s_magic[10];
- unsigned char s_dummy2[22];
- unsigned char s_uuid[16];
- char s_volume_name[16];
-};
-
-/* for now, only ext2 and xfs are supported */
-static int get_label_uuid(const char *device, char **label, char *uuid) {
- /* start with ext2 and xfs tests, taken from mount_guess_fstype */
- /* should merge these later */
- int fd, rv = 1;
- size_t namesize;
- struct ext2_super_block e2sb;
-#if HAVE_XFS_XQM_H
- struct xfs_super_block xfsb;
-#endif
- struct reiserfs_super_block reisersb;
-
- fd = open(device, O_RDONLY);
- if (fd == -1) {
- return rv;
- }
-
- if (lseek(fd, 1024, SEEK_SET) == 1024 &&
- read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb) &&
- ext2magic(e2sb) == EXT2_SUPER_MAGIC) {
- memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
- namesize = sizeof(e2sb.s_volume_name);
- *label = smalloc(namesize + 1);
- sstrncpy(*label, e2sb.s_volume_name, namesize);
- rv = 0;
-#if HAVE_XFS_XQM_H
- } else if (lseek(fd, 0, SEEK_SET) == 0 &&
- read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb) &&
- (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC_STR, 4) == 0 ||
- strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2_STR, 4) == 0)) {
- memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid));
- namesize = sizeof(xfsb.s_fsname);
- *label = smalloc(namesize + 1);
- sstrncpy(*label, xfsb.s_fsname, namesize);
- rv = 0;
-#endif /* HAVE_XFS_XQM_H */
- } else if (lseek(fd, 65536, SEEK_SET) == 65536 &&
- read(fd, (char *)&reisersb, sizeof(reisersb)) ==
- sizeof(reisersb) &&
- !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) {
- memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid));
- namesize = sizeof(reisersb.s_volume_name);
- *label = smalloc(namesize + 1);
- sstrncpy(*label, reisersb.s_volume_name, namesize);
- rv = 0;
- }
- close(fd);
- return rv;
-}
-
-static void uuidcache_addentry(char *device, char *label, char *uuid) {
- struct uuidCache_s *last;
-
- if (!uuidCache) {
- last = uuidCache = smalloc(sizeof(*uuidCache));
- } else {
- for (last = uuidCache; last->next; last = last->next)
- ;
- last->next = smalloc(sizeof(*uuidCache));
- last = last->next;
- }
- last->next = NULL;
- last->device = device;
- last->label = label;
- memcpy(last->uuid, uuid, sizeof(last->uuid));
-}
-
-static void uuidcache_init(void) {
- char line[100];
- char *s;
- int ma, mi, sz;
- static char ptname[100];
- FILE *procpt;
- char uuid[16], *label = NULL;
- char device[110];
- int handleOnFirst;
-
- if (uuidCache) {
- return;
- }
-
- procpt = fopen(PROC_PARTITIONS, "r");
- if (procpt == NULL) {
- return;
- }
-
- for (int firstPass = 1; firstPass >= 0; firstPass--) {
- fseek(procpt, 0, SEEK_SET);
- while (fgets(line, sizeof(line), procpt)) {
- if (sscanf(line, " %d %d %d %[^\n ]", &ma, &mi, &sz, ptname) != 4) {
- continue;
- }
-
- /* skip extended partitions (heuristic: size 1) */
- if (sz == 1) {
- continue;
- }
-
- /* look only at md devices on first pass */
- handleOnFirst = !strncmp(ptname, "md", 2);
- if (firstPass != handleOnFirst) {
- continue;
- }
-
- /* skip entire disk (minor 0, 64, ... on ide;
- 0, 16, ... on sd) */
- /* heuristic: partition name ends in a digit */
-
- for (s = ptname; *s; s++)
- ;
-
- if (isdigit((int)s[-1])) {
- /*
- * Note: this is a heuristic only - there is no reason
- * why these devices should live in /dev.
- * Perhaps this directory should be specifiable by option.
- * One might for example have /devlabel with links to /dev
- * for the devices that may be accessed in this way.
- * (This is useful, if the cdrom on /dev/hdc must not
- * be accessed.)
- */
- snprintf(device, sizeof(device), "%s/%s", DEVLABELDIR, ptname);
- if (!get_label_uuid(device, &label, uuid)) {
- uuidcache_addentry(sstrdup(device), label, uuid);
- }
- }
- }
- }
- fclose(procpt);
-}
-
-static unsigned char fromhex(char c) {
- if (isdigit((int)c)) {
- return c - '0';
- } else if (islower((int)c)) {
- return c - 'a' + 10;
- } else {
- return c - 'A' + 10;
- }
-}
-
-static char *get_spec_by_x(int n, const char *t) {
- struct uuidCache_s *uc;
-
- uuidcache_init();
- uc = uuidCache;
-
- while (uc) {
- switch (n) {
- case UUID:
- if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
- return sstrdup(uc->device);
- }
- break;
- case VOL:
- if (!strcmp(t, uc->label)) {
- return sstrdup(uc->device);
- }
- break;
- }
- uc = uc->next;
- }
- return NULL;
-}
-
-static char *get_spec_by_uuid(const char *s) {
- char uuid[16];
-
- if (strlen(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' ||
- s[23] != '-') {
- goto bad_uuid;
- }
-
- for (int i = 0; i < 16; i++) {
- if (*s == '-') {
- s++;
- }
- if (!isxdigit((int)s[0]) || !isxdigit((int)s[1])) {
- goto bad_uuid;
- }
- uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
- s += 2;
- }
- return get_spec_by_x(UUID, uuid);
-
-bad_uuid:
- DEBUG("utils_mount: Found an invalid UUID: %s", s);
- return NULL;
-}
-
-static char *get_spec_by_volume_label(const char *s) {
- return get_spec_by_x(VOL, s);
-}
-
-static char *get_device_name(const char *optstr) {
- char *rc;
-
- if (optstr == NULL) {
- return NULL;
- } else if (strncmp(optstr, "UUID=", 5) == 0) {
- DEBUG("utils_mount: TODO: check UUID= code!");
- rc = get_spec_by_uuid(optstr + 5);
- } else if (strncmp(optstr, "LABEL=", 6) == 0) {
- DEBUG("utils_mount: TODO: check LABEL= code!");
- rc = get_spec_by_volume_label(optstr + 6);
- } else {
- rc = sstrdup(optstr);
- }
-
- if (!rc) {
- DEBUG("utils_mount: Error checking device name: optstr = %s", optstr);
- }
- return rc;
-}
-
-/* What weird OS is this..? I can't find any info with google :/ -octo */
-#if HAVE_LISTMNTENT && 0
-static cu_mount_t *cu_mount_listmntent(void) {
- cu_mount_t *last = *list;
- struct mntent *mnt;
-
- struct tabmntent *mntlist;
- if (listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) {
-#if COLLECT_DEBUG
- DEBUG("utils_mount: calling listmntent() failed: %s", STRERRNO);
-#endif /* COLLECT_DEBUG */
- }
-
- for (struct tabmntent *p = mntlist; p; p = p->next) {
- char *loop = NULL, *device = NULL;
-
- mnt = p->ment;
- loop = cu_mount_getoptionvalue(mnt->mnt_opts, "loop=");
- if (loop == NULL) { /* no loop= mount */
- device = get_device_name(mnt->mnt_fsname);
- if (device == NULL) {
- DEBUG("utils_mount: can't get devicename for fs (%s) %s (%s)"
- ": ignored",
- mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname);
- continue;
- }
- } else {
- device = loop;
- }
- if (*list == NULL) {
- *list = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
- last = *list;
- } else {
- while (last->next != NULL) { /* is last really last? */
- last = last->next;
- }
- last->next = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
- last = last->next;
- }
- last->dir = sstrdup(mnt->mnt_dir);
- last->spec_device = sstrdup(mnt->mnt_fsname);
- last->device = device;
- last->type = sstrdup(mnt->mnt_type);
- last->options = sstrdup(mnt->mnt_opts);
- last->next = NULL;
- } /* for(p = mntlist; p; p = p->next) */
-
- return last;
-} /* cu_mount_t *cu_mount_listmntent(void) */
-/* #endif HAVE_LISTMNTENT */
-
-/* 4.4BSD and Mac OS X (getfsstat) or NetBSD (getvfsstat) */
-#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT
-static cu_mount_t *cu_mount_getfsstat(void) {
-#if HAVE_GETFSSTAT
-#define STRUCT_STATFS struct statfs
-#define CMD_STATFS getfsstat
-#define FLAGS_STATFS MNT_NOWAIT
-/* #endif HAVE_GETFSSTAT */
-#elif HAVE_GETVFSSTAT
-#define STRUCT_STATFS struct statvfs
-#define CMD_STATFS getvfsstat
-#define FLAGS_STATFS ST_NOWAIT
-#endif /* HAVE_GETVFSSTAT */
-
- int bufsize;
- STRUCT_STATFS *buf;
-
- int num;
-
- cu_mount_t *first = NULL;
- cu_mount_t *last = NULL;
- cu_mount_t *new = NULL;
-
- /* Get the number of mounted file systems */
- if ((bufsize = CMD_STATFS(NULL, 0, FLAGS_STATFS)) < 1) {
-#if COLLECT_DEBUG
- DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO);
-#endif /* COLLECT_DEBUG */
- return NULL;
- }
-
- if ((buf = calloc(bufsize, sizeof(*buf))) == NULL)
- return NULL;
-
- /* The bufsize needs to be passed in bytes. Really. This is not in the
- * manpage.. -octo */
- if ((num = CMD_STATFS(buf, bufsize * sizeof(STRUCT_STATFS), FLAGS_STATFS)) <
- 1) {
-#if COLLECT_DEBUG
- DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO);
-#endif /* COLLECT_DEBUG */
- free(buf);
- return NULL;
- }
-
- for (int i = 0; i < num; i++) {
- if ((new = calloc(1, sizeof(*new))) == NULL)
- break;
-
- /* Copy values from `struct mnttab' */
- new->dir = sstrdup(buf[i].f_mntonname);
- new->spec_device = sstrdup(buf[i].f_mntfromname);
- new->type = sstrdup(buf[i].f_fstypename);
- new->options = NULL;
- new->device = get_device_name(new->options);
- new->next = NULL;
-
- /* Append to list */
- if (first == NULL) {
- first = new;
- last = new;
- } else {
- last->next = new;
- last = new;
- }
- }
-
- free(buf);
-
- return first;
-}
-/* #endif HAVE_GETVFSSTAT || HAVE_GETFSSTAT */
-
-/* Solaris (SunOS 10): int getmntent(FILE *fp, struct mnttab *mp); */
-#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT
-static cu_mount_t *cu_mount_gen_getmntent(void) {
- struct mnttab mt;
- FILE *fp;
-
- cu_mount_t *first = NULL;
- cu_mount_t *last = NULL;
- cu_mount_t *new = NULL;
-
- DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
-
- if ((fp = fopen(COLLECTD_MNTTAB, "r")) == NULL) {
- ERROR("fopen (%s): %s", COLLECTD_MNTTAB, STRERRNO);
- return NULL;
- }
-
- while (getmntent(fp, &mt) == 0) {
- if ((new = calloc(1, sizeof(*new))) == NULL)
- break;
-
- /* Copy values from `struct mnttab' */
- new->dir = sstrdup(mt.mnt_mountp);
- new->spec_device = sstrdup(mt.mnt_special);
- new->type = sstrdup(mt.mnt_fstype);
- new->options = sstrdup(mt.mnt_mntopts);
- new->device = get_device_name(new->options);
- new->next = NULL;
-
- /* Append to list */
- if (first == NULL) {
- first = new;
- last = new;
- } else {
- last->next = new;
- last = new;
- }
- }
-
- fclose(fp);
-
- return first;
-} /* static cu_mount_t *cu_mount_gen_getmntent (void) */
-
-#elif HAVE_SEQ_GETMNTENT
-#warn "This version of `getmntent' hat not yet been implemented!"
-/* #endif HAVE_SEQ_GETMNTENT */
-
-#elif HAVE_GETMNTENT_R
-static cu_mount_t *cu_mount_getmntent(void) {
- FILE *fp;
- struct mntent me;
- char mntbuf[1024];
-
- cu_mount_t *first = NULL;
- cu_mount_t *last = NULL;
- cu_mount_t *new = NULL;
-
- DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
-
- if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) {
- ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO);
- return NULL;
- }
-
- while (getmntent_r(fp, &me, mntbuf, sizeof(mntbuf))) {
- if ((new = calloc(1, sizeof(*new))) == NULL)
- break;
-
- /* Copy values from `struct mntent *' */
- new->dir = sstrdup(me.mnt_dir);
- new->spec_device = sstrdup(me.mnt_fsname);
- new->type = sstrdup(me.mnt_type);
- new->options = sstrdup(me.mnt_opts);
- new->device = get_device_name(new->options);
- new->next = NULL;
-
- DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options "
- "= %s, device = %s}",
- new->dir, new->spec_device, new->type, new->options, new->device);
-
- /* Append to list */
- if (first == NULL) {
- first = new;
- last = new;
- } else {
- last->next = new;
- last = new;
- }
- }
-
- endmntent(fp);
-
- DEBUG("utils_mount: return 0x%p", (void *)first);
-
- return first;
-} /* HAVE_GETMNTENT_R */
-
-#elif HAVE_ONE_GETMNTENT
-static cu_mount_t *cu_mount_getmntent(void) {
- FILE *fp;
- struct mntent *me;
-
- cu_mount_t *first = NULL;
- cu_mount_t *last = NULL;
- cu_mount_t *new = NULL;
-
- DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
-
- if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) {
- ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO);
- return NULL;
- }
-
- while ((me = getmntent(fp)) != NULL) {
- if ((new = calloc(1, sizeof(*new))) == NULL)
- break;
-
- /* Copy values from `struct mntent *' */
- new->dir = sstrdup(me->mnt_dir);
- new->spec_device = sstrdup(me->mnt_fsname);
- new->type = sstrdup(me->mnt_type);
- new->options = sstrdup(me->mnt_opts);
- new->device = get_device_name(new->options);
- new->next = NULL;
-
- DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options "
- "= %s, device = %s}",
- new->dir, new->spec_device, new->type, new->options, new->device);
-
- /* Append to list */
- if (first == NULL) {
- first = new;
- last = new;
- } else {
- last->next = new;
- last = new;
- }
- }
-
- endmntent(fp);
-
- DEBUG("utils_mount: return 0x%p", (void *)first);
-
- return first;
-}
-#endif /* HAVE_ONE_GETMNTENT */
-
-/* *** *** *** ******************************************** *** *** *** */
-/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */
-/* *** *** *** ******************************************** *** *** *** */
-
-cu_mount_t *cu_mount_getlist(cu_mount_t **list) {
- cu_mount_t *new;
- cu_mount_t *first = NULL;
- cu_mount_t *last = NULL;
-
- if (list == NULL)
- return NULL;
-
- if (*list != NULL) {
- first = *list;
- last = first;
- while (last->next != NULL)
- last = last->next;
- }
-
-#if HAVE_LISTMNTENT && 0
- new = cu_mount_listmntent();
-#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT
- new = cu_mount_getfsstat();
-#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT
- new = cu_mount_gen_getmntent();
-#elif HAVE_SEQ_GETMNTENT
-#error "This version of `getmntent' hat not yet been implemented!"
-#elif HAVE_GETMNTENT_R
- new = cu_mount_getmntent();
-#elif HAVE_ONE_GETMNTENT
- new = cu_mount_getmntent();
-#else
-#error "Could not determine how to find mountpoints."
-#endif
-
- if (first != NULL) {
- last->next = new;
- } else {
- first = new;
- last = new;
- *list = first;
- }
-
- while ((last != NULL) && (last->next != NULL))
- last = last->next;
-
- return last;
-} /* cu_mount_t *cu_mount_getlist(cu_mount_t **list) */
-
-void cu_mount_freelist(cu_mount_t *list) {
- cu_mount_t *next;
-
- for (cu_mount_t *this = list; this != NULL; this = next) {
- next = this->next;
-
- sfree(this->dir);
- sfree(this->spec_device);
- sfree(this->device);
- sfree(this->type);
- sfree(this->options);
- sfree(this);
- }
-} /* void cu_mount_freelist(cu_mount_t *list) */
-
-char *cu_mount_checkoption(char *line, const char *keyword, int full) {
- char *line2, *l2, *p1, *p2;
- size_t l;
-
- if (line == NULL || keyword == NULL) {
- return NULL;
- }
-
- if (full != 0) {
- full = 1;
- }
-
- line2 = sstrdup(line);
- l2 = line2;
- while (*l2 != '\0') {
- if (*l2 == ',') {
- *l2 = '\0';
- }
- l2++;
- }
-
- l = strlen(keyword);
- p1 = line - 1;
- p2 = strchr(line, ',');
- do {
- if (strncmp(line2 + (p1 - line) + 1, keyword, l + full) == 0) {
- free(line2);
- return p1 + 1;
- }
- p1 = p2;
- if (p1 != NULL) {
- p2 = strchr(p1 + 1, ',');
- }
- } while (p1 != NULL);
-
- free(line2);
- return NULL;
-} /* char *cu_mount_checkoption(char *line, char *keyword, int full) */
-
-char *cu_mount_getoptionvalue(char *line, const char *keyword) {
- char *r;
-
- r = cu_mount_checkoption(line, keyword, 0);
- if (r != NULL) {
- char *p;
- r += strlen(keyword);
- p = strchr(r, ',');
- if (p == NULL) {
- return sstrdup(r);
- } else {
- char *m;
- if ((p - r) == 1) {
- return NULL;
- }
- m = smalloc(p - r + 1);
- sstrncpy(m, r, p - r + 1);
- return m;
- }
- }
- return r;
-} /* char *cu_mount_getoptionvalue(char *line, const char *keyword) */
-
-int cu_mount_type(const char *type) {
- if (strcmp(type, "ext3") == 0)
- return CUMT_EXT3;
- if (strcmp(type, "ext2") == 0)
- return CUMT_EXT2;
- if (strcmp(type, "ufs") == 0)
- return CUMT_UFS;
- if (strcmp(type, "vxfs") == 0)
- return CUMT_VXFS;
- if (strcmp(type, "zfs") == 0)
- return CUMT_ZFS;
- return CUMT_UNKNOWN;
-} /* int cu_mount_type(const char *type) */
+++ /dev/null
-/**
- * collectd - src/utils_mount.h
- * Copyright (C) 2005,2006 Niki W. Waibel
- *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
- *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence 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
- *
- * Author:
- * Niki W. Waibel <niki.waibel@gmx.net>
-**/
-
-/* See below for instructions how to use the public functions. */
-
-#ifndef COLLECTD_UTILS_MOUNT_H
-#define COLLECTD_UTILS_MOUNT_H 1
-
-#include <stdio.h>
-#if HAVE_FS_INFO_H
-#include <fs_info.h>
-#endif
-#if HAVE_FSHELP_H
-#include <fshelp.h>
-#endif
-#if HAVE_PATHS_H
-#include <paths.h>
-#endif
-#if HAVE_MNTENT_H
-#include <mntent.h>
-#endif
-#if HAVE_MNTTAB_H
-#include <mnttab.h>
-#endif
-#if HAVE_SYS_FSTYP_H
-#include <sys/fstyp.h>
-#endif
-#if HAVE_SYS_FS_TYPES_H
-#include <sys/fs_types.h>
-#endif
-#if HAVE_SYS_MNTENT_H
-#include <sys/mntent.h>
-#endif
-#if HAVE_SYS_MNTTAB_H
-#include <sys/mnttab.h>
-#endif
-#if HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
-#if HAVE_SYS_STATFS_H
-#include <sys/statfs.h>
-#endif
-#if HAVE_SYS_VFS_H
-#include <sys/vfs.h>
-#endif
-#if HAVE_SYS_VFSTAB_H
-#include <sys/vfstab.h>
-#endif
-
-/* Collectd Utils Mount Type */
-#define CUMT_UNKNOWN (0)
-#define CUMT_EXT2 (1)
-#define CUMT_EXT3 (2)
-#define CUMT_XFS (3)
-#define CUMT_UFS (4)
-#define CUMT_VXFS (5)
-#define CUMT_ZFS (6)
-
-typedef struct _cu_mount_t cu_mount_t;
-struct _cu_mount_t {
- char *dir; /* "/sys" or "/" */
- char *spec_device; /* "LABEL=/" or "none" or "proc" or "/dev/hda1" */
- char *device; /* "none" or "proc" or "/dev/hda1" */
- char *type; /* "sysfs" or "ext3" */
- char *options; /* "rw,noatime,commit=600,quota,grpquota" */
- cu_mount_t *next;
-};
-
-cu_mount_t *cu_mount_getlist(cu_mount_t **list);
-/*
- DESCRIPTION
- The cu_mount_getlist() function creates a list
- of all mountpoints.
-
- If *list is NULL, a new list is created and *list is
- set to point to the first entry.
-
- If *list is not NULL, the list of mountpoints is appended
- and *list is not changed.
-
- RETURN VALUE
- The cu_mount_getlist() function returns a pointer to
- the last entry of the list, or NULL if an error has
- occured.
-
- NOTES
- In case of an error, *list is not modified.
-*/
-
-void cu_mount_freelist(cu_mount_t *list);
-/*
- DESCRIPTION
- The cu_mount_freelist() function free()s all memory
- allocated by *list and *list itself as well.
-*/
-
-char *cu_mount_checkoption(char *line, const char *keyword, int full);
-/*
- DESCRIPTION
- The cu_mount_checkoption() function is a replacement of
- char *hasmntopt(const struct mntent *mnt, const char *opt).
- In fact hasmntopt() just looks for the first occurrence of the
- characters at opt in mnt->mnt_opts. cu_mount_checkoption()
- checks for the *option* keyword in line, starting at the
- first character of line or after a ','.
-
- If full is not 0 then also the end of keyword has to match
- either the end of line or a ',' after keyword.
-
- RETURN VALUE
- The cu_mount_checkoption() function returns a pointer into
- string line if a match of keyword is found. If no match is
- found cu_mount_checkoption() returns NULL.
-
- NOTES
- Do *not* try to free() the pointer which is returned! It is
- just part of the string line.
-
- full should be set to 0 when matching options like: rw, quota,
- noatime. Set full to 1 when matching options like: loop=,
- gid=, commit=.
-
- EXAMPLES
- If line is "rw,usrquota,grpquota", keyword is "quota", NULL
- will be returned (independend of full).
-
- If line is "rw,usrquota,grpquota", keyword is "usrquota",
- a pointer to "usrquota,grpquota" is returned (independend
- of full).
-
- If line is "rw,loop=/dev/loop1,quota", keyword is "loop="
- and full is 0, then a pointer to "loop=/dev/loop1,quota"
- is returned. If full is not 0 then NULL is returned. But
- maybe you might want to try cu_mount_getoptionvalue()...
-*/
-
-char *cu_mount_getoptionvalue(char *line, const char *keyword);
-/*
- DESCRIPTION
- The cu_mount_getoptionvalue() function can be used to grab
- a VALUE out of a mount option (line) like:
- loop=VALUE
- whereas "loop=" is the keyword.
-
- RETURN VALUE
- If the cu_mount_getoptionvalue() function can find the option
- keyword in line, then memory is allocated for the value of
- that option and a pointer to that value is returned.
-
- If the option keyword is not found, cu_mount_getoptionvalue()
- returns NULL;
-
- NOTES
- Internally it calls cu_mount_checkoption(), then it
- allocates memory for VALUE and returns a pointer to that
- string. So *do not forget* to free() the memory returned
- after use!!!
-*/
-
-int cu_mount_type(const char *type);
-/*
- DESCRIPTION
-
- RETURN VALUE
-*/
-
-#endif /* !COLLECTD_UTILS_MOUNT_H */
+++ /dev/null
-/**
- * collectd - src/tests/test_utils_mount.c
- * Copyright (C) 2013 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#include "collectd.h"
-
-#include "common.h"
-#include "testing.h"
-#include "utils_mount.h"
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-
-#if HAVE_LIBKSTAT
-kstat_ctl_t *kc;
-#endif /* HAVE_LIBKSTAT */
-
-DEF_TEST(cu_mount_checkoption) {
- char line_opts[] = "foo=one,bar=two,qux=three";
- char *foo = strstr(line_opts, "foo");
- char *bar = strstr(line_opts, "bar");
- char *qux = strstr(line_opts, "qux");
-
- char line_bool[] = "one,two,three";
- char *one = strstr(line_bool, "one");
- char *two = strstr(line_bool, "two");
- char *three = strstr(line_bool, "three");
-
- /* Normal operation */
- OK(foo == cu_mount_checkoption(line_opts, "foo", 0));
- OK(bar == cu_mount_checkoption(line_opts, "bar", 0));
- OK(qux == cu_mount_checkoption(line_opts, "qux", 0));
- OK(NULL == cu_mount_checkoption(line_opts, "unknown", 0));
-
- OK(one == cu_mount_checkoption(line_bool, "one", 0));
- OK(two == cu_mount_checkoption(line_bool, "two", 0));
- OK(three == cu_mount_checkoption(line_bool, "three", 0));
- OK(NULL == cu_mount_checkoption(line_bool, "four", 0));
-
- /* Shorter and longer parts */
- OK(foo == cu_mount_checkoption(line_opts, "fo", 0));
- OK(bar == cu_mount_checkoption(line_opts, "bar=", 0));
- OK(qux == cu_mount_checkoption(line_opts, "qux=thr", 0));
-
- OK(one == cu_mount_checkoption(line_bool, "o", 0));
- OK(two == cu_mount_checkoption(line_bool, "tw", 0));
- OK(three == cu_mount_checkoption(line_bool, "thr", 0));
-
- /* "full" flag */
- OK(one == cu_mount_checkoption(line_bool, "one", 1));
- OK(two == cu_mount_checkoption(line_bool, "two", 1));
- OK(three == cu_mount_checkoption(line_bool, "three", 1));
- OK(NULL == cu_mount_checkoption(line_bool, "four", 1));
-
- OK(NULL == cu_mount_checkoption(line_bool, "o", 1));
- OK(NULL == cu_mount_checkoption(line_bool, "tw", 1));
- OK(NULL == cu_mount_checkoption(line_bool, "thr", 1));
-
- return 0;
-}
-DEF_TEST(cu_mount_getoptionvalue) {
- char line_opts[] = "foo=one,bar=two,qux=three";
- char line_bool[] = "one,two,three";
- char *v;
-
- EXPECT_EQ_STR("one", v = cu_mount_getoptionvalue(line_opts, "foo="));
- sfree(v);
- EXPECT_EQ_STR("two", v = cu_mount_getoptionvalue(line_opts, "bar="));
- sfree(v);
- EXPECT_EQ_STR("three", v = cu_mount_getoptionvalue(line_opts, "qux="));
- sfree(v);
- OK(NULL == (v = cu_mount_getoptionvalue(line_opts, "unknown=")));
- sfree(v);
-
- EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "one"));
- sfree(v);
- EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "two"));
- sfree(v);
- EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "three"));
- sfree(v);
- OK(NULL == (v = cu_mount_getoptionvalue(line_bool, "four")));
- sfree(v);
-
- return 0;
-}
-
-int main(void) {
- RUN_TEST(cu_mount_checkoption);
- RUN_TEST(cu_mount_getoptionvalue);
-
- END_TEST;
-}
+++ /dev/null
-/**
- * collectd - src/utils_oauth.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_oauth.h"
-
-#include <curl/curl.h>
-
-#include <yajl/yajl_tree.h>
-#include <yajl/yajl_version.h>
-
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs12.h>
-#include <openssl/sha.h>
-
-/*
- * Private variables
- */
-#define GOOGLE_TOKEN_URL "https://accounts.google.com/o/oauth2/token"
-
-/* Max send buffer size, since there will be only one writer thread and
- * monitoring api supports up to 100K bytes in one request, 64K is reasonable
- */
-#define MAX_BUFFER_SIZE 65536
-#define MAX_ENCODE_SIZE 2048
-
-struct oauth_s {
- char *url;
- char *iss;
- char *aud;
- char *scope;
-
- EVP_PKEY *key;
-
- char *token;
- cdtime_t valid_until;
-};
-
-struct memory_s {
- char *memory;
- size_t size;
-};
-typedef struct memory_s memory_t;
-
-#define OAUTH_GRANT_TYPE "urn:ietf:params:oauth:grant-type:jwt-bearer"
-#define OAUTH_EXPIRATION_TIME TIME_T_TO_CDTIME_T(3600)
-#define OAUTH_HEADER "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"
-
-static const char OAUTH_CLAIM_FORMAT[] = "{"
- "\"iss\":\"%s\","
- "\"scope\":\"%s\","
- "\"aud\":\"%s\","
- "\"exp\":%lu,"
- "\"iat\":%lu"
- "}";
-
-static size_t write_memory(void *contents, size_t size, size_t nmemb, /* {{{ */
- void *userp) {
- size_t realsize = size * nmemb;
- memory_t *mem = (memory_t *)userp;
- char *tmp;
-
- if (0x7FFFFFF0 < mem->size || 0x7FFFFFF0 - mem->size < realsize) {
- ERROR("integer overflow");
- return 0;
- }
-
- tmp = (char *)realloc((void *)mem->memory, mem->size + realsize + 1);
- if (tmp == NULL) {
- /* out of memory! */
- ERROR("write_memory: not enough memory (realloc returned NULL)");
- return 0;
- }
- mem->memory = tmp;
-
- memcpy(&(mem->memory[mem->size]), contents, realsize);
- mem->size += realsize;
- mem->memory[mem->size] = 0;
-
- return realsize;
-} /* }}} size_t write_memory */
-
-/* Base64-encodes "s" and stores the result in buffer.
- * Returns zero on success, non-zero otherwise. */
-static int base64_encode_n(char const *s, size_t s_size, /* {{{ */
- char *buffer, size_t buffer_size) {
- BIO *b64;
- BUF_MEM *bptr;
- int status;
- size_t i;
-
- /* Set up the memory-base64 chain */
- b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- b64 = BIO_push(b64, BIO_new(BIO_s_mem()));
-
- /* Write data to the chain */
- BIO_write(b64, (void const *)s, s_size);
- status = BIO_flush(b64);
- if (status != 1) {
- ERROR("utils_oauth: base64_encode: BIO_flush() failed.");
- BIO_free_all(b64);
- return -1;
- }
-
- /* Never fails */
- BIO_get_mem_ptr(b64, &bptr);
-
- if (buffer_size <= bptr->length) {
- ERROR("utils_oauth: base64_encode: Buffer too small.");
- BIO_free_all(b64);
- return -1;
- }
-
- /* Copy data to buffer. */
- memcpy(buffer, bptr->data, bptr->length);
- buffer[bptr->length] = 0;
-
- /* replace + with -, / with _ and remove padding = at the end */
- for (i = 0; i < bptr->length; i++) {
- if (buffer[i] == '+') {
- buffer[i] = '-';
- } else if (buffer[i] == '/') {
- buffer[i] = '_';
- } else if (buffer[i] == '=') {
- buffer[i] = 0;
- }
- }
-
- BIO_free_all(b64);
- return 0;
-} /* }}} int base64_encode_n */
-
-/* Base64-encodes "s" and stores the result in buffer.
- * Returns zero on success, non-zero otherwise. */
-static int base64_encode(char const *s, /* {{{ */
- char *buffer, size_t buffer_size) {
- return base64_encode_n(s, strlen(s), buffer, buffer_size);
-} /* }}} int base64_encode */
-
-/* get_header returns the base64 encoded OAuth header. */
-static int get_header(char *buffer, size_t buffer_size) /* {{{ */
-{
- char header[] = OAUTH_HEADER;
-
- return base64_encode(header, buffer, buffer_size);
-} /* }}} int get_header */
-
-/* get_claim constructs an OAuth claim and returns it as base64 encoded string.
- */
-static int get_claim(oauth_t *auth, char *buffer, size_t buffer_size) /* {{{ */
-{
- char claim[buffer_size];
- cdtime_t exp;
- cdtime_t iat;
- int status;
-
- iat = cdtime();
- exp = iat + OAUTH_EXPIRATION_TIME;
-
- /* create the claim set */
- status =
- snprintf(claim, sizeof(claim), OAUTH_CLAIM_FORMAT, auth->iss, auth->scope,
- auth->aud, (unsigned long)CDTIME_T_TO_TIME_T(exp),
- (unsigned long)CDTIME_T_TO_TIME_T(iat));
- if (status < 1)
- return -1;
- else if ((size_t)status >= sizeof(claim))
- return ENOMEM;
-
- DEBUG("utils_oauth: get_claim() = %s", claim);
-
- return base64_encode(claim, buffer, buffer_size);
-} /* }}} int get_claim */
-
-/* get_signature signs header and claim with pkey and returns the signature in
- * buffer. */
-static int get_signature(char *buffer, size_t buffer_size, /* {{{ */
- char const *header, char const *claim,
- EVP_PKEY *pkey) {
- char payload[buffer_size];
- size_t payload_len;
- char signature[buffer_size];
- unsigned int signature_size;
- int status;
-
- /* Make the string to sign */
- payload_len = snprintf(payload, sizeof(payload), "%s.%s", header, claim);
- if (payload_len < 1) {
- return -1;
- } else if (payload_len >= sizeof(payload)) {
- return ENOMEM;
- }
-
- /* Create the signature */
- signature_size = EVP_PKEY_size(pkey);
- if (signature_size > sizeof(signature)) {
- ERROR("utils_oauth: Signature is too large (%u bytes).", signature_size);
- return -1;
- }
-
- EVP_MD_CTX *ctx = EVP_MD_CTX_new();
-
- /* EVP_SignInit(3SSL) claims this is a void function, but in fact it returns
- * an int. We're not going to rely on this, though. */
- EVP_SignInit(ctx, EVP_sha256());
-
- status = EVP_SignUpdate(ctx, payload, payload_len);
- if (status != 1) {
- char errbuf[1024];
- ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- ERROR("utils_oauth: EVP_SignUpdate failed: %s", errbuf);
-
- EVP_MD_CTX_free(ctx);
- return -1;
- }
-
- status =
- EVP_SignFinal(ctx, (unsigned char *)signature, &signature_size, pkey);
- if (status != 1) {
- char errbuf[1024];
- ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- ERROR("utils_oauth: EVP_SignFinal failed: %s", errbuf);
-
- EVP_MD_CTX_free(ctx);
- return -1;
- }
-
- EVP_MD_CTX_free(ctx);
-
- return base64_encode_n(signature, (size_t)signature_size, buffer,
- buffer_size);
-} /* }}} int get_signature */
-
-static int get_assertion(oauth_t *auth, char *buffer,
- size_t buffer_size) /* {{{ */
-{
- char header[buffer_size];
- char claim[buffer_size];
- char signature[buffer_size];
- int status;
-
- status = get_header(header, sizeof(header));
- if (status != 0)
- return -1;
-
- status = get_claim(auth, claim, sizeof(claim));
- if (status != 0)
- return -1;
-
- status =
- get_signature(signature, sizeof(signature), header, claim, auth->key);
- if (status != 0)
- return -1;
-
- status = snprintf(buffer, buffer_size, "%s.%s.%s", header, claim, signature);
- if (status < 1)
- return -1;
- else if (status >= buffer_size)
- return ENOMEM;
-
- return 0;
-} /* }}} int get_assertion */
-
-int oauth_parse_json_token(char const *json, /* {{{ */
- char *out_access_token, size_t access_token_size,
- cdtime_t *expires_in) {
- time_t expire_in_seconds = 0;
- yajl_val root;
- yajl_val token_val;
- yajl_val expire_val;
- char errbuf[1024];
- const char *token_path[] = {"access_token", NULL};
- const char *expire_path[] = {"expires_in", NULL};
-
- root = yajl_tree_parse(json, errbuf, sizeof(errbuf));
- if (root == NULL) {
- ERROR("utils_oauth: oauth_parse_json_token: parse error %s", errbuf);
- return -1;
- }
-
- token_val = yajl_tree_get(root, token_path, yajl_t_string);
- if (token_val == NULL) {
- ERROR("utils_oauth: oauth_parse_json_token: access token field not found");
- yajl_tree_free(root);
- return -1;
- }
- sstrncpy(out_access_token, YAJL_GET_STRING(token_val), access_token_size);
-
- expire_val = yajl_tree_get(root, expire_path, yajl_t_number);
- if (expire_val == NULL) {
- ERROR("utils_oauth: oauth_parse_json_token: expire field found");
- yajl_tree_free(root);
- return -1;
- }
- expire_in_seconds = (time_t)YAJL_GET_INTEGER(expire_val);
- DEBUG("oauth_parse_json_token: expires_in %lu",
- (unsigned long)expire_in_seconds);
-
- *expires_in = TIME_T_TO_CDTIME_T(expire_in_seconds);
- yajl_tree_free(root);
- return 0;
-} /* }}} int oauth_parse_json_token */
-
-static int new_token(oauth_t *auth) /* {{{ */
-{
- CURL *curl;
- char assertion[1024];
- char post_data[1024];
- memory_t data;
- char access_token[256];
- cdtime_t expires_in;
- cdtime_t now;
- char curl_errbuf[CURL_ERROR_SIZE];
- int status = 0;
-
- data.size = 0;
- data.memory = NULL;
-
- now = cdtime();
-
- status = get_assertion(auth, assertion, sizeof(assertion));
- if (status != 0) {
- ERROR("utils_oauth: Failed to get token using service account %s.",
- auth->iss);
- return -1;
- }
-
- snprintf(post_data, sizeof(post_data), "grant_type=%s&assertion=%s",
- OAUTH_GRANT_TYPE, assertion);
-
- curl = curl_easy_init();
- if (curl == NULL) {
- ERROR("utils_oauth: curl_easy_init failed.");
- return -1;
- }
-
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
- curl_easy_setopt(curl, CURLOPT_URL, auth->url);
-
- status = curl_easy_perform(curl);
- if (status != CURLE_OK) {
- ERROR("utils_oauth: curl_easy_perform failed with status %i: %s", status,
- curl_errbuf);
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return -1;
- } else {
- long http_code = 0;
-
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
- if ((http_code < 200) || (http_code >= 300)) {
- ERROR("utils_oauth: POST request to %s failed: HTTP error %ld", auth->url,
- http_code);
- if (data.memory != NULL)
- INFO("utils_oauth: Server replied: %s", data.memory);
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return -1;
- }
- }
-
- status = oauth_parse_json_token(data.memory, access_token,
- sizeof(access_token), &expires_in);
- if (status != 0) {
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return -1;
- }
-
- sfree(auth->token);
- auth->token = strdup(access_token);
- if (auth->token == NULL) {
- ERROR("utils_oauth: strdup failed");
- auth->valid_until = 0;
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
- return -1;
- }
-
- INFO("utils_oauth: OAuth2 access token is valid for %.3fs",
- CDTIME_T_TO_DOUBLE(expires_in));
- auth->valid_until = now + expires_in;
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return 0;
-} /* }}} int new_token */
-
-static int renew_token(oauth_t *auth) /* {{{ */
-{
- /* Renew OAuth token 30 seconds *before* it expires. */
- cdtime_t const slack = TIME_T_TO_CDTIME_T(30);
-
- if (auth->valid_until > (cdtime() + slack))
- return 0;
-
- return new_token(auth);
-} /* }}} int renew_token */
-
-static oauth_t *oauth_create(char const *url, char const *iss,
- char const *scope, char const *aud,
- EVP_PKEY *key) /* {{{ */
-{
- oauth_t *auth;
-
- if ((url == NULL) || (iss == NULL) || (scope == NULL) || (aud == NULL) ||
- (key == NULL))
- return NULL;
-
- auth = malloc(sizeof(*auth));
- if (auth == NULL)
- return NULL;
- memset(auth, 0, sizeof(*auth));
-
- auth->url = strdup(url);
- auth->iss = strdup(iss);
- auth->scope = strdup(scope);
- auth->aud = strdup(aud);
-
- if ((auth->url == NULL) || (auth->iss == NULL) || (auth->scope == NULL) ||
- (auth->aud == NULL)) {
- oauth_destroy(auth);
- return NULL;
- }
-
- auth->key = key;
-
- return auth;
-} /* }}} oauth_t *oauth_create */
-
-/*
- * Public
- */
-oauth_google_t oauth_create_google_json(char const *buffer, char const *scope) {
- char errbuf[1024];
- yajl_val root = yajl_tree_parse(buffer, errbuf, sizeof(errbuf));
- if (root == NULL) {
- ERROR("utils_oauth: oauth_create_google_json: parse error %s", errbuf);
- return (oauth_google_t){NULL};
- }
-
- yajl_val field_project =
- yajl_tree_get(root, (char const *[]){"project_id", NULL}, yajl_t_string);
- if (field_project == NULL) {
- ERROR("utils_oauth: oauth_create_google_json: project_id field not found");
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
- char const *project_id = YAJL_GET_STRING(field_project);
-
- yajl_val field_iss = yajl_tree_get(
- root, (char const *[]){"client_email", NULL}, yajl_t_string);
- if (field_iss == NULL) {
- ERROR(
- "utils_oauth: oauth_create_google_json: client_email field not found");
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- yajl_val field_token_uri =
- yajl_tree_get(root, (char const *[]){"token_uri", NULL}, yajl_t_string);
- char const *token_uri = (field_token_uri != NULL)
- ? YAJL_GET_STRING(field_token_uri)
- : GOOGLE_TOKEN_URL;
-
- yajl_val field_priv_key =
- yajl_tree_get(root, (char const *[]){"private_key", NULL}, yajl_t_string);
- if (field_priv_key == NULL) {
- ERROR("utils_oauth: oauth_create_google_json: private_key field not found");
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- BIO *bp = BIO_new_mem_buf(YAJL_GET_STRING(field_priv_key), -1);
- EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
- if (pkey == NULL) {
- char errbuf[1024];
- ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- ERROR(
- "utils_oauth: oauth_create_google_json: parsing private key failed: %s",
- errbuf);
- BIO_free(bp);
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- BIO_free(bp);
-
- oauth_t *oauth = oauth_create(token_uri, YAJL_GET_STRING(field_iss), scope,
- token_uri, pkey);
- if (oauth == NULL) {
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- oauth_google_t ret = {
- .project_id = strdup(project_id), .oauth = oauth,
- };
-
- yajl_tree_free(root);
- return ret;
-} /* oauth_google_t oauth_create_google_json */
-
-oauth_google_t oauth_create_google_file(char const *path,
- char const *scope) { /* {{{ */
- int fd = open(path, O_RDONLY);
- if (fd == -1)
- return (oauth_google_t){NULL};
-
- struct stat st = {0};
- if (fstat(fd, &st) != 0) {
- close(fd);
- return (oauth_google_t){NULL};
- }
-
- size_t buf_size = (size_t)st.st_size;
- char *buf = calloc(1, buf_size + 1);
- if (buf == NULL) {
- close(fd);
- return (oauth_google_t){NULL};
- }
-
- if (sread(fd, buf, buf_size) != 0) {
- free(buf);
- close(fd);
- return (oauth_google_t){NULL};
- }
- close(fd);
- buf[buf_size] = 0;
-
- oauth_google_t ret = oauth_create_google_json(buf, scope);
-
- free(buf);
- return ret;
-} /* }}} oauth_google_t oauth_create_google_file */
-
-/* oauth_create_google_default checks for JSON credentials in well-known
- * positions, similar to gcloud and other tools. */
-oauth_google_t oauth_create_google_default(char const *scope) {
- char const *app_creds;
- if ((app_creds = getenv("GOOGLE_APPLICATION_CREDENTIALS")) != NULL) {
- oauth_google_t ret = oauth_create_google_file(app_creds, scope);
- if (ret.oauth == NULL) {
- ERROR("The environment variable GOOGLE_APPLICATION_CREDENTIALS is set to "
- "\"%s\" but that file could not be read.",
- app_creds);
- } else {
- return ret;
- }
- }
-
- char const *home;
- if ((home = getenv("HOME")) != NULL) {
- char path[PATH_MAX];
- snprintf(path, sizeof(path),
- "%s/.config/gcloud/application_default_credentials.json", home);
-
- oauth_google_t ret = oauth_create_google_file(path, scope);
- if (ret.oauth != NULL) {
- return ret;
- }
- }
-
- return (oauth_google_t){NULL};
-} /* }}} oauth_google_t oauth_create_google_default */
-
-void oauth_destroy(oauth_t *auth) /* {{{ */
-{
- if (auth == NULL)
- return;
-
- sfree(auth->url);
- sfree(auth->iss);
- sfree(auth->scope);
- sfree(auth->aud);
-
- if (auth->key != NULL) {
- EVP_PKEY_free(auth->key);
- auth->key = NULL;
- }
-
- sfree(auth);
-} /* }}} void oauth_destroy */
-
-int oauth_access_token(oauth_t *auth, char *buffer,
- size_t buffer_size) /* {{{ */
-{
- int status;
-
- if (auth == NULL)
- return EINVAL;
-
- status = renew_token(auth);
- if (status != 0)
- return status;
- assert(auth->token != NULL);
-
- sstrncpy(buffer, auth->token, buffer_size);
- return 0;
-} /* }}} int oauth_access_token */
+++ /dev/null
-/**
- * collectd - src/utils_oauth.h
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_OAUTH_H
-#define UTILS_OAUTH_H
-
-#include "collectd.h"
-#include "utils_time.h"
-
-#ifndef GOOGLE_OAUTH_URL
-#define GOOGLE_OAUTH_URL "https://www.googleapis.com/oauth2/v3/token"
-#endif
-
-struct oauth_s;
-typedef struct oauth_s oauth_t;
-
-int oauth_parse_json_token(char const *json, char *out_access_token,
- size_t access_token_size, cdtime_t *expires_in);
-
-typedef struct {
- char *project_id;
- oauth_t *oauth;
-} oauth_google_t;
-
-/* oauth_create_google_json creates an OAuth object from JSON encoded
- * credentials. */
-oauth_google_t oauth_create_google_json(char const *json, char const *scope);
-
-/* oauth_create_google_file reads path, which contains JSON encoded service
- * account credentials, and returns an OAuth object. */
-oauth_google_t oauth_create_google_file(char const *path, char const *scope);
-
-/* oauth_create_google_default looks for service account credentials in a couple
- * of well-known places and returns an OAuth object if found. The well known
- * locations are:
- *
- * - ${GOOGLE_APPLICATION_CREDENTIALS}
- * - ${HOME}/.config/gcloud/application_default_credentials.json
- */
-oauth_google_t oauth_create_google_default(char const *scope);
-
-/* oauth_destroy frees all resources associated with an OAuth object. */
-void oauth_destroy(oauth_t *auth);
-
-int oauth_access_token(oauth_t *auth, char *buffer, size_t buffer_size);
-
-#endif
+++ /dev/null
-/**
- * collectd - src/tests/utils_oauth_test.c
- * Copyright (C) 2015 Google Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at google.com>
- **/
-
-#include "testing.h"
-#include "utils_oauth.h"
-
-struct {
- char *json;
- int status;
- char *access_token;
- cdtime_t expires_in;
-} cases[] = {
- {
- "{\"access_token\":\"MaeC6kaePhie1ree\",\"expires_in\":3600}",
- /* status = */ 0, "MaeC6kaePhie1ree", TIME_T_TO_CDTIME_T_STATIC(3600),
- },
- {
- "{\"token_type\":\"Bearer\",\"expires_in\":1800,\"access_token\":"
- "\"aeThiebee2gushuY\"}",
- /* status = */ 0, "aeThiebee2gushuY", TIME_T_TO_CDTIME_T_STATIC(1800),
- },
- {
- "{\"ignored_key\":\"uaph5aewaeghi1Ge\",\"expires_in\":3600}",
- /* status = */ -1, NULL, 0,
- },
- {
- /* expires_in missing */
- "{\"access_token\":\"shaephohbie9Ahch\"}",
- /* status = */ -1, NULL, 0,
- },
-};
-
-DEF_TEST(simple) /* {{{ */
-{
- size_t i;
- _Bool success = 1;
-
- for (i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
- char buffer[1024];
- cdtime_t expires_in;
-
- EXPECT_EQ_INT(cases[i].status,
- oauth_parse_json_token(cases[i].json, buffer, sizeof(buffer),
- &expires_in));
- if (cases[i].status != 0)
- continue;
-
- EXPECT_EQ_STR(cases[i].access_token, buffer);
- EXPECT_EQ_UINT64(cases[i].expires_in, expires_in);
- }
-
- return success ? 0 : -1;
-} /* }}} simple */
-
-DEF_TEST(oauth_create_google_json) {
- char const *in =
- "{\"type\": \"service_account\","
- "\"project_id\":\"collectd.org:unit-test\","
- "\"private_key_id\": \"ed7b4eb6c1b61a7bedab5bcafff374f7fc820698\","
- "\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n"
- "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNvS71Lr2WIEqx\\n"
- "U766iJGORVVib0FnHhOf/0FEI4Hw+tF11vP3LZj0AyQFIi/h2l2EDXOr43C6Gt+K\\n"
- "0stsyaWvRNzeQa+dUFY5A/ZEtdvYVPq7KudML5Hs9DNmWFlM/iIfQyIUJ+vHv7fe\\n"
- "pJGgu4ZgSkNWehmWj3qiRzIvYxKvDIQizqPZNlTh+33KQcT2x+ErkuB3snQu8hSK\\n"
- "HAg2sCvORqKGOvN9F4bAqXt5T0NVjGy4YXeuif1p/Np/GH6Ys1p+etgGwvIimXIv\\n"
- "jFL9K/ZtrTOcFdy4R5bwrj2piCZa2T5H6fupVp2tVgIuS53r2fEaBMLD97oAvwZ3\\n"
- "9XPxG1NLAgMBAAECggEACgHroKcrN1FkdgyzSIKFG1evCBCOV17kqHyI5wYXzNTT\\n"
- "zyNrZDjBFGQkt+U0/AucTznnnahSCZNuD+QiBgLRqYgJevwp99Z6YzVDS438Xsuq\\n"
- "Ezmf3O+sGEu78Pys11cTP38LT3yuS4iSqo9Jus5JrTG05dDJoYO4J4rxW3xlDRj8\\n"
- "lQUimXI+S9skaSusf0oErDrjuQG9dxmhnGcSEX+rIe9G0UygTNuI0KKGJ8jmnPz5\\n"
- "OS+sM8qrKcnjrvENFWKLb11HlliHkh6dILoO5rvf5DR+XGKM7BFAsdWg6oI7SFGh\\n"
- "S6zGZ0jUR7QAugrjbTlDOCnAuZ+Mbc/4yHZ3u5PlcQKBgQDuvH1ds1YmmbOllOK5\\n"
- "JtkdjCUUyH1bgkMrmcg/KkRARPRHQvfAioZsC6d0fa6jq0kTW/3Zu14IsVXgM8xK\\n"
- "fuNSp8LdY+NCtJnfvdLaChgAwZaQLX4qgV0qYw8iLv5ifa4ZY0qaZioJCzkv57y1\\n"
- "KkavYvITboO7aUSa441Zko9c+wKBgQDcndg0QpWH6JMz/FkCf/KDyW/cUODfKXhP\\n"
- "5p9eTcVlfDL2sAb2RzVhvKZcuWXVwnfaDP0oBj2/SBLGx0idUb+VHdM/IGiLroyK\\n"
- "pAHpNM//dowiGL1qPPOLXrzF/vn+w4t2Dqggfcqu52SzRiyaxUtSMnNyyyU19cO+\\n"
- "pb7wAS5x8QKBgCW7WL0UeQtEw6Xp8CN/RlVrLvkn7tglsGQVvBZvobXesBULOokN\\n"
- "28z70o2Qx6dKjRQoN+jPuj75eC8lQKaNg3Qu25eOD/8c+CzqnYakjcKg1iEXb5dc\\n"
- "NtNaMKwgbUg3wOp2TPY2K3KeeX1ezO59LgrOQqBbmSpnqtYoHNEJXus9AoGAWl/y\\n"
- "9J2eIdm9i5tBX0vIrgHz5/3d0K1tUtX3zSrwxT0Wp4W+pF7RWGNuhyePtvx+Gn4d\\n"
- "qqq72sMMpg93CLM3Vz+rjP2atjXf7t92xPDUkCMhDsqxtXaYkixSCo4EHUA/vjIM\\n"
- "35qIUBQMZYBGv3Q5AcgXERx09uDhuhSt3iWtwBECgYAHFnCh8fKsJbQrVN10tU/h\\n"
- "ofVx0KZkUpBz8eNQPuxt4aY+LyWsKVKtnduw2WdumuOY66cUN1lsi8Bz/cq1dhPt\\n"
- "Oc2S7pqjbu2Q1Oqx+/yr6jqsvKaSxHmcpbWQBsGn6UaWZgYZcAtQBcqDAp7pylwj\\n"
- "tejRh0NB8d81H5Dli1Qfzw==\\n"
- "-----END PRIVATE KEY-----\\n\","
- "\"client_email\":\"example-sacct@unit-test.iam.gserviceaccount.com\", "
- "\"client_id\": \"109958449193027604084\","
- "\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\","
- "\"token_uri\":\"https://accounts.google.com/o/oauth2/token\","
- "\"auth_provider_x509_cert_url\":"
- "\"https://www.googleapis.com/oauth2/v1/certs\","
- "\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/"
- "metadata/x509/example-sacct%40ssc-serv-dev.iam.gserviceaccount.com\"}";
-
- oauth_google_t ret =
- oauth_create_google_json(in, "https://collectd.org/example.scope");
-
- EXPECT_EQ_STR("collectd.org:unit-test", ret.project_id);
-
- CHECK_NOT_NULL(ret.oauth);
- struct {
- char *url;
- char *iss;
- char *aud;
- char *scope;
- } *obj = (void *)ret.oauth;
-
- EXPECT_EQ_STR("https://accounts.google.com/o/oauth2/token", obj->url);
- EXPECT_EQ_STR("example-sacct@unit-test.iam.gserviceaccount.com", obj->iss);
- EXPECT_EQ_STR("https://collectd.org/example.scope", obj->scope);
-
- free(ret.project_id);
- oauth_destroy(ret.oauth);
-
- return 0;
-}
-
-int main(int argc, char **argv) /* {{{ */
-{
- RUN_TEST(simple);
- RUN_TEST(oauth_create_google_json);
-
- END_TEST;
-} /* }}} int main */
+++ /dev/null
-/**
- * collectd - src/utils_ovs.c
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- *of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to
- *do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- *all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- * Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
- **/
-
-/* clang-format off */
-/*
- * OVS DB API internal architecture diagram
- * +------------------------------------------------------------------------------+
- * |OVS plugin |OVS utils |
- * | | +------------------------+ |
- * | | | echo handler | JSON request/ |
- * | | +--+ (ovs_db_table_echo_cb) +<---+---------+ update event/ |
- * | | | | | | | result |
- * | | | +------------------------+ | | |
- * | | | | +----+---+--------+ |
- * | +----------+ | | +------------------------+ | | | | |
- * | | update | | | | update handler | | | YAJL | JSON | |
- * | | callback +<-------+(ovs_db_table_update_cp)+<---+ | parser | reader | |
- * | +----------+ | | | | | | | | |
- * | | | +------------------------+ | +--------+---+----+ |
- * | | | | ^ |
- * | +----------+ | | +------------------------+ | | |
- * | | result | | | | result handler | | | |
- * | | callback +<-------+ (ovs_db_result_cb) +<---+ JSON raw | |
- * | +----------+ | | | | data | |
- * | | | +------------------------+ | |
- * | | | | |
- * | | | +------------------+ +------------+----+ |
- * | +----------+ | | |thread| | |thread| | |
- * | | init | | | | | reconnect | | |
- * | | callback +<---------+ EVENT WORKER +<------------+ POLL WORKER | |
- * | +----------+ | | +------------------+ +--------+--------+ |
- * | | | ^ |
- * +----------------+-------------------------------------------------------------+
- * | |
- * JSON|echo reply raw|data
- * v v
- * +-------------------+----------------------------------------------+-----------+
- * | TCP/UNIX socket |
- * +-------------------------------------------------------------------------------
- */
-/* clang-format on */
-
-/* collectd headers */
-#include "collectd.h"
-
-#include "common.h"
-
-/* private headers */
-#include "utils_ovs.h"
-
-/* system libraries */
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#if HAVE_POLL_H
-#include <poll.h>
-#endif
-#if HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
-
-#include <semaphore.h>
-
-#define OVS_ERROR(fmt, ...) \
- do { \
- ERROR("ovs_utils: " fmt, ##__VA_ARGS__); \
- } while (0)
-#define OVS_DEBUG(fmt, ...) \
- do { \
- DEBUG("%s:%d:%s(): " fmt, __FILE__, __LINE__, __FUNCTION__, \
- ##__VA_ARGS__); \
- } while (0)
-
-#define OVS_DB_POLL_TIMEOUT 1 /* poll receive timeout (sec) */
-#define OVS_DB_POLL_READ_BLOCK_SIZE 512 /* read block size (bytes) */
-#define OVS_DB_DEFAULT_DB_NAME "Open_vSwitch"
-
-#define OVS_DB_EVENT_NONE 0
-#define OVS_DB_EVENT_TIMEOUT 5 /* event thread timeout (sec) */
-#define OVS_DB_EVENT_TERMINATE 1
-#define OVS_DB_EVENT_CONN_ESTABLISHED 2
-#define OVS_DB_EVENT_CONN_TERMINATED 3
-
-#define OVS_DB_POLL_STATE_RUNNING 1
-#define OVS_DB_POLL_STATE_EXITING 2
-
-#define OVS_DB_SEND_REQ_TIMEOUT 5 /* send request timeout (sec) */
-
-#define OVS_YAJL_CALL(func, ...) \
- do { \
- yajl_gen_ret = yajl_gen_status_ok; \
- if ((yajl_gen_ret = func(__VA_ARGS__)) != yajl_gen_status_ok) \
- goto yajl_gen_failure; \
- } while (0)
-#define OVS_YAJL_ERROR_BUFFER_SIZE 1024
-#define OVS_ERROR_BUFF_SIZE 512
-#define OVS_UID_STR_SIZE 17 /* 64-bit HEX string len + '\0' */
-
-/* JSON reader internal data */
-struct ovs_json_reader_s {
- char *buff_ptr;
- size_t buff_size;
- size_t buff_offset;
- size_t json_offset;
-};
-typedef struct ovs_json_reader_s ovs_json_reader_t;
-
-/* Result callback declaration */
-struct ovs_result_cb_s {
- sem_t sync;
- ovs_db_result_cb_t call;
-};
-typedef struct ovs_result_cb_s ovs_result_cb_t;
-
-/* Table callback declaration */
-struct ovs_table_cb_s {
- ovs_db_table_cb_t call;
-};
-typedef struct ovs_table_cb_s ovs_table_cb_t;
-
-/* Callback declaration */
-struct ovs_callback_s {
- uint64_t uid;
- union {
- ovs_result_cb_t result;
- ovs_table_cb_t table;
- };
- struct ovs_callback_s *next;
- struct ovs_callback_s *prev;
-};
-typedef struct ovs_callback_s ovs_callback_t;
-
-/* Event thread data declaration */
-struct ovs_event_thread_s {
- pthread_t tid;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- int value;
-};
-typedef struct ovs_event_thread_s ovs_event_thread_t;
-
-/* Poll thread data declaration */
-struct ovs_poll_thread_s {
- pthread_t tid;
- pthread_mutex_t mutex;
- int state;
-};
-typedef struct ovs_poll_thread_s ovs_poll_thread_t;
-
-/* OVS DB internal data declaration */
-struct ovs_db_s {
- ovs_poll_thread_t poll_thread;
- ovs_event_thread_t event_thread;
- pthread_mutex_t mutex;
- ovs_callback_t *remote_cb;
- ovs_db_callback_t cb;
- char service[OVS_DB_ADDR_SERVICE_SIZE];
- char node[OVS_DB_ADDR_NODE_SIZE];
- char unix_path[OVS_DB_ADDR_NODE_SIZE];
- int sock;
-};
-
-/* Global variables */
-static uint64_t ovs_uid;
-static pthread_mutex_t ovs_uid_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/* Post an event to event thread.
- * Possible events are:
- * OVS_DB_EVENT_TERMINATE
- * OVS_DB_EVENT_CONN_ESTABLISHED
- * OVS_DB_EVENT_CONN_TERMINATED
- */
-static void ovs_db_event_post(ovs_db_t *pdb, int event) {
- pthread_mutex_lock(&pdb->event_thread.mutex);
- pdb->event_thread.value = event;
- pthread_mutex_unlock(&pdb->event_thread.mutex);
- pthread_cond_signal(&pdb->event_thread.cond);
-}
-
-/* Check if POLL thread is still running. Returns
- * 1 if running otherwise 0 is returned */
-static bool ovs_db_poll_is_running(ovs_db_t *pdb) {
- int state = 0;
- pthread_mutex_lock(&pdb->poll_thread.mutex);
- state = pdb->poll_thread.state;
- pthread_mutex_unlock(&pdb->poll_thread.mutex);
- return state == OVS_DB_POLL_STATE_RUNNING;
-}
-
-/* Generate unique identifier (UID). It is used by OVS DB API
- * to set "id" field for any OVS DB JSON request. */
-static uint64_t ovs_uid_generate() {
- uint64_t new_uid;
- pthread_mutex_lock(&ovs_uid_mutex);
- new_uid = ++ovs_uid;
- pthread_mutex_unlock(&ovs_uid_mutex);
- return new_uid;
-}
-
-/*
- * Callback API. These function are used to store
- * registered callbacks in OVS DB API.
- */
-
-/* Add new callback into OVS DB object */
-static void ovs_db_callback_add(ovs_db_t *pdb, ovs_callback_t *new_cb) {
- pthread_mutex_lock(&pdb->mutex);
- if (pdb->remote_cb)
- pdb->remote_cb->prev = new_cb;
- new_cb->next = pdb->remote_cb;
- new_cb->prev = NULL;
- pdb->remote_cb = new_cb;
- pthread_mutex_unlock(&pdb->mutex);
-}
-
-/* Remove callback from OVS DB object */
-static void ovs_db_callback_remove(ovs_db_t *pdb, ovs_callback_t *del_cb) {
- pthread_mutex_lock(&pdb->mutex);
- ovs_callback_t *pre_cb = del_cb->prev;
- ovs_callback_t *next_cb = del_cb->next;
-
- if (next_cb)
- next_cb->prev = del_cb->prev;
-
- if (pre_cb)
- pre_cb->next = del_cb->next;
- else
- pdb->remote_cb = del_cb->next;
-
- free(del_cb);
- pthread_mutex_unlock(&pdb->mutex);
-}
-
-/* Remove all callbacks form OVS DB object */
-static void ovs_db_callback_remove_all(ovs_db_t *pdb) {
- pthread_mutex_lock(&pdb->mutex);
- while (pdb->remote_cb != NULL) {
- ovs_callback_t *del_cb = pdb->remote_cb;
- pdb->remote_cb = del_cb->next;
- sfree(del_cb);
- }
- pthread_mutex_unlock(&pdb->mutex);
-}
-
-/* Get/find callback in OVS DB object by UID. Returns pointer
- * to requested callback otherwise NULL is returned.
- *
- * IMPORTANT NOTE:
- * The OVS DB mutex MUST be locked by the caller
- * to make sure that returned callback is still valid.
- */
-static ovs_callback_t *ovs_db_callback_get(ovs_db_t *pdb, uint64_t uid) {
- for (ovs_callback_t *cb = pdb->remote_cb; cb != NULL; cb = cb->next)
- if (cb->uid == uid)
- return cb;
- return NULL;
-}
-
-/* Send all requested data to the socket. Returns 0 if
- * ALL request data has been sent otherwise negative value
- * is returned */
-static int ovs_db_data_send(const ovs_db_t *pdb, const char *data, size_t len) {
- ssize_t nbytes = 0;
- size_t rem = len;
- size_t off = 0;
-
- while (rem > 0) {
- if ((nbytes = send(pdb->sock, data + off, rem, 0)) <= 0)
- return -1;
- rem -= (size_t)nbytes;
- off += (size_t)nbytes;
- }
- return 0;
-}
-
-/*
- * YAJL (Yet Another JSON Library) helper functions
- * Documentation (https://lloyd.github.io/yajl/)
- */
-
-/* Add null-terminated string into YAJL generator handle (JSON object).
- * Similar function to yajl_gen_string() but takes null-terminated string
- * instead of string and its length.
- *
- * jgen - YAJL generator handle allocated by yajl_gen_alloc()
- * string - Null-terminated string
- */
-static yajl_gen_status ovs_yajl_gen_tstring(yajl_gen hander,
- const char *string) {
- return yajl_gen_string(hander, (const unsigned char *)string, strlen(string));
-}
-
-/* Add YAJL value into YAJL generator handle (JSON object)
- *
- * jgen - YAJL generator handle allocated by yajl_gen_alloc()
- * jval - YAJL value usually returned by yajl_tree_get()
- */
-static yajl_gen_status ovs_yajl_gen_val(yajl_gen jgen, yajl_val jval) {
- size_t array_len = 0;
- yajl_val *jvalues = NULL;
- yajl_val jobj_value = NULL;
- const char *obj_key = NULL;
- size_t obj_len = 0;
- yajl_gen_status yajl_gen_ret = yajl_gen_status_ok;
-
- if (jval == NULL)
- return yajl_gen_generation_complete;
-
- if (YAJL_IS_STRING(jval))
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, YAJL_GET_STRING(jval));
- else if (YAJL_IS_DOUBLE(jval))
- OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_DOUBLE(jval));
- else if (YAJL_IS_INTEGER(jval))
- OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_INTEGER(jval));
- else if (YAJL_IS_TRUE(jval))
- OVS_YAJL_CALL(yajl_gen_bool, jgen, 1);
- else if (YAJL_IS_FALSE(jval))
- OVS_YAJL_CALL(yajl_gen_bool, jgen, 0);
- else if (YAJL_IS_NULL(jval))
- OVS_YAJL_CALL(yajl_gen_null, jgen);
- else if (YAJL_IS_ARRAY(jval)) {
- /* create new array and add all elements into the array */
- array_len = YAJL_GET_ARRAY(jval)->len;
- jvalues = YAJL_GET_ARRAY(jval)->values;
- OVS_YAJL_CALL(yajl_gen_array_open, jgen);
- for (size_t i = 0; i < array_len; i++)
- OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jvalues[i]);
- OVS_YAJL_CALL(yajl_gen_array_close, jgen);
- } else if (YAJL_IS_OBJECT(jval)) {
- /* create new object and add all elements into the object */
- OVS_YAJL_CALL(yajl_gen_map_open, jgen);
- obj_len = YAJL_GET_OBJECT(jval)->len;
- for (size_t i = 0; i < obj_len; i++) {
- obj_key = YAJL_GET_OBJECT(jval)->keys[i];
- jobj_value = YAJL_GET_OBJECT(jval)->values[i];
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, obj_key);
- OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jobj_value);
- }
- OVS_YAJL_CALL(yajl_gen_map_close, jgen);
- } else {
- OVS_ERROR("%s() unsupported value type %d (skip)", __FUNCTION__,
- (int)(jval)->type);
- goto yajl_gen_failure;
- }
- return yajl_gen_status_ok;
-
-yajl_gen_failure:
- OVS_ERROR("%s() error to generate value", __FUNCTION__);
- return yajl_gen_ret;
-}
-
-/* OVS DB echo request handler. When OVS DB sends
- * "echo" request to the client, client should generate
- * "echo" replay with the same content received in the
- * request */
-static int ovs_db_table_echo_cb(const ovs_db_t *pdb, yajl_val jnode) {
- yajl_val jparams;
- yajl_val jid;
- yajl_gen jgen;
- size_t resp_len = 0;
- const char *resp = NULL;
- const char *params_path[] = {"params", NULL};
- const char *id_path[] = {"id", NULL};
- yajl_gen_status yajl_gen_ret;
-
- if ((jgen = yajl_gen_alloc(NULL)) == NULL)
- return -1;
-
- /* check & get request attributes */
- if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL ||
- ((jid = yajl_tree_get(jnode, id_path, yajl_t_any)) == NULL)) {
- OVS_ERROR("parse echo request failed");
- goto yajl_gen_failure;
- }
-
- /* generate JSON echo response */
- OVS_YAJL_CALL(yajl_gen_map_open, jgen);
-
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "result");
- OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams);
-
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "error");
- OVS_YAJL_CALL(yajl_gen_null, jgen);
-
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id");
- OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jid);
-
- OVS_YAJL_CALL(yajl_gen_map_close, jgen);
- OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&resp,
- &resp_len);
-
- /* send the response */
- OVS_DEBUG("response: %s", resp);
- if (ovs_db_data_send(pdb, resp, resp_len) < 0) {
- OVS_ERROR("send echo reply failed");
- goto yajl_gen_failure;
- }
- /* clean up and return success */
- yajl_gen_clear(jgen);
- return 0;
-
-yajl_gen_failure:
- /* release memory */
- yajl_gen_clear(jgen);
- return -1;
-}
-
-/* Get OVS DB registered callback by YAJL val. The YAJL
- * value should be YAJL string (UID). Returns NULL if
- * callback hasn't been found. See also ovs_db_callback_get()
- * description for addition info.
- */
-static ovs_callback_t *ovs_db_table_callback_get(ovs_db_t *pdb, yajl_val jid) {
- char *endptr = NULL;
- const char *suid = NULL;
- uint64_t uid;
-
- if (jid && YAJL_IS_STRING(jid)) {
- suid = YAJL_GET_STRING(jid);
- uid = (uint64_t)strtoul(suid, &endptr, 16);
- if (*endptr == '\0' && uid)
- return ovs_db_callback_get(pdb, uid);
- }
-
- return NULL;
-}
-
-/* OVS DB table update event handler.
- * This callback is called by POLL thread if OVS DB
- * table update callback is received from the DB
- * server. Once registered callback found, it's called
- * by this handler. */
-static int ovs_db_table_update_cb(ovs_db_t *pdb, yajl_val jnode) {
- ovs_callback_t *cb = NULL;
- yajl_val jvalue;
- yajl_val jparams;
- yajl_val jtable_updates;
- const char *params_path[] = {"params", NULL};
- const char *id_path[] = {"id", NULL};
-
- /* check & get request attributes */
- if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL ||
- (yajl_tree_get(jnode, id_path, yajl_t_null) == NULL)) {
- OVS_ERROR("invalid OVS DB request received");
- return -1;
- }
-
- /* check array length: [<json-value>, <table-updates>] */
- if ((YAJL_GET_ARRAY(jparams) == NULL) ||
- (YAJL_GET_ARRAY(jparams)->len != 2)) {
- OVS_ERROR("invalid OVS DB request received");
- return -1;
- }
-
- jvalue = YAJL_GET_ARRAY(jparams)->values[0];
- jtable_updates = YAJL_GET_ARRAY(jparams)->values[1];
- if ((!YAJL_IS_OBJECT(jtable_updates)) || (!YAJL_IS_STRING(jvalue))) {
- OVS_ERROR("invalid OVS DB request id or table update received");
- return -1;
- }
-
- /* find registered callback based on <json-value> */
- pthread_mutex_lock(&pdb->mutex);
- cb = ovs_db_table_callback_get(pdb, jvalue);
- if (cb == NULL || cb->table.call == NULL) {
- OVS_ERROR("No OVS DB table update callback found");
- pthread_mutex_unlock(&pdb->mutex);
- return -1;
- }
-
- /* call registered callback */
- cb->table.call(jtable_updates);
- pthread_mutex_unlock(&pdb->mutex);
- return 0;
-}
-
-/* OVS DB result request handler.
- * This callback is called by POLL thread if OVS DB
- * result reply is received from the DB server.
- * Once registered callback found, it's called
- * by this handler. */
-static int ovs_db_result_cb(ovs_db_t *pdb, yajl_val jnode) {
- ovs_callback_t *cb = NULL;
- yajl_val jresult;
- yajl_val jerror;
- yajl_val jid;
- const char *result_path[] = {"result", NULL};
- const char *error_path[] = {"error", NULL};
- const char *id_path[] = {"id", NULL};
-
- jresult = yajl_tree_get(jnode, result_path, yajl_t_any);
- jerror = yajl_tree_get(jnode, error_path, yajl_t_any);
- jid = yajl_tree_get(jnode, id_path, yajl_t_string);
-
- /* check & get result attributes */
- if (!jresult || !jerror || !jid)
- return -1;
-
- /* try to find registered callback */
- pthread_mutex_lock(&pdb->mutex);
- cb = ovs_db_table_callback_get(pdb, jid);
- if (cb != NULL && cb->result.call != NULL) {
- /* call registered callback */
- cb->result.call(jresult, jerror);
- /* unlock owner of the reply */
- sem_post(&cb->result.sync);
- }
-
- pthread_mutex_unlock(&pdb->mutex);
- return 0;
-}
-
-/* Handle JSON data (one request) and call
- * appropriate event OVS DB handler. Currently,
- * update callback 'ovs_db_table_update_cb' and
- * result callback 'ovs_db_result_cb' is supported.
- */
-static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data,
- size_t len) {
- const char *method = NULL;
- char yajl_errbuf[OVS_YAJL_ERROR_BUFFER_SIZE];
- const char *method_path[] = {"method", NULL};
- const char *result_path[] = {"result", NULL};
- char *sjson = NULL;
- yajl_val jnode, jval;
-
- /* duplicate the data to make null-terminated string
- * required for yajl_tree_parse() */
- if ((sjson = calloc(1, len + 1)) == NULL)
- return -1;
-
- sstrncpy(sjson, data, len + 1);
- OVS_DEBUG("[len=%" PRIsz "] %s", len, sjson);
-
- /* parse json data */
- jnode = yajl_tree_parse(sjson, yajl_errbuf, sizeof(yajl_errbuf));
- if (jnode == NULL) {
- OVS_ERROR("yajl_tree_parse() %s", yajl_errbuf);
- sfree(sjson);
- return -1;
- }
-
- /* get method name */
- if ((jval = yajl_tree_get(jnode, method_path, yajl_t_string)) != NULL) {
- if ((method = YAJL_GET_STRING(jval)) == NULL) {
- yajl_tree_free(jnode);
- sfree(sjson);
- return -1;
- }
- if (strcmp("echo", method) == 0) {
- /* echo request from the server */
- if (ovs_db_table_echo_cb(pdb, jnode) < 0)
- OVS_ERROR("handle echo request failed");
- } else if (strcmp("update", method) == 0) {
- /* update notification */
- if (ovs_db_table_update_cb(pdb, jnode) < 0)
- OVS_ERROR("handle update notification failed");
- }
- } else if ((jval = yajl_tree_get(jnode, result_path, yajl_t_any)) != NULL) {
- /* result notification */
- if (ovs_db_result_cb(pdb, jnode) < 0)
- OVS_ERROR("handle result reply failed");
- } else
- OVS_ERROR("connot find method or result failed");
-
- /* release memory */
- yajl_tree_free(jnode);
- sfree(sjson);
- return 0;
-}
-
-/*
- * JSON reader implementation.
- *
- * This module process raw JSON data (byte stream) and
- * returns fully-fledged JSON data which can be processed
- * (parsed) by YAJL later.
- */
-
-/* Allocate JSON reader instance */
-static ovs_json_reader_t *ovs_json_reader_alloc() {
- ovs_json_reader_t *jreader = calloc(1, sizeof(*jreader));
- if (jreader == NULL)
- return NULL;
-
- return jreader;
-}
-
-/* Push raw data into into the JSON reader for processing */
-static int ovs_json_reader_push_data(ovs_json_reader_t *jreader,
- const char *data, size_t data_len) {
- char *new_buff = NULL;
- size_t available = jreader->buff_size - jreader->buff_offset;
-
- /* check/update required memory space */
- if (available < data_len) {
- OVS_DEBUG("Reallocate buffer [size=%d, available=%d required=%d]",
- (int)jreader->buff_size, (int)available, (int)data_len);
-
- /* allocate new chunk of memory */
- new_buff = realloc(jreader->buff_ptr, (jreader->buff_size + data_len));
- if (new_buff == NULL)
- return -1;
-
- /* point to new allocated memory */
- jreader->buff_ptr = new_buff;
- jreader->buff_size += data_len;
- }
-
- /* store input data */
- memcpy(jreader->buff_ptr + jreader->buff_offset, data, data_len);
- jreader->buff_offset += data_len;
- return 0;
-}
-
-/* Pop one fully-fledged JSON if already exists. Returns 0 if
- * completed JSON already exists otherwise negative value is
- * returned */
-static int ovs_json_reader_pop(ovs_json_reader_t *jreader,
- const char **json_ptr, size_t *json_len_ptr) {
- size_t nbraces = 0;
- size_t json_len = 0;
- char *json = NULL;
-
- /* search open/close brace */
- for (size_t i = jreader->json_offset; i < jreader->buff_offset; i++) {
- if (jreader->buff_ptr[i] == '{') {
- nbraces++;
- } else if (jreader->buff_ptr[i] == '}')
- if (nbraces)
- if (!(--nbraces)) {
- /* JSON data */
- *json_ptr = jreader->buff_ptr + jreader->json_offset;
- *json_len_ptr = json_len + 1;
- jreader->json_offset = i + 1;
- return 0;
- }
-
- /* increase JSON data length */
- if (nbraces)
- json_len++;
- }
-
- if (jreader->json_offset) {
- if (jreader->json_offset < jreader->buff_offset) {
- /* shift data to the beginning of the buffer
- * and zero rest of the buffer data */
- json = &jreader->buff_ptr[jreader->json_offset];
- json_len = jreader->buff_offset - jreader->json_offset;
- for (size_t i = 0; i < jreader->buff_size; i++)
- jreader->buff_ptr[i] = ((i < json_len) ? (json[i]) : (0));
- jreader->buff_offset = json_len;
- } else
- /* reset the buffer */
- jreader->buff_offset = 0;
-
- /* data is at the beginning of the buffer */
- jreader->json_offset = 0;
- }
-
- return -1;
-}
-
-/* Reset JSON reader. It is useful when start processing
- * new raw data. E.g.: in case of lost stream connection.
- */
-static void ovs_json_reader_reset(ovs_json_reader_t *jreader) {
- if (jreader) {
- jreader->buff_offset = 0;
- jreader->json_offset = 0;
- }
-}
-
-/* Release internal data allocated for JSON reader */
-static void ovs_json_reader_free(ovs_json_reader_t *jreader) {
- if (jreader) {
- free(jreader->buff_ptr);
- free(jreader);
- }
-}
-
-/* Reconnect to OVS DB and call the OVS DB post connection init callback
- * if connection has been established.
- */
-static void ovs_db_reconnect(ovs_db_t *pdb) {
- const char *node_info = pdb->node;
- struct addrinfo *result;
-
- if (pdb->unix_path[0] != '\0') {
- /* use UNIX socket instead of INET address */
- node_info = pdb->unix_path;
-
- struct sockaddr_un *sa_unix = calloc(1, sizeof(*sa_unix));
- if (sa_unix == NULL)
- return;
-
- result = calloc(1, sizeof(*result));
- if (result == NULL) {
- free(sa_unix);
- return;
- }
-
- result->ai_family = AF_UNIX;
- result->ai_socktype = SOCK_STREAM;
- result->ai_addrlen = sizeof(*sa_unix);
- result->ai_addr = (struct sockaddr *)sa_unix;
- sa_unix->sun_family = result->ai_family;
- sstrncpy(sa_unix->sun_path, pdb->unix_path, sizeof(sa_unix->sun_path));
- } else {
- /* inet socket address */
- struct addrinfo hints;
-
- /* setup criteria for selecting the socket address */
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- /* get socket addresses */
- int ret = getaddrinfo(pdb->node, pdb->service, &hints, &result);
- if (ret != 0) {
- OVS_ERROR("getaddrinfo(): %s", gai_strerror(ret));
- return;
- }
- }
- /* try to connect to the server */
- for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) {
- int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (sock < 0) {
- OVS_DEBUG("socket(): %s", STRERRNO);
- continue;
- }
- if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) {
- close(sock);
- OVS_DEBUG("connect(): %s [family=%d]", STRERRNO, rp->ai_family);
- } else {
- /* send notification to event thread */
- pdb->sock = sock;
- ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_ESTABLISHED);
- break;
- }
- }
-
- if (pdb->sock < 0)
- OVS_ERROR("connect to \"%s\" failed", node_info);
-
- freeaddrinfo(result);
-}
-
-/* POLL worker thread.
- * It listens on OVS DB connection for incoming
- * requests/reply/events etc. Also, it reconnects to OVS DB
- * if connection has been lost.
- */
-static void *ovs_poll_worker(void *arg) {
- ovs_db_t *pdb = (ovs_db_t *)arg; /* pointer to OVS DB */
- ovs_json_reader_t *jreader = NULL;
- struct pollfd poll_fd = {
- .fd = pdb->sock, .events = POLLIN | POLLPRI, .revents = 0,
- };
-
- /* create JSON reader instance */
- if ((jreader = ovs_json_reader_alloc()) == NULL) {
- OVS_ERROR("initialize json reader failed");
- return NULL;
- }
-
- /* poll data */
- while (ovs_db_poll_is_running(pdb)) {
- poll_fd.fd = pdb->sock;
- int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000);
- if (poll_ret < 0) {
- OVS_ERROR("poll(): %s", STRERRNO);
- break;
- } else if (poll_ret == 0) {
- OVS_DEBUG("poll(): timeout");
- if (pdb->sock < 0)
- /* invalid fd, so try to reconnect */
- ovs_db_reconnect(pdb);
- continue;
- }
- if (poll_fd.revents & POLLNVAL) {
- /* invalid file descriptor, clean-up */
- ovs_db_callback_remove_all(pdb);
- ovs_json_reader_reset(jreader);
- /* setting poll FD to -1 tells poll() call to ignore this FD.
- * In that case poll() call will return timeout all the time */
- pdb->sock = (-1);
- } else if ((poll_fd.revents & POLLERR) || (poll_fd.revents & POLLHUP)) {
- /* connection is broken */
- close(poll_fd.fd);
- ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED);
- OVS_ERROR("poll() peer closed its end of the channel");
- } else if ((poll_fd.revents & POLLIN) || (poll_fd.revents & POLLPRI)) {
- /* read incoming data */
- char buff[OVS_DB_POLL_READ_BLOCK_SIZE];
- ssize_t nbytes = recv(poll_fd.fd, buff, sizeof(buff), 0);
- if (nbytes < 0) {
- OVS_ERROR("recv(): %s", STRERRNO);
- /* read error? Try to reconnect */
- close(poll_fd.fd);
- continue;
- } else if (nbytes == 0) {
- close(poll_fd.fd);
- ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED);
- OVS_ERROR("recv() peer has performed an orderly shutdown");
- continue;
- }
- /* read incoming data */
- size_t json_len = 0;
- const char *json = NULL;
- OVS_DEBUG("recv(): received %zd bytes of data", nbytes);
- ovs_json_reader_push_data(jreader, buff, nbytes);
- while (!ovs_json_reader_pop(jreader, &json, &json_len))
- /* process JSON data */
- ovs_db_json_data_process(pdb, json, json_len);
- }
- }
-
- OVS_DEBUG("poll thread has been completed");
- ovs_json_reader_free(jreader);
- return NULL;
-}
-
-/* EVENT worker thread.
- * Perform task based on incoming events. This
- * task can be done asynchronously which allows to
- * handle OVS DB callback like 'init_cb'.
- */
-static void *ovs_event_worker(void *arg) {
- ovs_db_t *pdb = (ovs_db_t *)arg;
-
- while (pdb->event_thread.value != OVS_DB_EVENT_TERMINATE) {
- /* wait for an event */
- struct timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
- ts.tv_sec += (OVS_DB_EVENT_TIMEOUT);
- int ret = pthread_cond_timedwait(&pdb->event_thread.cond,
- &pdb->event_thread.mutex, &ts);
- if (!ret || ret == ETIMEDOUT) {
- /* handle the event */
- OVS_DEBUG("handle event %d", pdb->event_thread.value);
- switch (pdb->event_thread.value) {
- case OVS_DB_EVENT_CONN_ESTABLISHED:
- if (pdb->cb.post_conn_init)
- pdb->cb.post_conn_init(pdb);
- /* reset event */
- pdb->event_thread.value = OVS_DB_EVENT_NONE;
- break;
- case OVS_DB_EVENT_CONN_TERMINATED:
- if (pdb->cb.post_conn_terminate)
- pdb->cb.post_conn_terminate();
- /* reset event */
- pdb->event_thread.value = OVS_DB_EVENT_NONE;
- break;
- case OVS_DB_EVENT_NONE:
- /* wait timeout */
- OVS_DEBUG("no event received (timeout)");
- break;
- default:
- OVS_DEBUG("unknown event received");
- break;
- }
- } else {
- /* unexpected error */
- OVS_ERROR("pthread_cond_timedwait() failed");
- break;
- }
- }
-
- OVS_DEBUG("event thread has been completed");
- return NULL;
-}
-
-/* Initialize EVENT thread */
-static int ovs_db_event_thread_init(ovs_db_t *pdb) {
- pdb->event_thread.tid = (pthread_t){0};
- /* init event thread condition variable */
- if (pthread_cond_init(&pdb->event_thread.cond, NULL)) {
- return -1;
- }
- /* init event thread mutex */
- if (pthread_mutex_init(&pdb->event_thread.mutex, NULL)) {
- pthread_cond_destroy(&pdb->event_thread.cond);
- return -1;
- }
- /* Hold the event thread mutex. It ensures that no events
- * will be lost while thread is still starting. Once event
- * thread is started and ready to accept events, it will release
- * the mutex */
- if (pthread_mutex_lock(&pdb->event_thread.mutex)) {
- pthread_mutex_destroy(&pdb->event_thread.mutex);
- pthread_cond_destroy(&pdb->event_thread.cond);
- return -1;
- }
- /* start event thread */
- pthread_t tid;
- if (plugin_thread_create(&tid, NULL, ovs_event_worker, pdb,
- "utils_ovs:event") != 0) {
- pthread_mutex_unlock(&pdb->event_thread.mutex);
- pthread_mutex_destroy(&pdb->event_thread.mutex);
- pthread_cond_destroy(&pdb->event_thread.cond);
- return -1;
- }
- pdb->event_thread.tid = tid;
- return 0;
-}
-
-/* Terminate EVENT thread */
-static int ovs_db_event_thread_terminate(ovs_db_t *pdb) {
- if (pthread_equal(pdb->event_thread.tid, (pthread_t){0})) {
- /* already terminated */
- return 0;
- }
- ovs_db_event_post(pdb, OVS_DB_EVENT_TERMINATE);
- if (pthread_join(pdb->event_thread.tid, NULL) != 0)
- return -1;
- /* Event thread always holds the thread mutex when
- * performs some task (handles event) and releases it when
- * while sleeping. Thus, if event thread exits, the mutex
- * remains locked */
- pdb->event_thread.tid = (pthread_t){0};
- pthread_mutex_unlock(&pdb->event_thread.mutex);
- return 0;
-}
-
-/* Destroy EVENT thread private data */
-static void ovs_db_event_thread_data_destroy(ovs_db_t *pdb) {
- /* destroy mutex */
- pthread_mutex_destroy(&pdb->event_thread.mutex);
- pthread_cond_destroy(&pdb->event_thread.cond);
-}
-
-/* Initialize POLL thread */
-static int ovs_db_poll_thread_init(ovs_db_t *pdb) {
- pdb->poll_thread.tid = (pthread_t){0};
- /* init event thread mutex */
- if (pthread_mutex_init(&pdb->poll_thread.mutex, NULL)) {
- return -1;
- }
- /* start poll thread */
- pthread_t tid;
- pdb->poll_thread.state = OVS_DB_POLL_STATE_RUNNING;
- if (plugin_thread_create(&tid, NULL, ovs_poll_worker, pdb,
- "utils_ovs:poll") != 0) {
- pthread_mutex_destroy(&pdb->poll_thread.mutex);
- return -1;
- }
- pdb->poll_thread.tid = tid;
- return 0;
-}
-
-/* Destroy POLL thread */
-/* XXX: Must hold pdb->mutex when calling! */
-static int ovs_db_poll_thread_destroy(ovs_db_t *pdb) {
- if (pthread_equal(pdb->poll_thread.tid, (pthread_t){0})) {
- /* already destroyed */
- return 0;
- }
- /* change thread state */
- pthread_mutex_lock(&pdb->poll_thread.mutex);
- pdb->poll_thread.state = OVS_DB_POLL_STATE_EXITING;
- pthread_mutex_unlock(&pdb->poll_thread.mutex);
- /* join the thread */
- if (pthread_join(pdb->poll_thread.tid, NULL) != 0)
- return -1;
- pthread_mutex_destroy(&pdb->poll_thread.mutex);
- pdb->poll_thread.tid = (pthread_t){0};
- return 0;
-}
-
-/*
- * Public OVS DB API implementation
- */
-
-ovs_db_t *ovs_db_init(const char *node, const char *service,
- const char *unix_path, ovs_db_callback_t *cb) {
- int ret;
-
- /* sanity check */
- if (node == NULL || service == NULL || unix_path == NULL)
- return NULL;
-
- /* allocate db data & fill it */
- ovs_db_t *pdb = calloc(1, sizeof(*pdb));
- if (pdb == NULL)
- return NULL;
- pdb->sock = -1;
-
- /* store the OVS DB address */
- sstrncpy(pdb->node, node, sizeof(pdb->node));
- sstrncpy(pdb->service, service, sizeof(pdb->service));
- sstrncpy(pdb->unix_path, unix_path, sizeof(pdb->unix_path));
-
- /* setup OVS DB callbacks */
- if (cb)
- pdb->cb = *cb;
-
- /* init OVS DB mutex attributes */
- pthread_mutexattr_t mutex_attr;
- if (pthread_mutexattr_init(&mutex_attr)) {
- OVS_ERROR("OVS DB mutex attribute init failed");
- sfree(pdb);
- return NULL;
- }
- /* set OVS DB mutex as recursive */
- if (pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE)) {
- OVS_ERROR("Failed to set OVS DB mutex as recursive");
- pthread_mutexattr_destroy(&mutex_attr);
- sfree(pdb);
- return NULL;
- }
- /* init OVS DB mutex */
- if (pthread_mutex_init(&pdb->mutex, &mutex_attr)) {
- OVS_ERROR("OVS DB mutex init failed");
- pthread_mutexattr_destroy(&mutex_attr);
- sfree(pdb);
- return NULL;
- }
- /* destroy mutex attributes */
- pthread_mutexattr_destroy(&mutex_attr);
-
- /* init event thread */
- if (ovs_db_event_thread_init(pdb) < 0) {
- ret = ovs_db_destroy(pdb);
- if (ret > 0)
- goto failure;
- else
- return NULL;
- }
-
- /* init polling thread */
- if (ovs_db_poll_thread_init(pdb) < 0) {
- ret = ovs_db_destroy(pdb);
- if (ret > 0) {
- ovs_db_event_thread_data_destroy(pdb);
- goto failure;
- } else {
- return NULL;
- }
- }
- return pdb;
-
-failure:
- pthread_mutex_destroy(&pdb->mutex);
- sfree(pdb);
- return NULL;
-}
-
-int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params,
- ovs_db_result_cb_t cb) {
- int ret = 0;
- yajl_gen_status yajl_gen_ret;
- yajl_val jparams;
- yajl_gen jgen;
- ovs_callback_t *new_cb = NULL;
- uint64_t uid;
- char uid_buff[OVS_UID_STR_SIZE];
- const char *req = NULL;
- size_t req_len = 0;
- struct timespec ts;
-
- /* sanity check */
- if (!pdb || !method || !params)
- return -1;
-
- if ((jgen = yajl_gen_alloc(NULL)) == NULL)
- return -1;
-
- /* try to parse params */
- if ((jparams = yajl_tree_parse(params, NULL, 0)) == NULL) {
- OVS_ERROR("params is not a JSON string");
- yajl_gen_clear(jgen);
- return -1;
- }
-
- /* generate method field */
- OVS_YAJL_CALL(yajl_gen_map_open, jgen);
-
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "method");
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, method);
-
- /* generate params field */
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "params");
- OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams);
- yajl_tree_free(jparams);
-
- /* generate id field */
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id");
- uid = ovs_uid_generate();
- snprintf(uid_buff, sizeof(uid_buff), "%" PRIX64, uid);
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_buff);
-
- OVS_YAJL_CALL(yajl_gen_map_close, jgen);
-
- if (cb) {
- /* register result callback */
- if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL)
- goto yajl_gen_failure;
-
- /* add new callback to front */
- sem_init(&new_cb->result.sync, 0, 0);
- new_cb->result.call = cb;
- new_cb->uid = uid;
- ovs_db_callback_add(pdb, new_cb);
- }
-
- /* send the request */
- OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&req, &req_len);
- OVS_DEBUG("%s", req);
- if (!ovs_db_data_send(pdb, req, req_len)) {
- if (cb) {
- /* wait for result */
- clock_gettime(CLOCK_REALTIME, &ts);
- ts.tv_sec += OVS_DB_SEND_REQ_TIMEOUT;
- if (sem_timedwait(&new_cb->result.sync, &ts) < 0) {
- OVS_ERROR("%s() no replay received within %d sec", __FUNCTION__,
- OVS_DB_SEND_REQ_TIMEOUT);
- ret = (-1);
- }
- }
- } else {
- OVS_ERROR("ovs_db_data_send() failed");
- ret = (-1);
- }
-
-yajl_gen_failure:
- if (new_cb) {
- /* destroy callback */
- sem_destroy(&new_cb->result.sync);
- ovs_db_callback_remove(pdb, new_cb);
- }
-
- /* release memory */
- yajl_gen_clear(jgen);
- return (yajl_gen_ret != yajl_gen_status_ok) ? (-1) : ret;
-}
-
-int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name,
- const char **tb_column,
- ovs_db_table_cb_t update_cb,
- ovs_db_result_cb_t result_cb, unsigned int flags) {
- yajl_gen jgen;
- yajl_gen_status yajl_gen_ret;
- ovs_callback_t *new_cb = NULL;
- char uid_str[OVS_UID_STR_SIZE];
- char *params;
- size_t params_len;
- int ovs_db_ret = 0;
-
- /* sanity check */
- if (pdb == NULL || tb_name == NULL || update_cb == NULL)
- return -1;
-
- /* allocate new update callback */
- if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL)
- return -1;
-
- /* init YAJL generator */
- if ((jgen = yajl_gen_alloc(NULL)) == NULL) {
- sfree(new_cb);
- return -1;
- }
-
- /* add new callback to front */
- new_cb->table.call = update_cb;
- new_cb->uid = ovs_uid_generate();
- ovs_db_callback_add(pdb, new_cb);
-
- /* make update notification request
- * [<db-name>, <json-value>, <monitor-requests>] */
- OVS_YAJL_CALL(yajl_gen_array_open, jgen);
- {
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME);
-
- /* uid string <json-value> */
- snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid);
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str);
-
- /* <monitor-requests> */
- OVS_YAJL_CALL(yajl_gen_map_open, jgen);
- {
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, tb_name);
- OVS_YAJL_CALL(yajl_gen_array_open, jgen);
- {
- /* <monitor-request> */
- OVS_YAJL_CALL(yajl_gen_map_open, jgen);
- {
- if (tb_column) {
- /* columns within the table to be monitored */
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "columns");
- OVS_YAJL_CALL(yajl_gen_array_open, jgen);
- for (; *tb_column; tb_column++)
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, *tb_column);
- OVS_YAJL_CALL(yajl_gen_array_close, jgen);
- }
- /* specify select option */
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "select");
- {
- OVS_YAJL_CALL(yajl_gen_map_open, jgen);
- {
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "initial");
- OVS_YAJL_CALL(yajl_gen_bool, jgen,
- flags & OVS_DB_TABLE_CB_FLAG_INITIAL);
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "insert");
- OVS_YAJL_CALL(yajl_gen_bool, jgen,
- flags & OVS_DB_TABLE_CB_FLAG_INSERT);
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "delete");
- OVS_YAJL_CALL(yajl_gen_bool, jgen,
- flags & OVS_DB_TABLE_CB_FLAG_DELETE);
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "modify");
- OVS_YAJL_CALL(yajl_gen_bool, jgen,
- flags & OVS_DB_TABLE_CB_FLAG_MODIFY);
- }
- OVS_YAJL_CALL(yajl_gen_map_close, jgen);
- }
- }
- OVS_YAJL_CALL(yajl_gen_map_close, jgen);
- }
- OVS_YAJL_CALL(yajl_gen_array_close, jgen);
- }
- OVS_YAJL_CALL(yajl_gen_map_close, jgen);
- }
- OVS_YAJL_CALL(yajl_gen_array_close, jgen);
-
- /* make a request to subscribe to given table */
- OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)¶ms,
- ¶ms_len);
- if (ovs_db_send_request(pdb, "monitor", params, result_cb) < 0) {
- OVS_ERROR("Failed to subscribe to \"%s\" table", tb_name);
- ovs_db_ret = (-1);
- }
-
-yajl_gen_failure:
- /* release memory */
- yajl_gen_clear(jgen);
- return ovs_db_ret;
-}
-
-int ovs_db_destroy(ovs_db_t *pdb) {
- int ovs_db_ret = 0;
- int ret = 0;
-
- /* sanity check */
- if (pdb == NULL)
- return -1;
-
- /* stop event thread */
- if (ovs_db_event_thread_terminate(pdb) < 0) {
- OVS_ERROR("stop event thread failed");
- ovs_db_ret = -1;
- }
-
- /* try to lock the structure before releasing */
- if ((ret = pthread_mutex_lock(&pdb->mutex))) {
- OVS_ERROR("pthread_mutex_lock() DB mutex lock failed (%d)", ret);
- return ret;
- }
-
- /* stop poll thread and destroy thread's private data */
- if (ovs_db_poll_thread_destroy(pdb) < 0) {
- OVS_ERROR("destroy poll thread failed");
- ovs_db_ret = -1;
- }
-
- /* destroy event thread private data */
- ovs_db_event_thread_data_destroy(pdb);
-
- pthread_mutex_unlock(&pdb->mutex);
-
- /* unsubscribe callbacks */
- ovs_db_callback_remove_all(pdb);
-
- /* close connection */
- if (pdb->sock >= 0)
- close(pdb->sock);
-
- /* release DB handler */
- pthread_mutex_destroy(&pdb->mutex);
- sfree(pdb);
- return ovs_db_ret;
-}
-
-/*
- * Public OVS utils API implementation
- */
-
-/* Get YAJL value by key from YAJL dictionary
- *
- * EXAMPLE:
- * {
- * "key_a" : <YAJL return value>
- * "key_b" : <YAJL return value>
- * }
- */
-yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key) {
- const char *obj_key = NULL;
-
- /* check params */
- if (!YAJL_IS_OBJECT(jval) || (key == NULL))
- return NULL;
-
- /* find a value by key */
- for (size_t i = 0; i < YAJL_GET_OBJECT(jval)->len; i++) {
- obj_key = YAJL_GET_OBJECT(jval)->keys[i];
- if (strcmp(obj_key, key) == 0)
- return YAJL_GET_OBJECT(jval)->values[i];
- }
-
- return NULL;
-}
-
-/* Get OVS DB map value by given map key
- *
- * FROM RFC7047:
- *
- * <pair>
- * A 2-element JSON array that represents a pair within a database
- * map. The first element is an <atom> that represents the key, and
- * the second element is an <atom> that represents the value.
- *
- * <map>
- * A 2-element JSON array that represents a database map value. The
- * first element of the array must be the string "map", and the
- * second element must be an array of zero or more <pair>s giving the
- * values in the map. All of the <pair>s must have the same key and
- * value types.
- *
- * EXAMPLE:
- * [
- * "map", [
- * [ "key_a", <YAJL value>], [ "key_b", <YAJL value>], ...
- * ]
- * ]
- */
-yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key) {
- size_t map_len = 0;
- size_t array_len = 0;
- yajl_val *map_values = NULL;
- yajl_val *array_values = NULL;
- const char *str_val = NULL;
-
- /* check YAJL array */
- if (!YAJL_IS_ARRAY(jval) || (key == NULL))
- return NULL;
-
- /* check a database map value (2-element, first one should be a string */
- array_len = YAJL_GET_ARRAY(jval)->len;
- array_values = YAJL_GET_ARRAY(jval)->values;
- if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0])) ||
- (!YAJL_IS_ARRAY(array_values[1])))
- return NULL;
-
- /* check first element of the array */
- str_val = YAJL_GET_STRING(array_values[0]);
- if (str_val == NULL || strcmp("map", str_val) != 0)
- return NULL;
-
- /* try to find map value by map key */
- if (YAJL_GET_ARRAY(array_values[1]) == NULL)
- return NULL;
-
- map_len = YAJL_GET_ARRAY(array_values[1])->len;
- map_values = YAJL_GET_ARRAY(array_values[1])->values;
- for (size_t i = 0; i < map_len; i++) {
- /* check YAJL array */
- if (!YAJL_IS_ARRAY(map_values[i]) || YAJL_GET_ARRAY(map_values[i]) == NULL)
- break;
-
- /* check a database pair value (2-element, first one represents a key
- * and it should be a string in our case */
- array_len = YAJL_GET_ARRAY(map_values[i])->len;
- array_values = YAJL_GET_ARRAY(map_values[i])->values;
- if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0])))
- break;
-
- /* return map value if given key equals map key */
- str_val = YAJL_GET_STRING(array_values[0]);
- if (str_val != NULL && strcmp(key, str_val) == 0)
- return array_values[1];
- }
- return NULL;
-}
+++ /dev/null
-/**
- * collectd - src/utils_ovs.h
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- * Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
- *
- * Description:
- * The OVS util module provides the following features:
- * - Implements the OVS DB communication transport specified
- * by RFC7047:
- * * Connect/disconnect to OVS DB;
- * * Recovery mechanism in case of OVS DB connection lost;
- * * Subscription mechanism to OVS DB table update events
- * (insert/modify/delete);
- * * Send custom JSON request to OVS DB (poll table data, etc.)
- * * Handling of echo request from OVS DB server to verify the
- * liveness of the connection.
- * - Provides YAJL helpers functions.
- *
- * OVS DB API User Guide:
- * All OVS DB function/structure names begins from 'ovs_db_*' prefix. To
- * start using OVS DB API, client (plugin) should initialize the OVS DB
- * object (`ovs_db_t') by calling `ovs_db_init' function. It initializes
- * internal data and creates two main workers (threads). The result of the
- * function is a pointer to new OVS DB object which can be used by other
- * OVS DB API later and must be released by `ovs_db_destroy' function if
- * the object isn't needed anymore.
- * Once OVS DB API is initialized, the `init_cb' callback is called if
- * the connection to OVS DB has been established. This callback is called
- * every time the OVS DB is reconnected. So, if the client registers table
- * update event callbacks or does any other OVS DB setup that can be lost
- * after OVS DB reconnecting, it should be done in `init_cb' callback.
- * The `ovs_db_table_cb_register` function is used to register OVS DB
- * table update event callback and receive the table update notification
- * when requested event occurs (registered callback is called). See
- * function API for more info.
- * To send custom JSON-RPC request to OVS DB, the `ovs_db_send_request'
- * function is used. Please note, that connection to OVS DB should be
- * established otherwise the function will return error.
- * To verify the liveness of established connection, the OVS DB server
- * sends echo request to the client with a given interval. The OVS utils
- * takes care about this request and handles it properly.
- **/
-
-#ifndef UTILS_OVS_H
-#define UTILS_OVS_H
-
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_tree.h>
-
-/* Forward declaration */
-typedef struct ovs_db_s ovs_db_t;
-
-/* OVS DB callback type declaration */
-typedef void (*ovs_db_table_cb_t)(yajl_val jupdates);
-typedef void (*ovs_db_result_cb_t)(yajl_val jresult, yajl_val jerror);
-
-/* OVS DB structures */
-struct ovs_db_callback_s {
- /*
- * This callback is called when OVS DB connection
- * has been established and ready to use. Client
- * can use this callback to configure OVS DB, e.g.
- * to subscribe to table update notification or poll
- * some OVS DB data. This field can be NULL.
- */
- void (*post_conn_init)(ovs_db_t *pdb);
- /*
- * This callback is called when OVS DB connection
- * has been lost. This field can be NULL.
- */
- void (*post_conn_terminate)(void);
-};
-typedef struct ovs_db_callback_s ovs_db_callback_t;
-
-/* OVS DB defines */
-#define OVS_DB_ADDR_NODE_SIZE 256
-#define OVS_DB_ADDR_SERVICE_SIZE 128
-#define OVS_DB_ADDR_UNIX_SIZE 108
-
-/* OVS DB prototypes */
-
-/*
- * NAME
- * ovs_db_init
- *
- * DESCRIPTION
- * Initialize OVS DB internal data. The `ovs_db_destroy' function
- * shall destroy the returned object.
- *
- * PARAMETERS
- * `node' OVS DB Address.
- * `service' OVS DB service name.
- * `unix' OVS DB unix socket path.
- * `cb' OVS DB callbacks.
- *
- * RETURN VALUE
- * New ovs_db_t object upon success or NULL if an error occurred.
- */
-ovs_db_t *ovs_db_init(const char *node, const char *service,
- const char *unix_path, ovs_db_callback_t *cb);
-
-/*
- * NAME
- * ovs_db_destroy
- *
- * DESCRIPTION
- * Destroy OVS DB object referenced by `pdb'.
- *
- * PARAMETERS
- * `pdb' Pointer to OVS DB object.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred.
- */
-int ovs_db_destroy(ovs_db_t *pdb);
-
-/*
- * NAME
- * ovs_db_send_request
- *
- * DESCRIPTION
- * Send JSON request to OVS DB server.
- *
- * PARAMETERS
- * `pdb' Pointer to OVS DB object.
- * `method' Request method name.
- * `params' Method params to be sent (JSON value as a string).
- * `cb' Result callback of the request. If NULL, the request
- * is sent asynchronously.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred.
- */
-int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params,
- ovs_db_result_cb_t cb);
-
-/* callback types */
-#define OVS_DB_TABLE_CB_FLAG_INITIAL 0x01U
-#define OVS_DB_TABLE_CB_FLAG_INSERT 0x02U
-#define OVS_DB_TABLE_CB_FLAG_DELETE 0x04U
-#define OVS_DB_TABLE_CB_FLAG_MODIFY 0x08U
-#define OVS_DB_TABLE_CB_FLAG_ALL 0x0FU
-
-/*
- * NAME
- * ovs_db_table_cb_register
- *
- * DESCRIPTION
- * Subscribe a callback on OVS DB table event. It allows to
- * receive notifications (`update_cb' callback is called) of
- * changes to requested table.
- *
- * PARAMETERS
- * `pdb' Pointer to OVS DB object.
- * `tb_name' OVS DB Table name to be monitored.
- * `tb_column' OVS DB Table columns to be monitored. Last
- * element in the array should be NULL.
- * `update_cb' Callback function that is called when
- * requested table columns are changed.
- * `cb' Result callback of the request. If NULL, the call
- * becomes asynchronous.
- * Useful, if OVS_DB_TABLE_CB_FLAG_INITIAL is set.
- * `flags' Bit mask of:
- * OVS_DB_TABLE_CB_FLAG_INITIAL Receive initial values in
- * result callback.
- * OVS_DB_TABLE_CB_FLAG_INSERT Receive table insert events.
- * OVS_DB_TABLE_CB_FLAG_DELETE Receive table remove events.
- * OVS_DB_TABLE_CB_FLAG_MODIFY Receive table update events.
- * OVS_DB_TABLE_CB_FLAG_ALL Receive all events.
- *
- * RETURN VALUE
- * Zero upon success or non-zero if an error occurred.
- */
-int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name,
- const char **tb_column,
- ovs_db_table_cb_t update_cb,
- ovs_db_result_cb_t result_cb, unsigned int flags);
-
-/*
- * OVS utils API
- */
-
-/*
- * NAME
- * ovs_utils_get_value_by_key
- *
- * DESCRIPTION
- * Get YAJL value by object name.
- *
- * PARAMETERS
- * `jval' YAJL object value.
- * `key' Object key name.
- *
- * RETURN VALUE
- * YAJL value upon success or NULL if key not found.
- */
-yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key);
-
-/*
- * NAME
- * ovs_utils_get_map_value
- *
- * DESCRIPTION
- * Get OVS DB map value by given map key (rfc7047, "Notation" section).
- *
- * PARAMETERS
- * `jval' A 2-element YAJL array that represents a OVS DB map value.
- * `key' OVS DB map key name.
- *
- * RETURN VALUE
- * YAJL value upon success or NULL if key not found.
- */
-yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key);
-
-#endif
+++ /dev/null
-/**
- * collectd - src/utils_parse_option.c
- * Copyright (C) 2008 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "utils_parse_option.h"
-
-int parse_string(char **ret_buffer, char **ret_string) {
- char *buffer;
- char *string;
-
- buffer = *ret_buffer;
-
- /* Eat up leading spaces. */
- string = buffer;
- while (isspace((int)*string))
- string++;
- if (*string == 0)
- return 1;
-
- /* A quoted string */
- if (*string == '"') {
- char *dst;
-
- string++;
- if (*string == 0)
- return 1;
-
- dst = string;
- buffer = string;
- while ((*buffer != '"') && (*buffer != 0)) {
- /* Un-escape backslashes */
- if (*buffer == '\\') {
- buffer++;
- /* Catch a backslash at the end of buffer */
- if (*buffer == 0)
- return -1;
- }
- *dst = *buffer;
- buffer++;
- dst++;
- }
- /* No quote sign has been found */
- if (*buffer == 0)
- return -1;
-
- *dst = 0;
- dst++;
- *buffer = 0;
- buffer++;
-
- /* Check for trailing spaces. */
- if ((*buffer != 0) && !isspace((int)*buffer))
- return -1;
- } else /* an unquoted string */
- {
- buffer = string;
- while ((*buffer != 0) && !isspace((int)*buffer))
- buffer++;
- if (*buffer != 0) {
- *buffer = 0;
- buffer++;
- }
- }
-
- /* Eat up trailing spaces */
- while (isspace((int)*buffer))
- buffer++;
-
- *ret_buffer = buffer;
- *ret_string = string;
-
- return 0;
-} /* int parse_string */
-
-/*
- * parse_option
- * ------------
- * Parses an ``option'' as used with the unixsock and exec commands. An
- * option is of the form:
- * name0="value"
- * name1="value with \"quotes\""
- * name2="value \\ backslash"
- * However, if the value does *not* contain a space character, you can skip
- * the quotes.
- */
-int parse_option(char **ret_buffer, char **ret_key, char **ret_value) {
- char *buffer;
- char *key;
- char *value;
- int status;
-
- buffer = *ret_buffer;
-
- /* Eat up leading spaces */
- key = buffer;
- while (isspace((int)*key))
- key++;
- if (*key == 0)
- return 1;
-
- /* Look for the equal sign */
- buffer = key;
- while (isalnum((int)*buffer) || *buffer == '_' || *buffer == ':')
- buffer++;
- if ((*buffer != '=') || (buffer == key))
- return 1;
- *buffer = 0;
- buffer++;
- /* Empty values must be written as "" */
- if (isspace((int)*buffer) || (*buffer == 0))
- return -1;
-
- status = parse_string(&buffer, &value);
- if (status != 0)
- return -1;
-
- /* NB: parse_string will have eaten up all trailing spaces. */
-
- *ret_buffer = buffer;
- *ret_key = key;
- *ret_value = value;
-
- return 0;
-} /* int parse_option */
+++ /dev/null
-/**
- * collectd - src/utils_parse_option.h
- * Copyright (C) 2008 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_PARSE_OPTION
-#define UTILS_PARSE_OPTION 1
-
-int parse_string(char **ret_buffer, char **ret_string);
-int parse_option(char **ret_buffer, char **ret_key, char **ret_value);
-
-#endif /* UTILS_PARSE_OPTION */
+++ /dev/null
-/**
- * collectd - src/utils_rrdcreate.c
- * Copyright (C) 2006-2013 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "utils_rrdcreate.h"
-
-#include <pthread.h>
-#include <rrd.h>
-
-struct srrd_create_args_s {
- char *filename;
- unsigned long pdp_step;
- time_t last_up;
- int argc;
- char **argv;
-};
-typedef struct srrd_create_args_s srrd_create_args_t;
-
-struct async_create_file_s;
-typedef struct async_create_file_s async_create_file_t;
-struct async_create_file_s {
- char *filename;
- async_create_file_t *next;
-};
-
-/*
- * Private variables
- */
-static int rra_timespans[] = {3600, 86400, 604800, 2678400, 31622400};
-static int rra_timespans_num = STATIC_ARRAY_SIZE(rra_timespans);
-
-static const char *const rra_types[] = {"AVERAGE", "MIN", "MAX"};
-static int rra_types_num = STATIC_ARRAY_SIZE(rra_types);
-
-#if !defined(HAVE_THREADSAFE_LIBRRD)
-static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-static async_create_file_t *async_creation_list;
-static pthread_mutex_t async_creation_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/*
- * Private functions
- */
-static void rra_free(int rra_num, char **rra_def) /* {{{ */
-{
- for (int i = 0; i < rra_num; i++) {
- sfree(rra_def[i]);
- }
- sfree(rra_def);
-} /* }}} void rra_free */
-
-static void srrd_create_args_destroy(srrd_create_args_t *args) {
- if (args == NULL)
- return;
-
- sfree(args->filename);
- if (args->argv != NULL) {
- for (int i = 0; i < args->argc; i++)
- sfree(args->argv[i]);
- sfree(args->argv);
- }
- sfree(args);
-} /* void srrd_create_args_destroy */
-
-static srrd_create_args_t *srrd_create_args_create(const char *filename,
- unsigned long pdp_step,
- time_t last_up, int argc,
- const char **argv) {
- srrd_create_args_t *args;
-
- args = calloc(1, sizeof(*args));
- if (args == NULL) {
- P_ERROR("srrd_create_args_create: calloc failed.");
- return NULL;
- }
- args->filename = NULL;
- args->pdp_step = pdp_step;
- args->last_up = last_up;
- args->argv = NULL;
-
- args->filename = strdup(filename);
- if (args->filename == NULL) {
- P_ERROR("srrd_create_args_create: strdup failed.");
- srrd_create_args_destroy(args);
- return NULL;
- }
-
- args->argv = calloc(argc + 1, sizeof(*args->argv));
- if (args->argv == NULL) {
- P_ERROR("srrd_create_args_create: calloc failed.");
- srrd_create_args_destroy(args);
- return NULL;
- }
-
- for (args->argc = 0; args->argc < argc; args->argc++) {
- args->argv[args->argc] = strdup(argv[args->argc]);
- if (args->argv[args->argc] == NULL) {
- P_ERROR("srrd_create_args_create: strdup failed.");
- srrd_create_args_destroy(args);
- return NULL;
- }
- }
- assert(args->argc == argc);
- args->argv[args->argc] = NULL;
-
- return args;
-} /* srrd_create_args_t *srrd_create_args_create */
-
-/* * * * * * * * * *
- * WARNING: Magic *
- * * * * * * * * * */
-static int rra_get(char ***ret, const value_list_t *vl, /* {{{ */
- const rrdcreate_config_t *cfg) {
- char **rra_def;
- int rra_num;
-
- int *rts;
- int rts_num;
-
- int rra_max;
-
- int cdp_num;
- int cdp_len;
-
- /* The stepsize we use here: If it is user-set, use it. If not, use the
- * interval of the value-list. */
- int ss;
-
- if (cfg->rrarows <= 0) {
- *ret = NULL;
- return -1;
- }
-
- if ((cfg->xff < 0) || (cfg->xff >= 1.0)) {
- *ret = NULL;
- return -1;
- }
-
- if (cfg->stepsize > 0)
- ss = cfg->stepsize;
- else
- ss = (int)CDTIME_T_TO_TIME_T(vl->interval);
- if (ss <= 0) {
- *ret = NULL;
- return -1;
- }
-
- /* Use the configured timespans or fall back to the built-in defaults */
- if (cfg->timespans_num != 0) {
- rts = cfg->timespans;
- rts_num = cfg->timespans_num;
- } else {
- rts = rra_timespans;
- rts_num = rra_timespans_num;
- }
-
- rra_max = rts_num * rra_types_num;
- assert(rra_max > 0);
-
- if ((rra_def = calloc(rra_max + 1, sizeof(*rra_def))) == NULL)
- return -1;
- rra_num = 0;
-
- cdp_len = 0;
- for (int i = 0; i < rts_num; i++) {
- int span = rts[i];
-
- if ((span / ss) < cfg->rrarows)
- span = ss * cfg->rrarows;
-
- if (cdp_len == 0)
- cdp_len = 1;
- else
- cdp_len = (int)floor(((double)span) / ((double)(cfg->rrarows * ss)));
-
- cdp_num = (int)ceil(((double)span) / ((double)(cdp_len * ss)));
-
- for (int j = 0; j < rra_types_num; j++) {
- char buffer[128];
- int status;
-
- if (rra_num >= rra_max)
- break;
-
- status = snprintf(buffer, sizeof(buffer), "RRA:%s:%.10f:%u:%u",
- rra_types[j], cfg->xff, cdp_len, cdp_num);
-
- if ((status < 0) || ((size_t)status >= sizeof(buffer))) {
- P_ERROR("rra_get: Buffer would have been truncated.");
- continue;
- }
-
- rra_def[rra_num++] = sstrdup(buffer);
- }
- }
-
- if (rra_num <= 0) {
- sfree(rra_def);
- return 0;
- }
-
- *ret = rra_def;
- return rra_num;
-} /* }}} int rra_get */
-
-static void ds_free(int ds_num, char **ds_def) /* {{{ */
-{
- for (int i = 0; i < ds_num; i++)
- if (ds_def[i] != NULL)
- free(ds_def[i]);
- free(ds_def);
-} /* }}} void ds_free */
-
-static int ds_get(char ***ret, /* {{{ */
- const data_set_t *ds, const value_list_t *vl,
- const rrdcreate_config_t *cfg) {
- char **ds_def;
- size_t ds_num;
-
- char min[32];
- char max[32];
- char buffer[128];
-
- assert(ds->ds_num > 0);
-
- ds_def = calloc(ds->ds_num, sizeof(*ds_def));
- if (ds_def == NULL) {
- P_ERROR("ds_get: calloc failed: %s", STRERRNO);
- return -1;
- }
-
- for (ds_num = 0; ds_num < ds->ds_num; ds_num++) {
- data_source_t *d = ds->ds + ds_num;
- const char *type;
- int status;
-
- ds_def[ds_num] = NULL;
-
- if (d->type == DS_TYPE_COUNTER)
- type = "COUNTER";
- else if (d->type == DS_TYPE_GAUGE)
- type = "GAUGE";
- else if (d->type == DS_TYPE_DERIVE)
- type = "DERIVE";
- else if (d->type == DS_TYPE_ABSOLUTE)
- type = "ABSOLUTE";
- else {
- P_ERROR("ds_get: Unknown DS type: %i", d->type);
- break;
- }
-
- if (isnan(d->min)) {
- sstrncpy(min, "U", sizeof(min));
- } else
- snprintf(min, sizeof(min), "%f", d->min);
-
- if (isnan(d->max)) {
- sstrncpy(max, "U", sizeof(max));
- } else
- snprintf(max, sizeof(max), "%f", d->max);
-
- status = snprintf(
- buffer, sizeof(buffer), "DS:%s:%s:%i:%s:%s", d->name, type,
- (cfg->heartbeat > 0) ? cfg->heartbeat
- : (int)CDTIME_T_TO_TIME_T(2 * vl->interval),
- min, max);
- if ((status < 1) || ((size_t)status >= sizeof(buffer)))
- break;
-
- ds_def[ds_num] = sstrdup(buffer);
- } /* for ds_num = 0 .. ds->ds_num */
-
- if (ds_num != ds->ds_num) {
- ds_free(ds_num, ds_def);
- return -1;
- }
-
- if (ds_num == 0) {
- sfree(ds_def);
- return 0;
- }
-
- *ret = ds_def;
- return ds_num;
-} /* }}} int ds_get */
-
-#if HAVE_THREADSAFE_LIBRRD
-static int srrd_create(const char *filename, /* {{{ */
- unsigned long pdp_step, time_t last_up, int argc,
- const char **argv) {
- int status;
- char *filename_copy;
-
- if ((filename == NULL) || (argv == NULL))
- return -EINVAL;
-
- /* Some versions of librrd don't have the `const' qualifier for the first
- * argument, so we have to copy the pointer here to avoid warnings. It sucks,
- * but what else can we do? :( -octo */
- filename_copy = strdup(filename);
- if (filename_copy == NULL) {
- ERROR("srrd_create: strdup failed.");
- return -ENOMEM;
- }
-
- optind = 0; /* bug in librrd? */
- rrd_clear_error();
-
- status = rrd_create_r(filename_copy, pdp_step, last_up, argc, (void *)argv);
-
- if (status != 0) {
- P_WARNING("srrd_create: rrd_create_r (%s) failed: %s", filename,
- rrd_get_error());
- }
-
- sfree(filename_copy);
-
- return status;
-} /* }}} int srrd_create */
-/* #endif HAVE_THREADSAFE_LIBRRD */
-
-#else /* !HAVE_THREADSAFE_LIBRRD */
-static int srrd_create(const char *filename, /* {{{ */
- unsigned long pdp_step, time_t last_up, int argc,
- const char **argv) {
- int status;
-
- int new_argc;
- char **new_argv;
-
- char pdp_step_str[16];
- char last_up_str[16];
-
- new_argc = 6 + argc;
- new_argv = malloc((new_argc + 1) * sizeof(*new_argv));
- if (new_argv == NULL) {
- P_ERROR("srrd_create: malloc failed.");
- return -1;
- }
-
- if (last_up == 0)
- last_up = time(NULL) - 10;
-
- snprintf(pdp_step_str, sizeof(pdp_step_str), "%lu", pdp_step);
- snprintf(last_up_str, sizeof(last_up_str), "%lu", (unsigned long)last_up);
-
- new_argv[0] = "create";
- new_argv[1] = (void *)filename;
- new_argv[2] = "-s";
- new_argv[3] = pdp_step_str;
- new_argv[4] = "-b";
- new_argv[5] = last_up_str;
-
- memcpy(new_argv + 6, argv, argc * sizeof(char *));
- new_argv[new_argc] = NULL;
-
- pthread_mutex_lock(&librrd_lock);
- optind = 0; /* bug in librrd? */
- rrd_clear_error();
-
- status = rrd_create(new_argc, new_argv);
- pthread_mutex_unlock(&librrd_lock);
-
- if (status != 0) {
- P_WARNING("srrd_create: rrd_create (%s) failed: %s", filename,
- rrd_get_error());
- }
-
- sfree(new_argv);
-
- return status;
-} /* }}} int srrd_create */
-#endif /* !HAVE_THREADSAFE_LIBRRD */
-
-static int lock_file(char const *filename) /* {{{ */
-{
- async_create_file_t *ptr;
- struct stat sb;
- int status;
-
- pthread_mutex_lock(&async_creation_lock);
-
- for (ptr = async_creation_list; ptr != NULL; ptr = ptr->next)
- if (strcmp(filename, ptr->filename) == 0)
- break;
-
- if (ptr != NULL) {
- pthread_mutex_unlock(&async_creation_lock);
- return EEXIST;
- }
-
- status = stat(filename, &sb);
- if ((status == 0) || (errno != ENOENT)) {
- pthread_mutex_unlock(&async_creation_lock);
- return EEXIST;
- }
-
- ptr = malloc(sizeof(*ptr));
- if (ptr == NULL) {
- pthread_mutex_unlock(&async_creation_lock);
- return ENOMEM;
- }
-
- ptr->filename = strdup(filename);
- if (ptr->filename == NULL) {
- pthread_mutex_unlock(&async_creation_lock);
- sfree(ptr);
- return ENOMEM;
- }
-
- ptr->next = async_creation_list;
- async_creation_list = ptr;
-
- pthread_mutex_unlock(&async_creation_lock);
-
- return 0;
-} /* }}} int lock_file */
-
-static int unlock_file(char const *filename) /* {{{ */
-{
- async_create_file_t *this;
- async_create_file_t *prev;
-
- pthread_mutex_lock(&async_creation_lock);
-
- prev = NULL;
- for (this = async_creation_list; this != NULL; this = this->next) {
- if (strcmp(filename, this->filename) == 0)
- break;
- prev = this;
- }
-
- if (this == NULL) {
- pthread_mutex_unlock(&async_creation_lock);
- return ENOENT;
- }
-
- if (prev == NULL) {
- assert(this == async_creation_list);
- async_creation_list = this->next;
- } else {
- assert(this == prev->next);
- prev->next = this->next;
- }
- this->next = NULL;
-
- pthread_mutex_unlock(&async_creation_lock);
-
- sfree(this->filename);
- sfree(this);
-
- return 0;
-} /* }}} int unlock_file */
-
-static void *srrd_create_thread(void *targs) /* {{{ */
-{
- srrd_create_args_t *args = targs;
- char tmpfile[PATH_MAX];
- int status;
-
- status = lock_file(args->filename);
- if (status != 0) {
- if (status == EEXIST)
- P_NOTICE("srrd_create_thread: File \"%s\" is already being created.",
- args->filename);
- else
- P_ERROR("srrd_create_thread: Unable to lock file \"%s\".",
- args->filename);
- srrd_create_args_destroy(args);
- return 0;
- }
-
- snprintf(tmpfile, sizeof(tmpfile), "%s.async", args->filename);
-
- status = srrd_create(tmpfile, args->pdp_step, args->last_up, args->argc,
- (void *)args->argv);
- if (status != 0) {
- P_WARNING("srrd_create_thread: srrd_create (%s) returned status %i.",
- args->filename, status);
- unlink(tmpfile);
- unlock_file(args->filename);
- srrd_create_args_destroy(args);
- return 0;
- }
-
- status = rename(tmpfile, args->filename);
- if (status != 0) {
- P_ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile,
- args->filename, STRERRNO);
- unlink(tmpfile);
- unlock_file(args->filename);
- srrd_create_args_destroy(args);
- return 0;
- }
-
- DEBUG("srrd_create_thread: Successfully created RRD file \"%s\".",
- args->filename);
-
- unlock_file(args->filename);
- srrd_create_args_destroy(args);
-
- return 0;
-} /* }}} void *srrd_create_thread */
-
-static int srrd_create_async(const char *filename, /* {{{ */
- unsigned long pdp_step, time_t last_up, int argc,
- const char **argv) {
- srrd_create_args_t *args;
- pthread_t thread;
- pthread_attr_t attr;
- int status;
-
- DEBUG("srrd_create_async: Creating \"%s\" in the background.", filename);
-
- args = srrd_create_args_create(filename, pdp_step, last_up, argc, argv);
- if (args == NULL)
- return -1;
-
- status = pthread_attr_init(&attr);
- if (status != 0) {
- srrd_create_args_destroy(args);
- return -1;
- }
-
- status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- if (status != 0) {
- pthread_attr_destroy(&attr);
- srrd_create_args_destroy(args);
- return -1;
- }
-
- status = pthread_create(&thread, &attr, srrd_create_thread, args);
- if (status != 0) {
- P_ERROR("srrd_create_async: pthread_create failed: %s", STRERROR(status));
- pthread_attr_destroy(&attr);
- srrd_create_args_destroy(args);
- return status;
- }
-
- pthread_attr_destroy(&attr);
- /* args is freed in srrd_create_thread(). */
- return 0;
-} /* }}} int srrd_create_async */
-
-/*
- * Public functions
- */
-int cu_rrd_create_file(const char *filename, /* {{{ */
- const data_set_t *ds, const value_list_t *vl,
- const rrdcreate_config_t *cfg) {
- char **argv;
- int argc;
- char **rra_def = NULL;
- int rra_num;
- char **ds_def = NULL;
- int ds_num;
- int status = 0;
- time_t last_up;
- unsigned long stepsize;
-
- if (check_create_dir(filename))
- return -1;
-
- if ((rra_num = rra_get(&rra_def, vl, cfg)) < 1) {
- P_ERROR("cu_rrd_create_file failed: Could not calculate RRAs");
- return -1;
- }
-
- if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) {
- P_ERROR("cu_rrd_create_file failed: Could not calculate DSes");
- rra_free(rra_num, rra_def);
- return -1;
- }
-
- argc = ds_num + rra_num;
-
- if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) {
- P_ERROR("cu_rrd_create_file failed: %s", STRERRNO);
- rra_free(rra_num, rra_def);
- ds_free(ds_num, ds_def);
- return -1;
- }
-
- memcpy(argv, ds_def, ds_num * sizeof(char *));
- memcpy(argv + ds_num, rra_def, rra_num * sizeof(char *));
- argv[ds_num + rra_num] = NULL;
-
- last_up = CDTIME_T_TO_TIME_T(vl->time);
- if (last_up <= 0)
- last_up = time(NULL);
- last_up -= 1;
-
- if (cfg->stepsize > 0)
- stepsize = cfg->stepsize;
- else
- stepsize = (unsigned long)CDTIME_T_TO_TIME_T(vl->interval);
-
- if (cfg->async) {
- status = srrd_create_async(filename, stepsize, last_up, argc,
- (const char **)argv);
- if (status != 0)
- P_WARNING("cu_rrd_create_file: srrd_create_async (%s) "
- "returned status %i.",
- filename, status);
- } else /* synchronous */
- {
- status = lock_file(filename);
- if (status != 0) {
- if (status == EEXIST)
- P_NOTICE("cu_rrd_create_file: File \"%s\" is already being created.",
- filename);
- else
- P_ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename);
- } else {
- status =
- srrd_create(filename, stepsize, last_up, argc, (const char **)argv);
-
- if (status != 0) {
- P_WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.",
- filename, status);
- } else {
- DEBUG("cu_rrd_create_file: Successfully created RRD file \"%s\".",
- filename);
- }
- unlock_file(filename);
- }
- }
-
- free(argv);
- ds_free(ds_num, ds_def);
- rra_free(rra_num, rra_def);
-
- return status;
-} /* }}} int cu_rrd_create_file */
+++ /dev/null
-/**
- * collectd - src/utils_rrdcreate.h
- * Copyright (C) 2008-2013 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_RRDCREATE_H
-#define UTILS_RRDCREATE_H 1
-
-#include "plugin.h"
-
-#include <stddef.h>
-
-struct rrdcreate_config_s {
- unsigned long stepsize;
- int heartbeat;
- int rrarows;
- double xff;
-
- int *timespans;
- size_t timespans_num;
-
- char **consolidation_functions;
- size_t consolidation_functions_num;
-
- bool async;
-};
-typedef struct rrdcreate_config_s rrdcreate_config_t;
-
-int cu_rrd_create_file(const char *filename, const data_set_t *ds,
- const value_list_t *vl, const rrdcreate_config_t *cfg);
-
-#endif /* UTILS_RRDCREATE_H */
+++ /dev/null
-/**
- * collectd - src/utils_tail.c
- * Copyright (C) 2007-2008 C-Ware, Inc.
- * Copyright (C) 2008 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author:
- * Luke Heberling <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * Description:
- * Encapsulates useful code for plugins which must watch for appends to
- * the end of a file.
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "utils_tail.h"
-
-struct cu_tail_s {
- char *file;
- FILE *fh;
- struct stat stat;
-};
-
-static int cu_tail_reopen(cu_tail_t *obj) {
- int seek_end = 0;
- struct stat stat_buf = {0};
-
- int status = stat(obj->file, &stat_buf);
- if (status != 0) {
- P_ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO);
- return -1;
- }
-
- /* The file is already open.. */
- if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) {
- /* Seek to the beginning if file was truncated */
- if (stat_buf.st_size < obj->stat.st_size) {
- P_INFO("utils_tail: File `%s' was truncated.", obj->file);
- status = fseek(obj->fh, 0, SEEK_SET);
- if (status != 0) {
- P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
- fclose(obj->fh);
- obj->fh = NULL;
- return -1;
- }
- }
- memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
- return 1;
- }
-
- /* Seek to the end if we re-open the same file again or the file opened
- * is the first at all or the first after an error */
- if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
- seek_end = 1;
-
- FILE *fh = fopen(obj->file, "r");
- if (fh == NULL) {
- P_ERROR("utils_tail: fopen (%s) failed: %s", obj->file, STRERRNO);
- return -1;
- }
-
- if (seek_end != 0) {
- status = fseek(fh, 0, SEEK_END);
- if (status != 0) {
- P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
- fclose(fh);
- return -1;
- }
- }
-
- if (obj->fh != NULL)
- fclose(obj->fh);
- obj->fh = fh;
- memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
-
- return 0;
-} /* int cu_tail_reopen */
-
-cu_tail_t *cu_tail_create(const char *file) {
- cu_tail_t *obj;
-
- obj = calloc(1, sizeof(*obj));
- if (obj == NULL)
- return NULL;
-
- obj->file = strdup(file);
- if (obj->file == NULL) {
- free(obj);
- return NULL;
- }
-
- obj->fh = NULL;
-
- return obj;
-} /* cu_tail_t *cu_tail_create */
-
-int cu_tail_destroy(cu_tail_t *obj) {
- if (obj->fh != NULL)
- fclose(obj->fh);
- free(obj->file);
- free(obj);
-
- return 0;
-} /* int cu_tail_destroy */
-
-int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) {
- int status;
-
- if (buflen < 1) {
- ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen);
- return -1;
- }
-
- if (obj->fh == NULL) {
- status = cu_tail_reopen(obj);
- if (status < 0)
- return status;
- }
- assert(obj->fh != NULL);
-
- /* Try to read from the filehandle. If that succeeds, everything appears to
- * be fine and we can return. */
- clearerr(obj->fh);
- if (fgets(buf, buflen, obj->fh) != NULL) {
- buf[buflen - 1] = '\0';
- return 0;
- }
-
- /* Check if we encountered an error */
- if (ferror(obj->fh) != 0) {
- /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
- fclose(obj->fh);
- obj->fh = NULL;
- }
- /* else: eof -> check if the file was moved away and reopen the new file if
- * so.. */
-
- status = cu_tail_reopen(obj);
- /* error -> return with error */
- if (status < 0)
- return status;
- /* file end reached and file not reopened -> nothing more to read */
- else if (status > 0) {
- buf[0] = 0;
- return 0;
- }
-
- /* If we get here: file was re-opened and there may be more to read.. Let's
- * try again. */
- if (fgets(buf, buflen, obj->fh) != NULL) {
- buf[buflen - 1] = '\0';
- return 0;
- }
-
- if (ferror(obj->fh) != 0) {
- WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file,
- STRERRNO);
- fclose(obj->fh);
- obj->fh = NULL;
- return -1;
- }
-
- /* EOf, well, apparently the new file is empty.. */
- buf[0] = 0;
- return 0;
-} /* int cu_tail_readline */
-
-int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
- void *data) {
- int status;
-
- while (42) {
- size_t len;
-
- status = cu_tail_readline(obj, buf, buflen);
- if (status != 0) {
- ERROR("utils_tail: cu_tail_read: cu_tail_readline "
- "failed.");
- break;
- }
-
- /* check for EOF */
- if (buf[0] == 0)
- break;
-
- len = strlen(buf);
- while (len > 0) {
- if (buf[len - 1] != '\n')
- break;
- buf[len - 1] = '\0';
- len--;
- }
-
- status = callback(data, buf, buflen);
- if (status != 0) {
- ERROR("utils_tail: cu_tail_read: callback returned "
- "status %i.",
- status);
- break;
- }
- }
-
- return status;
-} /* int cu_tail_read */
+++ /dev/null
-/**
- * collectd - src/utils_tail.h
- * Copyright (C) 2007-2008 C-Ware, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author:
- * Luke Heberling <lukeh at c-ware.com>
- *
- * DESCRIPTION
- * Facilitates reading information that is appended to a file, taking into
- * account that the file may be rotated and a new file created under the
- * same name.
- **/
-
-#ifndef UTILS_TAIL_H
-#define UTILS_TAIL_H 1
-
-struct cu_tail_s;
-typedef struct cu_tail_s cu_tail_t;
-
-typedef int tailfunc_t(void *data, char *buf, int buflen);
-
-/*
- * NAME
- * cu_tail_create
- *
- * DESCRIPTION
- * Allocates a new tail object..
- *
- * PARAMETERS
- * `file' The name of the file to be tailed.
- */
-cu_tail_t *cu_tail_create(const char *file);
-
-/*
- * cu_tail_destroy
- *
- * Takes a tail object returned by `cu_tail_create' and destroys it, freeing
- * all internal memory.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_destroy(cu_tail_t *obj);
-
-/*
- * cu_tail_readline
- *
- * Reads from the file until `buflen' characters are read, a newline
- * character is read, or an eof condition is encountered. `buf' is
- * always null-terminated on successful return and isn't touched when non-zero
- * is returned.
- *
- * You can check if the EOF condition is reached by looking at the buffer: If
- * the length of the string stored in the buffer is zero, EOF occurred.
- * Otherwise at least the newline character will be in the buffer.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen);
-
-/*
- * cu_tail_readline
- *
- * Reads from the file until eof condition or an error is encountered.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
- void *data);
-
-#endif /* UTILS_TAIL_H */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_latency_config.h"
-#include "utils_match.h"
-#include "utils_tail.h"
+#include "utils/common/common.h"
+#include "utils/latency/latency_config.h"
+#include "utils/match/match.h"
+#include "utils/tail/tail.h"
#include "utils_tail_match.h"
struct cu_tail_match_simple_s {
* regular expressions.
*/
-#include "utils_latency_config.h"
-#include "utils_match.h"
+#include "utils/latency/latency_config.h"
+#include "utils/match/match.h"
struct cu_tail_match_s;
typedef struct cu_tail_match_s cu_tail_match_t;
+++ /dev/null
-/**
- * collectd - src/utils_taskstats.c
- * Copyright (C) 2017 Florian octo Forster
- *
- * ISC License (ISC)
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#include "collectd.h"
-#include "utils_taskstats.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_time.h"
-
-#include <libmnl/libmnl.h>
-#include <linux/genetlink.h>
-#include <linux/taskstats.h>
-
-struct ts_s {
- struct mnl_socket *nl;
- pid_t pid;
- uint32_t seq;
- uint16_t genl_id_taskstats;
- unsigned int port_id;
-};
-
-/* nlmsg_errno returns the errno encoded in nlh or zero if not an error. */
-static int nlmsg_errno(struct nlmsghdr *nlh, size_t sz) {
- if (!mnl_nlmsg_ok(nlh, (int)sz)) {
- ERROR("utils_taskstats: mnl_nlmsg_ok failed.");
- return EPROTO;
- }
-
- if (nlh->nlmsg_type != NLMSG_ERROR) {
- return 0;
- }
-
- struct nlmsgerr *nlerr = mnl_nlmsg_get_payload(nlh);
- /* (struct nlmsgerr).error holds a negative errno. */
- return nlerr->error * (-1);
-}
-
-static int get_taskstats_attr_cb(const struct nlattr *attr, void *data) {
- struct taskstats *ret_taskstats = data;
-
- uint16_t type = mnl_attr_get_type(attr);
- switch (type) {
- case TASKSTATS_TYPE_STATS:
- if (mnl_attr_get_payload_len(attr) != sizeof(*ret_taskstats)) {
- ERROR("utils_taskstats: mnl_attr_get_payload_len(attr) = %" PRIu32
- ", want %zu",
- mnl_attr_get_payload_len(attr), sizeof(*ret_taskstats));
- return MNL_CB_ERROR;
- }
- struct taskstats *ts = mnl_attr_get_payload(attr);
- memmove(ret_taskstats, ts, sizeof(*ret_taskstats));
- return MNL_CB_OK;
-
- case TASKSTATS_TYPE_AGGR_PID: /* fall through */
- case TASKSTATS_TYPE_AGGR_TGID:
- return mnl_attr_parse_nested(attr, get_taskstats_attr_cb, ret_taskstats);
-
- case TASKSTATS_TYPE_PID: /* fall through */
- case TASKSTATS_TYPE_TGID:
- /* ignore */
- return MNL_CB_OK;
-
- default:
- DEBUG("utils_taskstats: unknown attribute %" PRIu16
- ", want one of TASKSTATS_TYPE_AGGR_PID/TGID, TASKSTATS_TYPE_STATS",
- type);
- }
- return MNL_CB_OK;
-}
-
-static int get_taskstats_msg_cb(const struct nlmsghdr *nlh, void *data) {
- return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_taskstats_attr_cb,
- data);
-}
-
-static int get_taskstats(ts_t *ts, uint32_t tgid,
- struct taskstats *ret_taskstats) {
- char buffer[MNL_SOCKET_BUFFER_SIZE];
- uint32_t seq = ts->seq++;
-
- struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
- *nlh = (struct nlmsghdr){
- .nlmsg_len = nlh->nlmsg_len,
- .nlmsg_type = ts->genl_id_taskstats,
- .nlmsg_flags = NLM_F_REQUEST,
- .nlmsg_seq = seq,
- .nlmsg_pid = ts->pid,
- };
-
- struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
- *genh = (struct genlmsghdr){
- .cmd = TASKSTATS_CMD_GET,
- .version = TASKSTATS_GENL_VERSION, // or TASKSTATS_VERSION?
- };
-
- // mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_PID, tgid);
- mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_TGID, tgid);
-
- if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
- int status = errno;
- ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
- return status;
- }
-
- int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
- if (status < 0) {
- status = errno;
- ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
- return status;
- } else if (status == 0) {
- ERROR("utils_taskstats: mnl_socket_recvfrom() = 0");
- return ECONNABORTED;
- }
- size_t buffer_size = (size_t)status;
-
- if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
- ERROR("utils_taskstats: TASKSTATS_CMD_GET(TASKSTATS_CMD_ATTR_TGID = "
- "%" PRIu32 ") = %s",
- (uint32_t)tgid, STRERROR(status));
- return status;
- }
-
- status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
- get_taskstats_msg_cb, ret_taskstats);
- if (status < MNL_CB_STOP) {
- ERROR("utils_taskstats: Parsing message failed.");
- return EPROTO;
- }
-
- return 0;
-}
-
-static int get_family_id_attr_cb(const struct nlattr *attr, void *data) {
- uint16_t type = mnl_attr_get_type(attr);
- if (type != CTRL_ATTR_FAMILY_ID) {
- return MNL_CB_OK;
- }
-
- if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
- ERROR("mnl_attr_validate() = %s", STRERRNO);
- return MNL_CB_ERROR;
- }
-
- uint16_t *ret_family_id = data;
- *ret_family_id = mnl_attr_get_u16(attr);
- return MNL_CB_STOP;
-}
-
-static int get_family_id_msg_cb(const struct nlmsghdr *nlh, void *data) {
- return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb,
- data);
-}
-
-/* get_family_id initializes ts->genl_id_taskstats. Returns 0 on success and
- * an error code otherwise. */
-static int get_family_id(ts_t *ts) {
- char buffer[MNL_SOCKET_BUFFER_SIZE];
- uint32_t seq = ts->seq++;
-
- struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
- *nlh = (struct nlmsghdr){
- .nlmsg_len = nlh->nlmsg_len,
- .nlmsg_type = GENL_ID_CTRL,
- .nlmsg_flags = NLM_F_REQUEST,
- .nlmsg_seq = seq,
- .nlmsg_pid = ts->pid,
- };
-
- struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
- *genh = (struct genlmsghdr){
- .cmd = CTRL_CMD_GETFAMILY, .version = 0x01,
- };
-
- mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TASKSTATS_GENL_NAME);
-
- assert(genh->cmd == CTRL_CMD_GETFAMILY);
- assert(genh->version == TASKSTATS_GENL_VERSION);
-
- if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
- int status = errno;
- ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
- return status;
- }
-
- ts->genl_id_taskstats = 0;
- while (42) {
- int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
- if (status < 0) {
- status = errno;
- ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
- return status;
- } else if (status == 0) {
- break;
- }
- size_t buffer_size = (size_t)status;
-
- if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
- ERROR("utils_taskstats: CTRL_CMD_GETFAMILY(\"%s\"): %s",
- TASKSTATS_GENL_NAME, STRERROR(status));
- return status;
- }
-
- status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
- get_family_id_msg_cb, &ts->genl_id_taskstats);
- if (status < MNL_CB_STOP) {
- ERROR("utils_taskstats: Parsing message failed.");
- return EPROTO;
- } else if (status == MNL_CB_STOP) {
- break;
- }
- }
-
- if (ts->genl_id_taskstats == 0) {
- ERROR("utils_taskstats: Netlink communication succeeded, but "
- "genl_id_taskstats is still zero.");
- return ENOENT;
- }
-
- return 0;
-}
-
-void ts_destroy(ts_t *ts) {
- if (ts == NULL) {
- return;
- }
-
- if (ts->nl != NULL) {
- mnl_socket_close(ts->nl);
- ts->nl = NULL;
- }
-
- sfree(ts);
-}
-
-ts_t *ts_create(void) {
- ts_t *ts = calloc(1, sizeof(*ts));
- if (ts == NULL) {
- ERROR("utils_taskstats: calloc failed: %s", STRERRNO);
- return NULL;
- }
-
- if ((ts->nl = mnl_socket_open(NETLINK_GENERIC)) == NULL) {
- ERROR("utils_taskstats: mnl_socket_open(NETLINK_GENERIC) = %s", STRERRNO);
- ts_destroy(ts);
- return NULL;
- }
-
- if (mnl_socket_bind(ts->nl, 0, MNL_SOCKET_AUTOPID) != 0) {
- ERROR("utils_taskstats: mnl_socket_bind() = %s", STRERRNO);
- ts_destroy(ts);
- return NULL;
- }
-
- ts->pid = getpid();
- ts->port_id = mnl_socket_get_portid(ts->nl);
-
- int status = get_family_id(ts);
- if (status != 0) {
- ERROR("utils_taskstats: get_family_id() = %s", STRERROR(status));
- ts_destroy(ts);
- return NULL;
- }
-
- return ts;
-}
-
-int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out) {
- if ((ts == NULL) || (out == NULL)) {
- return EINVAL;
- }
-
- struct taskstats raw = {0};
-
- int status = get_taskstats(ts, tgid, &raw);
- if (status != 0) {
- return status;
- }
-
- *out = (ts_delay_t){
- .cpu_ns = raw.cpu_delay_total,
- .blkio_ns = raw.blkio_delay_total,
- .swapin_ns = raw.swapin_delay_total,
- .freepages_ns = raw.freepages_delay_total,
- };
- return 0;
-}
+++ /dev/null
-/**
- * collectd - src/utils_taskstats.h
- * Copyright (C) 2017 Florian octo Forster
- *
- * ISC License (ISC)
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- */
-
-#ifndef UTILS_TASKSTATS_H
-#define UTILS_TASKSTATS_H 1
-
-#include "collectd.h"
-
-#include "utils_time.h"
-
-struct ts_s;
-typedef struct ts_s ts_t;
-
-typedef struct {
- uint64_t cpu_ns;
- uint64_t blkio_ns;
- uint64_t swapin_ns;
- uint64_t freepages_ns;
-} ts_delay_t;
-
-ts_t *ts_create(void);
-void ts_destroy(ts_t *);
-
-/* ts_delay_by_tgid returns Linux delay accounting information for the task
- * identified by tgid. Returns zero on success and an errno otherwise. */
-int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out);
-
-#endif /* UTILS_TASKSTATS_H */
+++ /dev/null
-/**
- * collectd - src/utils_vl_lookup.c
- * Copyright (C) 2012 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include <pthread.h>
-#include <regex.h>
-
-#include "common.h"
-#include "utils_avltree.h"
-#include "utils_vl_lookup.h"
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-
-#if HAVE_LIBKSTAT
-kstat_ctl_t *kc;
-#endif /* HAVE_LIBKSTAT */
-
-#if BUILD_TEST
-#define sstrncpy strncpy
-#define plugin_log(s, ...) \
- do { \
- printf("[severity %i] ", s); \
- printf(__VA_ARGS__); \
- printf("\n"); \
- } while (0)
-#endif
-
-/*
- * Types
- */
-struct part_match_s {
- char str[DATA_MAX_NAME_LEN];
- regex_t regex;
- bool is_regex;
-};
-typedef struct part_match_s part_match_t;
-
-struct identifier_match_s {
- part_match_t host;
- part_match_t plugin;
- part_match_t plugin_instance;
- part_match_t type;
- part_match_t type_instance;
-
- unsigned int group_by;
-};
-typedef struct identifier_match_s identifier_match_t;
-
-struct lookup_s {
- c_avl_tree_t *by_type_tree;
-
- lookup_class_callback_t cb_user_class;
- lookup_obj_callback_t cb_user_obj;
- lookup_free_class_callback_t cb_free_class;
- lookup_free_obj_callback_t cb_free_obj;
-};
-
-struct user_obj_s;
-typedef struct user_obj_s user_obj_t;
-struct user_obj_s {
- void *user_obj;
- lookup_identifier_t ident;
-
- user_obj_t *next;
-};
-
-struct user_class_s {
- pthread_mutex_t lock;
- void *user_class;
- identifier_match_t match;
- user_obj_t *user_obj_list; /* list of user_obj */
-};
-typedef struct user_class_s user_class_t;
-
-struct user_class_list_s;
-typedef struct user_class_list_s user_class_list_t;
-struct user_class_list_s {
- user_class_t entry;
- user_class_list_t *next;
-};
-
-struct by_type_entry_s {
- c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */
- user_class_list_t *wildcard_plugin_list;
-};
-typedef struct by_type_entry_s by_type_entry_t;
-
-/*
- * Private functions
- */
-static bool lu_part_matches(part_match_t const *match, /* {{{ */
- char const *str) {
- if (match->is_regex) {
- /* Short cut popular catch-all regex. */
- if (strcmp(".*", match->str) == 0)
- return true;
-
- int status = regexec(&match->regex, str,
- /* nmatch = */ 0, /* pmatch = */ NULL,
- /* flags = */ 0);
- if (status == 0)
- return true;
- else
- return false;
- } else if (strcmp(match->str, str) == 0)
- return true;
- else
- return false;
-} /* }}} bool lu_part_matches */
-
-static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */
- char const *ident_part) {
- size_t len = strlen(ident_part);
- int status;
-
- if ((len < 3) || (ident_part[0] != '/') || (ident_part[len - 1] != '/')) {
- sstrncpy(match_part->str, ident_part, sizeof(match_part->str));
- match_part->is_regex = false;
- return 0;
- }
-
- /* Copy string without the leading slash. */
- sstrncpy(match_part->str, ident_part + 1, sizeof(match_part->str));
- assert(sizeof(match_part->str) > len);
- /* strip trailing slash */
- match_part->str[len - 2] = 0;
-
- status = regcomp(&match_part->regex, match_part->str,
- /* flags = */ REG_EXTENDED);
- if (status != 0) {
- char errbuf[1024];
- regerror(status, &match_part->regex, errbuf, sizeof(errbuf));
- ERROR("utils_vl_lookup: Compiling regular expression \"%s\" failed: %s",
- match_part->str, errbuf);
- return EINVAL;
- }
- match_part->is_regex = true;
-
- return 0;
-} /* }}} int lu_copy_ident_to_match_part */
-
-static int lu_copy_ident_to_match(identifier_match_t *match, /* {{{ */
- lookup_identifier_t const *ident,
- unsigned int group_by) {
- memset(match, 0, sizeof(*match));
-
- match->group_by = group_by;
-
-#define COPY_FIELD(field) \
- do { \
- int status = lu_copy_ident_to_match_part(&match->field, ident->field); \
- if (status != 0) \
- return status; \
- } while (0)
-
- COPY_FIELD(host);
- COPY_FIELD(plugin);
- COPY_FIELD(plugin_instance);
- COPY_FIELD(type);
- COPY_FIELD(type_instance);
-
-#undef COPY_FIELD
-
- return 0;
-} /* }}} int lu_copy_ident_to_match */
-
-/* user_class->lock must be held when calling this function */
-static void *lu_create_user_obj(lookup_t *obj, /* {{{ */
- data_set_t const *ds, value_list_t const *vl,
- user_class_t *user_class) {
- user_obj_t *user_obj;
-
- user_obj = calloc(1, sizeof(*user_obj));
- if (user_obj == NULL) {
- ERROR("utils_vl_lookup: calloc failed.");
- return NULL;
- }
- user_obj->next = NULL;
-
- user_obj->user_obj = obj->cb_user_class(ds, vl, user_class->user_class);
- if (user_obj->user_obj == NULL) {
- sfree(user_obj);
- WARNING("utils_vl_lookup: User-provided constructor failed.");
- return NULL;
- }
-
-#define COPY_FIELD(field, group_mask) \
- do { \
- if (user_class->match.field.is_regex && \
- ((user_class->match.group_by & group_mask) == 0)) \
- sstrncpy(user_obj->ident.field, "/.*/", sizeof(user_obj->ident.field)); \
- else \
- sstrncpy(user_obj->ident.field, vl->field, \
- sizeof(user_obj->ident.field)); \
- } while (0)
-
- COPY_FIELD(host, LU_GROUP_BY_HOST);
- COPY_FIELD(plugin, LU_GROUP_BY_PLUGIN);
- COPY_FIELD(plugin_instance, LU_GROUP_BY_PLUGIN_INSTANCE);
- COPY_FIELD(type, 0);
- COPY_FIELD(type_instance, LU_GROUP_BY_TYPE_INSTANCE);
-
-#undef COPY_FIELD
-
- if (user_class->user_obj_list == NULL) {
- user_class->user_obj_list = user_obj;
- } else {
- user_obj_t *last = user_class->user_obj_list;
- while (last->next != NULL)
- last = last->next;
- last->next = user_obj;
- }
-
- return user_obj;
-} /* }}} void *lu_create_user_obj */
-
-/* user_class->lock must be held when calling this function */
-static user_obj_t *lu_find_user_obj(user_class_t *user_class, /* {{{ */
- value_list_t const *vl) {
- user_obj_t *ptr;
-
- for (ptr = user_class->user_obj_list; ptr != NULL; ptr = ptr->next) {
- if (user_class->match.host.is_regex &&
- (user_class->match.group_by & LU_GROUP_BY_HOST) &&
- (strcmp(vl->host, ptr->ident.host) != 0))
- continue;
- if (user_class->match.plugin.is_regex &&
- (user_class->match.group_by & LU_GROUP_BY_PLUGIN) &&
- (strcmp(vl->plugin, ptr->ident.plugin) != 0))
- continue;
- if (user_class->match.plugin_instance.is_regex &&
- (user_class->match.group_by & LU_GROUP_BY_PLUGIN_INSTANCE) &&
- (strcmp(vl->plugin_instance, ptr->ident.plugin_instance) != 0))
- continue;
- if (user_class->match.type_instance.is_regex &&
- (user_class->match.group_by & LU_GROUP_BY_TYPE_INSTANCE) &&
- (strcmp(vl->type_instance, ptr->ident.type_instance) != 0))
- continue;
-
- return ptr;
- }
-
- return NULL;
-} /* }}} user_obj_t *lu_find_user_obj */
-
-static int lu_handle_user_class(lookup_t *obj, /* {{{ */
- data_set_t const *ds, value_list_t const *vl,
- user_class_t *user_class) {
- user_obj_t *user_obj;
- int status;
-
- assert(strcmp(vl->type, user_class->match.type.str) == 0);
- assert(user_class->match.plugin.is_regex ||
- (strcmp(vl->plugin, user_class->match.plugin.str)) == 0);
-
- if (!lu_part_matches(&user_class->match.type_instance, vl->type_instance) ||
- !lu_part_matches(&user_class->match.plugin_instance,
- vl->plugin_instance) ||
- !lu_part_matches(&user_class->match.plugin, vl->plugin) ||
- !lu_part_matches(&user_class->match.host, vl->host))
- return 1;
-
- pthread_mutex_lock(&user_class->lock);
- user_obj = lu_find_user_obj(user_class, vl);
- if (user_obj == NULL) {
- /* call lookup_class_callback_t() and insert into the list of user objects.
- */
- user_obj = lu_create_user_obj(obj, ds, vl, user_class);
- if (user_obj == NULL) {
- pthread_mutex_unlock(&user_class->lock);
- return -1;
- }
- }
- pthread_mutex_unlock(&user_class->lock);
-
- status = obj->cb_user_obj(ds, vl, user_class->user_class, user_obj->user_obj);
- if (status != 0) {
- ERROR("utils_vl_lookup: The user object callback failed with status %i.",
- status);
- /* Returning a negative value means: abort! */
- if (status < 0)
- return status;
- else
- return 1;
- }
-
- return 0;
-} /* }}} int lu_handle_user_class */
-
-static int lu_handle_user_class_list(lookup_t *obj, /* {{{ */
- data_set_t const *ds,
- value_list_t const *vl,
- user_class_list_t *user_class_list) {
- user_class_list_t *ptr;
- int retval = 0;
-
- for (ptr = user_class_list; ptr != NULL; ptr = ptr->next) {
- int status;
-
- status = lu_handle_user_class(obj, ds, vl, &ptr->entry);
- if (status < 0)
- return status;
- else if (status == 0)
- retval++;
- }
-
- return retval;
-} /* }}} int lu_handle_user_class_list */
-
-static by_type_entry_t *lu_search_by_type(lookup_t *obj, /* {{{ */
- char const *type,
- bool allocate_if_missing) {
- by_type_entry_t *by_type;
- char *type_copy;
- int status;
-
- status = c_avl_get(obj->by_type_tree, type, (void *)&by_type);
- if (status == 0)
- return by_type;
-
- if (!allocate_if_missing)
- return NULL;
-
- type_copy = strdup(type);
- if (type_copy == NULL) {
- ERROR("utils_vl_lookup: strdup failed.");
- return NULL;
- }
-
- by_type = calloc(1, sizeof(*by_type));
- if (by_type == NULL) {
- ERROR("utils_vl_lookup: calloc failed.");
- sfree(type_copy);
- return NULL;
- }
- by_type->wildcard_plugin_list = NULL;
-
- by_type->by_plugin_tree =
- c_avl_create((int (*)(const void *, const void *))strcmp);
- if (by_type->by_plugin_tree == NULL) {
- ERROR("utils_vl_lookup: c_avl_create failed.");
- sfree(by_type);
- sfree(type_copy);
- return NULL;
- }
-
- status = c_avl_insert(obj->by_type_tree,
- /* key = */ type_copy, /* value = */ by_type);
- assert(status <= 0); /* >0 => entry exists => race condition. */
- if (status != 0) {
- ERROR("utils_vl_lookup: c_avl_insert failed.");
- c_avl_destroy(by_type->by_plugin_tree);
- sfree(by_type);
- sfree(type_copy);
- return NULL;
- }
-
- return by_type;
-} /* }}} by_type_entry_t *lu_search_by_type */
-
-static int lu_add_by_plugin(by_type_entry_t *by_type, /* {{{ */
- user_class_list_t *user_class_list) {
- user_class_list_t *ptr = NULL;
- identifier_match_t const *match = &user_class_list->entry.match;
-
- /* Lookup user_class_list from the per-plugin structure. If this is the first
- * user_class to be added, the block returns immediately. Otherwise they will
- * set "ptr" to non-NULL. */
- if (match->plugin.is_regex) {
- if (by_type->wildcard_plugin_list == NULL) {
- by_type->wildcard_plugin_list = user_class_list;
- return 0;
- }
-
- ptr = by_type->wildcard_plugin_list;
- } /* if (plugin is wildcard) */
- else /* (plugin is not wildcard) */
- {
- int status;
-
- status =
- c_avl_get(by_type->by_plugin_tree, match->plugin.str, (void *)&ptr);
-
- if (status != 0) /* plugin not yet in tree */
- {
- char *plugin_copy = strdup(match->plugin.str);
-
- if (plugin_copy == NULL) {
- ERROR("utils_vl_lookup: strdup failed.");
- sfree(user_class_list);
- return ENOMEM;
- }
-
- status =
- c_avl_insert(by_type->by_plugin_tree, plugin_copy, user_class_list);
- if (status != 0) {
- ERROR("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.",
- plugin_copy, status);
- sfree(plugin_copy);
- sfree(user_class_list);
- return status;
- } else {
- return 0;
- }
- } /* if (plugin not yet in tree) */
- } /* if (plugin is not wildcard) */
-
- assert(ptr != NULL);
-
- while (ptr->next != NULL)
- ptr = ptr->next;
- ptr->next = user_class_list;
-
- return 0;
-} /* }}} int lu_add_by_plugin */
-
-static void lu_destroy_user_obj(lookup_t *obj, /* {{{ */
- user_obj_t *user_obj) {
- while (user_obj != NULL) {
- user_obj_t *next = user_obj->next;
-
- if (obj->cb_free_obj != NULL)
- obj->cb_free_obj(user_obj->user_obj);
- user_obj->user_obj = NULL;
-
- sfree(user_obj);
- user_obj = next;
- }
-} /* }}} void lu_destroy_user_obj */
-
-static void lu_destroy_user_class_list(lookup_t *obj, /* {{{ */
- user_class_list_t *user_class_list) {
- while (user_class_list != NULL) {
- user_class_list_t *next = user_class_list->next;
-
- if (obj->cb_free_class != NULL)
- obj->cb_free_class(user_class_list->entry.user_class);
- user_class_list->entry.user_class = NULL;
-
-#define CLEAR_FIELD(field) \
- do { \
- if (user_class_list->entry.match.field.is_regex) { \
- regfree(&user_class_list->entry.match.field.regex); \
- user_class_list->entry.match.field.is_regex = false; \
- } \
- } while (0)
-
- CLEAR_FIELD(host);
- CLEAR_FIELD(plugin);
- CLEAR_FIELD(plugin_instance);
- CLEAR_FIELD(type);
- CLEAR_FIELD(type_instance);
-
-#undef CLEAR_FIELD
-
- lu_destroy_user_obj(obj, user_class_list->entry.user_obj_list);
- user_class_list->entry.user_obj_list = NULL;
- pthread_mutex_destroy(&user_class_list->entry.lock);
-
- sfree(user_class_list);
- user_class_list = next;
- }
-} /* }}} void lu_destroy_user_class_list */
-
-static void lu_destroy_by_type(lookup_t *obj, /* {{{ */
- by_type_entry_t *by_type) {
-
- while (42) {
- char *plugin = NULL;
- user_class_list_t *user_class_list = NULL;
- int status;
-
- status = c_avl_pick(by_type->by_plugin_tree, (void *)&plugin,
- (void *)&user_class_list);
- if (status != 0)
- break;
-
- DEBUG("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".",
- plugin);
- sfree(plugin);
- lu_destroy_user_class_list(obj, user_class_list);
- }
-
- c_avl_destroy(by_type->by_plugin_tree);
- by_type->by_plugin_tree = NULL;
-
- lu_destroy_user_class_list(obj, by_type->wildcard_plugin_list);
- by_type->wildcard_plugin_list = NULL;
-
- sfree(by_type);
-} /* }}} int lu_destroy_by_type */
-
-/*
- * Public functions
- */
-lookup_t *lookup_create(lookup_class_callback_t cb_user_class, /* {{{ */
- lookup_obj_callback_t cb_user_obj,
- lookup_free_class_callback_t cb_free_class,
- lookup_free_obj_callback_t cb_free_obj) {
- lookup_t *obj = calloc(1, sizeof(*obj));
- if (obj == NULL) {
- ERROR("utils_vl_lookup: calloc failed.");
- return NULL;
- }
-
- obj->by_type_tree = c_avl_create((int (*)(const void *, const void *))strcmp);
- if (obj->by_type_tree == NULL) {
- ERROR("utils_vl_lookup: c_avl_create failed.");
- sfree(obj);
- return NULL;
- }
-
- obj->cb_user_class = cb_user_class;
- obj->cb_user_obj = cb_user_obj;
- obj->cb_free_class = cb_free_class;
- obj->cb_free_obj = cb_free_obj;
-
- return obj;
-} /* }}} lookup_t *lookup_create */
-
-void lookup_destroy(lookup_t *obj) /* {{{ */
-{
- int status;
-
- if (obj == NULL)
- return;
-
- while (42) {
- char *type = NULL;
- by_type_entry_t *by_type = NULL;
-
- status = c_avl_pick(obj->by_type_tree, (void *)&type, (void *)&by_type);
- if (status != 0)
- break;
-
- DEBUG("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
- sfree(type);
- lu_destroy_by_type(obj, by_type);
- }
-
- c_avl_destroy(obj->by_type_tree);
- obj->by_type_tree = NULL;
-
- sfree(obj);
-} /* }}} void lookup_destroy */
-
-int lookup_add(lookup_t *obj, /* {{{ */
- lookup_identifier_t const *ident, unsigned int group_by,
- void *user_class) {
- by_type_entry_t *by_type = NULL;
- user_class_list_t *user_class_obj;
-
- by_type = lu_search_by_type(obj, ident->type, /* allocate = */ true);
- if (by_type == NULL)
- return -1;
-
- user_class_obj = calloc(1, sizeof(*user_class_obj));
- if (user_class_obj == NULL) {
- ERROR("utils_vl_lookup: calloc failed.");
- return ENOMEM;
- }
- pthread_mutex_init(&user_class_obj->entry.lock, /* attr = */ NULL);
- user_class_obj->entry.user_class = user_class;
- lu_copy_ident_to_match(&user_class_obj->entry.match, ident, group_by);
- user_class_obj->entry.user_obj_list = NULL;
- user_class_obj->next = NULL;
-
- return lu_add_by_plugin(by_type, user_class_obj);
-} /* }}} int lookup_add */
-
-/* returns the number of successful calls to the callback function */
-int lookup_search(lookup_t *obj, /* {{{ */
- data_set_t const *ds, value_list_t const *vl) {
- by_type_entry_t *by_type = NULL;
- user_class_list_t *user_class_list = NULL;
- int retval = 0;
- int status;
-
- if ((obj == NULL) || (ds == NULL) || (vl == NULL))
- return -EINVAL;
-
- by_type = lu_search_by_type(obj, vl->type, /* allocate = */ false);
- if (by_type == NULL)
- return 0;
-
- status =
- c_avl_get(by_type->by_plugin_tree, vl->plugin, (void *)&user_class_list);
- if (status == 0) {
- status = lu_handle_user_class_list(obj, ds, vl, user_class_list);
- if (status < 0)
- return status;
- retval += status;
- }
-
- if (by_type->wildcard_plugin_list != NULL) {
- status =
- lu_handle_user_class_list(obj, ds, vl, by_type->wildcard_plugin_list);
- if (status < 0)
- return status;
- retval += status;
- }
-
- return retval;
-} /* }}} lookup_search */
+++ /dev/null
-/**
- * collectd - src/utils_vl_lookup.h
- * Copyright (C) 2012 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_VL_LOOKUP_H
-#define UTILS_VL_LOOKUP_H 1
-
-#include "plugin.h"
-
-/*
- * Types
- */
-struct lookup_s;
-typedef struct lookup_s lookup_t;
-
-/* Given a user_class, constructs a new user_obj. */
-typedef void *(*lookup_class_callback_t)(data_set_t const *ds,
- value_list_t const *vl,
- void *user_class);
-
-/* Given a user_class and a ds/vl combination, does stuff with the data.
- * This is the main working horse of the module. */
-typedef int (*lookup_obj_callback_t)(data_set_t const *ds,
- value_list_t const *vl, void *user_class,
- void *user_obj);
-
-/* Used to free user_class pointers. May be NULL in which case nothing is
- * freed. */
-typedef void (*lookup_free_class_callback_t)(void *user_class);
-
-/* Used to free user_obj pointers. May be NULL in which case nothing is
- * freed. */
-typedef void (*lookup_free_obj_callback_t)(void *user_obj);
-
-struct lookup_identifier_s {
- char host[DATA_MAX_NAME_LEN];
- char plugin[DATA_MAX_NAME_LEN];
- char plugin_instance[DATA_MAX_NAME_LEN];
- char type[DATA_MAX_NAME_LEN];
- char type_instance[DATA_MAX_NAME_LEN];
-};
-typedef struct lookup_identifier_s lookup_identifier_t;
-
-#define LU_GROUP_BY_HOST 0x01
-#define LU_GROUP_BY_PLUGIN 0x02
-#define LU_GROUP_BY_PLUGIN_INSTANCE 0x04
-/* #define LU_GROUP_BY_TYPE 0x00 */
-#define LU_GROUP_BY_TYPE_INSTANCE 0x10
-
-/*
- * Functions
- */
-__attribute__((nonnull(1, 2)))
-lookup_t *lookup_create(lookup_class_callback_t, lookup_obj_callback_t,
- lookup_free_class_callback_t,
- lookup_free_obj_callback_t);
-void lookup_destroy(lookup_t *obj);
-
-int lookup_add(lookup_t *obj, lookup_identifier_t const *ident,
- unsigned int group_by, void *user_class);
-
-/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
-int lookup_search(lookup_t *obj, data_set_t const *ds, value_list_t const *vl);
-
-#endif /* UTILS_VL_LOOKUP_H */
+++ /dev/null
-/**
- * collectd - src/tests/test_utils_vl_lookup.c
- * Copyright (C) 2012 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "testing.h"
-#include "utils_vl_lookup.h"
-
-static bool expect_new_obj;
-static bool have_new_obj;
-
-static lookup_identifier_t last_class_ident;
-static lookup_identifier_t last_obj_ident;
-
-static data_source_t dsrc_test = {"value", DS_TYPE_DERIVE, 0.0, NAN};
-static data_set_t const ds_test = {"test", 1, &dsrc_test};
-
-static data_source_t dsrc_unknown = {"value", DS_TYPE_DERIVE, 0.0, NAN};
-static data_set_t const ds_unknown = {"unknown", 1, &dsrc_unknown};
-
-static int lookup_obj_callback(data_set_t const *ds, value_list_t const *vl,
- void *user_class, void *user_obj) {
- lookup_identifier_t *class = user_class;
- lookup_identifier_t *obj = user_obj;
-
- OK1(expect_new_obj == have_new_obj,
- (expect_new_obj ? "New obj is created." : "Updating existing obj."));
-
- memcpy(&last_class_ident, class, sizeof(last_class_ident));
- memcpy(&last_obj_ident, obj, sizeof(last_obj_ident));
-
- if (strcmp(obj->plugin_instance, "failure") == 0)
- return -1;
-
- return 0;
-}
-
-static void *lookup_class_callback(data_set_t const *ds, value_list_t const *vl,
- void *user_class) {
- lookup_identifier_t *class = user_class;
- lookup_identifier_t *obj;
-
- assert(expect_new_obj);
-
- memcpy(&last_class_ident, class, sizeof(last_class_ident));
-
- obj = malloc(sizeof(*obj));
- strncpy(obj->host, vl->host, sizeof(obj->host));
- strncpy(obj->plugin, vl->plugin, sizeof(obj->plugin));
- strncpy(obj->plugin_instance, vl->plugin_instance,
- sizeof(obj->plugin_instance));
- strncpy(obj->type, vl->type, sizeof(obj->type));
- strncpy(obj->type_instance, vl->type_instance, sizeof(obj->type_instance));
-
- have_new_obj = true;
-
- return (void *)obj;
-}
-
-static int checked_lookup_add(lookup_t *obj, /* {{{ */
- char const *host, char const *plugin,
- char const *plugin_instance, char const *type,
- char const *type_instance,
- unsigned int group_by) {
- lookup_identifier_t ident = {{0}};
- void *user_class;
-
- strncpy(ident.host, host, sizeof(ident.host) - 1);
- strncpy(ident.plugin, plugin, sizeof(ident.plugin) - 1);
- strncpy(ident.plugin_instance, plugin_instance,
- sizeof(ident.plugin_instance) - 1);
- strncpy(ident.type, type, sizeof(ident.type) - 1);
- strncpy(ident.type_instance, type_instance, sizeof(ident.type_instance) - 1);
-
- user_class = malloc(sizeof(ident));
- memmove(user_class, &ident, sizeof(ident));
-
- OK(lookup_add(obj, &ident, group_by, user_class) == 0);
- return 0;
-} /* }}} int checked_lookup_add */
-
-static int checked_lookup_search(lookup_t *obj, char const *host,
- char const *plugin,
- char const *plugin_instance, char const *type,
- char const *type_instance, bool expect_new) {
- int status;
- value_list_t vl = VALUE_LIST_INIT;
- data_set_t const *ds = &ds_unknown;
-
- strncpy(vl.host, host, sizeof(vl.host) - 1);
- strncpy(vl.plugin, plugin, sizeof(vl.plugin) - 1);
- strncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance) - 1);
- strncpy(vl.type, type, sizeof(vl.type) - 1);
- strncpy(vl.type_instance, type_instance, sizeof(vl.type_instance) - 1);
-
- if (strcmp(vl.type, "test") == 0)
- ds = &ds_test;
-
- expect_new_obj = expect_new;
- have_new_obj = false;
-
- status = lookup_search(obj, ds, &vl);
- return status;
-}
-
-DEF_TEST(group_by_specific_host) {
- lookup_t *obj;
- CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
- (void *)free, (void *)free));
-
- checked_lookup_add(obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST);
- checked_lookup_search(obj, "host0", "test", "", "test", "0",
- /* expect new = */ 1);
- checked_lookup_search(obj, "host0", "test", "", "test", "1",
- /* expect new = */ 0);
- checked_lookup_search(obj, "host1", "test", "", "test", "0",
- /* expect new = */ 1);
- checked_lookup_search(obj, "host1", "test", "", "test", "1",
- /* expect new = */ 0);
-
- lookup_destroy(obj);
- return 0;
-}
-
-DEF_TEST(group_by_any_host) {
- lookup_t *obj;
- CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
- (void *)free, (void *)free));
-
- checked_lookup_add(obj, "/.*/", "/.*/", "/.*/", "test", "/.*/",
- LU_GROUP_BY_HOST);
- checked_lookup_search(obj, "host0", "plugin0", "", "test", "0",
- /* expect new = */ 1);
- checked_lookup_search(obj, "host0", "plugin0", "", "test", "1",
- /* expect new = */ 0);
- checked_lookup_search(obj, "host0", "plugin1", "", "test", "0",
- /* expect new = */ 0);
- checked_lookup_search(obj, "host0", "plugin1", "", "test", "1",
- /* expect new = */ 0);
- checked_lookup_search(obj, "host1", "plugin0", "", "test", "0",
- /* expect new = */ 1);
- checked_lookup_search(obj, "host1", "plugin0", "", "test", "1",
- /* expect new = */ 0);
- checked_lookup_search(obj, "host1", "plugin1", "", "test", "0",
- /* expect new = */ 0);
- checked_lookup_search(obj, "host1", "plugin1", "", "test", "1",
- /* expect new = */ 0);
-
- lookup_destroy(obj);
- return 0;
-}
-
-DEF_TEST(multiple_lookups) {
- lookup_t *obj;
- int status;
-
- CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
- (void *)free, (void *)free));
-
- checked_lookup_add(obj, "/.*/", "plugin0", "", "test", "/.*/",
- LU_GROUP_BY_HOST);
- checked_lookup_add(obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST);
-
- status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "",
- /* expect new = */ 0);
- assert(status == 0);
- status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "",
- /* expect new = */ 1);
- assert(status == 1);
- status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "ti0",
- /* expect new = */ 1);
- assert(status == 1);
- status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "ti0",
- /* expect new = */ 0);
- assert(status == 2);
-
- lookup_destroy(obj);
- return 0;
-}
-
-DEF_TEST(regex) {
- lookup_t *obj;
- CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback,
- (void *)free, (void *)free));
-
- checked_lookup_add(obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/",
- LU_GROUP_BY_TYPE_INSTANCE);
- checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "user",
- /* expect new = */ 1);
- checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "idle",
- /* expect new = */ 1);
- checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "user",
- /* expect new = */ 0);
- checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "idle",
- /* expect new = */ 0);
- checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "user",
- /* expect new = */ 0);
- checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "idle",
- /* expect new = */ 0);
- checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "user",
- /* expect new = */ 0);
- checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "idle",
- /* expect new = */ 0);
- checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "system",
- /* expect new = */ 1);
-
- lookup_destroy(obj);
- return 0;
-}
-
-int main(int argc, char **argv) /* {{{ */
-{
- RUN_TEST(group_by_specific_host);
- RUN_TEST(group_by_any_host);
- RUN_TEST(multiple_lookups);
- RUN_TEST(regex);
-
- END_TEST;
-} /* }}} int main */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_VARNISH_V4 || HAVE_VARNISH_V5
#include <vapi/vsc.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include "utils_complain.h"
-#include "utils_ignorelist.h"
#include <libgen.h> /* for basename(3) */
#include <libvirt/libvirt.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if KERNEL_LINUX
static const char *config_keys[] = {"Verbose"};
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <dirent.h>
#include <sys/types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if KERNEL_LINUX
#include <linux/if.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/format_graphite/format_graphite.h"
#include "utils_complain.h"
-#include "utils_format_graphite.h"
#include <netdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_format_json.h"
-#include "utils_format_kairosdb.h"
+#include "utils/common/common.h"
+#include "utils/format_json/format_json.h"
+#include "utils/format_kairosdb/format_kairosdb.h"
#include <curl/curl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_cmd_putval.h"
-#include "utils_format_graphite.h"
-#include "utils_format_json.h"
+#include "utils/cmds/putval.h"
+#include "utils/common/common.h"
+#include "utils/format_graphite/format_graphite.h"
+#include "utils/format_json/format_json.h"
#include "utils_random.h"
#include <errno.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
-#include "utils_format_graphite.h"
-#include "utils_format_json.h"
+#include "utils/format_graphite/format_graphite.h"
+#include "utils/format_json/format_json.h"
#include <netdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <mongoc.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
#include "utils_complain.h"
#include "utils_time.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <hiredis/hiredis.h>
#include <sys/time.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_complain.h"
#include "write_riemann_threshold.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_threshold.h"
#include "write_riemann_threshold.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <arpa/inet.h>
#include <errno.h>
#include "collectd.h"
-#include "common.h"
#include "configfile.h"
#include "plugin.h"
-#include "utils_format_stackdriver.h"
-#include "utils_gce.h"
-#include "utils_oauth.h"
+#include "utils/common/common.h"
+#include "utils/format_stackdriver/format_stackdriver.h"
+#include "utils/gce/gce.h"
+#include "utils/oauth/oauth.h"
#include <curl/curl.h>
#include <pthread.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_random.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <xenctrl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <xmms/xmmsctrl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
/*
* Global variables
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <procfs.h>
#include <zone.h>
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
#define MAX_PROCFS_PATH 40
#define FRC2PCT(pp) (((float)(pp)) / 0x8000 * 100)
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>