- LPAR plugin.
- Various fixes for AIX, HP-UX and Solaris.
-Benjamin Gilbert <bgilbert at cs.cmu.edu>
+Benjamin Gilbert <bgilbert at backtick.net>
- Improvements to the LVM plugin.
Bert Vermeulen <bert at biot.com>
Thanks for taking the time to contribute to the [collectd
project](https://collectd.org/)! This document tries to give some guidance to
-make the process of contributing to *collectd* as pleasant and possible.
+make the process of contributing to *collectd* as pleasant as possible.
## Bug reports
[stack trace](https://collectd.org/wiki/index.php/Core_file).
Please monitor your issue for a couple of days and reply to questions. To keep
-the project manageable have to do some housekeeping, meaning we will close
+the project manageable, we have to do some housekeeping; meaning we will close
issues that have become stale.
## Code contributions
coding style of the code around your changes.
* *Documentation:* New config options need to be documented in two places: the
manpage (`src/collectd.conf.pod`) and the example config
- (`src/collectd.conf.in`).
+ (`src/collectd.conf.in`). New plugins need to be added to the `README` file.
* *Continuous integration:* Once your PR is created, our continuous
integration environment will try to build it on a number of platforms. If
this reports a failure, please investigate and fix the problem. We will at
+2017-01-23, Version 5.7.1
+ * collectd: Handling of boolean configuration options has been unified.
+ Thanks to Sebastian Harl. #2083, #2098
+ * collectd: Reporting of internal statistics has been fixed. Thanks to
+ Florian Forster. #2108
+ * collectd, various plugins: Bugs and issues reported by scan-build and
+ coverity-scan have been fixed. Thanks to Ruben Kerkhof and Florian
+ Forster.
+ * Build system: Parallel build have been fixed. Thanks to Ruben Kerkhof.
+ #2110
+ * DPDKStat plugin: Portability issues and a double-close bug have been
+ fixed. Thanks to Ruben Kerkhof and Marc Fournier.
+ * Intel RDT plugin: A check for the libpqos library version has been
+ added. Thanks to Serhiy Pshyk.
+ * NetApp plugin: Compilation problems have been corrected. Thanks to
+ Florian Forster. #2120
+ * Write Prometheus plugin: A memory leak has been fixed. Thanks to Ruben
+ Kerkhof.
+
+2016-12-12, Version 5.7.0
+ * Documentation: The Turbostat plugin section has been improved. Thanks
+ to Florian Forster
+ * Documentation: The semantics of the "TypesDB" option have been
+ improved. Thanks to Florian Forster.
+ * collectd: A generic interface for parsing the text protocol has been
+ added. Thanks to Sebastian Harl. #1749
+ * collectd: Threads now get named, making them easier to track using
+ tools such as top, ps, etc. Thanks to Manuel Luis Sanmartín Rozada
+ and Marc Fournier. #547
+ * AMQP plugin, Write Graphite plugin, Write Kafka plugin: The new
+ "[Graphite]PreserveSeparator" option allows retaining the default dot
+ separator rather than escaping it. Thanks to Florian Forster. #419
+ * Battery plugin: A StateFS backend for gathering statistics has been
+ added. Thanks to Rinigus. #1795
+ * CPU plugin: CPU aggregation on AIX was fixed. Thanks to Chao Yang.
+ #1957
+ * Collectd::Unixsock: Fractional seconds support has been made more
+ robust. Thanks to Matthias Bethke. #2052
+ * DPDKStat plugin: This new plugin collects DPDK interface statistics.
+ Thanks to Maryam Tahhan, Harry van Haaren, Taras Chornyi and Kim
+ Jones. #1649
+ * gRPC plugin: The "DispatchValues" option has been renamed to
+ "PutValues". Thanks to Florian Forster.
+ * HDDTemp plugin: The 32 devices limit has been removed. Thanks to
+ Benjamin Gilbert. #631
+ * Hugepages plugin: This new plugin reports the number of used and free
+ hugepages on Linux. Thanks to Jaroslav Safka, Maryam Tahhan, Kim Jones
+ and Florian Forster. #1799
+ * Intel RDT plugin: This new plugin collects statistics exposed by
+ Intel's Resource Director Technology . Thanks to SerhiyX. #1970
+ * memcached plugin: The new "Address" option allows connecting to a
+ different server than specified by the "Host" option. Thanks to Pavel
+ Rochnyack. #1975
+ * nginx plugin: Support for reporting failed connections has been added.
+ Thanks to Pavel Rochnyack. #1609
+ * Perl plugin: Significant internal reworking has been made. The new
+ "RegisterLegacyFlush" option has been added. Thanks to Pavel
+ Rochnyack. #1731
+ * PostgreSQL plugin: Timestamps are now RFC 3339-formatted local time.
+ Thanks to Igor Peshansky and Dave Cunningham. #1918
+ * Processes plugin: Internal performance improvements have been made.
+ Thanks to Pavel Rochnyack. #1980, #1981
+ * RRDCacheD plugin: The plugin now tries to reconnect upon failed
+ operations. Thanks to Sebastian Harl. #1959
+ * SpamAssassin: The plugin can now run in Perl's "tainted mode" ("-T").
+ Thanks to Akos Vandra. #1962
+ * Tail plugin: Support for calculating latency distribution of matching
+ values has been added. Thanks to Pavel Rochnyack and Florian Forster.
+ #1700
+ * Tail plugin: The new "GaugePersist" option has been added. Thanks to
+ Florian Forster. #2015
+ * Target:Set, Target:Replace, Match:RegEx: MetaData support has been
+ added . Thanks to Igor Peshansky. #1922, #1923, #1930
+ * turbostat plugin: The new "LogicalCoreNames" option allows switching
+ to per-core naming rather than per-CPU. Thanks to Brock Johnson. #2056
+ * virt plugin: The new "BlockDeviceFormat" and
+ "BlockDeviceFormatBasename" options help controlling the names
+ reported for block-device metrics. Thanks to Deyan Chepishev. #2004
+ * Write Graphite plugin: A new "DropDuplicateFields" option has been
+ added. Thanks to Michael Leinartas. #1915
+ * Write Kafka plugin: The "Key Random" setting has been reintroduced.
+ Thanks to Florian Forster. #1977
+ * Write Log plugin: Support for formatting output in JSON has been
+ added, using the new "Format" configuration option. Thanks to Igor
+ Peshansky. #1924
+ * Write Prometheus plugin: This new plugin publishes values using an
+ embedded HTTP server, in a format compatible with Prometheus'
+ collectd_exporter. Thanks to Florian Forster. #1967
+
2016-11-30, Version 5.6.2
* collectd: A compile error on AIX has been fixed: "MSG_DONTWAIT" is not
available on AIX. Thanks to Chao Yang.
DNS traffic: Query types, response codes, opcodes and traffic/octets
transferred.
+ - dpdkstat
+ Collect DPDK interface statistics.
+ See docs/BUILD.dpdkstat.md for detailed build instructions.
+
- drbd
Collect individual drbd resource statistics.
- gps
Monitor gps related data through gpsd.
- - grpc
- Receive values over the network using the gRPC framework.
-
- hddtemp
Hard disk temperatures using hddtempd.
+ - hugepages
+ Report the number of used and free hugepages. More info on
+ hugepages can be found here:
+ https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt.
+
+ - intel_rdt
+ The intel_rdt plugin collects information provided by monitoring features
+ of Intel Resource Director Technology (Intel(R) RDT) like Cache Monitoring
+ Technology (CMT), Memory Bandwidth Monitoring (MBM). These features
+ provide information about utilization of shared resources like last level
+ cache occupancy, local memory bandwidth usage, remote memory bandwidth
+ usage, instructions per clock.
+ <https://01.org/packet-processing/cache-monitoring-technology-memory-bandwidth-monitoring-cache-allocation-technology-code-and-data>
+
- interface
Interface traffic: Number of octets, packets and errors for each
interface.
- java
Integrates a `Java Virtual Machine' (JVM) to execute plugins in Java
- bytecode. See “Configuring with libjvm” below.
+ bytecode.
+ See docs/BUILD.java.md for detailed build instructions.
- load
System load average over the last 1, 5 and 15 minutes.
Queries very detailed usage statistics from wireless LAN adapters and
interfaces that use the Atheros chipset and the MadWifi driver.
- - mbmon
- Motherboard sensors: temperature, fan speed and voltage information,
- using mbmon(1).
-
- md
Linux software-RAID device information (number of active, failed, spare
and missing disks).
+ - mbmon
+ Motherboard sensors: temperature, fan speed and voltage information,
+ using mbmon(1).
+
- memcachec
Query and parse data from a memcache daemon (memcached).
Reads values from Modbus/TCP enabled devices. Supports reading values
from multiple "slaves" so gateway devices can be used.
- - mqtt
- Publishes and subscribes to MQTT topics.
-
- multimeter
Information provided by serial multimeters, such as the `Metex
M-4650CR'.
CPU, memory, disk and network I/O statistics from virtual machines.
- vmem
- Virtual memory statistics, e. g. the number of page-ins/-outs or the
+ Virtual memory statistics, e.g. the number of page-ins/-outs or the
number of pagefaults.
- vserver
diskspace but is extremely portable and can be analysed with almost
every program that can analyse anything. Even Microsoft's Excel..
+ - grpc
+ Send and receive values over the network using the gRPC framework.
+
- lua
It's possible to implement write plugins in Lua using the Lua
plugin. See collectd-lua(5) for details.
+ - mqtt
+ Publishes and subscribes to MQTT topics.
+
- network
Send the data to a remote host to save the data somehow. This is useful
for large setups where the data should be saved by a dedicated machine.
- write_mongodb
Sends data to MongoDB, a NoSQL database.
+ - write_prometheus
+ Publish values using an embedded HTTP server, in a format compatible
+ with Prometheus' collectd_exporter.
+
- write_redis
Sends the values to a Redis key-value database server.
* Usual suspects: C compiler, linker, preprocessor, make, ...
+ collectd makes use of some common C99 features, e.g. compound literals and
+ mixed declarations, and therefore requires a C99 compatible compiler.
+
+ On Debian and Ubuntu, the "build-essential" package should pull in
+ everything that's necessary.
+
* A POSIX-threads (pthread) implementation.
Since gathering some statistics is slow (network connections, slow devices,
etc) collectd is parallelized. The POSIX threads interface is being
used and should be found in various implementations for hopefully all
platforms.
+ * When building from the Git repository, flex (tokenizer) and bison (parser
+ generator) are required. Release tarballs include the generated files – you
+ don't need these packages in that case.
+
* aerotools-ng (optional)
Used by the `aquaero' plugin. Currently, the `libaquaero5' library, which
is used by the `aerotools-ng' toolkit, is not compiled as a shared object
* libjvm (optional)
Library that encapsulates the `Java Virtual Machine' (JVM). This library is
- used by the `java' plugin to execute Java bytecode. See “Configuring with
- libjvm” below.
+ used by the `java' plugin to execute Java bytecode.
+ See docs/BUILD.java.md for detailed build instructions.
<http://openjdk.java.net/> (and others)
* libldap (optional)
Used by the `memcachec' plugin to connect to a memcache daemon.
<http://tangent.org/552/libmemcached.html>
+ * libmicrohttpd (optional)
+ Used by the write_prometheus plugin to run an http daemon.
+ <http://www.gnu.org/software/libmicrohttpd/>
+
* libmnl (optional)
Used by the `netlink' plugin.
<http://www.netfilter.org/projects/libmnl/>
The PostgreSQL C client library used by the `postgresql' plugin.
<http://www.postgresql.org/>
+ * libpqos (optional)
+ The PQoS library for Intel(R) Resource Director Technology used by the
+ `intel_rdt' plugin.
+ <https://github.com/01org/intel-cmt-cat>
+
* libprotobuf, protoc 3.0+ (optional)
Used by the `grpc' plugin to generate service stubs and code to handle
network packets of collectd's protobuf-based network protocol.
prefixed to all installation directories. This might be useful when creating
packages for collectd.
-Configuring with libjvm
------------------------
-
- To determine the location of the required files of a Java installation is not
- an easy task, because the locations vary with your kernel (Linux, SunOS, …)
- and with your architecture (x86, SPARC, …) and there is no ‘java-config’
- script we could use. Configuration of the JVM library is therefore a bit
- tricky.
-
- The easiest way to use the `--with-java=$JAVA_HOME' option, where
- `$JAVA_HOME' is usually something like:
- /usr/lib/jvm/java-1.5.0-sun-1.5.0.14
-
- The configure script will then use find(1) to look for the following files:
-
- - jni.h
- - jni_md.h
- - libjvm.so
-
- If found, appropriate CPP-flags and LD-flags are set and the following
- library checks succeed.
-
- If this doesn't work for you, you have the possibility to specify CPP-flags,
- C-flags, LD-flags and LIBS for the ‘Java’ plugin by hand, using the
- following environment variables:
-
- - JAVA_CPPFLAGS
- - JAVA_CFLAGS
- - JAVA_LDFLAGS
- - JAVA_LIBS
-
- For example (shortened for demonstration purposes):
-
- ./configure JAVA_CPPFLAGS="-I$JAVA_HOME/include -I$JAVA_HOME/include/linux"
-
- Adding "-ljvm" to JAVA_LIBS is done automatically, you don't have to
- do that.
-
Generating the configure script
-------------------------------
The `build.sh' script takes no arguments.
+
Crosscompiling
--------------
Contact
-------
- For questions, bug reports, development information and basically all other
- concerns please send an email to collectd's mailing list at
+ Please use GitHub to report bugs and submit pull requests:
+ <https://github.com/collectd/collectd/>.
+ See CONTRIBUTING.md for details.
+
+ For questions, development information and basically all other concerns please
+ send an email to collectd's mailing list at
<list at collectd.org>.
For live discussion and more personal contact visit us in IRC, we're in
Sebastian tokkee Harl <sh at tokkee.org>,
and many contributors (see `AUTHORS').
- Please use GitHub reporting bugs and submitting pull requests.
- See CONTRIBUTING.md for details.
+++ /dev/null
-* Finalize the onewire plugin.
-* Custom notification messages?
-* Implement moving-average calculation for the threshold stuff.
-
-src/battery.c: commend not working code.
-
-Wishlist:
-* Port nfs module to solaris
-* Port tape module to Linux
-* Port the apple_sensors plugin to Linux/PPC.
-* Maybe look into porting the serial module
-* Build Darwin package
-* Maybe let the network plugin configure whether or not notifications should be
- sent/received.
-* Maybe find a way for processes connected to the unixsock plugin to receive
- notifications, too.
-
-http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_IOKitLib_API/chapter_5_section_1.html
-http://developer.apple.com/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/index.html#//apple_ref/doc/uid/TP0000011
-http://www.gauchosoft.com/Software/X%20Resource%20Graph/
-http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification/
my $type = shift;
my %plugins;
- my $interval;
our $cb_name = undef;
}
if (TYPE_LOG != $type) {
- DEBUG ("Collectd::plugin_call: type = \"$type\" ("
+ DEBUG ("Collectd::plugin_call_all: type = \"$type\" ("
. $types{$type} . "), args=\""
. join(', ', map { defined($_) ? $_ : '<undef>' } @_) . "\"");
}
if (! defined $plugins[$type]) {
- ERROR ("Collectd::plugin_call: unknown type \"$type\"");
+ ERROR ("Collectd::plugin_call_all: unknown type \"$type\"");
return;
}
%plugins = %{$plugins[$type]};
}
- $interval = plugin_get_interval ();
-
foreach my $plugin (keys %plugins) {
- my $p = $plugins{$plugin};
-
- my $status = 0;
-
- if ($p->{'wait_left'} > 0) {
- $p->{'wait_left'} -= $interval;
- }
-
- next if ($p->{'wait_left'} > 0);
-
- $cb_name = $p->{'cb_name'};
- $status = call_by_name (@_);
+ $cb_name = $plugins{$plugin};
+ my $status = call_by_name (@_);
if (! $status) {
my $err = undef;
}
if ($status) {
- $p->{'wait_left'} = 0;
- $p->{'wait_time'} = $interval;
- }
- elsif (TYPE_READ == $type) {
- if ($p->{'wait_time'} < $interval) {
- $p->{'wait_time'} = $interval;
- }
-
- $p->{'wait_left'} = $p->{'wait_time'};
- $p->{'wait_time'} *= 2;
-
- if ($p->{'wait_time'} > 86400) {
- $p->{'wait_time'} = 86400;
- }
-
- WARNING ("${plugin}->read() failed with status $status. "
- . "Will suspend it for $p->{'wait_left'} seconds.");
+ #NOOP
}
elsif (TYPE_INIT == $type) {
ERROR ("${plugin}->init() failed with status $status. "
}
elsif ((TYPE_DATASET != $type) && (! ref $data)) {
my $pkg = scalar caller;
-
- my %p : shared;
-
if ($data !~ m/^$pkg\:\:/) {
$data = $pkg . "::" . $data;
}
-
- %p = (
- wait_time => plugin_get_interval (),
- wait_left => 0,
- cb_name => $data,
- );
-
+ if (TYPE_READ == $type) {
+ return plugin_register_read($name, $data);
+ }
+ if (TYPE_WRITE == $type) {
+ return plugin_register_write($name, $data);
+ }
+ if (TYPE_LOG == $type) {
+ return plugin_register_log($name, $data);
+ }
+ if (TYPE_NOTIF == $type) {
+ return plugin_register_notification($name, $data);
+ }
+ if (TYPE_FLUSH == $type) {
+ #For collectd-5.6 only
+ lock %{$plugins[$type]};
+ $plugins[$type]->{$name} = $data;
+ return plugin_register_flush($name, $data);
+ }
lock %{$plugins[$type]};
- $plugins[$type]->{$name} = \%p;
+ $plugins[$type]->{$name} = $data;
}
else {
ERROR ("Collectd::plugin_register: Invalid data.");
lock %cf_callbacks;
delete $cf_callbacks{$name};
}
+ elsif (TYPE_READ == $type) {
+ return plugin_unregister_read ($name);
+ }
+ elsif (TYPE_WRITE == $type) {
+ return plugin_unregister_write($name);
+ }
+ elsif (TYPE_LOG == $type) {
+ return plugin_unregister_log ($name);
+ }
+ elsif (TYPE_NOTIF == $type) {
+ return plugin_unregister_notification($name);
+ }
+ elsif (TYPE_FLUSH == $type) {
+ return plugin_unregister_flush($name);
+ }
elsif (defined $plugins[$type]) {
lock %{$plugins[$type]};
delete $plugins[$type]->{$name};
}
sub listval {
- my $now = time;
+ my @timevals = (
+ 1479835353.75,
+ 1479835354.434,
+ 1479835356,
+ 1479835354,
+ 1479835354,
+ 1479835350.820,
+ 1479835351,
+ 1479835354.2,
+ 1479835353,
+ );
+ my $i = 0;
return print_nvalues(scalar @metrics) .
- join('', map { "$now $_\n" } @metrics);
+ join('', map { $timevals[$i++ % @timevals] . " $_\n" } @metrics);
}
sub getval {
sub _escape_argument
{
- my $arg = shift;
+ my $arg = shift;
return $arg if $arg =~ /^\w+$/;
my $fh = $self->{sock} or confess ('object has no filehandle');
- if($args) {
- my $identifier = _create_identifier ($args) or return;
- $command .= ' ' . _escape_argument ($identifier) . "\n";
- } else {
- $command .= "\n";
- }
+ if($args) {
+ my $identifier = _create_identifier ($args) or return;
+ $command .= ' ' . _escape_argument ($identifier) . "\n";
+ } else {
+ $command .= "\n";
+ }
_debug "-> $command";
$fh->print($command);
my $response = $fh->getline;
chomp $response;
_debug "<- $response\n";
- return $response;
+ return $response;
}
# Read any remaining results from a socket and pass them to
my $entry = $fh->getline;
chomp $entry;
_debug "<- $entry\n";
- $callback->($entry, $cbdata);
+ $callback->($entry, $cbdata);
}
return $cbdata;
}
my %args = @_;
my $ret = {};
- my $msg = $self->_socket_command('GETVAL', \%args) or return;
- $self->_socket_chat($msg, sub {
- local $_ = shift;
- my $ret = shift;
- /^(\w+)=NaN$/ and $ret->{$1} = undef, return;
- /^(\w+)=(.*)$/ and looks_like_number($2) and $ret->{$1} = 0 + $2, return;
- }, $ret
- );
+ my $msg = $self->_socket_command('GETVAL', \%args) or return;
+ $self->_socket_chat($msg, sub {
+ local $_ = shift;
+ my $ret = shift;
+ /^(\w+)=NaN$/ and $ret->{$1} = undef, return;
+ /^(\w+)=(.*)$/ and looks_like_number($2) and $ret->{$1} = 0 + $2, return;
+ }, $ret
+ );
return $ret;
} # }}} sub getval
my %args = @_;
my $ret = {};
- my $msg = $self->_socket_command('GETTHRESHOLD', \%args) or return;
- $self->_socket_chat($msg, sub {
- local $_ = shift;
- my $ret = shift;
- my ( $key, $val );
- ( $key, $val ) = /^\s*([^:]+):\s*(.*)/ and do {
- $key =~ s/\s*$//;
- $ret->{$key} = $val;
- };
- }, $ret
- );
+ my $msg = $self->_socket_command('GETTHRESHOLD', \%args) or return;
+ $self->_socket_chat($msg, sub {
+ local $_ = shift;
+ my $ret = shift;
+ my ( $key, $val );
+ ( $key, $val ) = /^\s*([^:]+):\s*(.*)/ and do {
+ $key =~ s/\s*$//;
+ $ret->{$key} = $val;
+ };
+ }, $ret
+ );
return $ret;
} # }}} sub getthreshold
my $fh = $self->{sock} or confess;
my $interval = defined $args{interval} ?
- ' interval=' . _escape_argument ($args{interval}) : '';
+ ' interval=' . _escape_argument ($args{interval}) : '';
$identifier = _create_identifier (\%args) or return;
if (!$args{values})
sub listval_filter
{
my $self = shift;
- my %args = @_;
+ my %args = @_;
my @ret;
my $nresults;
my $fh = $self->{sock} or confess;
- my $pattern =
- (exists $args{host} ? "$args{host}" : '[^/]+') .
- (exists $args{plugin} ? "/$args{plugin}" : '/[^/-]+') .
+ my $pattern =
+ (exists $args{host} ? "$args{host}" : '[^/]+') .
+ (exists $args{plugin} ? "/$args{plugin}" : '/[^/-]+') .
(exists $args{plugin_instance} ? "-$args{plugin_instance}" : '(?:-[^/]+)?') .
(exists $args{type} ? "/$args{type}" : '/[^/-]+') .
(exists $args{type_instance} ? "-$args{type_instance}" : '(?:-[^/]+)?');
- $pattern = qr/^\d+ $pattern$/;
+ $pattern = qr/^\d+(?:\.\d+)? $pattern$/;
- my $msg = $self->_socket_command('LISTVAL') or return;
+ my $msg = $self->_socket_command('LISTVAL') or return;
($nresults, $msg) = split / /, $msg, 2;
- # This could use _socket_chat() but doesn't for speed reasons
+ # This could use _socket_chat() but doesn't for speed reasons
if ($nresults < 0)
{
$self->{error} = $msg;
my ($time, $ident) = split / /, $msg, 2;
$ident = _parse_identifier ($ident);
- $ident->{time} = int $time;
+ $ident->{time} = 0+$time;
push (@ret, $ident);
- } # for (i = 0 .. $status)
+ } # for (i = 0 .. $nresults)
return @ret;
-} # listval
+} # listval_filter
=item I<$res> = I<$self>-E<gt>B<listval> ()
my @ret;
my $fh = $self->{sock} or confess;
- my $msg = $self->_socket_command('LISTVAL') or return;
+ my $msg = $self->_socket_command('LISTVAL') or return;
($nresults, $msg) = split / /, $msg, 2;
- # This could use _socket_chat() but doesn't for speed reasons
+ # This could use _socket_chat() but doesn't for speed reasons
if ($nresults < 0)
{
$self->{error} = $msg;
my ($time, $ident) = split / /, $msg, 2;
$ident = _parse_identifier ($ident);
- $ident->{time} = int $time;
+ $ident->{time} = 0+$time;
push (@ret, $ident);
- } # for (i = 0 .. $status)
+ } # for (i = 0 .. $nresults)
return @ret;
} # listval
my $msg; # message sent to the socket
- for my $arg (qw( message severity ))
- {
- cluck ("Need argument `$arg'"), return unless $args{$arg};
- }
+ for my $arg (qw( message severity ))
+ {
+ cluck ("Need argument `$arg'"), return unless $args{$arg};
+ }
$args{severity} = lc $args{severity};
if (($args{severity} ne 'failure')
&& ($args{severity} ne 'warning')
my $fh = $self->{sock} or confess;
- my $msg = "FLUSH";
+ my $msg = "FLUSH";
- $msg .= " timeout=$args{timeout}" if defined $args{timeout};
+ $msg .= " timeout=$args{timeout}" if defined $args{timeout};
if ($args{plugins})
{
$self->_send_message($msg) or return;
$msg = $pre;
}
-
+
$msg .= $ident_str;
}
}
-
+
return $self->_send_message($msg);
}
=cut
1;
-# vim: set fdm=marker :
+# vim: set fdm=marker noexpandtab:
my $path = mockd_start();
END { mockd_stop(); }
-sub filter_time { return map { delete $_->{time}; $_ } @_ }
-
sub test_query {
my ($s, $attr, $results) = @_;
my ($nresults, $resultdata) = @$results;
my @values = $s->listval;
is(scalar @values, 4984, "Correct number of results from LISTVAL");
-delete $values[1234]{time}; # won't be constant
is_deeply($values[1234], {
type_instance => 'nice',
plugin_instance => 21,
plugin => 'cpu',
type => 'cpu',
- host => 'h2gdf6120'
+ host => 'h2gdf6120',
+ time => 1479835354.434,
}, "Correct data returned for select element");
@values = ();
-is_deeply([ filter_time $s->listval_filter() ] , [ filter_time $s->listval ], "listval_filter() w/o filter equivalent to listval()");
+is_deeply([ $s->listval_filter() ] , [ $s->listval ], "listval_filter() w/o filter equivalent to listval()");
is_deeply(
- [ filter_time $s->listval_filter(host => 'a1d8f6310', plugin => 'disk', plugin_instance => 'vda6') ],
+ [ $s->listval_filter(host => 'a1d8f6310', plugin => 'disk', plugin_instance => 'vda6') ],
[
- { 'plugin_instance' => 'vda6', 'type' => 'disk_merged', 'plugin' => 'disk', 'host' => 'a1d8f6310' },
- { 'host' => 'a1d8f6310', 'plugin' => 'disk', 'plugin_instance' => 'vda6', 'type' => 'disk_octets' },
- { 'type' => 'disk_ops', 'plugin_instance' => 'vda6', 'plugin' => 'disk', 'host' => 'a1d8f6310' },
- { 'plugin' => 'disk', 'host' => 'a1d8f6310', 'type' => 'disk_time', 'plugin_instance' => 'vda6' }
+ { 'plugin_instance' => 'vda6', 'type' => 'disk_merged', 'plugin' => 'disk', 'host' => 'a1d8f6310', time => 1479835354.434 },
+ { 'host' => 'a1d8f6310', 'plugin' => 'disk', 'plugin_instance' => 'vda6', 'type' => 'disk_octets', time => 1479835356 },
+ { 'type' => 'disk_ops', 'plugin_instance' => 'vda6', 'plugin' => 'disk', 'host' => 'a1d8f6310', time => 1479835354 },
+ { 'plugin' => 'disk', 'host' => 'a1d8f6310', 'type' => 'disk_time', 'plugin_instance' => 'vda6', time => 1479835354 }
],
"Correct result from listval_filter on <host>, <plugin> and <plugin_instance>"
);
AC_HEADER_DIRENT
AC_HEADER_STDBOOL
-AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h fnmatch.h libgen.h)
+AC_CHECK_HEADERS([ \
+ arpa/inet.h \
+ assert.h \
+ ctype.h \
+ endian.h \
+ errno.h \
+ fcntl.h \
+ fnmatch.h \
+ fs_info.h \
+ fshelp.h \
+ grp.h \
+ kstat.h \
+ kvm.h \
+ libgen.h \
+ limits.h \
+ locale.h \
+ math.h \
+ mntent.h \
+ mnttab.h \
+ netdb.h \
+ paths.h \
+ poll.h \
+ pthread_np.h \
+ pwd.h \
+ regex.h \
+ signal.h \
+ stdarg.h \
+ stdio.h \
+ sys/fs_types.h \
+ sys/fstyp.h \
+ sys/ioctl.h \
+ sys/isa_defs.h \
+ sys/mntent.h \
+ sys/mnttab.h \
+ sys/param.h \
+ sys/resource.h \
+ sys/select.h \
+ sys/socket.h \
+ sys/statfs.h \
+ sys/statvfs.h \
+ sys/types.h \
+ sys/un.h \
+ sys/vfs.h \
+ sys/vfstab.h \
+ sys/vmmeter.h \
+ syslog.h \
+ wordexp.h \
+])
# For entropy plugin on newer NetBSD
AC_CHECK_HEADERS(sys/rndio.h, [], [],
#endif
])
-AC_CHECK_HEADERS([ \
- ctype.h \
- fs_info.h \
- fshelp.h \
- grp.h \
- kvm.h \
- limits.h \
- locale.h \
- mntent.h \
- mnttab.h \
- paths.h \
- pwd.h \
- sys/fs_types.h \
- sys/fstyp.h \
- sys/mntent.h \
- sys/mnttab.h \
- sys/statfs.h \
- sys/statvfs.h \
- sys/un.h \
- sys/vfs.h \
- sys/vfstab.h \
- sys/vmmeter.h \
- wordexp.h \
-])
-
# --enable-xfs {{{
AC_ARG_ENABLE([xfs],
[AS_HELP_STRING([--enable-xfs], [xfs support in df plugin @<:@default=yes@:>@])],
#endif
])
+# check for pthread_setname_np
+SAVE_LDFLAGS="$LDFLAGS"
+LDFLAGS="$LDFLAGS -lpthread"
+
+AC_MSG_CHECKING([for pthread_setname_np])
+ have_pthread_setname_np="no"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#define _GNU_SOURCE
+#include <pthread.h>
+]],
+[[
+ pthread_setname_np((pthread_t) {0}, "conftest");
+]]
+ )], [
+ have_pthread_setname_np="yes"
+ AC_DEFINE(HAVE_PTHREAD_SETNAME_NP, 1, [pthread_setname_np() is available.])
+ ])
+
+AC_MSG_RESULT([$have_pthread_setname_np])
+
+# check for pthread_set_name_np(3) (FreeBSD)
+AC_MSG_CHECKING([for pthread_set_name_np])
+ have_pthread_set_name_np="no"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <pthread_np.h>
+]],
+[[
+ pthread_set_name_np((pthread_t) {0}, "conftest");
+]]
+ )], [
+ have_pthread_set_name_np="yes"
+ AC_DEFINE(HAVE_PTHREAD_SET_NAME_NP, 1, [pthread_set_name_np() is available.])
+ ])
+AC_MSG_RESULT([$have_pthread_set_name_np])
+
+LDFLAGS="$SAVE_LDFLAGS"
+
#
# Checks for libraries begin here
#
fi
# }}}
+# --with-libdpdk {{{
+AC_ARG_VAR([LIBDPDK_CPPFLAGS], [Preprocessor flags for libdpdk])
+AC_ARG_VAR([LIBDPDK_LDFLAGS], [Linker flags for libdpdk])
+
+AC_ARG_WITH([libdpdk], [AS_HELP_STRING([--without-libdpdk], [Disable libdpdk.])])
+
+if test "x$with_libdpdk" != "xno"
+then
+ if test "x$LIBDPDK_CPPFLAGS" = "x"
+ then
+ LIBDPDK_CPPFLAGS="-I/usr/include/dpdk"
+ fi
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$LIBDPDK_CPPFLAGS $CPPFLAGS"
+ AC_CHECK_HEADERS([rte_config.h],
+ [with_libdpdk="yes"],
+ [with_libdpdk="no (rte_config.h not found)"]
+ )
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_libdpdk" = "xyes"
+then
+ SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LIBDPDK_LDFLAGS $LDFLAGS"
+ AC_CHECK_LIB([dpdk], [rte_eal_init],
+ [with_libdpdk="yes"],
+ [with_libdpdk="no (symbol 'rte_eal_init' not found)"]
+ )
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+# }}}
+
# --with-java {{{
with_java_home="$JAVA_HOME"
if test "x$with_java_home" = "x"
AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes")
# }}}
+# --with-libmicrohttpd {{{
+with_libmicrohttpd_cppflags=""
+with_libmicrohttpd_ldflags=""
+AC_ARG_WITH([libmicrohttpd], [AS_HELP_STRING([--with-libmicrohttpd@<:@=PREFIX@:>@], [Path to libmicrohttpd.])],
+ [
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ then
+ with_libmicrohttpd_cppflags="-I$withval/include"
+ with_libmicrohttpd_ldflags="-L$withval/lib"
+ with_libmicrohttpd="yes"
+ fi
+ if test "x$withval" = "xno"
+ then
+ with_libmicrohttpd="no (disabled on command line)"
+ fi
+ ],
+ [withval="yes"]
+)
+if test "x$withval" = "xyes"
+then
+PKG_CHECK_MODULES([MICROHTTPD], [libmicrohttpd],
+ [with_libmicrohttpd="yes"],
+ [with_libmicrohttpd="no (pkg-config could not find libmicrohttpd)"]
+)
+fi
+
+if test "x$MICROHTTPD_LIBS" = "x"
+then
+ MICROHTTPD_LIBS="-lmicrohttpd"
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+SAVE_LIBS="$LIBS"
+CPPFLAGS="$with_libmicrohttpd_cppflags $MICROHTTPD_CFLAGS"
+LDFLAGS="$with_libmicrohttpd_ldflags $LDFLAGS"
+LIBS="$LIBS $MICROHTTPD_LIBS"
+
+if test "x$with_libmicrohttpd" = "xyes"
+then
+ AC_CHECK_HEADERS([microhttpd.h],
+ [with_libmicrohttpd="yes"],
+ [with_libmicrohttpd="no (<microhttpd.h> not found)"])
+fi
+
+if test "x$with_libmicrohttpd" = "xyes"
+then
+ AC_CHECK_LIB([microhttpd], [MHD_start_daemon],
+ [with_libmicrohttpd="yes"],
+ [with_libmicrohttpd="no (libmicrohttpd not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+LIBS="$SAVE_LIBS"
+
+BUILD_WITH_LIBMICROHTTPD_CPPFLAGS="$with_libmicrohttpd_cppflags $MICROHTTPD_CFLAGS"
+BUILD_WITH_LIBMICROHTTPD_LDFLAGS="$with_libmicrohttpd_ldflags"
+BUILD_WITH_LIBMICROHTTPD_LIBS="$MICROHTTPD_LIBS"
+AC_SUBST([BUILD_WITH_LIBMICROHTTPD_CPPFLAGS])
+AC_SUBST([BUILD_WITH_LIBMICROHTTPD_LDFLAGS])
+AC_SUBST([BUILD_WITH_LIBMICROHTTPD_LIBS])
+# }}}
+
# --with-libmodbus {{{
with_libmodbus_config=""
with_libmodbus_cflags=""
AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes")
# }}}
+# --with-libpqos {{{
+with_libpqos_cppflags=""
+with_libpqos_ldflags=""
+AC_ARG_WITH(libpqos, [AS_HELP_STRING([--with-libpqos@<:@=PREFIX@:>@], [Path to libpqos.])],
+[
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ then
+ with_libpqos_cppflags="-I$withval/include"
+ with_libpqos_ldflags="-L$withval/lib"
+ with_libpqos="yes"
+ else
+ with_libpqos="$withval"
+ fi
+],
+[
+ with_libpqos="yes"
+])
+if test "x$with_libpqos" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libpqos_cppflags"
+
+ AC_CHECK_HEADERS(pqos.h, [with_libpqos="yes"], [with_libpqos="no (pqos.h not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libpqos" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libpqos_cppflags"
+ LDFLAGS="$LDFLAGS $with_libpqos_ldflags"
+
+ AC_CHECK_LIB(pqos, pqos_init, [with_libpqos="yes"], [with_libpqos="no (Can't find libpqos)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libpqos" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libpqos_cppflags"
+ AC_RUN_IFELSE([AC_LANG_PROGRAM(
+ [[#include <pqos.h>]],
+ [[return !(PQOS_VERSION >= 106)]])],
+ [with_libpqos="yes"], [with_libpqos="no (pqos library version 1.06 or higher is required)"])
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libpqos" = "xyes"
+then
+ BUILD_WITH_LIBPQOS_CPPFLAGS="$with_libpqos_cppflags"
+ BUILD_WITH_LIBPQOS_LDFLAGS="$with_libpqos_ldflags"
+ BUILD_WITH_LIBPQOS_LIBS="-lpqos"
+ AC_SUBST(BUILD_WITH_LIBPQOS_CPPFLAGS)
+ AC_SUBST(BUILD_WITH_LIBPQOS_LDFLAGS)
+ AC_SUBST(BUILD_WITH_LIBPQOS_LIBS)
+fi
+# }}}
+
# --with-libprotobuf {{{
with_libprotobuf_cppflags=""
with_libprotobuf_ldflags=""
plugin_df="no"
plugin_disk="no"
plugin_drbd="no"
+plugin_dpdkstat="no"
plugin_entropy="no"
plugin_ethstat="no"
plugin_fhcount="no"
plugin_fscache="no"
plugin_gps="no"
plugin_grpc="no"
+plugin_hugepages="no"
+plugin_intel_rdt="no"
plugin_interface="no"
plugin_ipmi="no"
plugin_ipvs="no"
plugin_vmem="no"
plugin_vserver="no"
plugin_wireless="no"
+plugin_write_prometheus="no"
plugin_xencpu="no"
plugin_zfs_arc="no"
plugin_zone="no"
plugin_entropy="yes"
plugin_fhcount="yes"
plugin_fscache="yes"
+ plugin_hugepages="yes"
plugin_interface="yes"
plugin_ipc="yes"
plugin_irq="yes"
if test "x$have_protoc_c" = "xyes" && test "x$with_libprotobuf_c" = "xyes"
then
plugin_pinba="yes"
+ if test "x$with_libmicrohttpd" = "xyes"
+ then
+ plugin_write_prometheus="yes"
+ fi
fi
# Mac OS X memory interface
plugin_xencpu="yes"
fi
+if test "x$with_libdpdk" = "xyes"
+then
+ plugin_dpdkstat="yes"
+fi
+
m4_divert_once([HELP_ENABLE], [
collectd plugins:])
AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics])
AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics])
AC_PLUGIN([dns], [$with_libpcap], [DNS traffic analysis])
+AC_PLUGIN([dpdkstat], [$plugin_dpdkstat], [Stats & Status from DPDK])
AC_PLUGIN([drbd], [$plugin_drbd], [DRBD statistics])
AC_PLUGIN([email], [yes], [EMail statistics])
AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics])
AC_PLUGIN([gps], [$plugin_gps], [GPS plugin])
AC_PLUGIN([grpc], [$plugin_grpc], [gRPC plugin])
AC_PLUGIN([hddtemp], [yes], [Query hddtempd])
+AC_PLUGIN([hugepages], [$plugin_hugepages], [Hugepages statistics])
+AC_PLUGIN([intel_rdt], [$with_libpqos], [Intel RDT monitor plugin])
AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics])
AC_PLUGIN([ipc], [$plugin_ipc], [IPC statistics])
AC_PLUGIN([ipmi], [$plugin_ipmi], [IPMI sensor statistics])
AC_PLUGIN([write_kafka], [$with_librdkafka], [Kafka output plugin])
AC_PLUGIN([write_log], [yes], [Log output plugin])
AC_PLUGIN([write_mongodb], [$with_libmongoc], [MongoDB output plugin])
+AC_PLUGIN([write_prometheus], [$plugin_write_prometheus], [Prometheus write plugin])
AC_PLUGIN([write_redis], [$with_libhiredis], [Redis output plugin])
AC_PLUGIN([write_riemann], [$with_libriemann_client], [Riemann output plugin])
AC_PLUGIN([write_sensu], [yes], [Sensu output plugin])
AC_MSG_RESULT([ libatasmart . . . . . $with_libatasmart])
AC_MSG_RESULT([ libcurl . . . . . . . $with_libcurl])
AC_MSG_RESULT([ libdbi . . . . . . . $with_libdbi])
+AC_MSG_RESULT([ libdpdk . . . . . . . $with_libdpdk])
AC_MSG_RESULT([ libesmtp . . . . . . $with_libesmtp])
AC_MSG_RESULT([ libganglia . . . . . $with_libganglia])
AC_MSG_RESULT([ libgcrypt . . . . . . $with_libgcrypt])
AC_MSG_RESULT([ liblua . . . . . . . $with_liblua])
AC_MSG_RESULT([ liblvm2app . . . . . $with_liblvm2app])
AC_MSG_RESULT([ libmemcached . . . . $with_libmemcached])
+AC_MSG_RESULT([ libmicrohttpd . . . . $with_libmicrohttpd])
AC_MSG_RESULT([ libmnl . . . . . . . $with_libmnl])
AC_MSG_RESULT([ libmodbus . . . . . . $with_libmodbus])
AC_MSG_RESULT([ libmongoc . . . . . . $with_libmongoc])
AC_MSG_RESULT([ libperfstat . . . . . $with_perfstat])
AC_MSG_RESULT([ libperl . . . . . . . $with_libperl])
AC_MSG_RESULT([ libpq . . . . . . . . $with_libpq])
+AC_MSG_RESULT([ libpqos . . . . . . . $with_libpqos])
AC_MSG_RESULT([ libprotobuf . . . . . $with_libprotobuf])
AC_MSG_RESULT([ libprotobuf-c . . . . $with_libprotobuf_c])
AC_MSG_RESULT([ libpython . . . . . . $with_libpython])
AC_MSG_RESULT([ df . . . . . . . . . $enable_df])
AC_MSG_RESULT([ disk . . . . . . . . $enable_disk])
AC_MSG_RESULT([ dns . . . . . . . . . $enable_dns])
+AC_MSG_RESULT([ dpdkstat . . . . . . $enable_dpdkstat])
AC_MSG_RESULT([ drbd . . . . . . . . $enable_drbd])
AC_MSG_RESULT([ email . . . . . . . . $enable_email])
AC_MSG_RESULT([ entropy . . . . . . . $enable_entropy])
AC_MSG_RESULT([ gps . . . . . . . . . $enable_gps])
AC_MSG_RESULT([ grpc . . . . . . . . $enable_grpc])
AC_MSG_RESULT([ hddtemp . . . . . . . $enable_hddtemp])
+AC_MSG_RESULT([ hugepages . . . . . . $enable_hugepages])
+AC_MSG_RESULT([ intel_rdt. . . . . . $enable_intel_rdt])
AC_MSG_RESULT([ interface . . . . . . $enable_interface])
AC_MSG_RESULT([ ipc . . . . . . . . . $enable_ipc])
AC_MSG_RESULT([ ipmi . . . . . . . . $enable_ipmi])
AC_MSG_RESULT([ write_kafka . . . . . $enable_write_kafka])
AC_MSG_RESULT([ write_log . . . . . . $enable_write_log])
AC_MSG_RESULT([ write_mongodb . . . . $enable_write_mongodb])
+AC_MSG_RESULT([ write_prometheus. . . $enable_write_prometheus])
AC_MSG_RESULT([ write_redis . . . . . $enable_write_redis])
AC_MSG_RESULT([ write_riemann . . . . $enable_write_riemann])
AC_MSG_RESULT([ write_sensu . . . . . $enable_write_sensu])
#try at least $self->{main}->{conf}->{collectd_retries} to get a
#connection
for (my $i = 0; $i < $self->{main}->{conf}->{collectd_retries} ; ++$i) {
- last if $sock = new IO::Socket::UNIX
- ($self->{main}->{conf}->{collectd_socket});
+ my ($socket_path) = $self->{main}->{conf}->{collectd_socket} =~ /(.*)/; # Untaint path, which can contain any characters.
+ last if $sock = new IO::Socket::UNIX $socket_path;
#sleep a random value between 0 and 50 microsecs to try for a new
#thread
usleep(int(rand(50)));
} /* static int my_init (void) */
/*
- * This function is called in regular intervalls to collect the data.
+ * This is a utility function used by the read callback to populate a
+ * value_list_t and pass it to plugin_dispatch_values.
*/
-static int my_read(void) {
- value_t values[1]; /* the size of this list should equal the number of
- data sources */
+static int my_submit(gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
- /* do the magic to read the data */
- values[0].gauge = random();
-
- vl.values = values;
+ /* Convert the gauge_t to a value_t and add it to the value_list_t. */
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- vl.time = time(NULL);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+
+ /* Only set vl.time yourself if you update multiple metrics (i.e. you
+ * have multiple calls to plugin_dispatch_values()) and they need to all
+ * have the same timestamp. */
+ /* vl.time = cdtime(); */
+
sstrncpy(vl.plugin, "myplugin", sizeof(vl.plugin));
/* it is strongly recommended to use a type defined in the types.db file
/* dispatch the values to collectd which passes them on to all registered
* write functions */
- plugin_dispatch_values(&vl);
+ return plugin_dispatch_values(&vl);
+}
+
+/*
+ * This function is called in regular intervalls to collect the data.
+ */
+static int my_read(void) {
+ /* do the magic to read the data */
+ gauge_t value = random();
+
+ if (my_submit(value) != 0)
+ WARNING("myplugin plugin: Dispatching a random value failed.");
/* A return value != 0 indicates an error and the plugin will be skipped
* for an increasing amount of time. */
%define with_gmond 0%{!?_without_gmond:1}
%define with_gps 0%{!?_without_gps:1}
%define with_hddtemp 0%{!?_without_hddtemp:1}
+%define with_hugepages 0%{!?_without_hugepages:1}
%define with_interface 0%{!?_without_interface:1}
%define with_ipc 0%{!?_without_ipc:1}
%define with_ipmi 0%{!?_without_ipmi:1}
%define with_write_graphite 0%{!?_without_write_graphite:1}
%define with_write_http 0%{!?_without_write_http:1}
%define with_write_log 0%{!?_without_write_log:1}
+%define with_write_prometheus 0%{!?_without_write_prometheus:1}
%define with_write_redis 0%{!?_without_write_redis:1}
%define with_write_riemann 0%{!?_without_write_riemann:1}
%define with_write_sensu 0%{!?_without_write_sensu:1}
%define with_barometer 0%{!?_without_barometer:0}
# plugin grpc disabled, requires protobuf-compiler >= 3.0
%define with_grpc 0%{!?_without_grpc:0}
+# plugin dpdkstat disabled, requires libdpdk
+%define with_dpdkstat 0%{!?_without_dpdkstat:0}
# plugin lpar disabled, requires AIX
%define with_lpar 0%{!?_without_lpar:0}
+# plugin intel_rdt disabled, requires intel-cmt-cat
+%define with_intel_rdt 0%{!?_without_intel_rdt:0}
# plugin mic disabled, requires Mic
%define with_mic 0%{!?_without_mic:0}
# plugin netapp disabled, requires libnetapp
%define with_redis 0
%define with_smart 0
%define with_turbostat 0
+%define with_write_prometheus 0
%define with_write_redis 0
%define with_write_riemann 0
%endif
Summary: Statistics collection and monitoring daemon
Name: collectd
-Version: 5.6.1
-Release: 4%{?dist}
+Version: 5.7.1
+Release: 3%{?dist}
URL: https://collectd.org
Source: https://collectd.org/files/%{name}-%{version}.tar.bz2
License: GPLv2
provided via SMART and queried by the external hddtemp daemon.
%endif
+%if %{with_intel_rdt}
+%package intel_rdt
+Summary: Intel RDT plugin for collectd
+Group: System Environment/Daemons
+Requires: %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: intel-cmt-cat
+%description intel_rdt
+The intel_rdt plugin collects information provided by monitoring features of
+Intel Resource Director Technology (Intel(R) RDT).
+%endif
+
%if %{with_ipmi}
%package ipmi
Summary: IPMI plugin for collectd
The write_kafka plugin sends values to kafka, a distributed messaging system.
%endif
+%if %{with_write_prometheus}
+%package write_prometheus
+Summary: Write-prometheus plugin for collectd
+Group: System Environment/Daemons
+Requires: %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libmicrohttpd-devel
+%description write_prometheus
+The Write Prometheus plugin exposes collected values using an embedded HTTP
+server, turning the collectd daemon into a Prometheus exporter.
+%endif
+
%if %{with_write_redis}
%package write_redis
Summary: Write-Redis plugin for collectd
%define _with_drbd --disable-drbd
%endif
+%if %{with_dpdkstat}
+%define _with_dpdkstat --enable-dpdkstat
+%else
+%define _with_dpdkstat --disable-dpdkstat
+%endif
+
%if %{with_email}
%define _with_email --enable-email
%else
%define _with_hddtemp --disable-hddtemp
%endif
+%if %{with_hugepages}
+%define _with_hugepages --enable-hugepages
+%else
+%define _with_hugepages --disable-hugepages
+%endif
+
+%if %{with_intel_rdt}
+%define _with_intel_rdt --enable-intel_rdt
+%else
+%define _with_intel_rdt --disable-intel_rdt
+%endif
+
%if %{with_interface}
%define _with_interface --enable-interface
%else
%define _with_write_mongodb --disable-write_mongodb
%endif
+%if %{with_write_prometheus}
+%define _with_write_prometheus --enable-write_prometheus
+%else
+%define _with_write_prometheus --disable-write_prometheus
+%endif
+
%if %{with_write_redis}
%define _with_write_redis --enable-write_redis
%else
%{?_with_disk} \
%{?_with_dns} \
%{?_with_drbd} \
+ %{?_with_dpdkstat} \
%{?_with_email} \
%{?_with_entropy} \
%{?_with_ethstat} \
%{?_with_gps} \
%{?_with_grpc} \
%{?_with_hddtemp} \
+ %{?_with_hugepages} \
+ %{?_with_intel_rdt} \
%{?_with_interface} \
%{?_with_ipc} \
%{?_with_ipmi} \
%{?_with_write_kafka} \
%{?_with_write_log} \
%{?_with_write_mongodb} \
+ %{?_with_write_prometheus} \
%{?_with_write_redis} \
%{?_with_write_riemann} \
%{?_with_write_sensu} \
%if %{with_drbd}
%{_libdir}/%{name}/drbd.so
%endif
+%if %{with_dpdkstat}
+%{_libdir}/%{name}/dpdkstat.so
+%endif
%if %{with_ethstat}
%{_libdir}/%{name}/ethstat.so
%endif
%if %{with_fscache}
%{_libdir}/%{name}/fscache.so
%endif
+%if %{with_hugepages}
+%{_libdir}/%{name}/hugepages.so
+%endif
%if %{with_interface}
%{_libdir}/%{name}/interface.so
%endif
%{_libdir}/%{name}/hddtemp.so
%endif
+%if %{with_intel_rdt}
+%files intel_rdt
+%{_libdir}/%{name}/intel_rdt.so
+%endif
+
%if %{with_ipmi}
%files ipmi
%{_libdir}/%{name}/ipmi.so
%{_libdir}/%{name}/write_kafka.so
%endif
+%if %{with_write_prometheus}
+%files write_prometheus
+%{_libdir}/%{name}/write_prometheus.so
+%endif
+
%if %{with_write_redis}
%files write_redis
%{_libdir}/%{name}/write_redis.so
%doc contrib/
%changelog
-* Sun Mar 05 2017 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.6.1-4
+* Sun Mar 05 2017 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.7.1-2
- Don't enable XFS support on RHEL6, it is missing for i386
-* Wed Feb 22 2017 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.6.1-3
+* Wed Feb 22 2017 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.7.1-2
- Enable XFS support in df plugin
+- Fix bogus date in changelog
+
+* Sun Jan 01 2017 Marc Fournier <marc.fournier@camptocamp.com> - 5.7.1-1
+- New upstream version
+
+* Tue Nov 29 2016 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.7.0-2
+- Disable redis plugin on RHEL 6, hiredis has been retired from EPEL6
-* Tue Nov 29 2016 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.6.1-2
-- Disable redis plugin on RHEL < 7, hiredis has been retired from EPEL6
+* Mon Oct 10 2016 Marc Fournier <marc.fournier@camptocamp.com> - 5.7.0-1
+- New PRE-RELEASE version
+- New plugins enabled by default: hugepages, write_prometheus
+- New plugins disabled by default: dpdkstat, intel_rdt
* Mon Oct 10 2016 Victor Demonchy <v.demonchy@criteo.com> - 5.6.1-1
- New upstream version
--- /dev/null
+# The dpdkstat plugin
+
+**Data Plane Development Kit** (DPDK) is a set of drivers and libraries for fast
+packet processing.
+
+## Summary
+
+The *dpdkstat plugin* has the following requirements:
+
+ * DPDK 16.04 or later
+ * GCC 4.9 or later
+
+You can also build with GCC 4.8 (e.g. Ubuntu 14.04) if you specify the SSSE3
+instruction set manually:
+
+ make -j CFLAGS+='-mssse3'
+
+## Building DPDK
+
+ * Setup the build environment:
+
+ Ensure that you have GCC 4.9 or later. Ubuntu 14.04, for example, has GCC
+ 4.8 by default and requires an upgrade:
+
+ add-apt-repository ppa:ubuntu-toolchain-r/test
+ apt-get update
+ apt-get install gcc-4.9
+
+ If you know that the platform that you wish to run collectd on supports the
+ SSSE3 instruction set, GCC 4.8 also works if you enable SSSE3 manually:
+
+ make -j CFLAGS+='-mssse3'
+
+ * Clone DPDK:
+
+ git clone git://dpdk.org/dpdk
+
+ * Checkout the [DPDK system
+ requirements](http://dpdk.org/doc/guides/linux_gsg/sys_reqs.html) and make
+ sure you have the required tools and hugepage setup as specified there.
+
+ **Note:** It's recommended to use the 1GB hugepage setup for best
+ performance, please follow the instruction for "Reserving Hugepages for DPDK
+ Use" in the link above.
+
+ However if you plan on configuring 2MB hugepages on the fly please ensure to
+ add appropriate commands to reserve hugepages in a system startup script if
+ collectd is booted at system startup time. These commands include:
+
+ mkdir -p /mnt/huge
+ mount -t hugetlbfs nodev /mnt/huge
+ echo 64 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
+
+ * To configure the DPDK build for the combined shared library and enable autoload
+ of pmd drivers modify `config/common_base` in your DPDK as follows
+
+ #
+ # Compile to share library
+ #
+ -CONFIG_RTE_BUILD_SHARED_LIB=n
+ +CONFIG_RTE_BUILD_SHARED_LIB=y
+
+ # Default driver path (or "" to disable)
+ -CONFIG_RTE_EAL_PMD_PATH=""
+ +CONFIG_RTE_EAL_PMD_PATH="/usr/lib/dpdk-pmd/"
+
+ * Prepare the configuration for the appropriate target as specified at:
+ http://dpdk.org/doc/guides/linux_gsg/build_dpdk.html.
+
+ For example:
+
+ make config T=x86_64-native-linuxapp-gcc
+
+ * Build the target:
+
+ make
+
+ * Install DPDK to `/usr`
+
+ sudo make install prefix=/usr
+
+ * Create dpdk-pmd folder
+
+ mkdir -p /usr/lib/dpdk-pmd
+
+ * Create symlinks to pmd drivers
+
+ find /usr/lib -type f -name 'librte_pmd*' | while read path ; do ln -s $path /usr/lib/dpdk-pmd/`echo $path | grep -o 'librte_.*so'` ; done
+
+ **Note 1:** You must run make install as the configuration of collectd with
+ DPDK expects DPDK to be installed somewhere.
+
+ **Note 2:** If you don't specify a prefix then DPDK will be installed in
+ `/usr/local/`.
+
+ **Note 3:** If you are not root then use sudo to make install DPDK to the
+ appropriate location.
+
+ **Note 4:** You **MUST** create symlink to a NIC driver lib. This way collectd
+ will be able to work with device bound to dpdk.
+
+ * Check that the DPDK library has been installed in `/usr/lib` or `/lib`:
+
+ ls /usr/lib | grep dpdk
+
+ * Bind the interfaces to use with dpdkstat to DPDK:
+
+ DPDK devices can be setup with either the VFIO (for DPDK 1.7+) or UIO
+ modules.
+
+ **Note:** UIO requires inserting an out of tree driver `igb_uio.ko` that is
+ available in DPDK.
+
+ **UIO Setup:**
+
+ * Insert `uio.ko`:
+
+ sudo modprobe uio
+
+ * Insert `igb_uio.ko`:
+
+ sudo insmod $DPDK_BUILD/kmod/igb_uio.ko
+
+ * Bind network device to `igb_uio`:
+
+ sudo $DPDK_DIR/tools/dpdk_nic_bind.py --bind=igb_uio eth1
+
+ **VFIO Setup:**
+
+ * VFIO needs to be supported in the kernel and the BIOS. More information
+ can be found at: http://dpdk.org/doc/guides/linux_gsg/build_dpdk.html.
+ * Insert the `vfio-pci.ko` module:
+
+ modprobe vfio-pci
+
+ * Set the correct permissions for the VFIO device:
+
+ sudo /usr/bin/chmod a+x /dev/vfio
+ sudo /usr/bin/chmod 0666 /dev/vfio/*
+
+ * Bind the network device to `vfio-pci`:
+
+ sudo $DPDK_DIR/tools/dpdk_nic_bind.py --bind=vfio-pci eth1
+
+ **Note:** Please ensure to add appropriate commands to bind the network
+ interfaces to DPDK in a system startup script if collectd is booted at
+ system startup time.
+
+ * Run `ldconfig` to update the shared library cache.
+
+### Static library
+
+To build static DPDK library for use with collectd:
+
+ * To configure DPDK to build the combined static library `libdpdk.a` ensure
+ that `CONFIG_RTE_BUILD_SHARED_LIB` is set to “n” in `config/common_base` in
+ your DPDK as follows:
+
+ #
+ # Compile to share library
+ #
+ CONFIG_RTE_BUILD_SHARED_LIB=n
+
+ * Prepare the configuration for the appropriate target as specified at:
+ http://dpdk.org/doc/guides/linux_gsg/build_dpdk.html.
+
+ For example:
+
+ make config T=x86_64-native-linuxapp-gcc
+
+ * Build the target using `-fPIC`:
+
+ make EXTRA_CFLAGS=-fPIC -j
+
+ * Install DPDK to `/usr`:
+
+ sudo make install prefix=/usr
+
+## Build collectd with DPDK
+
+**Note:** DPDK 16.04 is the minimum version and currently supported version of
+DPDK required for the dpdkstat plugin. This is to allow the plugin to take
+advantage of functions added to detect if the DPDK primary process is alive.
+
+
+**Note:** The *Address-Space Layout Randomization* (ASLR) security feature in
+Linux should be disabled, in order for the same hugepage memory mappings to be
+present in all DPDK multi-process applications. Note that this has security
+implications.
+
+ * To disable ASLR:
+
+ echo 0 > /proc/sys/kernel/randomize_va_space
+
+ * To fully enable ASLR:
+
+ echo 2 > /proc/sys/kernel/randomize_va_space
+
+See also: http://dpdk.org/doc/guides/prog_guide/multi_proc_support.html
+
+ * Generate the build script as specified below. (i.e. run `build.sh`).
+ * Configure collectd with the DPDK shared library:
+
+ ./configure --with-libdpdk=/usr
+
+### Build with the static DPDK library
+
+To configure collectd with the DPDK static library:
+
+ * Run *configure* with the following CFLAGS:
+
+ ./configure --with-libdpdk=/usr CFLAGS=" -lpthread -Wl,--whole-archive -Wl,-ldpdk -Wl,-lm -Wl,-lrt -Wl,-lpcap -Wl,-ldl -Wl,--no-whole-archive"
+
+ * Make sure that dpdk and dpdkstat are enabled in the *configure* output.
+
+ Expected output:
+
+ Libraries:
+ ...
+ libdpdk . . . . . . . . yes
+
+ Modules:
+ ...
+ dpdkstat . . . . . . .yes
+
+ * Build collectd:
+
+ make -j && make -j install.
+
+ **Note:** As mentioned above, if you are building on Ubuntu 14.04 with
+ GCC <= 4.8.X, you need to use:
+
+ make -j CFLAGS+='-mssse3' && make -j install
+
+## Caveats
+
+ * The same PCI device configuration should be passed to the primary process as
+ the secondary process uses the same port indexes as the primary.
+ * A blacklist / whitelist of NICs isn't supported yet.
+
+## License
+
+The *dpdkstat plugin* is copyright (c) 2016 *Intel Corporation* and licensed
+under the *MIT license*. Full licensing terms can be found in the file
+`COPYING`.
--- /dev/null
+# Building with Java
+
+This file gives some background and hints how the *java plugin* needs to be
+configured.
+
+## Dependencies
+
+The *java plugin* requires a version of Java with *Java Native Interface* (JNI)
+**1.2** or later.
+
+## Configure and flags
+
+To determine the location of the required files of a Java installation is not an
+easy task, because the locations vary with your kernel (Linux, SunOS, …) and
+with your architecture (x86, SPARC, …) and there is no `java-config` script we
+could use. Configuration of the JVM library is therefore a bit tricky.
+
+The easiest way to use the `--with-java="${JAVA_HOME}"` option, where
+`JAVA_HOME` is usually something like:
+
+ /usr/lib/jvm/java-1.5.0-sun-1.5.0.14
+
+The configure script will then use *find(1)* to look for the following files:
+
+ * `jni.h`
+ * `jni_md.h`
+ * `libjvm.so`
+
+If found, appropriate CPP-flags and LD-flags are set and the following library
+checks succeed.
+
+If this doesn't work for you, you have the possibility to specify CPP-flags,
+C-flags, LD-flags and LIBS for the *java plugin* by hand, using the following
+environment variables:
+
+ * `JAVA_CPPFLAGS`
+ * `JAVA_CFLAGS`
+ * `JAVA_LDFLAGS`
+ * `JAVA_LIBS`
+
+For example (shortened for demonstration purposes):
+
+ ./configure JAVA_CPPFLAGS="-I$JAVA_HOME/include -I$JAVA_HOME/include/linux"
+
+Adding `-ljvm` to JAVA_LIBS is done automatically, you don't have to do that.
+
+## License
+
+The *java plugin* is licensed under the *GNU General Public License, version 2*.
+Full licensing terms can be found in the file `COPYING`.
-EXTRA_DIST = collectd.proto types.proto
+EXTRA_DIST = collectd.proto types.proto prometheus.proto
import "types.proto";
service Collectd {
- // DispatchValues reads the value lists from the DispatchValuesRequest stream.
+ // PutValues reads the value lists from the PutValuesRequest stream.
// The gRPC server embedded into collectd will inject them into the system
// just like the network plugin.
- rpc DispatchValues(stream DispatchValuesRequest)
- returns(DispatchValuesResponse);
+ rpc PutValues(stream PutValuesRequest) returns(PutValuesResponse);
// QueryValues returns a stream of matching value lists from collectd's
// internal cache.
rpc QueryValues(QueryValuesRequest) returns(stream QueryValuesResponse);
}
-// The arguments to DispatchValues.
-message DispatchValuesRequest {
+// The arguments to PutValues.
+message PutValuesRequest {
// value_list is the metric to be sent to the server.
collectd.types.ValueList value_list = 1;
}
-// The response from DispatchValues.
-message DispatchValuesResponse {}
+// The response from PutValues.
+message PutValuesResponse {}
// The arguments to QueryValues.
message QueryValuesRequest {
--- /dev/null
+// Copyright 2013 Prometheus Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+
+package io.prometheus.client;
+option java_package = "io.prometheus.client";
+
+message LabelPair {
+ optional string name = 1;
+ optional string value = 2;
+}
+
+enum MetricType {
+ COUNTER = 0; GAUGE = 1; SUMMARY = 2; UNTYPED = 3; HISTOGRAM = 4;
+}
+
+message Gauge { optional double value = 1; }
+
+message Counter { optional double value = 1; }
+
+message Quantile {
+ optional double quantile = 1;
+ optional double value = 2;
+}
+
+message Summary {
+ optional uint64 sample_count = 1;
+ optional double sample_sum = 2;
+ repeated Quantile quantile = 3;
+}
+
+message Untyped { optional double value = 1; }
+
+message Histogram {
+ optional uint64 sample_count = 1;
+ optional double sample_sum = 2;
+ repeated Bucket bucket =
+ 3; // Ordered in increasing order of upper_bound, +Inf bucket is optional.
+}
+
+message Bucket {
+ optional uint64 cumulative_count = 1; // Cumulative in increasing order.
+ optional double upper_bound = 2; // Inclusive.
+}
+
+message Metric {
+ repeated LabelPair label = 1;
+ optional Gauge gauge = 2;
+ optional Counter counter = 3;
+ optional Summary summary = 4;
+ optional Untyped untyped = 5;
+ optional Histogram histogram = 7;
+ optional int64 timestamp_ms = 6;
+}
+
+message MetricFamily {
+ optional string name = 1;
+ optional string help = 2;
+ optional MetricType type = 3;
+ repeated Metric metric = 4;
+}
check_PROGRAMS =
TESTS =
+noinst_LTLIBRARIES += libformat_graphite.la
+libformat_graphite_la_SOURCES = utils_format_graphite.c utils_format_graphite.h
+libformat_graphite_la_CPPFLAGS = $(AM_CPPFLAGS)
+libformat_graphite_la_LDFLAGS = $(AM_LDFLAGS)
+check_PROGRAMS += test_format_graphite
+TESTS += test_format_graphite
+test_format_graphite_SOURCES = utils_format_graphite_test.c testing.h
+test_format_graphite_LDADD = libformat_graphite.la daemon/libmetadata.la daemon/libplugin_mock.la -lm
+
noinst_LTLIBRARIES += libformat_json.la
libformat_json_la_SOURCES = utils_format_json.c utils_format_json.h
libformat_json_la_CPPFLAGS = $(AM_CPPFLAGS)
endif
noinst_LTLIBRARIES += liblatency.la
-liblatency_la_SOURCES = utils_latency.c utils_latency.h
+liblatency_la_SOURCES = utils_latency.c utils_latency.h utils_latency_config.c utils_latency_config.h
+liblatency_la_LIBADD = daemon/libcommon.la -lm
check_PROGRAMS += test_utils_latency
TESTS += test_utils_latency
test_utils_latency_SOURCES = utils_latency_test.c testing.h
test_utils_latency_LDADD = liblatency.la daemon/libplugin_mock.la -lm
+noinst_LTLIBRARIES += libcmds.la
+libcmds_la_SOURCES = utils_cmds.c utils_cmds.h \
+ utils_cmd_flush.c utils_cmd_flush.h \
+ utils_cmd_getthreshold.c utils_cmd_getthreshold.h \
+ utils_cmd_getval.c utils_cmd_getval.h \
+ utils_cmd_listval.c utils_cmd_listval.h \
+ utils_cmd_putnotif.c utils_cmd_putnotif.h \
+ utils_cmd_putval.c utils_cmd_putval.h \
+ utils_parse_option.c utils_parse_option.h
+libcmds_la_LIBADD = daemon/libcommon.la daemon/libmetadata.la -lm
+check_PROGRAMS += test_utils_cmds
+TESTS += test_utils_cmds
+test_utils_cmds_SOURCES = utils_cmds_test.c testing.h
+test_utils_cmds_LDADD = libcmds.la \
+ daemon/libplugin_mock.la
+
noinst_LTLIBRARIES += liblookup.la
liblookup_la_SOURCES = utils_vl_lookup.c utils_vl_lookup.h
liblookup_la_LIBADD = daemon/libavltree.la
if BUILD_PLUGIN_AMQP
pkglib_LTLIBRARIES += amqp.la
-amqp_la_SOURCES = amqp.c \
- utils_cmd_putval.c utils_cmd_putval.h \
- utils_parse_option.c utils_parse_option.h \
- utils_format_graphite.c utils_format_graphite.h
+amqp_la_SOURCES = amqp.c
amqp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
amqp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
-amqp_la_LIBADD = $(BUILD_WITH_LIBRABBITMQ_LIBS) libformat_json.la
+amqp_la_LIBADD = $(BUILD_WITH_LIBRABBITMQ_LIBS) \
+ libcmds.la libformat_graphite.la libformat_json.la
endif
if BUILD_PLUGIN_APACHE
if BUILD_PLUGIN_BATTERY
pkglib_LTLIBRARIES += battery.la
-battery_la_SOURCES = battery.c
+battery_la_SOURCES = battery.c battery_statefs.c
battery_la_LDFLAGS = $(PLUGIN_LDFLAGS)
if BUILD_WITH_LIBIOKIT
battery_la_LDFLAGS += -framework IOKit
if BUILD_PLUGIN_CURL
pkglib_LTLIBRARIES += curl.la
curl_la_SOURCES = curl.c \
- utils_curl_stats.c utils_curl_stats.h
+ utils_curl_stats.c utils_curl_stats.h \
+ utils_match.c utils_match.h
curl_la_LDFLAGS = $(PLUGIN_LDFLAGS)
curl_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
-curl_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS)
+curl_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) liblatency.la
endif
if BUILD_PLUGIN_CURL_JSON
dns_la_LIBADD = -lpcap
endif
+if BUILD_PLUGIN_DPDKSTAT
+pkglib_LTLIBRARIES += dpdkstat.la
+dpdkstat_la_SOURCES = dpdkstat.c
+dpdkstat_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
+dpdkstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
+dpdkstat_la_LIBADD = -ldpdk
+endif
+
if BUILD_PLUGIN_DRBD
pkglib_LTLIBRARIES += drbd.la
drbd_la_SOURCES = drbd.c
if BUILD_PLUGIN_EXEC
pkglib_LTLIBRARIES += exec.la
-exec_la_SOURCES = exec.c \
- utils_cmd_putnotif.c utils_cmd_putnotif.h \
- utils_cmd_putval.c utils_cmd_putval.h \
- utils_parse_option.h utils_parse_option.c
+exec_la_SOURCES = exec.c
exec_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+exec_la_LIBADD = libcmds.la
endif
if BUILD_PLUGIN_ETHSTAT
endif
endif
+if BUILD_PLUGIN_HUGEPAGES
+pkglib_LTLIBRARIES += hugepages.la
+hugepages_la_SOURCES = hugepages.c
+hugepages_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+endif
+
if BUILD_PLUGIN_INTERFACE
pkglib_LTLIBRARIES += interface.la
interface_la_SOURCES = interface.c
if BUILD_PLUGIN_MEMCACHEC
pkglib_LTLIBRARIES += memcachec.la
-memcachec_la_SOURCES = memcachec.c
+memcachec_la_SOURCES = memcachec.c \
+ utils_match.c utils_match.h
memcachec_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMEMCACHED_LDFLAGS)
memcachec_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS)
-memcachec_la_LIBADD = $(BUILD_WITH_LIBMEMCACHED_LIBS)
+memcachec_la_LIBADD = $(BUILD_WITH_LIBMEMCACHED_LIBS) liblatency.la
endif
if BUILD_PLUGIN_MEMCACHED
protocols_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
+if BUILD_PLUGIN_INTEL_RDT
+pkglib_LTLIBRARIES += intel_rdt.la
+intel_rdt_la_SOURCES = intel_rdt.c
+intel_rdt_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPQOS_LDFLAGS)
+intel_rdt_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBPQOS_CPPFLAGS)
+intel_rdt_la_LIBADD = $(BUILD_WITH_LIBPQOS_LIBS)
+endif
+
if BUILD_PLUGIN_REDIS
pkglib_LTLIBRARIES += redis.la
redis_la_SOURCES = redis.c
pkglib_LTLIBRARIES += statsd.la
statsd_la_SOURCES = statsd.c
statsd_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-statsd_la_LIBADD = liblatency.la -lm
+statsd_la_LIBADD = liblatency.la
endif
if BUILD_PLUGIN_SWAP
if BUILD_PLUGIN_TAIL
pkglib_LTLIBRARIES += tail.la
-tail_la_SOURCES = tail.c
+tail_la_SOURCES = tail.c \
+ utils_match.c utils_match.h \
+ utils_tail.c utils_tail.h \
+ utils_tail_match.c utils_tail_match.h
tail_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+tail_la_LIBADD = liblatency.la
endif
if BUILD_PLUGIN_TAIL_CSV
pkglib_LTLIBRARIES += tail_csv.la
-tail_csv_la_SOURCES = tail_csv.c
+tail_csv_la_SOURCES = tail_csv.c \
+ utils_tail.c utils_tail.h
tail_csv_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
if BUILD_PLUGIN_UNIXSOCK
pkglib_LTLIBRARIES += unixsock.la
-unixsock_la_SOURCES = unixsock.c \
- utils_cmd_flush.h utils_cmd_flush.c \
- utils_cmd_getval.h utils_cmd_getval.c \
- utils_cmd_getthreshold.h utils_cmd_getthreshold.c \
- utils_cmd_listval.h utils_cmd_listval.c \
- utils_cmd_putval.h utils_cmd_putval.c \
- utils_cmd_putnotif.h utils_cmd_putnotif.c \
- utils_parse_option.h utils_parse_option.c
+unixsock_la_SOURCES = unixsock.c
unixsock_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+unixsock_la_LIBADD = libcmds.la
endif
if BUILD_PLUGIN_UPTIME
if BUILD_PLUGIN_WRITE_GRAPHITE
pkglib_LTLIBRARIES += write_graphite.la
-write_graphite_la_SOURCES = write_graphite.c \
- utils_format_graphite.c utils_format_graphite.h
+write_graphite_la_SOURCES = write_graphite.c
write_graphite_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+write_graphite_la_LIBADD = libformat_graphite.la
endif
if BUILD_PLUGIN_WRITE_HTTP
if BUILD_PLUGIN_WRITE_KAFKA
pkglib_LTLIBRARIES += write_kafka.la
write_kafka_la_SOURCES = write_kafka.c \
- utils_format_graphite.c utils_format_graphite.h \
- utils_cmd_putval.c utils_cmd_putval.h \
- utils_crc32.c utils_crc32.h
+ utils_crc32.c utils_crc32.h
write_kafka_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRDKAFKA_CPPFLAGS)
write_kafka_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRDKAFKA_LDFLAGS)
-write_kafka_la_LIBADD = $(BUILD_WITH_LIBRDKAFKA_LIBS) libformat_json.la
+write_kafka_la_LIBADD = $(BUILD_WITH_LIBRDKAFKA_LIBS) \
+ libcmds.la libformat_graphite.la libformat_json.la
endif
if BUILD_PLUGIN_WRITE_LOG
pkglib_LTLIBRARIES += write_log.la
-write_log_la_SOURCES = write_log.c \
- utils_format_graphite.c utils_format_graphite.h
+write_log_la_SOURCES = write_log.c
write_log_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+write_log_la_LIBADD = libformat_graphite.la libformat_json.la
endif
if BUILD_PLUGIN_WRITE_MONGODB
write_mongodb_la_LIBADD = -lmongoc
endif
+if BUILD_PLUGIN_WRITE_PROMETHEUS
+pkglib_LTLIBRARIES += write_prometheus.la
+write_prometheus_la_SOURCES = write_prometheus.c
+nodist_write_prometheus_la_SOURCES = prometheus.pb-c.c prometheus.pb-c.h
+write_prometheus_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPROTOBUF_C_CPPFLAGS) $(BUILD_WITH_LIBMICROHTTPD_CPPFLAGS)
+write_prometheus_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPROTOBUF_C_LDFLAGS) $(BUILD_WITH_LIBMICROHTTPD_LDFLAGS)
+write_prometheus_la_LIBADD = $(BUILD_WITH_LIBPROTOBUF_C_LIBS) $(BUILD_WITH_LIBMICROHTTPD_LIBS)
+endif
+
if BUILD_PLUGIN_WRITE_REDIS
pkglib_LTLIBRARIES += write_redis.la
write_redis_la_SOURCES = write_redis.c
$(AM_V_PROTOC_C)$(PROTOC_C) -I$(srcdir) --c_out . $(srcdir)/pinba.proto
endif
+# Protocol buffer for the "write_prometheus" plugin.
+if BUILD_PLUGIN_WRITE_PROMETHEUS
+CLEANFILES += prometheus.pb-c.c prometheus.pb-c.h
+BUILT_SOURCES += prometheus.pb-c.c prometheus.pb-c.h
+
+prometheus.pb-c.c prometheus.pb-c.h: $(top_srcdir)/proto/prometheus.proto
+ $(AM_V_PROTOC_C)$(PROTOC_C) -I$(top_srcdir)/proto --c_out=$(builddir) $(top_srcdir)/proto/prometheus.proto
+endif
+
install-exec-hook:
$(mkinstalldirs) $(DESTDIR)$(sysconfdir)
if test -e $(DESTDIR)$(sysconfdir)/collectd.conf; \
struct aggregation_s /* {{{ */
{
- identifier_t ident;
+ lookup_identifier_t ident;
unsigned int group_by;
unsigned int regex_fields;
struct agg_instance_s /* {{{ */
{
pthread_mutex_t lock;
- identifier_t ident;
+ lookup_identifier_t ident;
int ds_type;
} /* while (received < body_size) */
if (strcasecmp("text/collectd", content_type) == 0) {
- status = handle_putval(stderr, body);
+ status = cmd_handle_putval(stderr, body);
if (status != 0)
- ERROR("amqp plugin: handle_putval failed with status %i.", status);
+ ERROR("amqp plugin: cmd_handle_putval failed with status %i.", status);
return (status);
} else if (strcasecmp("application/json", content_type) == 0) {
ERROR("amqp plugin: camqp_read_body: Parsing JSON data has not "
status = camqp_connect(conf);
if (status != 0) {
- struct timespec ts_interval;
ERROR("amqp plugin: camqp_connect failed. "
"Will sleep for %.3f seconds.",
CDTIME_T_TO_DOUBLE(interval));
- CDTIME_T_TO_TIMESPEC(interval, &ts_interval);
- nanosleep(&ts_interval, /* remaining = */ NULL);
+ nanosleep(&CDTIME_T_TO_TIMESPEC(interval), /* remaining = */ NULL);
continue;
}
status = amqp_simple_wait_frame(conf->connection, &frame);
if (status < 0) {
- struct timespec ts_interval;
ERROR("amqp plugin: amqp_simple_wait_frame failed. "
"Will sleep for %.3f seconds.",
CDTIME_T_TO_DOUBLE(interval));
camqp_close_connection(conf);
- CDTIME_T_TO_TIMESPEC(interval, &ts_interval);
- nanosleep(&ts_interval, /* remaining = */ NULL);
+ nanosleep(&CDTIME_T_TO_TIMESPEC(interval), /* remaining = */ NULL);
continue;
}
memset(tmp, 0, sizeof(*tmp));
status = plugin_thread_create(tmp, /* attr = */ NULL, camqp_subscribe_thread,
- conf);
+ conf, "amqp subscribe");
if (status != 0) {
char errbuf[1024];
ERROR("amqp plugin: pthread_create failed: %s",
}
if (conf->format == CAMQP_FORMAT_COMMAND) {
- status = create_putval(buffer, sizeof(buffer), ds, vl);
+ status = cmd_create_putval(buffer, sizeof(buffer), ds, vl);
if (status != 0) {
- ERROR("amqp plugin: create_putval failed with status %i.", status);
+ ERROR("amqp plugin: cmd_create_putval failed with status %i.", status);
return (status);
}
} else if (conf->format == CAMQP_FORMAT_JSON) {
else if ((strcasecmp("GraphiteAlwaysAppendDS", child->key) == 0) && publish)
status = cf_util_get_flag(child, &conf->graphite_flags,
GRAPHITE_ALWAYS_APPEND_DS);
+ else if ((strcasecmp("GraphitePreserveSeparator", child->key) == 0) &&
+ publish)
+ status = cf_util_get_flag(child, &conf->graphite_flags,
+ GRAPHITE_PRESERVE_SEPARATOR);
else if ((strcasecmp("GraphitePrefix", child->key) == 0) && publish)
status = cf_util_get_string(child, &conf->prefix);
else if ((strcasecmp("GraphitePostfix", child->key) == 0) && publish)
if (publish) {
char cbname[128];
- user_data_t ud = {conf, camqp_config_free};
-
ssnprintf(cbname, sizeof(cbname), "amqp/%s", conf->name);
- status = plugin_register_write(cbname, camqp_write, &ud);
+ status = plugin_register_write(
+ cbname, camqp_write, &(user_data_t){
+ .data = conf, .free_func = camqp_config_free,
+ });
if (status != 0) {
camqp_config_free(conf);
return (status);
}
if (status == 0) {
- user_data_t ud = {.data = st, .free_func = apache_free};
-
char callback_name[3 * DATA_MAX_NAME_LEN];
ssnprintf(callback_name, sizeof(callback_name), "apache/%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 = */ apache_read_host,
- /* interval = */ 0,
- /* user_data = */ &ud);
+ status = plugin_register_complex_read(
+ /* group = */ NULL,
+ /* name = */ callback_name,
+ /* callback = */ apache_read_host,
+ /* interval = */ 0, &(user_data_t){
+ .data = st, .free_func = apache_free,
+ });
}
if (status != 0) {
vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, (st->host != NULL) ? st->host : hostname_g,
- sizeof(vl.host));
+ if (st->host != NULL)
+ sstrncpy(vl.host, st->host, sizeof(vl.host));
sstrncpy(vl.plugin, "apache", sizeof(vl.plugin));
if (st->name != NULL)
} /* void submit_value */
static void submit_derive(const char *type, const char *type_instance,
- derive_t c, apache_t *st) {
- value_t v;
- v.derive = c;
- submit_value(type, type_instance, v, st);
+ derive_t d, apache_t *st) {
+ submit_value(type, type_instance, (value_t){.derive = d}, st);
} /* void submit_derive */
static void submit_gauge(const char *type, const char *type_instance, gauge_t g,
apache_t *st) {
- value_t v;
- v.gauge = g;
- submit_value(type, type_instance, v, st);
+ submit_value(type, type_instance, (value_t){.gauge = g}, st);
} /* void submit_gauge */
static void submit_scoreboard(char *buf, apache_t *st) {
static void apc_submit_generic(const char *type, const char *type_inst,
gauge_t value) {
- value_t values[1];
- value_list_t vl = VALUE_LIST_INIT;
-
if (isnan(value))
return;
- values[0].gauge = value;
-
- vl.values = values;
+ value_list_t vl = VALUE_LIST_INIT;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "apcups", sizeof(vl.plugin));
- sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_inst, sizeof(vl.type_instance));
}
static void as_submit(const char *type, const char *type_instance, double val) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- DEBUG("type = %s; type_instance = %s; val = %f;", type, type_instance, val);
-
- values[0].gauge = val;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = val};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "apple_sensors", sizeof(vl.plugin));
- sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
static void aquaero_submit(const char *type, const char *type_instance,
double value) {
const char *instance = conf_device ? conf_device : "default";
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
/* Don't report undefined values. */
if (value == AQ5_FLOAT_UNDEF)
return;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "aquaero", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
static int ascent_submit_gauge(const char *plugin_instance, /* {{{ */
const char *type, const char *type_instance,
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ascent", sizeof(vl.plugin));
if (plugin_instance != NULL)
norm_pressure = abs_to_mean_sea_level_pressure(pressure);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "barometer", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, "mpl115", sizeof(vl.plugin_instance));
norm_pressure = abs_to_mean_sea_level_pressure(pressure);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "barometer", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, "mpl3115", sizeof(vl.plugin_instance));
norm_pressure = abs_to_mean_sea_level_pressure(pressure);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "barometer", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, "bmp085", sizeof(vl.plugin_instance));
#define SYSFS_FACTOR 0.000001
#endif /* KERNEL_LINUX */
+int battery_read_statefs(
+ void); /* defined in battery_statefs; used by StateFS backend */
+
static _Bool report_percent = 0;
static _Bool report_degraded = 0;
+static _Bool query_statefs = 0;
static void battery_submit2(char const *plugin_instance, /* {{{ */
char const *type, char const *type_instance,
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "battery", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
gauge_t capacity_full = NAN; /* Total capacity */
gauge_t capacity_design = NAN; /* Full design capacity */
+ if (query_statefs)
+ return battery_read_statefs();
+
#if HAVE_IOKIT_PS_IOPOWERSOURCES_H
get_via_io_power_sources(&charge_rel, ¤t, &voltage);
#endif
static int sysfs_file_to_buffer(char const *dir, /* {{{ */
char const *power_supply, char const *basename,
char *buffer, size_t buffer_size) {
- int status;
- FILE *fp;
char filename[PATH_MAX];
+ int status;
ssnprintf(filename, sizeof(filename), "%s/%s/%s", dir, power_supply,
basename);
- /* No file isn't the end of the world -- not every system will be
- * reporting the same set of statistics */
- if (access(filename, R_OK) != 0)
- return ENOENT;
-
- fp = fopen(filename, "r");
- if (fp == NULL) {
- status = errno;
- if (status != ENOENT) {
- char errbuf[1024];
- WARNING("battery plugin: fopen (%s) failed: %s", filename,
- sstrerror(status, errbuf, sizeof(errbuf)));
- }
+ status = (int)read_file_contents(filename, buffer, buffer_size - 1);
+ if (status < 0)
return status;
- }
- if (fgets(buffer, buffer_size, fp) == NULL) {
- status = errno;
- if (status != ENODEV) {
- char errbuf[1024];
- WARNING("battery plugin: fgets (%s) failed: %s", filename,
- sstrerror(status, errbuf, sizeof(errbuf)));
- }
- fclose(fp);
- return status;
- }
+ buffer[status] = '\0';
strstripnewline(buffer);
-
- fclose(fp);
return 0;
} /* }}} int sysfs_file_to_buffer */
char const *power_supply, char const *basename,
gauge_t *ret_value) {
int status;
- char buffer[32] = "";
+ char buffer[32];
status =
sysfs_file_to_buffer(dir, power_supply, basename, buffer, sizeof(buffer));
{
int status;
+ if (query_statefs)
+ return battery_read_statefs();
+
DEBUG("battery plugin: Trying sysfs ...");
status = read_sysfs();
if (status == 0)
cf_util_get_boolean(child, &report_percent);
else if (strcasecmp("ReportDegraded", child->key) == 0)
cf_util_get_boolean(child, &report_degraded);
+ else if (strcasecmp("QueryStateFS", child->key) == 0)
+ cf_util_get_boolean(child, &query_statefs);
else
WARNING("battery plugin: Ignoring unknown "
"configuration option \"%s\".",
--- /dev/null
+/**
+ * collectd - src/statefs_battery.c
+ * Copyright (C) 2016 rinigus
+ *
+ *
+The MIT License (MIT)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+ * Authors:
+ * rinigus <http://github.com/rinigus>
+
+ Battery stats are collected from StateFS Battery namespace. Reported
+ units are as follows:
+
+ capacity %
+ charge %
+ current A
+ energy Wh
+ power W
+ temperature C
+ timefull and timelow seconds
+ voltage V
+
+ Provider at
+ https://git.merproject.org/mer-core/statefs-providers/blob/master/src/power_udev/provider_power_udev.cpp
+
+ **/
+
+#include "common.h"
+#include "plugin.h"
+#include "collectd.h"
+
+#include <stdio.h>
+
+#define STATEFS_ROOT "/run/state/namespaces/Battery/"
+
+static void battery_submit(const char *type, gauge_t value,
+ const char *type_instance) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &(value_t){.gauge = value};
+ vl.values_len = 1;
+ sstrncpy(vl.plugin, "battery", sizeof(vl.plugin));
+ /* statefs supports 1 battery at present */
+ sstrncpy(vl.plugin_instance, "0", sizeof(vl.plugin_instance));
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ if (type_instance != NULL)
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+ plugin_dispatch_values(&vl);
+}
+
+/* cannot be static, is referred to from battery.c */
+int battery_read_statefs(void) {
+ value_t v;
+ int success = 0;
+
+ if (parse_value_file(STATEFS_ROOT "ChargePercentage", &v, DS_TYPE_GAUGE) ==
+ 0) {
+ battery_submit("charge", v.gauge, NULL);
+ success++;
+ } else if (parse_value_file(STATEFS_ROOT "Capacity", &v, DS_TYPE_GAUGE) ==
+ 0) {
+ // Use capacity as a charge estimate if ChargePercentage is not available
+ battery_submit("charge", v.gauge, NULL);
+ success++;
+ } else {
+ WARNING("battery plugin: Neither \"" STATEFS_ROOT "ChargePercentage\" "
+ "nor \"" STATEFS_ROOT "Capacity\" could be read.");
+ }
+
+ struct {
+ char *path;
+ char *type;
+ char *type_instance;
+ gauge_t factor;
+ } metrics[] = {
+ {STATEFS_ROOT "Current", "current", NULL, 1e-6}, // from uA to A
+ {STATEFS_ROOT "Energy", "energy_wh", NULL, 1e-6}, // from uWh to Wh
+ {STATEFS_ROOT "Power", "power", NULL, 1e-6}, // from uW to W
+ {STATEFS_ROOT "Temperature", "temperature", NULL, 0.1}, // from 10xC to C
+ {STATEFS_ROOT "TimeUntilFull", "duration", "full", 1.0},
+ {STATEFS_ROOT "TimeUntilLow", "duration", "low", 1.0},
+ {STATEFS_ROOT "Voltage", "voltage", NULL, 1e-6}, // from uV to V
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(metrics); i++) {
+ if (parse_value_file(metrics[i].path, &v, DS_TYPE_GAUGE) != 0) {
+ WARNING("battery plugin: Reading \"%s\" failed.", metrics[i].path);
+ continue;
+ }
+
+ battery_submit(metrics[i].type, v.gauge * metrics[i].factor,
+ metrics[i].type_instance);
+ success++;
+ }
+
+ if (success == 0) {
+ ERROR("battery plugin: statefs backend: none of the statistics are "
+ "available");
+ return (-1);
+ }
+
+ return (0);
+}
{"ReqBadSIG", "dns_request", "BadSIG"},
{"ReqTCP", "dns_request", "TCP"},
/* Rejects */
- {"AuthQryRej", "dns_reject", "authorative"},
+ {"AuthQryRej", "dns_reject", "authoritative"},
{"RecQryRej", "dns_reject", "recursive"},
{"XfrRej", "dns_reject", "transfer"},
{"UpdateRej", "dns_reject", "update"},
{"RespTSIG", "dns_response", "TSIG"},
{"RespSIG0", "dns_response", "SIG0"},
/* Queries */
- {"QryAuthAns", "dns_query", "authorative"},
+ {"QryAuthAns", "dns_query", "authoritative"},
{"QryNoauthAns", "dns_query", "nonauth"},
{"QryReferral", "dns_query", "referral"},
{"QryRecursion", "dns_query", "recursion"},
- {"QryDuplicate", "dns_query", "dupliate"},
+ {"QryDuplicate", "dns_query", "duplicate"},
{"QryDropped", "dns_query", "dropped"},
{"QryFailure", "dns_query", "failure"},
/* Response codes */
{"QryFORMERR", "dns_rcode", "tx-FORMERR"},
{"QryNXDOMAIN", "dns_rcode", "tx-NXDOMAIN"}
#if 0
- { "XfrReqDone", "type", "type_instance" },
- { "UpdateReqFwd", "type", "type_instance" },
- { "UpdateRespFwd", "type", "type_instance" },
- { "UpdateFwdFail", "type", "type_instance" },
- { "UpdateDone", "type", "type_instance" },
- { "UpdateFail", "type", "type_instance" },
- { "UpdateBadPrereq", "type", "type_instance" },
+ { "XfrReqDone", "type", "type_instance" },
+ { "UpdateReqFwd", "type", "type_instance" },
+ { "UpdateRespFwd", "type", "type_instance" },
+ { "UpdateFwdFail", "type", "type_instance" },
+ { "UpdateDone", "type", "type_instance" },
+ { "UpdateFail", "type", "type_instance" },
+ { "UpdateBadPrereq", "type", "type_instance" },
#endif
};
static int nsstats_translation_table_length =
static void submit(time_t ts, const char *plugin_instance, /* {{{ */
const char *type, const char *type_instance, value_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0] = value;
-
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
if (config_parse_time)
vl.time = TIME_T_TO_CDTIME_T(ts);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "bind", sizeof(vl.plugin));
if (plugin_instance) {
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
}
vtmp->vlist = (value_list_t)VALUE_LIST_INIT;
- sstrncpy(vtmp->vlist.host, hostname_g, sizeof(vtmp->vlist.host));
sstrncpy(vtmp->vlist.plugin, "ceph", sizeof(vtmp->vlist.plugin));
sstrncpy(vtmp->vlist.plugin_instance, io->d->name,
sizeof(vtmp->vlist.plugin_instance));
struct timeval end_tv;
struct cconn io_array[g_num_daemons];
- DEBUG("ceph plugin: entering cconn_main_loop(request_type = %d)",
+ DEBUG("ceph plugin: entering cconn_main_loop(request_type = %" PRIu32 ")",
request_type);
+ if (g_num_daemons < 1) {
+ ERROR("ceph plugin: No daemons configured. See the \"Daemon\" config "
+ "option.");
+ return ENOENT;
+ }
+
/* create cconn array */
- memset(io_array, 0, sizeof(io_array));
- for (size_t i = 0; i < g_num_daemons; ++i) {
- io_array[i].d = g_daemons[i];
- io_array[i].request_type = request_type;
- io_array[i].state = CSTATE_UNCONNECTED;
+ for (size_t i = 0; i < g_num_daemons; i++) {
+ io_array[i] = (struct cconn){
+ .d = g_daemons[i],
+ .request_type = request_type,
+ .state = CSTATE_UNCONNECTED,
+ };
}
/** Calculate the time at which we should give up */
vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "cgroups", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "cpu", sizeof(vl.type));
static void chrony_push_data(const char *p_type, const char *p_type_inst,
double p_value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge =
- p_value; /* TODO: Check type??? (counter, gauge, derive, absolute) */
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = p_value};
vl.values_len = 1;
/* XXX: Shall g_chrony_host/g_chrony_port be reflected in the plugin's output?
*/
- /* hostname_g is set in daemon/collectd.c (from config, via gethostname or by
- * resolving localhost) */
- /* defined as: char hostname_g[DATA_MAX_NAME_LEN]; (never NULL) */
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, PLUGIN_NAME_SHORT, sizeof(vl.plugin));
if (g_chrony_plugin_instance != NULL) {
sstrncpy(vl.plugin_instance, g_chrony_plugin_instance,
command line option or B<use lib Dir> in the source code. Please note that it
only has effect on plugins loaded after this option.
+=item B<RegisterLegacyFlush> I<true|false>
+
+The C<Perl plugin> used to register one flush callback (called B<"perl">) and
+call all Perl-based flush handlers when this callback was called. Newer versions
+of the plugin wrap the Perl flush handlers and register them directly with the
+daemon I<in addition> to the legacy B<"perl"> callback. This allows to call
+specific Perl flush handlers, but has the downside that flushing I<all> plugins
+now calls the Perl flush handlers twice (once directly and once via the legacy
+callback). Unfortunately, removing the B<"perl"> callback would break backwards
+compatibility.
+
+This option allows you to disable the legacy B<"perl"> flush callback if you care
+about the double call and don't call the B<"perl"> callback in your setup.
+
=back
=head1 WRITING YOUR OWN PLUGINS
=back
-=head1 KNOWN BUGS
-
-=over 4
-
-=item *
-
-Currently, it is not possible to flush a single Perl plugin only. You can
-either flush all Perl plugins or none at all and you have to use C<perl> as
-plugin name when doing so.
-
-=back
-
=head1 SEE ALSO
L<collectd(1)>,
#@BUILD_PLUGIN_DF_TRUE@LoadPlugin df
#@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
#@BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns
+#@BUILD_PLUGIN_DPDKSTAT_TRUE@LoadPlugin dpdkstat
#@BUILD_PLUGIN_DRBD_TRUE@LoadPlugin drbd
#@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email
#@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
#@BUILD_PLUGIN_GPS_TRUE@LoadPlugin gps
#@BUILD_PLUGIN_GRPC_TRUE@LoadPlugin grpc
#@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
+#@BUILD_PLUGIN_HUGEPAGES_TRUE@LoadPlugin hugepages
+#@BUILD_PLUGIN_INTEL_RDT_TRUE@LoadPlugin intel_rdt
@BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
#@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc
#@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi
#@BUILD_PLUGIN_WRITE_KAFKA_TRUE@LoadPlugin write_kafka
#@BUILD_PLUGIN_WRITE_LOG_TRUE@LoadPlugin write_log
#@BUILD_PLUGIN_WRITE_MONGODB_TRUE@LoadPlugin write_mongodb
+#@BUILD_PLUGIN_WRITE_PROMETHEUS_TRUE@LoadPlugin write_prometheus
#@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
#@BUILD_PLUGIN_WRITE_RIEMANN_TRUE@LoadPlugin write_riemann
#@BUILD_PLUGIN_WRITE_SENSU_TRUE@LoadPlugin write_sensu
#<Plugin "battery">
# ValuesPercentage false
# ReportDegraded false
+# QueryStateFS false
#</Plugin>
#<Plugin "bind">
# SelectNumericQueryTypes true
#</Plugin>
+#<Plugin dpdkstat>
+# Interval 1
+# Coremask "0xf"
+# ProcessType "secondary"
+# FilePrefix "rte"
+# EnabledPortMask 0xffff
+# PortName "interface1"
+# PortName "interface2"
+#</Plugin>
+
#<Plugin email>
# SocketFile "@localstatedir@/run/@PACKAGE_NAME@-email"
# SocketGroup "collectd"
# Port "7634"
#</Plugin>
+#<Plugin hugepages>
+# ReportPerNodeHP true
+# ReportRootHP true
+# ValuesPages true
+# ValuesBytes false
+# ValuesPercentage false
+#</Plugin>
+
+#<Plugin "intel_rdt">
+# Cores "0-2"
+#</Plugin>
+
#<Plugin interface>
# Interface "eth0"
# IgnoreSelected false
#<Plugin memcached>
# <Instance "local">
-# Host "127.0.0.1"
+# #Host "memcache.example.com"
+# Address "127.0.0.1"
# Port "11211"
# </Instance>
#</Plugin>
# Instance "local_user"
# </Match>
# </File>
+# <File "/var/log/nginx/apache-time.log">
+# #Use the following log format in nginx:
+# #log_format response_time '[$host] "$upstream_response_time" ...'
+# Instance "apache"
+# <Match>
+# Regex "^\\S+ \"([0-9.]+)\""
+# <DSType Distribution>
+# Percentile 80 # -> latency-foo-80
+# Percentile 95 # -> latency-foo-95
+# Percentile 99 # -> latency-foo-99
+# Bucket 0 0.1 # -> bucket-latency-foo-0_0.1
+# Bucket 0.1 0.2 # -> bucket-latency-foo-0.1_0.2
+# Bucket 0.2 0.5 # -> bucket-latency-foo-0.2_0.5
+# 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
+# </DSType>
+# Type "latency"
+# Instance "foo"
+# </Match>
+# </File>
#</Plugin>
#<Plugin tail_csv>
# RefreshInterval 60
# Domain "name"
# BlockDevice "name:device"
+# BlockDeviceFormat target
+# BlockDeviceFormatBasename false
# InterfaceDevice "name:device"
# IgnoreSelected false
# HostnameFormat name
# AlwaysAppendDS false
# EscapeCharacter "_"
# SeparateInstances false
+# PreserveSeparator false
+# DropDuplicateFields false
# </Node>
#</Plugin>
# </Node>
#</Plugin>
+#<Plugin write_prometheus>
+# Port "9103"
+#</Plugin>
+
#<Plugin write_redis>
# <Node "example">
# Host "localhost"
# GraphiteEscapeChar "_"
# GraphiteSeparateInstances false
# GraphiteAlwaysAppendDS false
+ # GraphitePreserveSeparator false
</Publish>
# Receive values from an AMQP broker
identifier. If set to B<false> (the default), this is only done when there is
more than one DS.
+=item B<GraphitePreserveSeparator> B<false>|B<true>
+
+If set to B<false> (the default) the C<.> (dot) character is replaced with
+I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
+is preserved, i.e. passed through.
+
=back
=head2 Plugin C<apache>
and "remaining capacity") and B<degraded> (difference between "design capacity"
and "last full capacity").
+=item B<QueryStateFS> B<false>|B<true>
+
+When set to B<true>, the battery plugin will only read statistics
+related to battery performance as exposed by StateFS at
+/run/state. StateFS is used in Mer-based Sailfish OS, for
+example.
+
=back
=head2 Plugin C<bind>
=back
+=head2 Plugin C<dpdkstat>
+
+The I<dpdkstat plugin> collects information about DPDK interfaces using the
+extended NIC stats API in DPDK.
+
+B<Synopsis:>
+
+ <Plugin "dpdkstat">
+ Coremask "0x4"
+ MemoryChannels "4"
+ ProcessType "secondary"
+ FilePrefix "rte"
+ EnabledPortMask 0xffff
+ PortName "interface1"
+ PortName "interface2"
+ </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<Coremask> I<Mask>
+
+A string containing an hexadecimal bit mask of the cores to run on. Note that
+core numbering can change between platforms and should be determined beforehand.
+
+=item B<Memorychannels> I<Channels>
+
+A string containing a number of memory channels per processor socket.
+
+=item B<ProcessType> I<type>
+
+A string containing the type of DPDK process instance.
+
+=item B<FilePrefix> I<File>
+
+The prefix text used for hugepage filenames. The filename will be set to
+/var/run/.<prefix>_config where prefix is what is passed in by the user.
+
+=item B<SocketMemory> I<MB>
+
+A string containing amount of Memory to allocate from hugepages on specific
+sockets in MB
+
+=item B<EnabledPortMask> I<Mask>
+
+A hexidecimal bit mask of the DPDK ports which should be enabled. A mask
+of 0x0 means that all ports will be disabled. A bitmask of all Fs means
+that all ports will be enabled. This is an optional argument - default
+is all ports enabled.
+
+=item B<PortName> I<Name>
+
+A string containing an optional name for the enabled DPDK ports. Each PortName
+option should contain only one port name; specify as many PortName options as
+desired. Default naming convention will be used if PortName is blank. If there
+are less PortName options than there are enabled ports, the default naming
+convention will be used for the additional ports.
+
+=back
+
=head2 Plugin C<email>
=over 4
=back
+=head2 Plugin C<hugepages>
+
+To collect B<hugepages> information, collectd reads directories
+"/sys/devices/system/node/*/hugepages" and
+"/sys/kernel/mm/hugepages".
+Reading of these directories can be disabled by the following
+options (default is enabled).
+
+=over 4
+
+=item B<ReportPerNodeHP> B<true>|B<false>
+
+If enabled, information will be collected from the hugepage
+counters in "/sys/devices/system/node/*/hugepages".
+This is used to check the per-node hugepage statistics on
+a NUMA system.
+
+=item B<ReportRootHP> B<true>|B<false>
+
+If enabled, information will be collected from the hugepage
+counters in "/sys/kernel/mm/hugepages".
+This can be used on both NUMA and non-NUMA systems to check
+the overall hugepage statistics.
+
+=item B<ValuesPages> B<true>|B<false>
+
+Whether to report hugepages metrics in number of pages.
+Defaults to B<true>.
+
+=item B<ValuesBytes> B<false>|B<true>
+
+Whether to report hugepages metrics in bytes.
+Defaults to B<false>.
+
+=item B<ValuesPercentage> B<false>|B<true>
+
+Whether to report hugepages metrics as percentage.
+Defaults to B<false>.
+
+=back
+
+=head2 Plugin C<intel_rdt>
+
+The I<intel_rdt> plugin collects information provided by monitoring features of
+Intel Resource Director Technology (Intel(R) RDT) like Cache Monitoring
+Technology (CMT), Memory Bandwidth Monitoring (MBM). These features provide
+information about utilization of shared resources. CMT monitors last level cache
+occupancy (LLC). MBM supports two types of events reporting local and remote
+memory bandwidth. Local memory bandwidth (MBL) reports the bandwidth of
+accessing memory associated with the local socket. Remote memory bandwidth (MBR)
+reports the bandwidth of accessing the remote socket. Also this technology
+allows to monitor instructions per clock (IPC).
+Monitor events are hardware dependant. Monitoring capabilities are detected on
+plugin initialization and only supported events are monitored.
+
+B<Synopsis:>
+
+ <Plugin "intel_rdt">
+ Cores "0-2" "3,4,6" "8-10,15"
+ </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<Interval> I<seconds>
+
+The interval within which to retrieve statistics on monitored events in seconds.
+For milliseconds divide the time by 1000 for example if the desired interval
+is 50ms, set interval to 0.05. Due to limited capacity of counters it is not
+recommended to set interval higher than 1 sec.
+
+=item B<Cores> I<cores groups>
+
+All events are reported on a per core basis. Monitoring of the events can be
+configured for group of cores (aggregated statistics). This field defines groups
+of cores on which to monitor supported events. The field is represented as list
+of strings with core group values. Each string represents a list of cores in a
+group. Allowed formats are:
+ 0,1,2,3
+ 0-10,20-18
+ 1,3,5-8,10,0x10-12
+
+If an empty string is provided as value for this field default cores
+configuration is applied - a separate group is created for each core.
+
+=back
+
+B<Note:> By default global interval is used to retrieve statistics on monitored
+events. To configure a plugin specific interval use B<Interval> option of the
+intel_rdt <LoadPlugin> block. For milliseconds divide the time by 1000 for
+example if the desired interval is 50ms, set interval to 0.05.
+Due to limited capacity of counters it is not recommended to set interval higher
+than 1 sec.
+
=head2 Plugin C<interface>
=over 4
The B<memcached plugin> connects to a memcached server and queries statistics
about cache utilization, memory and bandwidth used.
-L<http://www.danga.com/memcached/>
+L<http://memcached.org/>
<Plugin "memcached">
<Instance "name">
- Host "memcache.example.com"
+ #Host "memcache.example.com"
+ Address "127.0.0.1"
Port 11211
</Instance>
</Plugin>
=item B<Host> I<Hostname>
-Hostname to connect to. Defaults to B<127.0.0.1>.
+Sets the B<host> field of dispatched values. Defaults to the global hostname
+setting.
+For backwards compatibility, values are also dispatched with the global
+hostname when B<Host> is set to B<127.0.0.1> or B<localhost> and B<Address> is
+not set.
+
+=item B<Address> I<Address>
+
+Hostname or IP to connect to. For backwards compatibility, defaults to the
+value of B<Host> or B<127.0.0.1> if B<Host> is unset.
=item B<Port> I<Port>
-TCP-Port to connect to. Defaults to B<11211>.
+TCP port to connect to. Defaults to B<11211>.
=item B<Socket> I<Path>
Connect to I<memcached> using the UNIX domain socket at I<Path>. If this
-setting is given, the B<Host> and B<Port> settings are ignored.
+setting is given, the B<Address> and B<Port> settings are ignored.
=back
=item B<$1>
-The timestamp of the queried value as a floating point number.
+The timestamp of the queried value as an RFC 3339-formatted local time.
=item B<$2>
Type "counter"
Instance "local_user"
</Match>
+ <Match>
+ Regex "l=([0-9]*\\.[0-9]*)"
+ <DSType "Distribution">
+ Percentile 99
+ Bucket 0 100
+ </DSType>
+ Type "latency"
+ Instance "foo"
+ </Match>
</File>
</Plugin>
Use the last number found.
+=item B<GaugePersist>
+
+Use the last number found. The number is not reset at the end of an interval.
+It is continously reported until another number is matched. This is intended
+for cases in which only state changes are reported, for example a thermometer
+that only reports the temperature when it changes.
+
=item B<CounterSet>
=item B<DeriveSet>
not use the matched subexpression, but simply count the number of matched
lines. Thus, you may use a regular expression without submatch in this case.
+=item B<Distribution>
+
+Type to do calculations based on the distribution of values, primarily
+calculating percentiles. This is primarily geared towards latency, but can be
+used for other metrics as well. The range of values tracked with this setting
+must be in the range (0–2^34) and can be fractional. Please note that neither
+zero nor 2^34 are inclusive bounds, i.e. zero I<cannot> be handled by a
+distribution.
+
+This option must be used together with the B<Percentile> and/or B<Bucket>
+options.
+
+B<Synopsis:>
+
+ <DSType "Distribution">
+ Percentile 99
+ Bucket 0 100
+ </DSType>
+
+=over 4
+
+=item B<Percentile> I<Percent>
+
+Calculate and dispatch the configured percentile, i.e. compute the value, so
+that I<Percent> of all matched values are smaller than or equal to the computed
+latency.
+
+Metrics are reported with the I<type> B<Type> (the value of the above option)
+and the I<type instance> C<[E<lt>InstanceE<gt>-]E<lt>PercentE<gt>>.
+
+This option may be repeated to calculate more than one percentile.
+
+=item B<Bucket> I<lower_bound> I<upper_bound>
+
+Export the number of values (a C<DERIVE>) falling within the given range. Both,
+I<lower_bound> and I<upper_bound> may be a fractional number, such as B<0.5>.
+Each B<Bucket> option specifies an interval C<(I<lower_bound>,
+I<upper_bound>]>, i.e. the range I<excludes> the lower bound and I<includes>
+the upper bound. I<lower_bound> and I<upper_bound> may be zero, meaning no
+lower/upper bound.
+
+To export the entire (0–inf) range without overlap, use the upper bound of the
+previous range as the lower bound of the following range. In other words, use
+the following schema:
+
+ Bucket 0 1
+ Bucket 1 2
+ Bucket 2 5
+ Bucket 5 10
+ Bucket 10 20
+ Bucket 20 50
+ Bucket 50 0
+
+Metrics are reported with the I<type> C<bucket> 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.
+
=back
-As you'd expect the B<Gauge*> types interpret the submatch as a floating point
-number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types interpret
-the submatch as an unsigned integer using L<strtoull(3)>. The B<Derive*> types
-interpret the submatch as a signed integer using L<strtoll(3)>. B<CounterInc>
-and B<DeriveInc> do not use the submatch at all and it may be omitted in this
-case.
+=back
+
+The B<Gauge*> and B<Distribution> types interpret the submatch as a floating
+point number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types
+interpret the submatch as an unsigned integer using L<strtoull(3)>. The
+B<Derive*> types interpret the submatch as a signed integer using
+L<strtoll(3)>. B<CounterInc> and B<DeriveInc> do not use the submatch at all
+and it may be omitted in this case.
=item B<Type> I<Type>
=item B<Host> I<Hostname/IP>
-The hostname or ip which identifies the server.
+The hostname or IP which identifies the server.
Default: B<127.0.0.1>
=item B<Port> I<Service/Port>
=head2 Plugin C<turbostat>
The I<Turbostat plugin> reads CPU frequency and C-state residency on modern
-Intel processors by using the new Model Specific Registers.
+Intel processors by using I<Model Specific Registers>.
=over 4
=item B<CoreCstates> I<Bitmask(Integer)>
-Bitmask of the list of core C states supported by the processor.
+Bit mask of the list of core C-states supported by the processor.
This option should only be used if the automated detection fails.
-Default value extracted from the cpu model and family.
+Default value extracted from the CPU model and family.
Currently supported C-states (by this plugin): 3, 6, 7
-Example: (1<<3)+(1<<6)+(1<<7) = 392 for all states
+B<Example:>
+
+ All states (3, 6 and 7):
+ (1<<3) + (1<<6) + (1<<7) = 392
=item B<PackageCstates> I<Bitmask(Integer)>
-Bitmask of the list of pacages C states supported by the processor.
-This option should only be used if the automated detection fails.
-Default value extracted from the cpu model and family.
+Bit mask of the list of packages C-states supported by the processor. This
+option should only be used if the automated detection fails. Default value
+extracted from the CPU model and family.
Currently supported C-states (by this plugin): 2, 3, 6, 7, 8, 9, 10
-Example: (1<<2)+(1<<3)+(1<<6)+(1<<7) = 396 for states 2, 3, 6 and 7
-
-=item B<SystemManagementInterrupt> I<true>|I<false>
+B<Example:>
-Boolean enabling the collection of the I/O System-Management Interrupt
-counter'. This option should only be used if the automated detection
-fails or if you want to disable this feature.
+ States 2, 3, 6 and 7:
+ (1<<2) + (1<<3) + (1<<6) + (1<<7) = 396
-=item B<DigitalTemperatureSensor> I<true>|I<false>
+=item B<SystemManagementInterrupt> I<true>|I<false>
-Boolean enabling the collection of the temperature of each core.
-This option should only be used if the automated detectionfails or
-if you want to disable this feature.
+Boolean enabling the collection of the I/O System-Management Interrupt counter.
+This option should only be used if the automated detection fails or if you want
+to disable this feature.
=item B<DigitalTemperatureSensor> I<true>|I<false>
-Boolean enabling the collection of the temperature of each package.
-This option should only be used if the automated detectionfails or
-if you want to disable this feature.
+Boolean enabling the collection of the temperature of each core. This option
+should only be used if the automated detection fails or if you want to disable
+this feature.
=item B<TCCActivationTemp> I<Temperature>
-Thermal Control Circuit Activation Temperature of the installed
-CPU. This temperature is used when collecting the temperature of
-cores or packages. This option should only be used if the automated
-detection fails. Default value extracted from B<MSR_IA32_TEMPERATURE_TARGET>
+I<Thermal Control Circuit Activation Temperature> of the installed CPU. This
+temperature is used when collecting the temperature of cores or packages. This
+option should only be used if the automated detection fails. Default value
+extracted from B<MSR_IA32_TEMPERATURE_TARGET>.
=item B<RunningAveragePowerLimit> I<Bitmask(Integer)>
-Bitmask of the list of elements to be thermally monitored. This option
-should only be used if the automated detection fails or if you want to
-disable some collections. The different bits of this bitmask accepted
-by this plugin are:
+Bit mask of the list of elements to be thermally monitored. This option should
+only be used if the automated detection fails or if you want to disable some
+collections. The different bits of this bit mask accepted by this plugin are:
=over 4
=back
+=item B<LogicalCoreNames> I<true>|I<false>
+
+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.
+
=back
=head2 Plugin C<unixsock>
Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
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.
+This corresponds to the C<E<lt>targetE<gt>> node in the XML definition of the
+domain.
+
+If I<BlockDeviceFormat> is set to B<source>, then metrics will be reported
+using the path of the source, e.g. an image file.
+This corresponds to the C<E<lt>sourceE<gt>> node in the XML definition of the
+domain.
+
+B<Example:>
+
+If the domain XML have the following device defined:
+
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw' cache='none' io='native' discard='unmap'/>
+ <source dev='/var/lib/libvirt/images/image1.qcow2'/>
+ <target dev='sda' bus='scsi'/>
+ <boot order='2'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+
+Setting C<BlockDeviceFormat target> will cause the I<type instance> to be set
+to C<sda>.
+Setting C<BlockDeviceFormat source> will cause the I<type instance> to be set
+to C<var_lib_libvirt_images_image1.qcow2>.
+
+=item B<BlockDeviceFormatBasename> B<false>|B<true>
+
+The B<BlockDeviceFormatBasename> controls whether the full path or the
+L<basename(1)> of the source is being used as the I<type instance> when
+B<BlockDeviceFormat> is set to B<source>. Defaults to B<false>.
+
+B<Example:>
+
+Assume the device path (source tag) is C</var/lib/libvirt/images/image1.qcow2>.
+Setting C<BlockDeviceFormatBasename false> will cause the I<type instance> to
+be set to C<var_lib_libvirt_images_image1.qcow2>.
+Setting C<BlockDeviceFormatBasename true> will cause the I<type instance> to be
+set to C<image1.qcow2>.
+
=item B<HostnameFormat> B<name|uuid|hostname|...>
When the virt plugin logs data, it sets the hostname of the collected data
identifier. If set to B<false> (the default), this is only done when there is
more than one DS.
+=item B<PreserveSeparator> B<false>|B<true>
+
+If set to B<false> (the default) the C<.> (dot) character is replaced with
+I<EscapeCharacter>. Otherwise, if set to B<true>, the C<.> (dot) character
+is preserved, i.e. passed through.
+
+=item B<DropDuplicateFields> B<false>|B<true>
+
+If set to B<true>, detect and remove duplicate components in Graphite metric
+names. For example, the metric name C<host.load.load.shortterm> will
+be shortened to C<host.load.shortterm>.
+
+=back
+
+=head2 Plugin C<write_log>
+
+The C<write_log> plugin writes metrics as INFO log messages.
+
+This plugin supports two output formats: I<Graphite> and I<JSON>.
+
+Synopsis:
+
+ <Plugin write_log>
+ Format Graphite
+ </Plugin>
+
+=over 4
+
+=item B<Format> I<Format>
+
+The output format to use. Can be one of C<Graphite> or C<JSON>.
+
=back
=head2 Plugin C<write_tsdb>
=back
+=head2 Plugin C<write_prometheus>
+
+The I<write_prometheus plugin> implements a tiny webserver that can be scraped
+using I<Prometheus>.
+
+B<Options:>
+
+=over 4
+
+=item B<Port> I<Port>
+
+Port the embedded webserver should listen on. Defaults to B<9103>.
+
+=item B<StalenessDelta> I<Seconds>
+
+Time in seconds after which I<Prometheus> considers a metric "stale" if it
+hasn't seen any update for it. This value must match the setting in Prometheus.
+It defaults to B<300> seconds (5 minutes), same as Prometheus.
+
+B<Background:>
+
+I<Prometheus> has a global setting, C<StalenessDelta>, which controls after
+which time a metric without updates is considered "stale". This setting
+effectively puts an upper limit on the interval in which metrics are reported.
+
+When the I<write_prometheus plugin> encounters a metric with an interval
+exceeding this limit, it will inform you, the user, and provide the metric to
+I<Prometheus> B<without> a timestamp. That causes I<Prometheus> to consider the
+metric "fresh" each time it is scraped, with the time of the scrape being
+considered the time of the update. The result is that there appear more
+datapoints in I<Prometheus> than were actually created, but at least the metric
+doesn't disappear periodically.
+
+=back
+
=head2 Plugin C<write_http>
This output plugin submits values to an HTTP server using POST requests and
default), the plugin and plugin instance (and likewise the type and type
instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+=item B<GraphiteAlwaysAppendDS> B<true>|B<false>
+
+If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
+identifier. If set to B<false> (the default), this is only done when there is
+more than one DS.
+
+=item B<GraphitePreserveSeparator> B<false>|B<true>
+
+If set to B<false> (the default) the C<.> (dot) character is replaced with
+I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
+is preserved, i.e. passed through.
+
=item B<StoreRates> B<true>|B<false>
If set to B<true> (the default), convert counter values to rates. If set to
=item B<TypeInstance> I<Regex>
+=item B<MetaData> I<String> I<Regex>
+
Match values where the given regular expressions match the various fields of
the identifier of a value. If multiple regular expressions are given, B<all>
regexen must match for a value to match.
=item B<TypeInstance> I<Regex> I<Replacement>
+=item B<MetaData> I<String> I<Regex> I<Replacement>
+
+=item B<DeleteMetaData> I<String> I<Regex>
+
Match the appropriate field with the given regular expression I<Regex>. If the
regular expression matches, that part that matches is replaced with
I<Replacement>. If multiple places of the input buffer match a given regular
=item B<MetaData> I<String> I<String>
-Set the appropriate field to the given string. The strings for plugin instance
-and type instance may be empty, the strings for host and plugin may not be
-empty. It's currently not possible to set the type of a value this way.
+Set the appropriate field to the given string. The strings for plugin instance,
+type instance, and meta data may be empty, the strings for host and plugin may
+not be empty. It's currently not possible to set the type of a value this way.
+
+The following placeholders will be replaced by an appropriate value:
+
+=over 4
+
+=item B<%{host}>
+
+=item B<%{plugin}>
+
+=item B<%{plugin_instance}>
+
+=item B<%{type}>
+
+=item B<%{type_instance}>
+
+These placeholders are replaced by the identifier field of the same name.
+
+=item B<%{meta:>I<name>B<}>
+
+These placeholders are replaced by the meta data value with the given name.
+
+=back
+
+Please note that these placeholders are B<case sensitive>!
+
+=item B<DeleteMetaData> I<String>
+
+Delete the named meta data field.
=back
vl.values = &conntrack;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "conntrack", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_instance != NULL)
static int conntrack_read(void) {
value_t conntrack, conntrack_max, conntrack_pct;
- FILE *fh;
- char buffer[64] = {0};
- size_t buffer_len;
- fh = fopen(old_files ? CONNTRACK_FILE_OLD : CONNTRACK_FILE, "r");
- if (fh == NULL)
+ char const *path = old_files ? CONNTRACK_FILE_OLD : CONNTRACK_FILE;
+ if (parse_value_file(path, &conntrack, DS_TYPE_GAUGE) != 0) {
+ ERROR("conntrack plugin: Reading \"%s\" failed.", path);
return (-1);
-
- if (fgets(buffer, sizeof(buffer), fh) == NULL) {
- fclose(fh);
- return (-1);
- }
- fclose(fh);
-
- /* strip trailing newline. */
- buffer_len = strlen(buffer);
- while ((buffer_len > 0) && isspace((int)buffer[buffer_len - 1])) {
- buffer[buffer_len - 1] = 0;
- buffer_len--;
}
- if (parse_value(buffer, &conntrack, DS_TYPE_GAUGE) != 0)
- return (-1);
-
- conntrack_submit("conntrack", NULL, conntrack);
-
- fh = fopen(old_files ? CONNTRACK_MAX_FILE_OLD : CONNTRACK_MAX_FILE, "r");
- if (fh == NULL)
+ path = old_files ? CONNTRACK_MAX_FILE_OLD : CONNTRACK_MAX_FILE;
+ if (parse_value_file(path, &conntrack_max, DS_TYPE_GAUGE) != 0) {
+ ERROR("conntrack plugin: Reading \"%s\" failed.", path);
return (-1);
-
- memset(buffer, 0, sizeof(buffer));
- if (fgets(buffer, sizeof(buffer), fh) == NULL) {
- fclose(fh);
- return (-1);
- }
- fclose(fh);
-
- /* strip trailing newline. */
- buffer_len = strlen(buffer);
- while ((buffer_len > 0) && isspace((int)buffer[buffer_len - 1])) {
- buffer[buffer_len - 1] = 0;
- buffer_len--;
}
- if (parse_value(buffer, &conntrack_max, DS_TYPE_GAUGE) != 0)
- return (-1);
+ conntrack_pct.gauge = (conntrack.gauge / conntrack_max.gauge) * 100;
+ conntrack_submit("conntrack", NULL, conntrack);
conntrack_submit("conntrack", "max", conntrack_max);
- conntrack_pct.gauge = (conntrack.gauge / conntrack_max.gauge) * 100;
conntrack_submit("percent", "used", conntrack_pct);
return (0);
#endif
static void cs_submit(derive_t context_switches) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = (derive_t)context_switches;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = context_switches};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "contextswitch", sizeof(vl.plugin));
sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
/* #endif HAVE_LIBSTATGRAB */
#elif defined(HAVE_PERFSTAT)
+#define TOTAL_IDLE 0
+#define TOTAL_USER 1
+#define TOTAL_SYS 2
+#define TOTAL_WAIT 3
+#define TOTAL_STAT_NUM 4
+static value_to_rate_state_t total_conv[TOTAL_STAT_NUM];
static perfstat_cpu_t *perfcpu;
static int numcpu;
static int pnumcpu;
static void submit_value(int cpu_num, int cpu_state, const char *type,
value_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- memcpy(&values[0], &value, sizeof(value));
-
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "cpu", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, cpu_state_names[cpu_state],
plugin_dispatch_values(&vl);
}
-static void submit_percent(int cpu_num, int cpu_state, gauge_t percent) {
- value_t value;
-
+static void submit_percent(int cpu_num, int cpu_state, gauge_t value) {
/* This function is called for all known CPU states, but each read
* method will only report a subset. The remaining states are left as
* NAN and we ignore them here. */
- if (isnan(percent))
+ if (isnan(value))
return;
- value.gauge = percent;
- submit_value(cpu_num, cpu_state, "percent", value);
+ submit_value(cpu_num, cpu_state, "percent", (value_t){.gauge = value});
}
-static void submit_derive(int cpu_num, int cpu_state, derive_t derive) {
- value_t value;
-
- value.derive = derive;
- submit_value(cpu_num, cpu_state, "cpu", value);
+static void submit_derive(int cpu_num, int cpu_state, derive_t value) {
+ submit_value(cpu_num, cpu_state, "cpu", (value_t){.derive = value});
}
/* Takes the zero-index number of a CPU and makes sure that the module-global
return (&cpu_states[index]);
} /* }}} cpu_state_t *get_cpu_state */
+#if defined(HAVE_PERFSTAT) /* {{{ */
+/* populate global aggregate cpu rate */
+static int total_rate(gauge_t *sum_by_state, size_t state, derive_t d,
+ value_to_rate_state_t *conv, cdtime_t now) {
+ gauge_t rate = NAN;
+ int status =
+ value_to_rate(&rate, (value_t){.derive = d}, DS_TYPE_DERIVE, now, conv);
+ if (status != 0)
+ return (status);
+
+ sum_by_state[state] = rate;
+
+ if (state != COLLECTD_CPU_STATE_IDLE)
+ RATE_ADD(sum_by_state[COLLECTD_CPU_STATE_ACTIVE], sum_by_state[state]);
+ return (0);
+}
+#endif /* }}} HAVE_PERFSTAT */
+
/* Populates the per-CPU COLLECTD_CPU_STATE_ACTIVE rate and the global
* rate_by_state
* array. */
RATE_ADD(sum_by_state[COLLECTD_CPU_STATE_ACTIVE],
this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].rate);
}
+
+#if defined(HAVE_PERFSTAT) /* {{{ */
+ cdtime_t now = cdtime();
+ perfstat_cpu_total_t cputotal = {0};
+
+ if (!perfstat_cpu_total(NULL, &cputotal, sizeof(cputotal), 1)) {
+ char errbuf[1024];
+ WARNING("cpu plugin: perfstat_cpu_total: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return;
+ }
+
+ /* Reset COLLECTD_CPU_STATE_ACTIVE */
+ sum_by_state[COLLECTD_CPU_STATE_ACTIVE] = NAN;
+
+ /* Physical Processor Utilization */
+ total_rate(sum_by_state, COLLECTD_CPU_STATE_IDLE, (derive_t)cputotal.pidle,
+ &total_conv[TOTAL_IDLE], now);
+ total_rate(sum_by_state, COLLECTD_CPU_STATE_USER, (derive_t)cputotal.puser,
+ &total_conv[TOTAL_USER], now);
+ total_rate(sum_by_state, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cputotal.psys,
+ &total_conv[TOTAL_SYS], now);
+ total_rate(sum_by_state, COLLECTD_CPU_STATE_WAIT, (derive_t)cputotal.pwait,
+ &total_conv[TOTAL_WAIT], now);
+#endif /* }}} HAVE_PERFSTAT */
} /* }}} void aggregate */
/* Commits (dispatches) the values for one CPU or the global aggregation.
} /* }}} void cpu_commit_one */
/* Commits the number of cores */
-static void cpu_commit_num_cpu(gauge_t num_cpu) /* {{{ */
+static void cpu_commit_num_cpu(gauge_t value) /* {{{ */
{
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = num_cpu;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "cpu", sizeof(vl.plugin));
sstrncpy(vl.type, "count", sizeof(vl.type));
#include "common.h"
#include "plugin.h"
-#define MODULE_NAME "cpufreq"
-
static int num_cpu = 0;
static int cpufreq_init(void) {
return (0);
} /* int cpufreq_init */
-static void cpufreq_submit(int cpu_num, double value) {
- value_t values[1];
+static void cpufreq_submit(int cpu_num, value_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "cpufreq", sizeof(vl.plugin));
sstrncpy(vl.type, "cpufreq", sizeof(vl.type));
ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%i", cpu_num);
}
static int cpufreq_read(void) {
- int status;
- unsigned long long val;
- FILE *fp;
- char filename[256];
- char buffer[16];
-
for (int i = 0; i < num_cpu; i++) {
- status = ssnprintf(filename, sizeof(filename),
- "/sys/devices/system/cpu/cpu%d/cpufreq/"
- "scaling_cur_freq",
- i);
- if ((status < 1) || ((unsigned int)status >= sizeof(filename)))
- return (-1);
-
- if ((fp = fopen(filename, "r")) == NULL) {
- char errbuf[1024];
- WARNING("cpufreq: fopen (%s): %s", filename,
- sstrerror(errno, errbuf, sizeof(errbuf)));
- return (-1);
- }
-
- if (fgets(buffer, 16, fp) == NULL) {
- char errbuf[1024];
- WARNING("cpufreq: fgets: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
- fclose(fp);
- return (-1);
- }
-
- if (fclose(fp)) {
- char errbuf[1024];
- WARNING("cpufreq: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+ char filename[PATH_MAX];
+ ssnprintf(filename, sizeof(filename),
+ "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
+
+ value_t v;
+ if (parse_value_file(filename, &v, DS_TYPE_GAUGE) != 0) {
+ WARNING("cpufreq plugin: Reading \"%s\" failed.", filename);
+ continue;
}
- /* You're seeing correctly: The file is reporting kHz values.. */
- val = atoll(buffer) * 1000;
+ /* convert kHz to Hz */
+ v.gauge *= 1000.0;
- cpufreq_submit(i, val);
+ cpufreq_submit(i, v);
}
return (0);
#include "plugin.h"
static void cpusleep_submit(derive_t cpu_sleep) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = cpu_sleep;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = cpu_sleep};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "cpusleep", sizeof(vl.plugin));
sstrncpy(vl.type, "total_time_in_ms", sizeof(vl.type));
} /* }}} int cc_init */
static void cc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */
- const cu_match_value_t *mv) {
- value_t values[1];
+ value_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0] = mv->value;
-
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, wm->type, sizeof(vl.type));
static void cc_submit_response_code(const web_page_t *wp, long code) /* {{{ */
{
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = code;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = (gauge_t)code};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "response_code", sizeof(vl.type));
} /* }}} void cc_submit_response_code */
static void cc_submit_response_time(const web_page_t *wp, /* {{{ */
- cdtime_t response_time) {
- value_t values[1];
+ gauge_t response_time) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = CDTIME_T_TO_DOUBLE(response_time);
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = response_time};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "response_time", sizeof(vl.type));
}
if (wp->response_time)
- cc_submit_response_time(wp, cdtime() - start);
+ cc_submit_response_time(wp, CDTIME_T_TO_DOUBLE(cdtime() - start));
if (wp->stats != NULL)
curl_stats_dispatch(wp->stats, wp->curl, hostname_g, "curl", wp->instance);
continue;
}
- cc_submit(wp, wm, mv);
+ cc_submit(wp, wm, mv->value);
match_value_reset(mv);
} /* for (wm = wp->matches; wm != NULL; wm = wm->next) */
cb_name = ssnprintf_alloc("curl_json-%s-%s", db->instance,
db->url ? db->url : db->sock);
- user_data_t ud = {.data = db, .free_func = cj_free};
-
plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read,
- /* interval = */ db->interval, &ud);
+ /* interval = */ db->interval,
+ &(user_data_t){
+ .data = db, .free_func = cj_free,
+ });
sfree(cb_name);
} else {
cj_free(db);
cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
- user_data_t ud = {.data = db, .free_func = cx_free};
-
plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
- /* interval = */ 0, &ud);
+ /* interval = */ 0,
+ &(user_data_t){
+ .data = db, .free_func = cx_free,
+ });
sfree(cb_name);
} else {
cx_free(db);
utils_ignorelist.c utils_ignorelist.h \
utils_llist.c utils_llist.h \
utils_random.c utils_random.h \
- utils_tail_match.c utils_tail_match.h \
- utils_match.c utils_match.h \
utils_subst.c utils_subst.h \
- utils_tail.c utils_tail.h \
utils_time.c utils_time.h \
types_list.c types_list.h \
utils_threshold.c utils_threshold.h
int status;
str = global_option_get("Hostname");
- if (str != NULL) {
+ if ((str != NULL) && (str[0] != 0)) {
sstrncpy(hostname_g, str, sizeof(hostname_g));
return (0);
}
wait_until = cdtime() + interval;
while (loop == 0) {
- struct timespec ts_wait = {0, 0};
cdtime_t now;
#if HAVE_LIBKSTAT
continue;
}
- CDTIME_T_TO_TIMESPEC(wait_until - now, &ts_wait);
+ struct timespec ts_wait = CDTIME_T_TO_TIMESPEC(wait_until - now);
wait_until = wait_until + interval;
while ((loop == 0) && (nanosleep(&ts_wait, &ts_wait) != 0)) {
* something wrong.
*/
if (init_global_variables() != 0)
- return (1);
+ exit(EXIT_FAILURE);
if (test_config)
return (0);
ptr = (const char *)buf;
nleft = count;
- if (fd < 0)
- return (-1);
+ if (fd < 0) {
+ errno = EINVAL;
+ return errno;
+ }
/* checking for closed peer connection */
pfd.fd = fd;
if (poll(&pfd, 1, 0) > 0) {
char buffer[32];
if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
- // if recv returns zero (even though poll() said there is data to be
- // read),
- // that means the connection has been closed
- return -1;
+ /* 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;
}
}
continue;
if (status < 0)
- return (status);
+ return errno ? errno : status;
nleft = nleft - ((size_t)status);
ptr = ptr + ((size_t)status);
int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num,
const char *sep) {
- size_t avail;
- char *ptr;
- size_t sep_len;
+ size_t avail = 0;
+ char *ptr = buffer;
+ size_t sep_len = 0;
- if ((buffer_size < 1) || (fields_num == 0))
- return (-1);
+ size_t buffer_req = 0;
+
+ if (((fields_num != 0) && (fields == NULL)) ||
+ ((buffer_size != 0) && (buffer == NULL)))
+ return (-EINVAL);
+
+ if (buffer != NULL)
+ buffer[0] = 0;
- memset(buffer, 0, buffer_size);
- ptr = buffer;
- avail = buffer_size - 1;
+ if (buffer_size != 0)
+ avail = buffer_size - 1;
- sep_len = 0;
if (sep != NULL)
sep_len = strlen(sep);
for (size_t i = 0; i < fields_num; i++) {
- size_t field_len;
+ size_t field_len = strlen(fields[i]);
- if ((i > 0) && (sep_len > 0)) {
- if (avail < sep_len)
- return (-1);
+ if (i != 0)
+ buffer_req += sep_len;
+ buffer_req += field_len;
+
+ if ((i != 0) && (sep_len > 0)) {
+ if (sep_len >= avail) {
+ /* prevent subsequent iterations from writing to the
+ * buffer. */
+ avail = 0;
+ continue;
+ }
memcpy(ptr, sep, sep_len);
+
ptr += sep_len;
avail -= sep_len;
}
- field_len = strlen(fields[i]);
- if (avail < field_len)
- return (-1);
+ if (field_len > avail)
+ field_len = avail;
memcpy(ptr, fields[i], field_len);
ptr += field_len;
+
avail -= field_len;
+ if (ptr != NULL)
+ *ptr = 0;
}
- assert(buffer[buffer_size - 1] == 0);
- return ((int)strlen(buffer));
+ return (int)buffer_req;
}
int escape_string(char *buffer, size_t buffer_size) {
int parse_identifier(char *str, char **ret_host, char **ret_plugin,
char **ret_plugin_instance, char **ret_type,
- char **ret_type_instance) {
+ char **ret_type_instance, char *default_host) {
char *hostname = NULL;
char *plugin = NULL;
char *plugin_instance = NULL;
plugin++;
type = strchr(plugin, '/');
- if (type == NULL)
- return (-1);
- *type = '\0';
- type++;
+ if (type == NULL) {
+ if (default_host == NULL)
+ return (-1);
+ /* else: no host specified; use default */
+ type = plugin;
+ plugin = hostname;
+ hostname = default_host;
+ } else {
+ *type = '\0';
+ type++;
+ }
plugin_instance = strchr(plugin, '-');
if (plugin_instance != NULL) {
sstrncpy(str_copy, str, sizeof(str_copy));
status = parse_identifier(str_copy, &host, &plugin, &plugin_instance, &type,
- &type_instance);
+ &type_instance,
+ /* default_host = */ NULL);
if (status != 0)
return (status);
return (0);
} /* int parse_values */
+int parse_value_file(char const *path, value_t *ret_value, int ds_type) {
+ FILE *fh;
+ char buffer[256];
+
+ fh = fopen(path, "r");
+ if (fh == NULL)
+ return (-1);
+
+ if (fgets(buffer, sizeof(buffer), fh) == NULL) {
+ fclose(fh);
+ return (-1);
+ }
+
+ fclose(fh);
+
+ strstripnewline(buffer);
+
+ return parse_value(buffer, ret_value, ds_type);
+} /* int parse_value_file */
+
#if !HAVE_GETPWNAM_R
int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
struct passwd **pwbufp) {
int status;
int socktype;
- socklen_t socklen = sizeof(socklen_t);
- int so_keepalive = 1;
-
- status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype, &socklen);
+ status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype,
+ &(socklen_t){sizeof(socktype)});
if (status != 0) {
WARNING("set_sock_opts: failed to determine socket type");
return;
}
if (socktype == SOCK_STREAM) {
- status = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &so_keepalive,
- sizeof(so_keepalive));
+ status =
+ setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int));
if (status != 0)
WARNING("set_sock_opts: failed to set socket keepalive flag");
* is equivalent to the Perl built-in `join'.
*
* PARAMETERS
- * `dst' Buffer where the result is stored.
+ * `dst' Buffer where the result is stored. Can be NULL if you need to
+ * determine the required buffer size only.
* `dst_len' Length of the destination buffer. No more than this many
* bytes will be written to the memory pointed to by `dst',
- * including the trailing null-byte.
+ * including the trailing null-byte. Must be zero if dst is
+ * NULL.
* `fields' Array of strings to be joined.
* `fields_num' Number of elements in the `fields' array.
* `sep' String to be inserted between any two elements of `fields'.
* Instead of passing "" (empty string) one can pass NULL.
*
* RETURN VALUE
- * Returns the number of characters in `dst', NOT including the trailing
- * null-byte. If an error occurred (empty array or `dst' too small) a value
- * smaller than zero will be returned.
+ * Returns the number of characters in the resulting string, excluding a
+ * tailing null byte. If this value is greater than or equal to "dst_len", the
+ * result in "dst" is truncated (but still null terminated). On error a
+ * negative value is returned.
*/
int strjoin(char *dst, size_t dst_len, char **fields, size_t fields_num,
const char *sep);
int parse_identifier(char *str, char **ret_host, char **ret_plugin,
char **ret_plugin_instance, char **ret_type,
- char **ret_type_instance);
+ char **ret_type_instance, char *default_host);
int parse_identifier_vl(const char *str, value_list_t *vl);
int parse_value(const char *value, value_t *ret_value, int ds_type);
int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds);
+/* parse_value_file reads "path" and parses its content as an integer or
+ * floating point, depending on "ds_type". On success, the value is stored in
+ * "ret_value" and zero is returned. On failure, a non-zero value is returned.
+ */
+int parse_value_file(char const *path, value_t *ret_value, int ds_type);
+
#if !HAVE_GETPWNAM_R
int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
struct passwd **pwbufp);
}
DEF_TEST(strjoin) {
- char buffer[16];
- char *fields[4];
- int status;
-
- fields[0] = "foo";
- fields[1] = "bar";
- fields[2] = "baz";
- fields[3] = "qux";
-
- status = strjoin(buffer, sizeof(buffer), fields, 2, "!");
- OK(status == 7);
- EXPECT_EQ_STR("foo!bar", buffer);
-
- status = strjoin(buffer, sizeof(buffer), fields, 1, "!");
- OK(status == 3);
- EXPECT_EQ_STR("foo", buffer);
-
- status = strjoin(buffer, sizeof(buffer), fields, 0, "!");
- OK(status < 0);
+ struct {
+ char **fields;
+ size_t fields_num;
+ char *separator;
- status = strjoin(buffer, sizeof(buffer), fields, 2, "rcht");
- OK(status == 10);
- EXPECT_EQ_STR("foorchtbar", buffer);
+ int want_return;
+ char *want_buffer;
+ } cases[] = {
+ /* Normal case. */
+ {(char *[]){"foo", "bar"}, 2, "!", 7, "foo!bar"},
+ /* One field only. */
+ {(char *[]){"foo"}, 1, "!", 3, "foo"},
+ /* No fields at all. */
+ {NULL, 0, "!", 0, ""},
+ /* Longer separator. */
+ {(char *[]){"foo", "bar"}, 2, "rcht", 10, "foorchtbar"},
+ /* Empty separator. */
+ {(char *[]){"foo", "bar"}, 2, "", 6, "foobar"},
+ /* NULL separator. */
+ {(char *[]){"foo", "bar"}, 2, NULL, 6, "foobar"},
+ /* buffer not large enough -> string is truncated. */
+ {(char *[]){"aaaaaa", "bbbbbb", "c!"}, 3, "-", 16, "aaaaaa-bbbbbb-c"},
+ /* buffer not large enough -> last field fills buffer completely. */
+ {(char *[]){"aaaaaaa", "bbbbbbb", "!"}, 3, "-", 17, "aaaaaaa-bbbbbbb"},
+ /* buffer not large enough -> string does *not* end in separator. */
+ {(char *[]){"aaaa", "bbbb", "cccc", "!"}, 4, "-", 16, "aaaa-bbbb-cccc"},
+ /* buffer not large enough -> string does not end with partial
+ separator. */
+ {(char *[]){"aaaaaa", "bbbbbb", "!"}, 3, "+-", 17, "aaaaaa+-bbbbbb"},
+ };
- status = strjoin(buffer, sizeof(buffer), fields, 4, "");
- OK(status == 12);
- EXPECT_EQ_STR("foobarbazqux", buffer);
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ char buffer[16];
+ int status;
- status = strjoin(buffer, sizeof(buffer), fields, 4, "!");
- OK(status == 15);
- EXPECT_EQ_STR("foo!bar!baz!qux", buffer);
+ memset(buffer, 0xFF, sizeof(buffer));
+ status = strjoin(buffer, sizeof(buffer), cases[i].fields,
+ cases[i].fields_num, cases[i].separator);
+ EXPECT_EQ_INT(cases[i].want_return, status);
+ EXPECT_EQ_STR(cases[i].want_buffer, buffer);
+ }
- fields[0] = "0123";
- fields[1] = "4567";
- fields[2] = "8901";
- fields[3] = "2345";
- status = strjoin(buffer, sizeof(buffer), fields, 4, "-");
- OK(status < 0);
+ /* use (NULL, 0) to determine required buffer size. */
+ EXPECT_EQ_INT(3, strjoin(NULL, 0, (char *[]){"a", "b"}, 2, "-"));
return (0);
}
};
for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
- value_to_rate_state_t state = {cases[i].v0,
- TIME_T_TO_CDTIME_T(cases[i].t0)};
+ cdtime_t t0 = TIME_T_TO_CDTIME_T(cases[i].t0);
+ value_to_rate_state_t state = {
+ .last_value = cases[i].v0, .last_time = t0,
+ };
gauge_t got;
if (cases[i].t0 == 0) {
- OK(value_to_rate(&got, cases[i].v1, cases[i].ds_type,
- TIME_T_TO_CDTIME_T(cases[i].t1), &state) == EAGAIN);
+ EXPECT_EQ_INT(EAGAIN,
+ value_to_rate(&got, cases[i].v1, cases[i].ds_type,
+ TIME_T_TO_CDTIME_T(cases[i].t1), &state));
continue;
}
- OK(value_to_rate(&got, cases[i].v1, cases[i].ds_type,
- TIME_T_TO_CDTIME_T(cases[i].t1), &state) == 0);
+ EXPECT_EQ_INT(0, value_to_rate(&got, cases[i].v1, cases[i].ds_type,
+ TIME_T_TO_CDTIME_T(cases[i].t1), &state));
EXPECT_EQ_DOUBLE(cases[i].want, got);
}
#include "collectd.h"
+#include "common.h"
#include "meta_data.h"
#include "plugin.h"
+#define MD_MAX_NONSTRING_CHARS 128
+
/*
* Data types
*/
return (0);
} /* }}} int meta_data_get_boolean */
+int meta_data_as_string(meta_data_t *md, /* {{{ */
+ const char *key, char **value) {
+ meta_entry_t *e;
+ char *actual;
+ char buffer[MD_MAX_NONSTRING_CHARS]; /* For non-string types. */
+ char *temp;
+ int type;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return (-EINVAL);
+
+ pthread_mutex_lock(&md->lock);
+
+ e = md_entry_lookup(md, key);
+ if (e == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ return (-ENOENT);
+ }
+
+ type = e->type;
+
+ switch (type) {
+ case MD_TYPE_STRING:
+ actual = e->value.mv_string;
+ break;
+ case MD_TYPE_SIGNED_INT:
+ ssnprintf(buffer, sizeof(buffer), "%" PRIi64, e->value.mv_signed_int);
+ actual = buffer;
+ break;
+ case MD_TYPE_UNSIGNED_INT:
+ ssnprintf(buffer, sizeof(buffer), "%" PRIu64, e->value.mv_unsigned_int);
+ actual = buffer;
+ break;
+ case MD_TYPE_DOUBLE:
+ ssnprintf(buffer, sizeof(buffer), GAUGE_FORMAT, e->value.mv_double);
+ actual = buffer;
+ break;
+ case MD_TYPE_BOOLEAN:
+ actual = e->value.mv_boolean ? "true" : "false";
+ break;
+ default:
+ pthread_mutex_unlock(&md->lock);
+ ERROR("meta_data_as_string: unknown type %d for key `%s'", type, key);
+ return (-ENOENT);
+ }
+
+ pthread_mutex_unlock(&md->lock);
+
+ temp = md_strdup(actual);
+ if (temp == NULL) {
+ pthread_mutex_unlock(&md->lock);
+ ERROR("meta_data_as_string: md_strdup failed for key `%s'.", key);
+ return (-ENOMEM);
+ }
+
+ *value = temp;
+
+ return (0);
+} /* }}} int meta_data_as_string */
+
/* vim: set sw=2 sts=2 et fdm=marker : */
int meta_data_get_double(meta_data_t *md, const char *key, double *value);
int meta_data_get_boolean(meta_data_t *md, const char *key, _Bool *value);
+/* Returns the value as a string, regardless of the type. */
+int meta_data_as_string(meta_data_t *md, const char *key, char **value);
+
#endif /* META_DATA_H */
/* vim: set sw=2 sts=2 et : */
* Sebastian Harl <sh at tokkee.org>
**/
+/* _GNU_SOURCE is needed in Linux to use pthread_setname_np */
+#define _GNU_SOURCE
+
#include "collectd.h"
#include "common.h"
#include "utils_random.h"
#include "utils_time.h"
+#if HAVE_PTHREAD_NP_H
+#include <pthread_np.h> /* for pthread_set_name_np(3) */
+#endif
+
#include <ltdl.h>
/*
static char *plugindir = NULL;
#ifndef DEFAULT_MAX_READ_INTERVAL
-#define DEFAULT_MAX_READ_INTERVAL TIME_T_TO_CDTIME_T(86400)
+#define DEFAULT_MAX_READ_INTERVAL TIME_T_TO_CDTIME_T_STATIC(86400)
#endif
static c_heap_t *read_heap = NULL;
static llist_t *read_list;
static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER;
static pthread_t *read_threads = NULL;
-static int read_threads_num = 0;
+static size_t read_threads_num = 0;
static cdtime_t max_read_interval = DEFAULT_MAX_READ_INTERVAL;
static write_queue_t *write_queue_head;
return (plugindir);
}
-static void plugin_update_internal_statistics(void) { /* {{{ */
- derive_t copy_write_queue_length;
- value_list_t vl = VALUE_LIST_INIT;
- value_t values[2];
-
- copy_write_queue_length = write_queue_length;
+static int plugin_update_internal_statistics(void) { /* {{{ */
+ gauge_t copy_write_queue_length = (gauge_t)write_queue_length;
/* Initialize `vl' */
- vl.values = values;
- vl.values_len = 2;
- vl.time = 0;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ value_list_t vl = VALUE_LIST_INIT;
sstrncpy(vl.plugin, "collectd", sizeof(vl.plugin));
-
- vl.type_instance[0] = 0;
- vl.values_len = 1;
+ vl.interval = plugin_get_interval();
/* Write queue */
sstrncpy(vl.plugin_instance, "write_queue", sizeof(vl.plugin_instance));
/* Write queue : queue length */
- vl.values[0].gauge = (gauge_t)copy_write_queue_length;
+ vl.values = &(value_t){.gauge = copy_write_queue_length};
+ vl.values_len = 1;
sstrncpy(vl.type, "queue_length", sizeof(vl.type));
vl.type_instance[0] = 0;
plugin_dispatch_values(&vl);
/* Write queue : Values dropped (queue length > low limit) */
- vl.values[0].derive = (derive_t)stats_values_dropped;
+ vl.values = &(value_t){.gauge = (gauge_t)stats_values_dropped};
+ vl.values_len = 1;
sstrncpy(vl.type, "derive", sizeof(vl.type));
sstrncpy(vl.type_instance, "dropped", sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
sstrncpy(vl.plugin_instance, "cache", sizeof(vl.plugin_instance));
/* Cache : Nb entry in cache tree */
- vl.values[0].gauge = (gauge_t)uc_get_size();
+ vl.values = &(value_t){.gauge = (gauge_t)uc_get_size()};
+ vl.values_len = 1;
sstrncpy(vl.type, "cache_size", sizeof(vl.type));
vl.type_instance[0] = 0;
plugin_dispatch_values(&vl);
- return;
-} /* }}} void plugin_update_internal_statistics */
+ return 0;
+} /* }}} int plugin_update_internal_statistics */
static void destroy_callback(callback_func_t *cf) /* {{{ */
{
* pthread_cond_timedwait returns. */
rc = 0;
while ((read_loop != 0) && (cdtime() < rf->rf_next_read) && rc == 0) {
- struct timespec ts = {0};
-
- CDTIME_T_TO_TIMESPEC(rf->rf_next_read, &ts);
-
- rc = pthread_cond_timedwait(&read_cond, &read_lock, &ts);
+ rc = pthread_cond_timedwait(&read_cond, &read_lock,
+ &CDTIME_T_TO_TIMESPEC(rf->rf_next_read));
}
/* Must hold `read_lock' when accessing `rf->rf_type'. */
return ((void *)0);
} /* void *plugin_read_thread */
-static void start_read_threads(int num) {
+#ifdef PTHREAD_MAX_NAMELEN_NP
+#define THREAD_NAME_MAX PTHREAD_MAX_NAMELEN_NP
+#else
+#define THREAD_NAME_MAX 16
+#endif
+
+static void set_thread_name(pthread_t tid, char const *name) {
+#if defined(HAVE_PTHREAD_SETNAME_NP) || defined(HAVE_PTHREAD_SET_NAME_NP)
+
+ /* glibc limits the length of the name and fails if the passed string
+ * is too long, so we truncate it here. */
+ char n[THREAD_NAME_MAX];
+ if (strlen(name) >= THREAD_NAME_MAX)
+ WARNING("set_thread_name(\"%s\"): name too long", name);
+ sstrncpy(n, name, sizeof(n));
+
+#if defined(HAVE_PTHREAD_SETNAME_NP)
+ int status = pthread_setname_np(tid, n);
+ if (status != 0) {
+ char errbuf[1024];
+ ERROR("set_thread_name(\"%s\"): %s", n,
+ sstrerror(status, errbuf, sizeof(errbuf)));
+ }
+#else /* if defined(HAVE_PTHREAD_SET_NAME_NP) */
+ pthread_set_name_np(tid, n);
+#endif
+
+#endif
+}
+
+static void start_read_threads(size_t num) /* {{{ */
+{
if (read_threads != NULL)
return;
}
read_threads_num = 0;
- for (int i = 0; i < num; i++) {
- if (pthread_create(read_threads + read_threads_num, NULL,
- plugin_read_thread, NULL) == 0) {
- read_threads_num++;
- } else {
- ERROR("plugin: start_read_threads: pthread_create failed.");
+ for (size_t i = 0; i < num; i++) {
+ int status = pthread_create(read_threads + read_threads_num,
+ /* attr = */ NULL, plugin_read_thread,
+ /* arg = */ NULL);
+ if (status != 0) {
+ char errbuf[1024];
+ ERROR("plugin: start_read_threads: pthread_create failed "
+ "with status %i (%s).",
+ status, sstrerror(status, errbuf, sizeof(errbuf)));
return;
}
+
+ char name[THREAD_NAME_MAX];
+ ssnprintf(name, sizeof(name), "reader#%zu", read_threads_num);
+ set_thread_name(read_threads[read_threads_num], name);
+
+ read_threads_num++;
} /* for (i) */
-} /* void start_read_threads */
+} /* }}} void start_read_threads */
static void stop_read_threads(void) {
if (read_threads == NULL)
return;
- INFO("collectd: Stopping %i read threads.", read_threads_num);
+ INFO("collectd: Stopping %zu read threads.", read_threads_num);
pthread_mutex_lock(&read_lock);
read_loop = 0;
pthread_cond_broadcast(&read_cond);
pthread_mutex_unlock(&read_lock);
- for (int i = 0; i < read_threads_num; i++) {
+ for (size_t i = 0; i < read_threads_num; i++) {
if (pthread_join(read_threads[i], NULL) != 0) {
ERROR("plugin: stop_read_threads: pthread_join failed.");
}
return (NULL);
memcpy(vl, vl_orig, sizeof(*vl));
+ if (vl->host[0] == 0)
+ sstrncpy(vl->host, hostname_g, sizeof(vl->host));
+
vl->values = calloc(vl_orig->values_len, sizeof(*vl->values));
if (vl->values == NULL) {
plugin_value_list_free(vl);
write_threads_num = 0;
for (size_t i = 0; i < num; i++) {
- int status;
-
- status = pthread_create(write_threads + write_threads_num,
- /* attr = */ NULL, plugin_write_thread,
- /* arg = */ NULL);
+ int status = pthread_create(write_threads + write_threads_num,
+ /* attr = */ NULL, plugin_write_thread,
+ /* arg = */ NULL);
if (status != 0) {
char errbuf[1024];
ERROR("plugin: start_write_threads: pthread_create failed "
return;
}
+ char name[THREAD_NAME_MAX];
+ ssnprintf(name, sizeof(name), "writer#%zu", write_threads_num);
+ set_thread_name(write_threads[write_threads_num], name);
+
write_threads_num++;
} /* for (i) */
} /* }}} void start_write_threads */
/* Init the value cache */
uc_init();
- if (IS_TRUE(global_option_get("CollectInternalStats")))
+ if (IS_TRUE(global_option_get("CollectInternalStats"))) {
record_statistics = 1;
+ plugin_register_read("collectd", plugin_update_internal_statistics);
+ }
chain_name = global_option_get("PreCacheChain");
pre_cache_chain = fc_chain_get_by_name(chain_name);
rt = global_option_get("ReadThreads");
num = atoi(rt);
if (num != -1)
- start_read_threads((num > 0) ? num : 5);
+ start_read_threads((num > 0) ? ((size_t)num) : 5);
}
return ret;
} /* void plugin_init_all */
/* TODO: Rename this function. */
void plugin_read_all(void) {
- if (record_statistics) {
- plugin_update_internal_statistics();
- }
uc_check_timeout();
return;
int status;
static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
- value_t *saved_values;
- int saved_values_len;
-
data_set_t *ds;
- int free_meta_data = 0;
+ _Bool free_meta_data = 0;
+
+ assert(vl != NULL);
- assert(vl);
- assert(vl->plugin);
+ /* These fields are initialized by plugin_value_list_clone() if needed: */
+ assert(vl->host[0] != 0);
+ assert(vl->time != 0); /* The time is determined at _enqueue_ time. */
+ assert(vl->interval != 0);
if (vl->type[0] == 0 || vl->values == NULL || vl->values_len < 1) {
ERROR("plugin_dispatch_values: Invalid value list "
return (-1);
}
- /* Assured by plugin_value_list_clone(). The time is determined at
- * _enqueue_ time. */
- assert(vl->time != 0);
- assert(vl->interval != 0);
-
DEBUG("plugin_dispatch_values: time = %.3f; interval = %.3f; "
"host = %s; "
"plugin = %s; plugin_instance = %s; "
escape_slashes(vl->type, sizeof(vl->type));
escape_slashes(vl->type_instance, sizeof(vl->type_instance));
- /* Copy the values. This way, we can assure `targets' that they get
- * dynamically allocated values, which they can free and replace if
- * they like. */
- if ((pre_cache_chain != NULL) || (post_cache_chain != NULL)) {
- saved_values = vl->values;
- saved_values_len = vl->values_len;
-
- vl->values = (value_t *)calloc(vl->values_len, sizeof(*vl->values));
- if (vl->values == NULL) {
- ERROR("plugin_dispatch_values: calloc failed.");
- vl->values = saved_values;
- return (-1);
- }
- memcpy(vl->values, saved_values, vl->values_len * sizeof(*vl->values));
- } else /* if ((pre == NULL) && (post == NULL)) */
- {
- saved_values = NULL;
- saved_values_len = 0;
- }
-
if (pre_cache_chain != NULL) {
status = fc_process_chain(ds, vl, pre_cache_chain);
if (status < 0) {
"pre-cache chain failed with "
"status %i (%#x).",
status, status);
- } else if (status == FC_TARGET_STOP) {
- /* Restore the state of the value_list so that plugins
- * don't get confused.. */
- if (saved_values != NULL) {
- sfree(vl->values);
- vl->values = saved_values;
- vl->values_len = saved_values_len;
- }
+ } else if (status == FC_TARGET_STOP)
return (0);
- }
}
/* Update the value cache */
} else
fc_default_action(ds, vl);
- /* Restore the state of the value_list so that plugins don't get
- * confused.. */
- if (saved_values != NULL) {
- sfree(vl->values);
- vl->values = saved_values;
- vl->values_len = saved_values_len;
- }
-
if ((free_meta_data != 0) && (vl->meta != NULL)) {
meta_data_destroy(vl->meta);
vl->meta = NULL;
} /* void *plugin_thread_start */
int plugin_thread_create(pthread_t *thread, const pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg) {
+ void *(*start_routine)(void *), void *arg,
+ char const *name) {
plugin_thread_t *plugin_thread;
plugin_thread = malloc(sizeof(*plugin_thread));
if (plugin_thread == NULL)
- return -1;
+ return ENOMEM;
plugin_thread->ctx = plugin_get_ctx();
plugin_thread->start_routine = start_routine;
plugin_thread->arg = arg;
- return pthread_create(thread, attr, plugin_thread_start, plugin_thread);
+ int ret = pthread_create(thread, attr, plugin_thread_start, plugin_thread);
+ if (ret != 0) {
+ sfree(plugin_thread);
+ return ret;
+ }
+
+ if (name != NULL)
+ set_thread_name(*thread, name);
+
+ return 0;
} /* int plugin_thread_create */
/* vim: set sw=8 ts=8 noet fdm=marker : */
/*
* Public data types
*/
+struct identifier_s {
+ char *host;
+ char *plugin;
+ char *plugin_instance;
+ char *type;
+ char *type_instance;
+};
+typedef struct identifier_s identifier_t;
+
typedef unsigned long long counter_t;
typedef double gauge_t;
typedef int64_t derive_t;
typedef struct value_list_s value_list_t;
#define VALUE_LIST_INIT \
- { NULL, 0, 0, plugin_get_interval(), "localhost", "", "", "", "", NULL }
-#define VALUE_LIST_STATIC \
- { NULL, 0, 0, 0, "localhost", "", "", "", "", NULL }
+ { .values = NULL, .meta = NULL }
struct data_source_s {
char name[DATA_MAX_NAME_LEN];
*/
int plugin_thread_create(pthread_t *thread, const pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg);
+ void *(*start_routine)(void *), void *arg,
+ char const *name);
/*
* Plugins need to implement this
int plugin_dispatch_values(value_list_t const *vl) { return ENOTSUP; }
+int plugin_flush(const char *plugin, cdtime_t timeout, const char *identifier) {
+ return ENOTSUP;
+}
+
+static data_source_t magic_ds[] = {{"value", DS_TYPE_DERIVE, 0.0, NAN}};
+static data_set_t magic = {"MAGIC", 1, magic_ds};
+const data_set_t *plugin_get_ds(const char *name) {
+ if (strcmp(name, "MAGIC"))
+ return NULL;
+
+ return &magic;
+}
+
void plugin_log(int level, char const *format, ...) {
char buffer[1024];
va_list ap;
} /* int uc_init */
int uc_check_timeout(void) {
- cdtime_t now;
- cache_entry_t *ce;
-
- char **keys = NULL;
- cdtime_t *keys_time = NULL;
- cdtime_t *keys_interval = NULL;
- int keys_len = 0;
-
- char *key;
- c_avl_iterator_t *iter;
-
- int status;
+ struct {
+ char *key;
+ cdtime_t time;
+ cdtime_t interval;
+ } *expired = NULL;
+ size_t expired_num = 0;
pthread_mutex_lock(&cache_lock);
-
- now = cdtime();
+ cdtime_t now = cdtime();
/* Build a list of entries to be flushed */
- iter = c_avl_get_iterator(cache_tree);
+ c_avl_iterator_t *iter = c_avl_get_iterator(cache_tree);
+ char *key = NULL;
+ cache_entry_t *ce = NULL;
while (c_avl_iterator_next(iter, (void *)&key, (void *)&ce) == 0) {
- char **tmp;
- cdtime_t *tmp_time;
-
/* If the entry is fresh enough, continue. */
if ((now - ce->last_update) < (ce->interval * timeout_g))
continue;
- /* If entry has not been updated, add to `keys' array */
- tmp = realloc((void *)keys, (keys_len + 1) * sizeof(char *));
+ void *tmp = realloc(expired, (expired_num + 1) * sizeof(*expired));
if (tmp == NULL) {
ERROR("uc_check_timeout: realloc failed.");
continue;
}
- keys = tmp;
-
- tmp_time = realloc(keys_time, (keys_len + 1) * sizeof(*keys_time));
- if (tmp_time == NULL) {
- ERROR("uc_check_timeout: realloc failed.");
- continue;
- }
- keys_time = tmp_time;
+ expired = tmp;
- tmp_time = realloc(keys_interval, (keys_len + 1) * sizeof(*keys_interval));
- if (tmp_time == NULL) {
- ERROR("uc_check_timeout: realloc failed.");
- continue;
- }
- keys_interval = tmp_time;
+ expired[expired_num].key = strdup(key);
+ expired[expired_num].time = ce->last_time;
+ expired[expired_num].interval = ce->interval;
- keys[keys_len] = strdup(key);
- if (keys[keys_len] == NULL) {
+ if (expired[expired_num].key == NULL) {
ERROR("uc_check_timeout: strdup failed.");
continue;
}
- keys_time[keys_len] = ce->last_time;
- keys_interval[keys_len] = ce->interval;
- keys_len++;
+ expired_num++;
} /* while (c_avl_iterator_next) */
c_avl_iterator_destroy(iter);
pthread_mutex_unlock(&cache_lock);
- if (keys_len == 0) {
- /* realloc() may have been called for these. */
- sfree(keys);
- sfree(keys_time);
- sfree(keys_interval);
+ if (expired_num == 0) {
+ sfree(expired);
return (0);
}
* including plugin specific meta data, rates, history, …. This must be done
* without holding the lock, otherwise we will run into a deadlock if a
* plugin calls the cache interface. */
- for (int i = 0; i < keys_len; i++) {
- value_list_t vl = VALUE_LIST_INIT;
-
- vl.values = NULL;
- vl.values_len = 0;
- vl.meta = NULL;
-
- status = parse_identifier_vl(keys[i], &vl);
- if (status != 0) {
- ERROR("uc_check_timeout: parse_identifier_vl (\"%s\") failed.", keys[i]);
+ for (size_t i = 0; i < expired_num; i++) {
+ value_list_t vl = {
+ .time = expired[i].time, .interval = expired[i].interval,
+ };
+
+ if (parse_identifier_vl(expired[i].key, &vl) != 0) {
+ ERROR("uc_check_timeout: parse_identifier_vl (\"%s\") failed.",
+ expired[i].key);
continue;
}
- vl.time = keys_time[i];
- vl.interval = keys_interval[i];
-
plugin_dispatch_missing(&vl);
- } /* for (i = 0; i < keys_len; i++) */
+ } /* for (i = 0; i < expired_num; i++) */
/* Now actually remove all the values from the cache. We don't re-evaluate
* the timestamp again, so in theory it is possible we remove a value after
* it is updated here. */
pthread_mutex_lock(&cache_lock);
- for (int i = 0; i < keys_len; i++) {
- key = NULL;
- ce = NULL;
-
- status = c_avl_remove(cache_tree, keys[i], (void *)&key, (void *)&ce);
- if (status != 0) {
- ERROR("uc_check_timeout: c_avl_remove (\"%s\") failed.", keys[i]);
- sfree(keys[i]);
+ for (size_t i = 0; i < expired_num; i++) {
+ char *key = NULL;
+ cache_entry_t *value = NULL;
+
+ if (c_avl_remove(cache_tree, expired[i].key, (void *)&key,
+ (void *)&value) != 0) {
+ ERROR("uc_check_timeout: c_avl_remove (\"%s\") failed.", expired[i].key);
+ sfree(expired[i].key);
continue;
}
-
- sfree(keys[i]);
sfree(key);
- cache_free(ce);
- } /* for (i = 0; i < keys_len; i++) */
- pthread_mutex_unlock(&cache_lock);
+ cache_free(value);
- sfree(keys);
- sfree(keys_time);
- sfree(keys_interval);
+ sfree(expired[i].key);
+ } /* for (i = 0; i < expired_num; i++) */
+ pthread_mutex_unlock(&cache_lock);
+ sfree(expired);
return (0);
} /* int uc_check_timeout */
/* remove missing values from getval */
if (ce->state == STATE_MISSING) {
+ DEBUG("utils_cache: uc_get_rate_by_name: requested metric \"%s\" is in "
+ "state \"missing\".",
+ name);
status = -1;
} else {
ret_num = ce->values_num;
/* This is important - the caller has no other way of knowing how many
* values are returned. */
- if (ret_num != (size_t)ds->ds_num) {
+ if (ret_num != ds->ds_num) {
ERROR("utils_cache: uc_get_rate: ds[%s] has %zu values, "
"but uc_get_rate_by_name returned %zu.",
ds->type, ds->ds_num, ret_num);
*
* PARAMETERS
* `iter' The iterator object to advance.
- * `ret_name' Pointer to a string where to store the name. The returned
- * value is a copy of the value and has to be freed by the
- * caller.
+ * `ret_name' Optional pointer to a string where to store the name. If not
+ * NULL, the returned value is a copy of the value and has to be
+ * freed by the caller.
*
* RETURN VALUE
* Zero upon success or non-zero if the iterator ie NULL or no further
* Florian octo Forster <octo at collectd.org>
*/
+#include <errno.h>
#include "utils_cache.h"
gauge_t *uc_get_rate(__attribute__((unused)) data_set_t const *ds,
__attribute__((unused)) value_list_t const *vl) {
+ errno = ENOTSUP;
return (NULL);
}
+
+int uc_get_rate_by_name(const char *name, gauge_t **ret_values,
+ size_t *ret_values_num) {
+ return (ENOTSUP);
+}
+
+int uc_get_names(char ***ret_names, cdtime_t **ret_times, size_t *ret_number) {
+ return (ENOTSUP);
+}
+++ /dev/null
-/**
- * collectd - src/utils_match.c
- * Copyright (C) 2008-2014 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_match.h"
-
-#include <regex.h>
-
-#define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
-#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
-#define UTILS_MATCH_FLAGS_REGEX 0x04
-
-struct cu_match_s {
- regex_t regex;
- regex_t excluderegex;
- int flags;
-
- int (*callback)(const char *str, char *const *matches, size_t matches_num,
- void *user_data);
- void *user_data;
-};
-
-/*
- * Private functions
- */
-static char *match_substr(const char *str, int begin, int end) {
- char *ret;
- size_t ret_len;
-
- if ((begin < 0) || (end < 0) || (begin >= end))
- return (NULL);
- if ((size_t)end > (strlen(str) + 1)) {
- ERROR("utils_match: match_substr: `end' points after end of string.");
- return (NULL);
- }
-
- ret_len = end - begin;
- ret = malloc(ret_len + 1);
- if (ret == NULL) {
- ERROR("utils_match: match_substr: malloc failed.");
- return (NULL);
- }
-
- sstrncpy(ret, str + begin, ret_len + 1);
- return (ret);
-} /* char *match_substr */
-
-static int default_callback(const char __attribute__((unused)) * str,
- char *const *matches, size_t matches_num,
- void *user_data) {
- cu_match_value_t *data = (cu_match_value_t *)user_data;
-
- if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) {
- gauge_t value;
- char *endptr = NULL;
-
- if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) {
- data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1;
- data->values_num++;
- return (0);
- }
-
- if (matches_num < 2)
- return (-1);
-
- value = (gauge_t)strtod(matches[1], &endptr);
- if (matches[1] == endptr)
- return (-1);
-
- if ((data->values_num == 0) ||
- (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST)) {
- data->value.gauge = value;
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) {
- double f = ((double)data->values_num) / ((double)(data->values_num + 1));
- data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) {
- if (data->value.gauge > value)
- data->value.gauge = value;
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) {
- if (data->value.gauge < value)
- data->value.gauge = value;
- } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) {
- data->value.gauge += value;
- } else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return (-1);
- }
-
- data->values_num++;
- } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) {
- counter_t value;
- char *endptr = NULL;
-
- if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) {
- data->value.counter++;
- data->values_num++;
- return (0);
- }
-
- if (matches_num < 2)
- return (-1);
-
- value = (counter_t)strtoull(matches[1], &endptr, 0);
- if (matches[1] == endptr)
- return (-1);
-
- if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
- data->value.counter = value;
- else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
- data->value.counter += value;
- else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return (-1);
- }
-
- data->values_num++;
- } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) {
- derive_t value;
- char *endptr = NULL;
-
- if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) {
- data->value.derive++;
- data->values_num++;
- return (0);
- }
-
- if (matches_num < 2)
- return (-1);
-
- value = (derive_t)strtoll(matches[1], &endptr, 0);
- if (matches[1] == endptr)
- return (-1);
-
- if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
- data->value.derive = value;
- else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
- data->value.derive += value;
- else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return (-1);
- }
-
- data->values_num++;
- } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) {
- absolute_t value;
- char *endptr = NULL;
-
- if (matches_num < 2)
- return (-1);
-
- value = (absolute_t)strtoull(matches[1], &endptr, 0);
- if (matches[1] == endptr)
- return (-1);
-
- if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
- data->value.absolute = value;
- else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return (-1);
- }
-
- data->values_num++;
- } else {
- ERROR("utils_match: default_callback: obj->ds_type is invalid!");
- return (-1);
- }
-
- return (0);
-} /* int default_callback */
-
-/*
- * Public functions
- */
-cu_match_t *
-match_create_callback(const char *regex, const char *excluderegex,
- int (*callback)(const char *str, char *const *matches,
- size_t matches_num, void *user_data),
- void *user_data) {
- cu_match_t *obj;
- int status;
-
- DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s",
- regex, excluderegex);
-
- obj = calloc(1, sizeof(*obj));
- if (obj == NULL)
- return (NULL);
-
- status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
- if (status != 0) {
- ERROR("Compiling the regular expression \"%s\" failed.", regex);
- sfree(obj);
- return (NULL);
- }
- obj->flags |= UTILS_MATCH_FLAGS_REGEX;
-
- if (excluderegex && strcmp(excluderegex, "") != 0) {
- status = regcomp(&obj->excluderegex, excluderegex, REG_EXTENDED);
- if (status != 0) {
- ERROR("Compiling the excluding regular expression \"%s\" failed.",
- excluderegex);
- sfree(obj);
- return (NULL);
- }
- obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
- }
-
- obj->callback = callback;
- obj->user_data = user_data;
-
- return (obj);
-} /* cu_match_t *match_create_callback */
-
-cu_match_t *match_create_simple(const char *regex, const char *excluderegex,
- int match_ds_type) {
- cu_match_value_t *user_data;
- cu_match_t *obj;
-
- user_data = calloc(1, sizeof(*user_data));
- if (user_data == NULL)
- return (NULL);
- user_data->ds_type = match_ds_type;
-
- obj = match_create_callback(regex, excluderegex, default_callback, user_data);
- if (obj == NULL) {
- sfree(user_data);
- return (NULL);
- }
-
- obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
-
- return (obj);
-} /* cu_match_t *match_create_simple */
-
-void match_value_reset(cu_match_value_t *mv) {
- if (mv == NULL)
- return;
-
- if (mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) {
- mv->value.gauge = NAN;
- mv->values_num = 0;
- }
-} /* }}} void match_value_reset */
-
-void match_destroy(cu_match_t *obj) {
- if (obj == NULL)
- return;
-
- if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
- sfree(obj->user_data);
- if (obj->flags & UTILS_MATCH_FLAGS_REGEX)
- regfree(&obj->regex);
- if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX)
- regfree(&obj->excluderegex);
-
- sfree(obj);
-} /* void match_destroy */
-
-int match_apply(cu_match_t *obj, const char *str) {
- int status;
- regmatch_t re_match[32];
- char *matches[32] = {0};
- size_t matches_num;
-
- if ((obj == NULL) || (str == NULL))
- return (-1);
-
- if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
- status =
- regexec(&obj->excluderegex, str, STATIC_ARRAY_SIZE(re_match), re_match,
- /* eflags = */ 0);
- /* Regex did match, so exclude this line */
- if (status == 0) {
- DEBUG("ExludeRegex matched, don't count that line\n");
- return (0);
- }
- }
-
- status = regexec(&obj->regex, str, STATIC_ARRAY_SIZE(re_match), re_match,
- /* eflags = */ 0);
-
- /* Regex did not match */
- if (status != 0)
- return (0);
-
- for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches);
- matches_num++) {
- if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0))
- break;
-
- matches[matches_num] = match_substr(str, re_match[matches_num].rm_so,
- re_match[matches_num].rm_eo);
- if (matches[matches_num] == NULL) {
- status = -1;
- break;
- }
- }
-
- if (status != 0) {
- ERROR("utils_match: match_apply: match_substr failed.");
- } else {
- status = obj->callback(str, matches, matches_num, obj->user_data);
- if (status != 0) {
- ERROR("utils_match: match_apply: callback failed.");
- }
- }
-
- for (size_t i = 0; i < matches_num; i++) {
- sfree(matches[i]);
- }
-
- return (status);
-} /* int match_apply */
-
-void *match_get_user_data(cu_match_t *obj) {
- if (obj == NULL)
- return (NULL);
- return (obj->user_data);
-} /* void *match_get_user_data */
-
-/* vim: set sw=2 sts=2 ts=8 : */
+++ /dev/null
-/**
- * collectd - src/utils_match.h
- * Copyright (C) 2008-2014 Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_MATCH_H
-#define UTILS_MATCH_H 1
-
-#include "plugin.h"
-
-/*
- * Each type may have 12 sub-types
- * 0x1000 = 1000000000000
- * ^ <- Type bit
- * ^^^^^^^^^^^^ <- Subtype bits
- */
-#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000
-#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000
-#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000
-#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000
-
-#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
-#define UTILS_MATCH_CF_GAUGE_MIN 0x02
-#define UTILS_MATCH_CF_GAUGE_MAX 0x04
-#define UTILS_MATCH_CF_GAUGE_LAST 0x08
-#define UTILS_MATCH_CF_GAUGE_INC 0x10
-#define UTILS_MATCH_CF_GAUGE_ADD 0x20
-
-#define UTILS_MATCH_CF_COUNTER_SET 0x01
-#define UTILS_MATCH_CF_COUNTER_ADD 0x02
-#define UTILS_MATCH_CF_COUNTER_INC 0x04
-
-#define UTILS_MATCH_CF_DERIVE_SET 0x01
-#define UTILS_MATCH_CF_DERIVE_ADD 0x02
-#define UTILS_MATCH_CF_DERIVE_INC 0x04
-
-#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01
-#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02
-#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04
-
-/*
- * Data types
- */
-struct cu_match_s;
-typedef struct cu_match_s cu_match_t;
-
-struct cu_match_value_s {
- int ds_type;
- value_t value;
- unsigned int values_num;
-};
-typedef struct cu_match_value_s cu_match_value_t;
-
-/*
- * Prototypes
- */
-/*
- * NAME
- * match_create_callback
- *
- * DESCRIPTION
- * Creates a new `cu_match_t' object which will use the regular expression
- * `regex' to match lines, see the `match_apply' method below. If the line
- * matches, the callback passed in `callback' will be called along with the
- * pointer `user_pointer'.
- * The string that's passed to the callback depends on the regular expression:
- * If the regular expression includes a sub-match, i. e. something like
- * "value=([0-9][0-9]*)"
- * then only the submatch (the part in the parenthesis) will be passed to the
- * callback. If there is no submatch, then the entire string is passed to the
- * callback.
- * The optional `excluderegex' allows to exclude the line from the match, if
- * the excluderegex matches.
- */
-cu_match_t *
-match_create_callback(const char *regex, const char *excluderegex,
- int (*callback)(const char *str, char *const *matches,
- size_t matches_num, void *user_data),
- void *user_data);
-
-/*
- * NAME
- * match_create_simple
- *
- * DESCRIPTION
- * Creates a new `cu_match_t' with a default callback. The user data for that
- * default callback will be a `cu_match_value_t' structure, with
- * `ds_type' copied to the structure. The default callback will handle the
- * string as containing a number (see strtoll(3) and strtod(3)) and store that
- * number in the `value' member. How that is done depends on `ds_type':
- *
- * UTILS_MATCH_DS_TYPE_GAUGE
- * The function will search for a floating point number in the string and
- * store it in value.gauge.
- * UTILS_MATCH_DS_TYPE_COUNTER_SET
- * The function will search for an integer in the string and store it in
- * value.counter.
- * UTILS_MATCH_DS_TYPE_COUNTER_ADD
- * The function will search for an integer in the string and add it to the
- * value in value.counter.
- * UTILS_MATCH_DS_TYPE_COUNTER_INC
- * The function will not search for anything in the string and increase
- * value.counter by one.
- */
-cu_match_t *match_create_simple(const char *regex, const char *excluderegex,
- int ds_type);
-
-/*
- * NAME
- * match_value_reset
- *
- * DESCRIPTION
- * Resets the internal state, if applicable. This function must be called
- * after each iteration for "simple" matches, usually after dispatching the
- * metrics.
- */
-void match_value_reset(cu_match_value_t *mv);
-
-/*
- * NAME
- * match_destroy
- *
- * DESCRIPTION
- * Destroys the object and frees all internal resources.
- */
-void match_destroy(cu_match_t *obj);
-
-/*
- * NAME
- * match_apply
- *
- * DESCRIPTION
- * Tries to match the string `str' with the regular expression of `obj'. If
- * the string matches, calls the callback in `obj' with the (sub-)match.
- *
- * The user_data pointer passed to `match_create_callback' is NOT freed
- * automatically. The `cu_match_value_t' structure allocated by
- * `match_create_callback' is freed automatically.
- */
-int match_apply(cu_match_t *obj, const char *str);
-
-/*
- * NAME
- * match_get_user_data
- *
- * DESCRIPTION
- * Returns the pointer passed to `match_create_callback' or a pointer to the
- * `cu_match_value_t' structure allocated by `match_create_simple'.
- */
-void *match_get_user_data(cu_match_t *obj);
-
-#endif /* UTILS_MATCH_H */
-
-/* vim: set sw=2 sts=2 ts=8 : */
+++ /dev/null
-/**
- * collectd - src/utils_tail.c
- * Copyright (C) 2007-2008 C-Ware, Inc.
- * Copyright (C) 2008 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author:
- * Luke Heberling <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * Description:
- * Encapsulates useful code for plugins which must watch for appends to
- * the end of a file.
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "utils_tail.h"
-
-struct cu_tail_s {
- char *file;
- FILE *fh;
- struct stat stat;
-};
-
-static int cu_tail_reopen(cu_tail_t *obj) {
- int seek_end = 0;
- FILE *fh;
- struct stat stat_buf = {0};
- int status;
-
- status = stat(obj->file, &stat_buf);
- if (status != 0) {
- char errbuf[1024];
- ERROR("utils_tail: stat (%s) failed: %s", obj->file,
- sstrerror(errno, errbuf, sizeof(errbuf)));
- return (-1);
- }
-
- /* The file is already open.. */
- if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) {
- /* Seek to the beginning if file was truncated */
- if (stat_buf.st_size < obj->stat.st_size) {
- INFO("utils_tail: File `%s' was truncated.", obj->file);
- status = fseek(obj->fh, 0, SEEK_SET);
- if (status != 0) {
- char errbuf[1024];
- ERROR("utils_tail: fseek (%s) failed: %s", obj->file,
- sstrerror(errno, errbuf, sizeof(errbuf)));
- fclose(obj->fh);
- obj->fh = NULL;
- return (-1);
- }
- }
- memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
- return (1);
- }
-
- /* Seek to the end if we re-open the same file again or the file opened
- * is the first at all or the first after an error */
- if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
- seek_end = 1;
-
- fh = fopen(obj->file, "r");
- if (fh == NULL) {
- char errbuf[1024];
- ERROR("utils_tail: fopen (%s) failed: %s", obj->file,
- sstrerror(errno, errbuf, sizeof(errbuf)));
- return (-1);
- }
-
- if (seek_end != 0) {
- status = fseek(fh, 0, SEEK_END);
- if (status != 0) {
- char errbuf[1024];
- ERROR("utils_tail: fseek (%s) failed: %s", obj->file,
- sstrerror(errno, errbuf, sizeof(errbuf)));
- fclose(fh);
- return (-1);
- }
- }
-
- if (obj->fh != NULL)
- fclose(obj->fh);
- obj->fh = fh;
- memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
-
- return (0);
-} /* int cu_tail_reopen */
-
-cu_tail_t *cu_tail_create(const char *file) {
- cu_tail_t *obj;
-
- obj = calloc(1, sizeof(*obj));
- if (obj == NULL)
- return (NULL);
-
- obj->file = strdup(file);
- if (obj->file == NULL) {
- free(obj);
- return (NULL);
- }
-
- obj->fh = NULL;
-
- return (obj);
-} /* cu_tail_t *cu_tail_create */
-
-int cu_tail_destroy(cu_tail_t *obj) {
- if (obj->fh != NULL)
- fclose(obj->fh);
- free(obj->file);
- free(obj);
-
- return (0);
-} /* int cu_tail_destroy */
-
-int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) {
- int status;
-
- if (buflen < 1) {
- ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen);
- return (-1);
- }
-
- if (obj->fh == NULL) {
- status = cu_tail_reopen(obj);
- if (status < 0)
- return (status);
- }
- assert(obj->fh != NULL);
-
- /* Try to read from the filehandle. If that succeeds, everything appears to
- * be fine and we can return. */
- clearerr(obj->fh);
- if (fgets(buf, buflen, obj->fh) != NULL) {
- buf[buflen - 1] = 0;
- return (0);
- }
-
- /* Check if we encountered an error */
- if (ferror(obj->fh) != 0) {
- /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
- fclose(obj->fh);
- obj->fh = NULL;
- }
- /* else: eof -> check if the file was moved away and reopen the new file if
- * so.. */
-
- status = cu_tail_reopen(obj);
- /* error -> return with error */
- if (status < 0)
- return (status);
- /* file end reached and file not reopened -> nothing more to read */
- else if (status > 0) {
- buf[0] = 0;
- return (0);
- }
-
- /* If we get here: file was re-opened and there may be more to read.. Let's
- * try again. */
- if (fgets(buf, buflen, obj->fh) != NULL) {
- buf[buflen - 1] = 0;
- return (0);
- }
-
- if (ferror(obj->fh) != 0) {
- char errbuf[1024];
- WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file,
- sstrerror(errno, errbuf, sizeof(errbuf)));
- fclose(obj->fh);
- obj->fh = NULL;
- return (-1);
- }
-
- /* EOf, well, apparently the new file is empty.. */
- buf[0] = 0;
- return (0);
-} /* int cu_tail_readline */
-
-int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
- void *data) {
- int status;
-
- while (42) {
- size_t len;
-
- status = cu_tail_readline(obj, buf, buflen);
- if (status != 0) {
- ERROR("utils_tail: cu_tail_read: cu_tail_readline "
- "failed.");
- break;
- }
-
- /* check for EOF */
- if (buf[0] == 0)
- break;
-
- len = strlen(buf);
- while (len > 0) {
- if (buf[len - 1] != '\n')
- break;
- buf[len - 1] = '\0';
- len--;
- }
-
- status = callback(data, buf, buflen);
- if (status != 0) {
- ERROR("utils_tail: cu_tail_read: callback returned "
- "status %i.",
- status);
- break;
- }
- }
-
- return status;
-} /* int cu_tail_read */
+++ /dev/null
-/**
- * collectd - src/utils_tail.h
- * Copyright (C) 2007-2008 C-Ware, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author:
- * Luke Heberling <lukeh at c-ware.com>
- *
- * DESCRIPTION
- * Facilitates reading information that is appended to a file, taking into
- * account that the file may be rotated and a new file created under the
- * same name.
- **/
-
-#ifndef UTILS_TAIL_H
-#define UTILS_TAIL_H 1
-
-struct cu_tail_s;
-typedef struct cu_tail_s cu_tail_t;
-
-typedef int tailfunc_t(void *data, char *buf, int buflen);
-
-/*
- * NAME
- * cu_tail_create
- *
- * DESCRIPTION
- * Allocates a new tail object..
- *
- * PARAMETERS
- * `file' The name of the file to be tailed.
- */
-cu_tail_t *cu_tail_create(const char *file);
-
-/*
- * cu_tail_destroy
- *
- * Takes a tail object returned by `cu_tail_create' and destroys it, freeing
- * all internal memory.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_destroy(cu_tail_t *obj);
-
-/*
- * cu_tail_readline
- *
- * Reads from the file until `buflen' characters are read, a newline
- * character is read, or an eof condition is encountered. `buf' is
- * always null-terminated on successful return and isn't touched when non-zero
- * is returned.
- *
- * You can check if the EOF condition is reached by looking at the buffer: If
- * the length of the string stored in the buffer is zero, EOF occurred.
- * Otherwise at least the newline character will be in the buffer.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen);
-
-/*
- * cu_tail_readline
- *
- * Reads from the file until eof condition or an error is encountered.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
- void *data);
-
-#endif /* UTILS_TAIL_H */
+++ /dev/null
-/*
- * collectd - src/utils_tail_match.c
- * Copyright (C) 2007-2008 C-Ware, Inc.
- * Copyright (C) 2008 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author:
- * Luke Heberling <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * Description:
- * Encapsulates useful code to plugins which must parse a log file.
- */
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_match.h"
-#include "utils_tail.h"
-#include "utils_tail_match.h"
-
-struct cu_tail_match_simple_s {
- char plugin[DATA_MAX_NAME_LEN];
- char plugin_instance[DATA_MAX_NAME_LEN];
- char type[DATA_MAX_NAME_LEN];
- char type_instance[DATA_MAX_NAME_LEN];
- cdtime_t interval;
-};
-typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
-
-struct cu_tail_match_match_s {
- cu_match_t *match;
- void *user_data;
- int (*submit)(cu_match_t *match, void *user_data);
- void (*free)(void *user_data);
-};
-typedef struct cu_tail_match_match_s cu_tail_match_match_t;
-
-struct cu_tail_match_s {
- int flags;
- cu_tail_t *tail;
-
- cdtime_t interval;
- cu_tail_match_match_t *matches;
- size_t matches_num;
-};
-
-/*
- * Private functions
- */
-static int simple_submit_match(cu_match_t *match, void *user_data) {
- cu_tail_match_simple_t *data = (cu_tail_match_simple_t *)user_data;
- cu_match_value_t *match_value;
- value_list_t vl = VALUE_LIST_INIT;
- value_t values[1];
-
- match_value = (cu_match_value_t *)match_get_user_data(match);
- if (match_value == NULL)
- return (-1);
-
- if ((match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
- (match_value->values_num == 0))
- values[0].gauge = NAN;
- else
- values[0] = match_value->value;
-
- vl.values = values;
- vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
- sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin));
- sstrncpy(vl.plugin_instance, data->plugin_instance,
- sizeof(vl.plugin_instance));
- sstrncpy(vl.type, data->type, sizeof(vl.type));
- sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance));
-
- vl.interval = data->interval;
- plugin_dispatch_values(&vl);
-
- if (match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) {
- match_value->value.gauge = NAN;
- match_value->values_num = 0;
- }
-
- return (0);
-} /* int simple_submit_match */
-
-static int tail_callback(void *data, char *buf,
- int __attribute__((unused)) buflen) {
- cu_tail_match_t *obj = (cu_tail_match_t *)data;
-
- for (size_t i = 0; i < obj->matches_num; i++)
- match_apply(obj->matches[i].match, buf);
-
- return (0);
-} /* int tail_callback */
-
-/*
- * Public functions
- */
-cu_tail_match_t *tail_match_create(const char *filename) {
- cu_tail_match_t *obj;
-
- obj = calloc(1, sizeof(*obj));
- if (obj == NULL)
- return (NULL);
-
- obj->tail = cu_tail_create(filename);
- if (obj->tail == NULL) {
- sfree(obj);
- return (NULL);
- }
-
- return (obj);
-} /* cu_tail_match_t *tail_match_create */
-
-void tail_match_destroy(cu_tail_match_t *obj) {
- if (obj == NULL)
- return;
-
- if (obj->tail != NULL) {
- cu_tail_destroy(obj->tail);
- obj->tail = NULL;
- }
-
- for (size_t i = 0; i < obj->matches_num; i++) {
- cu_tail_match_match_t *match = obj->matches + i;
- if (match->match != NULL) {
- match_destroy(match->match);
- match->match = NULL;
- }
-
- if ((match->user_data != NULL) && (match->free != NULL))
- (*match->free)(match->user_data);
- match->user_data = NULL;
- }
-
- sfree(obj->matches);
- sfree(obj);
-} /* void tail_match_destroy */
-
-int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
- int (*submit_match)(cu_match_t *match,
- void *user_data),
- void *user_data,
- void (*free_user_data)(void *user_data)) {
- cu_tail_match_match_t *temp;
-
- temp = realloc(obj->matches,
- sizeof(cu_tail_match_match_t) * (obj->matches_num + 1));
- if (temp == NULL)
- return (-1);
-
- obj->matches = temp;
- obj->matches_num++;
-
- DEBUG("tail_match_add_match interval %lf",
- CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
- temp = obj->matches + (obj->matches_num - 1);
-
- temp->match = match;
- temp->user_data = user_data;
- temp->submit = submit_match;
- temp->free = free_user_data;
-
- return (0);
-} /* int tail_match_add_match */
-
-int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
- const char *excluderegex, int ds_type,
- const char *plugin, const char *plugin_instance,
- const char *type, const char *type_instance,
- const cdtime_t interval) {
- cu_match_t *match;
- cu_tail_match_simple_t *user_data;
- int status;
-
- match = match_create_simple(regex, excluderegex, ds_type);
- if (match == NULL)
- return (-1);
-
- user_data = calloc(1, sizeof(*user_data));
- if (user_data == NULL) {
- match_destroy(match);
- return (-1);
- }
-
- sstrncpy(user_data->plugin, plugin, sizeof(user_data->plugin));
- if (plugin_instance != NULL)
- sstrncpy(user_data->plugin_instance, plugin_instance,
- sizeof(user_data->plugin_instance));
-
- sstrncpy(user_data->type, type, sizeof(user_data->type));
- if (type_instance != NULL)
- sstrncpy(user_data->type_instance, type_instance,
- sizeof(user_data->type_instance));
-
- user_data->interval = interval;
-
- status =
- tail_match_add_match(obj, match, simple_submit_match, user_data, free);
-
- if (status != 0) {
- match_destroy(match);
- sfree(user_data);
- }
-
- return (status);
-} /* int tail_match_add_match_simple */
-
-int tail_match_read(cu_tail_match_t *obj) {
- char buffer[4096];
- int status;
-
- status = cu_tail_read(obj->tail, buffer, sizeof(buffer), tail_callback,
- (void *)obj);
- if (status != 0) {
- ERROR("tail_match: cu_tail_read failed.");
- return (status);
- }
-
- for (size_t i = 0; i < obj->matches_num; i++) {
- cu_tail_match_match_t *lt_match = obj->matches + i;
-
- if (lt_match->submit == NULL)
- continue;
-
- (*lt_match->submit)(lt_match->match, lt_match->user_data);
- }
-
- return (0);
-} /* int tail_match_read */
-
-/* vim: set sw=2 sts=2 ts=8 : */
+++ /dev/null
-/*
- * collectd - src/utils_tail_match.h
- * Copyright (C) 2007-2008 C-Ware, Inc.
- * Copyright (C) 2008 Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Luke Heberling <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * Description:
- * `tail_match' uses `utils_tail' and `utils_match' to tail a file and try to
- * match it using several regular expressions. Matches are then passed to
- * user-provided callback functions or default handlers. This should keep all
- * of the parsing logic out of the actual plugin, which only operate with
- * regular expressions.
- */
-
-#include "utils_match.h"
-
-struct cu_tail_match_s;
-typedef struct cu_tail_match_s cu_tail_match_t;
-
-/*
- * NAME
- * tail_match_create
- *
- * DESCRIPTION
- * Allocates, initializes and returns a new `cu_tail_match_t' object.
- *
- * PARAMETERS
- * `filename' The name to read data from.
- *
- * RETURN VALUE
- * Returns NULL upon failure, non-NULL otherwise.
- */
-cu_tail_match_t *tail_match_create(const char *filename);
-
-/*
- * NAME
- * tail_match_destroy
- *
- * DESCRIPTION
- * Releases resources used by the `cu_tail_match_t' object.
- *
- * PARAMETERS
- * The object to destroy.
- */
-void tail_match_destroy(cu_tail_match_t *obj);
-
-/*
- * NAME
- * tail_match_add_match
- *
- * DESCRIPTION
- * Adds a match, in form of a `cu_match_t' object, to the object.
- * After data has been read from the logfile (using utils_tail) the callback
- * function `submit_match' is called with the match object and the user
- * supplied data.
- * Please note that his function is called regardless whether this match
- * matched any lines recently or not.
- * When `tail_match_destroy' is called the `user_data' pointer is freed using
- * the `free_user_data' callback - if it is not NULL.
- * When using this interface the `tail_match' module doesn't dispatch any
- * values
- * itself - all that has to happen in either the match-callbacks or the
- * submit_match callback.
- *
- * RETURN VALUE
- * Zero upon success, non-zero otherwise.
- */
-int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
- int (*submit_match)(cu_match_t *match,
- void *user_data),
- void *user_data,
- void (*free_user_data)(void *user_data));
-
-/*
- * NAME
- * tail_match_add_match_simple
- *
- * DESCRIPTION
- * A simplified version of `tail_match_add_match'. The regular expressen
- * `regex'
- * must match a number, which is then dispatched according to `ds_type'. See
- * the `match_create_simple' function in utils_match.h for a description how
- * this flag effects calculation of a new value.
- * The values gathered are dispatched by the tail_match module in this case.
- * The
- * passed `plugin', `plugin_instance', `type', and `type_instance' are
- * directly used when submitting these values.
- * With excluderegex it is possible to exlude lines from the match.
- *
- * RETURN VALUE
- * Zero upon success, non-zero otherwise.
- */
-int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
- const char *excluderegex, int ds_type,
- const char *plugin, const char *plugin_instance,
- const char *type, const char *type_instance,
- const cdtime_t interval);
-
-/*
- * NAME
- * tail_match_read
- *
- * DESCRIPTION
- * This function should be called periodically by plugins. It reads new lines
- * from the logfile using `utils_tail' and tries to match them using all
- * added `utils_match' objects.
- * After all lines have been read and processed, the submit_match callback is
- * called or, in case of tail_match_add_match_simple, the data is dispatched
- * to
- * the daemon directly.
- *
- * RETURN VALUE
- * Zero on success, nonzero on failure.
-*/
-int tail_match_read(cu_tail_match_t *obj);
-
-/* vim: set sw=2 sts=2 ts=8 : */
char errbuf[1024];
ERROR("cdtime: clock_gettime failed: %s",
sstrerror(errno, errbuf, sizeof(errbuf)));
- return (0);
+ return 0;
}
- return (TIMESPEC_TO_CDTIME_T(&ts));
+ return TIMESPEC_TO_CDTIME_T(&ts);
} /* }}} cdtime_t cdtime */
#else /* !HAVE_CLOCK_GETTIME */
/* Work around for Mac OS X which doesn't have clock_gettime(2). *sigh* */
char errbuf[1024];
ERROR("cdtime: gettimeofday failed: %s",
sstrerror(errno, errbuf, sizeof(errbuf)));
- return (0);
+ return 0;
}
- return (TIMEVAL_TO_CDTIME_T(&tv));
+ return TIMEVAL_TO_CDTIME_T(&tv);
} /* }}} cdtime_t cdtime */
#endif
#endif
+/**********************************************************************
+ Time retrieval functions
+***********************************************************************/
+
+static int get_utc_time(cdtime_t t, struct tm *t_tm, long *nsec) /* {{{ */
+{
+ struct timespec t_spec = CDTIME_T_TO_TIMESPEC(t);
+ NORMALIZE_TIMESPEC(t_spec);
+
+ if (gmtime_r(&t_spec.tv_sec, t_tm) == NULL) {
+ char errbuf[1024];
+ int status = errno;
+ ERROR("get_utc_time: gmtime_r failed: %s",
+ sstrerror(status, errbuf, sizeof(errbuf)));
+ return status;
+ }
+
+ *nsec = t_spec.tv_nsec;
+ return 0;
+} /* }}} int get_utc_time */
+
+static int get_local_time(cdtime_t t, struct tm *t_tm, long *nsec) /* {{{ */
+{
+ struct timespec t_spec = CDTIME_T_TO_TIMESPEC(t);
+ NORMALIZE_TIMESPEC(t_spec);
+
+ if (localtime_r(&t_spec.tv_sec, t_tm) == NULL) {
+ char errbuf[1024];
+ int status = errno;
+ ERROR("get_local_time: localtime_r failed: %s",
+ sstrerror(status, errbuf, sizeof(errbuf)));
+ return status;
+ }
+
+ *nsec = t_spec.tv_nsec;
+ return 0;
+} /* }}} int get_local_time */
+
+/**********************************************************************
+ Formatting functions
+***********************************************************************/
+
+static const char zulu_zone[] = "Z";
+
/* format_zone reads time zone information from "extern long timezone", exported
* by <time.h>, and formats it according to RFC 3339. This differs from
* strftime()'s "%z" format by including a colon between hour and minute. */
return 0;
} /* }}} int format_zone */
-static int format_rfc3339(char *buffer, size_t buffer_size, cdtime_t t,
- _Bool print_nano) /* {{{ */
+int format_rfc3339(char *buffer, size_t buffer_size, struct tm const *t_tm,
+ long nsec, _Bool print_nano, char const *zone) /* {{{ */
+{
+ int len;
+ char *pos = buffer;
+ size_t size_left = buffer_size;
+
+ if ((len = strftime(pos, size_left, "%Y-%m-%dT%H:%M:%S", t_tm)) == 0)
+ return ENOMEM;
+ pos += len;
+ size_left -= len;
+
+ if (print_nano) {
+ if ((len = ssnprintf(pos, size_left, ".%09ld", nsec)) == 0)
+ return ENOMEM;
+ pos += len;
+ size_left -= len;
+ }
+
+ sstrncpy(pos, zone, size_left);
+ return 0;
+} /* }}} int format_rfc3339 */
+
+int format_rfc3339_utc(char *buffer, size_t buffer_size, cdtime_t t,
+ _Bool print_nano) /* {{{ */
{
- struct timespec t_spec;
struct tm t_tm;
- char base[20]; /* 2006-01-02T15:04:05 */
- char nano[11]; /* .999999999 */
- char zone[7]; /* +00:00 */
- char *fields[] = {base, nano, zone};
- size_t len;
+ long nsec = 0;
int status;
- CDTIME_T_TO_TIMESPEC(t, &t_spec);
- NORMALIZE_TIMESPEC(t_spec);
+ if ((status = get_utc_time(t, &t_tm, &nsec)) != 0)
+ return status; /* The error should have already be reported. */
- if (localtime_r(&t_spec.tv_sec, &t_tm) == NULL) {
- char errbuf[1024];
- status = errno;
- ERROR("format_rfc3339: localtime_r failed: %s",
- sstrerror(status, errbuf, sizeof(errbuf)));
- return (status);
- }
+ return format_rfc3339(buffer, buffer_size, &t_tm, nsec, print_nano,
+ zulu_zone);
+} /* }}} int format_rfc3339_utc */
- len = strftime(base, sizeof(base), "%Y-%m-%dT%H:%M:%S", &t_tm);
- if (len == 0)
- return ENOMEM;
+int format_rfc3339_local(char *buffer, size_t buffer_size, cdtime_t t,
+ _Bool print_nano) /* {{{ */
+{
+ struct tm t_tm;
+ long nsec = 0;
+ int status;
+ char zone[7]; /* +00:00 */
- if (print_nano)
- ssnprintf(nano, sizeof(nano), ".%09ld", (long)t_spec.tv_nsec);
- else
- sstrncpy(nano, "", sizeof(nano));
+ if ((status = get_local_time(t, &t_tm, &nsec)) != 0)
+ return status; /* The error should have already be reported. */
- status = format_zone(zone, sizeof(zone), &t_tm);
- if (status != 0)
+ if ((status = format_zone(zone, sizeof(zone), &t_tm)) != 0)
return status;
- if (strjoin(buffer, buffer_size, fields, STATIC_ARRAY_SIZE(fields), "") < 0)
- return ENOMEM;
- return 0;
-} /* }}} int format_rfc3339 */
+ return format_rfc3339(buffer, buffer_size, &t_tm, nsec, print_nano, zone);
+} /* }}} int format_rfc3339_local */
+
+/**********************************************************************
+ Public functions
+***********************************************************************/
int rfc3339(char *buffer, size_t buffer_size, cdtime_t t) /* {{{ */
{
if (buffer_size < RFC3339_SIZE)
return ENOMEM;
- return format_rfc3339(buffer, buffer_size, t, 0);
-} /* }}} size_t cdtime_to_rfc3339 */
+ return format_rfc3339_utc(buffer, buffer_size, t, 0);
+} /* }}} int rfc3339 */
int rfc3339nano(char *buffer, size_t buffer_size, cdtime_t t) /* {{{ */
{
if (buffer_size < RFC3339NANO_SIZE)
return ENOMEM;
- return format_rfc3339(buffer, buffer_size, t, 1);
-} /* }}} size_t cdtime_to_rfc3339nano */
+ return format_rfc3339_utc(buffer, buffer_size, t, 1);
+} /* }}} int rfc3339nano */
+
+int rfc3339_local(char *buffer, size_t buffer_size, cdtime_t t) /* {{{ */
+{
+ if (buffer_size < RFC3339_SIZE)
+ return ENOMEM;
+
+ return format_rfc3339_local(buffer, buffer_size, t, 0);
+} /* }}} int rfc3339 */
+
+int rfc3339nano_local(char *buffer, size_t buffer_size, cdtime_t t) /* {{{ */
+{
+ if (buffer_size < RFC3339NANO_SIZE)
+ return ENOMEM;
+
+ return format_rfc3339_local(buffer, buffer_size, t, 1);
+} /* }}} int rfc3339nano */
/* vim: set sw=2 sts=2 et fdm=marker : */
/* typedef uint64_t cdtime_t; */
/* 2^30 = 1073741824 */
-#define TIME_T_TO_CDTIME_T(t) (((cdtime_t)(t)) << 30)
+#define TIME_T_TO_CDTIME_T_STATIC(t) (((cdtime_t)(t)) << 30)
+#define TIME_T_TO_CDTIME_T(t) \
+ (cdtime_t) { TIME_T_TO_CDTIME_T_STATIC(t) }
#define MS_TO_CDTIME_T(ms) \
- (((((cdtime_t)(ms)) / 1000) << 30) | \
- ((((((cdtime_t)(ms)) % 1000) << 30) + 500) / 1000))
+ (cdtime_t) { \
+ ((((cdtime_t)(ms)) / 1000) << 30) | \
+ ((((((cdtime_t)(ms)) % 1000) << 30) + 500) / 1000) \
+ }
#define US_TO_CDTIME_T(us) \
- (((((cdtime_t)(us)) / 1000000) << 30) | \
- ((((((cdtime_t)(us)) % 1000000) << 30) + 500000) / 1000000))
+ (cdtime_t) { \
+ ((((cdtime_t)(us)) / 1000000) << 30) | \
+ ((((((cdtime_t)(us)) % 1000000) << 30) + 500000) / 1000000) \
+ }
#define NS_TO_CDTIME_T(ns) \
- (((((cdtime_t)(ns)) / 1000000000) << 30) | \
- ((((((cdtime_t)(ns)) % 1000000000) << 30) + 500000000) / 1000000000))
+ (cdtime_t) { \
+ ((((cdtime_t)(ns)) / 1000000000) << 30) | \
+ ((((((cdtime_t)(ns)) % 1000000000) << 30) + 500000000) / 1000000000) \
+ }
-#define CDTIME_T_TO_TIME_T(t) ((time_t)(((t) + (1 << 29)) >> 30))
+#define CDTIME_T_TO_TIME_T(t) \
+ (time_t) { (time_t)(((t) + (1 << 29)) >> 30) }
#define CDTIME_T_TO_MS(t) \
- ((uint64_t)((((t) >> 30) * 1000) + \
- ((((t)&0x3fffffff) * 1000 + (1 << 29)) >> 30)))
+ (uint64_t) { \
+ (uint64_t)((((t) >> 30) * 1000) + \
+ ((((t)&0x3fffffff) * 1000 + (1 << 29)) >> 30)) \
+ }
#define CDTIME_T_TO_US(t) \
- ((uint64_t)((((t) >> 30) * 1000000) + \
- ((((t)&0x3fffffff) * 1000000 + (1 << 29)) >> 30)))
+ (uint64_t) { \
+ (uint64_t)((((t) >> 30) * 1000000) + \
+ ((((t)&0x3fffffff) * 1000000 + (1 << 29)) >> 30)) \
+ }
#define CDTIME_T_TO_NS(t) \
- ((uint64_t)((((t) >> 30) * 1000000000) + \
- ((((t)&0x3fffffff) * 1000000000 + (1 << 29)) >> 30)))
-
-#define CDTIME_T_TO_DOUBLE(t) (((double)(t)) / 1073741824.0)
-#define DOUBLE_TO_CDTIME_T(d) ((cdtime_t)((d)*1073741824.0))
-
-#define CDTIME_T_TO_TIMEVAL(cdt, tvp) \
- do { \
- (tvp)->tv_sec = (time_t)((cdt) >> 30); \
- (tvp)->tv_usec = \
- (suseconds_t)((((cdt)&0x3fffffff) * 1000000 + (1 << 29)) >> 30); \
- } while (0)
+ (uint64_t) { \
+ (uint64_t)((((t) >> 30) * 1000000000) + \
+ ((((t)&0x3fffffff) * 1000000000 + (1 << 29)) >> 30)) \
+ }
+
+#define CDTIME_T_TO_DOUBLE(t) \
+ (double) { ((double)(t)) / 1073741824.0 }
+#define DOUBLE_TO_CDTIME_T_STATIC(d) ((cdtime_t)((d)*1073741824.0))
+#define DOUBLE_TO_CDTIME_T(d) \
+ (cdtime_t) { DOUBLE_TO_CDTIME_T_STATIC(d) }
+
+#define CDTIME_T_TO_TIMEVAL(t) \
+ (struct timeval) { \
+ .tv_sec = (time_t)((t) >> 30), \
+ .tv_usec = (suseconds_t)((((t)&0x3fffffff) * 1000000 + (1 << 29)) >> 30), \
+ }
#define TIMEVAL_TO_CDTIME_T(tv) \
US_TO_CDTIME_T(1000000 * (tv)->tv_sec + (tv)->tv_usec)
-#define CDTIME_T_TO_TIMESPEC(cdt, tsp) \
- do { \
- (tsp)->tv_sec = (time_t)((cdt) >> 30); \
- (tsp)->tv_nsec = \
- (long)((((cdt)&0x3fffffff) * 1000000000 + (1 << 29)) >> 30); \
- } while (0)
+#define CDTIME_T_TO_TIMESPEC(t) \
+ (struct timespec) { \
+ .tv_sec = (time_t)((t) >> 30), \
+ .tv_nsec = (long)((((t)&0x3fffffff) * 1000000000 + (1 << 29)) >> 30), \
+ }
#define TIMESPEC_TO_CDTIME_T(ts) \
NS_TO_CDTIME_T(1000000000ULL * (ts)->tv_sec + (ts)->tv_nsec)
cdtime_t cdtime(void);
-#define RFC3339_SIZE 26
-#define RFC3339NANO_SIZE 36
+#define RFC3339_SIZE 26 /* 2006-01-02T15:04:05+00:00 */
+#define RFC3339NANO_SIZE 36 /* 2006-01-02T15:04:05.999999999+00:00 */
-/* rfc3339 formats a cdtime_t time in RFC 3339 format with second precision. */
+/* rfc3339 formats a cdtime_t time as UTC in RFC 3339 zulu format with second
+ * precision, e.g., "2006-01-02T15:04:05Z". */
int rfc3339(char *buffer, size_t buffer_size, cdtime_t t);
-/* rfc3339nano formats a cdtime_t time in RFC 3339 format with nanosecond
- * precision. */
+/* rfc3339nano formats a cdtime_t as UTC time in RFC 3339 zulu format with
+ * nanosecond precision, e.g., "2006-01-02T15:04:05.999999999Z". */
int rfc3339nano(char *buffer, size_t buffer_size, cdtime_t t);
+/* rfc3339 formats a cdtime_t time as local in RFC 3339 format with second
+ * precision, e.g., "2006-01-02T15:04:05+00:00". */
+int rfc3339_local(char *buffer, size_t buffer_size, cdtime_t t);
+
+/* rfc3339nano formats a cdtime_t time as local in RFC 3339 format with
+ * nanosecond precision, e.g., "2006-01-02T15:04:05.999999999+00:00". */
+int rfc3339nano_local(char *buffer, size_t buffer_size, cdtime_t t);
+
#endif /* UTILS_TIME_H */
/* vim: set sw=2 sts=2 et : */
};
for (size_t i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
- struct timeval tv;
- struct timespec ts;
-
// cdtime -> s
EXPECT_EQ_UINT64(cases[i].tt, CDTIME_T_TO_TIME_T(cases[i].t));
EXPECT_EQ_UINT64(cases[i].ms, CDTIME_T_TO_MS(cases[i].t));
// cdtime -> us
- CDTIME_T_TO_TIMEVAL(cases[i].t, &tv);
+ struct timeval tv = CDTIME_T_TO_TIMEVAL(cases[i].t);
EXPECT_EQ_UINT64(cases[i].tv.tv_sec, tv.tv_sec);
EXPECT_EQ_UINT64(cases[i].tv.tv_usec, tv.tv_usec);
// cdtime -> ns
- CDTIME_T_TO_TIMESPEC(cases[i].t, &ts);
+ struct timespec ts = CDTIME_T_TO_TIMESPEC(cases[i].t);
EXPECT_EQ_UINT64(cases[i].ts.tv_sec, ts.tv_sec);
EXPECT_EQ_UINT64(cases[i].ts.tv_nsec, ts.tv_nsec);
ERROR("dbi plugin: realloc failed");
status = -1;
} else {
- char *name = NULL;
-
databases = temp;
databases[databases_num] = db;
databases_num++;
- name = ssnprintf_alloc("dbi:%s", db->name);
-
- user_data_t ud = {.data = db};
-
+ char *name = ssnprintf_alloc("dbi:%s", db->name);
plugin_register_complex_read(
/* group = */ NULL,
/* name = */ name ? name : db->name,
/* callback = */ cdbi_read_database,
/* interval = */ (db->interval > 0) ? db->interval : 0,
- /* user_data = */ &ud);
- free(name);
+ &(user_data_t){
+ .data = db,
+ });
+ sfree(name);
}
}
if (queries_num == 0) {
ERROR("dbi plugin: No <Query> blocks have been found. Without them, "
- "this plugin can't do anything useful, so we will returns an error.");
+ "this plugin can't do anything useful, so we will return an error.");
return (-1);
}
if (databases_num == 0) {
ERROR("dbi plugin: No <Database> blocks have been found. Without them, "
- "this plugin can't do anything useful, so we will returns an error.");
+ "this plugin can't do anything useful, so we will return an error.");
return (-1);
}
const char *type,
const char *type_instance,
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "df", sizeof(vl.plugin));
if (plugin_instance != NULL)
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
}
/* Duplicate found: leave non-NULL dup_ptr. */
- if (by_device &&
+ if (by_device && (mnt_ptr->spec_device != NULL) &&
+ (dup_ptr->spec_device != NULL) &&
(strcmp(mnt_ptr->spec_device, dup_ptr->spec_device) == 0))
break;
else if (!by_device && (strcmp(mnt_ptr->dir, dup_ptr->dir) == 0))
/* #endif HAVE_LIBKSTAT */
#elif defined(HAVE_LIBSTATGRAB)
-/* #endif HAVE_LIBKSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
#elif HAVE_PERFSTAT
static perfstat_disk_t *stat_disk;
static void disk_submit(const char *plugin_instance, const char *type,
derive_t read, derive_t write) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = read;
- values[1].derive = write;
+ value_t values[] = {
+ {.derive = read}, {.derive = write},
+ };
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "disk", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
#if KERNEL_FREEBSD || KERNEL_LINUX
static void submit_io_time(char const *plugin_instance, derive_t io_time,
derive_t weighted_time) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = io_time;
- values[1].derive = weighted_time;
+ value_t values[] = {
+ {.derive = io_time}, {.derive = weighted_time},
+ };
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "disk", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "disk_io_time", sizeof(vl.type));
#if KERNEL_LINUX
static void submit_in_progress(char const *disk_name, gauge_t in_progress) {
- value_t v;
value_list_t vl = VALUE_LIST_INIT;
- v.gauge = in_progress;
-
- vl.values = &v;
+ vl.values = &(value_t){.gauge = in_progress};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "disk", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, disk_name, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "pending_operations", sizeof(vl.type));
static int dns_sleep_one_interval(void) /* {{{ */
{
- cdtime_t interval;
- struct timespec ts = {0, 0};
- int status = 0;
-
- interval = plugin_get_interval();
- CDTIME_T_TO_TIMESPEC(interval, &ts);
-
- while (42) {
- struct timespec rem = {0, 0};
-
- status = nanosleep(&ts, &rem);
- if (status == 0)
- break;
- else if ((errno == EINTR) || (errno == EAGAIN)) {
- ts = rem;
+ struct timespec ts = CDTIME_T_TO_TIMESPEC(plugin_get_interval());
+ while (nanosleep(&ts, &ts) != 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
continue;
- } else
- break;
+
+ return (errno);
}
- return (status);
+ return (0);
} /* }}} int dns_sleep_one_interval */
static void *dns_child_loop(__attribute__((unused)) void *dummy) /* {{{ */
if (listen_thread_init != 0)
return (-1);
- status =
- plugin_thread_create(&listen_thread, NULL, dns_child_loop, (void *)0);
+ status = plugin_thread_create(&listen_thread, NULL, dns_child_loop, (void *)0,
+ "dns listen");
if (status != 0) {
char errbuf[1024];
ERROR("dns plugin: pthread_create failed: %s",
static void submit_derive(const char *type, const char *type_instance,
derive_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = value;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "dns", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
} /* void submit_derive */
static void submit_octets(derive_t queries, derive_t responses) {
- value_t values[2];
+ value_t values[] = {
+ {.derive = queries}, {.derive = responses},
+ };
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = queries;
- values[1].derive = responses;
-
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "dns", sizeof(vl.plugin));
sstrncpy(vl.type, "dns_octets", sizeof(vl.type));
--- /dev/null
+/*-
+ * collectd - src/dpdkstat.c
+ * MIT License
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Maryam Tahhan <maryam.tahhan@intel.com>
+ * Harry van Haaren <harry.van.haaren@intel.com>
+ * Taras Chornyi <tarasx.chornyi@intel.com>
+ */
+
+#include "collectd.h"
+
+#include "common.h" /* auxiliary functions */
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "utils_time.h"
+
+#include <getopt.h>
+#include <poll.h>
+#include <semaphore.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+
+#include <rte_atomic.h>
+#include <rte_branch_prediction.h>
+#include <rte_common.h>
+#include <rte_config.h>
+#include <rte_debug.h>
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_per_lcore.h>
+#include <rte_string_fns.h>
+#include <rte_tailq.h>
+#include <rte_version.h>
+
+#define DPDK_DEFAULT_RTE_CONFIG "/var/run/.rte_config"
+#define DPDK_MAX_ARGC 8
+#define DPDKSTAT_MAX_BUFFER_SIZE (4096 * 4)
+#define DPDK_SHM_NAME "dpdk_collectd_stats_shm"
+#define ERR_BUF_SIZE 1024
+#define REINIT_SHM 1
+#define RESET 1
+#define NO_RESET 0
+
+#define RTE_VERSION_16_07 RTE_VERSION_NUM(16, 7, 0, 16)
+
+#if RTE_VERSION < RTE_VERSION_16_07
+#define DPDK_STATS_XSTAT_GET_VALUE(ctx, index) ctx->xstats[index].value
+#define DPDK_STATS_XSTAT_GET_NAME(ctx, index) ctx->xstats[index].name
+#define DPDK_STATS_CTX_GET_XSTAT_SIZE sizeof(struct rte_eth_xstats)
+#define DPDK_STATS_CTX_INIT(ctx) \
+ do { \
+ ctx->xstats = (struct rte_eth_xstats *)&ctx->raw_data[0]; \
+ } while (0)
+#else
+#define DPDK_STATS_XSTAT_GET_VALUE(ctx, index) ctx->xstats[index].value
+#define DPDK_STATS_XSTAT_GET_NAME(ctx, index) ctx->xnames[index].name
+#define DPDK_STATS_CTX_GET_XSTAT_SIZE \
+ (sizeof(struct rte_eth_xstat) + sizeof(struct rte_eth_xstat_name))
+#define DPDK_STATS_CTX_INIT(ctx) \
+ do { \
+ ctx->xstats = (struct rte_eth_xstat *)&ctx->raw_data[0]; \
+ ctx->xnames = \
+ (struct rte_eth_xstat_name *)&ctx \
+ ->raw_data[ctx->num_xstats * sizeof(struct rte_eth_xstat)]; \
+ } while (0)
+#endif
+
+enum DPDK_HELPER_ACTION {
+ DPDK_HELPER_ACTION_COUNT_STATS,
+ DPDK_HELPER_ACTION_SEND_STATS,
+};
+
+enum DPDK_HELPER_STATUS {
+ DPDK_HELPER_NOT_INITIALIZED = 0,
+ DPDK_HELPER_WAITING_ON_PRIMARY,
+ DPDK_HELPER_INITIALIZING_EAL,
+ DPDK_HELPER_ALIVE_SENDING_STATS,
+ DPDK_HELPER_GRACEFUL_QUIT,
+};
+
+struct dpdk_config_s {
+ /* General DPDK params */
+ char coremask[DATA_MAX_NAME_LEN];
+ char memory_channels[DATA_MAX_NAME_LEN];
+ char socket_memory[DATA_MAX_NAME_LEN];
+ char process_type[DATA_MAX_NAME_LEN];
+ char file_prefix[DATA_MAX_NAME_LEN];
+ cdtime_t interval;
+ uint32_t eal_initialized;
+ uint32_t enabled_port_mask;
+ char port_name[RTE_MAX_ETHPORTS][DATA_MAX_NAME_LEN];
+ uint32_t eal_argc;
+ /* Helper info */
+ int collectd_reinit_shm;
+ pid_t helper_pid;
+ sem_t sema_helper_get_stats;
+ sem_t sema_stats_in_shm;
+ int helper_pipes[2];
+ enum DPDK_HELPER_STATUS helper_status;
+ enum DPDK_HELPER_ACTION helper_action;
+ /* xstats info */
+ uint32_t num_ports;
+ uint32_t num_xstats;
+ cdtime_t port_read_time[RTE_MAX_ETHPORTS];
+ uint32_t num_stats_in_port[RTE_MAX_ETHPORTS];
+ struct rte_eth_link link_status[RTE_MAX_ETHPORTS];
+#if RTE_VERSION < RTE_VERSION_16_07
+ struct rte_eth_xstats *xstats;
+#else
+ struct rte_eth_xstat *xstats;
+ struct rte_eth_xstat_name *xnames;
+#endif
+ char *raw_data;
+ /* rte_eth_xstats from here on until the end of the SHM */
+};
+typedef struct dpdk_config_s dpdk_config_t;
+
+static int g_configured;
+static dpdk_config_t *g_configuration;
+
+static void dpdk_config_init_default(void);
+static int dpdk_config(oconfig_item_t *ci);
+static int dpdk_helper_init_eal(void);
+static int dpdk_helper_run(void);
+static int dpdk_helper_spawn(enum DPDK_HELPER_ACTION action);
+static int dpdk_init(void);
+static int dpdk_read(user_data_t *ud);
+static int dpdk_shm_cleanup(void);
+static int dpdk_shm_init(size_t size);
+
+/* Write the default configuration to the g_configuration instances */
+static void dpdk_config_init_default(void) {
+ g_configuration->interval = plugin_get_interval();
+ if (g_configuration->interval == cf_get_default_interval())
+ WARNING("dpdkstat: No time interval was configured, default value %" PRIu64
+ " ms is set",
+ CDTIME_T_TO_MS(g_configuration->interval));
+ /* Default is all ports enabled */
+ g_configuration->enabled_port_mask = ~0;
+ g_configuration->eal_argc = DPDK_MAX_ARGC;
+ g_configuration->eal_initialized = 0;
+ ssnprintf(g_configuration->coremask, DATA_MAX_NAME_LEN, "%s", "0xf");
+ ssnprintf(g_configuration->memory_channels, DATA_MAX_NAME_LEN, "%s", "1");
+ ssnprintf(g_configuration->process_type, DATA_MAX_NAME_LEN, "%s",
+ "secondary");
+ ssnprintf(g_configuration->file_prefix, DATA_MAX_NAME_LEN, "%s",
+ DPDK_DEFAULT_RTE_CONFIG);
+
+ for (int i = 0; i < RTE_MAX_ETHPORTS; i++)
+ g_configuration->port_name[i][0] = 0;
+}
+
+static int dpdk_config(oconfig_item_t *ci) {
+ int port_counter = 0;
+ /* Allocate g_configuration and
+ * initialize a POSIX SHared Memory (SHM) object.
+ */
+ int err = dpdk_shm_init(sizeof(dpdk_config_t));
+ if (err) {
+ char errbuf[ERR_BUF_SIZE];
+ ERROR("dpdkstat: error in shm_init, %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ /* Set defaults for config, overwritten by loop if config item exists */
+ dpdk_config_init_default();
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Coremask", child->key) == 0) {
+ cf_util_get_string_buffer(child, g_configuration->coremask,
+ sizeof(g_configuration->coremask));
+ DEBUG("dpdkstat:COREMASK %s ", g_configuration->coremask);
+ } else if (strcasecmp("MemoryChannels", child->key) == 0) {
+ cf_util_get_string_buffer(child, g_configuration->memory_channels,
+ sizeof(g_configuration->memory_channels));
+ DEBUG("dpdkstat:Memory Channels %s ", g_configuration->memory_channels);
+ } else if (strcasecmp("SocketMemory", child->key) == 0) {
+ cf_util_get_string_buffer(child, g_configuration->socket_memory,
+ sizeof(g_configuration->memory_channels));
+ DEBUG("dpdkstat: socket mem %s ", g_configuration->socket_memory);
+ } else if (strcasecmp("ProcessType", child->key) == 0) {
+ cf_util_get_string_buffer(child, g_configuration->process_type,
+ sizeof(g_configuration->process_type));
+ DEBUG("dpdkstat: proc type %s ", g_configuration->process_type);
+ } else if ((strcasecmp("FilePrefix", child->key) == 0) &&
+ (child->values[0].type == OCONFIG_TYPE_STRING)) {
+ ssnprintf(g_configuration->file_prefix, DATA_MAX_NAME_LEN,
+ "/var/run/.%s_config", child->values[0].value.string);
+ DEBUG("dpdkstat: file prefix %s ", g_configuration->file_prefix);
+ } else if ((strcasecmp("EnabledPortMask", child->key) == 0) &&
+ (child->values[0].type == OCONFIG_TYPE_NUMBER)) {
+ g_configuration->enabled_port_mask =
+ (uint32_t)child->values[0].value.number;
+ DEBUG("dpdkstat: Enabled Port Mask %u",
+ g_configuration->enabled_port_mask);
+ } else if (strcasecmp("PortName", child->key) == 0) {
+ cf_util_get_string_buffer(
+ child, g_configuration->port_name[port_counter],
+ sizeof(g_configuration->port_name[port_counter]));
+ DEBUG("dpdkstat: Port %d Name: %s ", port_counter,
+ g_configuration->port_name[port_counter]);
+ port_counter++;
+ } else {
+ WARNING("dpdkstat: The config option \"%s\" is unknown.", child->key);
+ }
+ } /* End for (int i = 0; i < ci->children_num; i++)*/
+ g_configured = 1; /* Bypass configuration in dpdk_shm_init(). */
+
+ return 0;
+}
+
+/*
+ * Allocate g_configuration and initialize SHared Memory (SHM)
+ * for config and helper process
+ */
+static int dpdk_shm_init(size_t size) {
+ /*
+ * Check if SHM is already configured: when config items are provided, the
+ * config function initializes SHM. If there is no config, then init() will
+ * just return.
+ */
+ if (g_configuration)
+ return 0;
+
+ char errbuf[ERR_BUF_SIZE];
+
+ /* Create and open a new object, or open an existing object. */
+ int fd = shm_open(DPDK_SHM_NAME, O_CREAT | O_TRUNC | O_RDWR, 0666);
+ if (fd < 0) {
+ WARNING("dpdkstat:Failed to open %s as SHM:%s", DPDK_SHM_NAME,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ goto fail;
+ }
+ /* Set the size of the shared memory object. */
+ int ret = ftruncate(fd, size);
+ if (ret != 0) {
+ WARNING("dpdkstat:Failed to resize SHM:%s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ goto fail_close;
+ }
+ /* Map the shared memory object into this process' virtual address space. */
+ g_configuration = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (g_configuration == MAP_FAILED) {
+ WARNING("dpdkstat:Failed to mmap SHM:%s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ goto fail_close;
+ }
+ /*
+ * Close the file descriptor, the shared memory object still exists
+ * and can only be removed by calling shm_unlink().
+ */
+ close(fd);
+
+ /* Initialize g_configuration. */
+ memset(g_configuration, 0, size);
+
+ /* Initialize the semaphores for SHM use */
+ int err = sem_init(&g_configuration->sema_helper_get_stats, 1, 0);
+ if (err) {
+ ERROR("dpdkstat semaphore init failed: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ goto fail;
+ }
+ err = sem_init(&g_configuration->sema_stats_in_shm, 1, 0);
+ if (err) {
+ ERROR("dpdkstat semaphore init failed: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ goto fail;
+ }
+
+ g_configuration->xstats = NULL;
+
+ return 0;
+
+fail_close:
+ close(fd);
+fail:
+ /* Reset to zero, as it was set to MAP_FAILED aka: (void *)-1. Avoid
+ * an issue if collectd attempts to run this plugin failure.
+ */
+ g_configuration = 0;
+ return -1;
+}
+
+static int dpdk_re_init_shm() {
+ dpdk_config_t temp_config;
+ memcpy(&temp_config, g_configuration, sizeof(dpdk_config_t));
+ DEBUG("dpdkstat: %s: ports %" PRIu32 ", xstats %" PRIu32, __func__,
+ temp_config.num_ports, temp_config.num_xstats);
+
+ size_t shm_xstats_size =
+ sizeof(dpdk_config_t) +
+ (DPDK_STATS_CTX_GET_XSTAT_SIZE * g_configuration->num_xstats);
+ DEBUG("=== SHM new size for %" PRIu32 " xstats", g_configuration->num_xstats);
+
+ int err = dpdk_shm_cleanup();
+ if (err) {
+ ERROR("dpdkstat: Error in shm_cleanup in %s", __func__);
+ return err;
+ }
+ err = dpdk_shm_init(shm_xstats_size);
+ if (err) {
+ WARNING("dpdkstat: Error in shm_init in %s", __func__);
+ return err;
+ }
+ /* If the XML config() function has been run, don't re-initialize defaults */
+ if (!g_configured)
+ dpdk_config_init_default();
+
+ memcpy(g_configuration, &temp_config, sizeof(dpdk_config_t));
+ g_configuration->collectd_reinit_shm = 0;
+ g_configuration->raw_data = (char *)(g_configuration + 1);
+ DPDK_STATS_CTX_INIT(g_configuration);
+ return 0;
+}
+
+static int dpdk_init(void) {
+ int err = dpdk_shm_init(sizeof(dpdk_config_t));
+ if (err) {
+ ERROR("dpdkstat: %s : error %d in shm_init()", __func__, err);
+ return err;
+ }
+
+ /* If the XML config() function has been run, dont re-initialize defaults */
+ if (!g_configured) {
+ dpdk_config_init_default();
+ }
+
+ return 0;
+}
+
+static int dpdk_helper_stop(int reset) {
+ g_configuration->helper_status = DPDK_HELPER_GRACEFUL_QUIT;
+ if (reset) {
+ g_configuration->eal_initialized = 0;
+ g_configuration->num_ports = 0;
+ g_configuration->xstats = NULL;
+ g_configuration->num_xstats = 0;
+ for (int i = 0; i < RTE_MAX_ETHPORTS; i++)
+ g_configuration->num_stats_in_port[i] = 0;
+ }
+ close(g_configuration->helper_pipes[1]);
+ int err = kill(g_configuration->helper_pid, SIGKILL);
+ if (err) {
+ char errbuf[ERR_BUF_SIZE];
+ WARNING("dpdkstat: error sending kill to helper: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ }
+
+ return 0;
+}
+
+static int dpdk_helper_spawn(enum DPDK_HELPER_ACTION action) {
+ char errbuf[ERR_BUF_SIZE];
+ g_configuration->eal_initialized = 0;
+ g_configuration->helper_action = action;
+ /*
+ * Create a pipe for helper stdout back to collectd. This is necessary for
+ * logging EAL failures, as rte_eal_init() calls rte_panic().
+ */
+ if (pipe(g_configuration->helper_pipes) != 0) {
+ DEBUG("dpdkstat: Could not create helper pipe: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ int pipe0_flags = fcntl(g_configuration->helper_pipes[0], F_GETFL, 0);
+ int pipe1_flags = fcntl(g_configuration->helper_pipes[1], F_GETFL, 0);
+ if (pipe0_flags == -1 || pipe1_flags == -1) {
+ WARNING("dpdkstat: Failed setting up pipe flags: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ }
+ int pipe0_err = fcntl(g_configuration->helper_pipes[0], F_SETFL,
+ pipe1_flags | O_NONBLOCK);
+ int pipe1_err = fcntl(g_configuration->helper_pipes[1], F_SETFL,
+ pipe0_flags | O_NONBLOCK);
+ if (pipe0_err == -1 || pipe1_err == -1) {
+ WARNING("dpdkstat: Failed setting up pipes: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ }
+
+ pid_t pid = fork();
+ if (pid > 0) {
+ close(g_configuration->helper_pipes[1]);
+ g_configuration->helper_pid = pid;
+ DEBUG("dpdkstat: helper pid %li", (long)g_configuration->helper_pid);
+ /* Kick helper once its alive to have it start processing */
+ sem_post(&g_configuration->sema_helper_get_stats);
+ } else if (pid == 0) {
+ /* Replace stdout with a pipe to collectd. */
+ close(g_configuration->helper_pipes[0]);
+ close(STDOUT_FILENO);
+ dup2(g_configuration->helper_pipes[1], STDOUT_FILENO);
+ dpdk_helper_run();
+ exit(0);
+ } else {
+ ERROR("dpdkstat: Failed to fork helper process: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Initialize the DPDK EAL, if this returns, EAL is successfully initialized.
+ * On failure, the EAL prints an error message, and the helper process exits.
+ */
+static int dpdk_helper_init_eal(void) {
+ g_configuration->helper_status = DPDK_HELPER_INITIALIZING_EAL;
+ char *argp[(g_configuration->eal_argc) + 1];
+ int i = 0;
+
+ argp[i++] = "collectd-dpdk";
+ if (strcasecmp(g_configuration->coremask, "") != 0) {
+ argp[i++] = "-c";
+ argp[i++] = g_configuration->coremask;
+ }
+ if (strcasecmp(g_configuration->memory_channels, "") != 0) {
+ argp[i++] = "-n";
+ argp[i++] = g_configuration->memory_channels;
+ }
+ if (strcasecmp(g_configuration->socket_memory, "") != 0) {
+ argp[i++] = "--socket-mem";
+ argp[i++] = g_configuration->socket_memory;
+ }
+ if (strcasecmp(g_configuration->file_prefix, "") != 0 &&
+ strcasecmp(g_configuration->file_prefix, DPDK_DEFAULT_RTE_CONFIG) != 0) {
+ argp[i++] = "--file-prefix";
+ argp[i++] = g_configuration->file_prefix;
+ }
+ if (strcasecmp(g_configuration->process_type, "") != 0) {
+ argp[i++] = "--proc-type";
+ argp[i++] = g_configuration->process_type;
+ }
+ g_configuration->eal_argc = i;
+
+ g_configuration->eal_initialized = 1;
+ int ret = rte_eal_init(g_configuration->eal_argc, argp);
+ if (ret < 0) {
+ g_configuration->eal_initialized = 0;
+ return ret;
+ }
+ return 0;
+}
+
+static int dpdk_helper_run(void) {
+ char errbuf[ERR_BUF_SIZE];
+ pid_t ppid = getppid();
+ g_configuration->helper_status = DPDK_HELPER_WAITING_ON_PRIMARY;
+
+ while (1) {
+ /* sem_timedwait() to avoid blocking forever */
+ cdtime_t now = cdtime();
+ cdtime_t safety_period = MS_TO_CDTIME_T(1500);
+ int ret =
+ sem_timedwait(&g_configuration->sema_helper_get_stats,
+ &CDTIME_T_TO_TIMESPEC(now + safety_period +
+ g_configuration->interval * 2));
+
+ if (ret == -1 && errno == ETIMEDOUT) {
+ ERROR("dpdkstat-helper: sem timedwait()"
+ " timeout, did collectd terminate?");
+ dpdk_helper_stop(RESET);
+ }
+ /* Parent PID change means collectd died so quit the helper process. */
+ if (ppid != getppid()) {
+ WARNING("dpdkstat-helper: parent PID changed, quitting.");
+ dpdk_helper_stop(RESET);
+ }
+
+ /* Checking for DPDK primary process. */
+ if (!rte_eal_primary_proc_alive(g_configuration->file_prefix)) {
+ if (g_configuration->eal_initialized) {
+ WARNING("dpdkstat-helper: no primary alive but EAL initialized:"
+ " quitting.");
+ dpdk_helper_stop(RESET);
+ }
+ g_configuration->helper_status = DPDK_HELPER_WAITING_ON_PRIMARY;
+ /* Back to start of while() - waiting for primary process */
+ continue;
+ }
+
+ if (!g_configuration->eal_initialized) {
+ /* Initialize EAL. */
+ int ret = dpdk_helper_init_eal();
+ if (ret != 0) {
+ WARNING("ERROR INITIALIZING EAL");
+ dpdk_helper_stop(RESET);
+ }
+ }
+
+ g_configuration->helper_status = DPDK_HELPER_ALIVE_SENDING_STATS;
+
+ uint8_t nb_ports = rte_eth_dev_count();
+ if (nb_ports == 0) {
+ DEBUG("dpdkstat-helper: No DPDK ports available. "
+ "Check bound devices to DPDK driver.");
+ dpdk_helper_stop(RESET);
+ }
+
+ if (nb_ports > RTE_MAX_ETHPORTS)
+ nb_ports = RTE_MAX_ETHPORTS;
+
+ int len = 0, enabled_port_count = 0, num_xstats = 0;
+ for (uint8_t i = 0; i < nb_ports; i++) {
+ if (!(g_configuration->enabled_port_mask & (1 << i)))
+ continue;
+
+ if (g_configuration->helper_action == DPDK_HELPER_ACTION_COUNT_STATS) {
+#if RTE_VERSION >= RTE_VERSION_16_07
+ len = rte_eth_xstats_get_names(i, NULL, 0);
+#else
+ len = rte_eth_xstats_get(i, NULL, 0);
+#endif
+ if (len < 0) {
+ ERROR("dpdkstat-helper: Cannot get xstats count on port %" PRIu8, i);
+ break;
+ }
+ num_xstats += len;
+ g_configuration->num_stats_in_port[enabled_port_count] = len;
+ enabled_port_count++;
+ continue;
+ } else {
+ len = g_configuration->num_stats_in_port[enabled_port_count];
+ g_configuration->port_read_time[enabled_port_count] = cdtime();
+ ret = rte_eth_xstats_get(
+ i, g_configuration->xstats + num_xstats,
+ g_configuration->num_stats_in_port[enabled_port_count]);
+ if (ret < 0 || ret != len) {
+ DEBUG("dpdkstat-helper: Error reading xstats on port %" PRIu8
+ " len = %d",
+ i, len);
+ break;
+ }
+#if RTE_VERSION >= RTE_VERSION_16_07
+ ret = rte_eth_xstats_get_names(i, g_configuration->xnames + num_xstats,
+ len);
+ if (ret < 0 || ret != len) {
+ ERROR("dpdkstat-helper: Error reading xstat names (port=%d; len=%d)",
+ i, len);
+ break;
+ }
+#endif
+ num_xstats += g_configuration->num_stats_in_port[enabled_port_count];
+ enabled_port_count++;
+ }
+ } /* for (nb_ports) */
+
+ if (g_configuration->helper_action == DPDK_HELPER_ACTION_COUNT_STATS) {
+ g_configuration->num_ports = enabled_port_count;
+ g_configuration->num_xstats = num_xstats;
+ DEBUG("dpdkstat-helper ports: %" PRIu32 ", num stats: %" PRIu32,
+ g_configuration->num_ports, g_configuration->num_xstats);
+ /* Exit, allowing collectd to re-init SHM to the right size */
+ g_configuration->collectd_reinit_shm = REINIT_SHM;
+ dpdk_helper_stop(NO_RESET);
+ }
+ /* Now kick collectd send thread to send the stats */
+ int err = sem_post(&g_configuration->sema_stats_in_shm);
+ if (err) {
+ WARNING("dpdkstat: error posting semaphore to helper %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ dpdk_helper_stop(RESET);
+ }
+ } /* while(1) */
+
+ return 0;
+}
+
+static void dpdk_submit_xstats(const char *dev_name, int count,
+ uint32_t counters, cdtime_t port_read_time) {
+ for (uint32_t j = 0; j < counters; j++) {
+ value_list_t vl = VALUE_LIST_INIT;
+ char *counter_name;
+ char *type_end;
+
+ vl.values = &(value_t){.derive = (derive_t)DPDK_STATS_XSTAT_GET_VALUE(
+ g_configuration, count + j)};
+ vl.values_len = 1; /* Submit stats one at a time */
+ vl.time = port_read_time;
+ sstrncpy(vl.plugin, "dpdkstat", sizeof(vl.plugin));
+ sstrncpy(vl.plugin_instance, dev_name, sizeof(vl.plugin_instance));
+ counter_name = DPDK_STATS_XSTAT_GET_NAME(g_configuration, count + j);
+ if (counter_name == NULL) {
+ WARNING("dpdkstat: Failed to get counter name.");
+ return;
+ }
+
+ type_end = strrchr(counter_name, '_');
+
+ if ((type_end != NULL) &&
+ (strncmp(counter_name, "rx_", strlen("rx_")) == 0)) {
+ if (strstr(type_end, "bytes") != NULL) {
+ sstrncpy(vl.type, "if_rx_octets", sizeof(vl.type));
+ } else if (strstr(type_end, "error") != NULL) {
+ sstrncpy(vl.type, "if_rx_errors", sizeof(vl.type));
+ } else if (strstr(type_end, "dropped") != NULL) {
+ sstrncpy(vl.type, "if_rx_dropped", sizeof(vl.type));
+ } else if (strstr(type_end, "packets") != NULL) {
+ sstrncpy(vl.type, "if_rx_packets", sizeof(vl.type));
+ } else if (strstr(type_end, "_placement") != NULL) {
+ sstrncpy(vl.type, "if_rx_errors", sizeof(vl.type));
+ } else if (strstr(type_end, "_buff") != NULL) {
+ sstrncpy(vl.type, "if_rx_errors", sizeof(vl.type));
+ } else {
+ /* Does not fit obvious type: use a more generic one */
+ sstrncpy(vl.type, "derive", sizeof(vl.type));
+ }
+
+ } else if ((type_end != NULL) &&
+ (strncmp(counter_name, "tx_", strlen("tx_"))) == 0) {
+ if (strstr(type_end, "bytes") != NULL) {
+ sstrncpy(vl.type, "if_tx_octets", sizeof(vl.type));
+ } else if (strstr(type_end, "error") != NULL) {
+ sstrncpy(vl.type, "if_tx_errors", sizeof(vl.type));
+ } else if (strstr(type_end, "dropped") != NULL) {
+ sstrncpy(vl.type, "if_tx_dropped", sizeof(vl.type));
+ } else if (strstr(type_end, "packets") != NULL) {
+ sstrncpy(vl.type, "if_tx_packets", sizeof(vl.type));
+ } else {
+ /* Does not fit obvious type: use a more generic one */
+ sstrncpy(vl.type, "derive", sizeof(vl.type));
+ }
+ } else if ((type_end != NULL) &&
+ (strncmp(counter_name, "flow_", strlen("flow_"))) == 0) {
+
+ if (strstr(type_end, "_filters") != NULL) {
+ sstrncpy(vl.type, "operations", sizeof(vl.type));
+ } else if (strstr(type_end, "error") != NULL)
+ sstrncpy(vl.type, "errors", sizeof(vl.type));
+
+ } else if ((type_end != NULL) &&
+ (strncmp(counter_name, "mac_", strlen("mac_"))) == 0) {
+ if (strstr(type_end, "error") != NULL) {
+ sstrncpy(vl.type, "errors", sizeof(vl.type));
+ }
+ } else {
+ /* Does not fit obvious type, or strrchr error:
+ * use a more generic type */
+ sstrncpy(vl.type, "derive", sizeof(vl.type));
+ }
+
+ sstrncpy(vl.type_instance, counter_name, sizeof(vl.type_instance));
+ plugin_dispatch_values(&vl);
+ }
+}
+
+static int dpdk_read(user_data_t *ud) {
+ int ret = 0;
+
+ /*
+ * Check if SHM flag is set to be re-initialized. AKA DPDK ports have been
+ * counted, so re-init SHM to be large enough to fit all the statistics.
+ */
+ if (g_configuration->collectd_reinit_shm) {
+ DEBUG("dpdkstat: read() now reinit SHM then launching send-thread");
+ dpdk_re_init_shm();
+ }
+
+ /*
+ * Check if DPDK proc is alive, and has already counted port / stats. This
+ * must be done in dpdk_read(), because the DPDK primary process may not be
+ * alive at dpdk_init() time.
+ */
+ if (g_configuration->helper_status == DPDK_HELPER_NOT_INITIALIZED ||
+ g_configuration->helper_status == DPDK_HELPER_GRACEFUL_QUIT) {
+ int action = DPDK_HELPER_ACTION_SEND_STATS;
+ if (g_configuration->num_xstats == 0)
+ action = DPDK_HELPER_ACTION_COUNT_STATS;
+ /* Spawn the helper thread to count stats or to read stats. */
+ int err = dpdk_helper_spawn(action);
+ if (err) {
+ char errbuf[ERR_BUF_SIZE];
+ ERROR("dpdkstat: error spawning helper %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+ }
+
+ pid_t ws = waitpid(g_configuration->helper_pid, NULL, WNOHANG);
+ /*
+ * Conditions under which to respawn helper:
+ * waitpid() fails, helper process died (or quit), so respawn
+ */
+ _Bool respawn_helper = 0;
+ if (ws != 0) {
+ respawn_helper = 1;
+ }
+
+ char buf[DPDKSTAT_MAX_BUFFER_SIZE];
+ char out[DPDKSTAT_MAX_BUFFER_SIZE];
+
+ /* non blocking check on helper logging pipe */
+ struct pollfd fds = {
+ .fd = g_configuration->helper_pipes[0], .events = POLLIN,
+ };
+ int data_avail = poll(&fds, 1, 0);
+ if (data_avail < 0) {
+ char errbuf[ERR_BUF_SIZE];
+ if (errno != EINTR || errno != EAGAIN)
+ ERROR("dpdkstats: poll(2) failed: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ }
+ while (data_avail) {
+ int nbytes = read(g_configuration->helper_pipes[0], buf, sizeof(buf));
+ if (nbytes <= 0)
+ break;
+ ssnprintf(out, nbytes, "%s", buf);
+ DEBUG("dpdkstat: helper-proc: %s", out);
+ }
+
+ if (respawn_helper) {
+ if (g_configuration->helper_pid)
+ dpdk_helper_stop(RESET);
+ dpdk_helper_spawn(DPDK_HELPER_ACTION_COUNT_STATS);
+ }
+
+ /* Kick helper process through SHM */
+ sem_post(&g_configuration->sema_helper_get_stats);
+
+ cdtime_t now = cdtime();
+ ret = sem_timedwait(&g_configuration->sema_stats_in_shm,
+ &CDTIME_T_TO_TIMESPEC(now + g_configuration->interval));
+ if (ret == -1) {
+ if (errno == ETIMEDOUT)
+ DEBUG(
+ "dpdkstat: timeout in collectd thread: is a DPDK Primary running? ");
+ return 0;
+ }
+
+ /* Dispatch the stats.*/
+ uint32_t count = 0, port_num = 0;
+
+ for (uint32_t i = 0; i < g_configuration->num_ports; i++) {
+ char dev_name[64];
+ cdtime_t port_read_time = g_configuration->port_read_time[i];
+ uint32_t counters_num = g_configuration->num_stats_in_port[i];
+ size_t ports_max = CHAR_BIT * sizeof(g_configuration->enabled_port_mask);
+ for (size_t j = port_num; j < ports_max; j++) {
+ if ((g_configuration->enabled_port_mask & (1 << j)) != 0)
+ break;
+ port_num++;
+ }
+
+ if (g_configuration->port_name[i][0] != 0)
+ ssnprintf(dev_name, sizeof(dev_name), "%s",
+ g_configuration->port_name[i]);
+ else
+ ssnprintf(dev_name, sizeof(dev_name), "port.%" PRIu32, port_num);
+ dpdk_submit_xstats(dev_name, count, counters_num, port_read_time);
+ count += counters_num;
+ port_num++;
+ } /* for each port */
+ return 0;
+}
+
+static int dpdk_shm_cleanup(void) {
+ int ret = munmap(g_configuration, sizeof(dpdk_config_t));
+ g_configuration = 0;
+ if (ret) {
+ ERROR("dpdkstat: munmap returned %d", ret);
+ return ret;
+ }
+ ret = shm_unlink(DPDK_SHM_NAME);
+ if (ret) {
+ ERROR("dpdkstat: shm_unlink returned %d", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int dpdk_shutdown(void) {
+ int ret = 0;
+ char errbuf[ERR_BUF_SIZE];
+ close(g_configuration->helper_pipes[1]);
+ int err = kill(g_configuration->helper_pid, SIGKILL);
+ if (err) {
+ ERROR("dpdkstat: error sending sigkill to helper %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ ret = -1;
+ }
+ err = dpdk_shm_cleanup();
+ if (err) {
+ ERROR("dpdkstat: error cleaning up SHM: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+void module_register(void) {
+ plugin_register_complex_config("dpdkstat", dpdk_config);
+ plugin_register_init("dpdkstat", dpdk_init);
+ plugin_register_complex_read(NULL, "dpdkstat", dpdk_read, 0, NULL);
+ plugin_register_shutdown("dpdkstat", dpdk_shutdown);
+}
}
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "drbd", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "drbd_resource", sizeof(vl.type));
} /* static void *collect (void *) */
static void *open_connection(void __attribute__((unused)) * arg) {
- struct sockaddr_un addr;
-
const char *path = (NULL == sock_file) ? SOCK_PATH : sock_file;
const char *group = (NULL == sock_group) ? COLLECTD_GRP_NAME : sock_group;
pthread_exit((void *)1);
}
- addr.sun_family = AF_UNIX;
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX
+ };
sstrncpy(addr.sun_path, path, (size_t)(UNIX_PATH_MAX - 1));
errno = 0;
collectors[i]->socket = NULL;
if (plugin_thread_create(&collectors[i]->thread, &ptattr, collect,
- collectors[i]) != 0) {
+ collectors[i], "email collector") != 0) {
char errbuf[1024];
log_err("plugin_thread_create() failed: %s",
sstrerror(errno, errbuf, sizeof(errbuf)));
} /* static void *open_connection (void *) */
static int email_init(void) {
- if (plugin_thread_create(&connector, NULL, open_connection, NULL) != 0) {
+ if (plugin_thread_create(&connector, NULL, open_connection, NULL,
+ "email listener") != 0) {
char errbuf[1024];
disabled = 1;
log_err("plugin_thread_create() failed: %s",
static void email_submit(const char *type, const char *type_instance,
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "email", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
#define ENTROPY_FILE "/proc/sys/kernel/random/entropy_avail"
-static void entropy_submit(double entropy) {
- value_t values[1];
+static void entropy_submit(value_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = entropy;
-
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "entropy", sizeof(vl.plugin));
sstrncpy(vl.type, "entropy", sizeof(vl.type));
}
static int entropy_read(void) {
- double entropy;
- FILE *fh;
- char buffer[64];
-
- fh = fopen(ENTROPY_FILE, "r");
- if (fh == NULL)
- return (-1);
-
- if (fgets(buffer, sizeof(buffer), fh) == NULL) {
- fclose(fh);
+ value_t v;
+ if (parse_value_file(ENTROPY_FILE, &v, DS_TYPE_GAUGE) != 0) {
+ ERROR("entropy plugin: Reading \"" ENTROPY_FILE "\" failed.");
return (-1);
}
- fclose(fh);
-
- entropy = atof(buffer);
-
- if (entropy > 0.0)
- entropy_submit(entropy);
+ entropy_submit(v);
return (0);
}
derive_t value) {
static c_complain_t complain_no_map = C_COMPLAIN_INIT_STATIC;
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
value_map_t *map = NULL;
return;
}
- values[0].derive = value;
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ethstat", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, device, sizeof(vl.plugin_instance));
if (map != NULL) {
CDTIME_T_TO_DOUBLE(plugin_get_interval()));
setenv("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
- ssnprintf(buffer, sizeof(buffer), "%s", hostname_g);
+ sstrncpy(buffer, hostname_g, sizeof(buffer));
setenv("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
#else
ssnprintf(buffer, sizeof(buffer), "COLLECTD_INTERVAL=%.3f",
static int parse_line(char *buffer) /* {{{ */
{
if (strncasecmp("PUTVAL", buffer, strlen("PUTVAL")) == 0)
- return (handle_putval(stdout, buffer));
+ return (cmd_handle_putval(stdout, buffer));
else if (strncasecmp("PUTNOTIF", buffer, strlen("PUTNOTIF")) == 0)
return (handle_putnotif(stdout, buffer));
else {
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- plugin_thread_create(&t, &attr, exec_read_one, (void *)pl);
+ plugin_thread_create(&t, &attr, exec_read_one, (void *)pl, "exec read");
pthread_attr_destroy(&attr);
} /* for (pl) */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- plugin_thread_create(&t, &attr, exec_notification_one, (void *)pln);
+ plugin_thread_create(&t, &attr, exec_notification_one, (void *)pln,
+ "exec notify");
pthread_attr_destroy(&attr);
} /* for (pl) */
static void fhcount_submit(const char *type, const char *type_instance,
gauge_t value) {
-
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
// Compose the metric
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "fhcount", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
static size_t directories_num = 0;
static void fc_submit_dir(const fc_directory_conf_t *dir) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = (gauge_t)dir->files_num;
-
- vl.values = values;
- vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ 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));
plugin_dispatch_values(&vl);
- values[0].gauge = (gauge_t)dir->files_size;
+ vl.values = &(value_t){.gauge = (gauge_t)dir->files_size};
sstrncpy(vl.type, "bytes", sizeof(vl.type));
plugin_dispatch_values(&vl);
vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "fscache", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, section, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "fscache_stat", sizeof(vl.type));
mc_receive_thread_loop = 1;
- status = plugin_thread_create(&mc_receive_thread_id, /* attr = */ NULL,
- mc_receive_thread, /* args = */ NULL);
+ status =
+ plugin_thread_create(&mc_receive_thread_id, /* attr = */ NULL,
+ mc_receive_thread, /* args = */ NULL, "gmond recv");
if (status != 0) {
ERROR("gmond plugin: Starting receive thread failed.");
mc_receive_thread_loop = 0;
static pthread_t cgps_thread_id;
static pthread_mutex_t cgps_data_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t cgps_thread_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cgps_thread_cond = PTHREAD_COND_INITIALIZER;
static int cgps_thread_shutdown = CGPS_FALSE;
static int cgps_thread_running = CGPS_FALSE;
* Non blocking pause for the thread.
*/
static int cgps_thread_pause(cdtime_t pTime) {
- cdtime_t now;
- now = cdtime();
- struct timespec pause_th;
- CDTIME_T_TO_TIMESPEC(MS_TO_CDTIME_T(10), &pause_th);
- while (CGPS_TRUE) {
- if ((cdtime() - now) > pTime) {
- break;
- }
+ cdtime_t until = cdtime() + pTime;
- pthread_mutex_lock(&cgps_thread_lock);
- if (cgps_thread_shutdown == CGPS_TRUE) {
- return CGPS_FALSE;
- }
- pthread_mutex_unlock(&cgps_thread_lock);
- nanosleep(&pause_th, NULL);
- }
+ pthread_mutex_lock(&cgps_thread_lock);
+ pthread_cond_timedwait(&cgps_thread_cond, &cgps_thread_lock,
+ &CDTIME_T_TO_TIMESPEC(until));
+
+ int ret = !cgps_thread_shutdown;
- return CGPS_TRUE;
+ pthread_mutex_lock(&cgps_thread_lock);
+ return ret;
}
/**
*/
static void cgps_submit(const char *type, gauge_t value,
const char *type_instance) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "gps", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
CDTIME_T_TO_DOUBLE(cgps_config_data.timeout),
CDTIME_T_TO_DOUBLE(cgps_config_data.pause_connect));
- status = plugin_thread_create(&cgps_thread_id, NULL, cgps_thread, NULL);
+ status =
+ plugin_thread_create(&cgps_thread_id, NULL, cgps_thread, NULL, "gps");
if (status != 0) {
ERROR("gps plugin: pthread_create() failed.");
return (-1);
pthread_mutex_lock(&cgps_thread_lock);
cgps_thread_shutdown = CGPS_TRUE;
+ pthread_cond_broadcast(&cgps_thread_cond);
pthread_mutex_unlock(&cgps_thread_lock);
pthread_join(cgps_thread_id, &res);
/**
* collectd - src/grpc.cc
* Copyright (C) 2015-2016 Sebastian Harl
+ * Copyright (C) 2016 Florian octo Forster
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*
* Authors:
* Sebastian Harl <sh at tokkee.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include <grpc++/grpc++.h>
using collectd::Collectd;
-using collectd::DispatchValuesRequest;
-using collectd::DispatchValuesResponse;
+using collectd::PutValuesRequest;
+using collectd::PutValuesResponse;
using collectd::QueryValuesRequest;
using collectd::QueryValuesResponse;
return status;
}
- grpc::Status DispatchValues(grpc::ServerContext *ctx,
- grpc::ServerReader<DispatchValuesRequest> *reader,
- DispatchValuesResponse *res) override {
- DispatchValuesRequest req;
+ grpc::Status PutValues(grpc::ServerContext *ctx,
+ grpc::ServerReader<PutValuesRequest> *reader,
+ PutValuesResponse *res) override {
+ PutValuesRequest req;
while (reader->Read(&req)) {
- value_list_t vl = VALUE_LIST_INIT;
+ value_list_t vl = {0};
auto status = unmarshal_value_list(req.value_list(), &vl);
if (!status.ok())
return status;
CollectdClient(std::shared_ptr<grpc::ChannelInterface> channel) : stub_(Collectd::NewStub(channel)) {
}
- int DispatchValues(value_list_t const *vl) {
+ int PutValues(value_list_t const *vl) {
grpc::ClientContext ctx;
- DispatchValuesRequest req;
+ PutValuesRequest req;
auto status = marshal_value_list(vl, req.mutable_value_list());
if (!status.ok()) {
ERROR("grpc: Marshalling value_list_t failed.");
return -1;
}
- DispatchValuesResponse res;
- auto stream = stub_->DispatchValues(&ctx, &res);
+ PutValuesResponse res;
+ auto stream = stub_->PutValues(&ctx, &res);
if (!stream->Write(req)) {
NOTICE("grpc: Broken stream.");
/* intentionally not returning. */
}
return 0;
- } /* int DispatchValues */
+ } /* int PutValues */
private:
std::unique_ptr<Collectd::Stub> stub_;
value_list_t const *vl,
user_data_t *ud) {
CollectdClient *c = (CollectdClient *) ud->data;
- return c->DispatchValues(vl);
+ return c->PutValues(vl);
}
static int c_grpc_config_listen(oconfig_item_t *ci)
* Copyright (C) 2005,2006 Vincent Stehlé
* Copyright (C) 2006-2010 Florian octo Forster
* Copyright (C) 2008 Sebastian Harl
+ * Copyright (C) 2014 Carnegie Mellon University
*
* 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
* Vincent Stehlé <vincent.stehle at free.fr>
* Florian octo Forster <octo at collectd.org>
* Sebastian Harl <sh at tokkee.org>
+ * Benjamin Gilbert <bgilbert at backtick.net>
*
* TODO:
* Do a pass, some day, and spare some memory. We consume too much for now
#include "common.h"
#include "plugin.h"
+#include <assert.h>
#include <libgen.h> /* for basename */
#include <netdb.h>
#include <netinet/in.h>
#define HDDTEMP_DEF_HOST "127.0.0.1"
#define HDDTEMP_DEF_PORT "7634"
+#define HDDTEMP_MAX_RECV_BUF (1 << 20)
static const char *config_keys[] = {"Host", "Port"};
static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
* we need to create a new socket each time. Is there another way?
* Hm, maybe we can re-use the `sockaddr' structure? -octo
*/
-static int hddtemp_query_daemon(char *buffer, int buffer_size) {
+static char *hddtemp_query_daemon(void) {
int fd;
ssize_t status;
+
+ char *buffer;
+ int buffer_size;
int buffer_fill;
+ char *new_buffer;
const char *host;
const char *port;
ERROR("hddtemp plugin: getaddrinfo (%s, %s): %s", host, port,
(ai_return == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
: gai_strerror(ai_return));
- return (-1);
+ return (NULL);
}
fd = -1;
if (fd < 0) {
ERROR("hddtemp plugin: Could not connect to daemon.");
- return (-1);
+ return (NULL);
}
/* receive data from the hddtemp daemon */
- memset(buffer, '\0', buffer_size);
-
+ buffer = NULL;
+ buffer_size = 0;
buffer_fill = 0;
- while ((status = read(fd, buffer + buffer_fill, buffer_size - buffer_fill)) !=
- 0) {
- if (status == -1) {
+ while (1) {
+ if ((buffer_size == 0) || (buffer_fill >= buffer_size - 1)) {
+ if (buffer_size == 0)
+ buffer_size = 1024;
+ else
+ buffer_size *= 2;
+ if (buffer_size > HDDTEMP_MAX_RECV_BUF) {
+ WARNING("hddtemp plugin: Message from hddtemp has been "
+ "truncated.");
+ break;
+ }
+ new_buffer = realloc(buffer, buffer_size);
+ if (new_buffer == NULL) {
+ close(fd);
+ free(buffer);
+ ERROR("hddtemp plugin: Allocation failed.");
+ return (NULL);
+ }
+ buffer = new_buffer;
+ }
+ status = read(fd, buffer + buffer_fill, buffer_size - buffer_fill - 1);
+ if (status == 0) {
+ break;
+ } else if (status == -1) {
char errbuf[1024];
if ((errno == EAGAIN) || (errno == EINTR))
ERROR("hddtemp plugin: Error reading from socket: %s",
sstrerror(errno, errbuf, sizeof(errbuf)));
close(fd);
- return (-1);
+ free(buffer);
+ return (NULL);
}
buffer_fill += status;
-
- if (buffer_fill >= buffer_size)
- break;
}
- if (buffer_fill >= buffer_size) {
- buffer[buffer_size - 1] = '\0';
- WARNING("hddtemp plugin: Message from hddtemp has been "
- "truncated.");
- } else if (buffer_fill == 0) {
+ if (buffer_fill == 0) {
WARNING("hddtemp plugin: Peer has unexpectedly shut down "
"the socket. Buffer: `%s'",
buffer);
close(fd);
- return (-1);
+ free(buffer);
+ return (NULL);
}
+ assert(buffer_fill < buffer_size);
+ buffer[buffer_fill] = '\0';
close(fd);
- return (0);
+ return (buffer);
}
static int hddtemp_config(const char *key, const char *value) {
}
static void hddtemp_submit(char *type_instance, double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "hddtemp", sizeof(vl.plugin));
sstrncpy(vl.type, "temperature", sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
}
static int hddtemp_read(void) {
- char buf[1024];
- char *fields[128];
+ char *buf;
char *ptr;
char *saveptr;
- int num_fields;
- int num_disks;
+ char *name;
+ char *model;
+ char *temperature;
+ char *mode;
/* get data from daemon */
- if (hddtemp_query_daemon(buf, sizeof(buf)) < 0)
+ buf = hddtemp_query_daemon();
+ if (buf == NULL)
return (-1);
/* NB: strtok_r will eat up "||" and leading "|"'s */
- num_fields = 0;
ptr = buf;
saveptr = NULL;
- while ((fields[num_fields] = strtok_r(ptr, "|", &saveptr)) != NULL) {
- ptr = NULL;
- num_fields++;
+ while ((name = strtok_r(ptr, "|", &saveptr)) != NULL &&
+ (model = strtok_r(NULL, "|", &saveptr)) != NULL &&
+ (temperature = strtok_r(NULL, "|", &saveptr)) != NULL &&
+ (mode = strtok_r(NULL, "|", &saveptr)) != NULL) {
+ double temperature_value;
- if (num_fields >= 128)
- break;
- }
-
- num_disks = num_fields / 4;
-
- for (int i = 0; i < num_disks; i++) {
- char *name;
- double temperature;
- char *mode;
-
- mode = fields[4 * i + 3];
- name = basename(fields[4 * i + 0]);
+ ptr = NULL;
/* Skip non-temperature information */
if (mode[0] != 'C' && mode[0] != 'F')
continue;
- temperature = atof(fields[4 * i + 2]);
+ name = basename(name);
+ temperature_value = atof(temperature);
/* Convert farenheit to celsius */
if (mode[0] == 'F')
- temperature = (temperature - 32.0) * 5.0 / 9.0;
+ temperature_value = (temperature_value - 32.0) * 5.0 / 9.0;
- hddtemp_submit(name, temperature);
+ hddtemp_submit(name, temperature_value);
}
+ free(buf);
return (0);
} /* int hddtemp_read */
--- /dev/null
+/*-
+ * collectd - src/hugepages.c
+ * MIT License
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Jaroslav Safka <jaroslavx.safka@intel.com>
+ * Kim-Marie Jones <kim-marie.jones@intel.com>
+ * Florian Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "common.h" /* auxiliary functions */
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+
+static const char g_plugin_name[] = "hugepages";
+
+static _Bool g_flag_rpt_numa = 1;
+static _Bool g_flag_rpt_mm = 1;
+
+static _Bool g_values_pages = 1;
+static _Bool g_values_bytes = 0;
+static _Bool g_values_percent = 0;
+
+#define HP_HAVE_NR 0x01
+#define HP_HAVE_SURPLUS 0x02
+#define HP_HAVE_FREE 0x04
+#define HP_HAVE_ALL 0x07
+
+struct entry_info {
+ char *d_name;
+ const char *node;
+ size_t page_size_kb;
+
+ gauge_t nr;
+ gauge_t surplus;
+ gauge_t free;
+ uint8_t flags;
+};
+
+static int hp_config(oconfig_item_t *ci) {
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+ if (strcasecmp("ReportPerNodeHP", child->key) == 0)
+ cf_util_get_boolean(child, &g_flag_rpt_numa);
+ else if (strcasecmp("ReportRootHP", child->key) == 0)
+ cf_util_get_boolean(child, &g_flag_rpt_mm);
+ else if (strcasecmp("ValuesPages", child->key) == 0)
+ cf_util_get_boolean(child, &g_values_pages);
+ else if (strcasecmp("ValuesBytes", child->key) == 0)
+ cf_util_get_boolean(child, &g_values_bytes);
+ else if (strcasecmp("ValuesPercentage", child->key) == 0)
+ cf_util_get_boolean(child, &g_values_percent);
+ else
+ ERROR("%s: Invalid configuration option: \"%s\".", g_plugin_name,
+ child->key);
+ }
+
+ return (0);
+}
+
+static void submit_hp(const struct entry_info *info) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &(value_t){.gauge = NAN};
+ vl.values_len = 1;
+
+ sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
+ if (info->node) {
+ ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%zuKb",
+ info->node, info->page_size_kb);
+ } else {
+ ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%zuKb",
+ info->page_size_kb);
+ }
+
+ /* ensure all metrics have the same timestamp */
+ vl.time = cdtime();
+
+ gauge_t free = info->free;
+ gauge_t used = (info->nr + info->surplus) - info->free;
+
+ if (g_values_pages) {
+ sstrncpy(vl.type, "vmpage_number", sizeof(vl.type));
+ plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
+ "free", free, "used", used, NULL);
+ }
+ if (g_values_bytes) {
+ gauge_t page_size = (gauge_t)(1024 * info->page_size_kb);
+ sstrncpy(vl.type, "memory", sizeof(vl.type));
+ plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
+ "free", free * page_size, "used",
+ used * page_size, NULL);
+ }
+ if (g_values_percent) {
+ sstrncpy(vl.type, "percent", sizeof(vl.type));
+ plugin_dispatch_multivalue(&vl, /* store_percentage = */ 1, DS_TYPE_GAUGE,
+ "free", free, "used", used, NULL);
+ }
+}
+
+static int read_hugepage_entry(const char *path, const char *entry,
+ void *e_info) {
+ char path2[PATH_MAX];
+ struct entry_info *info = e_info;
+ double value;
+
+ ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
+
+ FILE *fh = fopen(path2, "rt");
+ if (fh == NULL) {
+ ERROR("%s: cannot open %s", g_plugin_name, path2);
+ return -1;
+ }
+
+ if (fscanf(fh, "%lf", &value) != 1) {
+ ERROR("%s: cannot parse file %s", g_plugin_name, path2);
+ fclose(fh);
+ return -1;
+ }
+ fclose(fh);
+
+ if (strcmp(entry, "nr_hugepages") == 0) {
+ info->nr = value;
+ info->flags |= HP_HAVE_NR;
+ } else if (strcmp(entry, "surplus_hugepages") == 0) {
+ info->surplus = value;
+ info->flags |= HP_HAVE_SURPLUS;
+ } else if (strcmp(entry, "free_hugepages") == 0) {
+ info->free = value;
+ info->flags |= HP_HAVE_FREE;
+ }
+
+ if (info->flags != HP_HAVE_ALL) {
+ return 0;
+ }
+
+ submit_hp(info);
+
+ /* Reset flags so subsequent calls don't submit again. */
+ info->flags = 0;
+ return 0;
+}
+
+static int read_syshugepages(const char *path, const char *node) {
+ static const char hugepages_dir[] = "hugepages-";
+ DIR *dir;
+ struct dirent *result;
+ char path2[PATH_MAX];
+
+ dir = opendir(path);
+ if (dir == NULL) {
+ ERROR("%s: cannot open directory %s", g_plugin_name, path);
+ return -1;
+ }
+
+ /* read "hugepages-XXXXXkB" entries */
+ while ((result = readdir(dir)) != NULL) {
+ if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
+ /* not node dir */
+ errno = 0;
+ continue;
+ }
+
+ long page_size = strtol(result->d_name + strlen(hugepages_dir),
+ /* endptr = */ NULL, /* base = */ 10);
+ if (errno != 0) {
+ char errbuf[1024];
+ ERROR("%s: failed to determine page size from directory name \"%s\": %s",
+ g_plugin_name, result->d_name,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ continue;
+ }
+
+ /* /sys/devices/system/node/node?/hugepages/ */
+ ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
+
+ walk_directory(path2, read_hugepage_entry,
+ &(struct entry_info){
+ .d_name = result->d_name,
+ .node = node,
+ .page_size_kb = (size_t)page_size,
+ },
+ /* hidden = */ 0);
+ errno = 0;
+ }
+
+ /* Check if NULL return from readdir() was an error */
+ if (errno != 0) {
+ ERROR("%s: readdir failed", g_plugin_name);
+ closedir(dir);
+ return -1;
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+static int read_nodes(void) {
+ static const char sys_node[] = "/sys/devices/system/node";
+ static const char node_string[] = "node";
+ static const char sys_node_hugepages[] =
+ "/sys/devices/system/node/%s/hugepages";
+ DIR *dir;
+ struct dirent *result;
+ char path[PATH_MAX];
+
+ dir = opendir(sys_node);
+ if (dir == NULL) {
+ ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
+ return -1;
+ }
+
+ while ((result = readdir(dir)) != NULL) {
+ if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
+ /* not node dir */
+ errno = 0;
+ continue;
+ }
+
+ ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
+ read_syshugepages(path, result->d_name);
+ errno = 0;
+ }
+
+ /* Check if NULL return from readdir() was an error */
+ if (errno != 0) {
+ ERROR("%s: readdir failed", g_plugin_name);
+ closedir(dir);
+ return -1;
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+static int huge_read(void) {
+ static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
+
+ if (g_flag_rpt_mm) {
+ if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
+ return -1;
+ }
+ }
+ if (g_flag_rpt_numa) {
+ if (read_nodes() != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void module_register(void) {
+ plugin_register_complex_config(g_plugin_name, hp_config);
+ plugin_register_read(g_plugin_name, huge_read);
+}
--- /dev/null
+/**
+ * collectd - src/intel_rdt.c
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ **/
+
+#include "common.h"
+#include "collectd.h"
+
+#include <pqos.h>
+
+#define RDT_PLUGIN "intel_rdt"
+
+#define RDT_MAX_SOCKETS 8
+#define RDT_MAX_SOCKET_CORES 64
+#define RDT_MAX_CORES (RDT_MAX_SOCKET_CORES * RDT_MAX_SOCKETS)
+
+typedef enum {
+ UNKNOWN = 0,
+ CONFIGURATION_ERROR,
+} rdt_config_status;
+
+struct rdt_core_group_s {
+ char *desc;
+ size_t num_cores;
+ unsigned *cores;
+ enum pqos_mon_event events;
+};
+typedef struct rdt_core_group_s rdt_core_group_t;
+
+struct rdt_ctx_s {
+ rdt_core_group_t cgroups[RDT_MAX_CORES];
+ struct pqos_mon_data *pgroups[RDT_MAX_CORES];
+ size_t num_groups;
+ const struct pqos_cpuinfo *pqos_cpu;
+ const struct pqos_cap *pqos_cap;
+ const struct pqos_capability *cap_mon;
+};
+typedef struct rdt_ctx_s rdt_ctx_t;
+
+static rdt_ctx_t *g_rdt = NULL;
+
+static rdt_config_status g_state = UNKNOWN;
+
+static int isdup(const uint64_t *nums, size_t size, uint64_t val) {
+ for (size_t i = 0; i < size; i++)
+ if (nums[i] == val)
+ return 1;
+ return 0;
+}
+
+static int strtouint64(const char *s, uint64_t *n) {
+ char *endptr = NULL;
+
+ assert(s != NULL);
+ assert(n != NULL);
+
+ *n = strtoull(s, &endptr, 0);
+
+ if (!(*s != '\0' && *endptr == '\0')) {
+ DEBUG(RDT_PLUGIN ": Error converting '%s' to unsigned number.", s);
+ return (-EINVAL);
+ }
+
+ return (0);
+}
+
+/*
+ * NAME
+ * strlisttonums
+ *
+ * DESCRIPTION
+ * Converts string of characters representing list of numbers into array of
+ * numbers. Allowed formats are:
+ * 0,1,2,3
+ * 0-10,20-18
+ * 1,3,5-8,10,0x10-12
+ *
+ * Numbers can be in decimal or hexadecimal format.
+ *
+ * PARAMETERS
+ * `s' String representing list of unsigned numbers.
+ * `nums' Array to put converted numeric values into.
+ * `max' Maximum number of elements that nums can accommodate.
+ *
+ * RETURN VALUE
+ * Number of elements placed into nums.
+ */
+static size_t strlisttonums(char *s, uint64_t *nums, size_t max) {
+ int ret;
+ size_t index = 0;
+ char *saveptr = NULL;
+
+ if (s == NULL || nums == NULL || max == 0)
+ return index;
+
+ for (;;) {
+ char *p = NULL;
+ char *token = NULL;
+
+ token = strtok_r(s, ",", &saveptr);
+ if (token == NULL)
+ break;
+
+ s = NULL;
+
+ while (isspace(*token))
+ token++;
+ if (*token == '\0')
+ continue;
+
+ p = strchr(token, '-');
+ if (p != NULL) {
+ uint64_t n, start, end;
+ *p = '\0';
+ ret = strtouint64(token, &start);
+ if (ret < 0)
+ return (0);
+ ret = strtouint64(p + 1, &end);
+ if (ret < 0)
+ return (0);
+ if (start > end) {
+ return (0);
+ }
+ for (n = start; n <= end; n++) {
+ if (!(isdup(nums, index, n))) {
+ nums[index] = n;
+ index++;
+ }
+ if (index >= max)
+ return index;
+ }
+ } else {
+ uint64_t val;
+
+ ret = strtouint64(token, &val);
+ if (ret < 0)
+ return (0);
+
+ if (!(isdup(nums, index, val))) {
+ nums[index] = val;
+ index++;
+ }
+ if (index >= max)
+ return index;
+ }
+ }
+
+ return index;
+}
+
+/*
+ * NAME
+ * cgroup_cmp
+ *
+ * DESCRIPTION
+ * Function to compare cores in 2 core groups.
+ *
+ * PARAMETERS
+ * `cg_a' Pointer to core group a.
+ * `cg_b' Pointer to core group b.
+ *
+ * RETURN VALUE
+ * 1 if both groups contain the same cores
+ * 0 if none of their cores match
+ * -1 if some but not all cores match
+ */
+static int cgroup_cmp(const rdt_core_group_t *cg_a,
+ const rdt_core_group_t *cg_b) {
+ int found = 0;
+
+ assert(cg_a != NULL);
+ assert(cg_b != NULL);
+
+ const int sz_a = cg_a->num_cores;
+ const int sz_b = cg_b->num_cores;
+ const unsigned *tab_a = cg_a->cores;
+ const unsigned *tab_b = cg_b->cores;
+
+ for (int i = 0; i < sz_a; i++) {
+ for (int j = 0; j < sz_b; j++)
+ if (tab_a[i] == tab_b[j])
+ found++;
+ }
+ /* if no cores are the same */
+ if (!found)
+ return 0;
+ /* if group contains same cores */
+ if (sz_a == sz_b && sz_b == found)
+ return 1;
+ /* if not all cores are the same */
+ return -1;
+}
+
+static int cgroup_set(rdt_core_group_t *cg, char *desc, uint64_t *cores,
+ size_t num_cores) {
+ assert(cg != NULL);
+ assert(desc != NULL);
+ assert(cores != NULL);
+ assert(num_cores > 0);
+
+ cg->cores = calloc(num_cores, sizeof(unsigned));
+ if (cg->cores == NULL) {
+ ERROR(RDT_PLUGIN ": Error allocating core group table");
+ return (-ENOMEM);
+ }
+ cg->num_cores = num_cores;
+ cg->desc = strdup(desc);
+ if (cg->desc == NULL) {
+ ERROR(RDT_PLUGIN ": Error allocating core group description");
+ sfree(cg->cores);
+ return (-ENOMEM);
+ }
+
+ for (size_t i = 0; i < num_cores; i++)
+ cg->cores[i] = (unsigned)cores[i];
+
+ return 0;
+}
+
+/*
+ * NAME
+ * oconfig_to_cgroups
+ *
+ * DESCRIPTION
+ * Function to set the descriptions and cores for each core group.
+ * Takes a config option containing list of strings that are used to set
+ * core group values.
+ *
+ * PARAMETERS
+ * `item' Config option containing core groups.
+ * `groups' Table of core groups to set values in.
+ * `max_groups' Maximum number of core groups allowed.
+ * `max_core' Maximum allowed core value.
+ *
+ * RETURN VALUE
+ * On success, the number of core groups set up. On error, appropriate
+ * negative error value.
+ */
+static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
+ size_t max_groups, uint64_t max_core) {
+ int index = 0;
+
+ assert(groups != NULL);
+ assert(max_groups > 0);
+ assert(item != NULL);
+
+ for (int j = 0; j < item->values_num; j++) {
+ int ret;
+ size_t n;
+ uint64_t cores[RDT_MAX_CORES] = {0};
+ char value[DATA_MAX_NAME_LEN];
+
+ if ((item->values[j].value.string == NULL) ||
+ (strlen(item->values[j].value.string) == 0))
+ continue;
+
+ sstrncpy(value, item->values[j].value.string, sizeof(value));
+
+ n = strlisttonums(value, cores, STATIC_ARRAY_SIZE(cores));
+ if (n == 0) {
+ ERROR(RDT_PLUGIN ": Error parsing core group (%s)",
+ item->values[j].value.string);
+ return (-EINVAL);
+ }
+
+ for (int i = 0; i < n; i++) {
+ if (cores[i] > max_core) {
+ ERROR(RDT_PLUGIN ": Core group (%s) contains invalid core id (%d)",
+ item->values[j].value.string, (int)cores[i]);
+ return (-EINVAL);
+ }
+ }
+
+ /* set core group info */
+ ret = cgroup_set(&groups[index], item->values[j].value.string, cores, n);
+ if (ret < 0)
+ return ret;
+
+ index++;
+
+ if (index >= max_groups) {
+ WARNING(RDT_PLUGIN ": Too many core groups configured");
+ return index;
+ }
+ }
+
+ return index;
+}
+
+#if COLLECT_DEBUG
+static void rdt_dump_cgroups(void) {
+ char cores[RDT_MAX_CORES * 4];
+
+ if (g_rdt == NULL)
+ return;
+
+ DEBUG(RDT_PLUGIN ": Core Groups Dump");
+ DEBUG(RDT_PLUGIN ": groups count: %zu", g_rdt->num_groups);
+
+ for (int i = 0; i < g_rdt->num_groups; i++) {
+
+ memset(cores, 0, sizeof(cores));
+ for (int j = 0; j < g_rdt->cgroups[i].num_cores; j++) {
+ snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d",
+ g_rdt->cgroups[i].cores[j]);
+ }
+
+ DEBUG(RDT_PLUGIN ": group[%d]:", i);
+ DEBUG(RDT_PLUGIN ": description: %s", g_rdt->cgroups[i].desc);
+ DEBUG(RDT_PLUGIN ": cores: %s", cores);
+ DEBUG(RDT_PLUGIN ": events: 0x%X", g_rdt->cgroups[i].events);
+ }
+
+ return;
+}
+
+static inline double bytes_to_kb(const double bytes) { return bytes / 1024.0; }
+
+static inline double bytes_to_mb(const double bytes) {
+ return bytes / (1024.0 * 1024.0);
+}
+
+static void rdt_dump_data(void) {
+ /*
+ * CORE - monitored group of cores
+ * RMID - Resource Monitoring ID associated with the monitored group
+ * LLC - last level cache occupancy
+ * MBL - local memory bandwidth
+ * MBR - remote memory bandwidth
+ */
+ DEBUG(" CORE RMID LLC[KB] MBL[MB] MBR[MB]");
+ for (int i = 0; i < g_rdt->num_groups; i++) {
+
+ const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
+
+ double llc = bytes_to_kb(pv->llc);
+ double mbr = bytes_to_mb(pv->mbm_remote_delta);
+ double mbl = bytes_to_mb(pv->mbm_local_delta);
+
+ DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cgroups[i].desc,
+ g_rdt->pgroups[i]->poll_ctx[0].rmid, llc, mbl, mbr);
+ }
+}
+#endif /* COLLECT_DEBUG */
+
+static void rdt_free_cgroups(void) {
+ for (int i = 0; i < RDT_MAX_CORES; i++) {
+ sfree(g_rdt->cgroups[i].desc);
+
+ sfree(g_rdt->cgroups[i].cores);
+ g_rdt->cgroups[i].num_cores = 0;
+
+ sfree(g_rdt->pgroups[i]);
+ }
+}
+
+static int rdt_default_cgroups(void) {
+ int ret;
+
+ /* configure each core in separate group */
+ for (unsigned i = 0; i < g_rdt->pqos_cpu->num_cores; i++) {
+ char desc[DATA_MAX_NAME_LEN];
+ uint64_t core = i;
+
+ ssnprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
+
+ /* set core group info */
+ ret = cgroup_set(&g_rdt->cgroups[i], desc, &core, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return g_rdt->pqos_cpu->num_cores;
+}
+
+static int rdt_config_cgroups(oconfig_item_t *item) {
+ int n = 0;
+ enum pqos_mon_event events = 0;
+
+ if (item == NULL) {
+ DEBUG(RDT_PLUGIN ": cgroups_config: Invalid argument.");
+ return (-EINVAL);
+ }
+
+ DEBUG(RDT_PLUGIN ": Core groups [%d]:", item->values_num);
+ for (int j = 0; j < item->values_num; j++) {
+ if (item->values[j].type != OCONFIG_TYPE_STRING) {
+ ERROR(RDT_PLUGIN ": given core group value is not a string [idx=%d]", j);
+ return (-EINVAL);
+ }
+ DEBUG(RDT_PLUGIN ": [%d]: %s", j, item->values[j].value.string);
+ }
+
+ n = oconfig_to_cgroups(item, g_rdt->cgroups, RDT_MAX_CORES,
+ g_rdt->pqos_cpu->num_cores - 1);
+ if (n < 0) {
+ rdt_free_cgroups();
+ ERROR(RDT_PLUGIN ": Error parsing core groups configuration.");
+ return (-EINVAL);
+ }
+
+ if (n == 0) {
+ /* create default core groups if "Cores" config option is empty */
+ n = rdt_default_cgroups();
+ if (n < 0) {
+ rdt_free_cgroups();
+ ERROR(RDT_PLUGIN ": Error creating default core groups configuration.");
+ return n;
+ }
+ INFO(RDT_PLUGIN
+ ": No core groups configured. Default core groups created.");
+ }
+
+ /* Get all available events on this platform */
+ for (int i = 0; i < g_rdt->cap_mon->u.mon->num_events; i++)
+ events |= g_rdt->cap_mon->u.mon->events[i].type;
+
+ events &= ~(PQOS_PERF_EVENT_LLC_MISS);
+
+ DEBUG(RDT_PLUGIN ": Number of cores in the system: %u",
+ g_rdt->pqos_cpu->num_cores);
+ DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events);
+
+ g_rdt->num_groups = n;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < i; j++) {
+ int found = 0;
+ found = cgroup_cmp(&g_rdt->cgroups[j], &g_rdt->cgroups[i]);
+ if (found != 0) {
+ rdt_free_cgroups();
+ ERROR(RDT_PLUGIN ": Cannot monitor same cores in different groups.");
+ return (-EINVAL);
+ }
+ }
+
+ g_rdt->cgroups[i].events = events;
+ g_rdt->pgroups[i] = calloc(1, sizeof(*g_rdt->pgroups[i]));
+ if (g_rdt->pgroups[i] == NULL) {
+ rdt_free_cgroups();
+ ERROR(RDT_PLUGIN ": Failed to allocate memory for monitoring data.");
+ return (-ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static int rdt_preinit(void) {
+ int ret;
+
+ if (g_rdt != NULL) {
+ /* already initialized if config callback was called before init callback */
+ return (0);
+ }
+
+ g_rdt = calloc(1, sizeof(*g_rdt));
+ if (g_rdt == NULL) {
+ ERROR(RDT_PLUGIN ": Failed to allocate memory for rdt context.");
+ return (-ENOMEM);
+ }
+
+ /* In case previous instance of the application was not closed properly
+ * call fini and ignore return code. */
+ pqos_fini();
+
+ /* TODO:
+ * stdout should not be used here. Will be reworked when support of log
+ * callback is added to PQoS library.
+ */
+ ret = pqos_init(&(struct pqos_config){.fd_log = STDOUT_FILENO});
+ if (ret != PQOS_RETVAL_OK) {
+ ERROR(RDT_PLUGIN ": Error initializing PQoS library!");
+ goto rdt_preinit_error1;
+ }
+
+ ret = pqos_cap_get(&g_rdt->pqos_cap, &g_rdt->pqos_cpu);
+ if (ret != PQOS_RETVAL_OK) {
+ ERROR(RDT_PLUGIN ": Error retrieving PQoS capabilities.");
+ goto rdt_preinit_error2;
+ }
+
+ ret = pqos_cap_get_type(g_rdt->pqos_cap, PQOS_CAP_TYPE_MON, &g_rdt->cap_mon);
+ if (ret == PQOS_RETVAL_PARAM) {
+ ERROR(RDT_PLUGIN ": Error retrieving monitoring capabilities.");
+ goto rdt_preinit_error2;
+ }
+
+ if (g_rdt->cap_mon == NULL) {
+ ERROR(
+ RDT_PLUGIN
+ ": Monitoring capability not detected. Nothing to do for the plugin.");
+ goto rdt_preinit_error2;
+ }
+
+ return (0);
+
+rdt_preinit_error2:
+ pqos_fini();
+
+rdt_preinit_error1:
+
+ sfree(g_rdt);
+
+ return (-1);
+}
+
+static int rdt_config(oconfig_item_t *ci) {
+ int ret = 0;
+
+ ret = rdt_preinit();
+ if (ret != 0) {
+ g_state = CONFIGURATION_ERROR;
+ /* if we return -1 at this point collectd
+ reports a failure in configuration and
+ aborts
+ */
+ goto exit;
+ }
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Cores", child->key) == 0) {
+
+ ret = rdt_config_cgroups(child);
+ if (ret != 0) {
+ g_state = CONFIGURATION_ERROR;
+ /* if we return -1 at this point collectd
+ reports a failure in configuration and
+ aborts
+ */
+ goto exit;
+ }
+
+#if COLLECT_DEBUG
+ rdt_dump_cgroups();
+#endif /* COLLECT_DEBUG */
+
+ } else {
+ ERROR(RDT_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
+ }
+ }
+
+exit:
+ return (0);
+}
+
+static void rdt_submit_derive(char *cgroup, char *type, char *type_instance,
+ derive_t value) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &(value_t){.derive = value};
+ vl.values_len = 1;
+
+ sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
+ snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ if (type_instance)
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+
+ plugin_dispatch_values(&vl);
+}
+
+static void rdt_submit_gauge(char *cgroup, char *type, char *type_instance,
+ gauge_t value) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &(value_t){.gauge = value};
+ vl.values_len = 1;
+
+ sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
+ snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ if (type_instance)
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+
+ plugin_dispatch_values(&vl);
+}
+
+static int rdt_read(__attribute__((unused)) user_data_t *ud) {
+ int ret;
+
+ if (g_rdt == NULL) {
+ ERROR(RDT_PLUGIN ": rdt_read: plugin not initialized.");
+ return (-EINVAL);
+ }
+
+ ret = pqos_mon_poll(&g_rdt->pgroups[0], (unsigned)g_rdt->num_groups);
+ if (ret != PQOS_RETVAL_OK) {
+ ERROR(RDT_PLUGIN ": Failed to poll monitoring data.");
+ return (-1);
+ }
+
+#if COLLECT_DEBUG
+ rdt_dump_data();
+#endif /* COLLECT_DEBUG */
+
+ for (int i = 0; i < g_rdt->num_groups; i++) {
+ enum pqos_mon_event mbm_events =
+ (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW |
+ PQOS_MON_EVENT_RMEM_BW);
+
+ const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
+
+ /* Submit only monitored events data */
+
+ if (g_rdt->cgroups[i].events & PQOS_MON_EVENT_L3_OCCUP)
+ rdt_submit_gauge(g_rdt->cgroups[i].desc, "bytes", "llc", pv->llc);
+
+ if (g_rdt->cgroups[i].events & PQOS_PERF_EVENT_IPC)
+ rdt_submit_gauge(g_rdt->cgroups[i].desc, "ipc", NULL, pv->ipc);
+
+ if (g_rdt->cgroups[i].events & mbm_events) {
+ rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "local",
+ pv->mbm_local_delta);
+ rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "remote",
+ pv->mbm_remote_delta);
+ }
+ }
+
+ return (0);
+}
+
+static int rdt_init(void) {
+ int ret;
+
+ if(g_state == CONFIGURATION_ERROR)
+ return (-1);
+
+ ret = rdt_preinit();
+ if (ret != 0)
+ return ret;
+
+ /* Start monitoring */
+ for (int i = 0; i < g_rdt->num_groups; i++) {
+ rdt_core_group_t *cg = &g_rdt->cgroups[i];
+
+ ret = pqos_mon_start(cg->num_cores, cg->cores, cg->events, (void *)cg->desc,
+ g_rdt->pgroups[i]);
+
+ if (ret != PQOS_RETVAL_OK)
+ ERROR(RDT_PLUGIN ": Error starting monitoring group %s (pqos status=%d)",
+ cg->desc, ret);
+ }
+
+ return (0);
+}
+
+static int rdt_shutdown(void) {
+ int ret;
+
+ DEBUG(RDT_PLUGIN ": rdt_shutdown.");
+
+ if (g_rdt == NULL)
+ return (0);
+
+ /* Stop monitoring */
+ for (int i = 0; i < g_rdt->num_groups; i++) {
+ pqos_mon_stop(g_rdt->pgroups[i]);
+ }
+
+ ret = pqos_fini();
+ if (ret != PQOS_RETVAL_OK)
+ ERROR(RDT_PLUGIN ": Error shutting down PQoS library.");
+
+ rdt_free_cgroups();
+ sfree(g_rdt);
+
+ return (0);
+}
+
+void module_register(void) {
+ plugin_register_init(RDT_PLUGIN, rdt_init);
+ plugin_register_complex_config(RDT_PLUGIN, rdt_config);
+ plugin_register_complex_read(NULL, RDT_PLUGIN, rdt_read, 0, NULL);
+ plugin_register_shutdown(RDT_PLUGIN, rdt_shutdown);
+}
static void if_submit(const char *dev, const char *type, derive_t rx,
derive_t tx) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
if (ignorelist_match(ignorelist, dev) != 0)
return;
- values[0].derive = rx;
- values[1].derive = tx;
-
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "interface", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
ipc_submit_g(const char *plugin_instance, const char *type,
const char *type_instance, gauge_t value) /* {{{ */
{
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ipc", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
double value,
ipmi_states_t __attribute__((unused)) * states,
void *user_data) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
return;
}
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ipmi", sizeof(vl.plugin));
sstrncpy(vl.type, list_item->sensor_type, sizeof(vl.type));
sstrncpy(vl.type_instance, list_item->sensor_name, sizeof(vl.type_instance));
c_ipmi_active = 1;
status = plugin_thread_create(&thread_id, /* attr = */ NULL, thread_main,
- /* user data = */ NULL);
+ /* user data = */ NULL, "ipmi");
if (status != 0) {
c_ipmi_active = 0;
thread_id = (pthread_t)0;
const struct ip6t_entry *entry,
const ip_chain_t *chain, int rule_num) {
int status;
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
/* Select the rules to collect */
return (0);
}
- vl.values = values;
- vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ip6tables", sizeof(vl.plugin));
status = ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s",
}
sstrncpy(vl.type, "ipt_bytes", sizeof(vl.type));
- values[0].derive = (derive_t)entry->counters.bcnt;
+ vl.values = &(value_t){.derive = (derive_t)entry->counters.bcnt};
+ vl.values_len = 1;
plugin_dispatch_values(&vl);
sstrncpy(vl.type, "ipt_packets", sizeof(vl.type));
- values[0].derive = (derive_t)entry->counters.pcnt;
+ vl.values = &(value_t){.derive = (derive_t)entry->counters.pcnt};
plugin_dispatch_values(&vl);
return (0);
-} /* int submit_match */
+} /* int submit6_match */
/* This needs to return `int' for IPT_MATCH_ITERATE to work. */
static int submit_match(const struct ipt_entry_match *match,
const struct ipt_entry *entry, const ip_chain_t *chain,
int rule_num) {
int status;
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
/* Select the rules to collect */
return (0);
}
- vl.values = values;
- vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "iptables", sizeof(vl.plugin));
status = ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s",
}
sstrncpy(vl.type, "ipt_bytes", sizeof(vl.type));
- values[0].derive = (derive_t)entry->counters.bcnt;
+ vl.values = &(value_t){.derive = (derive_t)entry->counters.bcnt};
+ vl.values_len = 1;
plugin_dispatch_values(&vl);
sstrncpy(vl.type, "ipt_packets", sizeof(vl.type));
- values[0].derive = (derive_t)entry->counters.pcnt;
+ vl.values = &(value_t){.derive = (derive_t)entry->counters.pcnt};
plugin_dispatch_values(&vl);
return (0);
static void cipvs_submit_connections(const char *pi, const char *ti,
derive_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = value;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ipvs", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, pi, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "connections", sizeof(vl.type));
static void cipvs_submit_if(const char *pi, const char *t, const char *ti,
derive_t rx, derive_t tx) {
- value_t values[2];
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = rx;
- values[1].derive = tx;
-
vl.values = values;
- vl.values_len = 2;
+ vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ipvs", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, pi, sizeof(vl.plugin_instance));
sstrncpy(vl.type, t, sizeof(vl.type));
}
static void irq_submit(const char *irq_name, derive_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
if (ignorelist_match(ignorelist, irq_name) != 0)
return;
- values[0].derive = value;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "irq", sizeof(vl.plugin));
sstrncpy(vl.type, "irq", sizeof(vl.type));
sstrncpy(vl.type_instance, irq_name, sizeof(vl.type_instance));
DEBUG("java plugin: Registering new read callback: %s", cbi->name);
- user_data_t ud = {.data = cbi, .free_func = cjni_callback_info_destroy};
-
- plugin_register_complex_read(/* group = */ NULL, cbi->name, cjni_read,
- /* interval = */ 0, &ud);
+ plugin_register_complex_read(
+ /* group = */ NULL, cbi->name, cjni_read,
+ /* interval = */ 0,
+ &(user_data_t){
+ .data = cbi, .free_func = cjni_callback_info_destroy,
+ });
(*jvm_env)->DeleteLocalRef(jvm_env, o_read);
DEBUG("java plugin: Registering new write callback: %s", cbi->name);
- user_data_t ud = {.data = cbi, .free_func = cjni_callback_info_destroy};
-
- plugin_register_write(cbi->name, cjni_write, &ud);
+ plugin_register_write(
+ cbi->name, cjni_write,
+ &(user_data_t){
+ .data = cbi, .free_func = cjni_callback_info_destroy,
+ });
(*jvm_env)->DeleteLocalRef(jvm_env, o_write);
DEBUG("java plugin: Registering new flush callback: %s", cbi->name);
- user_data_t ud = {.data = cbi, .free_func = cjni_callback_info_destroy};
-
- plugin_register_flush(cbi->name, cjni_flush, &ud);
+ plugin_register_flush(
+ cbi->name, cjni_flush,
+ &(user_data_t){
+ .data = cbi, .free_func = cjni_callback_info_destroy,
+ });
(*jvm_env)->DeleteLocalRef(jvm_env, o_flush);
DEBUG("java plugin: Registering new log callback: %s", cbi->name);
- user_data_t ud = {.data = cbi, .free_func = cjni_callback_info_destroy};
-
- plugin_register_log(cbi->name, cjni_log, &ud);
+ plugin_register_log(cbi->name, cjni_log,
+ &(user_data_t){
+ .data = cbi, .free_func = cjni_callback_info_destroy,
+ });
(*jvm_env)->DeleteLocalRef(jvm_env, o_log);
DEBUG("java plugin: Registering new notification callback: %s", cbi->name);
- user_data_t ud = {.data = cbi, .free_func = cjni_callback_info_destroy};
-
- plugin_register_notification(cbi->name, cjni_notification, &ud);
+ plugin_register_notification(
+ cbi->name, cjni_notification,
+ &(user_data_t){
+ .data = cbi, .free_func = cjni_callback_info_destroy,
+ });
(*jvm_env)->DeleteLocalRef(jvm_env, o_notification);
return (-1);
}
static void load_submit(gauge_t snum, gauge_t mnum, gauge_t lnum) {
- value_t values[3];
- value_list_t vl = VALUE_LIST_INIT;
int cores = 0;
char errbuf[1024];
lnum /= cores;
}
- values[0].gauge = snum;
- values[1].gauge = mnum;
- values[2].gauge = lnum;
+ value_list_t vl = VALUE_LIST_INIT;
+ value_t values[] = {
+ {.gauge = snum}, {.gauge = mnum}, {.gauge = lnum},
+ };
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "load", sizeof(vl.plugin));
sstrncpy(vl.type, "load", sizeof(vl.type));
struct tm timestamp_tm;
char timestamp_str[64];
const unsigned char *buf;
- time_t tt;
#if HAVE_YAJL_V2
size_t len;
#else
yajl_gen_status_ok)
goto err;
- tt = CDTIME_T_TO_TIME_T(timestamp_time);
- gmtime_r(&tt, ×tamp_tm);
+ gmtime_r(&CDTIME_T_TO_TIME_T(timestamp_time), ×tamp_tm);
/*
* format time as a UTC ISO 8601 compliant string
cdtime_t timestamp_time) {
FILE *fh;
_Bool do_close = 0;
- struct tm timestamp_tm;
char timestamp_str[64];
char level_str[16] = "";
}
if (print_timestamp) {
- time_t tt = CDTIME_T_TO_TIME_T(timestamp_time);
- localtime_r(&tt, ×tamp_tm);
+ struct tm timestamp_tm;
+ localtime_r(&CDTIME_T_TO_TIME_T(timestamp_time), ×tamp_tm);
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S",
×tamp_tm);
} /* int lpar_init */
static void lpar_submit(const char *type_instance, double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = (gauge_t)value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
if (report_by_serial) {
sstrncpy(vl.host, serial, sizeof(vl.host));
sstrncpy(vl.plugin_instance, hostname_g, sizeof(vl.plugin));
- } else {
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
}
sstrncpy(vl.plugin, "lpar", sizeof(vl.plugin));
sstrncpy(vl.type, "vcpu", sizeof(vl.type));
cb->lua_function_name = strdup(function_name);
pthread_mutex_init(&cb->lock, NULL);
- user_data_t ud = {.data = cb};
-
int status = plugin_register_complex_read(/* group = */ "lua",
/* name = */ function_name,
/* callback = */ clua_read,
- /* interval = */ 0,
- /* user_data = */ &ud);
+ /* interval = */ 0, &(user_data_t){
+ .data = cb,
+ });
if (status != 0)
return luaL_error(L, "%s", "plugin_register_complex_read failed");
cb->lua_function_name = strdup(function_name);
pthread_mutex_init(&cb->lock, NULL);
- user_data_t ud = {.data = cb};
-
- int status = plugin_register_write(/* name = */ function_name,
- /* callback = */ clua_write,
- /* user_data = */ &ud);
+ int status =
+ plugin_register_write(/* name = */ function_name,
+ /* callback = */ clua_write, &(user_data_t){
+ .data = cb,
+ });
if (status != 0)
return luaL_error(L, "%s", "plugin_register_write failed");
*
* Authors:
* Chad Malfait <malfaitc at yahoo.com>
- * Benjamin Gilbert <bgilbert at cs.cmu.edu>
+ * Benjamin Gilbert <bgilbert at backtick.net>
**/
#include <lvm2app.h>
static void lvm_submit(char const *plugin_instance, char const *type_instance,
uint64_t ivalue) {
- value_t v;
value_list_t vl = VALUE_LIST_INIT;
- v.gauge = (gauge_t)ivalue;
-
- vl.values = &v;
+ vl.values = &(value_t){.gauge = (gauge_t)ivalue};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "lvm", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "df_complex", sizeof(vl.type));
}
static void submit(const char *dev, const char *type, const char *ti1,
- const char *ti2, value_t *val, int len) {
+ const char *ti2, value_t *val, size_t len) {
value_list_t vl = VALUE_LIST_INIT;
vl.values = val;
vl.values_len = len;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "madwifi", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
}
static void submit_derive(const char *dev, const char *type, const char *ti1,
- const char *ti2, derive_t val) {
- value_t item;
- item.derive = val;
- submit(dev, type, ti1, ti2, &item, 1);
+ const char *ti2, derive_t value) {
+ submit(dev, type, ti1, ti2, &(value_t){.derive = value}, 1);
}
static void submit_derive2(const char *dev, const char *type, const char *ti1,
const char *ti2, derive_t val1, derive_t val2) {
- value_t items[2];
- items[0].derive = val1;
- items[1].derive = val2;
- submit(dev, type, ti1, ti2, items, 2);
+ value_t values[] = {
+ {.derive = val1}, {.derive = val2},
+ };
+
+ submit(dev, type, ti1, ti2, values, STATIC_ARRAY_SIZE(values));
}
static void submit_gauge(const char *dev, const char *type, const char *ti1,
- const char *ti2, gauge_t val) {
- value_t item;
- item.gauge = val;
- submit(dev, type, ti1, ti2, &item, 1);
+ const char *ti2, gauge_t value) {
+ submit(dev, type, ti1, ti2, &(value_t){.gauge = value}, 1);
}
static void submit_antx(const char *dev, const char *name, u_int32_t *vals,
#include "collectd.h"
+#include "common.h"
#include "filter_chain.h"
+#include "meta_data.h"
+#include "utils_llist.h"
#include <regex.h>
#include <sys/types.h>
mr_regex_t *plugin_instance;
mr_regex_t *type;
mr_regex_t *type_instance;
+ llist_t *meta; /* Maps each meta key into mr_regex_t* */
_Bool invert;
};
regfree(&r->re);
memset(&r->re, 0, sizeof(r->re));
- free(r->re_str);
+ sfree(r->re_str);
if (r->next != NULL)
mr_free_regex(r->next);
mr_free_regex(m->plugin_instance);
mr_free_regex(m->type);
mr_free_regex(m->type_instance);
+ for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
+ sfree(e->key);
+ mr_free_regex((mr_regex_t *)e->value);
+ }
+ llist_destroy(m->meta);
- free(m);
+ sfree(m);
} /* }}} void mr_free_match */
static int mr_match_regexen(mr_regex_t *re_head, /* {{{ */
return (FC_MATCH_MATCHES);
} /* }}} int mr_match_regexen */
-static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
- oconfig_item_t *ci) {
+static int mr_add_regex(mr_regex_t **re_head, const char *re_str, /* {{{ */
+ const char *option) {
mr_regex_t *re;
int status;
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
- log_warn("`%s' needs exactly one string argument.", ci->key);
- return (-1);
- }
-
re = calloc(1, sizeof(*re));
if (re == NULL) {
- log_err("mr_config_add_regex: calloc failed.");
+ log_err("mr_add_regex: calloc failed.");
return (-1);
}
re->next = NULL;
- re->re_str = strdup(ci->values[0].value.string);
+ re->re_str = strdup(re_str);
if (re->re_str == NULL) {
- free(re);
- log_err("mr_config_add_regex: strdup failed.");
+ sfree(re);
+ log_err("mr_add_regex: strdup failed.");
return (-1);
}
char errmsg[1024];
regerror(status, &re->re, errmsg, sizeof(errmsg));
errmsg[sizeof(errmsg) - 1] = 0;
- log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, ci->key,
+ log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, option,
errmsg);
- free(re->re_str);
- free(re);
+ sfree(re->re_str);
+ sfree(re);
return (-1);
}
}
return (0);
+} /* }}} int mr_add_regex */
+
+static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
+ oconfig_item_t *ci) {
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+ log_warn("`%s' needs exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ return mr_add_regex(re_head, ci->values[0].value.string, ci->key);
} /* }}} int mr_config_add_regex */
+static int mr_config_add_meta_regex(llist_t **meta, /* {{{ */
+ oconfig_item_t *ci) {
+ char *meta_key;
+ llentry_t *entry;
+ mr_regex_t *re_head;
+ int status;
+ char buffer[1024];
+
+ if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
+ (ci->values[1].type != OCONFIG_TYPE_STRING)) {
+ log_warn("`%s' needs exactly two string arguments.", ci->key);
+ return (-1);
+ }
+
+ if (*meta == NULL) {
+ *meta = llist_create();
+ if (*meta == NULL) {
+ log_err("mr_config_add_meta_regex: llist_create failed.");
+ return (-1);
+ }
+ }
+
+ meta_key = ci->values[0].value.string;
+ entry = llist_search(*meta, meta_key);
+ if (entry == NULL) {
+ meta_key = strdup(meta_key);
+ if (meta_key == NULL) {
+ log_err("mr_config_add_meta_regex: strdup failed.");
+ return (-1);
+ }
+ entry = llentry_create(meta_key, NULL);
+ if (entry == NULL) {
+ log_err("mr_config_add_meta_regex: llentry_create failed.");
+ sfree(meta_key);
+ return (-1);
+ }
+ /* meta_key and entry will now be freed by mr_free_match(). */
+ llist_append(*meta, entry);
+ }
+
+ ssnprintf(buffer, sizeof(buffer), "%s `%s'", ci->key, meta_key);
+ /* Can't pass &entry->value into mr_add_regex, so copy in/out. */
+ re_head = entry->value;
+ status = mr_add_regex(&re_head, ci->values[1].value.string, buffer);
+ if (status == 0) {
+ entry->value = re_head;
+ }
+ return status;
+} /* }}} int mr_config_add_meta_regex */
+
static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
{
mr_match_t *m;
status = mr_config_add_regex(&m->type, child);
else if (strcasecmp("TypeInstance", child->key) == 0)
status = mr_config_add_regex(&m->type_instance, child);
+ else if (strcasecmp("MetaData", child->key) == 0)
+ status = mr_config_add_meta_regex(&m->meta, child);
else if (strcasecmp("Invert", child->key) == 0)
status = cf_util_get_boolean(child, &m->invert);
else {
while (status == 0) {
if ((m->host == NULL) && (m->plugin == NULL) &&
(m->plugin_instance == NULL) && (m->type == NULL) &&
- (m->type_instance == NULL)) {
+ (m->type_instance == NULL) && (m->meta == NULL)) {
log_err("No (valid) regular expressions have been configured. "
"This match will be ignored.");
status = -1;
if (mr_match_regexen(m->type_instance, vl->type_instance) ==
FC_MATCH_NO_MATCH)
return (nomatch_value);
+ if (vl->meta != NULL) {
+ for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
+ mr_regex_t *meta_re = (mr_regex_t *)e->value;
+ char *value;
+ int status = meta_data_get_string(vl->meta, e->key, &value);
+ if (status == (-ENOENT)) /* key is not present */
+ return (nomatch_value);
+ if (status != 0) /* some other problem */
+ continue; /* error will have already been printed. */
+ if (mr_match_regexen(meta_re, value) == FC_MATCH_NO_MATCH) {
+ sfree(value);
+ return (nomatch_value);
+ }
+ sfree(value);
+ }
+ }
return (match_value);
} /* }}} int mr_match */
static void mbmon_submit(const char *type, const char *type_instance,
double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "mbmon", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
static void md_submit(const int minor, const char *type_instance,
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "md", sizeof(vl.plugin));
ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", minor);
sstrncpy(vl.type, "md_disks", sizeof(vl.type));
} /* }}} int cmc_init */
static void cmc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */
- const cu_match_value_t *mv) {
- value_t values[1];
+ value_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0] = mv->value;
-
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "memcachec", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, wm->type, sizeof(vl.type));
continue;
}
- cmc_submit(wp, wm, mv);
+ cmc_submit(wp, wm, mv->value);
match_value_reset(mv);
} /* for (wm = wp->matches; wm != NULL; wm = wm->next) */
struct memcached_s {
char *name;
- char *socket;
char *host;
- char *port;
+ char *socket;
+ char *connhost;
+ char *connport;
};
typedef struct memcached_s memcached_t;
return;
sfree(st->name);
- sfree(st->socket);
sfree(st->host);
- sfree(st->port);
+ sfree(st->socket);
+ sfree(st->connhost);
+ sfree(st->connport);
sfree(st);
}
} /* int memcached_connect_unix */
static int memcached_connect_inet(memcached_t *st) {
- const char *host;
- const char *port;
-
struct addrinfo *ai_list;
int status;
int fd = -1;
- host = (st->host != NULL) ? st->host : MEMCACHED_DEF_HOST;
- port = (st->port != NULL) ? st->port : MEMCACHED_DEF_PORT;
-
struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
.ai_flags = AI_ADDRCONFIG,
.ai_socktype = SOCK_STREAM};
- status = getaddrinfo(host, port, &ai_hints, &ai_list);
+ status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
if (status != 0) {
char errbuf[1024];
ERROR("memcached plugin: memcached_connect_inet: "
"getaddrinfo(%s,%s) failed: %s",
- host, port,
+ st->connhost, st->connport,
(status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
: gai_strerror(status));
return (-1);
} /* int memcached_query_daemon */
static void memcached_init_vl(value_list_t *vl, memcached_t const *st) {
- char const *host = st->host;
-
- /* Set vl->host to hostname_g, if:
- * - Legacy mode is used.
- * - "Socket" option is given (doc: "Host option is ignored").
- * - "Host" option is not provided.
- * - "Host" option is set to "localhost" or "127.0.0.1". */
- if ((strcmp(st->name, "__legacy__") == 0) || (st->socket != NULL) ||
- (st->host == NULL) || (strcmp("127.0.0.1", st->host) == 0) ||
- (strcmp("localhost", st->host) == 0))
- host = hostname_g;
-
sstrncpy(vl->plugin, "memcached", sizeof(vl->plugin));
- sstrncpy(vl->host, host, sizeof(vl->host));
- if (strcmp(st->name, "__legacy__") != 0)
+ if (st->host != NULL)
+ sstrncpy(vl->host, st->host, sizeof(vl->host));
+ if (st->name != NULL)
sstrncpy(vl->plugin_instance, st->name, sizeof(vl->plugin_instance));
}
static void submit_derive(const char *type, const char *type_inst,
derive_t value, memcached_t *st) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- memcached_init_vl(&vl, st);
-
- values[0].derive = value;
- vl.values = values;
+ memcached_init_vl(&vl, st);
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_inst != NULL)
static void submit_derive2(const char *type, const char *type_inst,
derive_t value0, derive_t value1, memcached_t *st) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
- memcached_init_vl(&vl, st);
-
- values[0].derive = value0;
- values[1].derive = value1;
+ value_t values[] = {
+ {.derive = value0}, {.derive = value1},
+ };
+ memcached_init_vl(&vl, st);
vl.values = values;
- vl.values_len = 2;
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_inst != NULL)
sstrncpy(vl.type_instance, type_inst, sizeof(vl.type_instance));
static void submit_gauge(const char *type, const char *type_inst, gauge_t value,
memcached_t *st) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- memcached_init_vl(&vl, st);
- values[0].gauge = value;
-
- vl.values = values;
+ memcached_init_vl(&vl, st);
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_inst != NULL)
static void submit_gauge2(const char *type, const char *type_inst,
gauge_t value0, gauge_t value1, memcached_t *st) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
- memcached_init_vl(&vl, st);
-
- values[0].gauge = value0;
- values[1].gauge = value1;
+ value_t values[] = {
+ {.gauge = value0}, {.gauge = value1},
+ };
+ memcached_init_vl(&vl, st);
vl.values = values;
- vl.values_len = 2;
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_inst != NULL)
sstrncpy(vl.type_instance, type_inst, sizeof(vl.type_instance));
char callback_name[3 * DATA_MAX_NAME_LEN];
int status;
- assert(st->name != NULL);
- ssnprintf(callback_name, sizeof(callback_name), "memcached/%s", st->name);
+ ssnprintf(callback_name, sizeof(callback_name), "memcached/%s",
+ (st->name != NULL) ? st->name : "__legacy__");
+
+ /* If no <Address> used then:
+ * - Connect to the destination specified by <Host>, if present.
+ * If not, use the default address.
+ * - Use the default hostname (set st->host to NULL), if
+ * - Legacy mode is used (no configuration options at all), or
+ * - "Host" option is not provided, or
+ * - "Host" option is set to "localhost" or "127.0.0.1".
+ *
+ * If <Address> used then host may be set to "localhost" or "127.0.0.1"
+ * explicitly.
+ */
+ if (st->connhost == NULL) {
+ if (st->host) {
+ st->connhost = strdup(st->host);
+ if (st->connhost == NULL)
+ return (ENOMEM);
+
+ if ((strcmp("127.0.0.1", st->host) == 0) ||
+ (strcmp("localhost", st->host) == 0))
+ sfree(st->host);
+ } else {
+ st->connhost = strdup(MEMCACHED_DEF_HOST);
+ if (st->connhost == NULL)
+ return (ENOMEM);
+ }
+ }
+
+ if (st->connport == NULL) {
+ st->connport = strdup(MEMCACHED_DEF_PORT);
+ if (st->connport == NULL)
+ return (ENOMEM);
+ }
+
+ assert(st->connhost != NULL);
+ assert(st->connport != NULL);
- user_data_t ud = {.data = st, .free_func = memcached_free};
+ status = plugin_register_complex_read(
+ /* group = */ "memcached",
+ /* name = */ callback_name,
+ /* callback = */ memcached_read,
+ /* interval = */ 0, &(user_data_t){
+ .data = st, .free_func = memcached_free,
+ });
- status = plugin_register_complex_read(/* group = */ "memcached",
- /* name = */ callback_name,
- /* callback = */ memcached_read,
- /* interval = */ 0,
- /* user_data = */ &ud);
return (status);
} /* int memcached_add_read_callback */
* <Plugin memcached>
* <Instance "instance_name">
* Host foo.zomg.com
+ * Address 1.2.3.4
* Port "1234"
* </Instance>
* </Plugin>
st = calloc(1, sizeof(*st));
if (st == NULL) {
ERROR("memcached plugin: calloc failed.");
- return (-1);
+ return (ENOMEM);
}
st->name = NULL;
- st->socket = NULL;
st->host = NULL;
- st->port = NULL;
+ st->socket = NULL;
+ st->connhost = NULL;
+ st->connport = NULL;
- if (strcasecmp(ci->key, "Plugin") == 0) /* default instance */
- st->name = sstrdup("__legacy__");
- else /* <Instance /> block */
+ if (strcasecmp(ci->key, "Instance") == 0)
status = cf_util_get_string(ci, &st->name);
+
if (status != 0) {
sfree(st);
return (status);
}
- assert(st->name != NULL);
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
status = cf_util_get_string(child, &st->socket);
else if (strcasecmp("Host", child->key) == 0)
status = cf_util_get_string(child, &st->host);
+ else if (strcasecmp("Address", child->key) == 0)
+ status = cf_util_get_string(child, &st->connhost);
else if (strcasecmp("Port", child->key) == 0)
- status = cf_util_get_service(child, &st->port);
+ status = cf_util_get_service(child, &st->connport);
else {
WARNING("memcached plugin: Option `%s' not allowed here.", child->key);
status = -1;
st = calloc(1, sizeof(*st));
if (st == NULL)
return (ENOMEM);
- st->name = sstrdup("__legacy__");
- st->socket = NULL;
+ st->name = NULL;
st->host = NULL;
- st->port = NULL;
+ st->socket = NULL;
+ st->connhost = NULL;
+ st->connport = NULL;
status = memcached_add_read_callback(st);
if (status == 0)
vl.values = v;
vl.values_len = STATIC_ARRAY_SIZE(v);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "memory", sizeof(vl.plugin));
sstrncpy(vl.type, "memory", sizeof(vl.type));
vl.time = cdtime();
}
static void mic_submit_memory_use(int micnumber, const char *type_instance,
- U32 val) {
- value_t values[1];
+ U32 value) {
value_list_t vl = VALUE_LIST_INIT;
/* MicAccessAPI reports KB's of memory, adjust for this */
- DEBUG("mic plugin: Memory Value Report; %u %lf", val,
- ((gauge_t)val) * 1024.0);
- values[0].gauge = ((gauge_t)val) * 1024.0;
+ DEBUG("mic plugin: Memory Value Report; %u %lf", value,
+ ((gauge_t)value) * 1024.0);
- vl.values = values;
+ vl.values = &(value_t){.gauge = ((gauge_t)value) * 1024.0};
vl.values_len = 1;
- strncpy(vl.host, hostname_g, sizeof(vl.host));
strncpy(vl.plugin, "mic", sizeof(vl.plugin));
ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber);
strncpy(vl.type, "memory", sizeof(vl.type));
return (0);
}
-static void mic_submit_temp(int micnumber, const char *type, gauge_t val) {
- value_t values[1];
+static void mic_submit_temp(int micnumber, const char *type, gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = val;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
strncpy(vl.host, hostname_g, sizeof(vl.host));
}
static void mic_submit_cpu(int micnumber, const char *type_instance, int core,
- derive_t val) {
- value_t values[1];
+ derive_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = val;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
strncpy(vl.host, hostname_g, sizeof(vl.host));
}
static void mic_submit_power(int micnumber, const char *type,
- const char *type_instance, gauge_t val) {
- value_t values[1];
+ const char *type_instance, gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = val;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
strncpy(vl.host, hostname_g, sizeof(vl.host));
if (host->connection == NULL) {
status = EBADF;
} else if (host->conntype == MBCONN_TCP) {
+ /* getpeername() is used only to determine if the socket is connected, not
+ * because we're really interested in the peer's IP address. */
if (getpeername(modbus_get_socket(host->connection),
(void *)&(struct sockaddr_storage){0},
&(socklen_t){sizeof(struct sockaddr_storage)}) != 0)
}
if (status == 0) {
- user_data_t ud;
char name[1024];
- ud.data = host;
- ud.free_func = host_free;
-
ssnprintf(name, sizeof(name), "modbus-%s", host->host);
plugin_register_complex_read(/* group = */ NULL, name,
/* callback = */ mb_read,
- /* interval = */ host->interval, &ud);
+ /* interval = */ host->interval,
+ &(user_data_t){
+ .data = host, .free_func = host_free,
+ });
} else {
host_free(host);
}
}
ssnprintf(cb_name, sizeof(cb_name), "mqtt/%s", conf->name);
- user_data_t user_data = {.data = conf};
-
- plugin_register_write(cb_name, mqtt_write, &user_data);
+ plugin_register_write(cb_name, mqtt_write, &(user_data_t){
+ .data = conf,
+ });
return (0);
} /* mqtt_config_publisher */
status = plugin_thread_create(&subscribers[i]->thread,
/* attrs = */ NULL,
/* func = */ subscribers_thread,
- /* args = */ subscribers[i]);
+ /* args = */ subscribers[i],
+ /* name = */ "mqtt");
if (status != 0) {
char errbuf[1024];
ERROR("mqtt plugin: pthread_create failed: %s",
#undef LINE_LENGTH
static void multimeter_submit(double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "multimeter", sizeof(vl.plugin));
sstrncpy(vl.type, "multimeter", sizeof(vl.type));
else
sstrncpy(cb_name, "mysql", sizeof(cb_name));
- user_data_t ud = {.data = db, .free_func = mysql_database_free};
-
- plugin_register_complex_read(/* group = */ NULL, cb_name, mysql_read,
- /* interval = */ 0, &ud);
+ plugin_register_complex_read(
+ /* group = */ NULL, cb_name, mysql_read, /* interval = */ 0,
+ &(user_data_t){
+ .data = db, .free_func = mysql_database_free,
+ });
} else {
mysql_database_free(db);
return (-1);
plugin_dispatch_values(&vl);
} /* submit */
-static void counter_submit(const char *type, const char *type_instance,
- derive_t value, mysql_database_t *db) {
- value_t values[1];
-
- values[0].derive = value;
- submit(type, type_instance, values, STATIC_ARRAY_SIZE(values), db);
-} /* void counter_submit */
-
static void gauge_submit(const char *type, const char *type_instance,
gauge_t value, mysql_database_t *db) {
- value_t values[1];
-
- values[0].gauge = value;
- submit(type, type_instance, values, STATIC_ARRAY_SIZE(values), db);
+ submit(type, type_instance, &(value_t){.gauge = value}, 1, db);
} /* void gauge_submit */
static void derive_submit(const char *type, const char *type_instance,
derive_t value, mysql_database_t *db) {
- value_t values[1];
-
- values[0].derive = value;
- submit(type, type_instance, values, STATIC_ARRAY_SIZE(values), db);
+ submit(type, type_instance, &(value_t){.derive = value}, 1, db);
} /* void derive_submit */
static void traffic_submit(derive_t rx, derive_t tx, mysql_database_t *db) {
- value_t values[2];
-
- values[0].derive = rx;
- values[1].derive = tx;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
submit("mysql_octets", NULL, values, STATIC_ARRAY_SIZE(values), db);
} /* void traffic_submit */
}
position = atoll(row[1]);
- counter_submit("mysql_log_position", "master-bin", position, db);
+ derive_submit("mysql_log_position", "master-bin", position, db);
row = mysql_fetch_row(res);
if (row != NULL)
double gauge;
counter = atoll(row[READ_MASTER_LOG_POS_IDX]);
- counter_submit("mysql_log_position", "slave-read", counter, db);
+ derive_submit("mysql_log_position", "slave-read", counter, db);
counter = atoll(row[EXEC_MASTER_LOG_POS_IDX]);
- counter_submit("mysql_log_position", "slave-exec", counter, db);
+ derive_submit("mysql_log_position", "slave-exec", counter, db);
if (row[SECONDS_BEHIND_MASTER_IDX] != NULL) {
gauge = atof(row[SECONDS_BEHIND_MASTER_IDX]);
switch (metrics[i].ds_type) {
case DS_TYPE_COUNTER:
- counter_submit(metrics[i].type, key, (counter_t)val, db);
+ derive_submit(metrics[i].type, key, (counter_t)val, db);
break;
case DS_TYPE_GAUGE:
gauge_submit(metrics[i].type, key, (gauge_t)val, db);
/* Ignore `prepared statements' */
if (strncmp(key, "Com_stmt_", strlen("Com_stmt_")) != 0)
- counter_submit("mysql_commands", key + strlen("Com_"), val, db);
+ derive_submit("mysql_commands", key + strlen("Com_"), val, db);
} else if (strncmp(key, "Handler_", strlen("Handler_")) == 0) {
if (val == 0ULL)
continue;
- counter_submit("mysql_handler", key + strlen("Handler_"), val, db);
+ derive_submit("mysql_handler", key + strlen("Handler_"), val, db);
} else if (strncmp(key, "Qcache_", strlen("Qcache_")) == 0) {
if (strcmp(key, "Qcache_hits") == 0)
qcache_hits = (derive_t)val;
else if (strcmp(key, "Threads_created") == 0)
threads_created = (derive_t)val;
} else if (strncmp(key, "Table_locks_", strlen("Table_locks_")) == 0) {
- counter_submit("mysql_locks", key + strlen("Table_locks_"), val, db);
+ derive_submit("mysql_locks", key + strlen("Table_locks_"), val, db);
} else if (db->innodb_stats &&
strncmp(key, "Innodb_", strlen("Innodb_")) == 0) {
/* buffer pool */
else if (strcmp(key, "Innodb_buffer_pool_pages_dirty") == 0)
gauge_submit("mysql_bpool_pages", "dirty", val, db);
else if (strcmp(key, "Innodb_buffer_pool_pages_flushed") == 0)
- counter_submit("mysql_bpool_counters", "pages_flushed", val, db);
+ derive_submit("mysql_bpool_counters", "pages_flushed", val, db);
else if (strcmp(key, "Innodb_buffer_pool_pages_free") == 0)
gauge_submit("mysql_bpool_pages", "free", val, db);
else if (strcmp(key, "Innodb_buffer_pool_pages_misc") == 0)
else if (strcmp(key, "Innodb_buffer_pool_pages_total") == 0)
gauge_submit("mysql_bpool_pages", "total", val, db);
else if (strcmp(key, "Innodb_buffer_pool_read_ahead_rnd") == 0)
- counter_submit("mysql_bpool_counters", "read_ahead_rnd", val, db);
+ derive_submit("mysql_bpool_counters", "read_ahead_rnd", val, db);
else if (strcmp(key, "Innodb_buffer_pool_read_ahead") == 0)
- counter_submit("mysql_bpool_counters", "read_ahead", val, db);
+ derive_submit("mysql_bpool_counters", "read_ahead", val, db);
else if (strcmp(key, "Innodb_buffer_pool_read_ahead_evicted") == 0)
- counter_submit("mysql_bpool_counters", "read_ahead_evicted", val, db);
+ derive_submit("mysql_bpool_counters", "read_ahead_evicted", val, db);
else if (strcmp(key, "Innodb_buffer_pool_read_requests") == 0)
- counter_submit("mysql_bpool_counters", "read_requests", val, db);
+ derive_submit("mysql_bpool_counters", "read_requests", val, db);
else if (strcmp(key, "Innodb_buffer_pool_reads") == 0)
- counter_submit("mysql_bpool_counters", "reads", val, db);
+ derive_submit("mysql_bpool_counters", "reads", val, db);
else if (strcmp(key, "Innodb_buffer_pool_wait_free") == 0)
- counter_submit("mysql_bpool_counters", "wait_free", val, db);
+ derive_submit("mysql_bpool_counters", "wait_free", val, db);
else if (strcmp(key, "Innodb_buffer_pool_write_requests") == 0)
- counter_submit("mysql_bpool_counters", "write_requests", val, db);
+ derive_submit("mysql_bpool_counters", "write_requests", val, db);
else if (strcmp(key, "Innodb_buffer_pool_bytes_data") == 0)
gauge_submit("mysql_bpool_bytes", "data", val, db);
else if (strcmp(key, "Innodb_buffer_pool_bytes_dirty") == 0)
/* data */
if (strcmp(key, "Innodb_data_fsyncs") == 0)
- counter_submit("mysql_innodb_data", "fsyncs", val, db);
+ derive_submit("mysql_innodb_data", "fsyncs", val, db);
else if (strcmp(key, "Innodb_data_read") == 0)
- counter_submit("mysql_innodb_data", "read", val, db);
+ derive_submit("mysql_innodb_data", "read", val, db);
else if (strcmp(key, "Innodb_data_reads") == 0)
- counter_submit("mysql_innodb_data", "reads", val, db);
+ derive_submit("mysql_innodb_data", "reads", val, db);
else if (strcmp(key, "Innodb_data_writes") == 0)
- counter_submit("mysql_innodb_data", "writes", val, db);
+ derive_submit("mysql_innodb_data", "writes", val, db);
else if (strcmp(key, "Innodb_data_written") == 0)
- counter_submit("mysql_innodb_data", "written", val, db);
+ derive_submit("mysql_innodb_data", "written", val, db);
/* double write */
else if (strcmp(key, "Innodb_dblwr_writes") == 0)
- counter_submit("mysql_innodb_dblwr", "writes", val, db);
+ derive_submit("mysql_innodb_dblwr", "writes", val, db);
else if (strcmp(key, "Innodb_dblwr_pages_written") == 0)
- counter_submit("mysql_innodb_dblwr", "written", val, db);
+ derive_submit("mysql_innodb_dblwr", "written", val, db);
else if (strcmp(key, "Innodb_dblwr_page_size") == 0)
gauge_submit("mysql_innodb_dblwr", "page_size", val, db);
/* log */
else if (strcmp(key, "Innodb_log_waits") == 0)
- counter_submit("mysql_innodb_log", "waits", val, db);
+ derive_submit("mysql_innodb_log", "waits", val, db);
else if (strcmp(key, "Innodb_log_write_requests") == 0)
- counter_submit("mysql_innodb_log", "write_requests", val, db);
+ derive_submit("mysql_innodb_log", "write_requests", val, db);
else if (strcmp(key, "Innodb_log_writes") == 0)
- counter_submit("mysql_innodb_log", "writes", val, db);
+ derive_submit("mysql_innodb_log", "writes", val, db);
else if (strcmp(key, "Innodb_os_log_fsyncs") == 0)
- counter_submit("mysql_innodb_log", "fsyncs", val, db);
+ derive_submit("mysql_innodb_log", "fsyncs", val, db);
else if (strcmp(key, "Innodb_os_log_written") == 0)
- counter_submit("mysql_innodb_log", "written", val, db);
+ derive_submit("mysql_innodb_log", "written", val, db);
/* pages */
else if (strcmp(key, "Innodb_pages_created") == 0)
- counter_submit("mysql_innodb_pages", "created", val, db);
+ derive_submit("mysql_innodb_pages", "created", val, db);
else if (strcmp(key, "Innodb_pages_read") == 0)
- counter_submit("mysql_innodb_pages", "read", val, db);
+ derive_submit("mysql_innodb_pages", "read", val, db);
else if (strcmp(key, "Innodb_pages_written") == 0)
- counter_submit("mysql_innodb_pages", "written", val, db);
+ derive_submit("mysql_innodb_pages", "written", val, db);
/* row lock */
else if (strcmp(key, "Innodb_row_lock_time") == 0)
- counter_submit("mysql_innodb_row_lock", "time", val, db);
+ derive_submit("mysql_innodb_row_lock", "time", val, db);
else if (strcmp(key, "Innodb_row_lock_waits") == 0)
- counter_submit("mysql_innodb_row_lock", "waits", val, db);
+ derive_submit("mysql_innodb_row_lock", "waits", val, db);
/* rows */
else if (strcmp(key, "Innodb_rows_deleted") == 0)
- counter_submit("mysql_innodb_rows", "deleted", val, db);
+ derive_submit("mysql_innodb_rows", "deleted", val, db);
else if (strcmp(key, "Innodb_rows_inserted") == 0)
- counter_submit("mysql_innodb_rows", "inserted", val, db);
+ derive_submit("mysql_innodb_rows", "inserted", val, db);
else if (strcmp(key, "Innodb_rows_read") == 0)
- counter_submit("mysql_innodb_rows", "read", val, db);
+ derive_submit("mysql_innodb_rows", "read", val, db);
else if (strcmp(key, "Innodb_rows_updated") == 0)
- counter_submit("mysql_innodb_rows", "updated", val, db);
+ derive_submit("mysql_innodb_rows", "updated", val, db);
} else if (strncmp(key, "Select_", strlen("Select_")) == 0) {
- counter_submit("mysql_select", key + strlen("Select_"), val, db);
+ derive_submit("mysql_select", key + strlen("Select_"), val, db);
} else if (strncmp(key, "Sort_", strlen("Sort_")) == 0) {
if (strcmp(key, "Sort_merge_passes") == 0)
- counter_submit("mysql_sort_merge_passes", NULL, val, db);
+ derive_submit("mysql_sort_merge_passes", NULL, val, db);
else if (strcmp(key, "Sort_rows") == 0)
- counter_submit("mysql_sort_rows", NULL, val, db);
+ derive_submit("mysql_sort_rows", NULL, val, db);
else if (strcmp(key, "Sort_range") == 0)
- counter_submit("mysql_sort", "range", val, db);
+ derive_submit("mysql_sort", "range", val, db);
else if (strcmp(key, "Sort_scan") == 0)
- counter_submit("mysql_sort", "scan", val, db);
+ derive_submit("mysql_sort", "scan", val, db);
} else if (strncmp(key, "Slow_queries", strlen("Slow_queries")) == 0) {
- counter_submit("mysql_slow_queries", NULL, val, db);
+ derive_submit("mysql_slow_queries", NULL, val, db);
}
}
mysql_free_result(res);
*/
static int submit_values(const char *host, /* {{{ */
const char *plugin_inst, const char *type,
- const char *type_inst, value_t *values, int values_len,
- cdtime_t timestamp, cdtime_t interval) {
+ const char *type_inst, value_t *values,
+ size_t values_len, cdtime_t timestamp,
+ cdtime_t interval) {
value_list_t vl = VALUE_LIST_INIT;
vl.values = values;
if (host != NULL)
sstrncpy(vl.host, host, sizeof(vl.host));
- else
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "netapp", sizeof(vl.plugin));
if (plugin_inst != NULL)
sstrncpy(vl.plugin_instance, plugin_inst, sizeof(vl.plugin_instance));
const char *type, const char *type_inst,
derive_t val0, derive_t val1, cdtime_t timestamp,
cdtime_t interval) {
- value_t values[2];
-
- values[0].derive = val0;
- values[1].derive = val1;
+ value_t values[] = {
+ {.derive = val0}, {.derive = val1},
+ };
- return (submit_values(host, plugin_inst, type, type_inst, values, 2,
- timestamp, interval));
+ return (submit_values(host, plugin_inst, type, type_inst, values,
+ STATIC_ARRAY_SIZE(values), timestamp, interval));
} /* }}} int submit_two_derive */
static int submit_derive(const char *host, const char *plugin_inst, /* {{{ */
const char *type, const char *type_inst,
derive_t counter, cdtime_t timestamp,
cdtime_t interval) {
- value_t v;
-
- v.derive = counter;
-
- return (submit_values(host, plugin_inst, type, type_inst, &v, 1, timestamp,
- interval));
+ return (submit_values(host, plugin_inst, type, type_inst,
+ &(value_t){.derive = counter}, 1, timestamp, interval));
} /* }}} int submit_derive */
static int submit_two_gauge(const char *host, const char *plugin_inst, /* {{{ */
const char *type, const char *type_inst,
gauge_t val0, gauge_t val1, cdtime_t timestamp,
cdtime_t interval) {
- value_t values[2];
+ value_t values[] = {
+ {.gauge = val0}, {.gauge = val1},
+ };
- values[0].gauge = val0;
- values[1].gauge = val1;
-
- return (submit_values(host, plugin_inst, type, type_inst, values, 2,
- timestamp, interval));
+ return (submit_values(host, plugin_inst, type, type_inst, values,
+ STATIC_ARRAY_SIZE(values), timestamp, interval));
} /* }}} int submit_two_gauge */
static int submit_double(const char *host, const char *plugin_inst, /* {{{ */
const char *type, const char *type_inst, double d,
cdtime_t timestamp, cdtime_t interval) {
- value_t v;
-
- v.gauge = (gauge_t)d;
-
- return (submit_values(host, plugin_inst, type, type_inst, &v, 1, timestamp,
- interval));
+ return (submit_values(host, plugin_inst, type, type_inst,
+ &(value_t){.gauge = d}, 1, timestamp, interval));
} /* }}} int submit_uint64 */
/* Calculate hit ratio from old and new counters and submit the resulting
uint64_t new_hits, uint64_t new_misses,
uint64_t old_hits, uint64_t old_misses,
cdtime_t timestamp, cdtime_t interval) {
- value_t v;
+ value_t v = {.gauge = NAN};
if ((new_hits >= old_hits) && (new_misses >= old_misses)) {
uint64_t hits;
misses = new_misses - old_misses;
v.gauge = 100.0 * ((gauge_t)hits) / ((gauge_t)(hits + misses));
- } else {
- v.gauge = NAN;
}
return (submit_values(host, plugin_inst, "cache_ratio", type_inst, &v, 1,
static int cna_handle_snapvault_data(const char *hostname, /* {{{ */
cfg_snapvault_t *cfg_snapvault,
na_elem_t *data, cdtime_t interval) {
- na_elem_iter_t status_iter;
-
- status = na_elem_child(data, "status-list");
- if (!status) {
+ na_elem_t *status_list = na_elem_child(data, "status-list");
+ if (status_list == NULL) {
ERROR("netapp plugin: SnapVault status record missing status-list");
return (0);
}
- status_iter = na_child_iterator(status);
+ na_elem_iter_t status_iter = na_child_iterator(status_list);
for (na_elem_t *status = na_iterator_next(&status_iter); status != NULL;
status = na_iterator_next(&status_iter)) {
const char *dest_sys, *dest_path, *src_sys, *src_path;
else
ssnprintf(cb_name, sizeof(cb_name), "netapp-%s", host->name);
- user_data_t ud = {.data = host,
- .free_func = (void (*)(void *))free_host_config};
-
- plugin_register_complex_read(/* group = */ NULL, cb_name,
- /* callback = */ cna_read,
- /* interval = */ host->interval,
- /* user data = */ &ud);
+ plugin_register_complex_read(
+ /* group = */ NULL, cb_name,
+ /* callback = */ cna_read,
+ /* interval = */ host->interval,
+ &(user_data_t){
+ .data = host, .free_func = (void *)free_host_config,
+ });
return (0);
} /* }}} int cna_register_host */
static void submit_one(const char *dev, const char *type,
const char *type_instance, derive_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = value;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "netlink", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
static void submit_two(const char *dev, const char *type,
const char *type_instance, derive_t rx, derive_t tx) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = rx;
- values[1].derive = tx;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "netlink", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
static char *send_buffer_ptr;
static int send_buffer_fill;
static cdtime_t send_buffer_last_update;
-static value_list_t send_buffer_vl = VALUE_LIST_STATIC;
+static value_list_t send_buffer_vl = VALUE_LIST_INIT;
static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
/* XXX: These counters are incremented from one place only. The spot in which
vl.values = values;
vl.values_len = 2;
vl.time = 0;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "network", sizeof(vl.plugin));
/* Octets received / sent */
if (dispatch_thread_running == 0) {
int status;
status = plugin_thread_create(&dispatch_thread_id, NULL /* no attributes */,
- dispatch_thread, NULL /* no argument */);
+ dispatch_thread, NULL /* no argument */,
+ "network disp");
if (status != 0) {
char errbuf[1024];
ERROR("network: pthread_create failed: %s",
if (receive_thread_running == 0) {
int status;
status = plugin_thread_create(&receive_thread_id, NULL /* no attributes */,
- receive_thread, NULL /* no argument */);
+ receive_thread, NULL /* no argument */,
+ "network recv");
if (status != 0) {
char errbuf[1024];
ERROR("network: pthread_create failed: %s",
value_list_t vl = VALUE_LIST_INIT;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "nfs", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
return;
vl.values = values;
- vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "nginx", sizeof(vl.plugin));
- sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
if (inst != NULL)
/*
* Active connections: 291
* server accepts handled requests
- * 16630948 16630948 31070465
+ * 101059015 100422216 347910649
* Reading: 6 Writing: 179 Waiting: 106
*/
for (int i = 0; i < lines_num; i++) {
} else if ((atoll(fields[0]) != 0) && (atoll(fields[1]) != 0) &&
(atoll(fields[2]) != 0)) {
submit("connections", "accepted", atoll(fields[0]));
+ /* TODO: The legacy metric "handled", which is the sum of "accepted" and
+ * "failed", is reported for backwards compatibility only. Remove in the
+ * next major version. */
submit("connections", "handled", atoll(fields[1]));
+ submit("connections", "failed", (atoll(fields[0]) - atoll(fields[1])));
submit("nginx_requests", NULL, atoll(fields[2]));
}
} else if (fields_num == 6) {
user_data_t __attribute__((unused)) *
user_data) {
- time_t tt;
struct tm timestamp_tm;
char timestamp_str[64];
(email_subject == NULL) ? DEFAULT_SMTP_SUBJECT : email_subject,
severity, n->host);
- tt = CDTIME_T_TO_TIME_T(n->time);
- localtime_r(&tt, ×tamp_tm);
+ localtime_r(&CDTIME_T_TO_TIME_T(n->time), ×tamp_tm);
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S",
×tamp_tm);
timestamp_str[sizeof(timestamp_str) - 1] = '\0';
static void ntpd_submit(const char *type, const char *type_inst,
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ntpd", sizeof(vl.plugin));
- sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_inst, sizeof(vl.type_instance));
vl.values = &v;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "numa", sizeof(vl.plugin));
ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "node%i", node);
sstrncpy(vl.type, "vmpage_action", sizeof(vl.type));
static void nut_submit(nut_ups_t *ups, const char *type,
const char *type_instance, gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
- vl.values_len = STATIC_ARRAY_SIZE(values);
+ vl.values = &(value_t){.gauge = value};
+ vl.values_len = 1;
sstrncpy(vl.host,
(strcasecmp(ups->hostname, "localhost") == 0) ? hostname_g
: ups->hostname,
__attribute__((nonnull(2))) static void
olsrd_submit(const char *plugin_instance, /* {{{ */
const char *type, const char *type_instance, gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "olsrd", sizeof(vl.plugin));
if (plugin_instance != NULL)
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
static int cow_read_values(const char *path, const char *name,
const ow_family_features_t *family_info) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
int success = 0;
return 0;
}
- vl.values = values;
- vl.values_len = 1;
-
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "onewire", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, name, sizeof(vl.plugin_instance));
char *buffer;
size_t buffer_size;
int status;
+ char errbuf[1024];
char file[4096];
char *endptr;
DEBUG("Start reading onewire device %s", file);
status = OW_get(file, &buffer, &buffer_size);
if (status < 0) {
- ERROR("onewire plugin: OW_get (%s/%s) failed. status = %#x;", path,
- family_info->features[i].filename, status);
+ ERROR("onewire plugin: OW_get (%s/%s) failed. error = %s;", path,
+ family_info->features[i].filename,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
return (-1);
}
DEBUG("Read onewire device %s as %s", file, buffer);
endptr = NULL;
- values[0].gauge = strtod(buffer, &endptr);
+ gauge_t g = strtod(buffer, &endptr);
if (endptr == NULL) {
ERROR("onewire plugin: Buffer is not a number: %s", buffer);
continue;
sstrncpy(vl.type_instance, family_info->features[i].type_instance,
sizeof(vl.type_instance));
+ vl.values = &(value_t){.gauge = g};
+ vl.values_len = 1;
+
plugin_dispatch_values(&vl);
success++;
char *buffer;
size_t buffer_size;
int status;
+ char errbuf[1024];
char *buffer_ptr;
char *dummy;
status = OW_get(path, &buffer, &buffer_size);
if (status < 0) {
- ERROR("onewire plugin: OW_get (%s) failed. status = %#x;", path, status);
+ ERROR("onewire plugin: OW_get (%s) failed. error = %s;", path,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
return (-1);
}
DEBUG("onewire plugin: OW_get (%s) returned: %s", path, buffer);
*/
static int cow_simple_read(void) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
char *buffer;
size_t buffer_size;
int status;
+ char errbuf[1024];
char *endptr;
direct_access_element_t *traverse;
/* traverse list and check entries */
for (traverse = direct_list; traverse != NULL; traverse = traverse->next) {
- vl.values = values;
- vl.values_len = 1;
-
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "onewire", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, traverse->address, sizeof(vl.plugin_instance));
status = OW_get(traverse->path, &buffer, &buffer_size);
if (status < 0) {
- ERROR("onewire plugin: OW_get (%s) failed. status = %#x;", traverse->path,
- status);
+ ERROR("onewire plugin: OW_get (%s) failed. status = %s;", traverse->path,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
return (-1);
}
DEBUG("onewire plugin: Read onewire device %s as %s", traverse->path,
buffer);
endptr = NULL;
- values[0].gauge = strtod(buffer, &endptr);
+ gauge_t g = strtod(buffer, &endptr);
if (endptr == NULL) {
ERROR("onewire plugin: Buffer is not a number: %s", buffer);
continue;
sstrncpy(vl.type, traverse->file, sizeof(vl.type));
sstrncpy(vl.type_instance, "", sizeof(""));
+ vl.values = &(value_t){.gauge = g};
+ vl.values_len = 1;
+
plugin_dispatch_values(&vl);
free(buffer);
} /* for (traverse) */
static int cow_init(void) {
int status;
+ char errbuf[1024];
if (device_g == NULL) {
ERROR("onewire plugin: cow_init: No device configured.");
DEBUG("onewire plugin: about to init device <%s>.", device_g);
status = (int)OW_init(device_g);
if (status != 0) {
- ERROR("onewire plugin: OW_init(%s) failed: %i.", device_g, status);
+ ERROR("onewire plugin: OW_init(%s) failed: %s.", device_g,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
return (1);
}
vl.values = &value;
vl.values_len = 1;
- if ((st->host == NULL) || (strcmp("", st->host) == 0) ||
- (strcmp("localhost", st->host) == 0))
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
- else
+ if ((st->host != NULL) && (strcmp("localhost", st->host) != 0))
sstrncpy(vl.host, st->host, sizeof(vl.host));
sstrncpy(vl.plugin, "openldap", sizeof(vl.plugin));
static void cldap_submit_derive(const char *type,
const char *type_instance, /* {{{ */
derive_t d, cldap_t *st) {
- value_t v;
- v.derive = d;
- cldap_submit_value(type, type_instance, v, st);
+ cldap_submit_value(type, type_instance, (value_t){.derive = d}, st);
} /* }}} void cldap_submit_derive */
static void cldap_submit_gauge(const char *type,
const char *type_instance, /* {{{ */
gauge_t g, cldap_t *st) {
- value_t v;
- v.gauge = g;
- cldap_submit_value(type, type_instance, v, st);
+ cldap_submit_value(type, type_instance, (value_t){.gauge = g}, st);
} /* }}} void cldap_submit_gauge */
static int cldap_read_host(user_data_t *ud) /* {{{ */
}
st->starttls = 0;
- st->timeout = (long)(CDTIME_T_TO_MS(plugin_get_interval()) / 1000);
+ st->timeout = (long)CDTIME_T_TO_TIME_T(plugin_get_interval());
st->verifyhost = 1;
st->version = LDAP_VERSION3;
(st->host != NULL) ? st->host : hostname_g,
(st->name != NULL) ? st->name : "default");
- user_data_t ud = {.data = st};
-
status = plugin_register_complex_read(/* group = */ NULL,
/* name = */ callback_name,
/* callback = */ cldap_read_host,
- /* interval = */ 0,
- /* user_data = */ &ud);
+ /* interval = */ 0, &(user_data_t){
+ .data = st,
+ });
}
}
/* dispatches number of users */
static void numusers_submit(const char *pinst, const char *tinst,
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
- vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values = &(value_t){.gauge = value};
+ vl.values_len = 1;
sstrncpy(vl.plugin, "openvpn", sizeof(vl.plugin));
sstrncpy(vl.type, "users", sizeof(vl.type));
if (pinst != NULL)
* per single endpoint */
static void iostats_submit(const char *pinst, const char *tinst, derive_t rx,
derive_t tx) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = rx;
- values[1].derive = tx;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
/* NOTE ON THE NEW NAMING SCHEMA:
* using plugin_instance to identify each vpn config (and
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "openvpn", sizeof(vl.plugin));
if (pinst != NULL)
sstrncpy(vl.plugin_instance, pinst, sizeof(vl.plugin_instance));
/* dispatches stats about data compression shown when in single mode */
static void compression_submit(const char *pinst, const char *tinst,
derive_t uncompressed, derive_t compressed) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = uncompressed;
- values[1].derive = compressed;
+ value_t values[] = {
+ {.derive = uncompressed}, {.derive = compressed},
+ };
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "openvpn", sizeof(vl.plugin));
if (pinst != NULL)
sstrncpy(vl.plugin_instance, pinst, sizeof(vl.plugin_instance));
*
* Authors:
* Sebastian Harl <sh at tokkee.org>
+ * Pavel Rochnyak <pavel2000 ngs.ru>
**/
/*
#define PLUGIN_LOG 4
#define PLUGIN_NOTIF 5
#define PLUGIN_FLUSH 6
+#define PLUGIN_FLUSH_ALL 7 /* For collectd-5.6 only */
-#define PLUGIN_TYPES 7
+#define PLUGIN_TYPES 8
#define PLUGIN_CONFIG 254
#define PLUGIN_DATASET 255
/* this is defined in DynaLoader.a */
void boot_DynaLoader(PerlInterpreter *, CV *);
+static XS(Collectd_plugin_register_read);
+static XS(Collectd_plugin_register_write);
+static XS(Collectd_plugin_register_log);
+static XS(Collectd_plugin_register_notification);
+static XS(Collectd_plugin_register_flush);
+static XS(Collectd_plugin_unregister_read);
+static XS(Collectd_plugin_unregister_write);
+static XS(Collectd_plugin_unregister_log);
+static XS(Collectd_plugin_unregister_notification);
+static XS(Collectd_plugin_unregister_flush);
static XS(Collectd_plugin_register_ds);
static XS(Collectd_plugin_unregister_ds);
static XS(Collectd_plugin_dispatch_values);
static XS(Collectd__fc_register);
static XS(Collectd_call_by_name);
+static int perl_read(user_data_t *ud);
+static int perl_write(const data_set_t *ds, const value_list_t *vl,
+ user_data_t *user_data);
+static void perl_log(int level, const char *msg, user_data_t *user_data);
+static int perl_notify(const notification_t *notif, user_data_t *user_data);
+static int perl_flush(cdtime_t timeout, const char *identifier,
+ user_data_t *user_data);
+
/*
* private data types
*/
* private variables
*/
+static _Bool register_legacy_flush = 1;
+
/* if perl_threads != NULL perl_threads->head must
* point to the "base" thread */
static c_ithread_list_t *perl_threads = NULL;
char name[64];
XS((*f));
} api[] = {
+ {"Collectd::plugin_register_read", Collectd_plugin_register_read},
+ {"Collectd::plugin_register_write", Collectd_plugin_register_write},
+ {"Collectd::plugin_register_log", Collectd_plugin_register_log},
+ {"Collectd::plugin_register_notification",
+ Collectd_plugin_register_notification},
+ {"Collectd::plugin_register_flush", Collectd_plugin_register_flush},
+ {"Collectd::plugin_unregister_read", Collectd_plugin_unregister_read},
+ {"Collectd::plugin_unregister_write", Collectd_plugin_unregister_write},
+ {"Collectd::plugin_unregister_log", Collectd_plugin_unregister_log},
+ {"Collectd::plugin_unregister_notification",
+ Collectd_plugin_unregister_notification},
+ {"Collectd::plugin_unregister_flush", Collectd_plugin_unregister_flush},
{"Collectd::plugin_register_data_set", Collectd_plugin_register_ds},
{"Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds},
{"Collectd::plugin_dispatch_values", Collectd_plugin_dispatch_values},
return 0;
}
- ret = call_pv(sub_name, G_SCALAR);
+ ret = call_pv(sub_name, G_SCALAR | G_EVAL);
t->running = old_running;
return ret;
/*
* Call all working functions of the given type.
*/
-static int pplugin_call_all(pTHX_ int type, ...) {
+static int pplugin_call(pTHX_ int type, ...) {
int retvals = 0;
va_list ap;
int ret = 0;
+ char *subname;
dSP;
PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSViv((IV)type)));
+ if (PLUGIN_READ == type) {
+ subname = va_arg(ap, char *);
+ } else if (PLUGIN_WRITE == type) {
+ data_set_t *ds;
+ value_list_t *vl;
- if (PLUGIN_WRITE == type) {
+ AV *pds = newAV();
+ HV *pvl = newHV();
+
+ subname = va_arg(ap, char *);
/*
* $_[0] = $plugin_type;
*
* type_instance => $type_instance
* };
*/
- data_set_t *ds;
- value_list_t *vl;
-
- AV *pds = newAV();
- HV *pvl = newHV();
-
ds = va_arg(ap, data_set_t *);
vl = va_arg(ap, value_list_t *);
XPUSHs(sv_2mortal(newRV_noinc((SV *)pds)));
XPUSHs(sv_2mortal(newRV_noinc((SV *)pvl)));
} else if (PLUGIN_LOG == type) {
+ subname = va_arg(ap, char *);
/*
* $_[0] = $level;
*
XPUSHs(sv_2mortal(newSViv(va_arg(ap, int))));
XPUSHs(sv_2mortal(newSVpv(va_arg(ap, char *), 0)));
} else if (PLUGIN_NOTIF == type) {
+ notification_t *n;
+ HV *notif = newHV();
+
+ subname = va_arg(ap, char *);
/*
* $_[0] =
* {
* type_instance => $type_instance
* };
*/
- notification_t *n;
- HV *notif = newHV();
-
n = va_arg(ap, notification_t *);
if (-1 == notification2hv(aTHX_ n, notif)) {
XPUSHs(sv_2mortal(newRV_noinc((SV *)notif)));
} else if (PLUGIN_FLUSH == type) {
cdtime_t timeout;
+ subname = va_arg(ap, char *);
+ /*
+ * $_[0] = $timeout;
+ * $_[1] = $identifier;
+ */
+ timeout = va_arg(ap, cdtime_t);
+ XPUSHs(sv_2mortal(newSVnv(CDTIME_T_TO_DOUBLE(timeout))));
+ XPUSHs(sv_2mortal(newSVpv(va_arg(ap, char *), 0)));
+ } else if (PLUGIN_FLUSH_ALL == type) {
+ cdtime_t timeout;
+ subname = "Collectd::plugin_call_all";
/*
* $_[0] = $timeout;
* $_[1] = $identifier;
*/
timeout = va_arg(ap, cdtime_t);
+ XPUSHs(sv_2mortal(newSViv((IV)PLUGIN_FLUSH)));
XPUSHs(sv_2mortal(newSVnv(CDTIME_T_TO_DOUBLE(timeout))));
XPUSHs(sv_2mortal(newSVpv(va_arg(ap, char *), 0)));
+ } else if (PLUGIN_INIT == type) {
+ subname = "Collectd::plugin_call_all";
+ XPUSHs(sv_2mortal(newSViv((IV)type)));
+ } else if (PLUGIN_SHUTDOWN == type) {
+ subname = "Collectd::plugin_call_all";
+ XPUSHs(sv_2mortal(newSViv((IV)type)));
+ } else { /* Unknown type. Run 'plugin_call_all' and make compiler happy */
+ subname = "Collectd::plugin_call_all";
+ XPUSHs(sv_2mortal(newSViv((IV)type)));
}
PUTBACK;
- retvals = call_pv_locked(aTHX_ "Collectd::plugin_call_all");
+ retvals = call_pv_locked(aTHX_ subname);
SPAGAIN;
- if (0 < retvals) {
+ if (SvTRUE(ERRSV)) {
+ if (PLUGIN_LOG != type)
+ ERROR("perl: %s error: %s", subname, SvPV_nolen(ERRSV));
+ ret = -1;
+ } else if (0 < retvals) {
SV *tmp = POPs;
if (!SvTRUE(tmp))
ret = -1;
va_end(ap);
return ret;
-} /* static int pplugin_call_all (int, ...) */
+} /* static int pplugin_call (int, ...) */
/*
* collectd's Perl interpreter based thread implementation.
assert(NULL != perl_threads);
PERL_SET_CONTEXT(aTHX);
+ /* Mark as running to avoid deadlock:
+ c_ithread_destroy -> log_debug -> perl_log()
+ */
+ ithread->running = 1;
log_debug("Shutting down Perl interpreter %p...", aTHX);
#if COLLECT_DEBUG
}
SPAGAIN;
- if (0 < retvals) {
+ if (SvTRUE(ERRSV)) {
+ ERROR("perl: Collectd::fc_call error: %s", SvPV_nolen(ERRSV));
+ ret = -1;
+ } else if (0 < retvals) {
SV *tmp = POPs;
/* the exec callbacks return a status, while
* Exported Perl API.
*/
+static void _plugin_register_generic_userdata(pTHX, int type,
+ const char *desc) {
+ int ret = 0;
+ user_data_t userdata;
+ char *pluginname;
+
+ dXSARGS;
+
+ if (2 != items) {
+ log_err("Usage: Collectd::plugin_register_%s(pluginname, subname)", desc);
+ XSRETURN_EMPTY;
+ }
+
+ if (!SvOK(ST(0))) {
+ log_err("Collectd::plugin_register_%s(pluginname, subname): "
+ "Invalid pluginname",
+ desc);
+ XSRETURN_EMPTY;
+ }
+ if (!SvOK(ST(1))) {
+ log_err("Collectd::plugin_register_%s(pluginname, subname): "
+ "Invalid subname",
+ desc);
+ XSRETURN_EMPTY;
+ }
+
+ /* Use pluginname as-is to allow flush a single perl plugin */
+ pluginname = SvPV_nolen(ST(0));
+
+ log_debug("Collectd::plugin_register_%s: "
+ "plugin = \"%s\", sub = \"%s\"",
+ desc, pluginname, SvPV_nolen(ST(1)));
+
+ memset(&userdata, 0, sizeof(userdata));
+ userdata.data = strdup(SvPV_nolen(ST(1)));
+ userdata.free_func = free;
+
+ if (PLUGIN_READ == type) {
+ ret = plugin_register_complex_read(
+ "perl", /* group */
+ pluginname, perl_read, plugin_get_interval(), /* Default interval */
+ &userdata);
+ } else if (PLUGIN_WRITE == type) {
+ ret = plugin_register_write(pluginname, perl_write, &userdata);
+ } else if (PLUGIN_LOG == type) {
+ ret = plugin_register_log(pluginname, perl_log, &userdata);
+ } else if (PLUGIN_NOTIF == type) {
+ ret = plugin_register_notification(pluginname, perl_notify, &userdata);
+ } else if (PLUGIN_FLUSH == type) {
+ if (1 == register_legacy_flush) { /* For collectd-5.7 only, #1731 */
+ register_legacy_flush = 0;
+ ret = plugin_register_flush("perl", perl_flush, /* user_data = */ NULL);
+ }
+
+ if (0 == ret)
+ ret = plugin_register_flush(pluginname, perl_flush, &userdata);
+ } else {
+ ret = -1;
+ }
+
+ if (0 == ret)
+ XSRETURN_YES;
+ else {
+ free(userdata.data);
+ XSRETURN_EMPTY;
+ }
+} /* static void _plugin_register_generic_userdata ( ... ) */
+
+/*
+ * Collectd::plugin_register_TYPE (pluginname, subname).
+ *
+ * pluginname:
+ * name of the perl plugin
+ *
+ * subname:
+ * name of the plugin's subroutine that does the work
+ */
+
+static XS(Collectd_plugin_register_read) {
+ return _plugin_register_generic_userdata(aTHX, PLUGIN_READ, "read");
+}
+
+static XS(Collectd_plugin_register_write) {
+ return _plugin_register_generic_userdata(aTHX, PLUGIN_WRITE, "write");
+}
+
+static XS(Collectd_plugin_register_log) {
+ return _plugin_register_generic_userdata(aTHX, PLUGIN_LOG, "log");
+}
+
+static XS(Collectd_plugin_register_notification) {
+ return _plugin_register_generic_userdata(aTHX, PLUGIN_NOTIF, "notification");
+}
+
+static XS(Collectd_plugin_register_flush) {
+ return _plugin_register_generic_userdata(aTHX, PLUGIN_FLUSH, "flush");
+}
+
+typedef int perl_unregister_function_t(const char *name);
+
+static void _plugin_unregister_generic(pTHX, perl_unregister_function_t *unreg,
+ const char *desc) {
+ dXSARGS;
+
+ if (1 != items) {
+ log_err("Usage: Collectd::plugin_unregister_%s(pluginname)", desc);
+ XSRETURN_EMPTY;
+ }
+
+ if (!SvOK(ST(0))) {
+ log_err("Collectd::plugin_unregister_%s(pluginname): "
+ "Invalid pluginname",
+ desc);
+ XSRETURN_EMPTY;
+ }
+
+ log_debug("Collectd::plugin_unregister_%s: plugin = \"%s\"", desc,
+ SvPV_nolen(ST(0)));
+
+ unreg(SvPV_nolen(ST(0)));
+
+ XSRETURN_EMPTY;
+
+ return;
+} /* static void _plugin_unregister_generic ( ... ) */
+
+/*
+ * Collectd::plugin_unregister_TYPE (pluginname).
+ *
+ * TYPE:
+ * type of callback to be unregistered: read, write, log, notification, flush
+ *
+ * pluginname:
+ * name of the perl plugin
+ */
+
+static XS(Collectd_plugin_unregister_read) {
+ return _plugin_unregister_generic(aTHX, plugin_unregister_read, "read");
+}
+
+static XS(Collectd_plugin_unregister_write) {
+ return _plugin_unregister_generic(aTHX, plugin_unregister_write, "write");
+}
+
+static XS(Collectd_plugin_unregister_log) {
+ return _plugin_unregister_generic(aTHX, plugin_unregister_log, "log");
+}
+
+static XS(Collectd_plugin_unregister_notification) {
+ return _plugin_unregister_generic(aTHX, plugin_unregister_notification,
+ "notification");
+}
+
+static XS(Collectd_plugin_unregister_flush) {
+ return _plugin_unregister_generic(aTHX, plugin_unregister_flush, "flush");
+}
+
/*
* Collectd::plugin_register_data_set (type, dataset).
*
assert(aTHX == perl_threads->head->interp);
pthread_mutex_lock(&perl_threads->mutex);
- status = pplugin_call_all(aTHX_ PLUGIN_INIT);
+ status = pplugin_call(aTHX_ PLUGIN_INIT);
pthread_mutex_unlock(&perl_threads->mutex);
return status;
} /* static int perl_init (void) */
-static int perl_read(void) {
+static int perl_read(user_data_t *user_data) {
dTHX;
if (NULL == perl_threads)
log_debug("perl_read: c_ithread: interp = %p (active threads: %i)", aTHX,
perl_threads->number_of_threads);
- return pplugin_call_all(aTHX_ PLUGIN_READ);
-} /* static int perl_read (void) */
+
+ return pplugin_call(aTHX_ PLUGIN_READ, user_data->data);
+} /* static int perl_read (user_data_t *user_data) */
static int perl_write(const data_set_t *ds, const value_list_t *vl,
- user_data_t __attribute__((unused)) * user_data) {
+ user_data_t *user_data) {
int status;
dTHX;
log_debug("perl_write: c_ithread: interp = %p (active threads: %i)", aTHX,
perl_threads->number_of_threads);
- status = pplugin_call_all(aTHX_ PLUGIN_WRITE, ds, vl);
+ status = pplugin_call(aTHX_ PLUGIN_WRITE, user_data->data, ds, vl);
if (aTHX == perl_threads->head->interp)
pthread_mutex_unlock(&perl_threads->mutex);
return status;
} /* static int perl_write (const data_set_t *, const value_list_t *) */
-static void perl_log(int level, const char *msg,
- user_data_t __attribute__((unused)) * user_data) {
+static void perl_log(int level, const char *msg, user_data_t *user_data) {
dTHX;
if (NULL == perl_threads)
if (aTHX == perl_threads->head->interp)
pthread_mutex_lock(&perl_threads->mutex);
- pplugin_call_all(aTHX_ PLUGIN_LOG, level, msg);
+ pplugin_call(aTHX_ PLUGIN_LOG, user_data->data, level, msg);
if (aTHX == perl_threads->head->interp)
pthread_mutex_unlock(&perl_threads->mutex);
return;
} /* static void perl_log (int, const char *) */
-static int perl_notify(const notification_t *notif,
- user_data_t __attribute__((unused)) * user_data) {
+static int perl_notify(const notification_t *notif, user_data_t *user_data) {
dTHX;
if (NULL == perl_threads)
aTHX = t->interp;
}
- return pplugin_call_all(aTHX_ PLUGIN_NOTIF, notif);
+ return pplugin_call(aTHX_ PLUGIN_NOTIF, user_data->data, notif);
} /* static int perl_notify (const notification_t *) */
static int perl_flush(cdtime_t timeout, const char *identifier,
- user_data_t __attribute__((unused)) * user_data) {
+ user_data_t *user_data) {
dTHX;
if (NULL == perl_threads)
aTHX = t->interp;
}
- return pplugin_call_all(aTHX_ PLUGIN_FLUSH, timeout, identifier);
+
+ /* For collectd-5.6 only, #1731 */
+ if (user_data == NULL || user_data->data == NULL)
+ return pplugin_call(aTHX_ PLUGIN_FLUSH_ALL, timeout, identifier);
+
+ return pplugin_call(aTHX_ PLUGIN_FLUSH, user_data->data, timeout, identifier);
} /* static int perl_flush (const int) */
static int perl_shutdown(void) {
dTHX;
plugin_unregister_complex_config("perl");
+ plugin_unregister_read_group("perl");
if (NULL == perl_threads)
return 0;
log_debug("perl_shutdown: c_ithread: interp = %p (active threads: %i)", aTHX,
perl_threads->number_of_threads);
- plugin_unregister_log("perl");
- plugin_unregister_notification("perl");
plugin_unregister_init("perl");
- plugin_unregister_read("perl");
- plugin_unregister_write("perl");
- plugin_unregister_flush("perl");
+ plugin_unregister_flush("perl"); /* For collectd-5.6 only, #1731 */
- ret = pplugin_call_all(aTHX_ PLUGIN_SHUTDOWN);
+ ret = pplugin_call(aTHX_ PLUGIN_SHUTDOWN);
pthread_mutex_lock(&perl_threads->mutex);
t = perl_threads->tail;
perl_run(aTHX);
- plugin_register_log("perl", perl_log, /* user_data = */ NULL);
- plugin_register_notification("perl", perl_notify,
- /* user_data = */ NULL);
plugin_register_init("perl", perl_init);
-
- plugin_register_read("perl", perl_read);
-
- plugin_register_write("perl", perl_write, /* user_data = */ NULL);
- plugin_register_flush("perl", perl_flush, /* user_data = */ NULL);
plugin_register_shutdown("perl", perl_shutdown);
return 0;
} /* static int init_pi (const char **, const int) */
current_status = perl_config_includedir(aTHX_ c);
else if (0 == strcasecmp(c->key, "Plugin"))
current_status = perl_config_plugin(aTHX_ c);
+ else if (0 == strcasecmp(c->key, "RegisterLegacyFlush"))
+ cf_util_get_boolean(c, ®ister_legacy_flush);
else {
log_warn("Ignoring unknown config key \"%s\".", c->key);
current_status = 0;
vl.values = values;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "pf", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
status = plugin_thread_create(&collector_thread_id,
/* attrs = */ NULL, collector_thread,
- /* args = */ NULL);
+ /* args = */ NULL, "pinba collector");
if (status != 0) {
char errbuf[1024];
ERROR("pinba plugin: pthread_create(3) failed: %s",
static int plugin_submit(const pinba_statnode_t *res) /* {{{ */
{
- value_t value;
value_list_t vl = VALUE_LIST_INIT;
- vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "pinba", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, res->name, sizeof(vl.plugin_instance));
- value.derive = res->req_count;
+ vl.values = &(value_t){.derive = res->req_count};
sstrncpy(vl.type, "total_requests", sizeof(vl.type));
plugin_dispatch_values(&vl);
- value.derive = float_counter_get(&res->req_time, /* factor = */ 1000);
+ vl.values = &(value_t){
+ .derive = float_counter_get(&res->req_time, /* factor = */ 1000)};
sstrncpy(vl.type, "total_time_in_ms", sizeof(vl.type));
plugin_dispatch_values(&vl);
- value.derive = res->doc_size;
+ vl.values = &(value_t){.derive = res->doc_size};
sstrncpy(vl.type, "total_bytes", sizeof(vl.type));
plugin_dispatch_values(&vl);
- value.derive = float_counter_get(&res->ru_utime, /* factor = */ 100);
+ vl.values = &(value_t){
+ .derive = float_counter_get(&res->ru_utime, /* factor = */ 100)};
sstrncpy(vl.type, "cpu", sizeof(vl.type));
sstrncpy(vl.type_instance, "user", sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
- value.derive = float_counter_get(&res->ru_stime, /* factor = */ 100);
+ vl.values = &(value_t){
+ .derive = float_counter_get(&res->ru_stime, /* factor = */ 100)};
sstrncpy(vl.type, "cpu", sizeof(vl.type));
sstrncpy(vl.type_instance, "system", sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
- value.gauge = res->mem_peak;
+ vl.values = &(value_t){.gauge = res->mem_peak};
sstrncpy(vl.type, "memory", sizeof(vl.type));
sstrncpy(vl.type_instance, "peak", sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
ping_thread_loop = 1;
ping_thread_error = 0;
status = plugin_thread_create(&ping_thread_id, /* attr = */ NULL, ping_thread,
- /* arg = */ (void *)0);
+ /* arg = */ (void *)0, "ping");
if (status != 0) {
ping_thread_loop = 0;
ERROR("ping plugin: Starting thread failed.");
static void submit(const char *host, const char *type, /* {{{ */
gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ping", sizeof(vl.plugin));
- sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
sstrncpy(vl.type_instance, host, sizeof(vl.type_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
assert(db->database != NULL);
assert(db->writers != NULL);
- if (rfc3339nano(time_str, sizeof(time_str), vl->time) != 0) {
+ if (rfc3339nano_local(time_str, sizeof(time_str), vl->time) != 0) {
log_err("c_psql_write: Failed to convert time to RFC 3339 format");
return -1;
}
ERROR("powerdns plugin: %s failed: %s", func, \
sstrerror(errno, errbuf, sizeof(errbuf))); \
} while (0)
+#define SOCK_ERROR(func, sockpath) \
+ do { \
+ char errbuf[1024]; \
+ ERROR("powerdns plugin: Socket `%s` %s failed: %s", sockpath, func, \
+ sstrerror(errno, errbuf, sizeof(errbuf))); \
+ } while (0)
#define SERVER_SOCKET LOCALSTATEDIR "/run/pdns.controlsocket"
#define SERVER_COMMAND "SHOW * \n"
/* <https://doc.powerdns.com/md/recursor/stats/> */
static void submit(const char *plugin_instance, /* {{{ */
- const char *pdns_type, const char *value) {
+ const char *pdns_type, const char *value_str) {
value_list_t vl = VALUE_LIST_INIT;
- value_t values[1];
+ value_t value;
const char *type = NULL;
const char *type_instance = NULL;
if (i >= lookup_table_length) {
INFO("powerdns plugin: submit: Not found in lookup table: %s = %s;",
- pdns_type, value);
+ pdns_type, value_str);
return;
}
return;
}
- if (0 != parse_value(value, &values[0], ds->ds[0].type)) {
+ if (0 != parse_value(value_str, &value, ds->ds[0].type)) {
ERROR("powerdns plugin: Cannot convert `%s' "
"to a number.",
- value);
+ value_str);
return;
}
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "powerdns", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_instance != NULL)
struct sockaddr_un sa_unix = {0};
- struct timeval stv_timeout;
cdtime_t cdt_timeout;
sd = socket(PF_UNIX, item->socktype, 0);
status = unlink(sa_unix.sun_path);
if ((status != 0) && (errno != ENOENT)) {
- FUNC_ERROR("unlink");
+ SOCK_ERROR("unlink", sa_unix.sun_path);
close(sd);
return (-1);
}
* and otherwise the daemon cannot answer. */
status = bind(sd, (struct sockaddr *)&sa_unix, sizeof(sa_unix));
if (status != 0) {
- FUNC_ERROR("bind");
+ SOCK_ERROR("bind", sa_unix.sun_path);
break;
}
/* Make the socket writeable by the daemon.. */
status = chmod(sa_unix.sun_path, 0666);
if (status != 0) {
- FUNC_ERROR("chmod");
+ SOCK_ERROR("chmod", sa_unix.sun_path);
break;
}
if (cdt_timeout < TIME_T_TO_CDTIME_T(2))
cdt_timeout = TIME_T_TO_CDTIME_T(2);
- CDTIME_T_TO_TIMEVAL(cdt_timeout, &stv_timeout);
-
- status = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &stv_timeout,
- sizeof(stv_timeout));
+ status =
+ setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
+ &CDTIME_T_TO_TIMEVAL(cdt_timeout), sizeof(struct timeval));
if (status != 0) {
- FUNC_ERROR("setsockopt");
+ SOCK_ERROR("setsockopt", sa_unix.sun_path);
break;
}
status =
connect(sd, (struct sockaddr *)&item->sockaddr, sizeof(item->sockaddr));
if (status != 0) {
- FUNC_ERROR("connect");
+ SOCK_ERROR("connect", sa_unix.sun_path);
break;
}
status = send(sd, item->command, strlen(item->command), 0);
if (status < 0) {
- FUNC_ERROR("send");
+ SOCK_ERROR("send", sa_unix.sun_path);
break;
}
status = recv(sd, temp, sizeof(temp), /* flags = */ 0);
if (status < 0) {
- FUNC_ERROR("recv");
+ SOCK_ERROR("recv", sa_unix.sun_path);
break;
}
buffer_size = status + 1;
status =
connect(sd, (struct sockaddr *)&item->sockaddr, sizeof(item->sockaddr));
if (status != 0) {
- FUNC_ERROR("connect");
+ SOCK_ERROR("connect", item->sockaddr.sun_path);
close(sd);
return (-1);
}
status = send(sd, item->command, strlen(item->command) + 1,
/* flags = */ 0);
if (status < 0) {
- FUNC_ERROR("send");
+ SOCK_ERROR("send", item->sockaddr.sun_path);
close(sd);
return (-1);
}
status = recv(sd, temp, sizeof(temp), /* flags = */ 0);
if (status < 0) {
- FUNC_ERROR("recv");
+ SOCK_ERROR("recv", item->sockaddr.sun_path);
break;
} else if (status == 0)
break;
unsigned long vmem_code;
unsigned long stack_size;
- unsigned long vmem_minflt;
- unsigned long vmem_majflt;
derive_t vmem_minflt_counter;
derive_t vmem_majflt_counter;
- unsigned long cpu_user;
- unsigned long cpu_system;
derive_t cpu_user_counter;
derive_t cpu_system_counter;
derive_t io_wchar;
derive_t io_syscr;
derive_t io_syscw;
+ _Bool has_io;
derive_t cswitch_vol;
derive_t cswitch_invol;
+ _Bool has_cswitch;
struct procstat_entry_s *next;
} procstat_entry_t;
static procstat_t *list_head_g = NULL;
+static _Bool want_init = 1;
static _Bool report_ctx_switch = 0;
#if HAVE_THREAD_INFO
#elif KERNEL_LINUX
static long pagesize_g;
+static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry);
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKVM_GETPROCS && \
return (0);
} /* int ps_list_match */
-static void ps_update_counter(_Bool init, derive_t *group_counter,
- derive_t *curr_counter, unsigned long *curr_value,
- derive_t new_counter, unsigned long new_value) {
- if (init) {
- *curr_value = new_value;
- *curr_counter += new_value;
- *group_counter += new_value;
+static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
+ derive_t new_counter) {
+ unsigned long curr_value;
+
+ if (want_init) {
+ *curr_counter = new_counter;
return;
}
if (new_counter < *curr_counter)
- *curr_value = new_counter + (ULONG_MAX - *curr_counter);
+ curr_value = new_counter + (ULONG_MAX - *curr_counter);
else
- *curr_value = new_counter - *curr_counter;
+ curr_value = new_counter - *curr_counter;
*curr_counter = new_counter;
- *group_counter += *curr_value;
+ *group_counter += curr_value;
}
/* add process entry to 'instances' of process 'name' (or refresh it) */
return;
for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
- _Bool want_init;
-
if ((ps_list_match(name, cmdline, ps)) == 0)
continue;
+#if KERNEL_LINUX
+ ps_fill_details(ps, entry);
+#endif
+
for (pse = ps->instances; pse != NULL; pse = pse->next)
if ((pse->id == entry->id) || (pse->next == NULL))
break;
ps->cswitch_vol += ((pse->cswitch_vol == -1) ? 0 : pse->cswitch_vol);
ps->cswitch_invol += ((pse->cswitch_invol == -1) ? 0 : pse->cswitch_invol);
- want_init =
- (entry->vmem_minflt_counter == 0) && (entry->vmem_majflt_counter == 0);
- ps_update_counter(want_init, &ps->vmem_minflt_counter,
- &pse->vmem_minflt_counter, &pse->vmem_minflt,
- entry->vmem_minflt_counter, entry->vmem_minflt);
- ps_update_counter(want_init, &ps->vmem_majflt_counter,
- &pse->vmem_majflt_counter, &pse->vmem_majflt,
- entry->vmem_majflt_counter, entry->vmem_majflt);
-
- want_init =
- (entry->cpu_user_counter == 0) && (entry->cpu_system_counter == 0);
- ps_update_counter(want_init, &ps->cpu_user_counter, &pse->cpu_user_counter,
- &pse->cpu_user, entry->cpu_user_counter, entry->cpu_user);
- ps_update_counter(want_init, &ps->cpu_system_counter,
- &pse->cpu_system_counter, &pse->cpu_system,
- entry->cpu_system_counter, entry->cpu_system);
+ ps_update_counter(&ps->vmem_minflt_counter, &pse->vmem_minflt_counter,
+ entry->vmem_minflt_counter);
+ ps_update_counter(&ps->vmem_majflt_counter, &pse->vmem_majflt_counter,
+ entry->vmem_majflt_counter);
+
+ ps_update_counter(&ps->cpu_user_counter, &pse->cpu_user_counter,
+ entry->cpu_user_counter);
+ ps_update_counter(&ps->cpu_system_counter, &pse->cpu_system_counter,
+ entry->cpu_system_counter);
}
}
/* submit global state (e.g.: qty of zombies, running, etc..) */
static void ps_submit_state(const char *state, double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
sstrncpy(vl.type, "ps_state", sizeof(vl.type));
/* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
static void ps_submit_proc_list(procstat_t *ps) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
+ value_t values[2];
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, ps->name, sizeof(vl.plugin_instance));
#if KERNEL_LINUX || KERNEL_SOLARIS
static void ps_submit_fork_rate(derive_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = value;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
sstrncpy(vl.type, "fork_rate", sizeof(vl.type));
/* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
#if KERNEL_LINUX
-static procstat_t *ps_read_tasks_status(long pid, procstat_t *ps) {
+static int ps_read_tasks_status(procstat_entry_t *ps) {
char dirname[64];
DIR *dh;
char filename[64];
char *fields[8];
int numfields;
- ssnprintf(dirname, sizeof(dirname), "/proc/%li/task", pid);
+ ssnprintf(dirname, sizeof(dirname), "/proc/%li/task", ps->id);
if ((dh = opendir(dirname)) == NULL) {
DEBUG("Failed to open directory `%s'", dirname);
- return (NULL);
+ return (-1);
}
while ((ent = readdir(dh)) != NULL) {
tpid = ent->d_name;
- ssnprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", pid,
+ ssnprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", ps->id,
tpid);
if ((fh = fopen(filename, "r")) == NULL) {
DEBUG("Failed to open file `%s'", filename);
ps->cswitch_vol = cswitch_vol;
ps->cswitch_invol = cswitch_invol;
- return (ps);
+ return (0);
} /* int *ps_read_tasks_status */
/* Read data from /proc/pid/status */
return (ps);
} /* procstat_t *ps_read_vmem */
-static procstat_t *ps_read_io(long pid, procstat_t *ps) {
+static int ps_read_io(procstat_entry_t *ps) {
FILE *fh;
char buffer[1024];
char filename[64];
char *fields[8];
int numfields;
- ssnprintf(filename, sizeof(filename), "/proc/%li/io", pid);
+ ssnprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
if ((fh = fopen(filename, "r")) == NULL)
- return (NULL);
+ return (-1);
while (fgets(buffer, sizeof(buffer), fh) != NULL) {
derive_t *val = NULL;
char errbuf[1024];
WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
}
+ return (0);
+} /* int ps_read_io (...) */
- return (ps);
-} /* procstat_t *ps_read_io */
+static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry) {
+ if (entry->has_io == 0 && ps_read_io(entry) != 0) {
+ /* no io data */
+ entry->io_rchar = -1;
+ entry->io_wchar = -1;
+ entry->io_syscr = -1;
+ entry->io_syscw = -1;
+
+ DEBUG("ps_read_process: not get io data for pid %li", entry->id);
+ }
+ entry->has_io = 1;
+
+ if (report_ctx_switch) {
+ if (entry->has_cswitch == 0 && ps_read_tasks_status(entry) != 0) {
+ entry->cswitch_vol = -1;
+ entry->cswitch_invol = -1;
+
+ DEBUG("ps_read_tasks_status: not get context "
+ "switch data for pid %li",
+ entry->id);
+ }
+ entry->has_cswitch = 1;
+ }
+} /* void ps_fill_details (...) */
static int ps_read_process(long pid, procstat_t *ps, char *state) {
char filename[64];
ps->vmem_rss = (unsigned long)vmem_rss;
ps->stack_size = (unsigned long)stack_size;
- if ((ps_read_io(pid, ps)) == NULL) {
- /* no io data */
- ps->io_rchar = -1;
- ps->io_wchar = -1;
- ps->io_syscr = -1;
- ps->io_syscw = -1;
-
- DEBUG("ps_read_process: not get io data for pid %li", pid);
- }
-
- if (report_ctx_switch) {
- if ((ps_read_tasks_status(pid, ps)) == NULL) {
- ps->cswitch_vol = -1;
- ps->cswitch_invol = -1;
-
- DEBUG("ps_read_tasks_status: not get context "
- "switch data for pid %li",
- pid);
- }
- }
-
/* success */
return (0);
} /* int ps_read_process (...) */
pse.vmem_code = ps.vmem_code;
pse.stack_size = ps.stack_size;
- pse.vmem_minflt = 0;
pse.vmem_minflt_counter = ps.vmem_minflt_counter;
- pse.vmem_majflt = 0;
pse.vmem_majflt_counter = ps.vmem_majflt_counter;
- pse.cpu_user = 0;
pse.cpu_user_counter = ps.cpu_user_counter;
- pse.cpu_system = 0;
pse.cpu_system_counter = ps.cpu_system_counter;
pse.io_rchar = ps.io_rchar;
pse.vmem_data = procs[i].ki_dsize * pagesize;
pse.vmem_code = procs[i].ki_tsize * pagesize;
pse.stack_size = procs[i].ki_ssize * pagesize;
- pse.vmem_minflt = 0;
pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
- pse.vmem_majflt = 0;
pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
- pse.cpu_user = 0;
- pse.cpu_system = 0;
pse.cpu_user_counter = 0;
pse.cpu_system_counter = 0;
/*
pse.vmem_code = procs[i].p_vm_tsize * pagesize;
pse.stack_size = procs[i].p_vm_ssize * pagesize;
pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
- pse.vmem_minflt = 0;
pse.vmem_minflt_counter = procs[i].p_uru_minflt;
- pse.vmem_majflt = 0;
pse.vmem_majflt_counter = procs[i].p_uru_majflt;
- pse.cpu_user = 0;
- pse.cpu_system = 0;
pse.cpu_user_counter =
procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
pse.cpu_system_counter =
break;
}
- pse.cpu_user = 0;
/* tv_usec is nanosec ??? */
pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
procentry[i].pi_ru.ru_utime.tv_usec / 1000;
- pse.cpu_system = 0;
/* tv_usec is nanosec ??? */
pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
procentry[i].pi_ru.ru_stime.tv_usec / 1000;
- pse.vmem_minflt = 0;
pse.vmem_minflt_counter = procentry[i].pi_minflt;
- pse.vmem_majflt = 0;
pse.vmem_majflt_counter = procentry[i].pi_majflt;
pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
pse.vmem_code = ps.vmem_code;
pse.stack_size = ps.stack_size;
- pse.vmem_minflt = 0;
pse.vmem_minflt_counter = ps.vmem_minflt_counter;
- pse.vmem_majflt = 0;
pse.vmem_majflt_counter = ps.vmem_majflt_counter;
- pse.cpu_user = 0;
pse.cpu_user_counter = ps.cpu_user_counter;
- pse.cpu_system = 0;
pse.cpu_system_counter = ps.cpu_system_counter;
pse.io_rchar = ps.io_rchar;
read_fork_rate();
#endif /* KERNEL_SOLARIS */
+ want_init = 0;
+
return (0);
} /* int ps_read */
*/
static void submit(const char *protocol_name, const char *str_key,
const char *str_value) {
- value_t values[1];
+ value_t value;
value_list_t vl = VALUE_LIST_INIT;
int status;
- status = parse_value(str_value, values, DS_TYPE_DERIVE);
+ status = parse_value(str_value, &value, DS_TYPE_DERIVE);
if (status != 0) {
ERROR("protocols plugin: Parsing string as integer failed: %s", str_value);
return;
}
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "protocols", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, protocol_name, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "protocol_counter", sizeof(vl.type));
c->data = data;
c->next = NULL;
- user_data_t user_data = {.data = c, .free_func = cpy_destroy_user_data};
+ register_function(buf, handler,
+ &(user_data_t){
+ .data = c, .free_func = cpy_destroy_user_data,
+ });
- register_function(buf, handler, &user_data);
++cpy_num_callbacks;
return cpy_string_to_unicode_or_bytes(buf);
}
c->data = data;
c->next = NULL;
- user_data_t user_data = {.data = c, .free_func = cpy_destroy_user_data};
-
- plugin_register_complex_read(/* group = */ "python", buf, cpy_read_callback,
- DOUBLE_TO_CDTIME_T(interval), &user_data);
+ plugin_register_complex_read(
+ /* group = */ "python", buf, cpy_read_callback,
+ DOUBLE_TO_CDTIME_T(interval),
+ &(user_data_t){
+ .data = c, .free_func = cpy_destroy_user_data,
+ });
++cpy_num_callbacks;
return cpy_string_to_unicode_or_bytes(buf);
}
ERROR("python: Unable to create pipe.");
return 1;
}
- if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1)) {
+ if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
+ "python interpreter")) {
ERROR("python: Error creating thread for interactive interpreter.");
}
if (read(pipefd[0], &buf, 1))
redis_submit(char *plugin_instance, const char *type, const char *type_instance,
value_t value) /* {{{ */
{
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0] = value;
-
- vl.values = values;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "redis", sizeof(vl.plugin));
if (plugin_instance != NULL)
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
static void cr_submit_io(cr_data_t *rd, const char *type, /* {{{ */
const char *type_instance, derive_t rx, derive_t tx) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = rx;
- values[1].derive = tx;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
{
cr_data_t *router_data;
char read_name[128];
- user_data_t user_data;
int status;
router_data = calloc(1, sizeof(*router_data));
}
ssnprintf(read_name, sizeof(read_name), "routeros/%s", router_data->node);
- user_data.data = router_data;
- user_data.free_func = (void *)cr_free_data;
if (status == 0)
status = plugin_register_complex_read(
- /* group = */ NULL, read_name, cr_read, /* interval = */ 0, &user_data);
+ /* group = */ NULL, read_name, cr_read, /* interval = */ 0,
+ &(user_data_t){
+ .data = router_data, .free_func = (void *)cr_free_data,
+ });
if (status != 0)
cr_free_data(router_data);
rrdc_stats_t *head;
_Bool retried = 0;
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
+ vl.values = &(value_t){.gauge = NAN};
+ vl.values_len = 1;
if (daemon_address == NULL)
return (-1);
if (!config_collect_stats)
return (-1);
- vl.values = values;
- vl.values_len = 1;
-
- if ((strncmp("unix:", daemon_address, strlen("unix:")) == 0) ||
- (daemon_address[0] == '/'))
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
- else
+ if ((strncmp("unix:", daemon_address, strlen("unix:")) != 0) &&
+ (daemon_address[0] != '/'))
sstrncpy(vl.host, daemon_address, sizeof(vl.host));
sstrncpy(vl.plugin, "rrdcached", sizeof(vl.plugin));
for (rrdc_stats_t *ptr = head; ptr != NULL; ptr = ptr->next) {
if (ptr->type == RRDC_STATS_TYPE_GAUGE)
- values[0].gauge = (gauge_t)ptr->value.gauge;
+ vl.values[0].gauge = (gauge_t)ptr->value.gauge;
else if (ptr->type == RRDC_STATS_TYPE_COUNTER)
- values[0].counter = (counter_t)ptr->value.counter;
+ vl.values[0].counter = (counter_t)ptr->value.counter;
else
continue;
* ALWAYS lock `cache_lock' first! */
static cdtime_t cache_timeout = 0;
static cdtime_t cache_flush_timeout = 0;
-static cdtime_t random_timeout = TIME_T_TO_CDTIME_T(1);
+static cdtime_t random_timeout = TIME_T_TO_CDTIME_T_STATIC(1);
static cdtime_t cache_flush_last;
static c_avl_tree_t *cache = NULL;
static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_unlock(&cache_lock);
- status = plugin_thread_create(&queue_thread, /* attr = */ NULL,
- rrd_queue_thread, /* args = */ NULL);
+ status =
+ plugin_thread_create(&queue_thread, /* attr = */ NULL, rrd_queue_thread,
+ /* args = */ NULL, "rrdtool queue");
if (status != 0) {
ERROR("rrdtool plugin: Cannot create queue-thread.");
return (-1);
} /* int sensors_shutdown */
static void sensors_submit(const char *plugin_instance, const char *type,
- const char *type_instance, double val) {
+ const char *type_instance, double value) {
char match_key[1024];
int status;
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
status = ssnprintf(match_key, sizeof(match_key), "%s/%s-%s", plugin_instance,
return;
}
- values[0].gauge = val;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "sensors", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
#endif
static void serial_submit(const char *type_instance, derive_t rx, derive_t tx) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = rx;
- values[1].derive = tx;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "serial", sizeof(vl.plugin));
sstrncpy(vl.type, "serial_octets", sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
void *cb_data) {
const struct sr_datafeed_analog *analog;
struct config_device *cfdev;
- value_t value;
value_list_t vl = VALUE_LIST_INIT;
/* Find this device's configuration. */
/* Ignore all but the first sample on the first probe. */
analog = packet->payload;
- value.gauge = analog->data[0];
- vl.values = &value;
+ vl.values = &(value_t){.gauge = analog->data[0]};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "sigrok", sizeof(vl.plugin));
- ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cfdev->name);
+ sstrncpy(vl.plugin_instance, cfdev->name, sizeof(vl.plugin_instance));
sstrncpy(vl.type, sigrok_value_type(analog), sizeof(vl.type));
plugin_dispatch_values(&vl);
return -1;
}
- status = plugin_thread_create(&sr_thread, NULL, sigrok_read_thread, NULL);
+ status = plugin_thread_create(&sr_thread, NULL, sigrok_read_thread, NULL,
+ "sigrok read");
if (status != 0) {
char errbuf[1024];
ERROR("sigrok plugin: Failed to create thread: %s.",
static void smart_submit(const char *dev, const char *type,
const char *type_inst, double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "smart", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
static void handle_attribute(SkDisk *d, const SkSmartAttributeParsedData *a,
void *userdata) {
char const *name = userdata;
- value_t values[4];
- value_list_t vl = VALUE_LIST_INIT;
if (!a->current_value_valid || !a->worst_value_valid)
return;
- values[0].gauge = a->current_value;
- values[1].gauge = a->worst_value;
- values[2].gauge = a->threshold_valid ? a->threshold : 0;
- values[3].gauge = a->pretty_value;
+
+ value_list_t vl = VALUE_LIST_INIT;
+ value_t values[] = {
+ {.gauge = a->current_value},
+ {.gauge = a->worst_value},
+ {.gauge = a->threshold_valid ? a->threshold : 0},
+ {.gauge = a->pretty_value},
+ };
vl.values = values;
- vl.values_len = 4;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "smart", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, name, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "smart_attribute", sizeof(vl.type));
ssnprintf(cb_name, sizeof(cb_name), "snmp-%s", hd->name);
- user_data_t ud = {.data = hd, .free_func = csnmp_host_definition_destroy};
-
- status =
- plugin_register_complex_read(/* group = */ NULL, cb_name, csnmp_read_host,
- hd->interval, /* user_data = */ &ud);
+ status = plugin_register_complex_read(
+ /* group = */ NULL, cb_name, csnmp_read_host, hd->interval,
+ &(user_data_t){
+ .data = hd, .free_func = csnmp_host_definition_destroy,
+ });
if (status != 0) {
ERROR("snmp plugin: Registering complex read function failed.");
csnmp_host_definition_destroy(hd);
static int statsd_metric_submit_unsafe(char const *name,
statsd_metric_t *metric) /* {{{ */
{
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- vl.values = values;
+ vl.values = &(value_t){.gauge = NAN};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "statsd", sizeof(vl.plugin));
if (metric->type == STATSD_GAUGE)
sstrncpy(vl.type_instance, name, sizeof(vl.type_instance));
if (metric->type == STATSD_GAUGE)
- values[0].gauge = (gauge_t)metric->value;
+ vl.values[0].gauge = (gauge_t)metric->value;
else if (metric->type == STATSD_TIMER) {
_Bool have_events = (metric->updates_num > 0);
vl.time = cdtime();
ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-average", name);
- values[0].gauge =
+ vl.values[0].gauge =
have_events
? CDTIME_T_TO_DOUBLE(latency_counter_get_average(metric->latency))
: NAN;
if (conf_timer_lower) {
ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-lower", name);
- values[0].gauge =
+ vl.values[0].gauge =
have_events
? CDTIME_T_TO_DOUBLE(latency_counter_get_min(metric->latency))
: NAN;
if (conf_timer_upper) {
ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-upper", name);
- values[0].gauge =
+ vl.values[0].gauge =
have_events
? CDTIME_T_TO_DOUBLE(latency_counter_get_max(metric->latency))
: NAN;
if (conf_timer_sum) {
ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-sum", name);
- values[0].gauge =
+ vl.values[0].gauge =
have_events
? CDTIME_T_TO_DOUBLE(latency_counter_get_sum(metric->latency))
: NAN;
for (size_t i = 0; i < conf_timer_percentile_num; i++) {
ssnprintf(vl.type_instance, sizeof(vl.type_instance),
"%s-percentile-%.0f", name, conf_timer_percentile[i]);
- values[0].gauge = have_events
- ? CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(
- metric->latency, conf_timer_percentile[i]))
- : NAN;
+ vl.values[0].gauge =
+ have_events ? CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(
+ metric->latency, conf_timer_percentile[i]))
+ : NAN;
plugin_dispatch_values(&vl);
}
if (conf_timer_count) {
sstrncpy(vl.type, "gauge", sizeof(vl.type));
ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-count", name);
- values[0].gauge = latency_counter_get_num(metric->latency);
+ vl.values[0].gauge = latency_counter_get_num(metric->latency);
plugin_dispatch_values(&vl);
}
return (0);
} else if (metric->type == STATSD_SET) {
if (metric->set == NULL)
- values[0].gauge = 0.0;
+ vl.values[0].gauge = 0.0;
else
- values[0].gauge = (gauge_t)c_avl_size(metric->set);
+ vl.values[0].gauge = (gauge_t)c_avl_size(metric->set);
} else { /* STATSD_COUNTER */
gauge_t delta = nearbyint(metric->value);
* anyway - if requested by the user - for compatibility reasons. */
if (conf_counter_sum) {
sstrncpy(vl.type, "count", sizeof(vl.type));
- values[0].gauge = delta;
+ vl.values[0].gauge = delta;
plugin_dispatch_values(&vl);
/* restore vl.type */
metric->value -= delta;
metric->counter += (derive_t)delta;
- values[0].derive = metric->counter;
+ vl.values[0].derive = metric->counter;
}
return (plugin_dispatch_values(&vl));
static void swap_submit_usage(char const *plugin_instance, /* {{{ */
gauge_t used, gauge_t free,
char const *other_name, gauge_t other_value) {
- value_t v[1];
value_list_t vl = VALUE_LIST_INIT;
- vl.values = v;
- vl.values_len = STATIC_ARRAY_SIZE(v);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values = &(value_t){.gauge = NAN};
+ vl.values_len = 1;
sstrncpy(vl.plugin, "swap", sizeof(vl.plugin));
if (plugin_instance != NULL)
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
swap_submit_derive(char const *type_instance, /* {{{ */
derive_t value) {
value_list_t vl = VALUE_LIST_INIT;
- value_t v[1];
- v[0].derive = value;
-
- vl.values = v;
- vl.values_len = STATIC_ARRAY_SIZE(v);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values = &(value_t){.derive = value};
+ vl.values_len = 1;
sstrncpy(vl.plugin, "swap", sizeof(vl.plugin));
sstrncpy(vl.type, "swap_io", sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "table", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, tbl->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, res->type, sizeof(vl.type));
#include "common.h"
#include "plugin.h"
+#include "utils_latency_config.h"
#include "utils_tail_match.h"
/*
char *type;
char *type_instance;
cdtime_t interval;
+ latency_config_t latency;
};
typedef struct ctail_config_match_s ctail_config_match_t;
return (-1);
}
- if (strncasecmp("Gauge", ci->values[0].value.string, strlen("Gauge")) == 0) {
+ char const *ds_type = ci->values[0].value.string;
+ if (strncasecmp("Gauge", ds_type, strlen("Gauge")) == 0) {
cm->flags = UTILS_MATCH_DS_TYPE_GAUGE;
- if (strcasecmp("GaugeAverage", ci->values[0].value.string) == 0)
+ if (strcasecmp("GaugeAverage", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_GAUGE_AVERAGE;
- else if (strcasecmp("GaugeMin", ci->values[0].value.string) == 0)
+ else if (strcasecmp("GaugeMin", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_GAUGE_MIN;
- else if (strcasecmp("GaugeMax", ci->values[0].value.string) == 0)
+ else if (strcasecmp("GaugeMax", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_GAUGE_MAX;
- else if (strcasecmp("GaugeLast", ci->values[0].value.string) == 0)
+ else if (strcasecmp("GaugeLast", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_GAUGE_LAST;
- else if (strcasecmp("GaugeInc", ci->values[0].value.string) == 0)
+ else if (strcasecmp("GaugeInc", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_GAUGE_INC;
- else if (strcasecmp("GaugeAdd", ci->values[0].value.string) == 0)
+ else if (strcasecmp("GaugeAdd", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_GAUGE_ADD;
+ else if (strcasecmp("GaugePersist", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_GAUGE_PERSIST;
else
cm->flags = 0;
- } else if (strncasecmp("Counter", ci->values[0].value.string,
- strlen("Counter")) == 0) {
+ } else if (strcasecmp("Distribution", ds_type) == 0) {
+ cm->flags = UTILS_MATCH_DS_TYPE_GAUGE | UTILS_MATCH_CF_GAUGE_DIST;
+
+ int status = latency_config(&cm->latency, ci, "tail");
+ if (status != 0)
+ return (status);
+ } else if (strncasecmp("Counter", ds_type, strlen("Counter")) == 0) {
cm->flags = UTILS_MATCH_DS_TYPE_COUNTER;
- if (strcasecmp("CounterSet", ci->values[0].value.string) == 0)
+ if (strcasecmp("CounterSet", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_COUNTER_SET;
- else if (strcasecmp("CounterAdd", ci->values[0].value.string) == 0)
+ else if (strcasecmp("CounterAdd", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_COUNTER_ADD;
- else if (strcasecmp("CounterInc", ci->values[0].value.string) == 0)
+ else if (strcasecmp("CounterInc", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_COUNTER_INC;
else
cm->flags = 0;
- } else if (strncasecmp("Derive", ci->values[0].value.string,
- strlen("Derive")) == 0) {
+ } else if (strncasecmp("Derive", ds_type, strlen("Derive")) == 0) {
cm->flags = UTILS_MATCH_DS_TYPE_DERIVE;
- if (strcasecmp("DeriveSet", ci->values[0].value.string) == 0)
+ if (strcasecmp("DeriveSet", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_DERIVE_SET;
- else if (strcasecmp("DeriveAdd", ci->values[0].value.string) == 0)
+ else if (strcasecmp("DeriveAdd", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_DERIVE_ADD;
- else if (strcasecmp("DeriveInc", ci->values[0].value.string) == 0)
+ else if (strcasecmp("DeriveInc", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_DERIVE_INC;
else
cm->flags = 0;
- } else if (strncasecmp("Absolute", ci->values[0].value.string,
- strlen("Absolute")) == 0) {
+ } else if (strncasecmp("Absolute", ds_type, strlen("Absolute")) == 0) {
cm->flags = UTILS_MATCH_DS_TYPE_ABSOLUTE;
- if (strcasecmp("AbsoluteSet", ci->values[0].value.string) == 0)
+ if (strcasecmp("AbsoluteSet", ds_type) == 0)
cm->flags |= UTILS_MATCH_CF_ABSOLUTE_SET;
else
cm->flags = 0;
} /* while (status == 0) */
if (status == 0) {
- status = tail_match_add_match_simple(tm, cm.regex, cm.excluderegex,
- cm.flags, "tail", plugin_instance,
- cm.type, cm.type_instance, interval);
+ // TODO(octo): there's nothing "simple" about the latency stuff …
+ status = tail_match_add_match_simple(
+ tm, cm.regex, cm.excluderegex, cm.flags, "tail", plugin_instance,
+ cm.type, cm.type_instance, cm.latency, interval);
- if (status != 0) {
+ if (status != 0)
ERROR("tail plugin: tail_match_add_match_simple failed.");
- }
}
sfree(cm.regex);
sfree(cm.excluderegex);
sfree(cm.type);
sfree(cm.type_instance);
+ latency_config_free(cm.latency);
return (status);
} /* int ctail_config_add_match */
for (size_t i = 0; i < tail_match_list_num; i++) {
ssnprintf(str, sizeof(str), "tail-%zu", i);
- user_data_t ud = {.data = tail_match_list[i]};
-
plugin_register_complex_read(NULL, str, ctail_read,
- tail_match_list_intervals[i], &ud);
+ tail_match_list_intervals[i],
+ &(user_data_t){
+ .data = tail_match_list[i],
+ });
}
return (0);
vl.values_len = 1;
vl.values = &v;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "tail_csv", sizeof(vl.plugin));
if (id->instance != NULL)
sstrncpy(vl.plugin_instance, id->instance, sizeof(vl.plugin_instance));
ssnprintf(cb_name, sizeof(cb_name), "tail_csv/%s", id->path);
- user_data_t ud = {.data = id, .free_func = tcsv_instance_definition_destroy};
-
- status =
- plugin_register_complex_read(NULL, cb_name, tcsv_read, id->interval, &ud);
-
+ status = plugin_register_complex_read(
+ NULL, cb_name, tcsv_read, id->interval,
+ &(user_data_t){
+ .data = id, .free_func = tcsv_instance_definition_destroy,
+ });
if (status != 0) {
ERROR("tail_csv plugin: Registering complex read function failed.");
tcsv_instance_definition_destroy(id);
static void tape_submit(const char *plugin_instance, const char *type,
derive_t read, derive_t write) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = read;
- values[1].derive = write;
+ value_t values[] = {
+ {.derive = read}, {.derive = write},
+ };
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "tape", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
struct tr_action_s {
regex_t re;
char *replacement;
- int may_be_empty;
+ _Bool may_be_empty;
tr_action_t *next;
};
+struct tr_meta_data_action_s;
+typedef struct tr_meta_data_action_s tr_meta_data_action_t;
+struct tr_meta_data_action_s {
+ char *key;
+ regex_t re;
+ char *replacement;
+
+ tr_meta_data_action_t *next;
+};
+
struct tr_data_s {
tr_action_t *host;
tr_action_t *plugin;
tr_action_t *plugin_instance;
/* tr_action_t *type; */
tr_action_t *type_instance;
+ tr_meta_data_action_t *meta;
};
typedef struct tr_data_s tr_data_t;
sfree(act);
} /* }}} void tr_action_destroy */
+static void tr_meta_data_action_destroy(tr_meta_data_action_t *act) /* {{{ */
+{
+ if (act == NULL)
+ return;
+
+ sfree(act->key);
+ regfree(&act->re);
+ sfree(act->replacement);
+
+ if (act->next != NULL)
+ tr_meta_data_action_destroy(act->next);
+
+ sfree(act);
+} /* }}} void tr_meta_data_action_destroy */
+
static int tr_config_add_action(tr_action_t **dest, /* {{{ */
- const oconfig_item_t *ci, int may_be_empty) {
+ const oconfig_item_t *ci, _Bool may_be_empty) {
tr_action_t *act;
int status;
act->replacement = tr_strdup(ci->values[1].value.string);
if (act->replacement == NULL) {
ERROR("tr_config_add_action: tr_strdup failed.");
- regfree(&act->re);
- sfree(act);
+ tr_action_destroy(act);
return (-ENOMEM);
}
return (0);
} /* }}} int tr_config_add_action */
+static int tr_config_add_meta_action(tr_meta_data_action_t **dest, /* {{{ */
+ const oconfig_item_t *ci,
+ _Bool should_delete) {
+ tr_meta_data_action_t *act;
+ int status;
+
+ if (dest == NULL)
+ return (-EINVAL);
+
+ if (should_delete) {
+ if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
+ (ci->values[1].type != OCONFIG_TYPE_STRING)) {
+ ERROR("Target `replace': The `%s' option requires exactly two string "
+ "arguments.",
+ ci->key);
+ return (-1);
+ }
+ } else {
+ if ((ci->values_num != 3) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
+ (ci->values[1].type != OCONFIG_TYPE_STRING) ||
+ (ci->values[2].type != OCONFIG_TYPE_STRING)) {
+ ERROR("Target `replace': The `%s' option requires exactly three string "
+ "arguments.",
+ ci->key);
+ return (-1);
+ }
+ }
+
+ if (strlen(ci->values[0].value.string) == 0) {
+ ERROR("Target `replace': The `%s' option does not accept empty string as "
+ "first argument.",
+ ci->key);
+ return (-1);
+ }
+
+ act = calloc(1, sizeof(*act));
+ if (act == NULL) {
+ ERROR("tr_config_add_meta_action: calloc failed.");
+ return (-ENOMEM);
+ }
+
+ act->key = NULL;
+ act->replacement = NULL;
+
+ status = regcomp(&act->re, ci->values[1].value.string, REG_EXTENDED);
+ if (status != 0) {
+ char errbuf[1024] = "";
+
+ /* regerror assures null termination. */
+ regerror(status, &act->re, errbuf, sizeof(errbuf));
+ ERROR("Target `replace': Compiling the regular expression `%s' "
+ "failed: %s.",
+ ci->values[1].value.string, errbuf);
+ sfree(act->key);
+ sfree(act);
+ return (-EINVAL);
+ }
+
+ act->key = tr_strdup(ci->values[0].value.string);
+ if (act->key == NULL) {
+ ERROR("tr_config_add_meta_action: tr_strdup failed.");
+ tr_meta_data_action_destroy(act);
+ return (-ENOMEM);
+ }
+
+ if (!should_delete) {
+ act->replacement = tr_strdup(ci->values[2].value.string);
+ if (act->replacement == NULL) {
+ ERROR("tr_config_add_meta_action: tr_strdup failed.");
+ tr_meta_data_action_destroy(act);
+ return (-ENOMEM);
+ }
+ }
+
+ /* Insert action at end of list. */
+ if (*dest == NULL)
+ *dest = act;
+ else {
+ tr_meta_data_action_t *prev;
+
+ prev = *dest;
+ while (prev->next != NULL)
+ prev = prev->next;
+
+ prev->next = act;
+ }
+
+ return (0);
+} /* }}} int tr_config_add_meta_action */
+
static int tr_action_invoke(tr_action_t *act_head, /* {{{ */
char *buffer_in, size_t buffer_in_size,
- int may_be_empty) {
+ _Bool may_be_empty) {
int status;
char buffer[DATA_MAX_NAME_LEN];
regmatch_t matches[8] = {[0] = {0}};
return (0);
} /* }}} int tr_action_invoke */
+static int tr_meta_data_action_invoke(/* {{{ */
+ tr_meta_data_action_t *act_head,
+ meta_data_t **dest) {
+ int status;
+ regmatch_t matches[8] = {[0] = {0}};
+
+ if (act_head == NULL)
+ return (-EINVAL);
+
+ if ((*dest) == NULL) /* nothing to do */
+ return (0);
+
+ for (tr_meta_data_action_t *act = act_head; act != NULL; act = act->next) {
+ char temp[DATA_MAX_NAME_LEN];
+ char *subst_status;
+ int value_type;
+ int meta_data_status;
+ char *value;
+ meta_data_t *result;
+
+ value_type = meta_data_type(*dest, act->key);
+ if (value_type == 0) /* not found */
+ continue;
+ if (value_type != MD_TYPE_STRING) {
+ WARNING("Target `replace': Attempting replace on metadata key `%s', "
+ "which isn't a string.",
+ act->key);
+ continue;
+ }
+
+ meta_data_status = meta_data_get_string(*dest, act->key, &value);
+ if (meta_data_status != 0) {
+ ERROR("Target `replace': Unable to retrieve metadata value for `%s'.",
+ act->key);
+ return (meta_data_status);
+ }
+
+ DEBUG("target_replace plugin: tr_meta_data_action_invoke: `%s' "
+ "old value = `%s'",
+ act->key, value);
+
+ status = regexec(&act->re, value, STATIC_ARRAY_SIZE(matches), matches,
+ /* flags = */ 0);
+ if (status == REG_NOMATCH) {
+ sfree(value);
+ continue;
+ } else if (status != 0) {
+ char errbuf[1024] = "";
+
+ regerror(status, &act->re, errbuf, sizeof(errbuf));
+ ERROR("Target `replace': Executing a regular expression failed: %s.",
+ errbuf);
+ sfree(value);
+ continue;
+ }
+
+ if (act->replacement == NULL) {
+ /* no replacement; delete the key */
+ DEBUG("target_replace plugin: tr_meta_data_action_invoke: "
+ "deleting `%s'",
+ act->key);
+ meta_data_delete(*dest, act->key);
+ sfree(value);
+ continue;
+ }
+
+ subst_status = subst(temp, sizeof(temp), value, (size_t)matches[0].rm_so,
+ (size_t)matches[0].rm_eo, act->replacement);
+ if (subst_status == NULL) {
+ ERROR("Target `replace': subst (value = %s, start = %zu, end = %zu, "
+ "replacement = %s) failed.",
+ value, (size_t)matches[0].rm_so, (size_t)matches[0].rm_eo,
+ act->replacement);
+ sfree(value);
+ continue;
+ }
+
+ DEBUG("target_replace plugin: tr_meta_data_action_invoke: `%s' "
+ "value `%s' -> `%s'",
+ act->key, value, temp);
+
+ if ((result = meta_data_create()) == NULL) {
+ ERROR("Target `replace': failed to create metadata for `%s'.", act->key);
+ sfree(value);
+ return (-ENOMEM);
+ }
+
+ meta_data_status = meta_data_add_string(result, act->key, temp);
+ if (meta_data_status != 0) {
+ ERROR("Target `replace': Unable to set metadata value for `%s'.",
+ act->key);
+ meta_data_destroy(result);
+ sfree(value);
+ return (meta_data_status);
+ }
+
+ meta_data_clone_merge(dest, result);
+ meta_data_destroy(result);
+ sfree(value);
+ } /* for (act = act_head; act != NULL; act = act->next) */
+
+ return (0);
+} /* }}} int tr_meta_data_action_invoke */
+
static int tr_destroy(void **user_data) /* {{{ */
{
tr_data_t *data;
tr_action_destroy(data->plugin_instance);
/* tr_action_destroy (data->type); */
tr_action_destroy(data->type_instance);
+ tr_meta_data_action_destroy(data->meta);
sfree(data);
return (0);
data->plugin_instance = NULL;
/* data->type = NULL; */
data->type_instance = NULL;
+ data->meta = NULL;
status = 0;
for (int i = 0; i < ci->children_num; i++) {
else if (strcasecmp("TypeInstance", child->key) == 0)
status = tr_config_add_action(&data->type_instance, child,
/* may be empty = */ 1);
+ else if (strcasecmp("MetaData", child->key) == 0)
+ status = tr_config_add_meta_action(&data->meta, child,
+ /* should delete = */ 0);
+ else if (strcasecmp("DeleteMetaData", child->key) == 0)
+ status = tr_config_add_meta_action(&data->meta, child,
+ /* should delete = */ 1);
else {
ERROR("Target `replace': The `%s' configuration option is not understood "
"and will be ignored.",
if ((data->host == NULL) && (data->plugin == NULL) &&
(data->plugin_instance == NULL)
/* && (data->type == NULL) */
- && (data->type_instance == NULL)) {
+ && (data->type_instance == NULL) && (data->meta == NULL)) {
ERROR("Target `replace': You need to set at least one of `Host', "
"`Plugin', `PluginInstance' or `TypeInstance'.");
status = -1;
return (-EINVAL);
}
+ if (data->meta != NULL) {
+ tr_meta_data_action_invoke(data->meta, &(vl->meta));
+ }
+
#define HANDLE_FIELD(f, e) \
if (data->f != NULL) \
tr_action_invoke(data->f, vl->f, sizeof(vl->f), e)
HANDLE_FIELD(host, 0);
HANDLE_FIELD(plugin, 0);
HANDLE_FIELD(plugin_instance, 1);
- /* HANDLE_FIELD (type); */
+ /* HANDLE_FIELD (type, 0); */
HANDLE_FIELD(type_instance, 1);
return (FC_TARGET_CONTINUE);
#include "common.h"
#include "filter_chain.h"
+#include "meta_data.h"
+#include "utils_subst.h"
+
+struct ts_key_list_s {
+ char *key;
+ struct ts_key_list_s *next;
+};
+typedef struct ts_key_list_s ts_key_list_t;
+
+static void ts_key_list_free(ts_key_list_t *l) /* {{{ */
+{
+ if (l == NULL)
+ return;
+
+ sfree(l->key);
+
+ if (l->next != NULL)
+ ts_key_list_free(l->next);
+
+ sfree(l);
+} /* }}} void ts_name_list_free */
struct ts_data_s {
char *host;
/* char *type; */
char *type_instance;
meta_data_t *meta;
+ ts_key_list_t *meta_delete;
};
typedef struct ts_data_s ts_data_t;
if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
(ci->values[1].type != OCONFIG_TYPE_STRING)) {
ERROR("ts_util_get_key_and_string_wo_strdup: The %s option requires "
- "exactly two string argument.",
+ "exactly two string arguments.",
ci->key);
return (-1);
}
return (status);
if (strlen(key) == 0) {
- ERROR("Target `set': The `%s' option does not accept empty string as first "
- "argument.",
+ ERROR("Target `set': The `%s' option does not accept empty string as "
+ "first argument.",
ci->key);
return (-1);
}
}
if ((*dest) == NULL) {
- // Create a new meta_data_t
+ /* Create a new meta_data_t */
if ((*dest = meta_data_create()) == NULL) {
ERROR("Target `set': failed to create a meta data for `%s'.", ci->key);
- return (-1);
+ return (-ENOMEM);
}
}
return (meta_data_add_string(*dest, key, string));
} /* }}} int ts_config_add_meta */
+static int ts_config_add_meta_delete(ts_key_list_t **dest, /* {{{ */
+ const oconfig_item_t *ci) {
+ ts_key_list_t *entry = NULL;
+
+ entry = calloc(1, sizeof(*entry));
+ if (entry == NULL) {
+ ERROR("ts_config_add_meta_delete: calloc failed.");
+ return (-ENOMEM);
+ }
+
+ if (cf_util_get_string(ci, &entry->key) != 0) {
+ ts_key_list_free(entry);
+ return (-1); /* An error has already been reported. */
+ }
+
+ if (strlen(entry->key) == 0) {
+ ERROR("Target `set': The `%s' option does not accept empty string as "
+ "first argument.",
+ ci->key);
+ ts_key_list_free(entry);
+ return (-1);
+ }
+
+ entry->next = *dest;
+ *dest = entry;
+
+ return (0);
+} /* }}} int ts_config_add_meta_delete */
+
+static void ts_subst(char *dest, size_t size, const char *string, /* {{{ */
+ const value_list_t *vl) {
+ char temp[DATA_MAX_NAME_LEN];
+
+ /* Initialize the field with the template. */
+ sstrncpy(dest, string, size);
+
+ if (strchr(dest, '%') == NULL)
+ return;
+
+#define REPLACE_FIELD(t, v) \
+ if (subst_string(temp, sizeof(temp), dest, t, v) != NULL) \
+ sstrncpy(dest, temp, size);
+ REPLACE_FIELD("%{host}", vl->host);
+ REPLACE_FIELD("%{plugin}", vl->plugin);
+ REPLACE_FIELD("%{plugin_instance}", vl->plugin_instance);
+ REPLACE_FIELD("%{type}", vl->type);
+ REPLACE_FIELD("%{type_instance}", vl->type_instance);
+
+ if (vl->meta != NULL) {
+ char **meta_toc = NULL;
+ int meta_entries = meta_data_toc(vl->meta, &meta_toc);
+ if (meta_entries <= 0)
+ return;
+
+ for (int i = 0; i < meta_entries; i++) {
+ char meta_name[DATA_MAX_NAME_LEN];
+ char *value_str;
+ const char *key = meta_toc[i];
+
+ ssnprintf(meta_name, sizeof(meta_name), "%%{meta:%s}", key);
+ if (meta_data_as_string(vl->meta, key, &value_str) != 0)
+ continue;
+
+ REPLACE_FIELD(meta_name, value_str);
+ sfree(value_str);
+ }
+
+ strarray_free(meta_toc, (size_t)meta_entries);
+ }
+} /* }}} int ts_subst */
+
static int ts_destroy(void **user_data) /* {{{ */
{
ts_data_t *data;
/* free (data->type); */
free(data->type_instance);
meta_data_destroy(data->meta);
+ ts_key_list_free(data->meta_delete);
free(data);
return (0);
/* data->type = NULL; */
data->type_instance = NULL;
data->meta = NULL;
+ data->meta_delete = NULL;
status = 0;
for (int i = 0; i < ci->children_num; i++) {
else if (strcasecmp("MetaData", child->key) == 0)
status = ts_config_add_meta(&data->meta, child,
/* may be empty = */ 1);
+ else if (strcasecmp("DeleteMetaData", child->key) == 0)
+ status = ts_config_add_meta_delete(&data->meta_delete, child);
else {
ERROR("Target `set': The `%s' configuration option is not understood "
"and will be ignored.",
if ((data->host == NULL) && (data->plugin == NULL) &&
(data->plugin_instance == NULL)
/* && (data->type == NULL) */
- && (data->type_instance == NULL) && (data->meta == NULL)) {
+ && (data->type_instance == NULL) && (data->meta == NULL) &&
+ (data->meta_delete == NULL)) {
ERROR("Target `set': You need to set at least one of `Host', "
- "`Plugin', `PluginInstance', `TypeInstance', `MetaData'.");
+ "`Plugin', `PluginInstance', `TypeInstance', "
+ "`MetaData', or `DeleteMetaData'.");
status = -1;
}
+ if (data->meta != NULL) {
+ /* If data->meta_delete is NULL, this loop is a no-op. */
+ for (ts_key_list_t *l = data->meta_delete; l != NULL; l = l->next) {
+ if (meta_data_type(data->meta, l->key) != 0) {
+ /* MetaData and DeleteMetaData for the same key. */
+ ERROR("Target `set': Can only have one of `MetaData' or "
+ "`DeleteMetaData' for any given key.");
+ status = -1;
+ }
+ }
+ }
+
break;
}
notification_meta_t __attribute__((unused)) * *meta,
void **user_data) {
ts_data_t *data;
+ value_list_t orig;
+ meta_data_t *new_meta = NULL;
if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
return (-EINVAL);
return (-EINVAL);
}
+ orig = *vl;
+
if (data->meta != NULL) {
- meta_data_clone_merge(&(vl->meta), data->meta);
+ char temp[DATA_MAX_NAME_LEN * 2];
+ int meta_entries;
+ char **meta_toc;
+
+ if ((new_meta = meta_data_create()) == NULL) {
+ ERROR("Target `set': failed to create replacement metadata.");
+ return (-ENOMEM);
+ }
+
+ meta_entries = meta_data_toc(data->meta, &meta_toc);
+ for (int i = 0; i < meta_entries; i++) {
+ const char *key = meta_toc[i];
+ char *string;
+ int status;
+
+ status = meta_data_get_string(data->meta, key, &string);
+ if (status) {
+ ERROR("Target `set': Unable to get replacement metadata value `%s'.",
+ key);
+ strarray_free(meta_toc, (size_t)meta_entries);
+ meta_data_destroy(new_meta);
+ return (status);
+ }
+
+ ts_subst(temp, sizeof(temp), string, &orig);
+
+ DEBUG("target_set: ts_invoke: setting metadata value for key `%s': "
+ "`%s'.",
+ key, temp);
+
+ sfree(string);
+
+ status = meta_data_add_string(new_meta, key, temp);
+ if (status) {
+ ERROR("Target `set': Unable to set metadata value `%s'.", key);
+ strarray_free(meta_toc, (size_t)meta_entries);
+ meta_data_destroy(new_meta);
+ return (status);
+ }
+ }
+
+ strarray_free(meta_toc, (size_t)meta_entries);
}
-#define SET_FIELD(f) \
+#define SUBST_FIELD(f) \
if (data->f != NULL) { \
- sstrncpy(vl->f, data->f, sizeof(vl->f)); \
+ ts_subst(vl->f, sizeof(vl->f), data->f, &orig); \
+ DEBUG("target_set: ts_invoke: setting " #f ": `%s'.", vl->f); \
+ }
+ SUBST_FIELD(host);
+ SUBST_FIELD(plugin);
+ SUBST_FIELD(plugin_instance);
+ /* SUBST_FIELD (type); */
+ SUBST_FIELD(type_instance);
+
+ /* Need to merge the metadata in now, because of the shallow copy. */
+ if (new_meta != NULL) {
+ meta_data_clone_merge(&(vl->meta), new_meta);
+ meta_data_destroy(new_meta);
+ }
+
+ /* If data->meta_delete is NULL, this loop is a no-op. */
+ for (ts_key_list_t *l = data->meta_delete; l != NULL; l = l->next) {
+ DEBUG("target_set: ts_invoke: deleting metadata value for key `%s'.",
+ l->key);
+ meta_data_delete(vl->meta, l->key);
}
- SET_FIELD(host);
- SET_FIELD(plugin);
- SET_FIELD(plugin_instance);
- /* SET_FIELD (type); */
- SET_FIELD(type_instance);
return (FC_TARGET_CONTINUE);
} /* }}} int ts_invoke */
static int v5_df(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_value;
/* Can't upgrade if both instances have been set. */
if ((vl->plugin_instance[0] != 0) && (vl->type_instance[0] != 0))
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = &new_value;
+ new_vl.values = &(value_t){.gauge = NAN};
new_vl.values_len = 1;
new_vl.meta = NULL;
static int v5_mysql_qcache(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_value;
if (vl->values_len != 5)
return (FC_TARGET_STOP);
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = &new_value;
+ new_vl.values = &(value_t){.gauge = NAN};
new_vl.values_len = 1;
new_vl.meta = NULL;
static int v5_mysql_threads(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_value;
if (vl->values_len != 4)
return (FC_TARGET_STOP);
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = &new_value;
+ new_vl.values = &(value_t){.gauge = NAN};
new_vl.values_len = 1;
new_vl.meta = NULL;
static int v5_zfs_arc_counts(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_value;
_Bool is_hits;
if (vl->values_len != 4)
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = &new_value;
+ new_vl.values = &(value_t){.gauge = NAN};
new_vl.values_len = 1;
new_vl.meta = NULL;
static int v5_zfs_arc_l2_bytes(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_values[2];
if (vl->values_len != 2)
return (FC_TARGET_STOP);
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = new_values;
- new_vl.values_len = 2;
new_vl.meta = NULL;
/* Change the type/-instance to "io_octets-L2" */
sstrncpy(new_vl.type_instance, "L2", sizeof(new_vl.type_instance));
/* Copy the actual values. */
- new_vl.values[0].derive = (derive_t)vl->values[0].counter;
- new_vl.values[1].derive = (derive_t)vl->values[1].counter;
+ value_t values[] = {
+ {.derive = (derive_t)vl->values[0].counter},
+ {.derive = (derive_t)vl->values[1].counter},
+ };
+ new_vl.values = values;
+ new_vl.values_len = STATIC_ARRAY_SIZE(values);
/* Dispatch new value lists instead of this one */
plugin_dispatch_values(&new_vl);
static int v5_zfs_arc_l2_size(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_value;
if (vl->values_len != 1)
return (FC_TARGET_STOP);
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = &new_value;
+ new_vl.values = &(value_t){.gauge = NAN};
new_vl.values_len = 1;
new_vl.meta = NULL;
static int v5_zfs_arc_ratio(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_value;
if (vl->values_len != 1)
return (FC_TARGET_STOP);
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = &new_value;
+ new_vl.values = &(value_t){.gauge = NAN};
new_vl.values_len = 1;
new_vl.meta = NULL;
static int v5_zfs_arc_size(const data_set_t *ds, value_list_t *vl) /* {{{ */
{
value_list_t new_vl;
- value_t new_value;
if (vl->values_len != 4)
return (FC_TARGET_STOP);
memcpy(&new_vl, vl, sizeof(new_vl));
/* Reset data we can't simply copy */
- new_vl.values = &new_value;
+ new_vl.values = &(value_t){.gauge = NAN};
new_vl.values_len = 1;
new_vl.meta = NULL;
static void conn_prepare_vl(value_list_t *vl, value_t *values) {
vl->values = values;
vl->values_len = 1;
- sstrncpy(vl->host, hostname_g, sizeof(vl->host));
sstrncpy(vl->plugin, "tcpconns", sizeof(vl->plugin));
sstrncpy(vl->type, "tcp_connections", sizeof(vl->type));
}
/*
* Submits a gauge value to the collectd daemon
*/
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "teamspeak2", sizeof(vl.plugin));
if (plugin_instance != NULL)
/*
* Submits the io rx/tx tuple to the collectd daemon
*/
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = rx;
- values[1].derive = tx;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
vl.values = values;
- vl.values_len = 2;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values_len = STATIC_ARRAY_SIZE(values);
sstrncpy(vl.plugin, "teamspeak2", sizeof(vl.plugin));
if (plugin_instance != NULL)
} /* int ted_open_device */
static void ted_submit(const char *type, double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "ted", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
#define END_TEST exit((fail_count__ == 0) ? 0 : 1);
+#define LOG(result, text) \
+ printf("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text)
+
#define OK1(cond, text) \
do { \
_Bool result = (cond); \
- printf("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text); \
+ LOG(result, text); \
if (!result) { \
return -1; \
} \
enum dev_type { TEMP = 0, COOLING_DEV };
static void thermal_submit(const char *plugin_instance, enum dev_type dt,
- gauge_t value) {
+ value_t value) {
value_list_t vl = VALUE_LIST_INIT;
- value_t v;
-
- v.gauge = value;
- vl.values = &v;
+ vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "thermal", sizeof(vl.plugin));
if (plugin_instance != NULL)
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
static int thermal_sysfs_device_read(const char __attribute__((unused)) * dir,
const char *name,
void __attribute__((unused)) * user_data) {
- char filename[256];
- char data[1024];
- int len;
+ char filename[PATH_MAX];
_Bool success = 0;
+ value_t value;
if (device_list && ignorelist_match(device_list, name))
return -1;
- len =
- ssnprintf(filename, sizeof(filename), "%s/%s/temp", dirname_sysfs, name);
- if ((len < 0) || ((size_t)len >= sizeof(filename)))
- return -1;
-
- len = (ssize_t)read_file_contents(filename, data, sizeof(data));
- if (len > 1 && data[--len] == '\n') {
- char *endptr = NULL;
- double temp;
-
- data[len] = 0;
- errno = 0;
- temp = strtod(data, &endptr) / 1000.0;
-
- if (endptr == data + len && errno == 0) {
- thermal_submit(name, TEMP, temp);
- success = 1;
- }
+ ssnprintf(filename, sizeof(filename), "%s/%s/temp", dirname_sysfs, name);
+ if (parse_value_file(filename, &value, DS_TYPE_GAUGE) == 0) {
+ value.gauge /= 1000.0;
+ thermal_submit(name, TEMP, value);
+ success = 1;
}
- len = ssnprintf(filename, sizeof(filename), "%s/%s/cur_state", dirname_sysfs,
- name);
- if ((len < 0) || ((size_t)len >= sizeof(filename)))
- return -1;
-
- len = (ssize_t)read_file_contents(filename, data, sizeof(data));
- if (len > 1 && data[--len] == '\n') {
- char *endptr = NULL;
- double state;
-
- data[len] = 0;
- errno = 0;
- state = strtod(data, &endptr);
-
- if (endptr == data + len && errno == 0) {
- thermal_submit(name, COOLING_DEV, state);
- success = 1;
- }
+ ssnprintf(filename, sizeof(filename), "%s/%s/cur_state", dirname_sysfs, name);
+ if (parse_value_file(filename, &value, DS_TYPE_GAUGE) == 0) {
+ thermal_submit(name, COOLING_DEV, value);
+ success = 1;
}
return (success ? 0 : -1);
temp = (strtod(data + len, &endptr) + add) * factor;
if (endptr != data + len && errno == 0) {
- thermal_submit(name, TEMP, temp);
+ thermal_submit(name, TEMP, (value_t){.gauge = temp});
return 0;
}
}
ERROR("tokyotyrant plugin: error: %d, %s", ecode, tcrdberrmsg(ecode));
}
-static void tt_submit(gauge_t val, const char *type) {
- value_t values[1];
+static void tt_submit(gauge_t value, const char *type) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = val;
-
- vl.values = values;
- vl.values_len = STATIC_ARRAY_SIZE(values);
+ vl.values = &(value_t){.gauge = value};
+ vl.values_len = 1;
sstrncpy(vl.host, config_host, sizeof(vl.host));
sstrncpy(vl.plugin, "tokyotyrant", sizeof(vl.plugin));
static _Bool aperf_mperf_unstable;
/*
+ * If set, use kernel logical core numbering for all "per core" metrics.
+ */
+static _Bool config_lcn;
+
+/*
* Bitmask of the list of core C states supported by the processor.
* Currently supported C-states (by this plugin): 3, 6, 7
*/
"PackageThermalManagement",
"TCCActivationTemp",
"RunningAveragePowerLimit",
+ "LogicalCoreNames",
};
static const int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
static void turbostat_submit(const char *plugin_instance, const char *type,
const char *type_instance, gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
- value_t v;
- v.gauge = value;
- vl.values = &v;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, PLUGIN_NAME, sizeof(vl.plugin));
if (plugin_instance != NULL)
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
interval_float = CDTIME_T_TO_DOUBLE(time_delta);
+ DEBUG("turbostat plugin: submit stats for cpu: %d, core: %d, pkg: %d",
+ t->cpu_id, c->core_id, p->package_id);
+
ssnprintf(name, sizeof(name), "cpu%02d", t->cpu_id);
if (!aperf_mperf_unstable)
if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE))
goto done;
- ssnprintf(name, sizeof(name), "core%02d", c->core_id);
+ /* If not using logical core numbering, set core id */
+ if (!config_lcn) {
+ ssnprintf(name, sizeof(name), "core%02d", c->core_id);
+ }
if (do_core_cstate & (1 << 3))
turbostat_submit(name, "percent", "c3", 100.0 * c->c3 / t->tsc);
parse_int_file(const char *fmt, ...) {
va_list args;
char path[PATH_MAX];
- FILE *filep;
- int len, value;
+ int len;
va_start(args, fmt);
len = vsnprintf(path, sizeof(path), fmt, args);
return -1;
}
- filep = fopen(path, "r");
- if (!filep) {
- ERROR("turbostat plugin: Failed to open '%s'", path);
- return -1;
- }
- if (fscanf(filep, "%d", &value) != 1) {
- ERROR("turbostat plugin: Failed to parse number from '%s'", path);
- fclose(filep);
+ value_t v;
+ if (parse_value_file(path, &v, DS_TYPE_DERIVE) != 0) {
+ ERROR("turbostat plugin: Parsing \"%s\" failed.", path);
return -1;
}
- fclose(filep);
- return value;
+
+ return (int)v.derive;
}
static int get_threads_on_core(unsigned int cpu) {
} else if (strcasecmp("PackageThermalManagement", key) == 0) {
config_ptm = IS_TRUE(value);
apply_config_ptm = 1;
+ } else if (strcasecmp("LogicalCoreNames", key) == 0) {
+ config_lcn = IS_TRUE(value);
} else if (strcasecmp("RunningAveragePowerLimit", key) == 0) {
tmp_val = strtoul(value, &end, 0);
if (*end != '\0' || tmp_val > UINT_MAX) {
backends value:GAUGE:0:65535
bitrate value:GAUGE:0:4294967295
blocked_clients value:GAUGE:0:U
+bucket value:GAUGE:0:U
bytes value:GAUGE:0:U
cache_eviction value:DERIVE:0:U
cache_operation value:DERIVE:0:U
email_check value:GAUGE:0:U
email_count value:GAUGE:0:U
email_size value:GAUGE:0:U
+energy value:GAUGE:U:U
+energy_wh value:GAUGE:U:U
entropy value:GAUGE:0:4294967295
+errors value:DERIVE:0:U
evicted_keys value:DERIVE:0:U
expired_keys value:DERIVE:0:U
fanspeed value:GAUGE:0:U
file_handles value:GAUGE:0:U
file_size value:GAUGE:0:U
files value:GAUGE:0:U
+filter_result value:DERIVE:0:U
flow value:GAUGE:0:U
fork_rate value:DERIVE:0:U
frequency value:GAUGE:0:U
if_multicast value:DERIVE:0:U
if_octets rx:DERIVE:0:U, tx:DERIVE:0:U
if_packets rx:DERIVE:0:U, tx:DERIVE:0:U
+if_rx_dropped value:DERIVE:0:U
if_rx_errors value:DERIVE:0:U
if_rx_octets value:DERIVE:0:U
+if_rx_packets value:DERIVE:0:U
+if_tx_dropped value:DERIVE:0:U
if_tx_errors value:DERIVE:0:U
if_tx_octets value:DERIVE:0:U
+if_tx_packets value:DERIVE:0:U
invocations value:DERIVE:0:U
io_octets rx:DERIVE:0:U, tx:DERIVE:0:U
io_packets rx:DERIVE:0:U, tx:DERIVE:0:U
+ipc value:GAUGE:0:U
ipt_bytes value:DERIVE:0:U
ipt_packets value:DERIVE:0:U
irq value:DERIVE:0:U
latency value:GAUGE:0:U
links value:GAUGE:0:U
load shortterm:GAUGE:0:5000, midterm:GAUGE:0:5000, longterm:GAUGE:0:5000
+memory_bandwidth value:DERIVE:0:U
md_disks value:GAUGE:0:U
memcached_command value:DERIVE:0:U
memcached_connections value:GAUGE:0:U
}
if (strcasecmp(fields[0], "getval") == 0) {
- handle_getval(fhout, buffer);
+ cmd_handle_getval(fhout, buffer);
} else if (strcasecmp(fields[0], "getthreshold") == 0) {
handle_getthreshold(fhout, buffer);
} else if (strcasecmp(fields[0], "putval") == 0) {
- handle_putval(fhout, buffer);
+ cmd_handle_putval(fhout, buffer);
} else if (strcasecmp(fields[0], "listval") == 0) {
- handle_listval(fhout, buffer);
+ cmd_handle_listval(fhout, buffer);
} else if (strcasecmp(fields[0], "putnotif") == 0) {
handle_putnotif(fhout, buffer);
} else if (strcasecmp(fields[0], "flush") == 0) {
- handle_flush(fhout, buffer);
+ cmd_handle_flush(fhout, buffer);
} else {
if (fprintf(fhout, "-1 Unknown command: %s\n", fields[0]) < 0) {
char errbuf[1024];
DEBUG("Spawning child to handle connection on fd #%i", *remote_fd);
status = plugin_thread_create(&th, &th_attr, us_handle_client,
- (void *)remote_fd);
+ (void *)remote_fd, "unixsock conn");
if (status != 0) {
char errbuf[1024];
WARNING("unixsock plugin: pthread_create failed: %s",
loop = 1;
- status = plugin_thread_create(&listen_thread, NULL, us_server_thread, NULL);
+ status = plugin_thread_create(&listen_thread, NULL, us_server_thread, NULL,
+ "unixsock listen");
if (status != 0) {
char errbuf[1024];
ERROR("unixsock plugin: pthread_create failed: %s",
extern kstat_ctl_t *kc;
#endif /* #endif HAVE_LIBKSTAT */
-static void uptime_submit(gauge_t uptime) {
- value_t values[1];
+static void uptime_submit(gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = uptime;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "uptime", sizeof(vl.plugin));
sstrncpy(vl.type, "uptime", sizeof(vl.type));
#endif
static void users_submit(gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "users", sizeof(vl.plugin));
sstrncpy(vl.type, "users", sizeof(vl.plugin));
/**
* collectd - src/utils_cmd_flush.c
- * Copyright (C) 2008 Sebastian Harl
+ * Copyright (C) 2008, 2016 Sebastian Harl
* Copyright (C) 2008 Florian Forster
*
* Permission is hereby granted, free of charge, to any person obtaining a
#include "common.h"
#include "plugin.h"
#include "utils_cmd_flush.h"
-#include "utils_parse_option.h"
-int handle_flush(FILE *fh, char *buffer) {
- int success = 0;
- int error = 0;
-
- double timeout = 0.0;
- char **plugins = NULL;
- size_t plugins_num = 0;
- char **identifiers = NULL;
- size_t identifiers_num = 0;
-
-#define PRINT_TO_SOCK(fh, ...) \
- do { \
- if (fprintf(fh, __VA_ARGS__) < 0) { \
- char errbuf[1024]; \
- WARNING("handle_flush: failed to write to socket #%i: %s", fileno(fh), \
- sstrerror(errno, errbuf, sizeof(errbuf))); \
- strarray_free(plugins, plugins_num); \
- strarray_free(identifiers, identifiers_num); \
- return -1; \
- } \
- fflush(fh); \
- } while (0)
-
- if ((fh == NULL) || (buffer == NULL))
- return (-1);
-
- DEBUG("utils_cmd_flush: handle_flush (fh = %p, buffer = %s);", (void *)fh,
- buffer);
+cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
- if (strncasecmp("FLUSH", buffer, strlen("FLUSH")) != 0) {
- PRINT_TO_SOCK(fh, "-1 Cannot parse command.\n");
- return (-1);
+ if ((ret_flush == NULL) || (opts == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_flush.");
+ return (CMD_ERROR);
}
- buffer += strlen("FLUSH");
- while (*buffer != 0) {
+ for (size_t i = 0; i < argc; i++) {
char *opt_key;
char *opt_value;
int status;
opt_key = NULL;
opt_value = NULL;
- status = parse_option(&buffer, &opt_key, &opt_value);
+ status = cmd_parse_option(argv[i], &opt_key, &opt_value, err);
if (status != 0) {
- PRINT_TO_SOCK(fh, "-1 Parsing options failed.\n");
- strarray_free(plugins, plugins_num);
- strarray_free(identifiers, identifiers_num);
- return (-1);
+ if (status == CMD_NO_OPTION)
+ cmd_error(CMD_PARSE_ERROR, err, "Invalid option string `%s'.", argv[i]);
+ cmd_destroy_flush(ret_flush);
+ return (CMD_PARSE_ERROR);
}
- if (strcasecmp("plugin", opt_key) == 0)
- strarray_add(&plugins, &plugins_num, opt_value);
- else if (strcasecmp("identifier", opt_key) == 0)
- strarray_add(&identifiers, &identifiers_num, opt_value);
- else if (strcasecmp("timeout", opt_key) == 0) {
+ if (strcasecmp("plugin", opt_key) == 0) {
+ strarray_add(&ret_flush->plugins, &ret_flush->plugins_num, opt_value);
+ } else if (strcasecmp("identifier", opt_key) == 0) {
+ identifier_t *id =
+ realloc(ret_flush->identifiers,
+ (ret_flush->identifiers_num + 1) * sizeof(*id));
+ if (id == NULL) {
+ cmd_error(CMD_ERROR, err, "realloc failed.");
+ cmd_destroy_flush(ret_flush);
+ return (CMD_ERROR);
+ }
+
+ ret_flush->identifiers = id;
+ id = ret_flush->identifiers + ret_flush->identifiers_num;
+ ret_flush->identifiers_num++;
+ if (parse_identifier(opt_value, &id->host, &id->plugin,
+ &id->plugin_instance, &id->type, &id->type_instance,
+ opts->identifier_default_host) != 0) {
+ cmd_error(CMD_PARSE_ERROR, err, "Invalid identifier `%s'.", opt_value);
+ cmd_destroy_flush(ret_flush);
+ return (CMD_PARSE_ERROR);
+ }
+ } else if (strcasecmp("timeout", opt_key) == 0) {
char *endptr;
errno = 0;
endptr = NULL;
- timeout = strtod(opt_value, &endptr);
-
- if ((endptr == opt_value) || (errno != 0) || (!isfinite(timeout))) {
- PRINT_TO_SOCK(fh, "-1 Invalid value for option `timeout': "
- "%s\n",
- opt_value);
- strarray_free(plugins, plugins_num);
- strarray_free(identifiers, identifiers_num);
- return (-1);
- } else if (timeout < 0.0) {
- timeout = 0.0;
+ ret_flush->timeout = strtod(opt_value, &endptr);
+
+ if ((endptr == opt_value) || (errno != 0) ||
+ (!isfinite(ret_flush->timeout))) {
+ cmd_error(CMD_PARSE_ERROR, err,
+ "Invalid value for option `timeout': %s", opt_value);
+ cmd_destroy_flush(ret_flush);
+ return (CMD_PARSE_ERROR);
+ } else if (ret_flush->timeout < 0.0) {
+ ret_flush->timeout = 0.0;
}
} else {
- PRINT_TO_SOCK(fh, "-1 Cannot parse option %s\n", opt_key);
- strarray_free(plugins, plugins_num);
- strarray_free(identifiers, identifiers_num);
- return (-1);
+ cmd_error(CMD_PARSE_ERROR, err, "Cannot parse option `%s'.", opt_key);
+ cmd_destroy_flush(ret_flush);
+ return (CMD_PARSE_ERROR);
}
- } /* while (*buffer != 0) */
+ }
+
+ return (CMD_OK);
+} /* cmd_status_t cmd_parse_flush */
+
+cmd_status_t cmd_handle_flush(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_t cmd;
- for (size_t i = 0; (i == 0) || (i < plugins_num); i++) {
+ int success = 0;
+ int error = 0;
+ int status;
+
+ if ((fh == NULL) || (buffer == NULL))
+ return (-1);
+
+ DEBUG("utils_cmd_flush: cmd_handle_flush (fh = %p, buffer = %s);", (void *)fh,
+ buffer);
+
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_FLUSH) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ cmd_destroy(&cmd);
+ return (CMD_UNKNOWN_COMMAND);
+ }
+
+ for (size_t i = 0; (i == 0) || (i < cmd.cmd.flush.plugins_num); i++) {
char *plugin = NULL;
- if (plugins_num != 0)
- plugin = plugins[i];
+ if (cmd.cmd.flush.plugins_num != 0)
+ plugin = cmd.cmd.flush.plugins[i];
- for (size_t j = 0; (j == 0) || (j < identifiers_num); j++) {
+ for (size_t j = 0; (j == 0) || (j < cmd.cmd.flush.identifiers_num); j++) {
char *identifier = NULL;
+ char buffer[1024];
int status;
- if (identifiers_num != 0)
- identifier = identifiers[j];
+ if (cmd.cmd.flush.identifiers_num != 0) {
+ identifier_t *id = cmd.cmd.flush.identifiers + j;
+ if (format_name(buffer, sizeof(buffer), id->host, id->plugin,
+ id->plugin_instance, id->type,
+ id->type_instance) != 0) {
+ error++;
+ continue;
+ }
+ identifier = buffer;
+ }
- status = plugin_flush(plugin, DOUBLE_TO_CDTIME_T(timeout), identifier);
+ status = plugin_flush(plugin, DOUBLE_TO_CDTIME_T(cmd.cmd.flush.timeout),
+ identifier);
if (status == 0)
success++;
else
}
}
- PRINT_TO_SOCK(fh, "0 Done: %i successful, %i errors\n", success, error);
+ cmd_error(CMD_OK, &err, "Done: %i successful, %i errors", success, error);
- strarray_free(plugins, plugins_num);
- strarray_free(identifiers, identifiers_num);
+ cmd_destroy(&cmd);
return (0);
#undef PRINT_TO_SOCK
-} /* int handle_flush */
+} /* cmd_status_t cmd_handle_flush */
+
+void cmd_destroy_flush(cmd_flush_t *flush) {
+ if (flush == NULL)
+ return;
+
+ strarray_free(flush->plugins, flush->plugins_num);
+ flush->plugins = NULL;
+ flush->plugins_num = 0;
+
+ sfree(flush->identifiers);
+ flush->identifiers_num = 0;
+} /* void cmd_destroy_flush */
/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
/**
* collectd - src/utils_cmd_flush.h
- * Copyright (C) 2008 Sebastian Harl
+ * Copyright (C) 2008, 2016 Sebastian Harl
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
#ifndef UTILS_CMD_FLUSH_H
#define UTILS_CMD_FLUSH_H 1
+#include "utils_cmds.h"
+
#include <stdio.h>
-int handle_flush(FILE *fh, char *buffer);
+cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_flush(FILE *fh, char *buffer);
+
+void cmd_destroy_flush(cmd_flush_t *flush);
#endif /* UTILS_CMD_FLUSH_H */
identifier_copy = sstrdup(identifier);
status = parse_identifier(identifier_copy, &host, &plugin, &plugin_instance,
- &type, &type_instance);
+ &type, &type_instance,
+ /* default_host = */ NULL);
if (status != 0) {
DEBUG("handle_getthreshold: Cannot parse identifier `%s'.", identifier);
print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier);
print_to_socket(fh, "%zu Threshold found\n", i);
if (threshold.host[0] != 0)
- print_to_socket(fh, "Host: %s\n", threshold.host) if (
- threshold.plugin[0] !=
- 0) print_to_socket(fh, "Plugin: %s\n",
- threshold.plugin) if (threshold.plugin_instance[0] !=
- 0)
- print_to_socket(fh, "Plugin Instance: %s\n",
- threshold.plugin_instance) if (threshold.type[0] != 0)
- print_to_socket(fh, "Type: %s\n", threshold.type) if (
- threshold.type_instance[0] !=
- 0) print_to_socket(fh, "Type Instance: %s\n",
- threshold
- .type_instance) if (threshold.data_source
- [0] != 0)
- print_to_socket(
- fh, "Data Source: %s\n",
- threshold.data_source) if (!isnan(threshold.warning_min))
- print_to_socket(
- fh, "Warning Min: %g\n",
- threshold
- .warning_min) if (!isnan(threshold.warning_max))
- print_to_socket(
- fh, "Warning Max: %g\n",
- threshold
- .warning_max) if (!isnan(threshold.failure_min))
- print_to_socket(
- fh, "Failure Min: %g\n",
- threshold
- .failure_min) if (!isnan(threshold
- .failure_max))
- print_to_socket(
- fh, "Failure Max: %g\n",
- threshold.failure_max) if (threshold
- .hysteresis >
- 0.0)
- print_to_socket(
- fh, "Hysteresis: %g\n",
- threshold.hysteresis) if (threshold
- .hits > 1)
- print_to_socket(fh, "Hits: %i\n",
- threshold.hits)
-
- return (0);
+ print_to_socket(fh, "Host: %s\n", threshold.host);
+ if (threshold.plugin[0] != 0)
+ print_to_socket(fh, "Plugin: %s\n", threshold.plugin);
+ if (threshold.plugin_instance[0] != 0)
+ print_to_socket(fh, "Plugin Instance: %s\n", threshold.plugin_instance);
+ if (threshold.type[0] != 0)
+ print_to_socket(fh, "Type: %s\n", threshold.type);
+ if (threshold.type_instance[0] != 0)
+ print_to_socket(fh, "Type Instance: %s\n", threshold.type_instance);
+ if (threshold.data_source[0] != 0)
+ print_to_socket(fh, "Data Source: %s\n", threshold.data_source);
+ if (!isnan(threshold.warning_min))
+ print_to_socket(fh, "Warning Min: %g\n", threshold.warning_min);
+ if (!isnan(threshold.warning_max))
+ print_to_socket(fh, "Warning Max: %g\n", threshold.warning_max);
+ if (!isnan(threshold.failure_min))
+ print_to_socket(fh, "Failure Min: %g\n", threshold.failure_min);
+ if (!isnan(threshold.failure_max))
+ print_to_socket(fh, "Failure Max: %g\n", threshold.failure_max);
+ if (threshold.hysteresis > 0.0)
+ print_to_socket(fh, "Hysteresis: %g\n", threshold.hysteresis);
+ if (threshold.hits > 1)
+ print_to_socket(fh, "Hits: %i\n", threshold.hits);
+
+ return (0);
} /* int handle_getthreshold */
/* vim: set sw=2 sts=2 ts=8 et : */
#include "utils_cmd_getval.h"
#include "utils_parse_option.h"
+cmd_status_t cmd_parse_getval(size_t argc, char **argv,
+ cmd_getval_t *ret_getval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
+ char *identifier_copy;
+ int status;
+
+ if ((ret_getval == NULL) || (opts == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_getval.");
+ return (CMD_ERROR);
+ }
+
+ if (argc != 1) {
+ if (argc == 0)
+ cmd_error(CMD_PARSE_ERROR, err, "Missing identifier.");
+ else
+ cmd_error(CMD_PARSE_ERROR, err, "Garbage after identifier: `%s'.",
+ argv[1]);
+ return (CMD_PARSE_ERROR);
+ }
+
+ /* parse_identifier() modifies its first argument,
+ * returning pointers into it */
+ identifier_copy = sstrdup(argv[0]);
+
+ status = parse_identifier(
+ argv[0], &ret_getval->identifier.host, &ret_getval->identifier.plugin,
+ &ret_getval->identifier.plugin_instance, &ret_getval->identifier.type,
+ &ret_getval->identifier.type_instance, opts->identifier_default_host);
+ if (status != 0) {
+ DEBUG("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy);
+ cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
+ identifier_copy);
+ sfree(identifier_copy);
+ return (CMD_PARSE_ERROR);
+ }
+
+ ret_getval->raw_identifier = identifier_copy;
+ return (CMD_OK);
+} /* cmd_status_t cmd_parse_getval */
+
#define print_to_socket(fh, ...) \
do { \
if (fprintf(fh, __VA_ARGS__) < 0) { \
char errbuf[1024]; \
- WARNING("handle_getval: failed to write to socket #%i: %s", fileno(fh), \
- sstrerror(errno, errbuf, sizeof(errbuf))); \
+ WARNING("cmd_handle_getval: failed to write to socket #%i: %s", \
+ fileno(fh), sstrerror(errno, errbuf, sizeof(errbuf))); \
return -1; \
} \
fflush(fh); \
} while (0)
-int handle_getval(FILE *fh, char *buffer) {
- char *command;
- char *identifier;
- char *identifier_copy;
+cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_status_t status;
+ cmd_t cmd;
- char *hostname;
- char *plugin;
- char *plugin_instance;
- char *type;
- char *type_instance;
gauge_t *values;
size_t values_num;
const data_set_t *ds;
- int status;
-
if ((fh == NULL) || (buffer == NULL))
return (-1);
- DEBUG("utils_cmd_getval: handle_getval (fh = %p, buffer = %s);", (void *)fh,
- buffer);
+ DEBUG("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);",
+ (void *)fh, buffer);
- command = NULL;
- status = parse_string(&buffer, &command);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse command.\n");
- return (-1);
- }
- assert(command != NULL);
-
- if (strcasecmp("GETVAL", command) != 0) {
- print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
- return (-1);
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_GETVAL) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ cmd_destroy(&cmd);
+ return (CMD_UNKNOWN_COMMAND);
}
- identifier = NULL;
- status = parse_string(&buffer, &identifier);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse identifier.\n");
- return (-1);
- }
- assert(identifier != NULL);
-
- if (*buffer != 0) {
- print_to_socket(fh, "-1 Garbage after end of command: %s\n", buffer);
- return (-1);
- }
-
- /* parse_identifier() modifies its first argument,
- * returning pointers into it */
- identifier_copy = sstrdup(identifier);
-
- status = parse_identifier(identifier_copy, &hostname, &plugin,
- &plugin_instance, &type, &type_instance);
- if (status != 0) {
- DEBUG("handle_getval: Cannot parse identifier `%s'.", identifier);
- print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier);
- sfree(identifier_copy);
- return (-1);
- }
-
- ds = plugin_get_ds(type);
+ ds = plugin_get_ds(cmd.cmd.getval.identifier.type);
if (ds == NULL) {
- DEBUG("handle_getval: plugin_get_ds (%s) == NULL;", type);
- print_to_socket(fh, "-1 Type `%s' is unknown.\n", type);
- sfree(identifier_copy);
+ DEBUG("cmd_handle_getval: plugin_get_ds (%s) == NULL;",
+ cmd.cmd.getval.identifier.type);
+ cmd_error(CMD_ERROR, &err, "Type `%s' is unknown.\n",
+ cmd.cmd.getval.identifier.type);
+ cmd_destroy(&cmd);
return (-1);
}
values = NULL;
values_num = 0;
- status = uc_get_rate_by_name(identifier, &values, &values_num);
+ status =
+ uc_get_rate_by_name(cmd.cmd.getval.raw_identifier, &values, &values_num);
if (status != 0) {
- print_to_socket(fh, "-1 No such value\n");
- sfree(identifier_copy);
- return (-1);
+ cmd_error(CMD_ERROR, &err, "No such value.");
+ cmd_destroy(&cmd);
+ return (CMD_ERROR);
}
if (ds->ds_num != values_num) {
ERROR("ds[%s]->ds_num = %zu, "
"but uc_get_rate_by_name returned %zu values.",
ds->type, ds->ds_num, values_num);
- print_to_socket(fh, "-1 Error reading value from cache.\n");
+ cmd_error(CMD_ERROR, &err, "Error reading value from cache.");
sfree(values);
- sfree(identifier_copy);
- return (-1);
+ cmd_destroy(&cmd);
+ return (CMD_ERROR);
}
print_to_socket(fh, "%zu Value%s found\n", values_num,
}
sfree(values);
- sfree(identifier_copy);
+ cmd_destroy(&cmd);
+
+ return (CMD_OK);
+} /* cmd_status_t cmd_handle_getval */
+
+void cmd_destroy_getval(cmd_getval_t *getval) {
+ if (getval == NULL)
+ return;
- return (0);
-} /* int handle_getval */
+ sfree(getval->raw_identifier);
+} /* void cmd_destroy_getval */
/* vim: set sw=2 sts=2 ts=8 : */
#include <stdio.h>
-int handle_getval(FILE *fh, char *buffer);
+#include "utils_cmds.h"
+
+cmd_status_t cmd_parse_getval(size_t argc, char **argv,
+ cmd_getval_t *ret_getval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_getval(FILE *fh, char *buffer);
+
+void cmd_destroy_getval(cmd_getval_t *getval);
#endif /* UTILS_CMD_GETVAL_H */
#include "utils_cmd_listval.h"
#include "utils_parse_option.h"
+cmd_status_t cmd_parse_listval(size_t argc, char **argv,
+ cmd_listval_t *ret_listval
+ __attribute__((unused)),
+ const cmd_options_t *opts
+ __attribute__((unused)),
+ cmd_error_handler_t *err) {
+ if (argc != 0) {
+ cmd_error(CMD_PARSE_ERROR, err, "Garbage after end of command: `%s'.",
+ argv[0]);
+ return (CMD_PARSE_ERROR);
+ }
+
+ return (CMD_OK);
+} /* cmd_status_t cmd_parse_listval */
+
#define free_everything_and_return(status) \
do { \
for (size_t j = 0; j < number; j++) { \
char errbuf[1024]; \
WARNING("handle_listval: failed to write to socket #%i: %s", fileno(fh), \
sstrerror(errno, errbuf, sizeof(errbuf))); \
- free_everything_and_return(-1); \
+ free_everything_and_return(CMD_ERROR); \
} \
fflush(fh); \
} while (0)
-int handle_listval(FILE *fh, char *buffer) {
- char *command;
+cmd_status_t cmd_handle_listval(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_status_t status;
+ cmd_t cmd;
+
char **names = NULL;
cdtime_t *times = NULL;
size_t number = 0;
- int status;
DEBUG("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);", (void *)fh,
buffer);
- command = NULL;
- status = parse_string(&buffer, &command);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse command.\n");
- free_everything_and_return(-1);
- }
- assert(command != NULL);
-
- if (strcasecmp("LISTVAL", command) != 0) {
- print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
- free_everything_and_return(-1);
- }
-
- if (*buffer != 0) {
- print_to_socket(fh, "-1 Garbage after end of command: %s\n", buffer);
- free_everything_and_return(-1);
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_LISTVAL) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ free_everything_and_return(CMD_UNKNOWN_COMMAND);
}
status = uc_get_names(&names, ×, &number);
if (status != 0) {
DEBUG("command listval: uc_get_names failed with status %i", status);
- print_to_socket(fh, "-1 uc_get_names failed.\n");
- free_everything_and_return(-1);
+ cmd_error(CMD_ERROR, &err, "uc_get_names failed.");
+ free_everything_and_return(CMD_ERROR);
}
print_to_socket(fh, "%i Value%s found\n", (int)number,
for (size_t i = 0; i < number; i++)
print_to_socket(fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE(times[i]), names[i]);
- free_everything_and_return(0);
-} /* int handle_listval */
+ free_everything_and_return(CMD_OK);
+} /* cmd_status_t cmd_handle_listval */
+
+void cmd_destroy_listval(cmd_listval_t *listval __attribute__((unused))) {
+ /* nothing to do */
+} /* void cmd_destroy_listval */
/* vim: set sw=2 sts=2 ts=8 : */
#include <stdio.h>
-int handle_listval(FILE *fh, char *buffer);
+#include "utils_cmds.h"
+
+cmd_status_t cmd_parse_listval(size_t argc, char **argv,
+ cmd_listval_t *ret_listval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_listval(FILE *fh, char *buffer);
+
+void cmd_destroy_listval(cmd_listval_t *listval);
#endif /* UTILS_CMD_LISTVAL_H */
/**
* collectd - src/utils_cmd_putval.c
* Copyright (C) 2007-2009 Florian octo Forster
+ * Copyright (C) 2016 Sebastian tokkee Harl
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*
* Authors:
* Florian octo Forster <octo at collectd.org>
+ * Sebastian tokkee Harl <sh at tokkee.org>
**/
#include "collectd.h"
#include "common.h"
-#include "plugin.h"
-
#include "utils_cmd_putval.h"
-#include "utils_parse_option.h"
-
-#define print_to_socket(fh, ...) \
- do { \
- if (fprintf(fh, __VA_ARGS__) < 0) { \
- char errbuf[1024]; \
- WARNING("handle_putval: failed to write to socket #%i: %s", fileno(fh), \
- sstrerror(errno, errbuf, sizeof(errbuf))); \
- sfree(vl.values); \
- return -1; \
- } \
- fflush(fh); \
- } while (0)
+
+/*
+ * private helper functions
+ */
static int set_option(value_list_t *vl, const char *key, const char *value) {
if ((vl == NULL) || (key == NULL) || (value == NULL))
return (1);
return (0);
-} /* int parse_option */
+} /* int set_option */
+
+/*
+ * public API
+ */
+
+cmd_status_t cmd_parse_putval(size_t argc, char **argv,
+ cmd_putval_t *ret_putval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
+ cmd_status_t result;
-int handle_putval(FILE *fh, char *buffer) {
- char *command;
char *identifier;
char *hostname;
char *plugin;
char *type;
char *type_instance;
int status;
- int values_submitted;
char *identifier_copy;
const data_set_t *ds;
value_list_t vl = VALUE_LIST_INIT;
- vl.values = NULL;
-
- DEBUG("utils_cmd_putval: handle_putval (fh = %p, buffer = %s);", (void *)fh,
- buffer);
- command = NULL;
- status = parse_string(&buffer, &command);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse command.\n");
- return (-1);
+ if ((ret_putval == NULL) || (opts == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_putval.");
+ return (CMD_ERROR);
}
- assert(command != NULL);
- if (strcasecmp("PUTVAL", command) != 0) {
- print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
- return (-1);
+ if (argc < 2) {
+ cmd_error(CMD_PARSE_ERROR, err, "Missing identifier and/or value-list.");
+ return (CMD_PARSE_ERROR);
}
- identifier = NULL;
- status = parse_string(&buffer, &identifier);
- if (status != 0) {
- print_to_socket(fh, "-1 Cannot parse identifier.\n");
- return (-1);
- }
- assert(identifier != NULL);
+ identifier = argv[0];
- /* parse_identifier() modifies its first argument,
- * returning pointers into it */
+ /* parse_identifier() modifies its first argument, returning pointers into
+ * it; retain the old value for later. */
identifier_copy = sstrdup(identifier);
- status = parse_identifier(identifier_copy, &hostname, &plugin,
- &plugin_instance, &type, &type_instance);
+ status =
+ parse_identifier(identifier, &hostname, &plugin, &plugin_instance, &type,
+ &type_instance, opts->identifier_default_host);
if (status != 0) {
- DEBUG("handle_putval: Cannot parse identifier `%s'.", identifier);
- print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier);
+ DEBUG("cmd_handle_putval: Cannot parse identifier `%s'.", identifier_copy);
+ cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
+ identifier_copy);
sfree(identifier_copy);
- return (-1);
+ return (CMD_PARSE_ERROR);
}
if ((strlen(hostname) >= sizeof(vl.host)) ||
(strlen(plugin_instance) >= sizeof(vl.plugin_instance))) ||
((type_instance != NULL) &&
(strlen(type_instance) >= sizeof(vl.type_instance)))) {
- print_to_socket(fh, "-1 Identifier too long.\n");
+ cmd_error(CMD_PARSE_ERROR, err, "Identifier too long.");
sfree(identifier_copy);
- return (-1);
+ return (CMD_PARSE_ERROR);
}
sstrncpy(vl.host, hostname, sizeof(vl.host));
ds = plugin_get_ds(type);
if (ds == NULL) {
- print_to_socket(fh, "-1 Type `%s' isn't defined.\n", type);
+ cmd_error(CMD_PARSE_ERROR, err, "1 Type `%s' isn't defined.", type);
sfree(identifier_copy);
- return (-1);
+ return (CMD_PARSE_ERROR);
}
- /* Free identifier_copy */
hostname = NULL;
plugin = NULL;
plugin_instance = NULL;
type = NULL;
type_instance = NULL;
- sfree(identifier_copy);
- vl.values_len = ds->ds_num;
- vl.values = malloc(vl.values_len * sizeof(*vl.values));
- if (vl.values == NULL) {
- print_to_socket(fh, "-1 malloc failed.\n");
- return (-1);
+ ret_putval->raw_identifier = identifier_copy;
+ if (ret_putval->raw_identifier == NULL) {
+ cmd_error(CMD_ERROR, err, "malloc failed.");
+ cmd_destroy_putval(ret_putval);
+ sfree(vl.values);
+ return (CMD_ERROR);
}
- /* All the remaining fields are part of the optionlist. */
- values_submitted = 0;
- while (*buffer != 0) {
- char *string = NULL;
+ /* All the remaining fields are part of the option list. */
+ result = CMD_OK;
+ for (size_t i = 1; i < argc; ++i) {
+ value_list_t *tmp;
+
+ char *key = NULL;
char *value = NULL;
- status = parse_option(&buffer, &string, &value);
- if (status < 0) {
- /* parse_option failed, buffer has been modified.
- * => we need to abort */
- print_to_socket(fh, "-1 Misformatted option.\n");
- sfree(vl.values);
- return (-1);
- } else if (status == 0) {
- assert(string != NULL);
+ status = cmd_parse_option(argv[i], &key, &value, err);
+ if (status == CMD_OK) {
+ assert(key != NULL);
assert(value != NULL);
- set_option(&vl, string, value);
+ set_option(&vl, key, value);
continue;
+ } else if (status != CMD_NO_OPTION) {
+ /* parse_option failed, buffer has been modified.
+ * => we need to abort */
+ result = status;
+ break;
+ }
+ /* else: cmd_parse_option did not find an option; treat this as a
+ * value list. */
+
+ vl.values_len = ds->ds_num;
+ vl.values = calloc(vl.values_len, sizeof(*vl.values));
+ if (vl.values == NULL) {
+ cmd_error(CMD_ERROR, err, "malloc failed.");
+ result = CMD_ERROR;
+ break;
}
- /* else: parse_option but buffer has not been modified. This is
- * the default if no `=' is found.. */
- status = parse_string(&buffer, &string);
+ status = parse_values(argv[i], &vl, ds);
if (status != 0) {
- print_to_socket(fh, "-1 Misformatted value.\n");
+ cmd_error(CMD_PARSE_ERROR, err, "Parsing the values string failed.");
+ result = CMD_PARSE_ERROR;
+ vl.values_len = 0;
sfree(vl.values);
- return (-1);
+ break;
}
- assert(string != NULL);
- status = parse_values(string, &vl, ds);
- if (status != 0) {
- print_to_socket(fh, "-1 Parsing the values string failed.\n");
+ tmp = realloc(ret_putval->vl,
+ (ret_putval->vl_num + 1) * sizeof(*ret_putval->vl));
+ if (tmp == NULL) {
+ cmd_error(CMD_ERROR, err, "realloc failed.");
+ cmd_destroy_putval(ret_putval);
+ result = CMD_ERROR;
+ vl.values_len = 0;
sfree(vl.values);
- return (-1);
+ break;
}
- plugin_dispatch_values(&vl);
- values_submitted++;
+ ret_putval->vl = tmp;
+ ret_putval->vl_num++;
+ memcpy(&ret_putval->vl[ret_putval->vl_num - 1], &vl, sizeof(vl));
+
+ /* pointer is now owned by ret_putval->vl[] */
+ vl.values_len = 0;
+ vl.values = NULL;
} /* while (*buffer != 0) */
/* Done parsing the options. */
+ if (result != CMD_OK)
+ cmd_destroy_putval(ret_putval);
+
+ return (result);
+} /* cmd_status_t cmd_parse_putval */
+
+void cmd_destroy_putval(cmd_putval_t *putval) {
+ if (putval == NULL)
+ return;
+
+ sfree(putval->raw_identifier);
+
+ for (size_t i = 0; i < putval->vl_num; ++i) {
+ sfree(putval->vl[i].values);
+ meta_data_destroy(putval->vl[i].meta);
+ putval->vl[i].meta = NULL;
+ }
+ sfree(putval->vl);
+ putval->vl = NULL;
+ putval->vl_num = 0;
+} /* void cmd_destroy_putval */
+
+cmd_status_t cmd_handle_putval(FILE *fh, char *buffer) {
+ cmd_error_handler_t err = {cmd_error_fh, fh};
+ cmd_t cmd;
+
+ int status;
+
+ DEBUG("utils_cmd_putval: cmd_handle_putval (fh = %p, buffer = %s);",
+ (void *)fh, buffer);
+
+ if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_PUTVAL) {
+ cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING(cmd.type));
+ cmd_destroy(&cmd);
+ return (CMD_UNKNOWN_COMMAND);
+ }
+
+ for (size_t i = 0; i < cmd.cmd.putval.vl_num; ++i)
+ plugin_dispatch_values(&cmd.cmd.putval.vl[i]);
+
if (fh != stdout)
- print_to_socket(fh, "0 Success: %i %s been dispatched.\n", values_submitted,
- (values_submitted == 1) ? "value has" : "values have");
+ cmd_error(CMD_OK, &err, "Success: %i %s been dispatched.",
+ (int)cmd.cmd.putval.vl_num,
+ (cmd.cmd.putval.vl_num == 1) ? "value has" : "values have");
- sfree(vl.values);
- return (0);
-} /* int handle_putval */
+ cmd_destroy(&cmd);
+ return (CMD_OK);
+} /* int cmd_handle_putval */
-int create_putval(char *ret, size_t ret_len, /* {{{ */
- const data_set_t *ds, const value_list_t *vl) {
+int cmd_create_putval(char *ret, size_t ret_len, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl) {
char buffer_ident[6 * DATA_MAX_NAME_LEN];
char buffer_values[1024];
int status;
buffer_values);
return (0);
-} /* }}} int create_putval */
+} /* }}} int cmd_create_putval */
#ifndef UTILS_CMD_PUTVAL_H
#define UTILS_CMD_PUTVAL_H 1
+#include "plugin.h"
+#include "utils_cmds.h"
+
#include <stdio.h>
-#include "plugin.h"
+cmd_status_t cmd_parse_putval(size_t argc, char **argv,
+ cmd_putval_t *ret_putval,
+ const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_putval(FILE *fh, char *buffer);
-int handle_putval(FILE *fh, char *buffer);
+void cmd_destroy_putval(cmd_putval_t *putval);
-int create_putval(char *ret, size_t ret_len, const data_set_t *ds,
- const value_list_t *vl);
+int cmd_create_putval(char *ret, size_t ret_len, const data_set_t *ds,
+ const value_list_t *vl);
#endif /* UTILS_CMD_PUTVAL_H */
--- /dev/null
+/**
+ * collectd - src/utils_cmds.c
+ * Copyright (C) 2008 Florian Forster
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#include "daemon/common.h"
+#include "utils_cmd_flush.h"
+#include "utils_cmd_getval.h"
+#include "utils_cmd_listval.h"
+#include "utils_cmd_putval.h"
+#include "utils_cmds.h"
+#include "utils_parse_option.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+static cmd_options_t default_options = {
+ /* identifier_default_host = */ NULL,
+};
+
+/*
+ * private helper functions
+ */
+
+static cmd_status_t cmd_split(char *buffer, size_t *ret_len, char ***ret_fields,
+ cmd_error_handler_t *err) {
+ char *field;
+ bool in_field, in_quotes;
+
+ size_t estimate, len;
+ char **fields;
+
+ estimate = 0;
+ in_field = false;
+ for (char *string = buffer; *string != '\0'; ++string) {
+ /* Make a quick worst-case estimate of the number of fields by
+ * counting spaces and ignoring quotation marks. */
+ if (!isspace((int)*string)) {
+ if (!in_field) {
+ estimate++;
+ in_field = true;
+ }
+ } else {
+ in_field = false;
+ }
+ }
+
+ /* fields will be NULL-terminated */
+ fields = malloc((estimate + 1) * sizeof(*fields));
+ if (fields == NULL) {
+ cmd_error(CMD_ERROR, err, "malloc failed.");
+ return (CMD_ERROR);
+ }
+
+#define END_FIELD() \
+ do { \
+ *field = '\0'; \
+ field = NULL; \
+ in_field = false; \
+ } while (0)
+#define NEW_FIELD() \
+ do { \
+ field = string; \
+ in_field = true; \
+ assert(len < estimate); \
+ fields[len] = field; \
+ field++; \
+ len++; \
+ } while (0)
+
+ len = 0;
+ field = NULL;
+ in_field = false;
+ in_quotes = false;
+ for (char *string = buffer; *string != '\0'; string++) {
+ if (isspace((int)string[0])) {
+ if (!in_quotes) {
+ if (in_field)
+ END_FIELD();
+
+ /* skip space */
+ continue;
+ }
+ } else if (string[0] == '"') {
+ /* Note: Two consecutive quoted fields not separated by space are
+ * treated as different fields. This is the collectd 5.x behavior
+ * around splitting fields. */
+
+ if (in_quotes) {
+ /* end of quoted field */
+ if (!in_field) /* empty quoted string */
+ NEW_FIELD();
+ END_FIELD();
+ in_quotes = false;
+ continue;
+ }
+
+ in_quotes = true;
+ /* if (! in_field): add new field on next iteration
+ * else: quoted string following an unquoted string (one field)
+ * in either case: skip quotation mark */
+ continue;
+ } else if ((string[0] == '\\') && in_quotes) {
+ /* Outside of quotes, a backslash is a regular character (mostly
+ * for backward compatibility). */
+
+ if (string[1] == '\0') {
+ free(fields);
+ cmd_error(CMD_PARSE_ERROR, err, "Backslash at end of string.");
+ return (CMD_PARSE_ERROR);
+ }
+
+ /* un-escape the next character; skip backslash */
+ string++;
+ }
+
+ if (!in_field)
+ NEW_FIELD();
+ else {
+ *field = string[0];
+ field++;
+ }
+ }
+
+ if (in_quotes) {
+ free(fields);
+ cmd_error(CMD_PARSE_ERROR, err, "Unterminated quoted string.");
+ return (CMD_PARSE_ERROR);
+ }
+
+#undef NEW_FIELD
+#undef END_FIELD
+
+ fields[len] = NULL;
+ if (ret_len != NULL)
+ *ret_len = len;
+ if (ret_fields != NULL)
+ *ret_fields = fields;
+ else
+ free(fields);
+ return (CMD_OK);
+} /* int cmd_split */
+
+/*
+ * public API
+ */
+
+void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
+ const char *format, ...) {
+ va_list ap;
+
+ if ((err == NULL) || (err->cb == NULL))
+ return;
+
+ va_start(ap, format);
+ err->cb(err->ud, status, format, ap);
+ va_end(ap);
+} /* void cmd_error */
+
+cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
+ const cmd_options_t *opts, cmd_error_handler_t *err) {
+ char *command = NULL;
+ cmd_status_t status;
+
+ if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL)) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Missing command.");
+ return CMD_ERROR;
+ }
+
+ if (opts == NULL)
+ opts = &default_options;
+
+ memset(ret_cmd, 0, sizeof(*ret_cmd));
+ command = argv[0];
+ if (strcasecmp("FLUSH", command) == 0) {
+ ret_cmd->type = CMD_FLUSH;
+ status =
+ cmd_parse_flush(argc - 1, argv + 1, &ret_cmd->cmd.flush, opts, err);
+ } else if (strcasecmp("GETVAL", command) == 0) {
+ ret_cmd->type = CMD_GETVAL;
+ status =
+ cmd_parse_getval(argc - 1, argv + 1, &ret_cmd->cmd.getval, opts, err);
+ } else if (strcasecmp("LISTVAL", command) == 0) {
+ ret_cmd->type = CMD_LISTVAL;
+ status =
+ cmd_parse_listval(argc - 1, argv + 1, &ret_cmd->cmd.listval, opts, err);
+ } else if (strcasecmp("PUTVAL", command) == 0) {
+ ret_cmd->type = CMD_PUTVAL;
+ status =
+ cmd_parse_putval(argc - 1, argv + 1, &ret_cmd->cmd.putval, opts, err);
+ } else {
+ ret_cmd->type = CMD_UNKNOWN;
+ cmd_error(CMD_UNKNOWN_COMMAND, err, "Unknown command `%s'.", command);
+ return (CMD_UNKNOWN_COMMAND);
+ }
+
+ if (status != CMD_OK)
+ ret_cmd->type = CMD_UNKNOWN;
+ return (status);
+} /* cmd_status_t cmd_parsev */
+
+cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts,
+ cmd_error_handler_t *err) {
+ char **fields = NULL;
+ size_t fields_num = 0;
+ cmd_status_t status;
+
+ if ((status = cmd_split(buffer, &fields_num, &fields, err)) != CMD_OK)
+ return status;
+
+ status = cmd_parsev(fields_num, fields, ret_cmd, opts, err);
+ free(fields);
+ return (status);
+} /* cmd_status_t cmd_parse */
+
+void cmd_destroy(cmd_t *cmd) {
+ if (cmd == NULL)
+ return;
+
+ switch (cmd->type) {
+ case CMD_UNKNOWN:
+ /* nothing to do */
+ break;
+ case CMD_FLUSH:
+ cmd_destroy_flush(&cmd->cmd.flush);
+ break;
+ case CMD_GETVAL:
+ cmd_destroy_getval(&cmd->cmd.getval);
+ break;
+ case CMD_LISTVAL:
+ cmd_destroy_listval(&cmd->cmd.listval);
+ break;
+ case CMD_PUTVAL:
+ cmd_destroy_putval(&cmd->cmd.putval);
+ break;
+ }
+} /* void cmd_destroy */
+
+cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value,
+ cmd_error_handler_t *err) {
+ char *key, *value;
+
+ if (field == NULL) {
+ errno = EINVAL;
+ cmd_error(CMD_ERROR, err, "Invalid argument to cmd_parse_option.");
+ return (CMD_ERROR);
+ }
+ key = value = field;
+
+ /* Look for the equal sign. */
+ while (isalnum((int)value[0]) || (value[0] == '_') || (value[0] == ':'))
+ value++;
+ if ((value[0] != '=') || (value == key)) {
+ /* Whether this is a fatal error is up to the caller. */
+ return (CMD_NO_OPTION);
+ }
+ *value = '\0';
+ value++;
+
+ if (ret_key != NULL)
+ *ret_key = key;
+ if (ret_value != NULL)
+ *ret_value = value;
+
+ return (CMD_OK);
+} /* cmd_status_t cmd_parse_option */
+
+void cmd_error_fh(void *ud, cmd_status_t status, const char *format,
+ va_list ap) {
+ FILE *fh = ud;
+ int code = -1;
+ char buf[1024];
+
+ if (status == CMD_OK)
+ code = 0;
+
+ vsnprintf(buf, sizeof(buf), format, ap);
+ buf[sizeof(buf) - 1] = '\0';
+ if (fprintf(fh, "%i %s\n", code, buf) < 0) {
+ char errbuf[1024];
+ WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh),
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return;
+ }
+
+ fflush(fh);
+} /* void cmd_error_fh */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
--- /dev/null
+/**
+ * collectd - src/utils_cmds.h
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_CMDS_H
+#define UTILS_CMDS_H 1
+
+#include "plugin.h"
+
+#include <stdarg.h>
+
+typedef enum {
+ CMD_UNKNOWN = 0,
+ CMD_FLUSH = 1,
+ CMD_GETVAL = 2,
+ CMD_LISTVAL = 3,
+ CMD_PUTVAL = 4,
+} cmd_type_t;
+#define CMD_TO_STRING(type) \
+ ((type) == CMD_FLUSH) ? "FLUSH" : ((type) == CMD_GETVAL) \
+ ? "GETVAL" \
+ : ((type) == CMD_LISTVAL) \
+ ? "LISTVAL" \
+ : ((type) == CMD_PUTVAL) \
+ ? "PUTVAL" \
+ : "UNKNOWN"
+
+typedef struct {
+ double timeout;
+
+ char **plugins;
+ size_t plugins_num;
+ identifier_t *identifiers;
+ size_t identifiers_num;
+} cmd_flush_t;
+
+typedef struct {
+ char *raw_identifier;
+ identifier_t identifier;
+} cmd_getval_t;
+
+typedef struct {
+} cmd_listval_t;
+
+typedef struct {
+ /* The raw identifier as provided by the user. */
+ char *raw_identifier;
+
+ /* An array of the fully parsed identifier and all value lists, and their
+ * options as provided by the user. */
+ value_list_t *vl;
+ size_t vl_num;
+} cmd_putval_t;
+
+/*
+ * NAME
+ * cmd_t
+ *
+ * DESCRIPTION
+ * The representation of a fully parsed command.
+ */
+typedef struct {
+ cmd_type_t type;
+ union {
+ cmd_flush_t flush;
+ cmd_getval_t getval;
+ cmd_listval_t listval;
+ cmd_putval_t putval;
+ } cmd;
+} cmd_t;
+
+/*
+ * NAME
+ * cmd_options_t
+ *
+ * DESCRIPTIONS
+ * Optional settings for tuning the parser behavior.
+ */
+typedef struct {
+ /* identifier_default_host: If non-NULL, the hostname is optional and will
+ * default to the specified value. */
+ char *identifier_default_host;
+} cmd_options_t;
+
+/*
+ * NAME
+ * cmd_status_t
+ *
+ * DESCRIPTION
+ * Status codes describing the parse result.
+ */
+typedef enum {
+ CMD_OK = 0,
+ CMD_ERROR = -1,
+ CMD_PARSE_ERROR = -2,
+ CMD_UNKNOWN_COMMAND = -3,
+
+ /* Not necessarily fatal errors. */
+ CMD_NO_OPTION = 1,
+} cmd_status_t;
+
+/*
+ * NAME
+ * cmd_error_handler_t
+ *
+ * DESCRIPTION
+ * An error handler describes a callback to be invoked when the parser
+ * encounters an error. The user data pointer will be passed to the callback
+ * as the first argument.
+ */
+typedef struct {
+ void (*cb)(void *, cmd_status_t, const char *, va_list);
+ void *ud;
+} cmd_error_handler_t;
+
+/*
+ * NAME:
+ * cmd_error
+ *
+ * DESCRIPTION
+ * Reports an error via the specified error handler (if set).
+ */
+void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
+ const char *format, ...);
+
+/*
+ * NAME
+ * cmd_parse
+ *
+ * DESCRIPTION
+ * Parse a command string and populate a command object.
+ *
+ * PARAMETERS
+ * `buffer' The command string to be parsed.
+ * `ret_cmd' The parse result will be stored at this location.
+ * `opts' Parser options. If NULL, defaults will be used.
+ * `err' An optional error handler to invoke on error.
+ *
+ * RETURN VALUE
+ * CMD_OK on success or the respective error code otherwise.
+ */
+cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts,
+ cmd_error_handler_t *err);
+
+cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
+ const cmd_options_t *opts, cmd_error_handler_t *err);
+
+void cmd_destroy(cmd_t *cmd);
+
+/*
+ * NAME
+ * cmd_parse_option
+ *
+ * DESCRIPTION
+ * Parses a command option which must be of the form:
+ * name=value with \ and spaces
+ *
+ * PARAMETERS
+ * `field' The parsed input field with any quotes removed and special
+ * characters unescaped.
+ * `ret_key' The parsed key will be stored at this location.
+ * `ret_value' The parsed value will be stored at this location.
+ *
+ * RETURN VALUE
+ * CMD_OK on success or an error code otherwise.
+ * CMD_NO_OPTION if `field' does not represent an option at all (missing
+ * equal sign).
+ */
+cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value,
+ cmd_error_handler_t *err);
+
+/*
+ * NAME
+ * cmd_error_fh
+ *
+ * DESCRIPTION
+ * An error callback writing the message to an open file handle using the
+ * format expected by the unixsock or exec plugins.
+ *
+ * PARAMETERS
+ * `ud' Error handler user-data pointer. This must be an open
+ * file-handle (FILE *).
+ * `status' The error status code.
+ * `format' Printf-style format string.
+ * `ap' Variable argument list providing the arguments for the format
+ * string.
+ */
+void cmd_error_fh(void *ud, cmd_status_t status, const char *format,
+ va_list ap);
+
+#endif /* UTILS_CMDS_H */
--- /dev/null
+/**
+ * collectd - src/tests/utils_cmds_test.c
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#include "common.h"
+#include "testing.h"
+#include "utils_cmds.h"
+
+static void error_cb(void *ud, cmd_status_t status, const char *format,
+ va_list ap) {
+ if (status == CMD_OK)
+ return;
+
+ printf("ERROR[%d]: ", status);
+ vprintf(format, ap);
+ printf("\n");
+ fflush(stdout);
+} /* void error_cb */
+
+static cmd_options_t default_host_opts = {
+ /* identifier_default_host = */ "dummy-host",
+};
+
+static struct {
+ char *input;
+ cmd_options_t *opts;
+ cmd_status_t expected_status;
+ cmd_type_t expected_type;
+} parse_data[] = {
+ /* Valid FLUSH commands. */
+ {
+ "FLUSH", NULL, CMD_OK, CMD_FLUSH,
+ },
+ {
+ "FLUSH identifier=myhost/magic/MAGIC", NULL, CMD_OK, CMD_FLUSH,
+ },
+ {
+ "FLUSH identifier=magic/MAGIC", &default_host_opts, CMD_OK, CMD_FLUSH,
+ },
+ {
+ "FLUSH timeout=123 plugin=\"A\"", NULL, CMD_OK, CMD_FLUSH,
+ },
+ /* Invalid FLUSH commands. */
+ {
+ /* Missing hostname; no default. */
+ "FLUSH identifier=magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Missing 'identifier' key. */
+ "FLUSH myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Invalid timeout. */
+ "FLUSH timeout=A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Invalid identifier. */
+ "FLUSH identifier=invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ /* Invalid option. */
+ "FLUSH invalid=option", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+
+ /* Valid GETVAL commands. */
+ {
+ "GETVAL myhost/magic/MAGIC", NULL, CMD_OK, CMD_GETVAL,
+ },
+ {
+ "GETVAL magic/MAGIC", &default_host_opts, CMD_OK, CMD_GETVAL,
+ },
+
+ /* Invalid GETVAL commands. */
+ {
+ "GETVAL magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "GETVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "GETVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+
+ /* Valid LISTVAL commands. */
+ {
+ "LISTVAL", NULL, CMD_OK, CMD_LISTVAL,
+ },
+
+ /* Invalid LISTVAL commands. */
+ {
+ "LISTVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+
+ /* Valid PUTVAL commands. */
+ {
+ "PUTVAL magic/MAGIC N:42", &default_host_opts, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC N:42", NULL, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC 1234:42", NULL, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC 1234:42 2345:23", NULL, CMD_OK, CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC interval=2 1234:42", NULL, CMD_OK,
+ CMD_PUTVAL,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23", NULL,
+ CMD_OK, CMD_PUTVAL,
+ },
+
+ /* Invalid PUTVAL commands. */
+ {
+ "PUTVAL magic/MAGIC N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL invalid N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC A:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN,
+ },
+ {
+ "PUTVAL myhost/magic/UNKNOWN 1234:42", NULL, CMD_PARSE_ERROR,
+ CMD_UNKNOWN,
+ },
+ /*
+ * As of collectd 5.x, PUTVAL accepts invalid options.
+ {
+ "PUTVAL myhost/magic/MAGIC invalid=2 1234:42",
+ NULL,
+ CMD_PARSE_ERROR,
+ CMD_UNKNOWN,
+ },
+ */
+
+ /* Invalid commands. */
+ {
+ "INVALID", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN,
+ },
+ {
+ "INVALID interval=2", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN,
+ },
+};
+
+DEF_TEST(parse) {
+ cmd_error_handler_t err = {error_cb, NULL};
+ int test_result = 0;
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(parse_data); i++) {
+ char *input = strdup(parse_data[i].input);
+
+ char description[1024];
+ cmd_status_t status;
+ cmd_t cmd;
+
+ _Bool result;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ status = cmd_parse(input, &cmd, parse_data[i].opts, &err);
+ snprintf(description, sizeof(description), "cmd_parse (\"%s\", opts=%p) = "
+ "%d (type=%d [%s]); want %d "
+ "(type=%d [%s])",
+ parse_data[i].input, parse_data[i].opts, status, cmd.type,
+ CMD_TO_STRING(cmd.type), parse_data[i].expected_status,
+ parse_data[i].expected_type,
+ CMD_TO_STRING(parse_data[i].expected_type));
+ result = (status == parse_data[i].expected_status) &&
+ (cmd.type == parse_data[i].expected_type);
+ LOG(result, description);
+
+ /* Run all tests before failing. */
+ if (!result)
+ test_result = -1;
+
+ cmd_destroy(&cmd);
+ free(input);
+ }
+
+ return (test_result);
+}
+
+int main(int argc, char **argv) {
+ RUN_TEST(parse);
+ END_TEST;
+}
}
static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
- char escape_char) {
+ char escape_char, _Bool preserve_separator) {
memset(dst, 0, dst_len);
if (src == NULL)
break;
}
- if ((src[i] == '.') || isspace((int)src[i]) || iscntrl((int)src[i]))
+ if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) ||
+ iscntrl((int)src[i]))
dst[i] = escape_char;
else
dst[i] = src[i];
if (postfix == NULL)
postfix = "";
- gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char);
- gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char);
+ _Bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR) ? 1 : 0;
+
+ gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char,
+ preserve_separator);
gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
- sizeof(n_plugin_instance), escape_char);
- gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char);
+ sizeof(n_plugin_instance), escape_char,
+ preserve_separator);
+ gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char,
+ preserve_separator);
gr_copy_escape_part(n_type_instance, vl->type_instance,
- sizeof(n_type_instance), escape_char);
+ sizeof(n_type_instance), escape_char, preserve_separator);
if (n_plugin_instance[0] != '\0')
ssnprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin,
else
sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin));
- if (n_type_instance[0] != '\0')
- ssnprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type,
- (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
- n_type_instance);
- else
+ if (n_type_instance[0] != '\0') {
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type));
+ else
+ ssnprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type,
+ (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+ n_type_instance);
+ } else
sstrncpy(tmp_type, n_type, sizeof(tmp_type));
/* Assert always_append_ds -> ds_name */
assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
- if (ds_name != NULL)
- ssnprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix,
- tmp_plugin, tmp_type, ds_name);
- else
+ if (ds_name != NULL) {
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) &&
+ strcmp(tmp_plugin, tmp_type) == 0)
+ ssnprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix,
+ tmp_plugin, ds_name);
+ else
+ ssnprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix,
+ tmp_plugin, tmp_type, ds_name);
+ } else
ssnprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin,
tmp_type);
#define GRAPHITE_STORE_RATES 0x01
#define GRAPHITE_SEPARATE_INSTANCES 0x02
#define GRAPHITE_ALWAYS_APPEND_DS 0x04
+#define GRAPHITE_DROP_DUPE_FIELDS 0x08
+#define GRAPHITE_PRESERVE_SEPARATOR 0x10
int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
const value_list_t *vl, const char *prefix,
--- /dev/null
+/**
+ * collectd - src/utils_format_graphite_test.c
+ * Copyright (C) 2016 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "common.h" /* for STATIC_ARRAY_SIZE */
+#include "testing.h"
+#include "utils_format_graphite.h"
+
+static data_set_t ds_single = {
+ .type = "single",
+ .ds_num = 1,
+ .ds = &(data_source_t){"value", DS_TYPE_GAUGE, NAN, NAN},
+};
+
+/*
+static data_set_t ds_double = {
+ .type = "double",
+ .ds_num = 2,
+ .ds =
+ (data_source_t[]){
+ {"one", DS_TYPE_DERIVE, 0, NAN}, {"two", DS_TYPE_DERIVE, 0, NAN},
+ },
+};
+*/
+
+DEF_TEST(metric_name) {
+ struct {
+ char *plugin_instance;
+ char *type_instance;
+ char *prefix;
+ char *suffix;
+ unsigned int flags;
+ char *want_name;
+ } cases[] = {
+ {
+ .want_name = "example@com.test.single",
+ },
+ /* plugin and type instances */
+ {
+ .plugin_instance = "foo",
+ .type_instance = "bar",
+ .want_name = "example@com.test-foo.single-bar",
+ },
+ {
+ .plugin_instance = NULL,
+ .type_instance = "bar",
+ .want_name = "example@com.test.single-bar",
+ },
+ {
+ .plugin_instance = "foo",
+ .type_instance = NULL,
+ .want_name = "example@com.test-foo.single",
+ },
+ /* special chars */
+ {
+ .plugin_instance = "foo (test)",
+ .type_instance = "test: \"hello\"",
+ .want_name = "example@com.test-foo@@test@.single-test@@@hello@",
+ },
+ /* flag GRAPHITE_SEPARATE_INSTANCES */
+ {
+ .plugin_instance = "foo",
+ .type_instance = "bar",
+ .flags = GRAPHITE_SEPARATE_INSTANCES,
+ .want_name = "example@com.test.foo.single.bar",
+ },
+ /* flag GRAPHITE_ALWAYS_APPEND_DS */
+ {
+ .plugin_instance = "foo",
+ .type_instance = "bar",
+ .flags = GRAPHITE_ALWAYS_APPEND_DS,
+ .want_name = "example@com.test-foo.single-bar.value",
+ },
+ /* flag GRAPHITE_PRESERVE_SEPARATOR */
+ {
+ .plugin_instance = "f.o.o",
+ .type_instance = "b.a.r",
+ .flags = 0,
+ .want_name = "example@com.test-f@o@o.single-b@a@r",
+ },
+ {
+ .plugin_instance = "f.o.o",
+ .type_instance = "b.a.r",
+ .flags = GRAPHITE_PRESERVE_SEPARATOR,
+ .want_name = "example.com.test-f.o.o.single-b.a.r",
+ },
+ /* prefix and suffix */
+ {
+ .prefix = "foo.",
+ .suffix = ".bar",
+ .want_name = "foo.example@com.bar.test.single",
+ },
+ {
+ .prefix = NULL,
+ .suffix = ".bar",
+ .want_name = "example@com.bar.test.single",
+ },
+ {
+ .prefix = "foo.",
+ .suffix = NULL,
+ .want_name = "foo.example@com.test.single",
+ },
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ value_list_t vl = {
+ .values = &(value_t){.gauge = 42},
+ .values_len = 1,
+ .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
+ .interval = TIME_T_TO_CDTIME_T_STATIC(10),
+ .host = "example.com",
+ .plugin = "test",
+ .type = "single",
+ };
+
+ char want[1024];
+ snprintf(want, sizeof(want), "%s 42 1480063672\r\n", cases[i].want_name);
+
+ if (cases[i].plugin_instance != NULL)
+ sstrncpy(vl.plugin_instance, cases[i].plugin_instance,
+ sizeof(vl.plugin_instance));
+ if (cases[i].type_instance != NULL)
+ sstrncpy(vl.type_instance, cases[i].type_instance,
+ sizeof(vl.type_instance));
+
+ char got[1024];
+ EXPECT_EQ_INT(0, format_graphite(got, sizeof(got), &ds_single, &vl,
+ cases[i].prefix, cases[i].suffix, '@',
+ cases[i].flags));
+ EXPECT_EQ_STR(want, got);
+ }
+
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(metric_name);
+
+ END_TEST;
+}
#define LLONG_MAX 9223372036854775807LL
#endif
-#ifndef HISTOGRAM_NUM_BINS
-#define HISTOGRAM_NUM_BINS 1000
-#endif
-
#ifndef HISTOGRAM_DEFAULT_BIN_WIDTH
/* 1048576 = 2^20 ^= 1/1024 s */
#define HISTOGRAM_DEFAULT_BIN_WIDTH 1048576
if (lc->max < latency)
lc->max = latency;
- /* A latency of _exactly_ 1.0 ms should be stored in the buffer 0, so
+ /* A latency of _exactly_ 1.0 ms is stored in the buffer 0, so
* subtract one from the cdtime_t value so that exactly 1.0 ms get sorted
* accordingly. */
bin = (latency - 1) / lc->bin_width;
return (latency_interpolated);
} /* }}} cdtime_t latency_counter_get_percentile */
+double latency_counter_get_rate(const latency_counter_t *lc, /* {{{ */
+ cdtime_t lower, cdtime_t upper,
+ const cdtime_t now) {
+ if ((lc == NULL) || (lc->num == 0))
+ return (NAN);
+
+ if (upper && (upper < lower))
+ return (NAN);
+ if (lower == upper)
+ return (0);
+
+ /* Buckets have an exclusive lower bound and an inclusive upper bound. That
+ * means that the first bucket, index 0, represents (0-bin_width]. That means
+ * that latency==bin_width needs to result in bin=0, that's why we need to
+ * subtract one before dividing by bin_width. */
+ cdtime_t lower_bin = 0;
+ if (lower)
+ /* lower is *exclusive* => determine bucket for lower+1 */
+ lower_bin = ((lower + 1) - 1) / lc->bin_width;
+
+ /* lower is greater than the longest latency observed => rate is zero. */
+ if (lower_bin >= HISTOGRAM_NUM_BINS)
+ return (0);
+
+ cdtime_t upper_bin = HISTOGRAM_NUM_BINS - 1;
+ if (upper)
+ upper_bin = (upper - 1) / lc->bin_width;
+
+ if (upper_bin >= HISTOGRAM_NUM_BINS) {
+ upper_bin = HISTOGRAM_NUM_BINS - 1;
+ upper = 0;
+ }
+
+ double sum = 0;
+ for (size_t i = lower_bin; i <= upper_bin; i++)
+ sum += lc->histogram[i];
+
+ if (lower) {
+ /* Approximate ratio of requests in lower_bin, that fall between
+ * lower_bin_boundary and lower. This ratio is then subtracted from sum to
+ * increase accuracy. */
+ cdtime_t lower_bin_boundary = lower_bin * lc->bin_width;
+ assert(lower >= lower_bin_boundary);
+ double lower_ratio =
+ (double)(lower - lower_bin_boundary) / ((double)lc->bin_width);
+ sum -= lower_ratio * lc->histogram[lower_bin];
+ }
+
+ if (upper) {
+ /* As above: approximate ratio of requests in upper_bin, that fall between
+ * upper and upper_bin_boundary. */
+ cdtime_t upper_bin_boundary = (upper_bin + 1) * lc->bin_width;
+ assert(upper <= upper_bin_boundary);
+ double ratio = (double)(upper_bin_boundary - upper) / (double)lc->bin_width;
+ sum -= ratio * lc->histogram[upper_bin];
+ }
+
+ return sum / (CDTIME_T_TO_DOUBLE(now - lc->start_time));
+} /* }}} double latency_counter_get_rate */
+
/* vim: set sw=2 sts=2 et fdm=marker : */
#include "utils_time.h"
+#ifndef HISTOGRAM_NUM_BINS
+#define HISTOGRAM_NUM_BINS 1000
+#endif
+
struct latency_counter_s;
typedef struct latency_counter_s latency_counter_t;
cdtime_t latency_counter_get_average(latency_counter_t *lc);
cdtime_t latency_counter_get_percentile(latency_counter_t *lc, double percent);
+/*
+ * NAME
+ * latency_counter_get_rate(counter,lower,upper,now)
+ *
+ * DESCRIPTION
+ * Calculates rate of latency values fall within requested interval.
+ * Interval specified as (lower,upper], i.e. the lower boundary is exclusive,
+ * the upper boundary is inclusive.
+ * When lower is zero, then the interval is (0, upper].
+ * When upper is zero, then the interval is (lower, infinity).
+ */
+double latency_counter_get_rate(const latency_counter_t *lc, cdtime_t lower,
+ cdtime_t upper, const cdtime_t now);
+
/* vim: set sw=2 sts=2 et : */
--- /dev/null
+/**
+ * collectd - src/utils_latency_config.c
+ * Copyright (C) 2013-2016 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#include "common.h"
+#include "utils_latency_config.h"
+#include "collectd.h"
+
+static int latency_config_add_percentile(latency_config_t *conf,
+ oconfig_item_t *ci,
+ const char *plugin) {
+ double percent;
+ int status = cf_util_get_double(ci, &percent);
+ if (status != 0)
+ return status;
+
+ if ((percent <= 0.0) || (percent >= 100)) {
+ ERROR("%s plugin: The value for \"%s\" must be between 0 and 100, "
+ "exclusively.",
+ plugin, ci->key);
+ return ERANGE;
+ }
+
+ double *tmp = realloc(conf->percentile,
+ sizeof(*conf->percentile) * (conf->percentile_num + 1));
+ if (tmp == NULL) {
+ ERROR("%s plugin: realloc failed.", plugin);
+ return ENOMEM;
+ }
+ conf->percentile = tmp;
+ conf->percentile[conf->percentile_num] = percent;
+ conf->percentile_num++;
+
+ return 0;
+} /* int latency_config_add_percentile */
+
+static int latency_config_add_bucket(latency_config_t *conf, oconfig_item_t *ci,
+ const char *plugin) {
+ if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_NUMBER) ||
+ (ci->values[1].type != OCONFIG_TYPE_NUMBER)) {
+ ERROR("%s plugin: \"%s\" requires exactly two numeric arguments.", plugin,
+ ci->key);
+ return EINVAL;
+ }
+
+ if (ci->values[1].value.number &&
+ ci->values[1].value.number <= ci->values[0].value.number) {
+ ERROR("%s plugin: MIN must be less than MAX in \"%s\".", plugin, ci->key);
+ return ERANGE;
+ }
+
+ if (ci->values[0].value.number < 0) {
+ ERROR("%s plugin: MIN must be greater then or equal to zero in \"%s\".",
+ plugin, ci->key);
+ return ERANGE;
+ }
+
+ latency_bucket_t *tmp =
+ realloc(conf->buckets, sizeof(*conf->buckets) * (conf->buckets_num + 1));
+ if (tmp == NULL) {
+ ERROR("%s plugin: realloc failed.", plugin);
+ return ENOMEM;
+ }
+ conf->buckets = tmp;
+ conf->buckets[conf->buckets_num].lower_bound =
+ DOUBLE_TO_CDTIME_T(ci->values[0].value.number);
+ conf->buckets[conf->buckets_num].upper_bound =
+ DOUBLE_TO_CDTIME_T(ci->values[1].value.number);
+ conf->buckets_num++;
+
+ return (0);
+} /* int latency_config_add_bucket */
+
+int latency_config(latency_config_t *conf, oconfig_item_t *ci,
+ char const *plugin) {
+ int status = 0;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Percentile", child->key) == 0)
+ status = latency_config_add_percentile(conf, child, plugin);
+ else if (strcasecmp("Bucket", child->key) == 0)
+ status = latency_config_add_bucket(conf, child, plugin);
+ else
+ WARNING("%s plugin: \"%s\" is not a valid option within a \"%s\" block.",
+ plugin, child->key, ci->key);
+
+ if (status != 0)
+ return status;
+ }
+
+ if ((status == 0) && (conf->percentile_num == 0) &&
+ (conf->buckets_num == 0)) {
+ ERROR("%s plugin: The \"%s\" block must contain at least one "
+ "\"Percentile\" or \"Bucket\" option.",
+ plugin, ci->key);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int latency_config_copy(latency_config_t *dst, const latency_config_t src) {
+ *dst = (latency_config_t){
+ .percentile_num = src.percentile_num, .buckets_num = src.buckets_num,
+ };
+
+ dst->percentile = calloc(dst->percentile_num, sizeof(*dst->percentile));
+ dst->buckets = calloc(dst->buckets_num, sizeof(*dst->buckets));
+
+ if ((dst->percentile == NULL) || (dst->buckets == NULL)) {
+ latency_config_free(*dst);
+ return ENOMEM;
+ }
+
+ memmove(dst->percentile, src.percentile,
+ dst->percentile_num * sizeof(*dst->percentile));
+ memmove(dst->buckets, src.buckets, dst->buckets_num * sizeof(*dst->buckets));
+
+ return 0;
+} /* int latency_config_copy */
+
+void latency_config_free(latency_config_t conf) {
+ sfree(conf.percentile);
+ sfree(conf.buckets);
+} /* void latency_config_free */
--- /dev/null
+/**
+ * collectd - src/utils_latency_config.c
+ * Copyright (C) 2013-2016 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ * Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#ifndef UTILS_LATENCY_CONFIG_H
+#define UTILS_LATENCY_CONFIG_H 1
+
+#include "collectd.h"
+
+#include "liboconfig/oconfig.h"
+#include "utils_time.h"
+
+typedef struct {
+ cdtime_t lower_bound;
+ cdtime_t upper_bound;
+} latency_bucket_t;
+
+typedef struct {
+ double *percentile;
+ size_t percentile_num;
+
+ latency_bucket_t *buckets;
+ size_t buckets_num;
+
+ /*
+ _Bool lower;
+ _Bool upper;
+ _Bool avg;
+ */
+} latency_config_t;
+
+int latency_config(latency_config_t *conf, oconfig_item_t *ci,
+ char const *plugin);
+
+int latency_config_copy(latency_config_t *dst, const latency_config_t src);
+
+void latency_config_free(latency_config_t conf);
+
+#endif /* UTILS_LATENCY_CONFIG_H */
* Florian octo Forster <octo at collectd.org>
*/
-#define DBL_PRECISION 1e-9
+#define DBL_PRECISION 1e-6
#include "common.h" /* for STATIC_ARRAY_SIZE */
#include "collectd.h"
return 0;
}
+DEF_TEST(get_rate) {
+ /* We re-declare the struct here so we can inspect its content. */
+ struct {
+ cdtime_t start_time;
+ cdtime_t sum;
+ size_t num;
+ cdtime_t min;
+ cdtime_t max;
+ cdtime_t bin_width;
+ int histogram[HISTOGRAM_NUM_BINS];
+ } * peek;
+ latency_counter_t *l;
+
+ CHECK_NOT_NULL(l = latency_counter_create());
+ peek = (void *)l;
+
+ for (time_t i = 1; i <= 125; i++) {
+ latency_counter_add(l, TIME_T_TO_CDTIME_T(i));
+ }
+
+ /* We expect a bucket width of 125ms. */
+ EXPECT_EQ_UINT64(DOUBLE_TO_CDTIME_T(0.125), peek->bin_width);
+
+ struct {
+ size_t index;
+ int want;
+ } bucket_cases[] = {
+ {0, 0}, /* (0.000-0.125] */
+ {1, 0}, /* (0.125-0.250] */
+ {2, 0}, /* (0.250-0.375] */
+ {3, 0}, /* (0.375-0.500] */
+ {4, 0}, /* (0.500-0.625] */
+ {5, 0}, /* (0.625-0.750] */
+ {6, 0}, /* (0.750-0.875] */
+ {7, 1}, /* (0.875-1.000] */
+ {8, 0}, /* (1.000-1.125] */
+ {9, 0}, /* (1.125-1.250] */
+ {10, 0}, /* (1.250-1.375] */
+ {11, 0}, /* (1.375-1.500] */
+ {12, 0}, /* (1.500-1.625] */
+ {13, 0}, /* (1.625-1.750] */
+ {14, 0}, /* (1.750-1.875] */
+ {15, 1}, /* (1.875-2.000] */
+ {16, 0}, /* (2.000-2.125] */
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(bucket_cases); i++) {
+ size_t index = bucket_cases[i].index;
+ EXPECT_EQ_INT(bucket_cases[i].want, peek->histogram[index]);
+ }
+
+ struct {
+ cdtime_t lower_bound;
+ cdtime_t upper_bound;
+ double want;
+ } cases[] = {
+ {
+ // bucket 6 is zero
+ DOUBLE_TO_CDTIME_T_STATIC(0.750), DOUBLE_TO_CDTIME_T_STATIC(0.875),
+ 0.00,
+ },
+ {
+ // bucket 7 contains the t=1 update
+ DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(1.000),
+ 1.00,
+ },
+ {
+ // range: bucket 7 - bucket 15; contains the t=1 and t=2 updates
+ DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(2.000),
+ 2.00,
+ },
+ {
+ // lower bucket is only partially applied
+ DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
+ DOUBLE_TO_CDTIME_T_STATIC(2.000), 1.75,
+ },
+ {
+ // upper bucket is only partially applied
+ DOUBLE_TO_CDTIME_T_STATIC(0.875),
+ DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.75,
+ },
+ {
+ // both buckets are only partially applied
+ DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
+ DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.50,
+ },
+ {
+ // lower bound is unspecified
+ 0, DOUBLE_TO_CDTIME_T_STATIC(2.000), 2.00,
+ },
+ {
+ // upper bound is unspecified
+ DOUBLE_TO_CDTIME_T_STATIC(125.000 - 0.125), 0, 1.00,
+ },
+ {
+ // overflow test: upper >> longest latency
+ DOUBLE_TO_CDTIME_T_STATIC(1.000), DOUBLE_TO_CDTIME_T_STATIC(999999),
+ 124.00,
+ },
+ {
+ // overflow test: lower > longest latency
+ DOUBLE_TO_CDTIME_T_STATIC(130), 0, 0.00,
+ },
+ {
+ // lower > upper => error
+ DOUBLE_TO_CDTIME_T_STATIC(10), DOUBLE_TO_CDTIME_T_STATIC(9), NAN,
+ },
+ {
+ // lower == upper => zero
+ DOUBLE_TO_CDTIME_T_STATIC(9), DOUBLE_TO_CDTIME_T_STATIC(9), 0.00,
+ },
+ };
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+ cdtime_t now = peek->start_time + TIME_T_TO_CDTIME_T(1);
+ EXPECT_EQ_DOUBLE(cases[i].want,
+ latency_counter_get_rate(l, cases[i].lower_bound,
+ cases[i].upper_bound, now));
+ }
+
+ latency_counter_destroy(l);
+ return 0;
+}
+
int main(void) {
RUN_TEST(simple);
RUN_TEST(percentile);
+ RUN_TEST(get_rate);
END_TEST;
}
--- /dev/null
+/**
+ * collectd - src/utils_match.c
+ * Copyright (C) 2008-2014 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "plugin.h"
+
+#include "utils_match.h"
+
+#include <regex.h>
+
+#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
+#define UTILS_MATCH_FLAGS_REGEX 0x04
+
+struct cu_match_s {
+ regex_t regex;
+ regex_t excluderegex;
+ int flags;
+
+ int (*callback)(const char *str, char *const *matches, size_t matches_num,
+ void *user_data);
+ void *user_data;
+ void (*free)(void *user_data);
+};
+
+/*
+ * Private functions
+ */
+static char *match_substr(const char *str, int begin, int end) {
+ char *ret;
+ size_t ret_len;
+
+ if ((begin < 0) || (end < 0) || (begin >= end))
+ return (NULL);
+ if ((size_t)end > (strlen(str) + 1)) {
+ ERROR("utils_match: match_substr: `end' points after end of string.");
+ return (NULL);
+ }
+
+ ret_len = end - begin;
+ ret = malloc(ret_len + 1);
+ if (ret == NULL) {
+ ERROR("utils_match: match_substr: malloc failed.");
+ return (NULL);
+ }
+
+ sstrncpy(ret, str + begin, ret_len + 1);
+ return (ret);
+} /* char *match_substr */
+
+static int default_callback(const char __attribute__((unused)) * str,
+ char *const *matches, size_t matches_num,
+ void *user_data) {
+ cu_match_value_t *data = (cu_match_value_t *)user_data;
+
+ if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) {
+ gauge_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) {
+ data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1;
+ data->values_num++;
+ return (0);
+ }
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = (gauge_t)strtod(matches[1], &endptr);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if (data->ds_type & UTILS_MATCH_CF_GAUGE_DIST) {
+ latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value));
+ data->values_num++;
+ return (0);
+ }
+
+ if ((data->values_num == 0) ||
+ (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST) ||
+ (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
+ data->value.gauge = value;
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) {
+ double f = ((double)data->values_num) / ((double)(data->values_num + 1));
+ data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) {
+ if (data->value.gauge > value)
+ data->value.gauge = value;
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) {
+ if (data->value.gauge < value)
+ data->value.gauge = value;
+ } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) {
+ data->value.gauge += value;
+ } else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) {
+ counter_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) {
+ data->value.counter++;
+ data->values_num++;
+ return (0);
+ }
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = (counter_t)strtoull(matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
+ data->value.counter = value;
+ else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
+ data->value.counter += value;
+ else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) {
+ derive_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) {
+ data->value.derive++;
+ data->values_num++;
+ return (0);
+ }
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = (derive_t)strtoll(matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
+ data->value.derive = value;
+ else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
+ data->value.derive += value;
+ else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) {
+ absolute_t value;
+ char *endptr = NULL;
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = (absolute_t)strtoull(matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
+ data->value.absolute = value;
+ else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ } else {
+ ERROR("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ return (0);
+} /* int default_callback */
+
+static void match_simple_free(void *data) {
+ cu_match_value_t *user_data = (cu_match_value_t *)data;
+ if (user_data->latency)
+ latency_counter_destroy(user_data->latency);
+
+ free(data);
+} /* void match_simple_free */
+
+/*
+ * Public functions
+ */
+cu_match_t *
+match_create_callback(const char *regex, const char *excluderegex,
+ int (*callback)(const char *str, char *const *matches,
+ size_t matches_num, void *user_data),
+ void *user_data,
+ void (*free_user_data)(void *user_data)) {
+ cu_match_t *obj;
+ int status;
+
+ DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s",
+ regex, excluderegex);
+
+ obj = calloc(1, sizeof(*obj));
+ if (obj == NULL)
+ return (NULL);
+
+ status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
+ if (status != 0) {
+ ERROR("Compiling the regular expression \"%s\" failed.", regex);
+ sfree(obj);
+ return (NULL);
+ }
+ obj->flags |= UTILS_MATCH_FLAGS_REGEX;
+
+ if (excluderegex && strcmp(excluderegex, "") != 0) {
+ status = regcomp(&obj->excluderegex, excluderegex, REG_EXTENDED);
+ if (status != 0) {
+ ERROR("Compiling the excluding regular expression \"%s\" failed.",
+ excluderegex);
+ sfree(obj);
+ return (NULL);
+ }
+ obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
+ }
+
+ obj->callback = callback;
+ obj->user_data = user_data;
+ obj->free = free_user_data;
+
+ return (obj);
+} /* cu_match_t *match_create_callback */
+
+cu_match_t *match_create_simple(const char *regex, const char *excluderegex,
+ int match_ds_type) {
+ cu_match_value_t *user_data;
+ cu_match_t *obj;
+
+ user_data = calloc(1, sizeof(*user_data));
+ if (user_data == NULL)
+ return (NULL);
+ user_data->ds_type = match_ds_type;
+
+ if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
+ (match_ds_type & UTILS_MATCH_CF_GAUGE_DIST)) {
+ user_data->latency = latency_counter_create();
+ if (user_data->latency == NULL) {
+ ERROR("match_create_simple(): latency_counter_create() failed.");
+ free(user_data);
+ return (NULL);
+ }
+ }
+
+ obj = match_create_callback(regex, excluderegex, default_callback, user_data,
+ match_simple_free);
+ if (obj == NULL) {
+ if (user_data->latency)
+ latency_counter_destroy(user_data->latency);
+
+ sfree(user_data);
+ return (NULL);
+ }
+ return (obj);
+} /* cu_match_t *match_create_simple */
+
+void match_value_reset(cu_match_value_t *mv) {
+ if (mv == NULL)
+ return;
+
+ /* Reset GAUGE metrics only and except GAUGE_PERSIST. */
+ if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
+ !(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
+ mv->value.gauge = NAN;
+ mv->values_num = 0;
+ }
+} /* }}} void match_value_reset */
+
+void match_destroy(cu_match_t *obj) {
+ if (obj == NULL)
+ return;
+
+ if (obj->flags & UTILS_MATCH_FLAGS_REGEX)
+ regfree(&obj->regex);
+ if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX)
+ regfree(&obj->excluderegex);
+ if ((obj->user_data != NULL) && (obj->free != NULL))
+ (*obj->free)(obj->user_data);
+
+ sfree(obj);
+} /* void match_destroy */
+
+int match_apply(cu_match_t *obj, const char *str) {
+ int status;
+ regmatch_t re_match[32];
+ char *matches[32] = {0};
+ size_t matches_num;
+
+ if ((obj == NULL) || (str == NULL))
+ return (-1);
+
+ if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
+ status =
+ regexec(&obj->excluderegex, str, STATIC_ARRAY_SIZE(re_match), re_match,
+ /* eflags = */ 0);
+ /* Regex did match, so exclude this line */
+ if (status == 0) {
+ DEBUG("ExludeRegex matched, don't count that line\n");
+ return (0);
+ }
+ }
+
+ status = regexec(&obj->regex, str, STATIC_ARRAY_SIZE(re_match), re_match,
+ /* eflags = */ 0);
+
+ /* Regex did not match */
+ if (status != 0)
+ return (0);
+
+ for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches);
+ matches_num++) {
+ if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0))
+ break;
+
+ matches[matches_num] = match_substr(str, re_match[matches_num].rm_so,
+ re_match[matches_num].rm_eo);
+ if (matches[matches_num] == NULL) {
+ status = -1;
+ break;
+ }
+ }
+
+ if (status != 0) {
+ ERROR("utils_match: match_apply: match_substr failed.");
+ } else {
+ status = obj->callback(str, matches, matches_num, obj->user_data);
+ if (status != 0) {
+ ERROR("utils_match: match_apply: callback failed.");
+ }
+ }
+
+ for (size_t i = 0; i < matches_num; i++) {
+ sfree(matches[i]);
+ }
+
+ return (status);
+} /* int match_apply */
+
+void *match_get_user_data(cu_match_t *obj) {
+ if (obj == NULL)
+ return (NULL);
+ return (obj->user_data);
+} /* void *match_get_user_data */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/**
+ * collectd - src/utils_match.h
+ * Copyright (C) 2008-2014 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_MATCH_H
+#define UTILS_MATCH_H 1
+
+#include "plugin.h"
+#include "utils_latency.h"
+
+/*
+ * Each type may have 12 sub-types
+ * 0x1000 = 1000000000000
+ * ^ <- Type bit
+ * ^^^^^^^^^^^^ <- Subtype bits
+ */
+#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000
+#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000
+#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000
+#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000
+
+#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
+#define UTILS_MATCH_CF_GAUGE_MIN 0x02
+#define UTILS_MATCH_CF_GAUGE_MAX 0x04
+#define UTILS_MATCH_CF_GAUGE_LAST 0x08
+#define UTILS_MATCH_CF_GAUGE_INC 0x10
+#define UTILS_MATCH_CF_GAUGE_ADD 0x20
+#define UTILS_MATCH_CF_GAUGE_PERSIST 0x40
+#define UTILS_MATCH_CF_GAUGE_DIST 0x80
+
+#define UTILS_MATCH_CF_COUNTER_SET 0x01
+#define UTILS_MATCH_CF_COUNTER_ADD 0x02
+#define UTILS_MATCH_CF_COUNTER_INC 0x04
+
+#define UTILS_MATCH_CF_DERIVE_SET 0x01
+#define UTILS_MATCH_CF_DERIVE_ADD 0x02
+#define UTILS_MATCH_CF_DERIVE_INC 0x04
+
+#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01
+#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02
+#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04
+
+/*
+ * Data types
+ */
+struct cu_match_s;
+typedef struct cu_match_s cu_match_t;
+
+struct cu_match_value_s {
+ int ds_type;
+ value_t value;
+ unsigned int values_num;
+ latency_counter_t *latency;
+};
+typedef struct cu_match_value_s cu_match_value_t;
+
+/*
+ * Prototypes
+ */
+/*
+ * NAME
+ * match_create_callback
+ *
+ * DESCRIPTION
+ * Creates a new `cu_match_t' object which will use the regular expression
+ * `regex' to match lines, see the `match_apply' method below. If the line
+ * matches, the callback passed in `callback' will be called along with the
+ * pointer `user_pointer'.
+ * The string that's passed to the callback depends on the regular expression:
+ * If the regular expression includes a sub-match, i. e. something like
+ * "value=([0-9][0-9]*)"
+ * then only the submatch (the part in the parenthesis) will be passed to the
+ * callback. If there is no submatch, then the entire string is passed to the
+ * callback.
+ * The optional `excluderegex' allows to exclude the line from the match, if
+ * the excluderegex matches.
+ * When `match_destroy' is called the `user_data' pointer is freed using
+ * the `free_user_data' callback - if it is not NULL.
+ */
+cu_match_t *
+match_create_callback(const char *regex, const char *excluderegex,
+ int (*callback)(const char *str, char *const *matches,
+ size_t matches_num, void *user_data),
+ void *user_data, void (*free_user_data)(void *user_data));
+
+/*
+ * NAME
+ * match_create_simple
+ *
+ * DESCRIPTION
+ * Creates a new `cu_match_t' with a default callback. The user data for that
+ * default callback will be a `cu_match_value_t' structure, with
+ * `ds_type' copied to the structure. The default callback will handle the
+ * string as containing a number (see strtoll(3) and strtod(3)) and store that
+ * number in the `value' member. How that is done depends on `ds_type':
+ *
+ * UTILS_MATCH_DS_TYPE_GAUGE
+ * The function will search for a floating point number in the string and
+ * store it in value.gauge.
+ * UTILS_MATCH_DS_TYPE_COUNTER_SET
+ * The function will search for an integer in the string and store it in
+ * value.counter.
+ * UTILS_MATCH_DS_TYPE_COUNTER_ADD
+ * The function will search for an integer in the string and add it to the
+ * value in value.counter.
+ * UTILS_MATCH_DS_TYPE_COUNTER_INC
+ * The function will not search for anything in the string and increase
+ * value.counter by one.
+ */
+cu_match_t *match_create_simple(const char *regex, const char *excluderegex,
+ int ds_type);
+
+/*
+ * NAME
+ * match_value_reset
+ *
+ * DESCRIPTION
+ * Resets the internal state, if applicable. This function must be called
+ * after each iteration for "simple" matches, usually after dispatching the
+ * metrics.
+ */
+void match_value_reset(cu_match_value_t *mv);
+
+/*
+ * NAME
+ * match_destroy
+ *
+ * DESCRIPTION
+ * Destroys the object and frees all internal resources.
+ */
+void match_destroy(cu_match_t *obj);
+
+/*
+ * NAME
+ * match_apply
+ *
+ * DESCRIPTION
+ * Tries to match the string `str' with the regular expression of `obj'. If
+ * the string matches, calls the callback in `obj' with the (sub-)match.
+ *
+ * The user_data pointer passed to `match_create_callback' is NOT freed
+ * automatically. The `cu_match_value_t' structure allocated by
+ * `match_create_callback' is freed automatically.
+ */
+int match_apply(cu_match_t *obj, const char *str);
+
+/*
+ * NAME
+ * match_get_user_data
+ *
+ * DESCRIPTION
+ * Returns the pointer passed to `match_create_callback' or a pointer to the
+ * `cu_match_value_t' structure allocated by `match_create_simple'.
+ */
+void *match_get_user_data(cu_match_t *obj);
+
+#endif /* UTILS_MATCH_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
return (first);
} /* static cu_mount_t *cu_mount_gen_getmntent (void) */
- /* #endif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT */
+/* #endif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT */
#elif HAVE_SEQ_GETMNTENT
#warn "This version of `getmntent' hat not yet been implemented!"
--- /dev/null
+/**
+ * collectd - src/utils_tail.c
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * Copyright (C) 2008 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * Description:
+ * Encapsulates useful code for plugins which must watch for appends to
+ * the end of a file.
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "utils_tail.h"
+
+struct cu_tail_s {
+ char *file;
+ FILE *fh;
+ struct stat stat;
+};
+
+static int cu_tail_reopen(cu_tail_t *obj) {
+ int seek_end = 0;
+ FILE *fh;
+ struct stat stat_buf = {0};
+ int status;
+
+ status = stat(obj->file, &stat_buf);
+ if (status != 0) {
+ char errbuf[1024];
+ ERROR("utils_tail: stat (%s) failed: %s", obj->file,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ /* The file is already open.. */
+ if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) {
+ /* Seek to the beginning if file was truncated */
+ if (stat_buf.st_size < obj->stat.st_size) {
+ INFO("utils_tail: File `%s' was truncated.", obj->file);
+ status = fseek(obj->fh, 0, SEEK_SET);
+ if (status != 0) {
+ char errbuf[1024];
+ ERROR("utils_tail: fseek (%s) failed: %s", obj->file,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ fclose(obj->fh);
+ obj->fh = NULL;
+ return (-1);
+ }
+ }
+ memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
+ return (1);
+ }
+
+ /* Seek to the end if we re-open the same file again or the file opened
+ * is the first at all or the first after an error */
+ if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
+ seek_end = 1;
+
+ fh = fopen(obj->file, "r");
+ if (fh == NULL) {
+ char errbuf[1024];
+ ERROR("utils_tail: fopen (%s) failed: %s", obj->file,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ if (seek_end != 0) {
+ status = fseek(fh, 0, SEEK_END);
+ if (status != 0) {
+ char errbuf[1024];
+ ERROR("utils_tail: fseek (%s) failed: %s", obj->file,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ fclose(fh);
+ return (-1);
+ }
+ }
+
+ if (obj->fh != NULL)
+ fclose(obj->fh);
+ obj->fh = fh;
+ memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
+
+ return (0);
+} /* int cu_tail_reopen */
+
+cu_tail_t *cu_tail_create(const char *file) {
+ cu_tail_t *obj;
+
+ obj = calloc(1, sizeof(*obj));
+ if (obj == NULL)
+ return (NULL);
+
+ obj->file = strdup(file);
+ if (obj->file == NULL) {
+ free(obj);
+ return (NULL);
+ }
+
+ obj->fh = NULL;
+
+ return (obj);
+} /* cu_tail_t *cu_tail_create */
+
+int cu_tail_destroy(cu_tail_t *obj) {
+ if (obj->fh != NULL)
+ fclose(obj->fh);
+ free(obj->file);
+ free(obj);
+
+ return (0);
+} /* int cu_tail_destroy */
+
+int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) {
+ int status;
+
+ if (buflen < 1) {
+ ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen);
+ return (-1);
+ }
+
+ if (obj->fh == NULL) {
+ status = cu_tail_reopen(obj);
+ if (status < 0)
+ return (status);
+ }
+ assert(obj->fh != NULL);
+
+ /* Try to read from the filehandle. If that succeeds, everything appears to
+ * be fine and we can return. */
+ clearerr(obj->fh);
+ if (fgets(buf, buflen, obj->fh) != NULL) {
+ buf[buflen - 1] = 0;
+ return (0);
+ }
+
+ /* Check if we encountered an error */
+ if (ferror(obj->fh) != 0) {
+ /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
+ fclose(obj->fh);
+ obj->fh = NULL;
+ }
+ /* else: eof -> check if the file was moved away and reopen the new file if
+ * so.. */
+
+ status = cu_tail_reopen(obj);
+ /* error -> return with error */
+ if (status < 0)
+ return (status);
+ /* file end reached and file not reopened -> nothing more to read */
+ else if (status > 0) {
+ buf[0] = 0;
+ return (0);
+ }
+
+ /* If we get here: file was re-opened and there may be more to read.. Let's
+ * try again. */
+ if (fgets(buf, buflen, obj->fh) != NULL) {
+ buf[buflen - 1] = 0;
+ return (0);
+ }
+
+ if (ferror(obj->fh) != 0) {
+ char errbuf[1024];
+ WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ fclose(obj->fh);
+ obj->fh = NULL;
+ return (-1);
+ }
+
+ /* EOf, well, apparently the new file is empty.. */
+ buf[0] = 0;
+ return (0);
+} /* int cu_tail_readline */
+
+int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+ void *data) {
+ int status;
+
+ while (42) {
+ size_t len;
+
+ status = cu_tail_readline(obj, buf, buflen);
+ if (status != 0) {
+ ERROR("utils_tail: cu_tail_read: cu_tail_readline "
+ "failed.");
+ break;
+ }
+
+ /* check for EOF */
+ if (buf[0] == 0)
+ break;
+
+ len = strlen(buf);
+ while (len > 0) {
+ if (buf[len - 1] != '\n')
+ break;
+ buf[len - 1] = '\0';
+ len--;
+ }
+
+ status = callback(data, buf, buflen);
+ if (status != 0) {
+ ERROR("utils_tail: cu_tail_read: callback returned "
+ "status %i.",
+ status);
+ break;
+ }
+ }
+
+ return status;
+} /* int cu_tail_read */
--- /dev/null
+/**
+ * collectd - src/utils_tail.h
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ * Luke Heberling <lukeh at c-ware.com>
+ *
+ * DESCRIPTION
+ * Facilitates reading information that is appended to a file, taking into
+ * account that the file may be rotated and a new file created under the
+ * same name.
+ **/
+
+#ifndef UTILS_TAIL_H
+#define UTILS_TAIL_H 1
+
+struct cu_tail_s;
+typedef struct cu_tail_s cu_tail_t;
+
+typedef int tailfunc_t(void *data, char *buf, int buflen);
+
+/*
+ * NAME
+ * cu_tail_create
+ *
+ * DESCRIPTION
+ * Allocates a new tail object..
+ *
+ * PARAMETERS
+ * `file' The name of the file to be tailed.
+ */
+cu_tail_t *cu_tail_create(const char *file);
+
+/*
+ * cu_tail_destroy
+ *
+ * Takes a tail object returned by `cu_tail_create' and destroys it, freeing
+ * all internal memory.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_destroy(cu_tail_t *obj);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until `buflen' characters are read, a newline
+ * character is read, or an eof condition is encountered. `buf' is
+ * always null-terminated on successful return and isn't touched when non-zero
+ * is returned.
+ *
+ * You can check if the EOF condition is reached by looking at the buffer: If
+ * the length of the string stored in the buffer is zero, EOF occurred.
+ * Otherwise at least the newline character will be in the buffer.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until eof condition or an error is encountered.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+ void *data);
+
+#endif /* UTILS_TAIL_H */
--- /dev/null
+/*
+ * collectd - src/utils_tail_match.c
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * Copyright (C) 2008 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * Description:
+ * Encapsulates useful code to plugins which must parse a log file.
+ */
+
+#include "collectd.h"
+
+#include "common.h"
+#include "plugin.h"
+#include "utils_latency_config.h"
+#include "utils_match.h"
+#include "utils_tail.h"
+#include "utils_tail_match.h"
+
+struct cu_tail_match_simple_s {
+ char plugin[DATA_MAX_NAME_LEN];
+ char plugin_instance[DATA_MAX_NAME_LEN];
+ char type[DATA_MAX_NAME_LEN];
+ char type_instance[DATA_MAX_NAME_LEN];
+ cdtime_t interval;
+ latency_config_t latency_config;
+};
+typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
+
+struct cu_tail_match_match_s {
+ cu_match_t *match;
+ void *user_data;
+ int (*submit)(cu_match_t *match, void *user_data);
+ void (*free)(void *user_data);
+};
+typedef struct cu_tail_match_match_s cu_tail_match_match_t;
+
+struct cu_tail_match_s {
+ int flags;
+ cu_tail_t *tail;
+
+ cdtime_t interval;
+ cu_tail_match_match_t *matches;
+ size_t matches_num;
+};
+
+/*
+ * Private functions
+ */
+static int simple_submit_match(cu_match_t *match, void *user_data) {
+ cu_tail_match_simple_t *data = (cu_tail_match_simple_t *)user_data;
+ cu_match_value_t *match_value;
+ value_list_t vl = VALUE_LIST_INIT;
+ value_t values[1];
+
+ match_value = (cu_match_value_t *)match_get_user_data(match);
+ if (match_value == NULL)
+ return (-1);
+
+ if ((match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
+ (match_value->values_num == 0))
+ values[0].gauge = NAN;
+ else
+ values[0] = match_value->value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin));
+ sstrncpy(vl.plugin_instance, data->plugin_instance,
+ sizeof(vl.plugin_instance));
+ sstrncpy(vl.type, data->type, sizeof(vl.type));
+ sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance));
+
+ vl.interval = data->interval;
+ plugin_dispatch_values(&vl);
+
+ match_value_reset(match_value);
+ return (0);
+} /* int simple_submit_match */
+
+static int latency_submit_match(cu_match_t *match, void *user_data) {
+ cu_tail_match_simple_t *data = (cu_tail_match_simple_t *)user_data;
+ cu_match_value_t *match_value;
+ value_list_t vl = VALUE_LIST_INIT;
+
+ match_value = (cu_match_value_t *)match_get_user_data(match);
+ if (match_value == NULL)
+ return (-1);
+
+ sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin));
+ sstrncpy(vl.plugin_instance, data->plugin_instance,
+ sizeof(vl.plugin_instance));
+ vl.interval = data->interval;
+ vl.time = cdtime();
+
+ /* Submit percentiles */
+ sstrncpy(vl.type, data->type, sizeof(vl.type));
+ for (size_t i = 0; i < data->latency_config.percentile_num; i++) {
+ if (strlen(data->type_instance) != 0)
+ ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%.0f",
+ data->type_instance, data->latency_config.percentile[i]);
+ else
+ ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%.0f",
+ data->latency_config.percentile[i]);
+
+ vl.values = &(value_t){
+ .gauge =
+ (match_value->values_num != 0)
+ ? CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(
+ match_value->latency, data->latency_config.percentile[i]))
+ : NAN,
+ };
+ vl.values_len = 1;
+
+ plugin_dispatch_values(&vl);
+ }
+
+ /* Submit buckets */
+ 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];
+
+ double lower_bound = CDTIME_T_TO_DOUBLE(bucket.lower_bound);
+ double upper_bound =
+ bucket.upper_bound ? CDTIME_T_TO_DOUBLE(bucket.upper_bound) : INFINITY;
+
+ if (strlen(data->type_instance) != 0)
+ ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s-%g_%g",
+ data->type, data->type_instance, lower_bound, upper_bound);
+ else
+ ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%g_%g",
+ data->type, lower_bound, upper_bound);
+
+ vl.values = &(value_t){
+ .gauge =
+ latency_counter_get_rate(match_value->latency, bucket.lower_bound,
+ bucket.upper_bound, vl.time),
+ };
+ vl.values_len = 1;
+
+ plugin_dispatch_values(&vl);
+ }
+
+ match_value->value.gauge = NAN;
+ match_value->values_num = 0;
+ latency_counter_reset(match_value->latency);
+
+ return (0);
+} /* int latency_submit_match */
+
+static int tail_callback(void *data, char *buf,
+ int __attribute__((unused)) buflen) {
+ cu_tail_match_t *obj = (cu_tail_match_t *)data;
+
+ for (size_t i = 0; i < obj->matches_num; i++)
+ match_apply(obj->matches[i].match, buf);
+
+ return (0);
+} /* int tail_callback */
+
+static void tail_match_simple_free(void *data) {
+ cu_tail_match_simple_t *user_data = (cu_tail_match_simple_t *)data;
+ latency_config_free(user_data->latency_config);
+ sfree(user_data);
+} /* void tail_match_simple_free */
+
+/*
+ * Public functions
+ */
+cu_tail_match_t *tail_match_create(const char *filename) {
+ cu_tail_match_t *obj;
+
+ obj = calloc(1, sizeof(*obj));
+ if (obj == NULL)
+ return (NULL);
+
+ obj->tail = cu_tail_create(filename);
+ if (obj->tail == NULL) {
+ sfree(obj);
+ return (NULL);
+ }
+
+ return (obj);
+} /* cu_tail_match_t *tail_match_create */
+
+void tail_match_destroy(cu_tail_match_t *obj) {
+ if (obj == NULL)
+ return;
+
+ if (obj->tail != NULL) {
+ cu_tail_destroy(obj->tail);
+ obj->tail = NULL;
+ }
+
+ for (size_t i = 0; i < obj->matches_num; i++) {
+ cu_tail_match_match_t *match = obj->matches + i;
+ if (match->match != NULL) {
+ match_destroy(match->match);
+ match->match = NULL;
+ }
+
+ if ((match->user_data != NULL) && (match->free != NULL))
+ (*match->free)(match->user_data);
+ match->user_data = NULL;
+ }
+
+ sfree(obj->matches);
+ sfree(obj);
+} /* void tail_match_destroy */
+
+int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
+ int (*submit_match)(cu_match_t *match,
+ void *user_data),
+ void *user_data,
+ void (*free_user_data)(void *user_data)) {
+ cu_tail_match_match_t *temp;
+
+ temp = realloc(obj->matches,
+ sizeof(cu_tail_match_match_t) * (obj->matches_num + 1));
+ if (temp == NULL)
+ return (-1);
+
+ obj->matches = temp;
+ obj->matches_num++;
+
+ DEBUG("tail_match_add_match interval %lf",
+ CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
+ temp = obj->matches + (obj->matches_num - 1);
+
+ temp->match = match;
+ temp->user_data = user_data;
+ temp->submit = submit_match;
+ temp->free = free_user_data;
+
+ return (0);
+} /* int tail_match_add_match */
+
+int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
+ const char *excluderegex, int ds_type,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance,
+ const latency_config_t latency_cfg,
+ const cdtime_t interval) {
+ cu_match_t *match;
+ cu_tail_match_simple_t *user_data;
+ int status;
+
+ match = match_create_simple(regex, excluderegex, ds_type);
+ if (match == NULL)
+ return (-1);
+
+ user_data = calloc(1, sizeof(*user_data));
+ if (user_data == NULL) {
+ match_destroy(match);
+ return (-1);
+ }
+
+ sstrncpy(user_data->plugin, plugin, sizeof(user_data->plugin));
+ if (plugin_instance != NULL)
+ sstrncpy(user_data->plugin_instance, plugin_instance,
+ sizeof(user_data->plugin_instance));
+
+ sstrncpy(user_data->type, type, sizeof(user_data->type));
+ if (type_instance != NULL)
+ sstrncpy(user_data->type_instance, type_instance,
+ sizeof(user_data->type_instance));
+
+ user_data->interval = interval;
+
+ if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
+ (ds_type & UTILS_MATCH_CF_GAUGE_DIST)) {
+ status = latency_config_copy(&user_data->latency_config, latency_cfg);
+ if (status != 0) {
+ ERROR("tail_match_add_match_simple: latency_config_copy() failed.");
+ status = -1;
+ goto out;
+ }
+
+ status = tail_match_add_match(obj, match, latency_submit_match, user_data,
+ tail_match_simple_free);
+ } else {
+ status =
+ tail_match_add_match(obj, match, simple_submit_match, user_data, free);
+ }
+
+out:
+ if (status != 0) {
+ tail_match_simple_free(user_data);
+ match_destroy(match);
+ }
+
+ return (status);
+} /* int tail_match_add_match_simple */
+
+int tail_match_read(cu_tail_match_t *obj) {
+ char buffer[4096];
+ int status;
+
+ status = cu_tail_read(obj->tail, buffer, sizeof(buffer), tail_callback,
+ (void *)obj);
+ if (status != 0) {
+ ERROR("tail_match: cu_tail_read failed.");
+ return (status);
+ }
+
+ for (size_t i = 0; i < obj->matches_num; i++) {
+ cu_tail_match_match_t *lt_match = obj->matches + i;
+
+ if (lt_match->submit == NULL)
+ continue;
+
+ (*lt_match->submit)(lt_match->match, lt_match->user_data);
+ }
+
+ return (0);
+} /* int tail_match_read */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/*
+ * collectd - src/utils_tail_match.h
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * Copyright (C) 2008 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * Description:
+ * `tail_match' uses `utils_tail' and `utils_match' to tail a file and try to
+ * match it using several regular expressions. Matches are then passed to
+ * user-provided callback functions or default handlers. This should keep all
+ * of the parsing logic out of the actual plugin, which only operate with
+ * regular expressions.
+ */
+
+#include "utils_latency_config.h"
+#include "utils_match.h"
+
+struct cu_tail_match_s;
+typedef struct cu_tail_match_s cu_tail_match_t;
+
+/*
+ * NAME
+ * tail_match_create
+ *
+ * DESCRIPTION
+ * Allocates, initializes and returns a new `cu_tail_match_t' object.
+ *
+ * PARAMETERS
+ * `filename' The name to read data from.
+ *
+ * RETURN VALUE
+ * Returns NULL upon failure, non-NULL otherwise.
+ */
+cu_tail_match_t *tail_match_create(const char *filename);
+
+/*
+ * NAME
+ * tail_match_destroy
+ *
+ * DESCRIPTION
+ * Releases resources used by the `cu_tail_match_t' object.
+ *
+ * PARAMETERS
+ * The object to destroy.
+ */
+void tail_match_destroy(cu_tail_match_t *obj);
+
+/*
+ * NAME
+ * tail_match_add_match
+ *
+ * DESCRIPTION
+ * Adds a match, in form of a `cu_match_t' object, to the object.
+ * After data has been read from the logfile (using utils_tail) the callback
+ * function `submit_match' is called with the match object and the user
+ * supplied data.
+ * Please note that his function is called regardless whether this match
+ * matched any lines recently or not.
+ * When `tail_match_destroy' is called the `user_data' pointer is freed using
+ * the `free_user_data' callback - if it is not NULL.
+ * When using this interface the `tail_match' module doesn't dispatch any
+ * values
+ * itself - all that has to happen in either the match-callbacks or the
+ * submit_match callback.
+ *
+ * RETURN VALUE
+ * Zero upon success, non-zero otherwise.
+ */
+int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
+ int (*submit_match)(cu_match_t *match,
+ void *user_data),
+ void *user_data,
+ void (*free_user_data)(void *user_data));
+
+/*
+ * NAME
+ * tail_match_add_match_simple
+ *
+ * DESCRIPTION
+ * A simplified version of `tail_match_add_match'. The regular expressen
+ * `regex'
+ * must match a number, which is then dispatched according to `ds_type'. See
+ * the `match_create_simple' function in utils_match.h for a description how
+ * this flag effects calculation of a new value.
+ * The values gathered are dispatched by the tail_match module in this case.
+ * The
+ * passed `plugin', `plugin_instance', `type', and `type_instance' are
+ * directly used when submitting these values.
+ * With excluderegex it is possible to exlude lines from the match.
+ * The `latency_cfg' specifies configuration for submitting latency.
+ *
+ * RETURN VALUE
+ * Zero upon success, non-zero otherwise.
+ */
+int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
+ const char *excluderegex, int ds_type,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance,
+ const latency_config_t latency_cfg,
+ const cdtime_t interval);
+
+/*
+ * NAME
+ * tail_match_read
+ *
+ * DESCRIPTION
+ * This function should be called periodically by plugins. It reads new lines
+ * from the logfile using `utils_tail' and tries to match them using all
+ * added `utils_match' objects.
+ * After all lines have been read and processed, the submit_match callback is
+ * called or, in case of tail_match_add_match_simple, the data is dispatched
+ * to
+ * the daemon directly.
+ *
+ * RETURN VALUE
+ * Zero on success, nonzero on failure.
+*/
+int tail_match_read(cu_tail_match_t *obj);
+
+/* vim: set sw=2 sts=2 ts=8 : */
typedef struct user_obj_s user_obj_t;
struct user_obj_s {
void *user_obj;
- identifier_t ident;
+ lookup_identifier_t ident;
user_obj_t *next;
};
} /* }}} int lu_copy_ident_to_match_part */
static int lu_copy_ident_to_match(identifier_match_t *match, /* {{{ */
- identifier_t const *ident,
+ lookup_identifier_t const *ident,
unsigned int group_by) {
memset(match, 0, sizeof(*match));
} /* }}} void lookup_destroy */
int lookup_add(lookup_t *obj, /* {{{ */
- identifier_t const *ident, unsigned int group_by,
+ lookup_identifier_t const *ident, unsigned int group_by,
void *user_class) {
by_type_entry_t *by_type = NULL;
user_class_list_t *user_class_obj;
* freed. */
typedef void (*lookup_free_obj_callback_t)(void *user_obj);
-struct identifier_s {
+struct lookup_identifier_s {
char host[DATA_MAX_NAME_LEN];
char plugin[DATA_MAX_NAME_LEN];
char plugin_instance[DATA_MAX_NAME_LEN];
char type[DATA_MAX_NAME_LEN];
char type_instance[DATA_MAX_NAME_LEN];
};
-typedef struct identifier_s identifier_t;
+typedef struct lookup_identifier_s lookup_identifier_t;
#define LU_GROUP_BY_HOST 0x01
#define LU_GROUP_BY_PLUGIN 0x02
lookup_free_obj_callback_t);
void lookup_destroy(lookup_t *obj);
-int lookup_add(lookup_t *obj, identifier_t const *ident, unsigned int group_by,
- void *user_class);
+int lookup_add(lookup_t *obj, lookup_identifier_t const *ident,
+ unsigned int group_by, void *user_class);
/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
int lookup_search(lookup_t *obj, data_set_t const *ds, value_list_t const *vl);
static _Bool expect_new_obj = 0;
static _Bool have_new_obj = 0;
-static identifier_t last_class_ident;
-static identifier_t last_obj_ident;
+static lookup_identifier_t last_class_ident;
+static lookup_identifier_t last_obj_ident;
static data_source_t dsrc_test = {"value", DS_TYPE_DERIVE, 0.0, NAN};
static data_set_t const ds_test = {"test", 1, &dsrc_test};
static int lookup_obj_callback(data_set_t const *ds, value_list_t const *vl,
void *user_class, void *user_obj) {
- identifier_t *class = user_class;
- identifier_t *obj = user_obj;
+ lookup_identifier_t *class = user_class;
+ lookup_identifier_t *obj = user_obj;
OK1(expect_new_obj == have_new_obj,
(expect_new_obj ? "New obj is created." : "Updating existing obj."));
static void *lookup_class_callback(data_set_t const *ds, value_list_t const *vl,
void *user_class) {
- identifier_t *class = user_class;
- identifier_t *obj;
+ lookup_identifier_t *class = user_class;
+ lookup_identifier_t *obj;
assert(expect_new_obj);
char const *plugin_instance, char const *type,
char const *type_instance,
unsigned int group_by) {
- identifier_t ident;
+ lookup_identifier_t ident;
void *user_class;
strncpy(ident.host, host, sizeof(ident.host));
char const *plugin_instance, char const *type,
char const *type_instance, _Bool expect_new) {
int status;
- value_list_t vl = VALUE_LIST_STATIC;
+ value_list_t vl = VALUE_LIST_INIT;
data_set_t const *ds = &ds_unknown;
strncpy(vl.host, host, sizeof(vl.host));
Memcheck:Addr4
fun:parse_value
fun:parse_values
- fun:test_parse_values
+ ...
fun:main
}
vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
-
sstrncpy(vl.plugin, "varnish", sizeof(vl.plugin));
if (plugin_instance == NULL)
plugin_instance = "default";
-
ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s",
plugin_instance, category);
const char *category, const char *type,
const char *type_instance,
uint64_t gauge_value) {
- value_t value;
-
- value.gauge = (gauge_t)gauge_value;
-
- return (
- varnish_submit(plugin_instance, category, type, type_instance, value));
+ return (varnish_submit(plugin_instance, category, type, type_instance,
+ (value_t){.gauge = (gauge_t)gauge_value}));
} /* }}} int varnish_submit_gauge */
static int varnish_submit_derive(const char *plugin_instance, /* {{{ */
const char *category, const char *type,
const char *type_instance,
uint64_t derive_value) {
- value_t value;
-
- value.derive = (derive_t)derive_value;
-
- return (
- varnish_submit(plugin_instance, category, type, type_instance, value));
+ return (varnish_submit(plugin_instance, category, type, type_instance,
+ (value_t){.derive = (derive_t)derive_value}));
} /* }}} int varnish_submit_derive */
#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
varnish_config_apply_default(conf);
- user_data_t ud = {.data = conf, .free_func = varnish_config_free};
-
- plugin_register_complex_read(/* group = */ "varnish",
- /* name = */ "varnish/localhost",
- /* callback = */ varnish_read,
- /* interval = */ 0,
- /* user data = */ &ud);
+ plugin_register_complex_read(
+ /* group = */ "varnish",
+ /* name = */ "varnish/localhost",
+ /* callback = */ varnish_read,
+ /* interval = */ 0, &(user_data_t){
+ .data = conf, .free_func = varnish_config_free,
+ });
return (0);
} /* }}} int varnish_init */
static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */
{
user_config_t *conf;
- user_data_t ud;
char callback_name[DATA_MAX_NAME_LEN];
conf = calloc(1, sizeof(*conf));
ssnprintf(callback_name, sizeof(callback_name), "varnish/%s",
(conf->instance == NULL) ? "localhost" : conf->instance);
- ud.data = conf;
- ud.free_func = varnish_config_free;
-
- plugin_register_complex_read(/* group = */ "varnish",
- /* name = */ callback_name,
- /* callback = */ varnish_read,
- /* interval = */ 0,
- /* user data = */ &ud);
+ plugin_register_complex_read(
+ /* group = */ "varnish",
+ /* name = */ callback_name,
+ /* callback = */ varnish_read,
+ /* interval = */ 0, &(user_data_t){
+ .data = conf, .free_func = varnish_config_free,
+ });
have_instance = 1;
#include "utils_complain.h"
#include "utils_ignorelist.h"
+#include <libgen.h> /* for basename(3) */
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#include <libxml/parser.h>
"Domain",
"BlockDevice",
+ "BlockDeviceFormat",
+ "BlockDeviceFormatBasename",
"InterfaceDevice",
"IgnoreSelected",
static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] = {
plginst_none};
+/* BlockDeviceFormat */
+enum bd_field { target, source };
+
/* InterfaceFormat. */
enum if_field { if_address, if_name, if_number };
+/* BlockDeviceFormatBasename */
+_Bool blockdevice_format_basename = 0;
+static enum bd_field blockdevice_format = target;
static enum if_field interface_format = if_name;
/* Time that we last refreshed. */
} /* void init_value_list */
-static void memory_submit(gauge_t memory, virDomainPtr dom) {
- value_t values[1];
+static void submit(virDomainPtr dom, char const *type,
+ char const *type_instance, value_t *values,
+ size_t values_len) {
value_list_t vl = VALUE_LIST_INIT;
-
init_value_list(&vl, dom);
- values[0].gauge = memory;
-
vl.values = values;
- vl.values_len = 1;
+ vl.values_len = values_len;
- sstrncpy(vl.type, "memory", sizeof(vl.type));
- sstrncpy(vl.type_instance, "total", sizeof(vl.type_instance));
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ if (type_instance != NULL)
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
}
-static void memory_stats_submit(gauge_t memory, virDomainPtr dom,
+static void memory_submit(gauge_t value, virDomainPtr dom) {
+ submit(dom, "memory", "total", &(value_t){.gauge = value}, 1);
+}
+
+static void memory_stats_submit(gauge_t value, virDomainPtr dom,
int tag_index) {
static const char *tags[] = {"swap_in", "swap_out", "major_fault",
"minor_fault", "unused", "available",
"actual_balloon", "rss"};
- value_t values[1];
- value_list_t vl = VALUE_LIST_INIT;
-
- init_value_list(&vl, dom);
-
- values[0].gauge = memory;
-
- vl.values = values;
- vl.values_len = 1;
-
- sstrncpy(vl.type, "memory", sizeof(vl.type));
- sstrncpy(vl.type_instance, tags[tag_index], sizeof(vl.type_instance));
+ if ((tag_index < 0) || (tag_index >= STATIC_ARRAY_SIZE(tags))) {
+ ERROR("virt plugin: Array index out of bounds: tag_index = %d", tag_index);
+ return;
+ }
- plugin_dispatch_values(&vl);
+ submit(dom, "memory", tags[tag_index], &(value_t){.gauge = value}, 1);
}
-static void cpu_submit(unsigned long long cpu_time, virDomainPtr dom,
+static void cpu_submit(unsigned long long value, virDomainPtr dom,
const char *type) {
- value_t values[1];
- value_list_t vl = VALUE_LIST_INIT;
-
- init_value_list(&vl, dom);
-
- values[0].derive = cpu_time;
-
- vl.values = values;
- vl.values_len = 1;
-
- sstrncpy(vl.type, type, sizeof(vl.type));
-
- plugin_dispatch_values(&vl);
+ submit(dom, type, NULL, &(value_t){.derive = (derive_t)value}, 1);
}
-static void vcpu_submit(derive_t cpu_time, virDomainPtr dom, int vcpu_nr,
+static void vcpu_submit(derive_t value, virDomainPtr dom, int vcpu_nr,
const char *type) {
- value_t values[1];
- value_list_t vl = VALUE_LIST_INIT;
+ char type_instance[DATA_MAX_NAME_LEN];
- init_value_list(&vl, dom);
-
- values[0].derive = cpu_time;
- vl.values = values;
- vl.values_len = 1;
+ ssnprintf(type_instance, sizeof(type_instance), "%d", vcpu_nr);
- sstrncpy(vl.type, type, sizeof(vl.type));
- ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%d", vcpu_nr);
-
- plugin_dispatch_values(&vl);
+ submit(dom, type, type_instance, &(value_t){.derive = value}, 1);
}
static void submit_derive2(const char *type, derive_t v0, derive_t v1,
virDomainPtr dom, const char *devname) {
- value_t values[2];
- value_list_t vl = VALUE_LIST_INIT;
-
- init_value_list(&vl, dom);
-
- values[0].derive = v0;
- values[1].derive = v1;
- vl.values = values;
- vl.values_len = 2;
-
- sstrncpy(vl.type, type, sizeof(vl.type));
- sstrncpy(vl.type_instance, devname, sizeof(vl.type_instance));
+ value_t values[] = {
+ {.derive = v0}, {.derive = v1},
+ };
- plugin_dispatch_values(&vl);
+ submit(dom, type, devname, values, STATIC_ARRAY_SIZE(values));
} /* void submit_derive2 */
static int lv_init(void) {
return 1;
return 0;
}
+
+ if (strcasecmp(key, "BlockDeviceFormat") == 0) {
+ if (strcasecmp(value, "target") == 0)
+ blockdevice_format = target;
+ else if (strcasecmp(value, "source") == 0)
+ blockdevice_format = source;
+ else {
+ ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s", value);
+ return -1;
+ }
+ return 0;
+ }
+ if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) {
+ blockdevice_format_basename = IS_TRUE(value);
+ return 0;
+ }
if (strcasecmp(key, "InterfaceDevice") == 0) {
if (ignorelist_add(il_interface_devices, value))
return 1;
sizeof stats) != 0)
continue;
+ char *type_instance = NULL;
+ if (blockdevice_format_basename && blockdevice_format == source)
+ type_instance = strdup(basename(block_devices[i].path));
+ else
+ type_instance = strdup(block_devices[i].path);
+
if ((stats.rd_req != -1) && (stats.wr_req != -1))
submit_derive2("disk_ops", (derive_t)stats.rd_req, (derive_t)stats.wr_req,
- block_devices[i].dom, block_devices[i].path);
+ block_devices[i].dom, type_instance);
if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
submit_derive2("disk_octets", (derive_t)stats.rd_bytes,
(derive_t)stats.wr_bytes, block_devices[i].dom,
- block_devices[i].path);
+ type_instance);
+
+ sfree(type_instance);
} /* for (nr_block_devices) */
/* Get interface stats for each domain. */
xpath_ctx = xmlXPathNewContext(xml_doc);
/* Block devices. */
- xpath_obj = xmlXPathEval((xmlChar *)"/domain/devices/disk/target[@dev]",
- xpath_ctx);
+ char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
+ if (blockdevice_format == source)
+ bd_xmlpath = "/domain/devices/disk/source[@dev]";
+ xpath_obj = xmlXPathEval((xmlChar *)bd_xmlpath, xpath_ctx);
+
if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
xpath_obj->nodesetval == NULL)
goto cont;
vl.values = values;
vl.values_len = values_len;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "vmem", sizeof(vl.plugin));
if (plugin_instance != NULL)
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
static void submit_two(const char *plugin_instance, const char *type,
const char *type_instance, derive_t c0, derive_t c1) {
- value_t values[2];
+ value_t values[] = {
+ {.derive = c0}, {.derive = c1},
+ };
- values[0].derive = c0;
- values[1].derive = c1;
-
- submit(plugin_instance, type, type_instance, values, 2);
+ submit(plugin_instance, type, type_instance, values,
+ STATIC_ARRAY_SIZE(values));
} /* void submit_one */
static void submit_one(const char *plugin_instance, const char *type,
static void traffic_submit(const char *plugin_instance,
const char *type_instance, derive_t rx,
derive_t tx) {
- value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].derive = rx;
- values[1].derive = tx;
+ value_t values[] = {
+ {.derive = rx}, {.derive = tx},
+ };
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "if_octets", sizeof(vl.type));
static void load_submit(const char *plugin_instance, gauge_t snum, gauge_t mnum,
gauge_t lnum) {
- value_t values[3];
value_list_t vl = VALUE_LIST_INIT;
-
- values[0].gauge = snum;
- values[1].gauge = mnum;
- values[2].gauge = lnum;
+ value_t values[] = {
+ {.gauge = snum}, {.gauge = mnum}, {.gauge = lnum},
+ };
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "load", sizeof(vl.type));
const char *type_instance, gauge_t value)
{
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
- vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+ vl.values = &(value_t){.gauge = value};
+ vl.values_len = 1;
sstrncpy(vl.plugin, "vserver", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
static void wireless_submit(const char *plugin_instance, const char *type,
double value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "wireless", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
cf_util_get_flag(child, &cb->format_flags, GRAPHITE_SEPARATE_INSTANCES);
else if (strcasecmp("AlwaysAppendDS", child->key) == 0)
cf_util_get_flag(child, &cb->format_flags, GRAPHITE_ALWAYS_APPEND_DS);
+ else if (strcasecmp("PreserveSeparator", child->key) == 0)
+ cf_util_get_flag(child, &cb->format_flags, GRAPHITE_PRESERVE_SEPARATOR);
+ else if (strcasecmp("DropDuplicateFields", child->key) == 0)
+ cf_util_get_flag(child, &cb->format_flags, GRAPHITE_DROP_DUPE_FIELDS);
else if (strcasecmp("EscapeCharacter", child->key) == 0)
config_set_char(&cb->escape_char, child);
else {
ssnprintf(callback_name, sizeof(callback_name), "write_graphite/%s",
cb->name);
- user_data_t ud = {.data = cb, .free_func = wg_callback_free};
+ plugin_register_write(callback_name, wg_write,
+ &(user_data_t){
+ .data = cb, .free_func = wg_callback_free,
+ });
- plugin_register_write(callback_name, wg_write, &ud);
-
- ud.free_func = NULL;
- plugin_register_flush(callback_name, wg_flush, &ud);
+ plugin_register_flush(callback_name, wg_flush, &(user_data_t){.data = cb});
return (0);
}
}
assert(command_len < cb->send_buffer_free);
+ /* Make scan-build happy. */
+ assert(cb->send_buffer != NULL);
+
/* `command_len + 1' because `command_len' does not include the
* trailing null byte. Neither does `send_buffer_fill'. */
memcpy(cb->send_buffer + cb->send_buffer_fill, command, command_len + 1);
switch (ctx->format) {
case KAFKA_FORMAT_COMMAND:
- status = create_putval(buffer, sizeof(buffer), ds, vl);
+ status = cmd_create_putval(buffer, sizeof(buffer), ds, vl);
if (status != 0) {
- ERROR("write_kafka plugin: create_putval failed with status %i.", status);
+ ERROR("write_kafka plugin: cmd_create_putval failed with status %i.",
+ status);
return status;
}
blen = strlen(buffer);
status = cf_util_get_flag(child, &tctx->graphite_flags,
GRAPHITE_ALWAYS_APPEND_DS);
+ } else if (strcasecmp("GraphitePreserveSeparator", child->key) == 0) {
+ status = cf_util_get_flag(child, &tctx->graphite_flags,
+ GRAPHITE_PRESERVE_SEPARATOR);
+
} else if (strcasecmp("GraphitePrefix", child->key) == 0) {
status = cf_util_get_string(child, &tctx->prefix);
} else if (strcasecmp("GraphitePostfix", child->key) == 0) {
ssnprintf(callback_name, sizeof(callback_name), "write_kafka/%s",
tctx->topic_name);
- user_data_t ud = {.data = tctx, .free_func = kafka_topic_context_free};
-
- status = plugin_register_write(callback_name, kafka_write, &ud);
+ status = plugin_register_write(
+ callback_name, kafka_write,
+ &(user_data_t){
+ .data = tctx, .free_func = kafka_topic_context_free,
+ });
if (status != 0) {
WARNING("write_kafka plugin: plugin_register_write (\"%s\") "
"failed with status %i.",
#include "plugin.h"
#include "utils_format_graphite.h"
+#include "utils_format_json.h"
#include <netdb.h>
-#define WL_BUF_SIZE 8192
+#define WL_BUF_SIZE 16384
-static int wl_write_messages(const data_set_t *ds, const value_list_t *vl) {
+#define WL_FORMAT_GRAPHITE 1
+#define WL_FORMAT_JSON 2
+
+/* Plugin:WriteLog has to also operate without a config, so use a global. */
+int wl_format = WL_FORMAT_GRAPHITE;
+
+static int wl_write_graphite(const data_set_t *ds, const value_list_t *vl) {
char buffer[WL_BUF_SIZE] = {0};
int status;
if (0 != strcmp(ds->type, vl->type)) {
- ERROR("write_log plugin: DS type does not match "
- "value list type");
+ ERROR("write_log plugin: DS type does not match value list type");
return -1;
}
INFO("write_log values:\n%s", buffer);
return (0);
-} /* int wl_write_messages */
+} /* int wl_write_graphite */
+
+static int wl_write_json(const data_set_t *ds, const value_list_t *vl) {
+ char buffer[WL_BUF_SIZE] = {0};
+ size_t bfree = sizeof(buffer);
+ size_t bfill = 0;
+
+ if (0 != strcmp(ds->type, vl->type)) {
+ ERROR("write_log plugin: DS type does not match value list type");
+ return -1;
+ }
+
+ format_json_initialize(buffer, &bfill, &bfree);
+ format_json_value_list(buffer, &bfill, &bfree, ds, vl,
+ /* store rates = */ 0);
+ format_json_finalize(buffer, &bfill, &bfree);
+
+ INFO("write_log values:\n%s", buffer);
+
+ return (0);
+} /* int wl_write_json */
static int wl_write(const data_set_t *ds, const value_list_t *vl,
__attribute__((unused)) user_data_t *user_data) {
- int status;
+ int status = 0;
- status = wl_write_messages(ds, vl);
+ if (wl_format == WL_FORMAT_GRAPHITE) {
+ status = wl_write_graphite(ds, vl);
+ } else if (wl_format == WL_FORMAT_JSON) {
+ status = wl_write_json(ds, vl);
+ }
return (status);
}
+static int wl_config(oconfig_item_t *ci) /* {{{ */
+{
+ _Bool format_seen = 0;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Format", child->key) == 0) {
+ char str[16];
+
+ if (cf_util_get_string_buffer(child, str, sizeof(str)) != 0)
+ continue;
+
+ if (format_seen) {
+ WARNING("write_log plugin: Redefining option `%s'.", child->key);
+ }
+ format_seen = 1;
+
+ if (strcasecmp("Graphite", str) == 0)
+ wl_format = WL_FORMAT_GRAPHITE;
+ else if (strcasecmp("JSON", str) == 0)
+ wl_format = WL_FORMAT_JSON;
+ else {
+ ERROR("write_log plugin: Unknown format `%s' for option `%s'.", str,
+ child->key);
+ return (-EINVAL);
+ }
+ } else {
+ ERROR("write_log plugin: Invalid configuration option: `%s'.",
+ child->key);
+ return (-EINVAL);
+ }
+ }
+
+ return (0);
+} /* }}} int wl_config */
+
void module_register(void) {
+ plugin_register_complex_config("write_log", wl_config);
+ /* If config is supplied, the global wl_format will be set. */
plugin_register_write("write_log", wl_write, NULL);
}
ssnprintf(cb_name, sizeof(cb_name), "write_mongodb/%s", node->name);
- user_data_t ud = {.data = node, .free_func = wm_config_free};
-
- status = plugin_register_write(cb_name, wm_write, &ud);
+ status = plugin_register_write(
+ cb_name, wm_write, &(user_data_t){
+ .data = node, .free_func = wm_config_free,
+ });
INFO("write_mongodb plugin: registered write plugin %s %d", cb_name,
status);
}
--- /dev/null
+/**
+ * collectd - src/write_prometheus.c
+ * Copyright (C) 2016 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "common.h"
+#include "plugin.h"
+#include "utils_avltree.h"
+#include "utils_complain.h"
+#include "utils_time.h"
+
+#include "prometheus.pb-c.h"
+
+#include <microhttpd.h>
+
+#ifndef PROMETHEUS_DEFAULT_STALENESS_DELTA
+#define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T_STATIC(300)
+#endif
+
+#define VARINT_UINT32_BYTES 5
+
+#define CONTENT_TYPE_PROTO \
+ "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; " \
+ "encoding=delimited"
+#define CONTENT_TYPE_TEXT "text/plain; version=0.0.4"
+
+static c_avl_tree_t *metrics;
+static pthread_mutex_t metrics_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static unsigned short httpd_port = 9103;
+static struct MHD_Daemon *httpd;
+
+static cdtime_t staleness_delta = PROMETHEUS_DEFAULT_STALENESS_DELTA;
+
+/* Unfortunately, protoc-c doesn't export it's implementation of varint, so we
+ * need to implement our own. */
+static size_t varint(uint8_t buffer[static VARINT_UINT32_BYTES],
+ uint32_t value) {
+ for (size_t i = 0; i < VARINT_UINT32_BYTES; i++) {
+ buffer[i] = (uint8_t)(value & 0x7f);
+ value >>= 7;
+
+ if (value == 0)
+ return i + 1;
+
+ buffer[i] |= 0x80;
+ }
+
+ return 0;
+}
+
+/* format_protobuf iterates over all metric families in "metrics" and adds them
+ * to a buffer in ProtoBuf format. It prefixes each protobuf with its encoded
+ * size, the so called "delimited" format. */
+static void format_protobuf(ProtobufCBuffer *buffer) {
+ pthread_mutex_lock(&metrics_lock);
+
+ char *unused_name;
+ Io__Prometheus__Client__MetricFamily *fam;
+ c_avl_iterator_t *iter = c_avl_get_iterator(metrics);
+ while (c_avl_iterator_next(iter, (void *)&unused_name, (void *)&fam) == 0) {
+ /* Prometheus uses a message length prefix to determine where one
+ * MetricFamily ends and the next begins. This delimiter is encoded as a
+ * "varint", which is common in Protobufs. */
+ uint8_t delim[VARINT_UINT32_BYTES] = {0};
+ size_t delim_len = varint(
+ delim,
+ (uint32_t)io__prometheus__client__metric_family__get_packed_size(fam));
+ buffer->append(buffer, delim_len, delim);
+
+ io__prometheus__client__metric_family__pack_to_buffer(fam, buffer);
+ }
+ c_avl_iterator_destroy(iter);
+
+ pthread_mutex_unlock(&metrics_lock);
+}
+
+static char const *escape_label_value(char *buffer, size_t buffer_size,
+ char const *value) {
+ /* shortcut for values that don't need escaping. */
+ if (strpbrk(value, "\n\"\\") == NULL)
+ return value;
+
+ size_t value_len = strlen(value);
+ size_t buffer_len = 0;
+
+ for (size_t i = 0; i < value_len; i++) {
+ switch (value[i]) {
+ case '\n':
+ case '"':
+ case '\\':
+ if ((buffer_size - buffer_len) < 3) {
+ break;
+ }
+ buffer[buffer_len] = '\\';
+ buffer[buffer_len + 1] = (value[i] == '\n') ? 'n' : value[i];
+ buffer_len += 2;
+ break;
+
+ default:
+ if ((buffer_size - buffer_len) < 2) {
+ break;
+ }
+ buffer[buffer_len] = value[i];
+ buffer_len++;
+ break;
+ }
+ }
+
+ assert(buffer_len < buffer_size);
+ buffer[buffer_len] = 0;
+ return buffer;
+}
+
+/* format_labels formats a metric's labels in Prometheus-compatible format. This
+ * format looks like this:
+ *
+ * key0="value0",key1="value1"
+ */
+static char *format_labels(char *buffer, size_t buffer_size,
+ Io__Prometheus__Client__Metric const *m) {
+ /* our metrics always have at least one and at most three labels. */
+ assert(m->n_label >= 1);
+ assert(m->n_label <= 3);
+
+#define LABEL_KEY_SIZE DATA_MAX_NAME_LEN
+#define LABEL_VALUE_SIZE (2 * DATA_MAX_NAME_LEN - 1)
+#define LABEL_BUFFER_SIZE (LABEL_KEY_SIZE + LABEL_VALUE_SIZE + 4)
+
+ char *labels[3] = {
+ (char[LABEL_BUFFER_SIZE]){0}, (char[LABEL_BUFFER_SIZE]){0},
+ (char[LABEL_BUFFER_SIZE]){0},
+ };
+
+ /* N.B.: the label *names* are hard-coded by this plugin and therefore we
+ * know that they are sane. */
+ for (size_t i = 0; i < m->n_label; i++) {
+ char value[LABEL_VALUE_SIZE];
+ ssnprintf(labels[i], LABEL_BUFFER_SIZE, "%s=\"%s\"", m->label[i]->name,
+ escape_label_value(value, sizeof(value), m->label[i]->value));
+ }
+
+ strjoin(buffer, buffer_size, labels, m->n_label, ",");
+ return buffer;
+}
+
+/* format_protobuf iterates over all metric families in "metrics" and adds them
+ * to a buffer in plain text format. */
+static void format_text(ProtobufCBuffer *buffer) {
+ pthread_mutex_lock(&metrics_lock);
+
+ char *unused_name;
+ Io__Prometheus__Client__MetricFamily *fam;
+ c_avl_iterator_t *iter = c_avl_get_iterator(metrics);
+ while (c_avl_iterator_next(iter, (void *)&unused_name, (void *)&fam) == 0) {
+ char line[1024]; /* 4x DATA_MAX_NAME_LEN? */
+
+ ssnprintf(line, sizeof(line), "# HELP %s %s\n", fam->name, fam->help);
+ buffer->append(buffer, strlen(line), (uint8_t *)line);
+
+ ssnprintf(line, sizeof(line), "# TYPE %s %s\n", fam->name,
+ (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE)
+ ? "gauge"
+ : "counter");
+ buffer->append(buffer, strlen(line), (uint8_t *)line);
+
+ for (size_t i = 0; i < fam->n_metric; i++) {
+ Io__Prometheus__Client__Metric *m = fam->metric[i];
+
+ char labels[1024];
+
+ char timestamp_ms[24] = "";
+ if (m->has_timestamp_ms)
+ ssnprintf(timestamp_ms, sizeof(timestamp_ms), " %" PRIi64,
+ m->timestamp_ms);
+
+ if (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE)
+ ssnprintf(line, sizeof(line), "%s{%s} " GAUGE_FORMAT "%s\n", fam->name,
+ format_labels(labels, sizeof(labels), m), m->gauge->value,
+ timestamp_ms);
+ else /* if (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__COUNTER) */
+ ssnprintf(line, sizeof(line), "%s{%s} %.0f%s\n", fam->name,
+ format_labels(labels, sizeof(labels), m), m->counter->value,
+ timestamp_ms);
+
+ buffer->append(buffer, strlen(line), (uint8_t *)line);
+ }
+ }
+ c_avl_iterator_destroy(iter);
+
+ char server[1024];
+ ssnprintf(server, sizeof(server), "\n# collectd/write_prometheus %s at %s\n",
+ PACKAGE_VERSION, hostname_g);
+ buffer->append(buffer, strlen(server), (uint8_t *)server);
+
+ pthread_mutex_unlock(&metrics_lock);
+}
+
+/* http_handler is the callback called by the microhttpd library. It essentially
+ * handles all HTTP request aspects and creates an HTTP response. */
+static int http_handler(void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **connection_state) {
+ if (strcmp(method, MHD_HTTP_METHOD_GET) != 0) {
+ return MHD_NO;
+ }
+
+ /* On the first call for each connection, return without anything further.
+ * Apparently not everything has been initialized yet or so; the docs are not
+ * very specific on the issue. */
+ if (*connection_state == NULL) {
+ /* set to a random non-NULL pointer. */
+ *connection_state = &(int){42};
+ return MHD_YES;
+ }
+
+ char const *accept = MHD_lookup_connection_value(connection, MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_ACCEPT);
+ _Bool want_proto =
+ (accept != NULL) &&
+ (strstr(accept, "application/vnd.google.protobuf") != NULL);
+
+ uint8_t scratch[4096] = {0};
+ ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(scratch);
+ ProtobufCBuffer *buffer = (ProtobufCBuffer *)&simple;
+
+ if (want_proto)
+ format_protobuf(buffer);
+ else
+ format_text(buffer);
+
+#if defined(MHD_VERSION) && MHD_VERSION >= 0x00090500
+ struct MHD_Response *res = MHD_create_response_from_buffer(
+ simple.len, simple.data, MHD_RESPMEM_MUST_COPY);
+#else
+ struct MHD_Response *res = MHD_create_response_from_data(
+ simple.len, simple.data, /* must_free = */ 0, /* must_copy = */ 1);
+#endif
+ MHD_add_response_header(res, MHD_HTTP_HEADER_CONTENT_TYPE,
+ want_proto ? CONTENT_TYPE_PROTO : CONTENT_TYPE_TEXT);
+
+ int status = MHD_queue_response(connection, MHD_HTTP_OK, res);
+
+ MHD_destroy_response(res);
+ PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple);
+ return status;
+}
+
+/*
+ * Functions for manipulating the global state in "metrics". This is organized
+ * in two tiers: the global "metrics" tree holds "metric families", which are
+ * identified by a name (a string). Each metric family has one or more
+ * "metrics", which are identified by a unique set of key-value-pairs. For
+ * example:
+ *
+ * collectd_cpu_total
+ * {cpu="0",type="idle"}
+ * {cpu="0",type="user"}
+ * ...
+ * collectd_memory
+ * {memory="used"}
+ * {memory="free"}
+ * ...
+ * {{{ */
+/* label_pair_destroy frees the memory used by a label pair. */
+static void label_pair_destroy(Io__Prometheus__Client__LabelPair *msg) {
+ if (msg == NULL)
+ return;
+
+ sfree(msg->name);
+ sfree(msg->value);
+
+ sfree(msg);
+}
+
+/* label_pair_clone allocates and initializes a new label pair. */
+static Io__Prometheus__Client__LabelPair *
+label_pair_clone(Io__Prometheus__Client__LabelPair const *orig) {
+ Io__Prometheus__Client__LabelPair *copy = calloc(1, sizeof(*copy));
+ if (copy == NULL)
+ return NULL;
+ io__prometheus__client__label_pair__init(copy);
+
+ copy->name = strdup(orig->name);
+ copy->value = strdup(orig->value);
+ if ((copy->name == NULL) || (copy->value == NULL)) {
+ label_pair_destroy(copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+/* metric_destroy frees the memory used by a metric. */
+static void metric_destroy(Io__Prometheus__Client__Metric *msg) {
+ if (msg == NULL)
+ return;
+
+ for (size_t i = 0; i < msg->n_label; i++) {
+ label_pair_destroy(msg->label[i]);
+ }
+ sfree(msg->label);
+
+ sfree(msg->gauge);
+ sfree(msg->counter);
+
+ sfree(msg);
+}
+
+/* metric_cmp compares two metrics. It's prototype makes it easy to use with
+ * qsort(3) and bsearch(3). */
+static int metric_cmp(void const *a, void const *b) {
+ Io__Prometheus__Client__Metric const *m_a =
+ *((Io__Prometheus__Client__Metric **)a);
+ Io__Prometheus__Client__Metric const *m_b =
+ *((Io__Prometheus__Client__Metric **)b);
+
+ if (m_a->n_label < m_b->n_label)
+ return -1;
+ else if (m_a->n_label > m_b->n_label)
+ return 1;
+
+ /* Prometheus does not care about the order of labels. All labels in this
+ * plugin are created by METRIC_ADD_LABELS(), though, and therefore always
+ * appear in the same order. We take advantage of this and simplify the check
+ * by making sure all labels are the same in each position.
+ *
+ * We also only need to check the label values, because the label names are
+ * the same for all metrics in a metric family.
+ *
+ * 3 labels:
+ * [0] $plugin="$plugin_instance" => $plugin is the same within a family
+ * [1] type="$type_instance" => "type" is a static string
+ * [2] instance="$host" => "instance" is a static string
+ *
+ * 2 labels, variant 1:
+ * [0] $plugin="$plugin_instance" => $plugin is the same within a family
+ * [1] instance="$host" => "instance" is a static string
+ *
+ * 2 labels, variant 2:
+ * [0] $plugin="$type_instance" => $plugin is the same within a family
+ * [1] instance="$host" => "instance" is a static string
+ *
+ * 1 label:
+ * [1] instance="$host" => "instance" is a static string
+ */
+ for (size_t i = 0; i < m_a->n_label; i++) {
+ int status = strcmp(m_a->label[i]->value, m_b->label[i]->value);
+ if (status != 0)
+ return status;
+
+#if COLLECT_DEBUG
+ assert(strcmp(m_a->label[i]->name, m_b->label[i]->name) == 0);
+#endif
+ }
+
+ return 0;
+}
+
+#define METRIC_INIT \
+ &(Io__Prometheus__Client__Metric) { \
+ .label = \
+ (Io__Prometheus__Client__LabelPair *[]){ \
+ &(Io__Prometheus__Client__LabelPair){ \
+ .name = NULL, \
+ }, \
+ &(Io__Prometheus__Client__LabelPair){ \
+ .name = NULL, \
+ }, \
+ &(Io__Prometheus__Client__LabelPair){ \
+ .name = NULL, \
+ }, \
+ }, \
+ .n_label = 0, \
+ }
+
+#define METRIC_ADD_LABELS(m, vl) \
+ do { \
+ if (strlen((vl)->plugin_instance) != 0) { \
+ (m)->label[(m)->n_label]->name = (char *)(vl)->plugin; \
+ (m)->label[(m)->n_label]->value = (char *)(vl)->plugin_instance; \
+ (m)->n_label++; \
+ } \
+ \
+ if (strlen((vl)->type_instance) != 0) { \
+ (m)->label[(m)->n_label]->name = "type"; \
+ if (strlen((vl)->plugin_instance) == 0) \
+ (m)->label[(m)->n_label]->name = (char *)(vl)->plugin; \
+ (m)->label[(m)->n_label]->value = (char *)(vl)->type_instance; \
+ (m)->n_label++; \
+ } \
+ \
+ (m)->label[(m)->n_label]->name = "instance"; \
+ (m)->label[(m)->n_label]->value = (char *)(vl)->host; \
+ (m)->n_label++; \
+ } while (0)
+
+/* metric_clone allocates and initializes a new metric based on orig. */
+static Io__Prometheus__Client__Metric *
+metric_clone(Io__Prometheus__Client__Metric const *orig) {
+ Io__Prometheus__Client__Metric *copy = calloc(1, sizeof(*copy));
+ if (copy == NULL)
+ return NULL;
+ io__prometheus__client__metric__init(copy);
+
+ copy->n_label = orig->n_label;
+ copy->label = calloc(copy->n_label, sizeof(*copy->label));
+ if (copy->label == NULL) {
+ sfree(copy);
+ return NULL;
+ }
+
+ for (size_t i = 0; i < copy->n_label; i++) {
+ copy->label[i] = label_pair_clone(orig->label[i]);
+ if (copy->label[i] == NULL) {
+ metric_destroy(copy);
+ return NULL;
+ }
+ }
+
+ return copy;
+}
+
+/* metric_update stores the new value and timestamp in m. */
+static int metric_update(Io__Prometheus__Client__Metric *m, value_t value,
+ int ds_type, cdtime_t t, cdtime_t interval) {
+ if (ds_type == DS_TYPE_GAUGE) {
+ sfree(m->counter);
+ if (m->gauge == NULL) {
+ m->gauge = calloc(1, sizeof(*m->gauge));
+ if (m->gauge == NULL)
+ return ENOMEM;
+ io__prometheus__client__gauge__init(m->gauge);
+ }
+
+ m->gauge->value = (double)value.gauge;
+ m->gauge->has_value = 1;
+ } else { /* not gauge */
+ sfree(m->gauge);
+ if (m->counter == NULL) {
+ m->counter = calloc(1, sizeof(*m->counter));
+ if (m->counter == NULL)
+ return ENOMEM;
+ io__prometheus__client__counter__init(m->counter);
+ }
+
+ switch (ds_type) {
+ case DS_TYPE_ABSOLUTE:
+ m->counter->value = (double)value.absolute;
+ break;
+ case DS_TYPE_COUNTER:
+ m->counter->value = (double)value.counter;
+ break;
+ default:
+ m->counter->value = (double)value.derive;
+ break;
+ }
+ m->counter->has_value = 1;
+ }
+
+ /* Prometheus has a globally configured timeout after which metrics are
+ * considered stale. This causes problems when metrics have an interval
+ * exceeding that limit. We emulate the behavior of "pushgateway" and *not*
+ * send a timestamp value – Prometheus will fill in the current time. */
+ if (interval <= staleness_delta) {
+ m->timestamp_ms = CDTIME_T_TO_MS(t);
+ m->has_timestamp_ms = 1;
+ } else {
+ static c_complain_t long_metric = C_COMPLAIN_INIT_STATIC;
+ c_complain(
+ LOG_NOTICE, &long_metric,
+ "write_prometheus plugin: You have metrics with an interval exceeding "
+ "\"StalenessDelta\" setting (%.3fs). This is suboptimal, please check "
+ "the collectd.conf(5) manual page to understand what's going on.",
+ CDTIME_T_TO_DOUBLE(staleness_delta));
+
+ m->timestamp_ms = 0;
+ m->has_timestamp_ms = 0;
+ }
+
+ return 0;
+}
+
+/* metric_family_add_metric adds m to the metric list of fam. */
+static int metric_family_add_metric(Io__Prometheus__Client__MetricFamily *fam,
+ Io__Prometheus__Client__Metric *m) {
+ Io__Prometheus__Client__Metric **tmp =
+ realloc(fam->metric, (fam->n_metric + 1) * sizeof(*fam->metric));
+ if (tmp == NULL)
+ return ENOMEM;
+ fam->metric = tmp;
+
+ fam->metric[fam->n_metric] = m;
+ fam->n_metric++;
+
+ /* Sort the metrics so that lookup is fast. */
+ qsort(fam->metric, fam->n_metric, sizeof(*fam->metric), metric_cmp);
+
+ return 0;
+}
+
+/* metric_family_delete_metric looks up and deletes the metric corresponding to
+ * vl. */
+static int
+metric_family_delete_metric(Io__Prometheus__Client__MetricFamily *fam,
+ value_list_t const *vl) {
+ Io__Prometheus__Client__Metric *key = METRIC_INIT;
+ METRIC_ADD_LABELS(key, vl);
+
+ size_t i;
+ for (i = 0; i < fam->n_metric; i++) {
+ if (metric_cmp(&key, &fam->metric[i]) == 0)
+ break;
+ }
+
+ if (i >= fam->n_metric)
+ return ENOENT;
+
+ metric_destroy(fam->metric[i]);
+ if ((fam->n_metric - 1) > i)
+ memmove(&fam->metric[i], &fam->metric[i + 1],
+ ((fam->n_metric - 1) - i) * sizeof(fam->metric[i]));
+ fam->n_metric--;
+
+ if (fam->n_metric == 0) {
+ sfree(fam->metric);
+ return 0;
+ }
+
+ Io__Prometheus__Client__Metric **tmp =
+ realloc(fam->metric, fam->n_metric * sizeof(*fam->metric));
+ if (tmp != NULL)
+ fam->metric = tmp;
+
+ return 0;
+}
+
+/* metric_family_get_metric looks up the matching metric in a metric family,
+ * allocating it if necessary. */
+static Io__Prometheus__Client__Metric *
+metric_family_get_metric(Io__Prometheus__Client__MetricFamily *fam,
+ value_list_t const *vl) {
+ Io__Prometheus__Client__Metric *key = METRIC_INIT;
+ METRIC_ADD_LABELS(key, vl);
+
+ /* Metrics are sorted in metric_family_add_metric() so that we can do a binary
+ * search here. */
+ Io__Prometheus__Client__Metric **m = bsearch(
+ &key, fam->metric, fam->n_metric, sizeof(*fam->metric), metric_cmp);
+
+ if (m != NULL) {
+ return *m;
+ }
+
+ Io__Prometheus__Client__Metric *new_metric = metric_clone(key);
+ if (new_metric == NULL)
+ return NULL;
+
+ DEBUG("write_prometheus plugin: created new metric in family");
+ int status = metric_family_add_metric(fam, new_metric);
+ if (status != 0) {
+ metric_destroy(new_metric);
+ return NULL;
+ }
+
+ return new_metric;
+}
+
+/* metric_family_update looks up the matching metric in a metric family,
+ * allocating it if necessary, and updates the metric to the latest value. */
+static int metric_family_update(Io__Prometheus__Client__MetricFamily *fam,
+ data_set_t const *ds, value_list_t const *vl,
+ size_t ds_index) {
+ Io__Prometheus__Client__Metric *m = metric_family_get_metric(fam, vl);
+ if (m == NULL)
+ return -1;
+
+ return metric_update(m, vl->values[ds_index], ds->ds[ds_index].type, vl->time,
+ vl->interval);
+}
+
+/* metric_family_destroy frees the memory used by a metric family. */
+static void metric_family_destroy(Io__Prometheus__Client__MetricFamily *msg) {
+ if (msg == NULL)
+ return;
+
+ sfree(msg->name);
+ sfree(msg->help);
+
+ for (size_t i = 0; i < msg->n_metric; i++) {
+ metric_destroy(msg->metric[i]);
+ }
+ sfree(msg->metric);
+
+ sfree(msg);
+}
+
+/* metric_family_create allocates and initializes a new metric family. */
+static Io__Prometheus__Client__MetricFamily *
+metric_family_create(char *name, data_set_t const *ds, value_list_t const *vl,
+ size_t ds_index) {
+ Io__Prometheus__Client__MetricFamily *msg = calloc(1, sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+ io__prometheus__client__metric_family__init(msg);
+
+ msg->name = name;
+
+ char help[1024];
+ ssnprintf(
+ help, sizeof(help),
+ "write_prometheus plugin: '%s' Type: '%s', Dstype: '%s', Dsname: '%s'",
+ vl->plugin, vl->type, DS_TYPE_TO_STRING(ds->ds[ds_index].type),
+ ds->ds[ds_index].name);
+ msg->help = strdup(help);
+
+ msg->type = (ds->ds[ds_index].type == DS_TYPE_GAUGE)
+ ? IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE
+ : IO__PROMETHEUS__CLIENT__METRIC_TYPE__COUNTER;
+ msg->has_type = 1;
+
+ return msg;
+}
+
+/* metric_family_name creates a metric family's name from a data source. This is
+ * done in the same way as done by the "collectd_exporter" for best possible
+ * compatibility. In essence, the plugin, type and data source name go in the
+ * metric family name, while hostname, plugin instance and type instance go into
+ * the labels of a metric. */
+static char *metric_family_name(data_set_t const *ds, value_list_t const *vl,
+ size_t ds_index) {
+ char const *fields[5] = {"collectd"};
+ size_t fields_num = 1;
+
+ if (strcmp(vl->plugin, vl->type) != 0) {
+ fields[fields_num] = vl->plugin;
+ fields_num++;
+ }
+ fields[fields_num] = vl->type;
+ fields_num++;
+
+ if (strcmp("value", ds->ds[ds_index].name) != 0) {
+ fields[fields_num] = ds->ds[ds_index].name;
+ fields_num++;
+ }
+
+ /* Prometheus best practices:
+ * cumulative metrics should have a "total" suffix. */
+ if ((ds->ds[ds_index].type == DS_TYPE_COUNTER) ||
+ (ds->ds[ds_index].type == DS_TYPE_DERIVE)) {
+ fields[fields_num] = "total";
+ fields_num++;
+ }
+
+ char name[5 * DATA_MAX_NAME_LEN];
+ strjoin(name, sizeof(name), (char **)fields, fields_num, "_");
+ return strdup(name);
+}
+
+/* metric_family_get looks up the matching metric family, allocating it if
+ * necessary. */
+static Io__Prometheus__Client__MetricFamily *
+metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index,
+ _Bool allocate) {
+ char *name = metric_family_name(ds, vl, ds_index);
+ if (name == NULL) {
+ ERROR("write_prometheus plugin: Allocating metric family name failed.");
+ return NULL;
+ }
+
+ Io__Prometheus__Client__MetricFamily *fam = NULL;
+ if (c_avl_get(metrics, name, (void *)&fam) == 0) {
+ sfree(name);
+ assert(fam != NULL);
+ return fam;
+ }
+
+ if (!allocate) {
+ sfree(name);
+ return NULL;
+ }
+
+ fam = metric_family_create(name, ds, vl, ds_index);
+ if (fam == NULL) {
+ ERROR("write_prometheus plugin: Allocating metric family failed.");
+ sfree(name);
+ return NULL;
+ }
+
+ /* If successful, "name" is owned by "fam", i.e. don't free it here. */
+ DEBUG("write_prometheus plugin: metric family \"%s\" has been created.",
+ name);
+ name = NULL;
+
+ int status = c_avl_insert(metrics, fam->name, fam);
+ if (status != 0) {
+ ERROR("write_prometheus plugin: Adding \"%s\" failed.", name);
+ metric_family_destroy(fam);
+ return NULL;
+ }
+
+ return fam;
+}
+/* }}} */
+
+/*
+ * collectd callbacks
+ */
+static int prom_config(oconfig_item_t *ci) {
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Port", child->key) == 0) {
+ int status = cf_util_get_port_number(child);
+ if (status > 0)
+ httpd_port = (unsigned short)status;
+ } else if (strcasecmp("StalenessDelta", child->key) == 0) {
+ cf_util_get_cdtime(child, &staleness_delta);
+ } else {
+ WARNING("write_prometheus plugin: Ignoring unknown configuration option "
+ "\"%s\".",
+ child->key);
+ }
+ }
+
+ return 0;
+}
+
+static int prom_init() {
+ if (metrics == NULL) {
+ metrics = c_avl_create((void *)strcmp);
+ if (metrics == NULL) {
+ ERROR("write_prometheus plugin: c_avl_create() failed.");
+ return -1;
+ }
+ }
+
+ if (httpd == NULL) {
+ unsigned int flags = MHD_USE_THREAD_PER_CONNECTION;
+#if MHD_VERSION >= 0x00093300
+ flags |= MHD_USE_DUAL_STACK;
+#endif
+
+ httpd = MHD_start_daemon(flags, httpd_port,
+ /* MHD_AcceptPolicyCallback = */ NULL,
+ /* MHD_AcceptPolicyCallback arg = */ NULL,
+ http_handler, NULL, MHD_OPTION_END);
+ if (httpd == NULL) {
+ ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
+ return -1;
+ }
+ DEBUG("write_prometheus plugin: Successfully started microhttpd %s",
+ MHD_get_version());
+ }
+
+ return 0;
+}
+
+static int prom_write(data_set_t const *ds, value_list_t const *vl,
+ __attribute__((unused)) user_data_t *ud) {
+ pthread_mutex_lock(&metrics_lock);
+
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ Io__Prometheus__Client__MetricFamily *fam =
+ metric_family_get(ds, vl, i, /* allocate = */ 1);
+ if (fam == NULL)
+ continue;
+
+ int status = metric_family_update(fam, ds, vl, i);
+ if (status != 0) {
+ ERROR("write_prometheus plugin: Updating metric \"%s\" failed with "
+ "status %d",
+ fam->name, status);
+ continue;
+ }
+ }
+
+ pthread_mutex_unlock(&metrics_lock);
+ return 0;
+}
+
+static int prom_missing(value_list_t const *vl,
+ __attribute__((unused)) user_data_t *ud) {
+ data_set_t const *ds = plugin_get_ds(vl->type);
+ if (ds == NULL)
+ return ENOENT;
+
+ pthread_mutex_lock(&metrics_lock);
+
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ Io__Prometheus__Client__MetricFamily *fam =
+ metric_family_get(ds, vl, i, /* allocate = */ 0);
+ if (fam == NULL)
+ continue;
+
+ int status = metric_family_delete_metric(fam, vl);
+ if (status != 0) {
+ ERROR("write_prometheus plugin: Deleting a metric in family \"%s\" "
+ "failed with status %d",
+ fam->name, status);
+
+ continue;
+ }
+
+ if (fam->n_metric == 0) {
+ int status = c_avl_remove(metrics, fam->name, NULL, NULL);
+ if (status != 0) {
+ ERROR("write_prometheus plugin: Deleting metric family \"%s\" failed "
+ "with status %d",
+ fam->name, status);
+ continue;
+ }
+ metric_family_destroy(fam);
+ }
+ }
+
+ pthread_mutex_unlock(&metrics_lock);
+ return 0;
+}
+
+static int prom_shutdown() {
+ if (httpd != NULL) {
+ MHD_stop_daemon(httpd);
+ httpd = NULL;
+ }
+
+ pthread_mutex_lock(&metrics_lock);
+ if (metrics != NULL) {
+ char *name;
+ Io__Prometheus__Client__MetricFamily *fam;
+ while (c_avl_pick(metrics, (void *)&name, (void *)&fam) == 0) {
+ assert(name == fam->name);
+ name = NULL;
+
+ metric_family_destroy(fam);
+ }
+ c_avl_destroy(metrics);
+ metrics = NULL;
+ }
+ pthread_mutex_unlock(&metrics_lock);
+
+ return 0;
+}
+
+void module_register() {
+ plugin_register_complex_config("write_prometheus", prom_config);
+ plugin_register_init("write_prometheus", prom_init);
+ plugin_register_write("write_prometheus", prom_write,
+ /* user data = */ NULL);
+ plugin_register_missing("write_prometheus", prom_missing,
+ /* user data = */ NULL);
+ plugin_register_shutdown("write_prometheus", prom_shutdown);
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
ssnprintf(cb_name, sizeof(cb_name), "write_redis/%s", node->name);
- user_data_t ud = {.data = node, .free_func = wr_config_free};
-
- status = plugin_register_write(cb_name, wr_write, &ud);
+ status = plugin_register_write(
+ cb_name, wr_write, &(user_data_t){
+ .data = node, .free_func = wr_config_free,
+ });
}
if (status != 0)
return 0;
} /* static int xencpu_shutdown */
-static void submit_value(int cpu_num, gauge_t percent) {
- value_t values[1];
+static void submit_value(int cpu_num, gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = percent;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "xencpu", sizeof(vl.plugin));
sstrncpy(vl.type, "percent", sizeof(vl.type));
sstrncpy(vl.type_instance, "load", sizeof(vl.type_instance));
int status;
for (int cpu = 0; cpu < nr_cpus; cpu++) {
gauge_t rate = NAN;
- value_t value = {.derive = cpu_info[cpu].idletime};
- status = value_to_rate(&rate, value, DS_TYPE_DERIVE, now, &cpu_states[cpu]);
+ status = value_to_rate(&rate, (value_t){.derive = cpu_info[cpu].idletime},
+ DS_TYPE_DERIVE, now, &cpu_states[cpu]);
if (status == 0) {
submit_value(cpu, 100 - rate / 10000000);
}
static gint xmms_session;
static void cxmms_submit(const char *type, gauge_t value) {
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "xmms", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
#endif
static void za_submit(const char *type, const char *type_instance,
- value_t *values, int values_len) {
+ value_t *values, size_t values_len) {
value_list_t vl = VALUE_LIST_INIT;
vl.values = values;
vl.values_len = values_len;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "zfs_arc", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
static void za_submit_gauge(const char *type, const char *type_instance,
gauge_t value) {
- value_t vv;
-
- vv.gauge = value;
- za_submit(type, type_instance, &vv, 1);
+ za_submit(type, type_instance, &(value_t){.gauge = value}, 1);
}
static int za_read_derive(kstat_t *ksp, const char *kstat_value,
const char *type, const char *type_instance) {
- long long tmp;
- value_t v;
-
- tmp = get_zfs_value(ksp, (char *)kstat_value);
+ long long tmp = get_zfs_value(ksp, (char *)kstat_value);
if (tmp == -1LL) {
WARNING("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
return (-1);
}
- v.derive = (derive_t)tmp;
- za_submit(type, type_instance, /* values = */ &v, /* values_num = */ 1);
+ za_submit(type, type_instance, &(value_t){.derive = (derive_t)tmp},
+ /* values_num = */ 1);
return (0);
}
static int za_read_gauge(kstat_t *ksp, const char *kstat_value,
const char *type, const char *type_instance) {
- long long tmp;
- value_t v;
-
- tmp = get_zfs_value(ksp, (char *)kstat_value);
+ long long tmp = get_zfs_value(ksp, (char *)kstat_value);
if (tmp == -1LL) {
WARNING("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
return (-1);
}
- v.gauge = (gauge_t)tmp;
- za_submit(type, type_instance, /* values = */ &v, /* values_num = */ 1);
+ za_submit(type, type_instance, &(value_t){.gauge = (gauge_t)tmp},
+ /* values_num = */ 1);
return (0);
}
static int za_read(void) {
gauge_t arc_hits, arc_misses, l2_hits, l2_misses;
- value_t l2_io[2];
kstat_t *ksp = NULL;
#if defined(KERNEL_LINUX)
za_submit_ratio("L2", l2_hits, l2_misses);
/* I/O */
- l2_io[0].derive = get_zfs_value(ksp, "l2_read_bytes");
- l2_io[1].derive = get_zfs_value(ksp, "l2_write_bytes");
-
- za_submit("io_octets", "L2", l2_io, /* num values = */ 2);
+ value_t l2_io[] = {
+ {.derive = (derive_t)get_zfs_value(ksp, "l2_read_bytes")},
+ {.derive = (derive_t)get_zfs_value(ksp, "l2_write_bytes")},
+ };
+ za_submit("io_octets", "L2", l2_io, STATIC_ARRAY_SIZE(l2_io));
#if defined(KERNEL_LINUX)
free_zfs_values(ksp);
vl.values = values;
vl.values_len = 1; /*STATIC_ARRAY_SIZE (values);*/
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "zone", sizeof(vl.plugin));
sstrncpy(vl.type, "percent", sizeof(vl.type));
sstrncpy(vl.type_instance, zone, sizeof(vl.type_instance));
}
static void zookeeper_submit_gauge(const char *type, const char *type_inst,
- gauge_t val) {
- value_t values[1];
+ gauge_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = val;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "zookeeper", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_inst != NULL)
} /* zookeeper_submit_gauge */
static void zookeeper_submit_derive(const char *type, const char *type_inst,
- derive_t val) {
- value_t values[1];
+ derive_t value) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = val;
-
- vl.values = values;
+ vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, "zookeeper", sizeof(vl.plugin));
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_inst != NULL)
#!/bin/sh
-DEFAULT_VERSION="5.6.2.git"
+DEFAULT_VERSION="5.7.1.git"
if [ -d .git ]; then
VERSION="`git describe --dirty=+ --abbrev=7 2> /dev/null | grep collectd | sed -e 's/^collectd-//' -e 's/-/./g'`"