Merge remote-tracking branch 'github/pr/2437'
authorFlorian Forster <octo@collectd.org>
Wed, 27 Sep 2017 10:31:37 +0000 (12:31 +0200)
committerFlorian Forster <octo@collectd.org>
Wed, 27 Sep 2017 10:31:37 +0000 (12:31 +0200)
27 files changed:
Makefile.am
configure.ac
contrib/redhat/collectd.spec
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.pod
src/cpu.c
src/daemon/collectd.c
src/daemon/common.c
src/filecount.c
src/memcached.c
src/mqtt.c
src/msr-index.h [new file with mode: 0644]
src/network.c
src/ntpd.c
src/openldap.c
src/openvpn.c
src/perl.c
src/swap.c
src/table.c
src/turbostat.c
src/uptime.c
src/utils_latency_config.c
src/utils_latency_config.h
src/utils_tail_match.c
src/write_redis.c
src/zfs_arc.c

index 24c5b8c..04636b3 100644 (file)
@@ -1680,7 +1680,9 @@ endif
 
 if BUILD_PLUGIN_TURBOSTAT
 pkglib_LTLIBRARIES += turbostat.la
-turbostat_la_SOURCES = src/turbostat.c
+turbostat_la_SOURCES = \
+       src/turbostat.c \
+       src/msr-index.h
 turbostat_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
index 8f2a9ab..55f78d8 100644 (file)
@@ -521,32 +521,6 @@ if test "x$ac_system" = "xLinux"; then
     ]]
   )
   # For the turbostat plugin
-  AC_CHECK_HEADERS([asm/msr-index.h],
-    [have_asm_msrindex_h="yes"],
-    [have_asm_msrindex_h="no"]
-  )
-
-  if test "x$have_asm_msrindex_h" = "xyes"; then
-    AC_CACHE_CHECK([whether asm/msr-index.h has MSR_PKG_C10_RESIDENCY],
-      [c_cv_have_usable_asm_msrindex_h],
-      [
-        AC_COMPILE_IFELSE(
-          [
-            AC_LANG_PROGRAM(
-              [[#include<asm/msr-index.h>]],
-              [[
-                int y = MSR_PKG_C10_RESIDENCY;
-                return(y);
-              ]]
-            )
-          ],
-          [c_cv_have_usable_asm_msrindex_h="yes"],
-          [c_cv_have_usable_asm_msrindex_h="no"],
-        )
-      ]
-    )
-  fi
-
   AC_CHECK_HEADERS([cpuid.h],
     [have_cpuid_h="yes"],
     [have_cpuid_h="no (cpuid.h not found)"]
@@ -6227,7 +6201,7 @@ if test "x$ac_system" = "xLinux"; then
     plugin_ipvs="yes"
   fi
 
-  if test "x$c_cv_have_usable_asm_msrindex_h" = "xyes" && test "x$have_cpuid_h" = "xyes"; then
+  if test "x$have_cpuid_h" = "xyes"; then
     plugin_turbostat="yes"
   fi
 
index 66d1914..23cac9a 100644 (file)
@@ -521,8 +521,8 @@ the byte- and packet-counters of selected rules and submit them to collectd.
 Summary:       Java plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: java-devel, jpackage-utils
-Requires:      java, jpackage-utils
+BuildRequires: java-devel >= 1.6, jpackage-utils >= 1.6
+Requires:      java >= 1.6, jpackage-utils >= 1.6
 %description java
 This plugin for collectd allows plugins to be written in Java and executed
 in an embedded JVM.
index 6e24c93..bde168c 100644 (file)
 #  ReportByCpu true
 #  ReportByState true
 #  ValuesPercentage false
+#  ReportNumCpu false
+#  ReportGuestState false
+#  SubtractGuestState true
 #</Plugin>
 #
 #<Plugin csv>
 
 #<Plugin filecount>
 #      <Directory "/path/to/dir">
+#              #Plugin "foo"
 #              Instance "foodir"
 #              Name "*.conf"
 #              MTime "-5m"
 #              Size "+10k"
 #              Recursive true
 #              IncludeHidden false
+#              #FilesSizeType "bytes"
+#              #FilesCountType "files"
+#              #TypeInstance "instance"
 #      </Directory>
 #</Plugin>
 
 #              QoS 2
 #              Topic "collectd/#"
 #              CleanSession true
+#              CACert "/etc/ssl/ca.crt"
+#              CertificateFile "/etc/ssl/client.crt"
+#              CertificateKeyFile "/etc/ssl/client.pem"
+#              TLSProtocol "tlsv1.2"
+#              CipherSuite "ciphers"
 #      </Subscribe>
 #</Plugin>
 
 #      ReportBytes true
 #      ValuesAbsolute true
 #      ValuesPercentage false
+#      ReportIO true
 #</Plugin>
 
 #<Plugin table>
 #      <Table "/proc/slabinfo">
+#              #Plugin "table"
 #              Instance "slabinfo"
 #              Separator " "
 #              <Result>
 #        Bucket 0.5 1.0   # -> bucket-latency-foo-0.5_1
 #        Bucket 1.0 2.0   # -> bucket-latency-foo-1_2
 #        Bucket 2.0 0     # -> bucket-latency-foo-2_inf
+#        #BucketType "bucket"
 #      </DSType>
 #      Type "latency"
 #      Instance "foo"
 #      SystemManagementInterrupt true
 #      DigitalTemperatureSensor true
 #      PackageThermalManagement true
-#      RunningAveragePowerLimit "7"    
+#      RunningAveragePowerLimit "7"
 #</Plugin>
 
 #<Plugin unixsock>
index 8b9ef54..91d5381 100644 (file)
@@ -1474,6 +1474,19 @@ in the un-aggregated (per-CPU, per-state) mode as well.
 When set to B<true>, reports the number of available CPUs.
 Defaults to B<false>.
 
+=item B<ReportGuestState> B<false>|B<true>
+
+When set to B<true>, reports the "guest" and "guest_nice" CPU states.
+Defaults to B<false>.
+
+=item B<SubtractGuestState> B<false>|B<true>
+
+This option is only considered when B<ReportGuestState> is set to B<true>.
+"guest" and "guest_nice" are included in respectively "user" and "nice".
+If set to B<true>, "guest" will be subtracted from "user" and "guest_nice"
+will be subtracted from "nice".
+Defaults to B<true>.
+
 =back
 
 =head2 Plugin C<cpufreq>
@@ -2730,12 +2743,16 @@ blocks, the following options are recognized:
 
 =over 4
 
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to B<filecount>.
+
 =item B<Instance> I<Instance>
 
-Sets the plugin instance to I<Instance>. That instance name must be unique, but
-it's your responsibility, the plugin doesn't check for that. If not given, the
-instance is set to the directory name with all slashes replaced by underscores
-and all leading underscores removed.
+Sets the plugin instance to I<Instance>. If not given, the instance is set to
+the directory name with all slashes replaced by underscores and all leading
+underscores removed. Empty value is allowed.
 
 =item B<Name> I<Pattern>
 
@@ -2781,6 +2798,21 @@ Controls whether or not to include "hidden" files and directories in the count.
 "Hidden" files and directories are those, whose name begins with a dot.
 Defaults to I<false>, i.e. by default hidden files and directories are ignored.
 
+=item B<FilesSizeType> I<Type>
+
+Sets the type used to dispatch files combined size. Empty value ("") disables
+reporting. Defaults to B<bytes>.
+
+=item B<FilesCountType> I<Type>
+
+Sets the type used to dispatch number of files. Empty value ("") disables
+reporting. Defaults to B<files>.
+
+=item B<TypeInstance> I<Instance>
+
+Sets the I<type instance> used to dispatch values. Defaults to an empty string
+(no plugin instance).
+
 =back
 
 =head2 Plugin C<GenericJMX>
@@ -4173,18 +4205,18 @@ the B<collectd> branch.
 Path to the PEM-encoded CA certificate file. Setting this option enables TLS
 communication with the MQTT broker, and as such, B<Port> should be the TLS-enabled
 port of the MQTT broker.
-A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
+This option enables the use of TLS.
 
 =item B<CertificateFile> I<file>
 
 Path to the PEM-encoded certificate file to use as client certificate when
 connecting to the MQTT broker.
-A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
+Only valid if B<CACert> and B<CertificateKeyFile> are also set.
 
 =item B<CertificateKeyFile> I<file>
 
 Path to the unencrypted PEM-encoded key file corresponding to B<CertificateFile>.
-A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
+Only valid if B<CACert> and B<CertificateFile> are also set.
 
 =item B<TLSProtocol> I<protocol>
 
@@ -4192,13 +4224,14 @@ If configured, this specifies the string protocol version (e.g. C<tlsv1>,
 C<tlsv1.2>) to use for the TLS connection to the broker. If not set a default
 version is used which depends on the version of OpenSSL the Mosquitto library
 was linked against.
+Only valid if B<CACert> is set.
 
 =item B<CipherSuite> I<ciphersuite>
 
 A string describing the ciphers available for use. See L<ciphers(1)> and the
 C<openssl ciphers> utility for more information. If unset, the default ciphers
 will be used.
-
+Only valid if B<CACert> is set.
 
 =back
 
@@ -4328,11 +4361,11 @@ If enabled, the plugin sends a notification if the replication slave I/O and /
 or SQL threads are not running. Defaults to B<false>.
 
 =item B<WsrepStats> I<true|false>
+
  Enable the collection of wsrep plugin statistics, used in Master-Master
  replication setups like in MySQL Galera/Percona XtraDB Cluster.
  User needs only privileges to execute 'SHOW GLOBAL STATUS'
+
 =item B<ConnectTimeout> I<Seconds>
 
 Sets the connect timeout for the MySQL client.
@@ -5173,7 +5206,7 @@ System (NFS). It counts the number of procedure calls for each procedure,
 grouped by version and whether the system runs as server or client.
 
 It is possibly to omit metrics for a specific NFS version by setting one or
-more of the following options to B<false> (all of them default to B<true>). 
+more of the following options to B<false> (all of them default to B<true>).
 
 =over 4
 
@@ -5655,15 +5688,12 @@ The OpenVPN plugin reads a status file maintained by OpenVPN and gathers
 traffic statistics about connected clients.
 
 To set up OpenVPN to write to the status file periodically, use the
-B<--status> option of OpenVPN. Since OpenVPN can write two different formats,
-you need to set the required format, too. This is done by setting
-B<--status-version> to B<2>.
+B<--status> option of OpenVPN.
 
 So, in a nutshell you need:
 
   openvpn $OTHER_OPTIONS \
-    --status "/var/run/openvpn-status" 10 \
-    --status-version 2
+    --status "/var/run/openvpn-status" 10
 
 Available options:
 
@@ -7527,6 +7557,13 @@ available and free. Defaults to B<false>.
 This is useful for deploying I<collectd> in a heterogeneous environment, where
 swap sizes differ and you want to specify generic thresholds or similar.
 
+=item B<ReportIO> B<true>|B<false>
+
+Enables or disables reporting swap IO. Defaults to B<true>.
+
+This is useful for the cases when swap IO is not neccessary, is not available,
+or is not reliable.
+
 =back
 
 =head2 Plugin C<syslog>
@@ -7562,6 +7599,7 @@ filesystem or CSV (comma separated values) files.
 
   <Plugin table>
     <Table "/proc/slabinfo">
+      #Plugin "slab"
       Instance "slabinfo"
       Separator " "
       <Result>
@@ -7588,10 +7626,14 @@ The following options are available inside a B<Table> block:
 
 =over 4
 
+=item B<Plugin> I<Plugin>
+
+If specified, I<Plugin> is used as the plugin name when submitting values.
+Defaults to B<table>.
+
 =item B<Instance> I<instance>
 
-If specified, I<instance> is used as the plugin instance. So, in the above
-example, the plugin name C<table-slabinfo> would be used. If omitted, the
+If specified, I<instance> is used as the plugin instance. If omitted, the
 filename of the table is used instead, with all special characters replaced
 with an underscore (C<_>).
 
@@ -7681,6 +7723,7 @@ user using (extended) regular expressions, as described in L<regex(7)>.
         <DSType "Distribution">
           Percentile 99
           Bucket 0 100
+          #BucketType "bucket"
         </DSType>
         Type "latency"
         Instance "foo"
@@ -7800,6 +7843,7 @@ B<Synopsis:>
   <DSType "Distribution">
     Percentile 99
     Bucket 0 100
+    BucketType "bucket"
   </DSType>
 
 =over 4
@@ -7836,11 +7880,17 @@ the following schema:
   Bucket  20  50
   Bucket  50   0
 
-Metrics are reported with the I<type> C<bucket> and the I<type instance>
+Metrics are reported with the I<type> set by B<BucketType> option (C<bucket> 
+by default) and the I<type instance>
 C<E<lt>TypeE<gt>[-E<lt>InstanceE<gt>]-E<lt>lower_boundE<gt>_E<lt>upper_boundE<gt>>.
 
 This option may be repeated to calculate more than one rate.
 
+=item B<BucketType> I<Type>
+
+Sets the type used to dispatch B<Bucket> metrics.
+Optional, by default C<bucket> will be used.
+
 =back
 
 =back
@@ -8192,9 +8242,9 @@ collections. The different bits of this bit mask accepted by this plugin are:
 
 Boolean enabling the use of logical core numbering for per core statistics.
 When enabled, C<cpuE<lt>nE<gt>> is used as plugin instance, where I<n> is a
-sequential number assigned by the kernel. Otherwise, C<coreE<lt>nE<gt>> is used
-where I<n> is the n-th core of the socket, causing name conflicts when there is
-more than one socket.
+dynamic number assigned by the kernel. Otherwise, C<coreE<lt>nE<gt>> is used
+if there is only one package and C<pkgE<lt>nE<gt>-coreE<lt>mE<gt>> if there is
+more than one, where I<n> is the n-th core of package I<m>.
 
 =back
 
@@ -8512,7 +8562,7 @@ will be collected.
 =item B<BlockDeviceFormat> B<target>|B<source>
 
 If I<BlockDeviceFormat> is set to B<target>, the default, then the device name
-seen by the guest will be used for reporting metrics. 
+seen by the guest will be used for reporting metrics.
 This corresponds to the C<E<lt>targetE<gt>> node in the XML definition of the
 domain.
 
index 8e68fc0..1dd899b 100644 (file)
@@ -40,11 +40,17 @@ the read callbacks once. A return code not equal to zero indicates an error.
 
 =item B<-P> I<E<lt>pid-fileE<gt>>
 
-Specify an alternative pid file. This overwrites any settings in the config 
+Specify an alternative pid file. This overwrites any settings in the config
 file. This is thought for init-scripts that require the PID-file in a certain
 directory to work correctly. For everyday-usage use the B<PIDFile>
 config-option.
 
+=item B<-B>
+
+If set, collectd will I<not> try to create its base directory. If the base
+directory does not exist, it will exit rather than trying to create the
+directory.
+
 =item B<-f>
 
 Don't fork to the background. I<collectd> will also B<not> close standard file
index 307ae49..d48ab88 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
 #define COLLECTD_CPU_STATE_INTERRUPT 5
 #define COLLECTD_CPU_STATE_SOFTIRQ 6
 #define COLLECTD_CPU_STATE_STEAL 7
-#define COLLECTD_CPU_STATE_IDLE 8
-#define COLLECTD_CPU_STATE_ACTIVE 9 /* sum of (!idle) */
-#define COLLECTD_CPU_STATE_MAX 10   /* #states */
+#define COLLECTD_CPU_STATE_GUEST 8
+#define COLLECTD_CPU_STATE_GUEST_NICE 9
+#define COLLECTD_CPU_STATE_IDLE 10
+#define COLLECTD_CPU_STATE_ACTIVE 11 /* sum of (!idle) */
+#define COLLECTD_CPU_STATE_MAX 12   /* #states */
 
 #if HAVE_STATGRAB_H
 #include <statgrab.h>
 
 static const char *cpu_state_names[] = {"user", "system",    "wait",    "nice",
                                         "swap", "interrupt", "softirq", "steal",
-                                        "idle", "active"};
+                                        "guest", "guest_nice", "idle", "active"};
 
 #ifdef PROCESSOR_CPU_LOAD_INFO
 static mach_port_t port_host;
@@ -193,9 +195,12 @@ static _Bool report_by_cpu = 1;
 static _Bool report_by_state = 1;
 static _Bool report_percent = 0;
 static _Bool report_num_cpu = 0;
+static _Bool report_guest = 0;
+static _Bool subtract_guest = 1;
 
 static const char *config_keys[] = {"ReportByCpu", "ReportByState",
-                                    "ReportNumCpu", "ValuesPercentage"};
+                                    "ReportNumCpu", "ValuesPercentage",
+                                    "ReportGuestState", "SubtractGuestState"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
 static int cpu_config(char const *key, char const *value) /* {{{ */
@@ -208,6 +213,10 @@ static int cpu_config(char const *key, char const *value) /* {{{ */
     report_by_state = IS_TRUE(value) ? 1 : 0;
   else if (strcasecmp(key, "ReportNumCpu") == 0)
     report_num_cpu = IS_TRUE(value) ? 1 : 0;
+  else if (strcasecmp(key, "ReportGuestState") == 0)
+    report_guest = IS_TRUE(value) ? 1 : 0;
+  else if (strcasecmp(key, "SubtractGuestState") == 0)
+    subtract_guest = IS_TRUE(value) ? 1 : 0;
   else
     return -1;
 
@@ -524,7 +533,7 @@ static void cpu_commit_without_aggregation(void) /* {{{ */
 static void cpu_commit(void) /* {{{ */
 {
   gauge_t global_rates[COLLECTD_CPU_STATE_MAX] = {
-      NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN /* Batman! */
+      NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN /* Batman! */
   };
 
   if (report_num_cpu)
@@ -545,7 +554,8 @@ static void cpu_commit(void) /* {{{ */
   for (size_t cpu_num = 0; cpu_num < global_cpu_num; cpu_num++) {
     cpu_state_t *this_cpu_states = get_cpu_state(cpu_num, 0);
     gauge_t local_rates[COLLECTD_CPU_STATE_MAX] = {NAN, NAN, NAN, NAN, NAN,
-                                                   NAN, NAN, NAN, NAN, NAN};
+                                                   NAN, NAN, NAN, NAN, NAN,
+                                                   NAN, NAN };
 
     for (size_t state = 0; state < COLLECTD_CPU_STATE_MAX; state++)
       if (this_cpu_states[state].has_value)
@@ -632,7 +642,7 @@ static int cpu_read(void) {
   FILE *fh;
   char buf[1024];
 
-  char *fields[9];
+  char *fields[11];
   int numfields;
 
   if ((fh = fopen("/proc/stat", "r")) == NULL) {
@@ -648,14 +658,15 @@ static int cpu_read(void) {
     if ((buf[3] < '0') || (buf[3] > '9'))
       continue;
 
-    numfields = strsplit(buf, fields, 9);
+    numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE(fields));
     if (numfields < 5)
       continue;
 
     cpu = atoi(fields[0] + 3);
 
-    cpu_stage(cpu, COLLECTD_CPU_STATE_USER, (derive_t)atoll(fields[1]), now);
-    cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)atoll(fields[2]), now);
+    /* Do not stage User and Nice immediately: we may need to alter them later: */
+    long long user_value = atoll(fields[1]);
+    long long nice_value = atoll(fields[2]);
     cpu_stage(cpu, COLLECTD_CPU_STATE_SYSTEM, (derive_t)atoll(fields[3]), now);
     cpu_stage(cpu, COLLECTD_CPU_STATE_IDLE, (derive_t)atoll(fields[4]), now);
 
@@ -665,11 +676,40 @@ static int cpu_read(void) {
                 now);
       cpu_stage(cpu, COLLECTD_CPU_STATE_SOFTIRQ, (derive_t)atoll(fields[7]),
                 now);
+       }
 
-      if (numfields >= 9)
-        cpu_stage(cpu, COLLECTD_CPU_STATE_STEAL, (derive_t)atoll(fields[8]),
-                  now);
+    if (numfields >= 9) { /* Steal (since Linux 2.6.11) */
+      cpu_stage(cpu, COLLECTD_CPU_STATE_STEAL, (derive_t)atoll(fields[8]), now);
     }
+
+    if (numfields >= 10) { /* Guest (since Linux 2.6.24) */
+      if (report_guest) {
+        long long value = atoll(fields[9]);
+        cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST, (derive_t)value, now);
+        /* Guest is included in User; optionally subtract Guest from User: */
+        if (subtract_guest) {
+          user_value -= value;
+          if (user_value < 0) user_value = 0;
+        }
+      }
+    }
+
+    if (numfields >= 11) { /* Guest_nice (since Linux 2.6.33) */
+      if (report_guest) {
+        long long value = atoll(fields[10]);
+        cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST_NICE, (derive_t)value, now);
+        /* Guest_nice is included in Nice; optionally subtract Guest_nice from
+           Nice: */
+        if (subtract_guest) {
+          nice_value -= value;
+          if (nice_value < 0) nice_value = 0;
+        }
+      }
+    }
+
+    /* Eventually stage User and Nice: */
+    cpu_stage(cpu, COLLECTD_CPU_STATE_USER, (derive_t)user_value, now);
+    cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)nice_value, now);
   }
   fclose(fh);
 /* }}} #endif defined(KERNEL_LINUX) */
index 2edfa37..af8fb56 100644 (file)
@@ -158,7 +158,7 @@ static int init_global_variables(void) {
   return 0;
 } /* int init_global_variables */
 
-static int change_basedir(const char *orig_dir) {
+static int change_basedir(const char *orig_dir, _Bool create) {
   char *dir;
   size_t dirlen;
   int status;
@@ -183,7 +183,7 @@ static int change_basedir(const char *orig_dir) {
   if (status == 0) {
     free(dir);
     return 0;
-  } else if (errno != ENOENT) {
+  } else if (!create || (errno != ENOENT)) {
     char errbuf[1024];
     ERROR("change_basedir: chdir (%s): %s", dir,
           sstrerror(errno, errbuf, sizeof(errbuf)));
@@ -250,6 +250,7 @@ __attribute__((noreturn)) static void exit_usage(int status) {
 #if COLLECT_DAEMON
          "    -f              Don't fork to the background.\n"
 #endif
+         "    -B              Don't create the BaseDir\n"
          "    -h              Display help (this message)\n"
          "\nBuiltin defaults:\n"
          "  Config file       " CONFIGFILE "\n"
@@ -459,6 +460,7 @@ int main(int argc, char **argv) {
   int test_config = 0;
   int test_readall = 0;
   const char *basedir;
+  _Bool opt_create_basedir = 1;
 #if COLLECT_DAEMON
   pid_t pid;
   int daemonize = 1;
@@ -469,7 +471,7 @@ int main(int argc, char **argv) {
   while (1) {
     int c;
 
-    c = getopt(argc, argv, "htTC:"
+    c = getopt(argc, argv, "BhtTC:"
 #if COLLECT_DAEMON
                            "fP:"
 #endif
@@ -479,6 +481,9 @@ int main(int argc, char **argv) {
       break;
 
     switch (c) {
+    case 'B':
+      opt_create_basedir = 0;
+      break;
     case 'C':
       configfile = optarg;
       break;
@@ -533,7 +538,7 @@ int main(int argc, char **argv) {
     fprintf(stderr,
             "Don't have a basedir to use. This should not happen. Ever.");
     return 1;
-  } else if (change_basedir(basedir)) {
+  } else if (change_basedir(basedir, opt_create_basedir)) {
     fprintf(stderr, "Error: Unable to change to directory `%s'.\n", basedir);
     return 1;
   }
index 3ae61d8..6c856a6 100644 (file)
@@ -269,7 +269,8 @@ ssize_t swrite(int fd, const void *buf, size_t count) {
     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 */
-      return errno ? errno : -1;
+      errno = ECONNRESET;
+      return -1;
     }
   }
 
index a2ec8e8..5b812b8 100644 (file)
 
 struct fc_directory_conf_s {
   char *path;
+  char *plugin_name;
   char *instance;
+  char *files_size_type;
+  char *files_num_type;
+  char *type_instance;
 
   int options;
 
@@ -58,31 +62,56 @@ typedef struct fc_directory_conf_s fc_directory_conf_t;
 static fc_directory_conf_t **directories = NULL;
 static size_t directories_num = 0;
 
+void fc_free_dir(fc_directory_conf_t *dir) {
+  sfree(dir->path);
+  sfree(dir->plugin_name);
+  sfree(dir->instance);
+  sfree(dir->files_size_type);
+  sfree(dir->files_num_type);
+  sfree(dir->type_instance);
+  sfree(dir->name);
+
+  sfree(dir);
+} /* void fc_free_dir */
+
 static void fc_submit_dir(const fc_directory_conf_t *dir) {
   value_list_t vl = VALUE_LIST_INIT;
 
-  vl.values = &(value_t){.gauge = (gauge_t)dir->files_num};
-  vl.values_len = 1;
-  sstrncpy(vl.plugin, "filecount", sizeof(vl.plugin));
-  sstrncpy(vl.plugin_instance, dir->instance, sizeof(vl.plugin_instance));
-  sstrncpy(vl.type, "files", sizeof(vl.type));
+  sstrncpy(vl.plugin, dir->plugin_name, sizeof(vl.plugin));
+  if (dir->instance != NULL)
+    sstrncpy(vl.plugin_instance, dir->instance, sizeof(vl.plugin_instance));
+  if (dir->type_instance != NULL)
+    sstrncpy(vl.type_instance, dir->type_instance, sizeof(vl.type_instance));
 
-  plugin_dispatch_values(&vl);
+  vl.values_len = 1;
 
-  vl.values = &(value_t){.gauge = (gauge_t)dir->files_size};
-  sstrncpy(vl.type, "bytes", sizeof(vl.type));
+  if (dir->files_num_type != NULL) {
+    vl.values = &(value_t){.gauge = (gauge_t)dir->files_num};
+    sstrncpy(vl.type, dir->files_num_type, sizeof(vl.type));
+    plugin_dispatch_values(&vl);
+  }
 
-  plugin_dispatch_values(&vl);
+  if (dir->files_size_type != NULL) {
+    vl.values = &(value_t){.gauge = (gauge_t)dir->files_size};
+    sstrncpy(vl.type, dir->files_size_type, sizeof(vl.type));
+    plugin_dispatch_values(&vl);
+  }
 } /* void fc_submit_dir */
 
 /*
  * Config:
  * <Plugin filecount>
  *   <Directory /path/to/dir>
+ *     Plugin "foo"
  *     Instance "foobar"
  *     Name "*.conf"
  *     MTime -3600
  *     Size "+10M"
+ *     Recursive true
+ *     IncludeHidden false
+ *     FilesSizeType "bytes"
+ *     FilesCountType "files"
+ *     TypeInstance "instance"
  *   </Directory>
  * </Plugin>
  *
@@ -94,7 +123,6 @@ static void fc_submit_dir(const fc_directory_conf_t *dir) {
 static int fc_config_set_instance(fc_directory_conf_t *dir, const char *str) {
   char buffer[1024];
   char *ptr;
-  char *copy;
 
   sstrncpy(buffer, str, sizeof(buffer));
   for (ptr = buffer; *ptr != 0; ptr++)
@@ -104,10 +132,7 @@ static int fc_config_set_instance(fc_directory_conf_t *dir, const char *str) {
   for (ptr = buffer; *ptr == '_'; ptr++)
     /* do nothing */;
 
-  if (*ptr == 0)
-    return -1;
-
-  copy = strdup(ptr);
+  char *copy = strdup(ptr);
   if (copy == NULL)
     return -1;
 
@@ -130,15 +155,13 @@ static int fc_config_add_dir_instance(fc_directory_conf_t *dir,
 
 static int fc_config_add_dir_name(fc_directory_conf_t *dir,
                                   oconfig_item_t *ci) {
-  char *temp;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("filecount plugin: The `Name' config option needs exactly one "
             "string argument.");
     return -1;
   }
 
-  temp = strdup(ci->values[0].value.string);
+  char *temp = strdup(ci->values[0].value.string);
   if (temp == NULL) {
     ERROR("filecount plugin: strdup failed.");
     return -1;
@@ -152,9 +175,6 @@ static int fc_config_add_dir_name(fc_directory_conf_t *dir,
 
 static int fc_config_add_dir_mtime(fc_directory_conf_t *dir,
                                    oconfig_item_t *ci) {
-  char *endptr;
-  double temp;
-
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
                                 (ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
     WARNING("filecount plugin: The `MTime' config option needs exactly one "
@@ -168,8 +188,8 @@ static int fc_config_add_dir_mtime(fc_directory_conf_t *dir,
   }
 
   errno = 0;
-  endptr = NULL;
-  temp = strtod(ci->values[0].value.string, &endptr);
+  char *endptr = NULL;
+  double temp = strtod(ci->values[0].value.string, &endptr);
   if ((errno != 0) || (endptr == NULL) ||
       (endptr == ci->values[0].value.string)) {
     WARNING("filecount plugin: Converting `%s' to a number failed.",
@@ -220,9 +240,6 @@ static int fc_config_add_dir_mtime(fc_directory_conf_t *dir,
 
 static int fc_config_add_dir_size(fc_directory_conf_t *dir,
                                   oconfig_item_t *ci) {
-  char *endptr;
-  double temp;
-
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
                                 (ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
     WARNING("filecount plugin: The `Size' config option needs exactly one "
@@ -236,8 +253,8 @@ static int fc_config_add_dir_size(fc_directory_conf_t *dir,
   }
 
   errno = 0;
-  endptr = NULL;
-  temp = strtod(ci->values[0].value.string, &endptr);
+  char *endptr = NULL;
+  double temp = strtod(ci->values[0].value.string, &endptr);
   if ((errno != 0) || (endptr == NULL) ||
       (endptr == ci->values[0].value.string)) {
     WARNING("filecount plugin: Converting `%s' to a number failed.",
@@ -303,9 +320,6 @@ static int fc_config_add_dir_option(fc_directory_conf_t *dir,
 } /* int fc_config_add_dir_option */
 
 static int fc_config_add_dir(oconfig_item_t *ci) {
-  fc_directory_conf_t *dir;
-  int status;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("filecount plugin: `Directory' needs exactly one string "
             "argument.");
@@ -313,7 +327,7 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
   }
 
   /* Initialize `dir' */
-  dir = calloc(1, sizeof(*dir));
+  fc_directory_conf_t *dir = calloc(1, sizeof(*dir));
   if (dir == NULL) {
     ERROR("filecount plugin: calloc failed.");
     return -1;
@@ -322,23 +336,36 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
   dir->path = strdup(ci->values[0].value.string);
   if (dir->path == NULL) {
     ERROR("filecount plugin: strdup failed.");
-    sfree(dir);
+    fc_free_dir(dir);
     return -1;
   }
 
-  fc_config_set_instance(dir, dir->path);
-
   dir->options = FC_RECURSIVE;
 
   dir->name = NULL;
+  dir->plugin_name = strdup("filecount");
+  dir->instance = NULL;
+  dir->type_instance = NULL;
   dir->mtime = 0;
   dir->size = 0;
 
-  status = 0;
+  dir->files_size_type = strdup("bytes");
+  dir->files_num_type = strdup("files");
+
+  if (dir->plugin_name == NULL || dir->files_size_type == NULL ||
+      dir->files_num_type == NULL) {
+    ERROR("filecount plugin: strdup failed.");
+    fc_free_dir(dir);
+    return -1;
+  }
+
+  int status = 0;
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
-    if (strcasecmp("Instance", option->key) == 0)
+    if (strcasecmp("Plugin", option->key) == 0)
+      status = cf_util_get_string(option, &dir->plugin_name);
+    else if (strcasecmp("Instance", option->key) == 0)
       status = fc_config_add_dir_instance(dir, option);
     else if (strcasecmp("Name", option->key) == 0)
       status = fc_config_add_dir_name(dir, option);
@@ -350,6 +377,12 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
       status = fc_config_add_dir_option(dir, option, FC_RECURSIVE);
     else if (strcasecmp("IncludeHidden", option->key) == 0)
       status = fc_config_add_dir_option(dir, option, FC_HIDDEN);
+    else if (strcasecmp("FilesSizeType", option->key) == 0)
+      status = cf_util_get_string(option, &dir->files_size_type);
+    else if (strcasecmp("FilesCountType", option->key) == 0)
+      status = cf_util_get_string(option, &dir->files_num_type);
+    else if (strcasecmp("TypeInstance", option->key) == 0)
+      status = cf_util_get_string(option, &dir->type_instance);
     else {
       WARNING("filecount plugin: fc_config_add_dir: "
               "Option `%s' not allowed here.",
@@ -361,28 +394,52 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
       break;
   } /* for (ci->children) */
 
-  if (status == 0) {
-    fc_directory_conf_t **temp;
+  if (status != 0) {
+    fc_free_dir(dir);
+    return -1;
+  }
 
-    temp = realloc(directories, sizeof(*directories) * (directories_num + 1));
-    if (temp == NULL) {
-      ERROR("filecount plugin: realloc failed.");
-      status = -1;
-    } else {
-      directories = temp;
-      directories[directories_num] = dir;
-      directories_num++;
+  /* Set default plugin instance */
+  if (dir->instance == NULL) {
+    fc_config_set_instance(dir, dir->path);
+    if (dir->instance == NULL || strlen(dir->instance) == 0) {
+      ERROR("filecount plugin: failed to build plugin instance name.");
+      fc_free_dir(dir);
+      return -1;
     }
   }
 
-  if (status != 0) {
-    sfree(dir->name);
+  /* Handle disabled types */
+  if (strlen(dir->instance) == 0)
     sfree(dir->instance);
-    sfree(dir->path);
-    sfree(dir);
+
+  if (strlen(dir->files_size_type) == 0)
+    sfree(dir->files_size_type);
+
+  if (strlen(dir->files_num_type) == 0)
+    sfree(dir->files_num_type);
+
+  if (dir->files_size_type == NULL && dir->files_num_type == NULL) {
+    WARNING("filecount plugin: Both `FilesSizeType' and `FilesCountType ' "
+            "are disabled for '%s'. There's no types to report.",
+            dir->path);
+    fc_free_dir(dir);
+    return -1;
+  }
+
+  /* Ready to add it to list */
+  fc_directory_conf_t **temp =
+      realloc(directories, sizeof(*directories) * (directories_num + 1));
+  if (temp == NULL) {
+    ERROR("filecount plugin: realloc failed.");
+    fc_free_dir(dir);
     return -1;
   }
 
+  directories = temp;
+  directories[directories_num] = dir;
+  directories_num++;
+
   return 0;
 } /* int fc_config_add_dir */
 
@@ -414,14 +471,13 @@ static int fc_read_dir_callback(const char *dirname, const char *filename,
   fc_directory_conf_t *dir = user_data;
   char abs_path[PATH_MAX];
   struct stat statbuf;
-  int status;
 
   if (dir == NULL)
     return -1;
 
   snprintf(abs_path, sizeof(abs_path), "%s/%s", dirname, filename);
 
-  status = lstat(abs_path, &statbuf);
+  int status = lstat(abs_path, &statbuf);
   if (status != 0) {
     ERROR("filecount plugin: stat (%s) failed.", abs_path);
     return -1;
@@ -478,15 +534,13 @@ static int fc_read_dir_callback(const char *dirname, const char *filename,
 } /* int fc_read_dir_callback */
 
 static int fc_read_dir(fc_directory_conf_t *dir) {
-  int status;
-
   dir->files_num = 0;
   dir->files_size = 0;
 
   if (dir->mtime != 0)
     dir->now = time(NULL);
 
-  status =
+  int status =
       walk_directory(dir->path, fc_read_dir_callback, dir,
                      /* include hidden */ (dir->options & FC_HIDDEN) ? 1 : 0);
   if (status != 0) {
index 3502e35..90f323f 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (C) 2009       Doug MacEachern
  * Copyright (C) 2009       Franck Lombardi
  * Copyright (C) 2012       Nicolas Szalay
+ * Copyright (C) 2017       Pavel Rochnyak
  *
  * 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
@@ -26,6 +27,7 @@
  *   Doug MacEachern <dougm at hyperic.com>
  *   Franck Lombardi
  *   Nicolas Szalay
+ *   Pavel Rochnyak <pavel2000 ngs.ru>
  **/
 
 #include "collectd.h"
 #include <netinet/tcp.h>
 #include <sys/un.h>
 
+#include <poll.h>
+
 #define MEMCACHED_DEF_HOST "127.0.0.1"
 #define MEMCACHED_DEF_PORT "11211"
+#define MEMCACHED_CONNECT_TIMEOUT 10000
+#define MEMCACHED_IO_TIMEOUT 5000
 
 struct memcached_s {
   char *name;
@@ -47,6 +53,7 @@ struct memcached_s {
   char *socket;
   char *connhost;
   char *connport;
+  int fd;
 };
 typedef struct memcached_s memcached_t;
 
@@ -57,6 +64,12 @@ static void memcached_free(void *arg) {
   if (st == NULL)
     return;
 
+  if (st->fd >= 0) {
+    shutdown(st->fd, SHUT_RDWR);
+    close(st->fd);
+    st->fd = -1;
+  }
+
   sfree(st->name);
   sfree(st->host);
   sfree(st->socket);
@@ -86,7 +99,15 @@ static int memcached_connect_unix(memcached_t *st) {
   if (status != 0) {
     shutdown(fd, SHUT_RDWR);
     close(fd);
-    fd = -1;
+    return -1;
+  }
+
+  /* switch to non-blocking mode */
+  int flags = fcntl(fd, F_GETFL);
+  status = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+  if (status != 0) {
+    close(fd);
+    return -1;
   }
 
   return fd;
@@ -124,16 +145,47 @@ static int memcached_connect_inet(memcached_t *st) {
       continue;
     }
 
+    /* switch socket to non-blocking mode */
+    int flags = fcntl(fd, F_GETFL);
+    status = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+    if (status != 0) {
+      close(fd);
+      fd = -1;
+      continue;
+    }
+
     /* connect to the memcached daemon */
     status = (int)connect(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
-    if (status != 0) {
+    if (status != 0 && errno != EINPROGRESS) {
       shutdown(fd, SHUT_RDWR);
       close(fd);
       fd = -1;
       continue;
     }
 
-    /* A socket could be opened and connecting succeeded. We're done. */
+    /* Wait until connection establishes */
+    struct pollfd pollfd = {
+        .fd = fd, .events = POLLOUT,
+    };
+    do
+      status = poll(&pollfd, 1, MEMCACHED_CONNECT_TIMEOUT);
+    while (status < 0 && errno == EINTR);
+    if (status <= 0) {
+      close(fd);
+      fd = -1;
+      continue;
+    }
+
+    /* Check if all is good */
+    int socket_error;
+    status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&socket_error,
+                        &(socklen_t){sizeof(socket_error)});
+    if (status != 0 || socket_error != 0) {
+      close(fd);
+      fd = -1;
+      continue;
+    }
+    /* A socket is opened and connection succeeded. We're done. */
     break;
   }
 
@@ -141,32 +193,55 @@ static int memcached_connect_inet(memcached_t *st) {
   return fd;
 } /* int memcached_connect_inet */
 
-static int memcached_connect(memcached_t *st) {
+static void memcached_connect(memcached_t *st) {
+  if (st->fd >= 0)
+    return;
+
   if (st->socket != NULL)
-    return memcached_connect_unix(st);
+    st->fd = memcached_connect_unix(st);
   else
-    return memcached_connect_inet(st);
+    st->fd = memcached_connect_inet(st);
+
+  if (st->fd >= 0)
+    INFO("memcached plugin: Instance \"%s\": connection established.",
+         st->name);
 }
 
 static int memcached_query_daemon(char *buffer, size_t buffer_size,
                                   memcached_t *st) {
-  int fd, status;
+  int status;
   size_t buffer_fill;
 
-  fd = memcached_connect(st);
-  if (fd < 0) {
+  memcached_connect(st);
+  if (st->fd < 0) {
     ERROR("memcached plugin: Instance \"%s\" could not connect to daemon.",
           st->name);
     return -1;
   }
 
-  status = (int)swrite(fd, "stats\r\n", strlen("stats\r\n"));
+  struct pollfd pollfd = {
+      .fd = st->fd, .events = POLLOUT,
+  };
+
+  do
+    status = poll(&pollfd, 1, MEMCACHED_IO_TIMEOUT);
+  while (status < 0 && errno == EINTR);
+
+  if (status <= 0) {
+    ERROR("memcached plugin: poll() failed for write() call.");
+    close(st->fd);
+    st->fd = -1;
+    return -1;
+  }
+
+  status = (int)swrite(st->fd, "stats\r\n", strlen("stats\r\n"));
   if (status != 0) {
     char errbuf[1024];
-    ERROR("memcached plugin: write(2) failed: %s",
+    ERROR("memcached plugin: Instance \"%s\": write(2) failed: %s", st->name,
           sstrerror(errno, errbuf, sizeof(errbuf)));
-    shutdown(fd, SHUT_RDWR);
-    close(fd);
+    shutdown(st->fd, SHUT_RDWR);
+    close(st->fd);
+    st->fd = -1;
     return -1;
   }
 
@@ -174,27 +249,48 @@ static int memcached_query_daemon(char *buffer, size_t buffer_size,
   memset(buffer, 0, buffer_size);
 
   buffer_fill = 0;
-  while ((status = (int)recv(fd, buffer + buffer_fill,
-                             buffer_size - buffer_fill, /* flags = */ 0)) !=
-         0) {
+  pollfd.events = POLLIN;
+  while (1) {
+    do
+      status = poll(&pollfd, 1, MEMCACHED_IO_TIMEOUT);
+    while (status < 0 && errno == EINTR);
+
+    if (status <= 0) {
+      ERROR("memcached plugin: Instance \"%s\": Timeout reading from socket",
+            st->name);
+      close(st->fd);
+      st->fd = -1;
+      return -1;
+    }
+
+    do
+      status = (int)recv(st->fd, buffer + buffer_fill,
+                         buffer_size - buffer_fill, /* flags = */ 0);
+    while (status < 0 && errno == EINTR);
+
     char const end_token[5] = {'E', 'N', 'D', '\r', '\n'};
     if (status < 0) {
       char errbuf[1024];
 
-      if ((errno == EAGAIN) || (errno == EINTR))
+      if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
         continue;
 
-      ERROR("memcached: Error reading from socket: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-      shutdown(fd, SHUT_RDWR);
-      close(fd);
+      ERROR("memcached plugin: Instance \"%s\": Error reading from socket: %s",
+            st->name, sstrerror(errno, errbuf, sizeof(errbuf)));
+      shutdown(st->fd, SHUT_RDWR);
+      close(st->fd);
+      st->fd = -1;
       return -1;
     }
 
     buffer_fill += (size_t)status;
     if (buffer_fill > buffer_size) {
       buffer_fill = buffer_size;
-      WARNING("memcached plugin: Message was truncated.");
+      WARNING("memcached plugin: Instance \"%s\": Message was truncated.",
+              st->name);
+      shutdown(st->fd, SHUT_RDWR);
+      close(st->fd);
+      st->fd = -1;
       break;
     }
 
@@ -206,12 +302,11 @@ static int memcached_query_daemon(char *buffer, size_t buffer_size,
 
   status = 0;
   if (buffer_fill == 0) {
-    WARNING("memcached plugin: No data returned by memcached.");
+    WARNING("memcached plugin: Instance \"%s\": No data returned by memcached.",
+            st->name);
     status = -1;
   }
 
-  shutdown(fd, SHUT_RDWR);
-  close(fd);
   return status;
 } /* int memcached_query_daemon */
 
@@ -377,6 +472,13 @@ static int memcached_read(user_data_t *user_data) {
     } else if (FIELD_IS("listen_disabled_num")) {
       submit_derive("connections", "listen_disabled", atof(fields[2]), st);
     }
+    /*
+     * Total number of connections opened since the server started running
+     * Report this as connection rate.
+     */
+    else if (FIELD_IS("total_connections")) {
+      submit_derive("connections", "opened", atof(fields[2]), st);
+    }
 
     /*
      * Commands
@@ -562,6 +664,8 @@ static int config_add_instance(oconfig_item_t *ci) {
   st->connhost = NULL;
   st->connport = NULL;
 
+  st->fd = -1;
+
   if (strcasecmp(ci->key, "Instance") == 0)
     status = cf_util_get_string(ci, &st->name);
 
@@ -639,6 +743,8 @@ static int memcached_init(void) {
   st->connhost = NULL;
   st->connport = NULL;
 
+  st->fd = -1;
+
   status = memcached_add_read_callback(st);
   if (status == 0)
     memcached_have_instances = 1;
index 851866b..5164485 100644 (file)
@@ -525,10 +525,10 @@ static int mqtt_write(const data_set_t *ds, const value_list_t *vl,
  *   StoreRates true
  *   Retain false
  *   QoS 0
- *   CACert "ca.pem"                   Enables TLS if set
- *   CertificateFile "client-cert.pem"         optional
- *   CertificateKeyFile "client-key.pem"               optional
- *   TLSProtocol "tlsv1.2"             optional
+ *   CACert "ca.pem"                      Enables TLS if set
+ *   CertificateFile "client-cert.pem"   optional
+ *   CertificateKeyFile "client-key.pem"  optional
+ *   TLSProtocol "tlsv1.2"                optional
  * </Publish>
  */
 static int mqtt_config_publisher(oconfig_item_t *ci) {
@@ -624,6 +624,10 @@ static int mqtt_config_publisher(oconfig_item_t *ci) {
  *   User "guest"
  *   Password "secret"
  *   Topic "collectd/#"
+ *   CACert "ca.pem"                      Enables TLS if set
+ *   CertificateFile "client-cert.pem"   optional
+ *   CertificateKeyFile "client-key.pem"  optional
+ *   TLSProtocol "tlsv1.2"                optional
  * </Subscribe>
  */
 static int mqtt_config_subscriber(oconfig_item_t *ci) {
@@ -687,6 +691,16 @@ static int mqtt_config_subscriber(oconfig_item_t *ci) {
       cf_util_get_string(child, &conf->topic);
     else if (strcasecmp("CleanSession", child->key) == 0)
       cf_util_get_boolean(child, &conf->clean_session);
+    else if (strcasecmp("CACert", child->key) == 0)
+      cf_util_get_string(child, &conf->cacertificatefile);
+    else if (strcasecmp("CertificateFile", child->key) == 0)
+      cf_util_get_string(child, &conf->certificatefile);
+    else if (strcasecmp("CertificateKeyFile", child->key) == 0)
+      cf_util_get_string(child, &conf->certificatekeyfile);
+    else if (strcasecmp("TLSProtocol", child->key) == 0)
+      cf_util_get_string(child, &conf->tlsprotocol);
+    else if (strcasecmp("CipherSuite", child->key) == 0)
+      cf_util_get_string(child, &conf->ciphersuite);
     else
       ERROR("mqtt plugin: Unknown config option: %s", child->key);
   }
diff --git a/src/msr-index.h b/src/msr-index.h
new file mode 100644 (file)
index 0000000..2adca57
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Partial header file imported from the linux kernel
+ * (arch/x86/include/asm/msr-index.h)
+ * as it is not provided by the kernel sources anymore
+ *
+ * Only the minimal blocks of macro have been included
+ * ----
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ * ----
+ */
+
+#ifndef _ASM_X86_MSR_INDEX_H
+#define _ASM_X86_MSR_INDEX_H
+
+/*
+ * CPU model specific register (MSR) numbers.
+ *
+ * Do not add new entries to this file unless the definitions are shared
+ * between multiple compilation units.
+ */
+
+/* Intel MSRs. Some also available on other CPUs */
+
+/* C-state Residency Counters */
+#define MSR_PKG_C3_RESIDENCY           0x000003f8
+#define MSR_PKG_C6_RESIDENCY           0x000003f9
+#define MSR_ATOM_PKG_C6_RESIDENCY      0x000003fa
+#define MSR_PKG_C7_RESIDENCY           0x000003fa
+#define MSR_CORE_C3_RESIDENCY          0x000003fc
+#define MSR_CORE_C6_RESIDENCY          0x000003fd
+#define MSR_CORE_C7_RESIDENCY          0x000003fe
+#define MSR_KNL_CORE_C6_RESIDENCY      0x000003ff
+#define MSR_PKG_C2_RESIDENCY           0x0000060d
+#define MSR_PKG_C8_RESIDENCY           0x00000630
+#define MSR_PKG_C9_RESIDENCY           0x00000631
+#define MSR_PKG_C10_RESIDENCY          0x00000632
+
+/* Run Time Average Power Limiting (RAPL) Interface */
+
+#define MSR_RAPL_POWER_UNIT            0x00000606
+
+#define MSR_PKG_POWER_LIMIT            0x00000610
+#define MSR_PKG_ENERGY_STATUS          0x00000611
+#define MSR_PKG_PERF_STATUS            0x00000613
+#define MSR_PKG_POWER_INFO             0x00000614
+
+#define MSR_DRAM_POWER_LIMIT           0x00000618
+#define MSR_DRAM_ENERGY_STATUS         0x00000619
+#define MSR_DRAM_PERF_STATUS           0x0000061b
+#define MSR_DRAM_POWER_INFO            0x0000061c
+
+#define MSR_PP0_POWER_LIMIT            0x00000638
+#define MSR_PP0_ENERGY_STATUS          0x00000639
+#define MSR_PP0_POLICY                 0x0000063a
+#define MSR_PP0_PERF_STATUS            0x0000063b
+
+#define MSR_PP1_POWER_LIMIT            0x00000640
+#define MSR_PP1_ENERGY_STATUS          0x00000641
+#define MSR_PP1_POLICY                 0x00000642
+
+
+
+/* Intel defined MSRs. */
+#define MSR_IA32_TSC                   0x00000010
+#define MSR_SMI_COUNT                  0x00000034
+
+#define MSR_IA32_MPERF                 0x000000e7
+#define MSR_IA32_APERF                 0x000000e8
+
+#define MSR_IA32_THERM_STATUS          0x0000019c
+
+#define MSR_IA32_TEMPERATURE_TARGET    0x000001a2
+
+#define MSR_IA32_PACKAGE_THERM_STATUS          0x000001b1
+
+
+#endif /* _ASM_X86_MSR_INDEX_H */
index 375da84..4e68421 100644 (file)
@@ -1230,9 +1230,9 @@ static int parse_part_encr_aes256(sockent_t *se, /* {{{ */
                             part_size - buffer_offset,
                             /* in = */ NULL, /* in len = */ 0);
   if (err != 0) {
-    sfree(pea.username);
     ERROR("network plugin: gcry_cipher_decrypt returned: %s. Username: %s",
           gcry_strerror(err), pea.username);
+    sfree(pea.username);
     return -1;
   }
 
@@ -1254,8 +1254,6 @@ static int parse_part_encr_aes256(sockent_t *se, /* {{{ */
   parse_packet(se, buffer + buffer_offset, payload_len, flags | PP_ENCRYPTED,
                pea.username);
 
-  /* XXX: Free pea.username?!? */
-
   /* Update return values */
   *ret_buffer = buffer + part_size;
   *ret_buffer_len = buffer_len - part_size;
@@ -2652,10 +2650,10 @@ static int network_write(const data_set_t *ds, const value_list_t *vl,
 
   pthread_mutex_lock(&send_buffer_lock);
 
-  status = add_to_buffer(send_buffer_ptr,
-                         network_config_packet_size -
-                             (send_buffer_fill + BUFF_SIG_SIZE),
-                         &send_buffer_vl, ds, vl);
+  status =
+      add_to_buffer(send_buffer_ptr, network_config_packet_size -
+                                         (send_buffer_fill + BUFF_SIG_SIZE),
+                    &send_buffer_vl, ds, vl);
   if (status >= 0) {
     /* status == bytes added to the buffer */
     send_buffer_fill += status;
@@ -2666,10 +2664,10 @@ static int network_write(const data_set_t *ds, const value_list_t *vl,
   } else {
     flush_buffer();
 
-    status = add_to_buffer(send_buffer_ptr,
-                           network_config_packet_size -
-                               (send_buffer_fill + BUFF_SIG_SIZE),
-                           &send_buffer_vl, ds, vl);
+    status =
+        add_to_buffer(send_buffer_ptr, network_config_packet_size -
+                                           (send_buffer_fill + BUFF_SIG_SIZE),
+                      &send_buffer_vl, ds, vl);
 
     if (status >= 0) {
       send_buffer_fill += status;
index a19d05c..48d7aa7 100644 (file)
@@ -846,9 +846,9 @@ static int ntpd_read(void) {
   }
 
   /* kerninfo -> estimated error */
-  offset_loop = scale_loop * ((gauge_t)ntohl(ik->offset));
+  offset_loop = (gauge_t)((int32_t)ntohl(ik->offset) * scale_loop);
   freq_loop = ntpd_read_fp(ik->freq);
-  offset_error = scale_error * ((gauge_t)ntohl(ik->esterror));
+  offset_error = (gauge_t)((int32_t)ntohl(ik->esterror) * scale_error);
 
   DEBUG("info_kernel:\n"
         "  pll offset    = %.8g\n"
index 227c8e1..afe2479 100644 (file)
@@ -47,7 +47,6 @@ struct cldap_s /* {{{ */
   char *password;
   char *cacert;
   char *host;
-  int state;
   _Bool starttls;
   int timeout;
   char *url;
@@ -58,11 +57,10 @@ struct cldap_s /* {{{ */
 };
 typedef struct cldap_s cldap_t; /* }}} */
 
-static cldap_t **databases = NULL;
-static size_t databases_num = 0;
-
-static void cldap_free(cldap_t *st) /* {{{ */
+static void cldap_free(void *arg) /* {{{ */
 {
+  cldap_t *st = arg;
+
   if (st == NULL)
     return;
 
@@ -73,32 +71,30 @@ static void cldap_free(cldap_t *st) /* {{{ */
   sfree(st->name);
   sfree(st->url);
   if (st->ld)
-    ldap_memfree(st->ld);
+    ldap_unbind_ext_s(st->ld, NULL, NULL);
+
   sfree(st);
 } /* }}} void cldap_free */
 
 /* initialize ldap for each host */
 static int cldap_init_host(cldap_t *st) /* {{{ */
 {
-  LDAP *ld;
   int rc;
 
-  if (st->state && st->ld) {
+  if (st->ld) {
     DEBUG("openldap plugin: Already connected to %s", st->url);
     return 0;
   }
 
-  rc = ldap_initialize(&ld, st->url);
+  rc = ldap_initialize(&st->ld, st->url);
   if (rc != LDAP_SUCCESS) {
     ERROR("openldap plugin: ldap_initialize failed: %s", ldap_err2string(rc));
-    st->state = 0;
-    if (ld != NULL)
-      ldap_unbind_ext_s(ld, NULL, NULL);
+    if (st->ld != NULL)
+      ldap_unbind_ext_s(st->ld, NULL, NULL);
+    st->ld = NULL;
     return (-1);
   }
 
-  st->ld = ld;
-
   ldap_set_option(st->ld, LDAP_OPT_PROTOCOL_VERSION, &st->version);
 
   ldap_set_option(st->ld, LDAP_OPT_TIMEOUT,
@@ -115,13 +111,12 @@ static int cldap_init_host(cldap_t *st) /* {{{ */
   }
 
   if (st->starttls != 0) {
-    rc = ldap_start_tls_s(ld, NULL, NULL);
+    rc = ldap_start_tls_s(st->ld, NULL, NULL);
     if (rc != LDAP_SUCCESS) {
       ERROR("openldap plugin: Failed to start tls on %s: %s", st->url,
             ldap_err2string(rc));
-      st->state = 0;
-      if (st->ld != NULL)
-        ldap_unbind_ext_s(st->ld, NULL, NULL);
+      ldap_unbind_ext_s(st->ld, NULL, NULL);
+      st->ld = NULL;
       return (-1);
     }
   }
@@ -140,13 +135,11 @@ static int cldap_init_host(cldap_t *st) /* {{{ */
   if (rc != LDAP_SUCCESS) {
     ERROR("openldap plugin: Failed to bind to %s: %s", st->url,
           ldap_err2string(rc));
-    st->state = 0;
-    if (st->ld != NULL)
-      ldap_unbind_ext_s(st->ld, NULL, NULL);
+    ldap_unbind_ext_s(st->ld, NULL, NULL);
+    st->ld = NULL;
     return (-1);
   } else {
     DEBUG("openldap plugin: Successfully connected to %s", st->url);
-    st->state = 1;
     return 0;
   }
 } /* }}} static cldap_init_host */
@@ -216,9 +209,8 @@ static int cldap_read_host(user_data_t *ud) /* {{{ */
   if (rc != LDAP_SUCCESS) {
     ERROR("openldap plugin: Failed to execute search: %s", ldap_err2string(rc));
     ldap_msgfree(result);
-    st->state = 0;
-    if (st->ld != NULL)
-      ldap_unbind_ext_s(st->ld, NULL, NULL);
+    ldap_unbind_ext_s(st->ld, NULL, NULL);
+    st->ld = NULL;
     return (-1);
   }
 
@@ -463,42 +455,24 @@ static int cldap_config_add(oconfig_item_t *ci) /* {{{ */
     ldap_free_urldesc(ludpp);
   }
 
-  if (status == 0) {
-    cldap_t **temp;
-
-    temp = (cldap_t **)realloc(databases,
-                               sizeof(*databases) * (databases_num + 1));
-
-    if (temp == NULL) {
-      ERROR("openldap plugin: realloc failed");
-      status = -1;
-    } else {
-      char callback_name[3 * DATA_MAX_NAME_LEN] = {0};
-
-      databases = temp;
-      databases[databases_num] = st;
-      databases_num++;
-
-      snprintf(callback_name, sizeof(callback_name), "openldap/%s/%s",
-               (st->host != NULL) ? st->host : hostname_g,
-               (st->name != NULL) ? st->name : "default");
-
-      status = plugin_register_complex_read(/* group = */ NULL,
-                                            /* name      = */ callback_name,
-                                            /* callback  = */ cldap_read_host,
-                                            /* interval  = */ 0,
-                                            &(user_data_t){
-                                                .data = st,
-                                            });
-    }
-  }
-
   if (status != 0) {
     cldap_free(st);
     return -1;
   }
 
-  return 0;
+  char callback_name[3 * DATA_MAX_NAME_LEN] = {0};
+
+  snprintf(callback_name, sizeof(callback_name), "openldap/%s/%s",
+           (st->host != NULL) ? st->host : hostname_g,
+           (st->name != NULL) ? st->name : "default");
+
+  return plugin_register_complex_read(/* group = */ NULL,
+                                      /* name      = */ callback_name,
+                                      /* callback  = */ cldap_read_host,
+                                      /* interval  = */ 0,
+                                      &(user_data_t){
+                                          .data = st, .free_func = cldap_free,
+                                      });
 } /* }}} int cldap_config_add */
 
 static int cldap_config(oconfig_item_t *ci) /* {{{ */
@@ -532,22 +506,10 @@ static int cldap_init(void) /* {{{ */
   return 0;
 } /* }}} int cldap_init */
 
-static int cldap_shutdown(void) /* {{{ */
-{
-  for (size_t i = 0; i < databases_num; i++)
-    if (databases[i] != NULL && databases[i]->ld != NULL)
-      ldap_unbind_ext_s(databases[i]->ld, NULL, NULL);
-  sfree(databases);
-  databases_num = 0;
-
-  return 0;
-} /* }}} int cldap_shutdown */
-
 void module_register(void) /* {{{ */
 {
   plugin_register_complex_config("openldap", cldap_config);
   plugin_register_init("openldap", cldap_init);
-  plugin_register_shutdown("openldap", cldap_shutdown);
 } /* }}} void module_register */
 
 #if defined(__APPLE__)
index 143a770..a98649b 100644 (file)
@@ -23,6 +23,7 @@
  *   Florian octo Forster <octo at collectd.org>
  *   Marco Chiappero <marco at absence.it>
  *   Fabian Schuh <mail at xeroc.org>
+ *   Pavel Rochnyak <pavel2000 ngs.ru>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 
-#define V1STRING                                                               \
+/**
+ * There is two main kinds of OpenVPN status file:
+ * - for 'single' mode (point-to-point or client mode)
+ * - for 'multi' mode  (server with multiple clients)
+ *
+ * For 'multi' there is 3 versions of status file format:
+ * - version 1 - First version of status file: without line type tokens,
+ *   comma delimited for easy machine parsing. Currently used by default.
+ *   Added in openvpn-2.0-beta3.
+ * - version 2 - with line type tokens, with 'HEADER' line type, uses comma
+ *   as a delimiter.
+ *   Added in openvpn-2.0-beta15.
+ * - version 3 - The only difference from version 2 is delimiter: in version 3
+ *   tabs are used instead of commas. Set of fields is the same.
+ *   Added in openvpn-2.1_rc14.
+ *
+ * For versions 2/3 there may be different sets of fields in different
+ * OpenVPN versions.
+ *
+ * Versions 2.0, 2.1, 2.2:
+ * Common Name,Real Address,Virtual Address,
+ * Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)
+ *
+ * Version 2.3:
+ * Common Name,Real Address,Virtual Address,
+ * Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username
+ *
+ * Version 2.4:
+ * Common Name,Real Address,Virtual Address,Virtual IPv6 Address,
+ * Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,
+ * Client ID,Peer ID
+ *
+ * Current Collectd code tries to handle changes in this field set,
+ * if they are backward-compatible.
+ **/
+
+#define TITLE_SINGLE "OpenVPN STATISTICS\n"
+#define TITLE_V1 "OpenVPN CLIENT LIST\n"
+#define TITLE_V2 "TITLE"
+
+#define V1HEADER                                                               \
   "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n"
-#define V2STRING                                                               \
-  "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes "         \
-  "Received,Bytes Sent,Connected Since,Connected Since (time_t)\n"
-#define V3STRING                                                               \
-  "HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes "         \
-  "Received Bytes Sent Connected Since Connected Since (time_t)\n"
-#define V4STRING                                                               \
-  "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes "         \
-  "Received,Bytes Sent,Connected Since,Connected Since (time_t),Username\n"
-#define VSSTRING "OpenVPN STATISTICS\n"
 
 struct vpn_status_s {
   char *file;
-  enum {
-    MULTI1 = 1, /* status-version 1 */
-    MULTI2,     /* status-version 2 */
-    MULTI3,     /* status-version 3 */
-    MULTI4,     /* status-version 4 */
-    SINGLE = 10 /* currently no versions for single mode, maybe in the future */
-  } version;
   char *name;
 };
 typedef struct vpn_status_s vpn_status_t;
 
-static vpn_status_t **vpn_list = NULL;
-static int vpn_num = 0;
-
 static _Bool new_naming_schema = 0;
 static _Bool collect_compression = 1;
 static _Bool collect_user_count = 0;
@@ -71,16 +92,13 @@ static const char *config_keys[] = {
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
 /* Helper function
- * copy-n-pasted from common.c - changed delim to ","  */
+ * copy-n-pasted from common.c - changed delim to ",\t"  */
 static int openvpn_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, ",", &saveptr)) != NULL) {
+  size_t i = 0;
+  char *ptr = string;
+  char *saveptr = NULL;
+
+  while ((fields[i] = strtok_r(ptr, ",\t", &saveptr)) != NULL) {
     ptr = NULL;
     i++;
 
@@ -91,6 +109,13 @@ static int openvpn_strsplit(char *string, char **fields, size_t size) {
   return i;
 } /* int openvpn_strsplit */
 
+static void openvpn_free(void *arg) {
+  vpn_status_t *st = arg;
+
+  sfree(st->file);
+  sfree(st);
+} /* void openvpn_free */
+
 /* dispatches number of users */
 static void numusers_submit(const char *pinst, const char *tinst,
                             gauge_t value) {
@@ -159,25 +184,14 @@ static int single_read(const char *name, FILE *fh) {
   char buffer[1024];
   char *fields[4];
   const int max_fields = STATIC_ARRAY_SIZE(fields);
-  int fields_num, read = 0;
-
-  derive_t link_rx, link_tx;
-  derive_t tun_rx, tun_tx;
-  derive_t pre_compress, post_compress;
-  derive_t pre_decompress, post_decompress;
-  derive_t overhead_rx, overhead_tx;
-
-  link_rx = 0;
-  link_tx = 0;
-  tun_rx = 0;
-  tun_tx = 0;
-  pre_compress = 0;
-  post_compress = 0;
-  pre_decompress = 0;
-  post_decompress = 0;
+
+  derive_t link_rx = 0, link_tx = 0;
+  derive_t tun_rx = 0, tun_tx = 0;
+  derive_t pre_compress = 0, post_compress = 0;
+  derive_t pre_decompress = 0, post_decompress = 0;
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
-    fields_num = openvpn_strsplit(buffer, fields, max_fields);
+    int fields_num = openvpn_strsplit(buffer, fields, max_fields);
 
     /* status file is generated by openvpn/sig.c:print_status()
      * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/sig.c
@@ -213,8 +227,9 @@ static int single_read(const char *name, FILE *fh) {
   iostats_submit(name, "traffic", link_rx, link_tx);
 
   /* we need to force this order to avoid negative values with these unsigned */
-  overhead_rx = (((link_rx - pre_decompress) + post_decompress) - tun_rx);
-  overhead_tx = (((link_tx - post_compress) + pre_compress) - tun_tx);
+  derive_t overhead_rx =
+      (((link_rx - pre_decompress) + post_decompress) - tun_rx);
+  derive_t overhead_tx = (((link_tx - post_compress) + pre_compress) - tun_tx);
 
   iostats_submit(name, "overhead", overhead_rx, overhead_tx);
 
@@ -223,17 +238,16 @@ static int single_read(const char *name, FILE *fh) {
     compression_submit(name, "data_out", pre_compress, post_compress);
   }
 
-  read = 1;
-
-  return read;
+  return 0;
 } /* int single_read */
 
 /* for reading status version 1 */
 static int multi1_read(const char *name, FILE *fh) {
   char buffer[1024];
   char *fields[10];
-  int fields_num, found_header = 0;
+  const int max_fields = STATIC_ARRAY_SIZE(fields);
   long long sum_users = 0;
+  _Bool found_header = 0;
 
   /* read the file until the "ROUTING TABLE" line is found (no more info after)
    */
@@ -241,7 +255,7 @@ static int multi1_read(const char *name, FILE *fh) {
     if (strcmp(buffer, "ROUTING TABLE\n") == 0)
       break;
 
-    if (strcmp(buffer, V1STRING) == 0) {
+    if (strcmp(buffer, V1HEADER) == 0) {
       found_header = 1;
       continue;
     }
@@ -251,7 +265,7 @@ static int multi1_read(const char *name, FILE *fh) {
       /* we can't start reading data until this string is found */
       continue;
 
-    fields_num = openvpn_strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
+    int fields_num = openvpn_strsplit(buffer, fields, max_fields);
     if (fields_num < 4)
       continue;
 
@@ -276,325 +290,196 @@ static int multi1_read(const char *name, FILE *fh) {
   }
 
   if (ferror(fh))
-    return 0;
+    return -1;
+
+  if (found_header == 0) {
+    NOTICE("openvpn plugin: Unknown file format in instance %s, please "
+           "report this as bug. Make sure to include "
+           "your status file, so the plugin can "
+           "be adapted.",
+           name);
+    return -1;
+  }
 
   if (collect_user_count)
     numusers_submit(name, name, sum_users);
 
-  return 1;
+  return 0;
 } /* int multi1_read */
 
-/* for reading status version 2 */
+/* for reading status version 2 / version 3
+ * status file is generated by openvpn/multi.c:multi_print_status()
+ * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+ */
 static int multi2_read(const char *name, FILE *fh) {
   char buffer[1024];
-  char *fields[10];
+  /* OpenVPN-2.4 has 11 fields of data + 2 fields for "HEADER" and "CLIENT_LIST"
+   * So, set array size to 20 elements, to support future extensions.
+   */
+  char *fields[20];
   const int max_fields = STATIC_ARRAY_SIZE(fields);
-  int fields_num, read = 0;
   long long sum_users = 0;
 
-  while (fgets(buffer, sizeof(buffer), fh) != NULL) {
-    fields_num = openvpn_strsplit(buffer, fields, max_fields);
-
-    /* status file is generated by openvpn/multi.c:multi_print_status()
-     * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
-     *
-     * The line we're expecting has 8 fields. We ignore all lines
-     *  with more or less fields.
-     */
-    if (fields_num != 8)
-      continue;
-
-    if (strcmp(fields[0], "CLIENT_LIST") != 0)
-      continue;
-
-    if (collect_user_count)
-    /* If so, sum all users, ignore the individuals*/
-    {
-      sum_users += 1;
-    }
-    if (collect_individual_users) {
-      if (new_naming_schema) {
-        /* plugin inst = file name, type inst = fields[1] */
-        iostats_submit(name,              /* vpn instance */
-                       fields[1],         /* "Common Name" */
-                       atoll(fields[4]),  /* "Bytes Received" */
-                       atoll(fields[5])); /* "Bytes Sent" */
-      } else {
-        /* plugin inst = fields[1], type inst = "" */
-        iostats_submit(fields[1],         /* "Common Name" */
-                       NULL,              /* unused when in multimode */
-                       atoll(fields[4]),  /* "Bytes Received" */
-                       atoll(fields[5])); /* "Bytes Sent" */
-      }
-    }
-
-    read = 1;
-  }
-
-  if (collect_user_count) {
-    numusers_submit(name, name, sum_users);
-    read = 1;
-  }
-
-  return read;
-} /* int multi2_read */
-
-/* for reading status version 3 */
-static int multi3_read(const char *name, FILE *fh) {
-  char buffer[1024];
-  char *fields[15];
-  const int max_fields = STATIC_ARRAY_SIZE(fields);
-  int fields_num, read = 0;
-  long long sum_users = 0;
+  _Bool found_header = 0;
+  int idx_cname = 0;
+  int idx_bytes_recv = 0;
+  int idx_bytes_sent = 0;
+  int columns = 0;
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
-    fields_num = strsplit(buffer, fields, max_fields);
+    int fields_num = openvpn_strsplit(buffer, fields, max_fields);
 
-    /* status file is generated by openvpn/multi.c:multi_print_status()
-     * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
-     *
-     * The line we're expecting has 12 fields. We ignore all lines
-     *  with more or less fields.
-     */
-    if (fields_num != 12) {
-      continue;
-    } else {
-      if (strcmp(fields[0], "CLIENT_LIST") != 0)
+    /* Try to find section header */
+    if (found_header == 0) {
+      if (fields_num < 2)
+        continue;
+      if (strcmp(fields[0], "HEADER") != 0)
+        continue;
+      if (strcmp(fields[1], "CLIENT_LIST") != 0)
         continue;
 
-      if (collect_user_count)
-      /* If so, sum all users, ignore the individuals*/
-      {
-        sum_users += 1;
-      }
-
-      if (collect_individual_users) {
-        if (new_naming_schema) {
-          iostats_submit(name,              /* vpn instance */
-                         fields[1],         /* "Common Name" */
-                         atoll(fields[4]),  /* "Bytes Received" */
-                         atoll(fields[5])); /* "Bytes Sent" */
-        } else {
-          iostats_submit(fields[1],         /* "Common Name" */
-                         NULL,              /* unused when in multimode */
-                         atoll(fields[4]),  /* "Bytes Received" */
-                         atoll(fields[5])); /* "Bytes Sent" */
+      for (int i = 2; i < fields_num; i++) {
+        if (strcmp(fields[i], "Common Name") == 0) {
+          idx_cname = i - 1;
+        } else if (strcmp(fields[i], "Bytes Received") == 0) {
+          idx_bytes_recv = i - 1;
+        } else if (strcmp(fields[i], "Bytes Sent") == 0) {
+          idx_bytes_sent = i - 1;
         }
       }
 
-      read = 1;
-    }
-  }
-
-  if (collect_user_count) {
-    numusers_submit(name, name, sum_users);
-    read = 1;
-  }
+      DEBUG("openvpn plugin: found MULTI v2/v3 HEADER. "
+            "Column idx: cname: %d, bytes_recv: %d, bytes_sent: %d",
+            idx_cname, idx_bytes_recv, idx_bytes_sent);
 
-  return read;
-} /* int multi3_read */
+      if (idx_cname == 0 || idx_bytes_recv == 0 || idx_bytes_sent == 0)
+        break;
 
-/* for reading status version 4 */
-static int multi4_read(const char *name, FILE *fh) {
-  char buffer[1024];
-  char *fields[11];
-  const int max_fields = STATIC_ARRAY_SIZE(fields);
-  int fields_num, read = 0;
-  long long sum_users = 0;
+      /* Data row has 1 field ("HEADER") less than header row */
+      columns = fields_num - 1;
 
-  while (fgets(buffer, sizeof(buffer), fh) != NULL) {
-    fields_num = openvpn_strsplit(buffer, fields, max_fields);
+      found_header = 1;
+      continue;
+    }
 
-    /* status file is generated by openvpn/multi.c:multi_print_status()
-     * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
-     *
-     * The line we're expecting has 9 fields. We ignore all lines
-     *  with more or less fields.
+    /* Header already found. Check if the line is the section data.
+     * If no match, then section was finished and there is no more data.
+     * Empty section is OK too.
      */
-    if (fields_num != 9)
-      continue;
+    if (fields_num == 0 || strcmp(fields[0], "CLIENT_LIST") != 0)
+      break;
 
-    if (strcmp(fields[0], "CLIENT_LIST") != 0)
-      continue;
+    /* Check if the data line fields count matches header line. */
+    if (fields_num != columns) {
+      ERROR("openvpn plugin: File format error in instance %s: Fields count "
+            "mismatch.",
+            name);
+      return -1;
+    }
+
+    DEBUG("openvpn plugin: found MULTI v2/v3 CLIENT_LIST. "
+          "Columns: cname: %s, bytes_recv: %s, bytes_sent: %s",
+          fields[idx_cname], fields[idx_bytes_recv], fields[idx_bytes_sent]);
 
     if (collect_user_count)
-    /* If so, sum all users, ignore the individuals*/
-    {
       sum_users += 1;
-    }
+
     if (collect_individual_users) {
       if (new_naming_schema) {
         /* plugin inst = file name, type inst = fields[1] */
-        iostats_submit(name,              /* vpn instance */
-                       fields[1],         /* "Common Name" */
-                       atoll(fields[4]),  /* "Bytes Received" */
-                       atoll(fields[5])); /* "Bytes Sent" */
+        iostats_submit(name,                           /* vpn instance     */
+                       fields[idx_cname],              /* "Common Name"    */
+                       atoll(fields[idx_bytes_recv]),  /* "Bytes Received" */
+                       atoll(fields[idx_bytes_sent])); /* "Bytes Sent"     */
       } else {
-        /* plugin inst = fields[1], type inst = "" */
-        iostats_submit(fields[1],         /* "Common Name" */
-                       NULL,              /* unused when in multimode */
-                       atoll(fields[4]),  /* "Bytes Received" */
-                       atoll(fields[5])); /* "Bytes Sent" */
+        /* plugin inst = fields[idx_cname], type inst = "" */
+        iostats_submit(fields[idx_cname], /*              "Common Name"    */
+                       NULL,              /* unused when in multimode      */
+                       atoll(fields[idx_bytes_recv]),  /* "Bytes Received" */
+                       atoll(fields[idx_bytes_sent])); /* "Bytes Sent"     */
       }
     }
+  }
+
+  if (ferror(fh))
+    return -1;
 
-    read = 1;
+  if (found_header == 0) {
+    NOTICE("openvpn plugin: Unknown file format in instance %s, please "
+           "report this as bug. Make sure to include "
+           "your status file, so the plugin can "
+           "be adapted.",
+           name);
+    return -1;
   }
 
   if (collect_user_count) {
     numusers_submit(name, name, sum_users);
-    read = 1;
   }
 
-  return read;
-} /* int multi4_read */
+  return 0;
+} /* int multi2_read */
 
 /* read callback */
-static int openvpn_read(void) {
-  FILE *fh;
-  int read;
-
-  read = 0;
-
-  if (vpn_num == 0)
-    return 0;
-
-  /* call the right read function for every status entry in the list */
-  for (int i = 0; i < vpn_num; i++) {
-    int vpn_read = 0;
-
-    fh = fopen(vpn_list[i]->file, "r");
-    if (fh == NULL) {
-      char errbuf[1024];
-      WARNING("openvpn plugin: fopen(%s) failed: %s", vpn_list[i]->file,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
-
-      continue;
-    }
-
-    switch (vpn_list[i]->version) {
-    case SINGLE:
-      vpn_read = single_read(vpn_list[i]->name, fh);
-      break;
-
-    case MULTI1:
-      vpn_read = multi1_read(vpn_list[i]->name, fh);
-      break;
-
-    case MULTI2:
-      vpn_read = multi2_read(vpn_list[i]->name, fh);
-      break;
-
-    case MULTI3:
-      vpn_read = multi3_read(vpn_list[i]->name, fh);
-      break;
-
-    case MULTI4:
-      vpn_read = multi4_read(vpn_list[i]->name, fh);
-      break;
-    }
-
-    fclose(fh);
-    read += vpn_read;
-  }
-
-  return read ? 0 : -1;
-} /* int openvpn_read */
-
-static int version_detect(const char *filename) {
-  FILE *fh;
+static int openvpn_read(user_data_t *user_data) {
   char buffer[1024];
-  int version = 0;
+  int read = 0;
 
-  /* Sanity checking. We're called from the config handling routine, so
-   * better play it save. */
-  if ((filename == NULL) || (*filename == 0))
-    return 0;
+  vpn_status_t *st = user_data->data;
 
-  fh = fopen(filename, "r");
+  FILE *fh = fopen(st->file, "r");
   if (fh == NULL) {
     char errbuf[1024];
-    WARNING("openvpn plugin: Unable to read \"%s\": %s", filename,
+    WARNING("openvpn plugin: fopen(%s) failed: %s", st->file,
             sstrerror(errno, errbuf, sizeof(errbuf)));
-    return 0;
+
+    return -1;
   }
 
-  /* now search for the specific multimode data format */
-  while ((fgets(buffer, sizeof(buffer), fh)) != NULL) {
-    /* we look at the first line searching for SINGLE mode configuration */
-    if (strcmp(buffer, VSSTRING) == 0) {
-      DEBUG("openvpn plugin: found status file version SINGLE");
-      version = SINGLE;
-      break;
-    }
-    /* searching for multi version 1 */
-    else if (strcmp(buffer, V1STRING) == 0) {
-      DEBUG("openvpn plugin: found status file version MULTI1");
-      version = MULTI1;
-      break;
-    }
-    /* searching for multi version 2 */
-    else if (strcmp(buffer, V2STRING) == 0) {
-      DEBUG("openvpn plugin: found status file version MULTI2");
-      version = MULTI2;
-      break;
-    }
-    /* searching for multi version 3 */
-    else if (strcmp(buffer, V3STRING) == 0) {
-      DEBUG("openvpn plugin: found status file version MULTI3");
-      version = MULTI3;
-      break;
-    }
-    /* searching for multi version 4 */
-    else if (strcmp(buffer, V4STRING) == 0) {
-      DEBUG("openvpn plugin: found status file version MULTI4");
-      version = MULTI4;
-      break;
-    }
+  // Try to detect file format by its first line
+  if ((fgets(buffer, sizeof(buffer), fh)) == NULL) {
+    WARNING("openvpn plugin: failed to get data from: %s", st->file);
+    fclose(fh);
+    return -1;
   }
 
-  if (version == 0) {
-    /* This is only reached during configuration, so complaining to
-     * the user is in order. */
+  if (strcmp(buffer, TITLE_SINGLE) == 0) { // OpenVPN STATISTICS
+    DEBUG("openvpn plugin: found status file SINGLE");
+    read = single_read(st->name, fh);
+  } else if (strcmp(buffer, TITLE_V1) == 0) { // OpenVPN CLIENT LIST
+    DEBUG("openvpn plugin: found status file MULTI version 1");
+    read = multi1_read(st->name, fh);
+  } else if (strncmp(buffer, TITLE_V2, strlen(TITLE_V2)) == 0) { // TITLE
+    DEBUG("openvpn plugin: found status file MULTI version 2/3");
+    read = multi2_read(st->name, fh);
+  } else {
     NOTICE("openvpn plugin: %s: Unknown file format, please "
            "report this as bug. Make sure to include "
            "your status file, so the plugin can "
            "be adapted.",
-           filename);
+           st->file);
+    read = -1;
   }
-
   fclose(fh);
-
-  return version;
-} /* int version_detect */
+  return read;
+} /* int openvpn_read */
 
 static int openvpn_config(const char *key, const char *value) {
   if (strcasecmp("StatusFile", key) == 0) {
-    char *status_file, *status_name, *filename;
-    int status_version;
-    vpn_status_t *temp;
+    char callback_name[3 * DATA_MAX_NAME_LEN];
+    char *status_name;
 
-    /* try to detect the status file format */
-    status_version = version_detect(value);
-
-    if (status_version == 0) {
-      WARNING("openvpn plugin: unable to detect status version, "
-              "discarding status file \"%s\".",
-              value);
-      return 1;
-    }
-
-    status_file = sstrdup(value);
+    char *status_file = strdup(value);
     if (status_file == NULL) {
       char errbuf[1024];
-      WARNING("openvpn plugin: sstrdup failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("openvpn plugin: strdup failed: %s",
+            sstrerror(errno, errbuf, sizeof(errbuf)));
       return 1;
     }
 
     /* it determines the file name as string starting at location filename + 1
      */
-    filename = strrchr(status_file, (int)'/');
+    char *filename = strrchr(status_file, (int)'/');
     if (filename == NULL) {
       /* status_file is already the file name only */
       status_name = status_file;
@@ -603,50 +488,38 @@ static int openvpn_config(const char *key, const char *value) {
       status_name = filename + 1;
     }
 
-    /* scan the list looking for a clone */
-    for (int i = 0; i < vpn_num; i++) {
-      if (strcasecmp(vpn_list[i]->name, status_name) == 0) {
-        WARNING("openvpn plugin: status filename \"%s\" "
-                "already used, please choose a "
-                "different one.",
-                status_name);
-        sfree(status_file);
-        return 1;
-      }
-    }
-
-    /* create a new vpn element since file, version and name are ok */
-    temp = malloc(sizeof(*temp));
-    if (temp == NULL) {
+    /* create a new vpn element */
+    vpn_status_t *instance = calloc(1, sizeof(*instance));
+    if (instance == NULL) {
       char errbuf[1024];
       ERROR("openvpn plugin: malloc failed: %s",
             sstrerror(errno, errbuf, sizeof(errbuf)));
       sfree(status_file);
       return 1;
     }
-    temp->file = status_file;
-    temp->version = status_version;
-    temp->name = status_name;
-
-    vpn_status_t **tmp_list =
-        realloc(vpn_list, (vpn_num + 1) * sizeof(*vpn_list));
-    if (tmp_list == NULL) {
-      char errbuf[1024];
-      ERROR("openvpn plugin: realloc failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-
-      sfree(vpn_list);
-      sfree(temp->file);
-      sfree(temp);
-      return 1;
+    instance->file = status_file;
+    instance->name = status_name;
+
+    snprintf(callback_name, sizeof(callback_name), "openvpn/%s", status_name);
+
+    int status = plugin_register_complex_read(
+        /* group = */ "openvpn",
+        /* name      = */ callback_name,
+        /* callback  = */ openvpn_read,
+        /* interval  = */ 0,
+        &(user_data_t){
+            .data = instance, .free_func = openvpn_free,
+        });
+
+    if (status == EINVAL) {
+      WARNING("openvpn plugin: status filename \"%s\" "
+              "already used, please choose a "
+              "different one.",
+              status_name);
+      return -1;
     }
-    vpn_list = tmp_list;
-
-    vpn_list[vpn_num] = temp;
-    vpn_num++;
-
-    DEBUG("openvpn plugin: status file \"%s\" added", temp->file);
 
+    DEBUG("openvpn plugin: status file \"%s\" added", instance->file);
   } /* if (strcasecmp ("StatusFile", key) == 0) */
   else if ((strcasecmp("CollectCompression", key) == 0) ||
            (strcasecmp("Compression", key) == 0)) /* old, deprecated name */
@@ -683,18 +556,6 @@ static int openvpn_config(const char *key, const char *value) {
   return 0;
 } /* int openvpn_config */
 
-/* shutdown callback */
-static int openvpn_shutdown(void) {
-  for (int i = 0; i < vpn_num; i++) {
-    sfree(vpn_list[i]->file);
-    sfree(vpn_list[i]);
-  }
-
-  sfree(vpn_list);
-
-  return 0;
-} /* int openvpn_shutdown */
-
 static int openvpn_init(void) {
   if (!collect_individual_users && !collect_compression &&
       !collect_user_count) {
@@ -704,9 +565,6 @@ static int openvpn_init(void) {
     return -1;
   }
 
-  plugin_register_read("openvpn", openvpn_read);
-  plugin_register_shutdown("openvpn", openvpn_shutdown);
-
   return 0;
 } /* int openvpn_init */
 
index a1ac8d7..d2a00bf 100644 (file)
@@ -2616,6 +2616,12 @@ static int perl_config_plugin(pTHX_ oconfig_item_t *ci) {
   char *plugin;
   HV *config;
 
+  if (NULL == perl_threads) {
+    log_err("A `Plugin' block was encountered but no plugin was loaded yet. "
+            "Put the appropriate `LoadPlugin' option in front of it.");
+    return -1;
+  }
+
   dSP;
 
   if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
index a5531c2..78f05c5 100644 (file)
@@ -111,6 +111,7 @@ static int pagesize;
 
 static _Bool values_absolute = 1;
 static _Bool values_percentage = 0;
+static _Bool report_io = 1;
 
 static int swap_config(oconfig_item_t *ci) /* {{{ */
 {
@@ -136,6 +137,8 @@ static int swap_config(oconfig_item_t *ci) /* {{{ */
       cf_util_get_boolean(child, &values_absolute);
     else if (strcasecmp("ValuesPercentage", child->key) == 0)
       cf_util_get_boolean(child, &values_percentage);
+    else if (strcasecmp("ReportIO", child->key) == 0)
+      cf_util_get_boolean(child, &report_io);
     else
       WARNING("swap plugin: Unknown config option: \"%s\"", child->key);
   }
@@ -406,7 +409,8 @@ static int swap_read(void) /* {{{ */
   else
     swap_read_combined();
 
-  swap_read_io();
+  if (report_io)
+    swap_read_io();
 
   return 0;
 } /* }}} int swap_read */
@@ -726,8 +730,11 @@ static int swap_read(void) /* {{{ */
   reserved = (gauge_t)(pmemory.pgsp_rsvd * pagesize);
 
   swap_submit_usage(NULL, total - free, free, "reserved", reserved);
-  swap_submit_derive("in", (derive_t)pmemory.pgspins * pagesize);
-  swap_submit_derive("out", (derive_t)pmemory.pgspouts * pagesize);
+
+  if (report_io) {
+    swap_submit_derive("in", (derive_t)pmemory.pgspins * pagesize);
+    swap_submit_derive("out", (derive_t)pmemory.pgspouts * pagesize);
+  }
 
   return 0;
 } /* }}} int swap_read */
index 578e019..5fb5151 100644 (file)
@@ -55,6 +55,7 @@ typedef struct {
 typedef struct {
   char *file;
   char *sep;
+  char *plugin_name;
   char *instance;
 
   tbl_result_t *results;
@@ -92,6 +93,7 @@ static void tbl_result_clear(tbl_result_t *res) {
 static void tbl_setup(tbl_t *tbl, char *file) {
   tbl->file = sstrdup(file);
   tbl->sep = NULL;
+  tbl->plugin_name = NULL;
   tbl->instance = NULL;
 
   tbl->results = NULL;
@@ -103,6 +105,7 @@ static void tbl_setup(tbl_t *tbl, char *file) {
 static void tbl_clear(tbl_t *tbl) {
   sfree(tbl->file);
   sfree(tbl->sep);
+  sfree(tbl->plugin_name);
   sfree(tbl->instance);
 
   for (size_t i = 0; i < tbl->results_num; ++i)
@@ -256,6 +259,8 @@ static int tbl_config_table(oconfig_item_t *ci) {
 
     if (0 == strcasecmp(c->key, "Separator"))
       tbl_config_set_s(c->key, &tbl->sep, c);
+    else if (0 == strcasecmp(c->key, "Plugin"))
+      tbl_config_set_s(c->key, &tbl->plugin_name, c);
     else if (0 == strcasecmp(c->key, "Instance"))
       tbl_config_set_s(c->key, &tbl->instance, c);
     else if (0 == strcasecmp(c->key, "Result"))
@@ -367,7 +372,8 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields,
   vl.values = values;
   vl.values_len = STATIC_ARRAY_SIZE(values);
 
-  sstrncpy(vl.plugin, "table", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, (tbl->plugin_name != NULL) ? tbl->plugin_name : "table",
+           sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, tbl->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, res->type, sizeof(vl.type));
 
index 1049fb2..e4419b8 100644 (file)
@@ -41,7 +41,7 @@
 #include "plugin.h"
 #include "utils_time.h"
 
-#include <asm/msr-index.h>
+#include "msr-index.h"
 #include <cpuid.h>
 #ifdef HAVE_SYS_CAPABILITY_H
 #include <sys/capability.h>
@@ -585,7 +585,10 @@ static int submit_counters(struct thread_data *t, struct core_data *c,
 
   /* If not using logical core numbering, set core id */
   if (!config_lcn) {
-    snprintf(name, sizeof(name), "core%02d", c->core_id);
+    if (topology.num_packages > 1)
+      snprintf(name, sizeof(name), "pkg%02d-core%02d", p->package_id, c->core_id);
+    else
+      snprintf(name, sizeof(name), "core%02d", c->core_id);
   }
 
   if (do_core_cstate & (1 << 3))
index 8b83cf9..d51aa39 100644 (file)
@@ -25,8 +25,7 @@
 #include "plugin.h"
 
 #if KERNEL_LINUX
-#define UPTIME_FILE "/proc/uptime"
-/* Using /proc filesystem to retrieve the boot time, Linux only. */
+#include <sys/sysinfo.h>
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
@@ -53,8 +52,6 @@
 /*
  * Global variables
  */
-/* boottime always used, no OS distinction */
-static time_t boottime;
 
 #if HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
@@ -72,8 +69,6 @@ static void uptime_submit(gauge_t value) {
   plugin_dispatch_values(&vl);
 }
 
-static int uptime_init(void) /* {{{ */
-{
 /*
  * On most unix systems the uptime is calculated by looking at the boot
  * time (stored in unix time, since epoch) and the current one. We are
@@ -84,32 +79,21 @@ static int uptime_init(void) /* {{{ */
  * the boot time, the plugin is unregistered and there is no chance to
  * try again later. Nevertheless, this is very unlikely to happen.
  */
-
+static time_t uptime_get_sys(void) { /* {{{ */
+  time_t result;
 #if KERNEL_LINUX
-  FILE *fh = fopen(UPTIME_FILE, "r");
-  if (fh == NULL) {
+  struct sysinfo info;
+  int status;
+
+  status = sysinfo(&info);
+  if (status != 0) {
     char errbuf[1024];
-    ERROR("uptime plugin: Cannot open " UPTIME_FILE ": %s",
+    ERROR("uptime plugin: Error calling sysinfo: %s",
           sstrerror(errno, errbuf, sizeof(errbuf)));
     return -1;
   }
 
-  double uptime_seconds = 0.0;
-  if (fscanf(fh, "%lf", &uptime_seconds) != 1) {
-    ERROR("uptime plugin: No value read from " UPTIME_FILE "");
-    fclose(fh);
-    return -1;
-  }
-
-  fclose(fh);
-
-  if (uptime_seconds == 0.0) {
-    ERROR("uptime plugin: uptime read from " UPTIME_FILE ", "
-          "but it is zero!");
-    return -1;
-  }
-
-  boottime = time(NULL) - (long)uptime_seconds;
+  result = (time_t)info.uptime;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
@@ -143,13 +127,13 @@ static int uptime_init(void) /* {{{ */
     return -1;
   }
 
-  boottime = (time_t)knp->value.ui32;
-
-  if (boottime == 0) {
+  if (knp->value.ui32 == 0) {
     ERROR("uptime plugin: kstat_data_lookup returned success, "
           "but `boottime' is zero!");
     return -1;
   }
+
+  result = time(NULL) - (time_t)knp->value.ui32;
 /* #endif HAVE_LIBKSTAT */
 
 #elif HAVE_SYS_SYSCTL_H
@@ -170,13 +154,13 @@ static int uptime_init(void) /* {{{ */
     return -1;
   }
 
-  boottime = boottv.tv_sec;
-
-  if (boottime == 0) {
+  if (boottv.tv_sec == 0) {
     ERROR("uptime plugin: sysctl(3) returned success, "
           "but `boottime' is zero!");
     return -1;
   }
+
+  result = time(NULL) - boottv.tv_sec;
 /* #endif HAVE_SYS_SYSCTL_H */
 
 #elif HAVE_PERFSTAT
@@ -196,18 +180,18 @@ static int uptime_init(void) /* {{{ */
   if (hertz <= 0)
     hertz = HZ;
 
-  boottime = time(NULL) - cputotal.lbolt / hertz;
+  result = cputotal.lbolt / hertz;
 #endif /* HAVE_PERFSTAT */
 
-  return 0;
-} /* }}} int uptime_init */
+  return result;
+} /* }}} int uptime_get_sys */
 
 static int uptime_read(void) {
   gauge_t uptime;
   time_t elapsed;
 
   /* calculate the amount of time elapsed since boot, AKA uptime */
-  elapsed = time(NULL) - boottime;
+  elapsed = uptime_get_sys();
 
   uptime = (gauge_t)elapsed;
 
@@ -217,6 +201,5 @@ static int uptime_read(void) {
 }
 
 void module_register(void) {
-  plugin_register_init("uptime", uptime_init);
   plugin_register_read("uptime", uptime_read);
 } /* void module_register */
index 0f8c2a2..5eb5b6d 100644 (file)
@@ -105,6 +105,8 @@ int latency_config(latency_config_t *conf, oconfig_item_t *ci,
       status = latency_config_add_percentile(conf, child, plugin);
     else if (strcasecmp("Bucket", child->key) == 0)
       status = latency_config_add_bucket(conf, child, plugin);
+    else if (strcasecmp("BucketType", child->key) == 0)
+      status = cf_util_get_string(child, &conf->bucket_type);
     else
       WARNING("%s plugin: \"%s\" is not a valid option within a \"%s\" block.",
               plugin, child->key, ci->key);
@@ -137,6 +139,14 @@ int latency_config_copy(latency_config_t *dst, const latency_config_t src) {
     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));
@@ -147,4 +157,5 @@ int latency_config_copy(latency_config_t *dst, const latency_config_t src) {
 void latency_config_free(latency_config_t conf) {
   sfree(conf.percentile);
   sfree(conf.buckets);
+  sfree(conf.bucket_type);
 } /* void latency_config_free */
index 9a7a11a..7008fd0 100644 (file)
@@ -44,6 +44,7 @@ typedef struct {
 
   latency_bucket_t *buckets;
   size_t buckets_num;
+  char *bucket_type;
 
   /*
   _Bool lower;
index 242cc38..65655dc 100644 (file)
@@ -137,7 +137,11 @@ static int latency_submit_match(cu_match_t *match, void *user_data) {
   }
 
   /* Submit buckets */
-  sstrncpy(vl.type, "bucket", sizeof(vl.type));
+  if (data->latency_config.bucket_type != NULL)
+    sstrncpy(vl.type, data->latency_config.bucket_type, sizeof(vl.type));
+  else
+    sstrncpy(vl.type, "bucket", sizeof(vl.type));
+
   for (size_t i = 0; i < data->latency_config.buckets_num; i++) {
     latency_bucket_t bucket = data->latency_config.buckets[i];
 
index 5a029de..7dd5029 100644 (file)
@@ -45,6 +45,7 @@ struct wr_node_s {
   char *prefix;
   int database;
   int max_set_size;
+  int max_set_duration;
   _Bool store_rates;
 
   redisContext *conn;
@@ -125,6 +126,20 @@ static int wr_write(const data_set_t *ds, /* {{{ */
       freeReplyObject(rr);
   }
 
+  if (node->max_set_duration > 0) {
+    /*
+     * remove element, scored less than 'current-max_set_duration'
+     * '(%d' indicates 'less than' in redis CLI.
+     */
+    rr = redisCommand(node->conn, "ZREMRANGEBYSCORE %s -1 (%d", key,
+                      (time - node->max_set_duration) + 1);
+    if (rr == NULL)
+      WARNING("ZREMRANGEBYSCORE command error. key:%s message:%s", key,
+              node->conn->errstr);
+    else
+      freeReplyObject(rr);
+  }
+
   /* TODO(octo): This is more overhead than necessary. Use the cache and
    * metadata to determine if it is a new metric and call SADD only once for
    * each metric. */
@@ -175,6 +190,7 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */
   node->prefix = NULL;
   node->database = 0;
   node->max_set_size = -1;
+  node->max_set_duration = -1;
   node->store_rates = 1;
   pthread_mutex_init(&node->lock, /* attr = */ NULL);
 
@@ -205,6 +221,8 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_int(child, &node->database);
     } else if (strcasecmp("MaxSetSize", child->key) == 0) {
       status = cf_util_get_int(child, &node->max_set_size);
+    } else if (strcasecmp("MaxSetDuration", child->key) == 0) {
+      status = cf_util_get_int(child, &node->max_set_duration);
     } else if (strcasecmp("StoreRates", child->key) == 0) {
       status = cf_util_get_boolean(child, &node->store_rates);
     } else
index 730d304..e589184 100644 (file)
 /*
  * Global variables
  */
+static value_to_rate_state_t arc_hits_state;
+static value_to_rate_state_t arc_misses_state;
+static value_to_rate_state_t l2_hits_state;
+static value_to_rate_state_t l2_misses_state;
 
 #if defined(KERNEL_LINUX)
 #include "utils_llist.h"
@@ -313,14 +317,25 @@ static int za_read(void) {
   za_read_derive(ksp, "mru_hits", "cache_result", "mru-hit");
   za_read_derive(ksp, "mru_ghost_hits", "cache_result", "mru_ghost-hit");
 
+  cdtime_t now = cdtime();
+
   /* Ratios */
-  arc_hits = (gauge_t)get_zfs_value(ksp, "hits");
-  arc_misses = (gauge_t)get_zfs_value(ksp, "misses");
-  l2_hits = (gauge_t)get_zfs_value(ksp, "l2_hits");
-  l2_misses = (gauge_t)get_zfs_value(ksp, "l2_misses");
+  if ((value_to_rate(&arc_hits, (value_t){.derive = get_zfs_value(ksp, "hits")},
+                     DS_TYPE_DERIVE, now, &arc_hits_state) == 0) &&
+      (value_to_rate(&arc_misses,
+                     (value_t){.derive = get_zfs_value(ksp, "misses")},
+                     DS_TYPE_DERIVE, now, &arc_misses_state) == 0)) {
+    za_submit_ratio("arc", arc_hits, arc_misses);
+  }
 
-  za_submit_ratio("arc", arc_hits, arc_misses);
-  za_submit_ratio("L2", l2_hits, l2_misses);
+  if ((value_to_rate(&l2_hits,
+                     (value_t){.derive = get_zfs_value(ksp, "l2_hits")},
+                     DS_TYPE_DERIVE, now, &l2_hits_state) == 0) &&
+      (value_to_rate(&l2_misses,
+                     (value_t){.derive = get_zfs_value(ksp, "l2_misses")},
+                     DS_TYPE_DERIVE, now, &l2_misses_state) == 0)) {
+    za_submit_ratio("L2", l2_hits, l2_misses);
+  }
 
   /* I/O */
   value_t l2_io[] = {