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.
# ReportByCpu true
# ReportByState true
# ValuesPercentage false
+# ReportNumCpu false
+# ReportGuestState false
+# SubtractGuestState true
#</Plugin>
#
#<Plugin csv>
# 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>
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>
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>
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
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.
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
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:
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>
<Plugin table>
<Table "/proc/slabinfo">
+ #Plugin "slab"
Instance "slabinfo"
Separator " "
<Result>
=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<_>).
<DSType "Distribution">
Percentile 99
Bucket 0 100
+ #BucketType "bucket"
</DSType>
Type "latency"
Instance "foo"
<DSType "Distribution">
Percentile 99
Bucket 0 100
+ BucketType "bucket"
</DSType>
=over 4
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
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
=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.
=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
#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;
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) /* {{{ */
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;
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)
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)
FILE *fh;
char buf[1024];
- char *fields[9];
+ char *fields[11];
int numfields;
if ((fh = fopen("/proc/stat", "r")) == NULL) {
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);
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) */
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;
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)));
#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"
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;
while (1) {
int c;
- c = getopt(argc, argv, "htTC:"
+ c = getopt(argc, argv, "BhtTC:"
#if COLLECT_DAEMON
"fP:"
#endif
break;
switch (c) {
+ case 'B':
+ opt_create_basedir = 0;
+ break;
case 'C':
configfile = optarg;
break;
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;
}
* 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) {
* 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) {
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);
}
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;
}
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;
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;
} 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;
}
/* 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"
char *password;
char *cacert;
char *host;
- int state;
_Bool starttls;
int timeout;
char *url;
};
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;
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,
}
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);
}
}
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 */
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);
}
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) /* {{{ */
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__)
* 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;
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++;
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) {
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
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);
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)
*/
if (strcmp(buffer, "ROUTING TABLE\n") == 0)
break;
- if (strcmp(buffer, V1STRING) == 0) {
+ if (strcmp(buffer, V1HEADER) == 0) {
found_header = 1;
continue;
}
/* 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;
}
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;
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 */
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) {
return -1;
}
- plugin_register_read("openvpn", openvpn_read);
- plugin_register_shutdown("openvpn", openvpn_shutdown);
-
return 0;
} /* int openvpn_init */
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)) {
static _Bool values_absolute = 1;
static _Bool values_percentage = 0;
+static _Bool report_io = 1;
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);
}
else
swap_read_combined();
- swap_read_io();
+ if (report_io)
+ swap_read_io();
return 0;
} /* }}} int swap_read */
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 */
typedef struct {
char *file;
char *sep;
+ char *plugin_name;
char *instance;
tbl_result_t *results;
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;
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)
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"))
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));
/* 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))
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);
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));
void latency_config_free(latency_config_t conf) {
sfree(conf.percentile);
sfree(conf.buckets);
+ sfree(conf.bucket_type);
} /* void latency_config_free */
latency_bucket_t *buckets;
size_t buckets_num;
+ char *bucket_type;
/*
_Bool lower;
}
/* 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];
char *prefix;
int database;
int max_set_size;
+ int max_set_duration;
_Bool store_rates;
redisContext *conn;
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. */
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);
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
/*
* 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"
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[] = {