From: Florian Forster Date: Thu, 18 Oct 2018 15:03:47 +0000 (+0200) Subject: Tree wide: Move utilities and libraries to src/utils/. X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=6378ec288f34ff250b2971a1452338a2b34c240a;p=collectd.git Tree wide: Move utilities and libraries to src/utils/. This is a first step that does not yet include *all* "utils_*.c" files, but we're getting there. --- diff --git a/Makefile.am b/Makefile.am index 9bb22fc5..83613b0b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -236,8 +236,8 @@ collectd_SOURCES = \ 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 \ @@ -341,22 +341,22 @@ endif 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) @@ -372,30 +372,30 @@ test_utils_subst_SOURCES = \ 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 \ @@ -409,11 +409,11 @@ libplugin_mock_la_CPPFLAGS = $(AM_CPPFLAGS) -DMOCK_TIME 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 \ @@ -422,8 +422,8 @@ test_format_graphite_LDADD = \ -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 = @@ -435,7 +435,7 @@ libformat_json_la_LIBADD += $(BUILD_WITH_LIBYAJL_LIBS) 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 \ @@ -453,16 +453,16 @@ check_PROGRAMS += test_plugin_ceph 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 \ @@ -470,41 +470,41 @@ test_utils_latency_LDADD = \ -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 \ @@ -514,11 +514,11 @@ test_utils_vl_lookup_LDADD += -lkstat 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 \ @@ -579,8 +579,8 @@ if BUILD_WITH_LIBSSL 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) \ @@ -594,7 +594,7 @@ liboauth_la_LIBADD = \ 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 \ @@ -602,8 +602,8 @@ test_utils_oauth_LDADD = \ 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) @@ -616,8 +616,8 @@ endif 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) @@ -629,7 +629,7 @@ libformat_stackdriver_la_LIBADD = \ 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 \ @@ -641,8 +641,8 @@ if BUILD_PLUGIN_AGGREGATION 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 @@ -663,7 +663,7 @@ if BUILD_PLUGIN_AMQP1 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 = \ @@ -827,10 +827,10 @@ if BUILD_PLUGIN_CURL 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) @@ -840,15 +840,15 @@ if BUILD_PLUGIN_CURL_JSON 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) @@ -861,8 +861,8 @@ if BUILD_PLUGIN_CURL_XML 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) @@ -873,8 +873,8 @@ if BUILD_PLUGIN_DBI 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) @@ -924,8 +924,8 @@ if BUILD_PLUGIN_DNS 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) @@ -933,7 +933,7 @@ endif 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) @@ -942,7 +942,7 @@ endif 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) @@ -1054,8 +1054,8 @@ if BUILD_PLUGIN_INTEL_PMU 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) @@ -1065,8 +1065,8 @@ if BUILD_PLUGIN_INTEL_RDT 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) @@ -1260,8 +1260,8 @@ if BUILD_PLUGIN_MEMCACHEC 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) @@ -1468,8 +1468,8 @@ if BUILD_PLUGIN_ORACLE 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) @@ -1479,8 +1479,8 @@ if BUILD_PLUGIN_OVS_EVENTS 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) @@ -1490,8 +1490,8 @@ if BUILD_PLUGIN_OVS_STATS 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) @@ -1561,8 +1561,8 @@ if BUILD_PLUGIN_POSTGRESQL 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) @@ -1589,8 +1589,8 @@ endif 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 @@ -1637,8 +1637,8 @@ if BUILD_PLUGIN_RRDCACHED 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) @@ -1648,8 +1648,8 @@ if BUILD_PLUGIN_RRDTOOL 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) @@ -1703,7 +1703,7 @@ snmp_agent_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS) 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 @@ -1774,10 +1774,10 @@ if BUILD_PLUGIN_TAIL 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) @@ -1788,8 +1788,8 @@ if BUILD_PLUGIN_TAIL_CSV 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 @@ -1979,8 +1979,8 @@ if BUILD_PLUGIN_WRITE_HTTP 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) diff --git a/src/aggregation.c b/src/aggregation.c index a8021996..089ff1da 100644 --- a/src/aggregation.c +++ b/src/aggregation.c @@ -26,12 +26,12 @@ #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}" diff --git a/src/amqp.c b/src/amqp.c index 281130b1..9eb51653 100644 --- a/src/amqp.c +++ b/src/amqp.c @@ -28,11 +28,11 @@ #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 #include diff --git a/src/amqp1.c b/src/amqp1.c index 87bb50cf..a7fd26be 100644 --- a/src/amqp1.c +++ b/src/amqp1.c @@ -26,12 +26,12 @@ #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 diff --git a/src/apache.c b/src/apache.c index 5c67a388..d64e5471 100644 --- a/src/apache.c +++ b/src/apache.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/apcups.c b/src/apcups.c index 2931d2c0..83a5c87a 100644 --- a/src/apcups.c +++ b/src/apcups.c @@ -26,8 +26,8 @@ #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 diff --git a/src/apple_sensors.c b/src/apple_sensors.c index f78c3da2..ad6e6c03 100644 --- a/src/apple_sensors.c +++ b/src/apple_sensors.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_MACH_MACH_TYPES_H #include diff --git a/src/aquaero.c b/src/aquaero.c index 937742b0..dfa2804d 100644 --- a/src/aquaero.c +++ b/src/aquaero.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/ascent.c b/src/ascent.c index 22358658..e5589bf6 100644 --- a/src/ascent.c +++ b/src/ascent.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/barometer.c b/src/barometer.c index b6f2bc00..468a2371 100644 --- a/src/barometer.c +++ b/src/barometer.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include diff --git a/src/battery.c b/src/battery.c index a74e7b64..8e6c4b29 100644 --- a/src/battery.c +++ b/src/battery.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_MACH_MACH_TYPES_H #include diff --git a/src/battery_statefs.c b/src/battery_statefs.c index 149512b9..d2c1ed9d 100644 --- a/src/battery_statefs.c +++ b/src/battery_statefs.c @@ -44,9 +44,9 @@ SOFTWARE. **/ -#include "common.h" -#include "plugin.h" #include "collectd.h" +#include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/bind.c b/src/bind.c index fe3480d0..29f65d40 100644 --- a/src/bind.c +++ b/src/bind.c @@ -43,8 +43,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/ceph.c b/src/ceph.c index 26cf2155..19a09d86 100644 --- a/src/ceph.c +++ b/src/ceph.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/cgroups.c b/src/cgroups.c index 9304216d..89252398 100644 --- a/src/cgroups.c +++ b/src/cgroups.c @@ -23,10 +23,10 @@ #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); diff --git a/src/chrony.c b/src/chrony.c index 913aab94..65e3c4c8 100644 --- a/src/chrony.c +++ b/src/chrony.c @@ -28,8 +28,8 @@ #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 /* struct addrinfo */ diff --git a/src/collectd-tg.c b/src/collectd-tg.c index 5210a221..92d1f012 100644 --- a/src/collectd-tg.c +++ b/src/collectd-tg.c @@ -43,7 +43,7 @@ #include #include -#include "utils_heap.h" +#include "utils/heap/heap.h" #include "collectd/client.h" #include "collectd/network.h" diff --git a/src/conntrack.c b/src/conntrack.c index 29c7003e..7b61eef0 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/contextswitch.c b/src/contextswitch.c index 35ac5a39..acf3a74d 100644 --- a/src/contextswitch.c +++ b/src/contextswitch.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_SYS_SYSCTL_H #include diff --git a/src/cpu.c b/src/cpu.c index 2a697129..e7f3c187 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_MACH_KERN_RETURN_H #include diff --git a/src/cpufreq.c b/src/cpufreq.c index 21b6429a..35ec07fb 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -22,8 +22,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #define MAX_AVAIL_FREQS 20 diff --git a/src/cpusleep.c b/src/cpusleep.c index aa14cc12..b5cbe660 100644 --- a/src/cpusleep.c +++ b/src/cpusleep.c @@ -32,9 +32,9 @@ #include "collectd.h" -#include -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" +#include static void cpusleep_submit(derive_t cpu_sleep) { value_list_t vl = VALUE_LIST_INIT; diff --git a/src/csv.c b/src/csv.c index 88726bba..953473fc 100644 --- a/src/csv.c +++ b/src/csv.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" /* diff --git a/src/curl.c b/src/curl.c index 4925ad09..9ad3dc83 100644 --- a/src/curl.c +++ b/src/curl.c @@ -23,10 +23,10 @@ #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 diff --git a/src/curl_json.c b/src/curl_json.c index 3b2fffe0..a26664f0 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -23,11 +23,11 @@ #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 #include diff --git a/src/curl_xml.c b/src/curl_xml.c index 0bed05a5..ed70f698 100644 --- a/src/curl_xml.c +++ b/src/curl_xml.c @@ -21,9 +21,9 @@ #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 diff --git a/src/daemon/cmd.c b/src/daemon/cmd.c index 7b779955..49f9272c 100644 --- a/src/daemon/cmd.c +++ b/src/daemon/cmd.c @@ -24,7 +24,7 @@ #include "cmd.h" #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include static void *do_flush(void __attribute__((unused)) * arg) { diff --git a/src/daemon/collectd.c b/src/daemon/collectd.c index f1a49237..28fa7155 100644 --- a/src/daemon/collectd.c +++ b/src/daemon/collectd.c @@ -28,9 +28,9 @@ #include "cmd.h" #include "collectd.h" -#include "common.h" #include "configfile.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/daemon/common.c b/src/daemon/common.c deleted file mode 100644 index b5a1c980..00000000 --- a/src/daemon/common.c +++ /dev/null @@ -1,1589 +0,0 @@ -/** - * 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 - * Niki W. Waibel - * Sebastian Harl - * Michał Mirosław -**/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_cache.h" - -/* for getaddrinfo */ -#include -#include - -#include - -#if HAVE_NETINET_IN_H -#include -#endif - -#if HAVE_NETINET_TCP_H -#include -#endif - -/* for ntohl and htonl */ -#if HAVE_ARPA_INET_H -#include -#endif - -#if HAVE_CAPABILITY -#include -#endif - -#if HAVE_KSTAT_H -#include -#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 */ diff --git a/src/daemon/common.h b/src/daemon/common.h deleted file mode 100644 index addf06d3..00000000 --- a/src/daemon/common.h +++ /dev/null @@ -1,396 +0,0 @@ -/** - * 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 - * Niki W. Waibel -**/ - -#ifndef COMMON_H -#define COMMON_H - -#include "collectd.h" - -#include "plugin.h" - -#if HAVE_PWD_H -#include -#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 -> - * \n -> - * \r -> - * - * 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 -#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 */ diff --git a/src/daemon/common_test.c b/src/daemon/common_test.c deleted file mode 100644 index 93a19d1b..00000000 --- a/src/daemon/common_test.c +++ /dev/null @@ -1,378 +0,0 @@ -/** - * 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 - */ - -#include "common.h" -#include "testing.h" - -#if HAVE_KSTAT_H -#include -#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; -} diff --git a/src/daemon/configfile.c b/src/daemon/configfile.c index 95cb32fc..8bf6b8d7 100644 --- a/src/daemon/configfile.c +++ b/src/daemon/configfile.c @@ -29,11 +29,11 @@ #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 diff --git a/src/daemon/filter_chain.c b/src/daemon/filter_chain.c index a0a76876..d5e14a34 100644 --- a/src/daemon/filter_chain.c +++ b/src/daemon/filter_chain.c @@ -26,10 +26,10 @@ #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" /* diff --git a/src/daemon/globals.c b/src/daemon/globals.c index 5c6749ff..a76a44c4 100644 --- a/src/daemon/globals.c +++ b/src/daemon/globals.c @@ -21,8 +21,8 @@ * DEALINGS IN THE SOFTWARE. **/ -#include "common.h" #include "globals.h" +#include "utils/common/common.h" #if HAVE_KSTAT_H #include diff --git a/src/daemon/meta_data.c b/src/daemon/meta_data.c deleted file mode 100644 index 08f682e9..00000000 --- a/src/daemon/meta_data.c +++ /dev/null @@ -1,747 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/daemon/meta_data.h b/src/daemon/meta_data.h deleted file mode 100644 index 203b1460..00000000 --- a/src/daemon/meta_data.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/daemon/meta_data_test.c b/src/daemon/meta_data_test.c deleted file mode 100644 index ca808364..00000000 --- a/src/daemon/meta_data_test.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * 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 - */ - -#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; -} diff --git a/src/daemon/plugin.c b/src/daemon/plugin.c index de04665a..b4e5ae72 100644 --- a/src/daemon/plugin.c +++ b/src/daemon/plugin.c @@ -30,14 +30,14 @@ #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" diff --git a/src/daemon/plugin.h b/src/daemon/plugin.h index 616889ad..6b3a030c 100644 --- a/src/daemon/plugin.h +++ b/src/daemon/plugin.h @@ -31,7 +31,7 @@ #include "collectd.h" #include "configfile.h" -#include "meta_data.h" +#include "utils/metadata/meta_data.h" #include "utils_time.h" #include diff --git a/src/daemon/types_list.c b/src/daemon/types_list.c index c3f590c1..9c610409 100644 --- a/src/daemon/types_list.c +++ b/src/daemon/types_list.c @@ -26,7 +26,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "configfile.h" #include "plugin.h" diff --git a/src/daemon/utils_avltree.c b/src/daemon/utils_avltree.c deleted file mode 100644 index 568d68c1..00000000 --- a/src/daemon/utils_avltree.c +++ /dev/null @@ -1,648 +0,0 @@ -/** - * 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 - **/ - -#include -#include - -#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; -} diff --git a/src/daemon/utils_avltree.h b/src/daemon/utils_avltree.h deleted file mode 100644 index 3f52b931..00000000 --- a/src/daemon/utils_avltree.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/daemon/utils_avltree_test.c b/src/daemon/utils_avltree_test.c deleted file mode 100644 index 4be49416..00000000 --- a/src/daemon/utils_avltree_test.c +++ /dev/null @@ -1,180 +0,0 @@ -/** - * 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 - */ - -#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; -} diff --git a/src/daemon/utils_cache.c b/src/daemon/utils_cache.c index cdb76422..c53e5d14 100644 --- a/src/daemon/utils_cache.c +++ b/src/daemon/utils_cache.c @@ -28,10 +28,10 @@ #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 diff --git a/src/daemon/utils_heap.c b/src/daemon/utils_heap.c deleted file mode 100644 index 3cecd893..00000000 --- a/src/daemon/utils_heap.c +++ /dev/null @@ -1,207 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include -#include -#include -#include - -#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 */ diff --git a/src/daemon/utils_heap.h b/src/daemon/utils_heap.h deleted file mode 100644 index d2d70cdc..00000000 --- a/src/daemon/utils_heap.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/daemon/utils_heap_test.c b/src/daemon/utils_heap_test.c deleted file mode 100644 index 827c090f..00000000 --- a/src/daemon/utils_heap_test.c +++ /dev/null @@ -1,78 +0,0 @@ -/** - * 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 - */ - -#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; -} diff --git a/src/daemon/utils_subst.c b/src/daemon/utils_subst.c index 28924e44..0b5f00d1 100644 --- a/src/daemon/utils_subst.c +++ b/src/daemon/utils_subst.c @@ -30,7 +30,7 @@ #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, diff --git a/src/daemon/utils_subst_test.c b/src/daemon/utils_subst_test.c index 20560966..0e582306 100644 --- a/src/daemon/utils_subst_test.c +++ b/src/daemon/utils_subst_test.c @@ -25,7 +25,7 @@ */ #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" diff --git a/src/daemon/utils_threshold.c b/src/daemon/utils_threshold.c index 8c033411..52af648f 100644 --- a/src/daemon/utils_threshold.c +++ b/src/daemon/utils_threshold.c @@ -26,8 +26,8 @@ #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 diff --git a/src/daemon/utils_time.c b/src/daemon/utils_time.c index 4637122e..5dd7d0e1 100644 --- a/src/daemon/utils_time.c +++ b/src/daemon/utils_time.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_time.h" #ifndef DEFAULT_MOCK_TIME diff --git a/src/dbi.c b/src/dbi.c index 899c802c..8466bd11 100644 --- a/src/dbi.c +++ b/src/dbi.c @@ -26,9 +26,9 @@ #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 diff --git a/src/df.c b/src/df.c index 8877b740..5b3fbd28 100644 --- a/src/df.c +++ b/src/df.c @@ -23,10 +23,10 @@ #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 diff --git a/src/disk.c b/src/disk.c index 7ceb95a6..e73a5c08 100644 --- a/src/disk.c +++ b/src/disk.c @@ -23,9 +23,9 @@ #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 diff --git a/src/dns.c b/src/dns.c index bd6820fa..7fa297b8 100644 --- a/src/dns.c +++ b/src/dns.c @@ -26,10 +26,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" -#include "utils_dns.h" +#include "utils/dns/dns.h" #include #include diff --git a/src/dpdkevents.c b/src/dpdkevents.c index 2a44b2c1..4cdf01df 100644 --- a/src/dpdkevents.c +++ b/src/dpdkevents.c @@ -32,12 +32,12 @@ #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 diff --git a/src/dpdkstat.c b/src/dpdkstat.c index 59ab9760..0005d091 100644 --- a/src/dpdkstat.c +++ b/src/dpdkstat.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" -#include "utils_dpdk.h" +#include "utils/common/common.h" +#include "utils/dpdk/dpdk.h" #include #include diff --git a/src/drbd.c b/src/drbd.c index 69dc4ef9..0f54dd5e 100644 --- a/src/drbd.c +++ b/src/drbd.c @@ -36,8 +36,8 @@ #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[] = { diff --git a/src/email.c b/src/email.c index f8a94fb6..deb66006 100644 --- a/src/email.c +++ b/src/email.c @@ -40,8 +40,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/entropy.c b/src/entropy.c index c7b5b3f3..94a291ec 100644 --- a/src/entropy.c +++ b/src/entropy.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/ethstat.c b/src/ethstat.c index 0d4c7e15..f8bc5b54 100644 --- a/src/ethstat.c +++ b/src/ethstat.c @@ -24,9 +24,9 @@ #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 diff --git a/src/exec.c b/src/exec.c index 26b8fa7e..7e161677 100644 --- a/src/exec.c +++ b/src/exec.c @@ -28,11 +28,11 @@ #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 #include diff --git a/src/fhcount.c b/src/fhcount.c index 9bcb9115..93ad903e 100644 --- a/src/fhcount.c +++ b/src/fhcount.c @@ -19,8 +19,8 @@ #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); diff --git a/src/filecount.c b/src/filecount.c index 9091ff55..5acd47b3 100644 --- a/src/filecount.c +++ b/src/filecount.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/fscache.c b/src/fscache.c index dd36b8b9..e875da8d 100644 --- a/src/fscache.c +++ b/src/fscache.c @@ -21,11 +21,11 @@ #include "collectd.h" +#include "plugin.h" +#include "utils/common/common.h" #include /* a header needed for FILE */ #include /* used for atoi */ #include /* 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" diff --git a/src/gmond.c b/src/gmond.c index 3312f96e..b14dee3b 100644 --- a/src/gmond.c +++ b/src/gmond.c @@ -26,9 +26,9 @@ #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 diff --git a/src/gps.c b/src/gps.c index b22c3a2e..4d651768 100644 --- a/src/gps.c +++ b/src/gps.c @@ -27,8 +27,8 @@ **/ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_time.h" #define CGPS_TRUE 1 diff --git a/src/gpu_nvidia.c b/src/gpu_nvidia.c index 812cfeb0..d76e503d 100644 --- a/src/gpu_nvidia.c +++ b/src/gpu_nvidia.c @@ -21,8 +21,8 @@ SOFTWARE. */ #include "daemon/collectd.h" -#include "daemon/common.h" #include "daemon/plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/grpc.cc b/src/grpc.cc index 17168ec2..1e9cb20c 100644 --- a/src/grpc.cc +++ b/src/grpc.cc @@ -41,8 +41,8 @@ extern "C" { #include #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "daemon/utils_cache.h" } diff --git a/src/hddtemp.c b/src/hddtemp.c index 80daf15b..96b4f0c5 100644 --- a/src/hddtemp.c +++ b/src/hddtemp.c @@ -33,8 +33,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include /* for basename */ diff --git a/src/hugepages.c b/src/hugepages.c index dd897356..e066300b 100644 --- a/src/hugepages.c +++ b/src/hugepages.c @@ -30,8 +30,8 @@ #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"; diff --git a/src/intel_pmu.c b/src/intel_pmu.c index ff92beed..f04f8871 100644 --- a/src/intel_pmu.c +++ b/src/intel_pmu.c @@ -27,9 +27,9 @@ **/ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" -#include "utils_config_cores.h" +#include "utils/config_cores/config_cores.h" #include #include diff --git a/src/intel_rdt.c b/src/intel_rdt.c index df9c9c44..d9491147 100644 --- a/src/intel_rdt.c +++ b/src/intel_rdt.c @@ -26,8 +26,8 @@ **/ #include "collectd.h" -#include "common.h" -#include "utils_config_cores.h" +#include "utils/common/common.h" +#include "utils/config_cores/config_cores.h" #include diff --git a/src/interface.c b/src/interface.c index 86110b0c..0e139708 100644 --- a/src/interface.c +++ b/src/interface.c @@ -24,9 +24,9 @@ #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 diff --git a/src/ipc.c b/src/ipc.c index 6e888c46..ab4b214d 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -28,8 +28,8 @@ #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 */ diff --git a/src/ipmi.c b/src/ipmi.c index 58dfb411..d78ffa95 100644 --- a/src/ipmi.c +++ b/src/ipmi.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include diff --git a/src/iptables.c b/src/iptables.c index 225ed2c1..e1d83dfc 100644 --- a/src/iptables.c +++ b/src/iptables.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/ipvs.c b/src/ipvs.c index 0afc7494..0259df80 100644 --- a/src/ipvs.c +++ b/src/ipvs.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_ARPA_INET_H #include diff --git a/src/irq.c b/src/irq.c index eeea058a..91563560 100644 --- a/src/irq.c +++ b/src/irq.c @@ -23,9 +23,9 @@ #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." diff --git a/src/java.c b/src/java.c index 0a5336a5..cf301c6b 100644 --- a/src/java.c +++ b/src/java.c @@ -23,9 +23,9 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/load.c b/src/load.c index 858d9bea..da7fe58f 100644 --- a/src/load.c +++ b/src/load.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/log_logstash.c b/src/log_logstash.c index d115ae56..b04aaddd 100644 --- a/src/log_logstash.c +++ b/src/log_logstash.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/logfile.c b/src/logfile.c index fa56a1bc..3a25319e 100644 --- a/src/logfile.c +++ b/src/logfile.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if COLLECT_DEBUG static int log_level = LOG_DEBUG; diff --git a/src/lpar.c b/src/lpar.c index df18b525..dc3739b8 100644 --- a/src/lpar.c +++ b/src/lpar.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/lua.c b/src/lua.c index f66d8526..d91676c9 100644 --- a/src/lua.c +++ b/src/lua.c @@ -29,8 +29,8 @@ **/ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_lua.h" /* Include the Lua API header files. */ diff --git a/src/lvm.c b/src/lvm.c index 3ec79dea..c30489cb 100644 --- a/src/lvm.c +++ b/src/lvm.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/madwifi.c b/src/madwifi.c index 60ac3c8c..85454c3f 100644 --- a/src/madwifi.c +++ b/src/madwifi.c @@ -88,9 +88,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include diff --git a/src/match_empty_counter.c b/src/match_empty_counter.c index 27817fe5..fd87b384 100644 --- a/src/match_empty_counter.c +++ b/src/match_empty_counter.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" /* * internal helper functions diff --git a/src/match_hashed.c b/src/match_hashed.c index c0554b23..4911ee2c 100644 --- a/src/match_hashed.c +++ b/src/match_hashed.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" /* * private data types diff --git a/src/match_regex.c b/src/match_regex.c index 20445cc1..4052ad55 100644 --- a/src/match_regex.c +++ b/src/match_regex.c @@ -33,9 +33,9 @@ #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 diff --git a/src/match_timediff.c b/src/match_timediff.c index c80694de..172b3126 100644 --- a/src/match_timediff.c +++ b/src/match_timediff.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #define SATISFY_ALL 0 #define SATISFY_ANY 1 diff --git a/src/match_value.c b/src/match_value.c index 44cea268..7b9da693 100644 --- a/src/match_value.c +++ b/src/match_value.c @@ -31,8 +31,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_cache.h" #define SATISFY_ALL 0 diff --git a/src/mbmon.c b/src/mbmon.c index 63a300de..50955f3c 100644 --- a/src/mbmon.c +++ b/src/mbmon.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/mcelog.c b/src/mcelog.c index 4e514004..0cce0c60 100644 --- a/src/mcelog.c +++ b/src/mcelog.c @@ -31,7 +31,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "utils_llist.h" #include diff --git a/src/md.c b/src/md.c index 0a015c7b..643dabd7 100644 --- a/src/md.c +++ b/src/md.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include diff --git a/src/memcachec.c b/src/memcachec.c index f293aa1b..eefcfb73 100644 --- a/src/memcachec.c +++ b/src/memcachec.c @@ -23,9 +23,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_match.h" +#include "utils/common/common.h" +#include "utils/match/match.h" #include diff --git a/src/memcached.c b/src/memcached.c index 4ff70f7c..0baf6c22 100644 --- a/src/memcached.c +++ b/src/memcached.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/memory.c b/src/memory.c index cc95496a..4a3a7723 100644 --- a/src/memory.c +++ b/src/memory.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_SYS_SYSCTL_H #include diff --git a/src/mic.c b/src/mic.c index 4f4a9bac..6924eaf7 100644 --- a/src/mic.c +++ b/src/mic.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include diff --git a/src/modbus.c b/src/modbus.c index 0a4f40ca..ed53319f 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -22,9 +22,9 @@ #include "collectd.h" -#include "common.h" #include "configfile.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/mqtt.c b/src/mqtt.c index 48c34edc..630114e4 100644 --- a/src/mqtt.c +++ b/src/mqtt.c @@ -31,8 +31,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_complain.h" #include diff --git a/src/multimeter.c b/src/multimeter.c index ca9b15d4..5a7d5a2a 100644 --- a/src/multimeter.c +++ b/src/multimeter.c @@ -24,8 +24,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H #include diff --git a/src/mysql.c b/src/mysql.c index e7ffb489..7399fe21 100644 --- a/src/mysql.c +++ b/src/mysql.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_MYSQL_H #include diff --git a/src/netapp.c b/src/netapp.c index 0c1fe31d..1b510d2a 100644 --- a/src/netapp.c +++ b/src/netapp.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include diff --git a/src/netlink.c b/src/netlink.c index a1f52a45..37c2e294 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -27,8 +27,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/network.c b/src/network.c index af23d485..f6f0ac15 100644 --- a/src/network.c +++ b/src/network.c @@ -27,8 +27,8 @@ #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" diff --git a/src/nfs.c b/src/nfs.c index 481aa79e..320caa4a 100644 --- a/src/nfs.c +++ b/src/nfs.c @@ -24,8 +24,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_KSTAT_H #include diff --git a/src/nginx.c b/src/nginx.c index e5ca89c0..7bb307a3 100644 --- a/src/nginx.c +++ b/src/nginx.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/notify_desktop.c b/src/notify_desktop.c index e391cf27..849b1d4c 100644 --- a/src/notify_desktop.c +++ b/src/notify_desktop.c @@ -30,8 +30,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/notify_email.c b/src/notify_email.c index bb36ff27..dddb8b26 100644 --- a/src/notify_email.c +++ b/src/notify_email.c @@ -24,8 +24,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/notify_nagios.c b/src/notify_nagios.c index 68f6e2a7..79926b52 100644 --- a/src/notify_nagios.c +++ b/src/notify_nagios.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #define NAGIOS_OK 0 #define NAGIOS_WARNING 1 diff --git a/src/ntpd.c b/src/ntpd.c index 0b824ba2..33acc69f 100644 --- a/src/ntpd.c +++ b/src/ntpd.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_NETDB_H #include diff --git a/src/numa.c b/src/numa.c index c68fb869..dfe7a6dc 100644 --- a/src/numa.c +++ b/src/numa.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/nut.c b/src/nut.c index 997d1a50..ae48692b 100644 --- a/src/nut.c +++ b/src/nut.c @@ -27,8 +27,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/olsrd.c b/src/olsrd.c index df052889..c8b8b7a9 100644 --- a/src/olsrd.c +++ b/src/olsrd.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/onewire.c b/src/onewire.c index 575a6820..a0a546b2 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include diff --git a/src/openldap.c b/src/openldap.c index 3897cd16..5659c69f 100644 --- a/src/openldap.c +++ b/src/openldap.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if defined(__APPLE__) #pragma clang diagnostic push diff --git a/src/openvpn.c b/src/openvpn.c index 193a9b43..4d4a878f 100644 --- a/src/openvpn.c +++ b/src/openvpn.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" /** * There is two main kinds of OpenVPN status file: diff --git a/src/oracle.c b/src/oracle.c index f54b285a..3f28110f 100644 --- a/src/oracle.c +++ b/src/oracle.c @@ -47,9 +47,9 @@ #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 diff --git a/src/ovs_events.c b/src/ovs_events.c index ba3238b6..0f9a57ca 100644 --- a/src/ovs_events.c +++ b/src/ovs_events.c @@ -30,9 +30,9 @@ #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 diff --git a/src/ovs_stats.c b/src/ovs_stats.c index eca7329e..fe1953f2 100644 --- a/src/ovs_stats.c +++ b/src/ovs_stats.c @@ -28,9 +28,9 @@ * Taras Chornyi */ -#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"; @@ -280,8 +280,11 @@ static void ovs_stats_submit_interfaces(port_list_t *port) { 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], diff --git a/src/pcie_errors.c b/src/pcie_errors.c index b239a8c5..63996050 100644 --- a/src/pcie_errors.c +++ b/src/pcie_errors.c @@ -27,7 +27,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "utils_llist.h" #include diff --git a/src/perl.c b/src/perl.c index fffbc21d..09e6e5aa 100644 --- a/src/perl.c +++ b/src/perl.c @@ -47,8 +47,8 @@ #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" diff --git a/src/pf.c b/src/pf.c index 88a4c2d0..9681d366 100644 --- a/src/pf.c +++ b/src/pf.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYS_IOCTL_H #include diff --git a/src/pinba.c b/src/pinba.c index 9f571d04..61d226c4 100644 --- a/src/pinba.c +++ b/src/pinba.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/ping.c b/src/ping.c index ffb16910..203d2230 100644 --- a/src/ping.c +++ b/src/ping.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_complain.h" #include diff --git a/src/postgresql.c b/src/postgresql.c index e6278062..8e4328fa 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -32,13 +32,13 @@ #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 #include diff --git a/src/powerdns.c b/src/powerdns.c index b2cebbf8..a5b45a19 100644 --- a/src/powerdns.c +++ b/src/powerdns.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_llist.h" #include diff --git a/src/processes.c b/src/processes.c index 2e3b927e..ac5ec604 100644 --- a/src/processes.c +++ b/src/processes.c @@ -38,12 +38,12 @@ #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.. */ diff --git a/src/protocols.c b/src/protocols.c index 36b1d83b..65b450c9 100644 --- a/src/protocols.c +++ b/src/protocols.c @@ -26,9 +26,9 @@ #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." diff --git a/src/pyconfig.c b/src/pyconfig.c index 4ba7e0d2..5f51b937 100644 --- a/src/pyconfig.c +++ b/src/pyconfig.c @@ -29,7 +29,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "cpython.h" diff --git a/src/python.c b/src/python.c index 2f08bd34..9d47d701 100644 --- a/src/python.c +++ b/src/python.c @@ -31,7 +31,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "cpython.h" diff --git a/src/pyvalues.c b/src/pyvalues.c index 301df442..967fecfd 100644 --- a/src/pyvalues.c +++ b/src/pyvalues.c @@ -29,7 +29,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "cpython.h" diff --git a/src/redis.c b/src/redis.c index e24abd54..37dc8d68 100644 --- a/src/redis.c +++ b/src/redis.c @@ -22,8 +22,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/routeros.c b/src/routeros.c index 1286805f..70dd75ee 100644 --- a/src/routeros.c +++ b/src/routeros.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/rrdcached.c b/src/rrdcached.c index 8b742bb5..b5f0970b 100644 --- a/src/rrdcached.c +++ b/src/rrdcached.c @@ -26,9 +26,9 @@ #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 diff --git a/src/rrdtool.c b/src/rrdtool.c index 605a16cc..d0849d12 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -25,11 +25,11 @@ #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 diff --git a/src/sensors.c b/src/sensors.c index b800e98f..8d1ece9a 100644 --- a/src/sensors.c +++ b/src/sensors.c @@ -35,9 +35,9 @@ #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 diff --git a/src/serial.c b/src/serial.c index 2b865370..aae99789 100644 --- a/src/serial.c +++ b/src/serial.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/sigrok.c b/src/sigrok.c index eeab8c95..a8b67afe 100644 --- a/src/sigrok.c +++ b/src/sigrok.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/smart.c b/src/smart.c index 62cbb4f1..2dfb924f 100644 --- a/src/smart.c +++ b/src/smart.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include diff --git a/src/snmp.c b/src/snmp.c index fb1b83c3..c921e028 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -26,10 +26,10 @@ #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 #include diff --git a/src/snmp_agent.c b/src/snmp_agent.c index 2dfa6613..cbd33664 100644 --- a/src/snmp_agent.c +++ b/src/snmp_agent.c @@ -29,8 +29,8 @@ #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 diff --git a/src/statsd.c b/src/statsd.c index 1558ec84..6c7820a1 100644 --- a/src/statsd.c +++ b/src/statsd.c @@ -26,10 +26,10 @@ #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 #include diff --git a/src/swap.c b/src/swap.c index 929df6db..9e58919e 100644 --- a/src/swap.c +++ b/src/swap.c @@ -37,8 +37,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYS_SWAP_H #include diff --git a/src/synproxy.c b/src/synproxy.c index 6c6b997d..d51077f2 100644 --- a/src/synproxy.c +++ b/src/synproxy.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/syslog.c b/src/syslog.c index beb82456..a600f30e 100644 --- a/src/syslog.c +++ b/src/syslog.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYSLOG_H #include diff --git a/src/table.c b/src/table.c index 492bea61..f181de94 100644 --- a/src/table.c +++ b/src/table.c @@ -30,7 +30,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "plugin.h" diff --git a/src/tail.c b/src/tail.c index df94580c..8c9dfbe5 100644 --- a/src/tail.c +++ b/src/tail.c @@ -26,9 +26,9 @@ #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" /* diff --git a/src/tail_csv.c b/src/tail_csv.c index 4c589716..e1d473eb 100644 --- a/src/tail_csv.c +++ b/src/tail_csv.c @@ -23,9 +23,9 @@ #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 #include diff --git a/src/tape.c b/src/tape.c index 26bd969a..ccf88255 100644 --- a/src/tape.c +++ b/src/tape.c @@ -22,8 +22,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !HAVE_LIBKSTAT #error "No applicable input method." diff --git a/src/target_notification.c b/src/target_notification.c index f83a904a..6d8059ea 100644 --- a/src/target_notification.c +++ b/src/target_notification.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_subst.h" diff --git a/src/target_replace.c b/src/target_replace.c index 887507e1..62928c09 100644 --- a/src/target_replace.c +++ b/src/target_replace.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_subst.h" #include diff --git a/src/target_scale.c b/src/target_scale.c index 1cc5d793..4a9e9df4 100644 --- a/src/target_scale.c +++ b/src/target_scale.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_cache.h" diff --git a/src/target_set.c b/src/target_set.c index 9629e0ed..4bbd2c0d 100644 --- a/src/target_set.c +++ b/src/target_set.c @@ -26,9 +26,9 @@ #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 { diff --git a/src/target_v5upgrade.c b/src/target_v5upgrade.c index 650f9a5e..c2fe1922 100644 --- a/src/target_v5upgrade.c +++ b/src/target_v5upgrade.c @@ -26,9 +26,9 @@ #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) /* {{{ */ { diff --git a/src/tcpconns.c b/src/tcpconns.c index 5a3fd4ef..d01bcd23 100644 --- a/src/tcpconns.c +++ b/src/tcpconns.c @@ -59,8 +59,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if defined(__OpenBSD__) #define HAVE_KVM_GETFILES 1 diff --git a/src/teamspeak2.c b/src/teamspeak2.c index 567e5fb8..1ab6e4bf 100644 --- a/src/teamspeak2.c +++ b/src/teamspeak2.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/ted.c b/src/ted.c index b5fa4c1e..e1b48fd6 100644 --- a/src/ted.c +++ b/src/ted.c @@ -36,8 +36,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H #include diff --git a/src/thermal.c b/src/thermal.c index 959fec64..5f448363 100644 --- a/src/thermal.c +++ b/src/thermal.c @@ -21,9 +21,9 @@ #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." diff --git a/src/threshold.c b/src/threshold.c index 9d343630..a3d865ba 100644 --- a/src/threshold.c +++ b/src/threshold.c @@ -25,9 +25,9 @@ #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" diff --git a/src/tokyotyrant.c b/src/tokyotyrant.c index aca0a4e3..59b7a342 100644 --- a/src/tokyotyrant.c +++ b/src/tokyotyrant.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include diff --git a/src/turbostat.c b/src/turbostat.c index 4a7af4c7..deb16e0c 100644 --- a/src/turbostat.c +++ b/src/turbostat.c @@ -37,8 +37,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_time.h" #include "msr-index.h" diff --git a/src/unixsock.c b/src/unixsock.c index 8c08e18a..6ff54997 100644 --- a/src/unixsock.c +++ b/src/unixsock.c @@ -26,15 +26,15 @@ #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 #include diff --git a/src/uptime.c b/src/uptime.c index 43d72e53..dd33ab38 100644 --- a/src/uptime.c +++ b/src/uptime.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if KERNEL_LINUX #include diff --git a/src/users.c b/src/users.c index 6bc7cc34..fc03ba2c 100644 --- a/src/users.c +++ b/src/users.c @@ -27,8 +27,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_STATGRAB_H #include diff --git a/src/utils/avltree/avltree.c b/src/utils/avltree/avltree.c new file mode 100644 index 00000000..af6efed9 --- /dev/null +++ b/src/utils/avltree/avltree.c @@ -0,0 +1,648 @@ +/** + * 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 + **/ + +#include +#include + +#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; +} diff --git a/src/utils/avltree/avltree.h b/src/utils/avltree/avltree.h new file mode 100644 index 00000000..3f52b931 --- /dev/null +++ b/src/utils/avltree/avltree.h @@ -0,0 +1,169 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/avltree/avltree_test.c b/src/utils/avltree/avltree_test.c new file mode 100644 index 00000000..8cbcb13c --- /dev/null +++ b/src/utils/avltree/avltree_test.c @@ -0,0 +1,180 @@ +/** + * 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 + */ + +#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; +} diff --git a/src/utils/cmds/cmds.c b/src/utils/cmds/cmds.c new file mode 100644 index 00000000..036cefa1 --- /dev/null +++ b/src/utils/cmds/cmds.c @@ -0,0 +1,308 @@ +/** + * 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 + * Sebastian 'tokkee' Harl + **/ + +#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 +#include + +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 */ diff --git a/src/utils/cmds/cmds.h b/src/utils/cmds/cmds.h new file mode 100644 index 00000000..f3882f54 --- /dev/null +++ b/src/utils/cmds/cmds.h @@ -0,0 +1,209 @@ +/** + * 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 + **/ + +#ifndef UTILS_CMDS_H +#define UTILS_CMDS_H 1 + +#include "plugin.h" + +#include + +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 */ diff --git a/src/utils/cmds/cmds_test.c b/src/utils/cmds/cmds_test.c new file mode 100644 index 00000000..713f0324 --- /dev/null +++ b/src/utils/cmds/cmds_test.c @@ -0,0 +1,224 @@ +/** + * 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 + **/ + +#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; +} diff --git a/src/utils/cmds/flush.c b/src/utils/cmds/flush.c new file mode 100644 index 00000000..d080d481 --- /dev/null +++ b/src/utils/cmds/flush.c @@ -0,0 +1,177 @@ +/** + * 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 + * Florian "octo" Forster + **/ + +#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 */ diff --git a/src/utils/cmds/flush.h b/src/utils/cmds/flush.h new file mode 100644 index 00000000..794ac1c9 --- /dev/null +++ b/src/utils/cmds/flush.h @@ -0,0 +1,42 @@ +/** + * 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 + **/ + +#ifndef UTILS_CMD_FLUSH_H +#define UTILS_CMD_FLUSH_H 1 + +#include "utils/cmds/cmds.h" + +#include + +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 */ diff --git a/src/utils/cmds/getthreshold.c b/src/utils/cmds/getthreshold.c new file mode 100644 index 00000000..0d4e2f1f --- /dev/null +++ b/src/utils/cmds/getthreshold.c @@ -0,0 +1,182 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/cmds/getthreshold.h b/src/utils/cmds/getthreshold.h new file mode 100644 index 00000000..78700ee2 --- /dev/null +++ b/src/utils/cmds/getthreshold.h @@ -0,0 +1,34 @@ +/** + * 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 + **/ + +#ifndef UTILS_CMD_GETTHRESHOLD_H +#define UTILS_CMD_GETTHRESHOLD_H 1 + +#include + +int handle_getthreshold(FILE *fh, char *buffer); + +#endif /* UTILS_CMD_GETTHRESHOLD_H */ diff --git a/src/utils/cmds/getval.c b/src/utils/cmds/getval.c new file mode 100644 index 00000000..f2586e12 --- /dev/null +++ b/src/utils/cmds/getval.c @@ -0,0 +1,165 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/cmds/getval.h b/src/utils/cmds/getval.h new file mode 100644 index 00000000..f2f1c2eb --- /dev/null +++ b/src/utils/cmds/getval.h @@ -0,0 +1,43 @@ +/** + * 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 + **/ + +#ifndef UTILS_CMD_GETVAL_H +#define UTILS_CMD_GETVAL_H 1 + +#include + +#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 */ diff --git a/src/utils/cmds/listval.c b/src/utils/cmds/listval.c new file mode 100644 index 00000000..287786bb --- /dev/null +++ b/src/utils/cmds/listval.c @@ -0,0 +1,103 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/cmds/listval.h b/src/utils/cmds/listval.h new file mode 100644 index 00000000..6fff019e --- /dev/null +++ b/src/utils/cmds/listval.h @@ -0,0 +1,40 @@ +/** + * 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 + **/ + +#ifndef UTILS_CMD_LISTVAL_H +#define UTILS_CMD_LISTVAL_H 1 + +#include + +#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 */ diff --git a/src/utils/cmds/parse_option.c b/src/utils/cmds/parse_option.c new file mode 100644 index 00000000..e6b516d6 --- /dev/null +++ b/src/utils/cmds/parse_option.c @@ -0,0 +1,148 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/cmds/parse_option.h b/src/utils/cmds/parse_option.h new file mode 100644 index 00000000..3dd0a792 --- /dev/null +++ b/src/utils/cmds/parse_option.h @@ -0,0 +1,33 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/cmds/putnotif.c b/src/utils/cmds/putnotif.c new file mode 100644 index 00000000..e1fecf84 --- /dev/null +++ b/src/utils/cmds/putnotif.c @@ -0,0 +1,181 @@ +/** + * 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 + **/ + +#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: : */ + 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 */ diff --git a/src/utils/cmds/putnotif.h b/src/utils/cmds/putnotif.h new file mode 100644 index 00000000..7ad0f1a5 --- /dev/null +++ b/src/utils/cmds/putnotif.h @@ -0,0 +1,34 @@ +/** + * 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 + **/ + +#ifndef UTILS_CMD_PUTNOTIF_H +#define UTILS_CMD_PUTNOTIF_H 1 + +#include + +int handle_putnotif(FILE *fh, char *buffer); + +#endif /* UTILS_CMD_PUTNOTIF_H */ diff --git a/src/utils/cmds/putval.c b/src/utils/cmds/putval.c new file mode 100644 index 00000000..b6d5ccc6 --- /dev/null +++ b/src/utils/cmds/putval.c @@ -0,0 +1,285 @@ +/** + * 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 + * Sebastian tokkee Harl + **/ + +#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 */ diff --git a/src/utils/cmds/putval.h b/src/utils/cmds/putval.h new file mode 100644 index 00000000..f6c3e3b8 --- /dev/null +++ b/src/utils/cmds/putval.h @@ -0,0 +1,47 @@ +/** + * 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 + **/ + +#ifndef UTILS_CMD_PUTVAL_H +#define UTILS_CMD_PUTVAL_H 1 + +#include "plugin.h" +#include "utils/cmds/cmds.h" + +#include + +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 */ diff --git a/src/utils/common/common.c b/src/utils/common/common.c new file mode 100644 index 00000000..d15f9b7c --- /dev/null +++ b/src/utils/common/common.c @@ -0,0 +1,1589 @@ +/** + * 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 + * Niki W. Waibel + * Sebastian Harl + * Michał Mirosław +**/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils_cache.h" + +/* for getaddrinfo */ +#include +#include + +#include + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_NETINET_TCP_H +#include +#endif + +/* for ntohl and htonl */ +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_CAPABILITY +#include +#endif + +#if HAVE_KSTAT_H +#include +#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 */ diff --git a/src/utils/common/common.h b/src/utils/common/common.h new file mode 100644 index 00000000..addf06d3 --- /dev/null +++ b/src/utils/common/common.h @@ -0,0 +1,396 @@ +/** + * 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 + * Niki W. Waibel +**/ + +#ifndef COMMON_H +#define COMMON_H + +#include "collectd.h" + +#include "plugin.h" + +#if HAVE_PWD_H +#include +#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 -> + * \n -> + * \r -> + * + * 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 +#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 */ diff --git a/src/utils/common/common_test.c b/src/utils/common/common_test.c new file mode 100644 index 00000000..426082fa --- /dev/null +++ b/src/utils/common/common_test.c @@ -0,0 +1,378 @@ +/** + * 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 + */ + +#include "testing.h" +#include "utils/common/common.h" + +#if HAVE_KSTAT_H +#include +#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; +} diff --git a/src/utils/config_cores/config_cores.c b/src/utils/config_cores/config_cores.c new file mode 100644 index 00000000..13ea6878 --- /dev/null +++ b/src/utils/config_cores/config_cores.c @@ -0,0 +1,372 @@ +/** + * 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 + **/ + +#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; +} diff --git a/src/utils/config_cores/config_cores.h b/src/utils/config_cores/config_cores.h new file mode 100644 index 00000000..d45f8480 --- /dev/null +++ b/src/utils/config_cores/config_cores.h @@ -0,0 +1,136 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/config_cores/config_cores_test.c b/src/utils/config_cores/config_cores_test.c new file mode 100644 index 00000000..8b4f4c4c --- /dev/null +++ b/src/utils/config_cores/config_cores_test.c @@ -0,0 +1,249 @@ +/** + * 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 + **/ + +#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; +} diff --git a/src/utils/crc32/crc32.c b/src/utils/crc32/crc32.c new file mode 100644 index 00000000..afc566a9 --- /dev/null +++ b/src/utils/crc32/crc32.c @@ -0,0 +1,107 @@ +/* + * 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 +#include + +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; +} diff --git a/src/utils/crc32/crc32.h b/src/utils/crc32/crc32.h new file mode 100644 index 00000000..8e2c2126 --- /dev/null +++ b/src/utils/crc32/crc32.h @@ -0,0 +1,32 @@ +/** + * 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 + */ + +#ifndef UTILS_CRC32_H +#define UTILS_CRC32_H 1 + +uint32_t crc32_buffer(const unsigned char *, size_t); + +#endif diff --git a/src/utils/curl_stats/curl_stats.c b/src/utils/curl_stats/curl_stats.c new file mode 100644 index 00000000..a8971719 --- /dev/null +++ b/src/utils/curl_stats/curl_stats.c @@ -0,0 +1,252 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include "utils/common/common.h" +#include "utils/curl_stats/curl_stats.h" + +#include +#include + +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 ? "" : 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 */ diff --git a/src/utils/curl_stats/curl_stats.h b/src/utils/curl_stats/curl_stats.h new file mode 100644 index 00000000..3f83aab9 --- /dev/null +++ b/src/utils/curl_stats/curl_stats.h @@ -0,0 +1,56 @@ +/** + * 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 + **/ + +#ifndef UTILS_CURL_STATS_H +#define UTILS_CURL_STATS_H 1 + +#include "plugin.h" + +#include + +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 */ diff --git a/src/utils/db_query/db_query.c b/src/utils/db_query/db_query.c new file mode 100644 index 00000000..392bd56f --- /dev/null +++ b/src/utils/db_query/db_query.c @@ -0,0 +1,1036 @@ +/** + * 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 + **/ + +#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 " + "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 */ diff --git a/src/utils/db_query/db_query.h b/src/utils/db_query/db_query.h new file mode 100644 index 00000000..f173204c --- /dev/null +++ b/src/utils/db_query/db_query.h @@ -0,0 +1,85 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/deq/deq.h b/src/utils/deq/deq.h new file mode 100644 index 00000000..3182baae --- /dev/null +++ b/src/utils/deq/deq.h @@ -0,0 +1,214 @@ +/** + * 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 + */ + +#ifndef utils_deq_h +#define utils_deq_h 1 + +#include +#include +#include + +#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 diff --git a/src/utils/dns/dns.c b/src/utils/dns/dns.c new file mode 100644 index 00000000..2ea919b7 --- /dev/null +++ b/src/utils/dns/dns.c @@ -0,0 +1,1171 @@ +/* + * 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. + * Florian octo Forster + */ + +#define _DEFAULT_SOURCE +#define _BSD_SOURCE + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#if HAVE_NET_IF_ARP_H +#include +#endif +#if HAVE_NET_IF_H +#include +#endif +#if HAVE_NET_PPP_DEFS_H +#include +#endif +#if HAVE_NET_IF_PPP_H +#include +#endif + +#if HAVE_NETINET_IN_SYSTM_H +#include +#endif +#if HAVE_NETINET_IN_H +#include +#endif +#if HAVE_NETINET_IP6_H +#include +#endif +#if HAVE_NETINET_IF_ETHER_H +#include +#endif +#if HAVE_NETINET_IP_H +#include +#endif +#ifdef HAVE_NETINET_IP_VAR_H +#include +#endif +#if HAVE_NETINET_UDP_H +#include +#endif + +#if HAVE_ARPA_INET_H +#include +#endif +#if HAVE_ARPA_NAMESER_H +#include +#endif +#if HAVE_ARPA_NAMESER_COMPAT_H +#include +#endif + +#if HAVE_NETDB_H +#include +#endif + +#if HAVE_PCAP_H +#include +#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 diff --git a/src/utils/dns/dns.h b/src/utils/dns/dns.h new file mode 100644 index 00000000..9d9b75fd --- /dev/null +++ b/src/utils/dns/dns.h @@ -0,0 +1,91 @@ +/* + * 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. + * Florian octo Forster + */ + +#ifndef COLLECTD_UTILS_DNS_H +#define COLLECTD_UTILS_DNS_H 1 + +#include "config.h" + +#include +#include + +#if HAVE_PCAP_H +#include +#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 */ diff --git a/src/utils/dpdk/dpdk.c b/src/utils/dpdk/dpdk.c new file mode 100644 index 00000000..5e38ab36 --- /dev/null +++ b/src/utils/dpdk/dpdk.c @@ -0,0 +1,885 @@ +/* + * 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 + * Harry van Haaren + * Taras Chornyi + * Serhiy Pshyk + * Krzysztof Matczak + */ + +#include "collectd.h" + +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/src/utils/dpdk/dpdk.h b/src/utils/dpdk/dpdk.h new file mode 100644 index 00000000..d4551d86 --- /dev/null +++ b/src/utils/dpdk/dpdk.h @@ -0,0 +1,90 @@ +/* + * 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 + * Harry van Haaren + * Taras Chornyi + * Serhiy Pshyk + * Krzysztof Matczak + */ + +#ifndef UTILS_DPDK_H +#define UTILS_DPDK_H + +#include + +#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 */ diff --git a/src/utils/format_graphite/format_graphite.c b/src/utils/format_graphite/format_graphite.c new file mode 100644 index 00000000..d0e047ff --- /dev/null +++ b/src/utils/format_graphite/format_graphite.c @@ -0,0 +1,335 @@ +/** + * 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 + * Florian octo Forster + **/ + +#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 */ diff --git a/src/utils/format_graphite/format_graphite.h b/src/utils/format_graphite/format_graphite.h new file mode 100644 index 00000000..60b89ae7 --- /dev/null +++ b/src/utils/format_graphite/format_graphite.h @@ -0,0 +1,41 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/format_graphite/format_graphite_test.c b/src/utils/format_graphite/format_graphite_test.c new file mode 100644 index 00000000..2c14d010 --- /dev/null +++ b/src/utils/format_graphite/format_graphite_test.c @@ -0,0 +1,207 @@ +/** + * 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 + */ + +#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; +} diff --git a/src/utils/format_json/format_json.c b/src/utils/format_json/format_json.c new file mode 100644 index 00000000..b82f21dc --- /dev/null +++ b/src/utils/format_json/format_json.c @@ -0,0 +1,696 @@ +/** + * 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 + **/ + +#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 +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#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": , + * "endsAt": , // 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 diff --git a/src/utils/format_json/format_json.h b/src/utils/format_json/format_json.h new file mode 100644 index 00000000..d3d02160 --- /dev/null +++ b/src/utils/format_json/format_json.h @@ -0,0 +1,48 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/format_json/format_json_test.c b/src/utils/format_json/format_json_test.c new file mode 100644 index 00000000..d0416049 --- /dev/null +++ b/src/utils/format_json/format_json_test.c @@ -0,0 +1,183 @@ +/** + * 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 + */ + +/* 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 +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#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; +} diff --git a/src/utils/format_kairosdb/format_kairosdb.c b/src/utils/format_kairosdb/format_kairosdb.c new file mode 100644 index 00000000..3f29fcd9 --- /dev/null +++ b/src/utils/format_kairosdb/format_kairosdb.c @@ -0,0 +1,358 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/format_kairosdb/format_kairosdb.h b/src/utils/format_kairosdb/format_kairosdb.h new file mode 100644 index 00000000..7b9e0e7a --- /dev/null +++ b/src/utils/format_kairosdb/format_kairosdb.h @@ -0,0 +1,49 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/format_stackdriver/format_stackdriver.c b/src/utils/format_stackdriver/format_stackdriver.c new file mode 100644 index 00000000..80b85ae4 --- /dev/null +++ b/src/utils/format_stackdriver/format_stackdriver.c @@ -0,0 +1,766 @@ +/** + * 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 + **/ + +#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 +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#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 */ diff --git a/src/utils/format_stackdriver/format_stackdriver.h b/src/utils/format_stackdriver/format_stackdriver.h new file mode 100644 index 00000000..fee260e3 --- /dev/null +++ b/src/utils/format_stackdriver/format_stackdriver.h @@ -0,0 +1,79 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/format_stackdriver/format_stackdriver_test.c b/src/utils/format_stackdriver/format_stackdriver_test.c new file mode 100644 index 00000000..d4935a31 --- /dev/null +++ b/src/utils/format_stackdriver/format_stackdriver_test.c @@ -0,0 +1,77 @@ +/** + * 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 + **/ + +#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; +} diff --git a/src/utils/gce/gce.c b/src/utils/gce/gce.c new file mode 100644 index 00000000..80927656 --- /dev/null +++ b/src/utils/gce/gce.c @@ -0,0 +1,284 @@ +/** + * 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 + **/ + +#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 + +#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 */ diff --git a/src/utils/gce/gce.h b/src/utils/gce/gce.h new file mode 100644 index 00000000..2ee3f6eb --- /dev/null +++ b/src/utils/gce/gce.h @@ -0,0 +1,52 @@ +/** + * 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 + **/ + +#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 diff --git a/src/utils/heap/heap.c b/src/utils/heap/heap.c new file mode 100644 index 00000000..2fe2bcb7 --- /dev/null +++ b/src/utils/heap/heap.c @@ -0,0 +1,207 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include +#include +#include +#include + +#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 */ diff --git a/src/utils/heap/heap.h b/src/utils/heap/heap.h new file mode 100644 index 00000000..d2d70cdc --- /dev/null +++ b/src/utils/heap/heap.h @@ -0,0 +1,99 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/heap/heap_test.c b/src/utils/heap/heap_test.c new file mode 100644 index 00000000..169824e7 --- /dev/null +++ b/src/utils/heap/heap_test.c @@ -0,0 +1,78 @@ +/** + * 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 + */ + +#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; +} diff --git a/src/utils/ignorelist/ignorelist.c b/src/utils/ignorelist/ignorelist.c new file mode 100644 index 00000000..9e1b9e3a --- /dev/null +++ b/src/utils/ignorelist/ignorelist.c @@ -0,0 +1,309 @@ +/** + * collectd - src/utils_ignorelist.c + * Copyright (C) 2006 Lubos Stanek + * Copyright (C) 2008 Florian Forster + * + * 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 + * Florian Forster + **/ +/** + * 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) */ diff --git a/src/utils/ignorelist/ignorelist.h b/src/utils/ignorelist/ignorelist.h new file mode 100644 index 00000000..a7fa86d5 --- /dev/null +++ b/src/utils/ignorelist/ignorelist.h @@ -0,0 +1,69 @@ +/** + * collectd - src/utils_ignorelist.h + * Copyright (C) 2006 Lubos Stanek + * + * 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 + **/ +/** + * 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 +#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 */ diff --git a/src/utils/latency/latency.c b/src/utils/latency/latency.c new file mode 100644 index 00000000..12ff2ca1 --- /dev/null +++ b/src/utils/latency/latency.c @@ -0,0 +1,344 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils/latency/latency.h" + +#include +#include + +#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 */ diff --git a/src/utils/latency/latency.h b/src/utils/latency/latency.h new file mode 100644 index 00000000..9d878dab --- /dev/null +++ b/src/utils/latency/latency.h @@ -0,0 +1,63 @@ +/** + * 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 + **/ + +#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); diff --git a/src/utils/latency/latency_config.c b/src/utils/latency/latency_config.c new file mode 100644 index 00000000..a5ae4719 --- /dev/null +++ b/src/utils/latency/latency_config.c @@ -0,0 +1,157 @@ +/** + * 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 + * Pavel Rochnyack + */ + +#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 */ diff --git a/src/utils/latency/latency_config.h b/src/utils/latency/latency_config.h new file mode 100644 index 00000000..3d2691a6 --- /dev/null +++ b/src/utils/latency/latency_config.h @@ -0,0 +1,62 @@ +/** + * 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 + * Pavel Rochnyack + */ + +#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 */ diff --git a/src/utils/latency/latency_test.c b/src/utils/latency/latency_test.c new file mode 100644 index 00000000..13250174 --- /dev/null +++ b/src/utils/latency/latency_test.c @@ -0,0 +1,234 @@ +/** + * 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 + */ + +#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; +} diff --git a/src/utils/lookup/vl_lookup.c b/src/utils/lookup/vl_lookup.c new file mode 100644 index 00000000..38d81ca4 --- /dev/null +++ b/src/utils/lookup/vl_lookup.c @@ -0,0 +1,630 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include +#include + +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils/lookup/vl_lookup.h" + +#if HAVE_KSTAT_H +#include +#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 */ diff --git a/src/utils/lookup/vl_lookup.h b/src/utils/lookup/vl_lookup.h new file mode 100644 index 00000000..90a4ee52 --- /dev/null +++ b/src/utils/lookup/vl_lookup.h @@ -0,0 +1,87 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/lookup/vl_lookup_test.c b/src/utils/lookup/vl_lookup_test.c new file mode 100644 index 00000000..2dd53ce3 --- /dev/null +++ b/src/utils/lookup/vl_lookup_test.c @@ -0,0 +1,242 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/match/match.c b/src/utils/match/match.c new file mode 100644 index 00000000..ca6f1aaa --- /dev/null +++ b/src/utils/match/match.c @@ -0,0 +1,376 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/match/match.h" + +#include + +#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 */ diff --git a/src/utils/match/match.h b/src/utils/match/match.h new file mode 100644 index 00000000..c4aee0a1 --- /dev/null +++ b/src/utils/match/match.h @@ -0,0 +1,179 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/metadata/meta_data.c b/src/utils/metadata/meta_data.c new file mode 100644 index 00000000..963aebbe --- /dev/null +++ b/src/utils/metadata/meta_data.c @@ -0,0 +1,747 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/metadata/meta_data.h b/src/utils/metadata/meta_data.h new file mode 100644 index 00000000..203b1460 --- /dev/null +++ b/src/utils/metadata/meta_data.h @@ -0,0 +1,71 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/metadata/meta_data_test.c b/src/utils/metadata/meta_data_test.c new file mode 100644 index 00000000..b9ff86d5 --- /dev/null +++ b/src/utils/metadata/meta_data_test.c @@ -0,0 +1,117 @@ +/** + * 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 + */ + +#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; +} diff --git a/src/utils/mount/mount.c b/src/utils/mount/mount.c new file mode 100644 index 00000000..319c6248 --- /dev/null +++ b/src/utils/mount/mount.c @@ -0,0 +1,765 @@ +/** + * 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 +**/ + +#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 +#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 +#endif +#if HAVE_SYS_STATVFS_H +#include +#endif +/* #endif HAVE_GETVFSSTAT */ + +#elif HAVE_GETFSSTAT +#if HAVE_SYS_PARAM_H +#include +#endif +#if HAVE_SYS_UCRED_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#endif +#endif /* HAVE_GETFSSTAT */ + +#if HAVE_MNTENT_H +#include +#endif +#if HAVE_SYS_MNTTAB_H +#include +#endif + +#if HAVE_PATHS_H +#include +#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) */ diff --git a/src/utils/mount/mount.h b/src/utils/mount/mount.h new file mode 100644 index 00000000..0ad7d020 --- /dev/null +++ b/src/utils/mount/mount.h @@ -0,0 +1,186 @@ +/** + * 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 +**/ + +/* See below for instructions how to use the public functions. */ + +#ifndef COLLECTD_UTILS_MOUNT_H +#define COLLECTD_UTILS_MOUNT_H 1 + +#include +#if HAVE_FS_INFO_H +#include +#endif +#if HAVE_FSHELP_H +#include +#endif +#if HAVE_PATHS_H +#include +#endif +#if HAVE_MNTENT_H +#include +#endif +#if HAVE_MNTTAB_H +#include +#endif +#if HAVE_SYS_FSTYP_H +#include +#endif +#if HAVE_SYS_FS_TYPES_H +#include +#endif +#if HAVE_SYS_MNTENT_H +#include +#endif +#if HAVE_SYS_MNTTAB_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#endif +#if HAVE_SYS_STATFS_H +#include +#endif +#if HAVE_SYS_VFS_H +#include +#endif +#if HAVE_SYS_VFSTAB_H +#include +#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 */ diff --git a/src/utils/mount/mount_test.c b/src/utils/mount/mount_test.c new file mode 100644 index 00000000..e34e677c --- /dev/null +++ b/src/utils/mount/mount_test.c @@ -0,0 +1,115 @@ +/** + * 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 + */ + +#include "collectd.h" + +#include "testing.h" +#include "utils/common/common.h" +#include "utils/mount/mount.h" + +#if HAVE_KSTAT_H +#include +#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; +} diff --git a/src/utils/oauth/oauth.c b/src/utils/oauth/oauth.c new file mode 100644 index 00000000..671de84d --- /dev/null +++ b/src/utils/oauth/oauth.c @@ -0,0 +1,637 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils/oauth/oauth.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* + * 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 */ diff --git a/src/utils/oauth/oauth.h b/src/utils/oauth/oauth.h new file mode 100644 index 00000000..b93c87b8 --- /dev/null +++ b/src/utils/oauth/oauth.h @@ -0,0 +1,66 @@ +/** + * 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 + **/ + +#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 diff --git a/src/utils/oauth/oauth_test.c b/src/utils/oauth/oauth_test.c new file mode 100644 index 00000000..aa6e99a3 --- /dev/null +++ b/src/utils/oauth/oauth_test.c @@ -0,0 +1,149 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils/ovs/ovs.c b/src/utils/ovs/ovs.c new file mode 100644 index 00000000..46c2c269 --- /dev/null +++ b/src/utils/ovs/ovs.c @@ -0,0 +1,1412 @@ +/** + * 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 + **/ + +/* 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 +#endif +#if HAVE_ARPA_INET_H +#include +#endif +#if HAVE_POLL_H +#include +#endif +#if HAVE_SYS_UN_H +#include +#endif + +#include + +#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: [, ] */ + 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 */ + 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 + * [, , ] */ + OVS_YAJL_CALL(yajl_gen_array_open, jgen); + { + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME); + + /* uid string */ + snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid); + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str); + + /* */ + 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); + { + /* */ + 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" : + * "key_b" : + * } + */ +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: + * + * + * A 2-element JSON array that represents a pair within a database + * map. The first element is an that represents the key, and + * the second element is an that represents the value. + * + * + * 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 s giving the + * values in the map. All of the s must have the same key and + * value types. + * + * EXAMPLE: + * [ + * "map", [ + * [ "key_a", ], [ "key_b", ], ... + * ] + * ] + */ +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; +} diff --git a/src/utils/ovs/ovs.h b/src/utils/ovs/ovs.h new file mode 100644 index 00000000..c93322ea --- /dev/null +++ b/src/utils/ovs/ovs.h @@ -0,0 +1,236 @@ +/** + * 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 + * + * 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 +#include + +/* 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 diff --git a/src/utils/rrdcreate/rrdcreate.c b/src/utils/rrdcreate/rrdcreate.c new file mode 100644 index 00000000..ef012343 --- /dev/null +++ b/src/utils/rrdcreate/rrdcreate.c @@ -0,0 +1,660 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include "utils/common/common.h" +#include "utils/rrdcreate/rrdcreate.h" + +#include +#include + +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 */ diff --git a/src/utils/rrdcreate/rrdcreate.h b/src/utils/rrdcreate/rrdcreate.h new file mode 100644 index 00000000..b2277e75 --- /dev/null +++ b/src/utils/rrdcreate/rrdcreate.h @@ -0,0 +1,53 @@ +/** + * 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 + **/ + +#ifndef UTILS_RRDCREATE_H +#define UTILS_RRDCREATE_H 1 + +#include "plugin.h" + +#include + +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 */ diff --git a/src/utils/tail/tail.c b/src/utils/tail/tail.c new file mode 100644 index 00000000..db34a72b --- /dev/null +++ b/src/utils/tail/tail.c @@ -0,0 +1,224 @@ +/** + * 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 + * Florian Forster + * + * 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 */ diff --git a/src/utils/tail/tail.h b/src/utils/tail/tail.h new file mode 100644 index 00000000..73a6de21 --- /dev/null +++ b/src/utils/tail/tail.h @@ -0,0 +1,88 @@ +/** + * 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 + * + * 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 */ diff --git a/src/utils/taskstats/taskstats.c b/src/utils/taskstats/taskstats.c new file mode 100644 index 00000000..b020c06c --- /dev/null +++ b/src/utils/taskstats/taskstats.c @@ -0,0 +1,306 @@ +/** + * 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 + */ + +#include "collectd.h" +#include "utils/taskstats/taskstats.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils_time.h" + +#include +#include +#include + +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; +} diff --git a/src/utils/taskstats/taskstats.h b/src/utils/taskstats/taskstats.h new file mode 100644 index 00000000..de07427c --- /dev/null +++ b/src/utils/taskstats/taskstats.h @@ -0,0 +1,47 @@ +/** + * 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 + */ + +#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 */ diff --git a/src/utils_cmd_flush.c b/src/utils_cmd_flush.c deleted file mode 100644 index b180a501..00000000 --- a/src/utils_cmd_flush.c +++ /dev/null @@ -1,177 +0,0 @@ -/** - * 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 - * Florian "octo" Forster - **/ - -#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 */ diff --git a/src/utils_cmd_flush.h b/src/utils_cmd_flush.h deleted file mode 100644 index 129aa85e..00000000 --- a/src/utils_cmd_flush.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CMD_FLUSH_H -#define UTILS_CMD_FLUSH_H 1 - -#include "utils_cmds.h" - -#include - -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 */ diff --git a/src/utils_cmd_getthreshold.c b/src/utils_cmd_getthreshold.c deleted file mode 100644 index c1f3f562..00000000 --- a/src/utils_cmd_getthreshold.c +++ /dev/null @@ -1,182 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_cmd_getthreshold.h b/src/utils_cmd_getthreshold.h deleted file mode 100644 index 78700ee2..00000000 --- a/src/utils_cmd_getthreshold.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CMD_GETTHRESHOLD_H -#define UTILS_CMD_GETTHRESHOLD_H 1 - -#include - -int handle_getthreshold(FILE *fh, char *buffer); - -#endif /* UTILS_CMD_GETTHRESHOLD_H */ diff --git a/src/utils_cmd_getval.c b/src/utils_cmd_getval.c deleted file mode 100644 index f747d5b8..00000000 --- a/src/utils_cmd_getval.c +++ /dev/null @@ -1,165 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_cmd_getval.h b/src/utils_cmd_getval.h deleted file mode 100644 index 5c03fa4c..00000000 --- a/src/utils_cmd_getval.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CMD_GETVAL_H -#define UTILS_CMD_GETVAL_H 1 - -#include - -#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 */ diff --git a/src/utils_cmd_listval.c b/src/utils_cmd_listval.c deleted file mode 100644 index 24ec9421..00000000 --- a/src/utils_cmd_listval.c +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_cmd_listval.h b/src/utils_cmd_listval.h deleted file mode 100644 index 6dbaabcb..00000000 --- a/src/utils_cmd_listval.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CMD_LISTVAL_H -#define UTILS_CMD_LISTVAL_H 1 - -#include - -#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 */ diff --git a/src/utils_cmd_putnotif.c b/src/utils_cmd_putnotif.c deleted file mode 100644 index 75a8fae8..00000000 --- a/src/utils_cmd_putnotif.c +++ /dev/null @@ -1,181 +0,0 @@ -/** - * 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 - **/ - -#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: : */ - 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 */ diff --git a/src/utils_cmd_putnotif.h b/src/utils_cmd_putnotif.h deleted file mode 100644 index 7ad0f1a5..00000000 --- a/src/utils_cmd_putnotif.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CMD_PUTNOTIF_H -#define UTILS_CMD_PUTNOTIF_H 1 - -#include - -int handle_putnotif(FILE *fh, char *buffer); - -#endif /* UTILS_CMD_PUTNOTIF_H */ diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c deleted file mode 100644 index b5b9065b..00000000 --- a/src/utils_cmd_putval.c +++ /dev/null @@ -1,285 +0,0 @@ -/** - * 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 - * Sebastian tokkee Harl - **/ - -#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 */ diff --git a/src/utils_cmd_putval.h b/src/utils_cmd_putval.h deleted file mode 100644 index bc6e193e..00000000 --- a/src/utils_cmd_putval.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CMD_PUTVAL_H -#define UTILS_CMD_PUTVAL_H 1 - -#include "plugin.h" -#include "utils_cmds.h" - -#include - -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 */ diff --git a/src/utils_cmds.c b/src/utils_cmds.c deleted file mode 100644 index 88fdfc7f..00000000 --- a/src/utils_cmds.c +++ /dev/null @@ -1,308 +0,0 @@ -/** - * 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 - * Sebastian 'tokkee' Harl - **/ - -#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 -#include - -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 */ diff --git a/src/utils_cmds.h b/src/utils_cmds.h deleted file mode 100644 index f3882f54..00000000 --- a/src/utils_cmds.h +++ /dev/null @@ -1,209 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CMDS_H -#define UTILS_CMDS_H 1 - -#include "plugin.h" - -#include - -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 */ diff --git a/src/utils_cmds_test.c b/src/utils_cmds_test.c deleted file mode 100644 index 93bf5129..00000000 --- a/src/utils_cmds_test.c +++ /dev/null @@ -1,224 +0,0 @@ -/** - * 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 - **/ - -#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; -} diff --git a/src/utils_config_cores.c b/src/utils_config_cores.c deleted file mode 100644 index 94657459..00000000 --- a/src/utils_config_cores.c +++ /dev/null @@ -1,372 +0,0 @@ -/** - * 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 - **/ - -#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; -} diff --git a/src/utils_config_cores.h b/src/utils_config_cores.h deleted file mode 100644 index d45f8480..00000000 --- a/src/utils_config_cores.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_config_cores_test.c b/src/utils_config_cores_test.c deleted file mode 100644 index 2c6f5b60..00000000 --- a/src/utils_config_cores_test.c +++ /dev/null @@ -1,249 +0,0 @@ -/** - * 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 - **/ - -#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; -} diff --git a/src/utils_crc32.c b/src/utils_crc32.c deleted file mode 100644 index afc566a9..00000000 --- a/src/utils_crc32.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 -#include - -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; -} diff --git a/src/utils_crc32.h b/src/utils_crc32.h deleted file mode 100644 index 8e2c2126..00000000 --- a/src/utils_crc32.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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 - */ - -#ifndef UTILS_CRC32_H -#define UTILS_CRC32_H 1 - -uint32_t crc32_buffer(const unsigned char *, size_t); - -#endif diff --git a/src/utils_curl_stats.c b/src/utils_curl_stats.c deleted file mode 100644 index 09856599..00000000 --- a/src/utils_curl_stats.c +++ /dev/null @@ -1,252 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include "common.h" -#include "utils_curl_stats.h" - -#include -#include - -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 ? "" : 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 */ diff --git a/src/utils_curl_stats.h b/src/utils_curl_stats.h deleted file mode 100644 index 3f83aab9..00000000 --- a/src/utils_curl_stats.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_CURL_STATS_H -#define UTILS_CURL_STATS_H 1 - -#include "plugin.h" - -#include - -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 */ diff --git a/src/utils_db_query.c b/src/utils_db_query.c deleted file mode 100644 index 73c1b45e..00000000 --- a/src/utils_db_query.c +++ /dev/null @@ -1,1036 +0,0 @@ -/** - * 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 - **/ - -#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 " - "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 */ diff --git a/src/utils_db_query.h b/src/utils_db_query.h deleted file mode 100644 index f173204c..00000000 --- a/src/utils_db_query.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_deq.h b/src/utils_deq.h deleted file mode 100644 index 3182baae..00000000 --- a/src/utils_deq.h +++ /dev/null @@ -1,214 +0,0 @@ -/** - * 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 - */ - -#ifndef utils_deq_h -#define utils_deq_h 1 - -#include -#include -#include - -#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 diff --git a/src/utils_dns.c b/src/utils_dns.c deleted file mode 100644 index 7b20e139..00000000 --- a/src/utils_dns.c +++ /dev/null @@ -1,1171 +0,0 @@ -/* - * 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. - * Florian octo Forster - */ - -#define _DEFAULT_SOURCE -#define _BSD_SOURCE - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#if HAVE_NET_IF_ARP_H -#include -#endif -#if HAVE_NET_IF_H -#include -#endif -#if HAVE_NET_PPP_DEFS_H -#include -#endif -#if HAVE_NET_IF_PPP_H -#include -#endif - -#if HAVE_NETINET_IN_SYSTM_H -#include -#endif -#if HAVE_NETINET_IN_H -#include -#endif -#if HAVE_NETINET_IP6_H -#include -#endif -#if HAVE_NETINET_IF_ETHER_H -#include -#endif -#if HAVE_NETINET_IP_H -#include -#endif -#ifdef HAVE_NETINET_IP_VAR_H -#include -#endif -#if HAVE_NETINET_UDP_H -#include -#endif - -#if HAVE_ARPA_INET_H -#include -#endif -#if HAVE_ARPA_NAMESER_H -#include -#endif -#if HAVE_ARPA_NAMESER_COMPAT_H -#include -#endif - -#if HAVE_NETDB_H -#include -#endif - -#if HAVE_PCAP_H -#include -#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 diff --git a/src/utils_dns.h b/src/utils_dns.h deleted file mode 100644 index 9d9b75fd..00000000 --- a/src/utils_dns.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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. - * Florian octo Forster - */ - -#ifndef COLLECTD_UTILS_DNS_H -#define COLLECTD_UTILS_DNS_H 1 - -#include "config.h" - -#include -#include - -#if HAVE_PCAP_H -#include -#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 */ diff --git a/src/utils_dpdk.c b/src/utils_dpdk.c deleted file mode 100644 index 3591eae1..00000000 --- a/src/utils_dpdk.c +++ /dev/null @@ -1,885 +0,0 @@ -/* - * 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 - * Harry van Haaren - * Taras Chornyi - * Serhiy Pshyk - * Krzysztof Matczak - */ - -#include "collectd.h" - -#include -#include -#include - -#include -#include -#include - -#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; -} diff --git a/src/utils_dpdk.h b/src/utils_dpdk.h deleted file mode 100644 index d4551d86..00000000 --- a/src/utils_dpdk.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 - * Harry van Haaren - * Taras Chornyi - * Serhiy Pshyk - * Krzysztof Matczak - */ - -#ifndef UTILS_DPDK_H -#define UTILS_DPDK_H - -#include - -#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 */ diff --git a/src/utils_fbhash.c b/src/utils_fbhash.c index 07134635..7c342e53 100644 --- a/src/utils_fbhash.c +++ b/src/utils_fbhash.c @@ -28,7 +28,7 @@ #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" #include "utils_fbhash.h" struct fbhash_s { diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c deleted file mode 100644 index de3f0c2e..00000000 --- a/src/utils_format_graphite.c +++ /dev/null @@ -1,335 +0,0 @@ -/** - * 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 - * Florian octo Forster - **/ - -#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 */ diff --git a/src/utils_format_graphite.h b/src/utils_format_graphite.h deleted file mode 100644 index 60b89ae7..00000000 --- a/src/utils_format_graphite.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_format_graphite_test.c b/src/utils_format_graphite_test.c deleted file mode 100644 index 42efa681..00000000 --- a/src/utils_format_graphite_test.c +++ /dev/null @@ -1,207 +0,0 @@ -/** - * 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 - */ - -#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; -} diff --git a/src/utils_format_json.c b/src/utils_format_json.c deleted file mode 100644 index 25cbb0a7..00000000 --- a/src/utils_format_json.c +++ /dev/null @@ -1,696 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include "utils_format_json.h" - -#include "common.h" -#include "plugin.h" -#include "utils_cache.h" - -#if HAVE_LIBYAJL -#include -#include -#if HAVE_YAJL_YAJL_VERSION_H -#include -#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": , - * "endsAt": , // 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 diff --git a/src/utils_format_json.h b/src/utils_format_json.h deleted file mode 100644 index d3d02160..00000000 --- a/src/utils_format_json.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_format_json_test.c b/src/utils_format_json_test.c deleted file mode 100644 index b230ef35..00000000 --- a/src/utils_format_json_test.c +++ /dev/null @@ -1,183 +0,0 @@ -/** - * 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 - */ - -/* 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 -#include -#if HAVE_YAJL_YAJL_VERSION_H -#include -#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; -} diff --git a/src/utils_format_kairosdb.c b/src/utils_format_kairosdb.c deleted file mode 100644 index d957bc80..00000000 --- a/src/utils_format_kairosdb.c +++ /dev/null @@ -1,358 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_format_kairosdb.h b/src/utils_format_kairosdb.h deleted file mode 100644 index 7b9e0e7a..00000000 --- a/src/utils_format_kairosdb.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_format_stackdriver.c b/src/utils_format_stackdriver.c deleted file mode 100644 index afaa8ed8..00000000 --- a/src/utils_format_stackdriver.c +++ /dev/null @@ -1,766 +0,0 @@ -/** - * 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 - **/ - -#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 -#include -#if HAVE_YAJL_YAJL_VERSION_H -#include -#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 */ diff --git a/src/utils_format_stackdriver.h b/src/utils_format_stackdriver.h deleted file mode 100644 index fee260e3..00000000 --- a/src/utils_format_stackdriver.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_format_stackdriver_test.c b/src/utils_format_stackdriver_test.c deleted file mode 100644 index 1e96b65e..00000000 --- a/src/utils_format_stackdriver_test.c +++ /dev/null @@ -1,77 +0,0 @@ -/** - * 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 - **/ - -#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; -} diff --git a/src/utils_gce.c b/src/utils_gce.c deleted file mode 100644 index d43d1de5..00000000 --- a/src/utils_gce.c +++ /dev/null @@ -1,284 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_gce.h" -#include "utils_oauth.h" -#include "utils_time.h" - -#include - -#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 */ diff --git a/src/utils_gce.h b/src/utils_gce.h deleted file mode 100644 index 2ee3f6eb..00000000 --- a/src/utils_gce.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 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 - **/ - -#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 diff --git a/src/utils_ignorelist.c b/src/utils_ignorelist.c deleted file mode 100644 index b3851026..00000000 --- a/src/utils_ignorelist.c +++ /dev/null @@ -1,309 +0,0 @@ -/** - * collectd - src/utils_ignorelist.c - * Copyright (C) 2006 Lubos Stanek - * Copyright (C) 2008 Florian Forster - * - * 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 - * Florian Forster - **/ -/** - * 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) */ diff --git a/src/utils_ignorelist.h b/src/utils_ignorelist.h deleted file mode 100644 index a7fa86d5..00000000 --- a/src/utils_ignorelist.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * collectd - src/utils_ignorelist.h - * Copyright (C) 2006 Lubos Stanek - * - * 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 - **/ -/** - * 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 -#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 */ diff --git a/src/utils_latency.c b/src/utils_latency.c deleted file mode 100644 index 6e4f8732..00000000 --- a/src/utils_latency.c +++ /dev/null @@ -1,344 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_latency.h" - -#include -#include - -#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 */ diff --git a/src/utils_latency.h b/src/utils_latency.h deleted file mode 100644 index 9d878dab..00000000 --- a/src/utils_latency.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 - **/ - -#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); diff --git a/src/utils_latency_config.c b/src/utils_latency_config.c deleted file mode 100644 index 9a91f113..00000000 --- a/src/utils_latency_config.c +++ /dev/null @@ -1,157 +0,0 @@ -/** - * 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 - * Pavel Rochnyack - */ - -#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 */ diff --git a/src/utils_latency_config.h b/src/utils_latency_config.h deleted file mode 100644 index 3d2691a6..00000000 --- a/src/utils_latency_config.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * 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 - * Pavel Rochnyack - */ - -#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 */ diff --git a/src/utils_latency_test.c b/src/utils_latency_test.c deleted file mode 100644 index 42a6e87e..00000000 --- a/src/utils_latency_test.c +++ /dev/null @@ -1,234 +0,0 @@ -/** - * 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 - */ - -#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; -} diff --git a/src/utils_lua.c b/src/utils_lua.c index 11ac0010..d0de9085 100644 --- a/src/utils_lua.c +++ b/src/utils_lua.c @@ -24,7 +24,7 @@ * Florian Forster **/ -#include "common.h" +#include "utils/common/common.h" #include "utils_lua.h" static int ltoc_values(lua_State *L, /* {{{ */ diff --git a/src/utils_match.c b/src/utils_match.c deleted file mode 100644 index d3edb57c..00000000 --- a/src/utils_match.c +++ /dev/null @@ -1,376 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_match.h" - -#include - -#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 */ diff --git a/src/utils_match.h b/src/utils_match.h deleted file mode 100644 index 1cff1eb7..00000000 --- a/src/utils_match.h +++ /dev/null @@ -1,179 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_mount.c b/src/utils_mount.c deleted file mode 100644 index 279f8e2f..00000000 --- a/src/utils_mount.c +++ /dev/null @@ -1,765 +0,0 @@ -/** - * 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 -**/ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#define _GNU_SOURCE - -#include "collectd.h" - -#include "utils_mount.h" - -#if HAVE_XFS_XQM_H -#include -#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 -#endif -#if HAVE_SYS_STATVFS_H -#include -#endif -/* #endif HAVE_GETVFSSTAT */ - -#elif HAVE_GETFSSTAT -#if HAVE_SYS_PARAM_H -#include -#endif -#if HAVE_SYS_UCRED_H -#include -#endif -#if HAVE_SYS_MOUNT_H -#include -#endif -#endif /* HAVE_GETFSSTAT */ - -#if HAVE_MNTENT_H -#include -#endif -#if HAVE_SYS_MNTTAB_H -#include -#endif - -#if HAVE_PATHS_H -#include -#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) */ diff --git a/src/utils_mount.h b/src/utils_mount.h deleted file mode 100644 index 0ad7d020..00000000 --- a/src/utils_mount.h +++ /dev/null @@ -1,186 +0,0 @@ -/** - * 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 -**/ - -/* See below for instructions how to use the public functions. */ - -#ifndef COLLECTD_UTILS_MOUNT_H -#define COLLECTD_UTILS_MOUNT_H 1 - -#include -#if HAVE_FS_INFO_H -#include -#endif -#if HAVE_FSHELP_H -#include -#endif -#if HAVE_PATHS_H -#include -#endif -#if HAVE_MNTENT_H -#include -#endif -#if HAVE_MNTTAB_H -#include -#endif -#if HAVE_SYS_FSTYP_H -#include -#endif -#if HAVE_SYS_FS_TYPES_H -#include -#endif -#if HAVE_SYS_MNTENT_H -#include -#endif -#if HAVE_SYS_MNTTAB_H -#include -#endif -#if HAVE_SYS_MOUNT_H -#include -#endif -#if HAVE_SYS_STATFS_H -#include -#endif -#if HAVE_SYS_VFS_H -#include -#endif -#if HAVE_SYS_VFSTAB_H -#include -#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 */ diff --git a/src/utils_mount_test.c b/src/utils_mount_test.c deleted file mode 100644 index e8f30094..00000000 --- a/src/utils_mount_test.c +++ /dev/null @@ -1,115 +0,0 @@ -/** - * 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 - */ - -#include "collectd.h" - -#include "common.h" -#include "testing.h" -#include "utils_mount.h" - -#if HAVE_KSTAT_H -#include -#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; -} diff --git a/src/utils_oauth.c b/src/utils_oauth.c deleted file mode 100644 index d804b51a..00000000 --- a/src/utils_oauth.c +++ /dev/null @@ -1,637 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_oauth.h" - -#include - -#include -#include - -#include -#include -#include -#include -#include - -/* - * 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 */ diff --git a/src/utils_oauth.h b/src/utils_oauth.h deleted file mode 100644 index b93c87b8..00000000 --- a/src/utils_oauth.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * 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 - **/ - -#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 diff --git a/src/utils_oauth_test.c b/src/utils_oauth_test.c deleted file mode 100644 index 791564fb..00000000 --- a/src/utils_oauth_test.c +++ /dev/null @@ -1,149 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_ovs.c b/src/utils_ovs.c deleted file mode 100644 index 6fe6fe76..00000000 --- a/src/utils_ovs.c +++ /dev/null @@ -1,1412 +0,0 @@ -/** - * 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 - **/ - -/* 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 -#endif -#if HAVE_ARPA_INET_H -#include -#endif -#if HAVE_POLL_H -#include -#endif -#if HAVE_SYS_UN_H -#include -#endif - -#include - -#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: [, ] */ - 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 */ - 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 - * [, , ] */ - OVS_YAJL_CALL(yajl_gen_array_open, jgen); - { - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME); - - /* uid string */ - snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid); - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str); - - /* */ - 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); - { - /* */ - 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" : - * "key_b" : - * } - */ -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: - * - * - * A 2-element JSON array that represents a pair within a database - * map. The first element is an that represents the key, and - * the second element is an that represents the value. - * - * - * 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 s giving the - * values in the map. All of the s must have the same key and - * value types. - * - * EXAMPLE: - * [ - * "map", [ - * [ "key_a", ], [ "key_b", ], ... - * ] - * ] - */ -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; -} diff --git a/src/utils_ovs.h b/src/utils_ovs.h deleted file mode 100644 index 52c2f915..00000000 --- a/src/utils_ovs.h +++ /dev/null @@ -1,236 +0,0 @@ -/** - * 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 - * - * 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 -#include - -/* 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 diff --git a/src/utils_parse_option.c b/src/utils_parse_option.c deleted file mode 100644 index 005715c9..00000000 --- a/src/utils_parse_option.c +++ /dev/null @@ -1,148 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_parse_option.h b/src/utils_parse_option.h deleted file mode 100644 index 3dd0a792..00000000 --- a/src/utils_parse_option.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c deleted file mode 100644 index 7f1f2354..00000000 --- a/src/utils_rrdcreate.c +++ /dev/null @@ -1,660 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include "common.h" -#include "utils_rrdcreate.h" - -#include -#include - -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 */ diff --git a/src/utils_rrdcreate.h b/src/utils_rrdcreate.h deleted file mode 100644 index b2277e75..00000000 --- a/src/utils_rrdcreate.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 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 - **/ - -#ifndef UTILS_RRDCREATE_H -#define UTILS_RRDCREATE_H 1 - -#include "plugin.h" - -#include - -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 */ diff --git a/src/utils_tail.c b/src/utils_tail.c deleted file mode 100644 index 365bf558..00000000 --- a/src/utils_tail.c +++ /dev/null @@ -1,224 +0,0 @@ -/** - * 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 - * Florian Forster - * - * 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 */ diff --git a/src/utils_tail.h b/src/utils_tail.h deleted file mode 100644 index 73a6de21..00000000 --- a/src/utils_tail.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * 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 - * - * 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 */ diff --git a/src/utils_tail_match.c b/src/utils_tail_match.c index ccab5ace..0e5a8611 100644 --- a/src/utils_tail_match.c +++ b/src/utils_tail_match.c @@ -31,11 +31,11 @@ #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 { diff --git a/src/utils_tail_match.h b/src/utils_tail_match.h index 2d4c2531..0217a7e8 100644 --- a/src/utils_tail_match.h +++ b/src/utils_tail_match.h @@ -33,8 +33,8 @@ * 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; diff --git a/src/utils_taskstats.c b/src/utils_taskstats.c deleted file mode 100644 index f0d73334..00000000 --- a/src/utils_taskstats.c +++ /dev/null @@ -1,306 +0,0 @@ -/** - * 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 - */ - -#include "collectd.h" -#include "utils_taskstats.h" - -#include "common.h" -#include "plugin.h" -#include "utils_time.h" - -#include -#include -#include - -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; -} diff --git a/src/utils_taskstats.h b/src/utils_taskstats.h deleted file mode 100644 index de07427c..00000000 --- a/src/utils_taskstats.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 - */ - -#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 */ diff --git a/src/utils_vl_lookup.c b/src/utils_vl_lookup.c deleted file mode 100644 index 03e61f80..00000000 --- a/src/utils_vl_lookup.c +++ /dev/null @@ -1,630 +0,0 @@ -/** - * 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 - **/ - -#include "collectd.h" - -#include -#include - -#include "common.h" -#include "utils_avltree.h" -#include "utils_vl_lookup.h" - -#if HAVE_KSTAT_H -#include -#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 */ diff --git a/src/utils_vl_lookup.h b/src/utils_vl_lookup.h deleted file mode 100644 index 90a4ee52..00000000 --- a/src/utils_vl_lookup.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c deleted file mode 100644 index 1cb7b65b..00000000 --- a/src/utils_vl_lookup_test.c +++ /dev/null @@ -1,242 +0,0 @@ -/** - * 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 - **/ - -#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 */ diff --git a/src/uuid.c b/src/uuid.c index c7878c74..60d09b51 100644 --- a/src/uuid.c +++ b/src/uuid.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYS_SYSCTL_H #include diff --git a/src/varnish.c b/src/varnish.c index b515be89..b4ae4385 100644 --- a/src/varnish.c +++ b/src/varnish.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_VARNISH_V4 || HAVE_VARNISH_V5 #include diff --git a/src/virt.c b/src/virt.c index a3f94051..fd20c77e 100644 --- a/src/virt.c +++ b/src/virt.c @@ -22,10 +22,10 @@ #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 /* for basename(3) */ #include diff --git a/src/vmem.c b/src/vmem.c index c7229756..2ab7dda7 100644 --- a/src/vmem.c +++ b/src/vmem.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if KERNEL_LINUX static const char *config_keys[] = {"Verbose"}; diff --git a/src/vserver.c b/src/vserver.c index 3c6d58cd..e1d1b352 100644 --- a/src/vserver.c +++ b/src/vserver.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/wireless.c b/src/wireless.c index 4208d366..d49f1d30 100644 --- a/src/wireless.c +++ b/src/wireless.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if KERNEL_LINUX #include diff --git a/src/write_graphite.c b/src/write_graphite.c index 7624e243..000b62ed 100644 --- a/src/write_graphite.c +++ b/src/write_graphite.c @@ -45,11 +45,11 @@ #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 diff --git a/src/write_http.c b/src/write_http.c index ad0cb5e4..74fdaca0 100644 --- a/src/write_http.c +++ b/src/write_http.c @@ -25,10 +25,10 @@ #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 diff --git a/src/write_kafka.c b/src/write_kafka.c index 04e67b92..4c7a4715 100644 --- a/src/write_kafka.c +++ b/src/write_kafka.c @@ -26,11 +26,11 @@ #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 diff --git a/src/write_log.c b/src/write_log.c index 52ad6104..3e143168 100644 --- a/src/write_log.c +++ b/src/write_log.c @@ -27,11 +27,11 @@ #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 diff --git a/src/write_mongodb.c b/src/write_mongodb.c index 9cddc916..0cb1e02f 100644 --- a/src/write_mongodb.c +++ b/src/write_mongodb.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include diff --git a/src/write_prometheus.c b/src/write_prometheus.c index 3b32ac09..b109d422 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -26,9 +26,9 @@ #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" diff --git a/src/write_redis.c b/src/write_redis.c index 72cb5946..324999c1 100644 --- a/src/write_redis.c +++ b/src/write_redis.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/write_riemann.c b/src/write_riemann.c index b35d10ee..62ddc67c 100644 --- a/src/write_riemann.c +++ b/src/write_riemann.c @@ -30,8 +30,8 @@ #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" diff --git a/src/write_riemann_threshold.c b/src/write_riemann_threshold.c index 9d8267dc..041ed7d5 100644 --- a/src/write_riemann_threshold.c +++ b/src/write_riemann_threshold.c @@ -27,9 +27,9 @@ #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" diff --git a/src/write_sensu.c b/src/write_sensu.c index 6ea8106c..1bff27b3 100644 --- a/src/write_sensu.c +++ b/src/write_sensu.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include #include diff --git a/src/write_stackdriver.c b/src/write_stackdriver.c index a1341d9d..fd0192a0 100644 --- a/src/write_stackdriver.c +++ b/src/write_stackdriver.c @@ -22,12 +22,12 @@ #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 #include diff --git a/src/write_tsdb.c b/src/write_tsdb.c index 42f5d65b..f8f4cb91 100644 --- a/src/write_tsdb.c +++ b/src/write_tsdb.c @@ -43,8 +43,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_random.h" diff --git a/src/xencpu.c b/src/xencpu.c index 8f177803..e63a7664 100644 --- a/src/xencpu.c +++ b/src/xencpu.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/xmms.c b/src/xmms.c index 3e3a3c3c..2d550b42 100644 --- a/src/xmms.c +++ b/src/xmms.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/zfs_arc.c b/src/zfs_arc.c index d1ee111b..d18a93f7 100644 --- a/src/zfs_arc.c +++ b/src/zfs_arc.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" /* * Global variables diff --git a/src/zone.c b/src/zone.c index 16df4043..cd804f74 100644 --- a/src/zone.c +++ b/src/zone.c @@ -33,13 +33,13 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" #define MAX_PROCFS_PATH 40 #define FRC2PCT(pp) (((float)(pp)) / 0x8000 * 100) diff --git a/src/zookeeper.c b/src/zookeeper.c index a99bbc01..9c70ea57 100644 --- a/src/zookeeper.c +++ b/src/zookeeper.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include