apcups plugin by:
Anthony Gialluca <tonyabg at charter.net>
-cpufreq module by:
+cpufreq and multimeter module by:
Peter Holik <peter at holik.at>
hddtemp module by:
+pending, Version 3.11.0
+ * collectd: The new command line option `-P' makes it easier for
+ distributors to change the location of PID-files.
+ * collectd: The daemon shuts down faster now which makes it easier to
+ write init.d-scripts for it.
+ * apache plugin: Increase the buffersize to 16k, because the 4k buffer
+ caused problems every now and then.
+ * df plugin: New config options allow to ignore certain mountpoints,
+ filesystem types or devices.
+ * dns plugin: The new dns plugin uses `libpcap' to capture DNS traffic
+ and interprets it. It collects traffic as well as qtype, opcode and
+ rcode counts.
+ * email plugin: Sebastian Harl has contributed this plugin which
+ counts received mails in categories (e. g. ham, spam, virus), spam
+ score (as given by SpamAssassin) and check types.
+ * processes plugin: Collect detailed statistics for configured
+ processes, that's process and thread count, CPU usage, resident
+ segment size and pagefaults.
+ * multimeter plugin: Peter Holik contributed a new plugin which
+ queries multimeters.
+ * sensors plugin: Lubos Stanek has put much effort into improving this
+ plugin, including `extended naming', collection of voltage values
+ and the possibility to ignore certain values.
+
2006-12-21, Version 3.10.4
* Max Kellermann has identified a bug in the server routine: When
opening a socket fails the daemon will (re)try opening the socket in
- Disk utilization
(Sectors read/written, number of read/write actions, time spent doing IO)
+ - DNS traffic
+ (query types, response codes, opcodes and traffic)
+
+ - Email statistics
+ (count, traffic, spam scores and checks)
+
- Harddisk temperatures
(Uhm, yeah, temperature of harddisks that is ;)
(Number of running, sleeping, zombie, ... processes)
- Sensors
- (System temperatured and fan rotation speeds)
+ (lm_sensors voltages, temperatures and fan rotation speeds)
- Serial
(RX and TX of serial interfaces)
%attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so*
%attr(0444,root,root) %{_libdir}/%{name}/load.so*
%attr(0444,root,root) %{_libdir}/%{name}/memory.so*
+%attr(0444,root,root) %{_libdir}/%{name}/multimeter.so*
%attr(0444,root,root) %{_libdir}/%{name}/nfs.so*
%attr(0444,root,root) %{_libdir}/%{name}/ntpd.so*
%attr(0444,root,root) %{_libdir}/%{name}/ping.so*
AC_LIBTOOL_DLOPEN
AC_PROG_LIBTOOL
#AC_PROG_RANLIB
-AC_CONFIG_SUBDIRS(libltdl src/libconfig)
+AC_CONFIG_SUBDIRS(libltdl)
#
# Checks for header files.
# For load module
AC_CHECK_HEADERS(sys/loadavg.h)
+# For the processes plugin
+AC_CHECK_HEADERS(linux/config.h)
+
# For the swap module
AC_CHECK_HEADERS(sys/swap.h)
# For the swap plugin, FreeBSD
AC_CHECK_HEADERS(kvm.h)
+# For the email plugin
+AC_CHECK_HEADERS(linux/un.h, [], [],
+[
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+])
+AC_CHECK_HEADERS(sys/un.h)
+AC_CHECK_HEADERS(grp.h)
+
# For debugging interface (variable number of arguments)
AC_CHECK_HEADERS(stdarg.h)
+# Regular expressions for the ignorelist.
+AC_CHECK_HEADERS(regex.h)
+
+# For the dns plugin
+AC_CHECK_HEADERS(arpa/nameser.h arpa/nameser_compat.h)
+
+AC_CHECK_HEADERS(net/if_arp.h)
+AC_CHECK_HEADERS(net/if_ppp.h)
+AC_CHECK_HEADERS(netinet/if_ether.h)
+AC_CHECK_HEADERS(netinet/udp.h)
+
dnl Checking for libraries
AC_CHECK_LIB(m, ext)
AC_CHECK_FUNCS(nanosleep, [], AC_CHECK_LIB(rt, nanosleep, [nanosleep_needs_rt="yes"], AC_MSG_ERROR(cannot find nanosleep)))
AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
+# Regular expressions for the ignorelist.
+AC_CHECK_FUNCS(regcomp regerror regexec regfree)
+
# For cpu module
AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
[Wether or not to use rrdtool library])
AM_CONDITIONAL(BUILD_WITH_RRDTOOL, test "x$with_rrdtool" = "xyes")
-#AC_ARG_WITH(pth, [AS_HELP_STRING([--with-pth=@<:@=PREFIX@:>@], [Path to pth (experimental).]),
-#[ if test "x$withval" != "xno" && test "x$withval" != "xyes"
-# then
-# LDFLAGS="$LDFLAGS -L$withval/lib"
-# CPPFLAGS="$CPPFLAGS -I$withval/include"
-# with_pth="yes"
-# fi
-#], [with_pth="no"])
-#if test "x$with_pth" = "xyes"
-#then
-# AC_CHECK_LIB(pth, pth_init,, [with_pth="no (libpth not found)"], [])
-#fi
-#if test "x$with_pth" = "xyes"
-#then
-# AC_CHECK_HEADERS(pth.h,, [with_pth="no (pth.h not found)"])
-#fi
-#if test "x$with_pth" = "xyes"
-#then
-# collect_pth=1
-#else
-# collect_pth=0
-#fi
-#AC_DEFINE_UNQUOTED(COLLECT_PTH, [$collect_pth],
-# [Wether or not to use pth (portable threads) library])
-#AM_CONDITIONAL(BUILD_WITH_PTH, test "x$with_pth" = "xyes")
+AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])],
+[ if test "x$withval" != "xno" -a "x$withval" != "xyes"
+ then
+ LDFLAGS="$LDFLAGS -L$withval/lib"
+ CPPFLAGS="$CPPFLAGS -I$withval/include"
+ with_libpthread="yes"
+ else
+ if test "x$withval" = "xno"
+ then
+ with_libpthread="no (disabled)"
+ fi
+ fi
+], [with_libpthread="yes"])
+if test "x$with_libpthread" = "xyes"
+then
+ AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], [])
+fi
+if test "x$with_libpthread" = "xyes"
+then
+ AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
+fi
+if test "x$with_libpthread" = "xyes"
+then
+ collect_pthread=1
+else
+ collect_pthread=0
+fi
+AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
+ [Wether or not to use pthread (POSIX threads) library])
+AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
if test "$ac_system" = "Solaris"
then
with_liboping="yes (shipped version)"
fi
+AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])],
+[
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ then
+ LDFLAGS="$LDFLAGS -L$withval/lib"
+ CPPFLAGS="$CPPFLAGS -I$withval/include"
+ with_libpcap="yes"
+ fi
+],
+[
+ with_libpcap="yes"
+])
+if test "x$with_libpcap" = "xyes"
+then
+ AC_CHECK_LIB(pcap, pcap_open_live,
+ [
+ AC_DEFINE(HAVE_LIBPCAP, 1, [Define to 1 if you have the pcap library (-lpcap).])
+ ], [with_libpcap="no (libpcap not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+ AC_CHECK_HEADERS(pcap.h,
+ [
+ AC_DEFINE(HAVE_PCAP_H, 1, [Define to 1 if you have the <pcap.h> header file.])
+ ], [with_libpcap="no (pcap.h not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+ collect_libpcap=1
+else
+ collect_libpcap=0
+fi
+AC_DEFINE_UNQUOTED(COLLECT_LIBPCAP, [$collect_libpcap],
+ [Wether or not to use the pcap library])
+AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
+
# Define `step' and `hearbeat' values..
declare -i collectd_step=10
declare -i collectd_heartbeat=25
AC_DEFINE_UNQUOTED(COLLECTD_HEARTBEAT, "$collectd_heartbeat", [Interval in which plugins are queried.])
fi
-#
# Check for enabled/disabled features
#
AC_COLLECTD([cpufreq], [disable], [module], [system cpu frequency statistics])
AC_COLLECTD([disk], [disable], [module], [disk/partition statistics])
AC_COLLECTD([df], [disable], [module], [df statistics])
+AC_COLLECTD([dns], [disable], [module], [dns statistics])
+AC_COLLECTD([email], [disable], [module], [email statistics])
AC_COLLECTD([quota], [enable], [module], [quota statistics (experimental)])
AC_COLLECTD([hddtemp], [disable], [module], [hdd temperature statistics])
AC_COLLECTD([load], [disable], [module], [system load statistics])
AC_COLLECTD([memory], [disable], [module], [memory statistics])
+AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics])
AC_COLLECTD([mysql], [disable], [module], [mysql statistics])
AC_COLLECTD([nfs], [disable], [module], [nfs statistics])
AC_COLLECTD([ntpd], [disable], [module], [nfs statistics])
libstatgrab . . . . $with_libstatgrab
libkstat . . . . . $with_kstat
libmysql . . . . . $with_libmysql
+ libpcap . . . . . . $with_libpcap
+ libpthread . . . . $with_libpthread
Features:
debug . . . . . . . $enable_debug
cpufreq . . . . . . $enable_cpufreq
df . . . . . . . . $enable_df
disk . . . . . . . $enable_disk
+ dns . . . . . . . . $enable_dns
+ email . . . . . . . $enable_email
hddtemp . . . . . . $enable_hddtemp
load . . . . . . . $enable_load
memory . . . . . . $enable_memory
+ multimeter . . . . $enable_multimeter
mysql . . . . . . . $enable_mysql
nfs . . . . . . . . $enable_nfs
ntpd . . . . . . . $enable_ntpd
--- /dev/null
+#!/usr/bin/perl
+
+=head1 NAME
+
+Collectd - plugin for filling collectd with stats
+
+=head1 INSTALLATION
+
+Just copy Collectd.pm into your SpamAssassin Plugin path
+(e.g /usr/share/perl5/Mail/SpamAssassin/Plugin/) and
+add a loadplugin call into your init.pre file.
+
+=head1 SYNOPSIS
+
+ loadplugin Mail::SpamAssassin::Plugin::Collectd
+
+=head1 USER SETTINGS
+
+=over 4
+
+=item collectd_socket [ socket path ] (default: /tmp/.collectd-email)
+
+Where the collectd socket is
+
+=cut
+
+=item collectd_buffersize [ size ] (default: 256)
+
+the email plugin uses a fixed buffer, if a line exceeds this size
+it has to be continued in another line. (This is of course handled internally)
+If you have changed this setting please get it in sync with the SA Plugin
+config.
+
+=cut
+
+=item collectd_timeout [ sec ] (default: 2)
+
+if sending data to to collectd takes too long the connection will be aborted.
+
+=cut
+
+=item collectd_retries [ tries ] (default: 3)
+
+the collectd plugin uses a tread pool, if this is empty the connection fails,
+the SA Plugin then tries to reconnect. With this variable you can indicate how
+often it should try.
+
+=cut
+
+=head1 DESCRIPTION
+
+This modules uses the email plugin of collectd from Sebastian Harl to
+collect statistical informations in rrd files to create some nice looking
+graphs with rrdtool. They communicate over a unix socket that the collectd
+plugin creates. The generated graphs will be placed in /var/lib/collectd/email
+
+=head1 AUTHOR
+
+Alexander Wirt <formorer@formorer.de>
+
+=head1 COPYRIGHT
+
+ Copyright 2006 Alexander Wirt <formorer@formorer.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the the terms of either:
+
+ a) the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+
+ or
+
+ b) the GPL (http://www.gnu.org/copyleft/gpl.html)
+
+ use whatever you like more.
+
+=cut
+
+package Mail::SpamAssassin::Plugin::Collectd;
+
+use Mail::SpamAssassin::Plugin;
+use Mail::SpamAssassin::Logger;
+use strict;
+use bytes;
+use warnings;
+use Time::HiRes qw(usleep);
+use IO::Socket;
+
+use vars qw(@ISA);
+@ISA = qw(Mail::SpamAssassin::Plugin);
+
+sub new {
+ my ($class, $mailsa) = @_;
+
+ # the usual perlobj boilerplate to create a subclass object
+ $class = ref($class) || $class;
+ my $self = $class->SUPER::new($mailsa);
+ bless ($self, $class);
+
+ # register our config options
+ $self->set_config($mailsa->{conf});
+
+ # and return the new plugin object
+ return $self;
+}
+
+sub set_config {
+ my ($self, $conf) = @_;
+ my @cmds = ();
+
+ push (@cmds, {
+ setting => 'collectd_buffersize',
+ default => 256,
+ type =>
+ $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
+ });
+
+ push (@cmds, {
+ setting => 'collectd_socket',
+ default => '/tmp/.collectd-email',
+ type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
+ });
+
+ push (@cmds, {
+ setting => 'collectd_timeout',
+ default => 2,
+ type =>
+ $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
+ });
+
+ push (@cmds, {
+ setting => 'collectd_retries',
+ default => 3,
+ type =>
+ $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
+ });
+
+
+ $conf->{parser}->register_commands(\@cmds);
+}
+
+sub check_end {
+ my ($self, $params) = @_;
+ my $message_status = $params->{permsgstatus};
+ #create new connection to our socket
+ eval {
+ local $SIG{ALRM} = sub { die "Sending to collectd timed out.\n" }; # NB: \n required
+
+ #generate a timeout
+ alarm $self->{main}->{conf}->{collectd_timeout};
+
+ my $sock;
+ #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});
+ #sleep a random value between 0 and 50 microsecs to try for a new
+ #thread
+ usleep(int(rand(50)));
+ }
+
+ die("could not connect to " .
+ $self->{main}->{conf}->{collectd_socket} . ": $! - collectd plugin disabled") unless $sock;
+
+ $sock->autoflush(1);
+
+ my $score = $message_status->{score};
+ #get the size of the message
+ my $body = $message_status->{msg}->{pristine_body};
+
+ my $len = length($body);
+
+ if ($message_status->{score} >= $self->{main}->{conf}->{required_score} ) {
+ #hey we have spam
+ print $sock "e:spam:$len\n";
+ } else {
+ print $sock "e:ham:$len\n";
+ }
+ print $sock "s:$score\n";
+ my @tmp_array;
+ my @tests = @{$message_status->{test_names_hit}};
+
+ my $buffersize = $self->{main}->{conf}->{collectd_buffersize};
+ dbg("collectd: buffersize: $buffersize");
+
+ while (scalar(@tests) > 0) {
+ push (@tmp_array, pop(@tests));
+ if (length(join(',', @tmp_array) . '\n') > $buffersize) {
+ push (@tests, pop(@tmp_array));
+ if (length(join(',', @tmp_array) . '\n') > $buffersize or scalar(@tmp_array) == 0) {
+ dbg("collectd: this shouldn't happen. Do you have tests"
+ ." with names that have more than ~ $buffersize Bytes?");
+ return 1;
+ } else {
+ dbg ( "collectd: c:" . join(',', @tmp_array) . "\n" );
+ print $sock "c:" . join(',', @tmp_array) . "\n";
+ #clean the array
+ @tmp_array = ();
+ }
+ } elsif ( scalar(@tests) == 0 ) {
+ dbg ( "collectd: c:" . join(',', @tmp_array) . '\n' );
+ print $sock "c:" . join(',', @tmp_array) . "\n";
+ }
+ }
+ close($sock);
+ alarm 0;
+ };
+ if ($@) {
+ my $message = $@;
+ chomp($message);
+ info("collectd: $message");
+ return -1;
+ }
+}
+
+1;
+
+# vim: syntax=perl sw=4 ts=4 noet shiftround
--- /dev/null
+collectd_buffersize 256
+collectd_socket /tmp/.collectd-email
+collectd_timeout 2
+collectd_retries 3
+
'GPRINT:total_max_ms:MAX:%5.1lf%s Max,',
'GPRINT:total_avg_ms:LAST:%5.1lf%s Last'
],
+ dns_traffic => ['DEF:rsp_min_raw={file}:responses:MIN',
+ 'DEF:rsp_avg_raw={file}:responses:AVERAGE',
+ 'DEF:rsp_max_raw={file}:responses:MAX',
+ 'DEF:qry_min_raw={file}:queries:MIN',
+ 'DEF:qry_avg_raw={file}:queries:AVERAGE',
+ 'DEF:qry_max_raw={file}:queries:MAX',
+ 'CDEF:rsp_min=rsp_min_raw,8,*',
+ 'CDEF:rsp_avg=rsp_avg_raw,8,*',
+ 'CDEF:rsp_max=rsp_max_raw,8,*',
+ 'CDEF:qry_min=qry_min_raw,8,*',
+ 'CDEF:qry_avg=qry_avg_raw,8,*',
+ 'CDEF:qry_max=qry_max_raw,8,*',
+ 'CDEF:overlap=rsp_avg,qry_avg,GT,qry_avg,rsp_avg,IF',
+ 'CDEF:mytime=rsp_avg_raw,TIME,TIME,IF',
+ 'CDEF:sample_len_raw=mytime,PREV(mytime),-',
+ 'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
+ 'CDEF:rsp_avg_sample=rsp_avg_raw,UN,0,rsp_avg_raw,IF,sample_len,*',
+ 'CDEF:rsp_avg_sum=PREV,UN,0,PREV,IF,rsp_avg_sample,+',
+ 'CDEF:qry_avg_sample=qry_avg_raw,UN,0,qry_avg_raw,IF,sample_len,*',
+ 'CDEF:qry_avg_sum=PREV,UN,0,PREV,IF,qry_avg_sample,+',
+ "AREA:rsp_avg#$HalfGreen",
+ "AREA:qry_avg#$HalfBlue",
+ "AREA:overlap#$HalfBlueGreen",
+ "LINE1:rsp_avg#$FullGreen:Responses",
+ 'GPRINT:rsp_avg:AVERAGE:%5.1lf%s Avg,',
+ 'GPRINT:rsp_max:MAX:%5.1lf%s Max,',
+ 'GPRINT:rsp_avg:LAST:%5.1lf%s Last',
+ 'GPRINT:rsp_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
+ "LINE1:qry_avg#$FullBlue:Queries ",
+ #'GPRINT:qry_min:MIN:%5.1lf %s Min,',
+ 'GPRINT:qry_avg:AVERAGE:%5.1lf%s Avg,',
+ 'GPRINT:qry_max:MAX:%5.1lf%s Max,',
+ 'GPRINT:qry_avg:LAST:%5.1lf%s Last',
+ 'GPRINT:qry_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
+ ],
+ email => [
+ 'DEF:avg={file}:count:AVERAGE',
+ 'DEF:min={file}:count:MIN',
+ 'DEF:max={file}:count:MAX',
+ "AREA:max#$HalfMagenta",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullMagenta:Count ",
+ 'GPRINT:min:MIN:%4.1lf Min,',
+ 'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+ 'GPRINT:max:MAX:%4.1lf Max,',
+ 'GPRINT:avg:LAST:%4.1lf Last\l'
+ ],
+ email_size => [
+ 'DEF:avg={file}:size:AVERAGE',
+ 'DEF:min={file}:size:MIN',
+ 'DEF:max={file}:size:MAX',
+ "AREA:max#$HalfMagenta",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullMagenta:Count ",
+ 'GPRINT:min:MIN:%4.1lf Min,',
+ 'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+ 'GPRINT:max:MAX:%4.1lf Max,',
+ 'GPRINT:avg:LAST:%4.1lf Last\l'
+ ],
+ spam_score => [
+ 'DEF:avg={file}:score:AVERAGE',
+ 'DEF:min={file}:score:MIN',
+ 'DEF:max={file}:score:MAX',
+ "AREA:max#$HalfMagenta",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullMagenta:Count ",
+ 'GPRINT:min:MIN:%4.1lf Min,',
+ 'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+ 'GPRINT:max:MAX:%4.1lf Max,',
+ 'GPRINT:avg:LAST:%4.1lf Last\l'
+ ],
+ spam_check => [
+ 'DEF:avg={file}:hits:AVERAGE',
+ 'DEF:min={file}:hits:MIN',
+ 'DEF:max={file}:hits:MAX',
+ "AREA:max#$HalfMagenta",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullMagenta:Count ",
+ 'GPRINT:min:MIN:%4.1lf Min,',
+ 'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+ 'GPRINT:max:MAX:%4.1lf Max,',
+ 'GPRINT:avg:LAST:%4.1lf Last\l'
+ ],
fanspeed => [
'DEF:temp_avg={file}:value:AVERAGE',
'DEF:temp_min={file}:value:MIN',
'DEF:temp_max={file}:value:MAX',
- "AREA:temp_max#$HalfBlue",
+ "AREA:temp_max#$HalfMagenta",
"AREA:temp_min#$Canvas",
- "LINE1:temp_avg#$FullBlue:RPM",
+ "LINE1:temp_avg#$FullMagenta:RPM",
'GPRINT:temp_min:MIN:%4.1lf Min,',
'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
'GPRINT:temp_max:MAX:%4.1lf Max,',
'DEF:temp_avg={file}:value:AVERAGE',
'DEF:temp_min={file}:value:MIN',
'DEF:temp_max={file}:value:MAX',
- "AREA:temp_max#$HalfBlue",
+ "AREA:temp_max#$HalfRed",
"AREA:temp_min#$Canvas",
- "LINE1:temp_avg#$FullBlue:Temperature",
+ "LINE1:temp_avg#$FullRed:Temperature",
'GPRINT:temp_min:MIN:%4.1lf Min,',
'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
'GPRINT:temp_max:MAX:%4.1lf Max,',
'GPRINT:tx_avg:AVERAGE:%5.1lf%s Avg,',
'GPRINT:tx_max:MAX:%5.1lf%s Max,',
'GPRINT:tx_avg:LAST:%5.1lf%s Last',
- 'GPRINT:tx_avg_sum:LAST:(ca. %.0lf Total)\l',
+ 'GPRINT:tx_avg_sum:LAST:(ca. %4.0lf%s Total)\l',
"LINE1:rx_avg#$FullBlue:RX",
#'GPRINT:rx_min:MIN:%5.1lf %s Min,',
'GPRINT:rx_avg:AVERAGE:%5.1lf%s Avg,',
'GPRINT:rx_max:MAX:%5.1lf%s Max,',
'GPRINT:rx_avg:LAST:%5.1lf%s Last',
- 'GPRINT:rx_avg_sum:LAST:(ca. %.0lf Total)\l'
+ 'GPRINT:rx_avg_sum:LAST:(ca. %4.0lf%s Total)\l'
],
load => ['DEF:s_avg={file}:shortterm:AVERAGE',
'DEF:s_min={file}:shortterm:MIN',
'GPRINT:read_avg:AVERAGE:%5.1lf Avg,',
'GPRINT:read_avg:LAST:%5.1lf Last\l'
],
+ opcode => [
+ 'DEF:avg={file}:value:AVERAGE',
+ 'DEF:min={file}:value:MIN',
+ 'DEF:max={file}:value:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Queries/s",
+ 'GPRINT:min:MIN:%9.3lf Min,',
+ 'GPRINT:avg:AVERAGE:%9.3lf Average,',
+ 'GPRINT:max:MAX:%9.3lf Max,',
+ 'GPRINT:avg:LAST:%9.3lf Last\l'
+ ],
partition => [
"DEF:rbyte_avg={file}:rbytes:AVERAGE",
"DEF:rbyte_min={file}:rbytes:MIN",
'GPRINT:sleeping_max:MAX:%5.1lf Max,',
'GPRINT:sleeping_avg:LAST:%5.1lf Last\l'
],
+ ps_rss => [
+ 'DEF:avg={file}:byte:AVERAGE',
+ 'DEF:min={file}:byte:MIN',
+ 'DEF:max={file}:byte:MAX',
+ "AREA:avg#$HalfBlue",
+ "LINE1:avg#$FullBlue:RSS",
+ 'GPRINT:min:MIN:%5.1lf%s Min,',
+ 'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
+ 'GPRINT:max:MAX:%5.1lf%s Max,',
+ 'GPRINT:avg:LAST:%5.1lf%s Last\l'
+ ],
+ ps_cputime => [
+ 'DEF:user_avg_raw={file}:user:AVERAGE',
+ 'DEF:user_min_raw={file}:user:MIN',
+ 'DEF:user_max_raw={file}:user:MAX',
+ 'DEF:syst_avg_raw={file}:syst:AVERAGE',
+ 'DEF:syst_min_raw={file}:syst:MIN',
+ 'DEF:syst_max_raw={file}:syst:MAX',
+ 'CDEF:user_avg=user_avg_raw,1000000,/',
+ 'CDEF:user_min=user_min_raw,1000000,/',
+ 'CDEF:user_max=user_max_raw,1000000,/',
+ 'CDEF:syst_avg=syst_avg_raw,1000000,/',
+ 'CDEF:syst_min=syst_min_raw,1000000,/',
+ 'CDEF:syst_max=syst_max_raw,1000000,/',
+ 'CDEF:user_syst=syst_avg,UN,0,syst_avg,IF,user_avg,+',
+ "AREA:user_syst#$HalfBlue",
+ "AREA:syst_avg#$HalfRed",
+ "LINE1:user_syst#$FullBlue:User ",
+ 'GPRINT:user_min:MIN:%5.1lf%s Min,',
+ 'GPRINT:user_avg:AVERAGE:%5.1lf%s Avg,',
+ 'GPRINT:user_max:MAX:%5.1lf%s Max,',
+ 'GPRINT:user_avg:LAST:%5.1lf%s Last\l',
+ "LINE1:syst_avg#$FullRed:System",
+ 'GPRINT:syst_min:MIN:%5.1lf%s Min,',
+ 'GPRINT:syst_avg:AVERAGE:%5.1lf%s Avg,',
+ 'GPRINT:syst_max:MAX:%5.1lf%s Max,',
+ 'GPRINT:syst_avg:LAST:%5.1lf%s Last\l'
+ ],
+ ps_count => [
+ 'DEF:procs_avg={file}:processes:AVERAGE',
+ 'DEF:procs_min={file}:processes:MIN',
+ 'DEF:procs_max={file}:processes:MAX',
+ 'DEF:thrds_avg={file}:threads:AVERAGE',
+ 'DEF:thrds_min={file}:threads:MIN',
+ 'DEF:thrds_max={file}:threads:MAX',
+ "AREA:thrds_avg#$HalfBlue",
+ "AREA:procs_avg#$HalfRed",
+ "LINE1:thrds_avg#$FullBlue:Threads ",
+ 'GPRINT:thrds_min:MIN:%5.1lf Min,',
+ 'GPRINT:thrds_avg:AVERAGE:%5.1lf Avg,',
+ 'GPRINT:thrds_max:MAX:%5.1lf Max,',
+ 'GPRINT:thrds_avg:LAST:%5.1lf Last\l',
+ "LINE1:procs_avg#$FullRed:Processes",
+ 'GPRINT:procs_min:MIN:%5.1lf Min,',
+ 'GPRINT:procs_avg:AVERAGE:%5.1lf Avg,',
+ 'GPRINT:procs_max:MAX:%5.1lf Max,',
+ 'GPRINT:procs_avg:LAST:%5.1lf Last\l'
+ ],
+ ps_pagefaults => [
+ 'DEF:minor_avg={file}:minflt:AVERAGE',
+ 'DEF:minor_min={file}:minflt:MIN',
+ 'DEF:minor_max={file}:minflt:MAX',
+ 'DEF:major_avg={file}:majflt:AVERAGE',
+ 'DEF:major_min={file}:majflt:MIN',
+ 'DEF:major_max={file}:majflt:MAX',
+ 'CDEF:minor_major=major_avg,UN,0,major_avg,IF,minor_avg,+',
+ "AREA:minor_major#$HalfBlue",
+ "AREA:major_avg#$HalfRed",
+ "LINE1:minor_major#$FullBlue:Minor",
+ 'GPRINT:minor_min:MIN:%5.1lf%s Min,',
+ 'GPRINT:minor_avg:AVERAGE:%5.1lf%s Avg,',
+ 'GPRINT:minor_max:MAX:%5.1lf%s Max,',
+ 'GPRINT:minor_avg:LAST:%5.1lf%s Last\l',
+ "LINE1:major_avg#$FullRed:Major",
+ 'GPRINT:major_min:MIN:%5.1lf%s Min,',
+ 'GPRINT:major_avg:AVERAGE:%5.1lf%s Avg,',
+ 'GPRINT:major_max:MAX:%5.1lf%s Max,',
+ 'GPRINT:major_avg:LAST:%5.1lf%s Last\l'
+ ],
+ qtype => [
+ 'DEF:avg={file}:value:AVERAGE',
+ 'DEF:min={file}:value:MIN',
+ 'DEF:max={file}:value:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Queries/s",
+ 'GPRINT:min:MIN:%9.3lf Min,',
+ 'GPRINT:avg:AVERAGE:%9.3lf Average,',
+ 'GPRINT:max:MAX:%9.3lf Max,',
+ 'GPRINT:avg:LAST:%9.3lf Last\l'
+ ],
+ rcode => [
+ 'DEF:avg={file}:value:AVERAGE',
+ 'DEF:min={file}:value:MIN',
+ 'DEF:max={file}:value:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Queries/s",
+ 'GPRINT:min:MIN:%9.3lf Min,',
+ 'GPRINT:avg:AVERAGE:%9.3lf Average,',
+ 'GPRINT:max:MAX:%9.3lf Max,',
+ 'GPRINT:avg:LAST:%9.3lf Last\l'
+ ],
swap => [
'DEF:used_avg={file}:used:AVERAGE',
'DEF:used_min={file}:used:MIN',
'DEF:temp_avg={file}:value:AVERAGE',
'DEF:temp_min={file}:value:MIN',
'DEF:temp_max={file}:value:MAX',
- "AREA:temp_max#$HalfBlue",
+ "AREA:temp_max#$HalfRed",
"AREA:temp_min#$Canvas",
- "LINE1:temp_avg#$FullBlue:Value",
+ "LINE1:temp_avg#$FullRed:Temperature",
'GPRINT:temp_min:MIN:%4.1lf Min,',
'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
'GPRINT:temp_max:MAX:%4.1lf Max,',
'GPRINT:cpufreq_max:MAX:%5.1lf%s Max,',
'GPRINT:cpufreq_avg:LAST:%5.1lf%s Last\l'
],
+ multimeter => [
+ 'DEF:multimeter_avg={file}:value:AVERAGE',
+ 'DEF:multimeter_min={file}:value:MIN',
+ 'DEF:multimeter_max={file}:value:MAX',
+ "AREA:multimeter_max#$HalfBlue",
+ "AREA:multimeter_min#$Canvas",
+ "LINE1:multimeter_avg#$FullBlue:Multimeter",
+ 'GPRINT:multimeter_min:MIN:%4.1lf Min,',
+ 'GPRINT:multimeter_avg:AVERAGE:%4.1lf Average,',
+ 'GPRINT:multimeter_max:MAX:%4.1lf Max,',
+ 'GPRINT:multimeter_avg:LAST:%4.1lf Last\l'
+ ],
users => [
'DEF:users_avg={file}:users:AVERAGE',
'DEF:users_min={file}:users:MIN',
delay => ['-t', 'NTPd peer delay ({inst})', '-v', 'Seconds'],
df => ['-t', '{host}:{inst} usage', '-v', 'Percent', '-l', '0'],
disk => ['-t', '{host} disk {inst} usage', '-v', 'Byte/s'],
- fanspeed => ['-t', '{host} fanspeed {inst}', '-v', 'rpm'],
+ dns_traffic => ['-t', '{host} DNS traffic', '-v', 'Bit/s'],
+ email => ['-t', '{host} E-Mail {inst} count', '-v', 'E-Mails'],
+ email_size => ['-t', '{host} E-Mail {inst} size', '-v', 'Bytes'],
+ spam_score => ['-t', '{host} spam score', '-v', 'score'],
+ spam_check => ['-t', '{host} spam checks {inst}', '-v', 'hits'],
+ fanspeed => ['-t', '{host} fanspeed {inst}', '-v', 'RPM'],
frequency_offset => ['-t', 'NTPd frequency offset ({inst})', '-v', 'Parts per million'],
hddtemp => ['-t', '{host} hdd temperature {inst}', '-v', '°Celsius'],
if_errors => ['-t', '{host} {inst} errors', '-v', 'Errors/s'],
mysql_qcache => ['-t', 'mysql query cache', '-v', 'Queries/s' ],
mysql_threads => ['-t', 'mysql threads', '-v', 'Threads' ],
nfs3_procedures => ['-t', '{host} NFSv3 {inst} procedures', '-v', 'Procedures/s' ],
+ opcode => ['-t', 'OpCode {inst}', '-v', 'Queries/s'],
partition => ['-t', '{host} partition {inst} usage', '-v', 'Byte/s'],
ping => ['-t', '{host} ping to {inst}', '-v', 'ms'],
processes => ['-t', '{host} processes', '-v', 'Processes'],
+ ps_rss => ['-t', '{host} process {inst} RSS', '-v', 'Bytes', '-b', '1024'],
+ ps_cputime => ['-t', '{host} process {inst} CPU usage', '-v', 'Seconds'],
+ ps_count => ['-t', '{host} process {inst} count', '-v', 'Threads/Processes'],
+ ps_pagefaults => ['-t', '{host} process {inst} pagefaults', '-v', 'Pagefaults/s'],
+ qtype => ['-t', 'QType {inst}', '-v', 'Queries/s'],
+ rcode => ['-t', 'RCode {inst}', '-v', 'Queries/s'],
sensors => ['-t', '{host} sensor {inst}', '-v', '°Celsius'],
swap => ['-t', '{host} swap usage', '-v', 'Bytes', '-b', '1024', '-l', '0'],
temperature => ['-t', '{host} temperature {inst}', '-v', '°Celsius'],
time_dispersion => ['-t', 'NTPd time dispersion ({inst})', '-v', 'Seconds'],
traffic => ['-t', '{host} {inst} traffic', '-v', 'Bit/s'],
users => ['-t', '{host} users', '-v', 'Users'],
- voltage => ['-t', '{host} voltage', '-v', 'Volts'],
+ multimeter => ['-t', '{host} multimeter', '-v', 'Value'],
+ voltage => ['-t', '{host} voltage {inst}', '-v', 'Volts'],
vs_threads => ['-t', '{host} threads', '-v', 'Threads'],
vs_memory => ['-t', '{host} memory usage', '-v', 'Bytes'],
vs_processes => ['-t', '{host} processes', '-v', 'Processes'],
cpu => \&output_graph_cpu,
cpufreq => 1,
disk => 1,
+ email => \&output_graph_email_count,
+ email_size => \&output_graph_email_size,
+ spam_score => 1,
+ spam_check => \&output_graph_spam_check,
load => 0,
mails => 0,
memory => 0,
mysql_handler => \&output_graph_mysql_handler,
partition => 1,
ping => \&output_graph_ping,
+ qtype => \&output_graph_named_qtype,
+ rcode => \&output_graph_named_rcode,
sensors => 1,
traffic => 1,
- users => 1
+ users => 1,
+ multimeter => 1
};
our @Info;
return (@ret);
}
+sub output_graph_email_count
+{
+ my @inst = @_;
+ my @ret = ();
+
+ my $c = scalar @inst;
+
+ my @colors = get_n_colors ($c);
+
+ for (my $i = 0; $i < $c; ++$i) {
+ push @ret,
+ "DEF:min_$i=$AbsDir/email-$inst[$i].rrd:count:MIN",
+ "DEF:avg_$i=$AbsDir/email-$inst[$i].rrd:count:AVERAGE",
+ "DEF:max_$i=$AbsDir/email-$inst[$i].rrd:count:MAX";
+ }
+
+ for (my $i = 0; $i < $c; ++$i) {
+ my $s = "";
+
+ if (length $inst[$i] > 10) {
+ $s = substr ($inst[$i], 0, 7) . '...';
+ }
+ else {
+ $s = sprintf '%-10s', $inst[$i];
+ }
+
+ push @ret,
+ "LINE1:avg_$i#$colors[$i]:$s",
+ "GPRINT:min_$i:MIN:%4.1lf Min,",
+ "GPRINT:avg_$i:AVERAGE:%4.1lf Avg,",
+ "GPRINT:max_$i:MAX:%4.1lf Max,",
+ "GPRINT:avg_$i:LAST:%4.1lf Last\\l";
+ }
+ return @ret;
+}
+
+sub output_graph_email_size
+{
+ my @inst = @_;
+ my @ret = ();
+
+ my $c = scalar @inst;
+
+ my @colors = get_n_colors ($c);
+
+ for (my $i = 0; $i < $c; ++$i) {
+ push @ret,
+ "DEF:min_$i=$AbsDir/email_size-$inst[$i].rrd:size:MIN",
+ "DEF:avg_$i=$AbsDir/email_size-$inst[$i].rrd:size:AVERAGE",
+ "DEF:max_$i=$AbsDir/email_size-$inst[$i].rrd:size:MAX";
+ }
+
+ for (my $i = 0; $i < $c; ++$i) {
+ my $s = "";
+
+ if (length $inst[$i] > 10) {
+ $s = substr ($inst[$i], 0, 7) . '...';
+ }
+ else {
+ $s = sprintf '%-10s', $inst[$i];
+ }
+
+ push @ret,
+ "LINE1:avg_$i#$colors[$i]:$s",
+ "GPRINT:min_$i:MIN:%4.1lf Min,",
+ "GPRINT:avg_$i:AVERAGE:%4.1lf Avg,",
+ "GPRINT:max_$i:MAX:%4.1lf Max,",
+ "GPRINT:avg_$i:LAST:%4.1lf Last\\l";
+ }
+ return @ret;
+}
+
+sub output_graph_spam_check
+{
+ my @inst = sort @_;
+ my @ret = ();
+
+ my $c = scalar @inst;
+
+ my @colors = get_n_colors ($c);
+
+ for (my $i = 0; $i < $c; ++$i) {
+ push @ret,
+ "DEF:min_$i=$AbsDir/spam_check-$inst[$i].rrd:hits:MIN",
+ "DEF:avg_$i=$AbsDir/spam_check-$inst[$i].rrd:hits:AVERAGE",
+ "DEF:max_$i=$AbsDir/spam_check-$inst[$i].rrd:hits:MAX";
+ }
+
+ for (my $i = 0; $i < $c; ++$i) {
+ my $s = "";
+
+ if (length $inst[$i] > 25) {
+ $s = substr ($inst[$i], 0, 22) . '...';
+ }
+ else {
+ $s = sprintf '%-25s', $inst[$i];
+ }
+
+ push @ret,
+ "LINE1:avg_$i#$colors[$i]:$s",
+ "GPRINT:min_$i:MIN:%4.1lf Min,",
+ "GPRINT:avg_$i:AVERAGE:%4.1lf Avg,",
+ "GPRINT:max_$i:MAX:%4.1lf Max,",
+ "GPRINT:avg_$i:LAST:%4.1lf Last\\l";
+ }
+ return @ret;
+}
+
sub output_graph_mysql_commands
{
my @inst = @_;
return (@ret);
}
+sub output_graph_named_qtype
+{
+ my @inst = @_;
+ my @ret = ();
+
+ die if (@inst < 2);
+
+ my @colors = get_n_colors (scalar (@inst));
+
+ for (my $i = 0; $i < scalar (@inst); $i++)
+ {
+ my $inst = $inst[$i];
+ push (@ret,
+ "DEF:avg_$i=$AbsDir/qtype-$inst.rrd:value:AVERAGE",
+ "DEF:min_$i=$AbsDir/qtype-$inst.rrd:value:MIN",
+ "DEF:max_$i=$AbsDir/qtype-$inst.rrd:value:MAX");
+ }
+
+ for (my $i = 0; $i < scalar (@inst); $i++)
+ {
+ my $inst = $inst[$i];
+ my $color = $colors[$i];
+ my $type = ($i == 0) ? 'AREA' : 'STACK';
+
+ if (length ($inst) > 5)
+ {
+ $inst = substr ($inst, 0, 5);
+ }
+ else
+ {
+ $inst = sprintf ('%-5s', $inst);
+ }
+
+ push (@ret,
+ "$type:avg_$i#$color:$inst",
+ "GPRINT:min_$i:MIN:%9.3lf Min,",
+ "GPRINT:avg_$i:AVERAGE:%9.3lf Avg,",
+ "GPRINT:max_$i:MAX:%9.3lf Max,",
+ "GPRINT:avg_$i:LAST:%9.3lf Last\\l");
+ }
+
+ return (@ret);
+}
+
+sub output_graph_named_rcode
+{
+ my @inst = @_;
+ my @ret = ();
+
+ die if (@inst < 2);
+
+ my @colors = get_n_colors (scalar (@inst));
+
+ for (my $i = 0; $i < scalar (@inst); $i++)
+ {
+ my $inst = $inst[$i];
+ push (@ret,
+ "DEF:avg_$i=$AbsDir/rcode-$inst.rrd:value:AVERAGE",
+ "DEF:min_$i=$AbsDir/rcode-$inst.rrd:value:MIN",
+ "DEF:max_$i=$AbsDir/rcode-$inst.rrd:value:MAX");
+ }
+
+ for (my $i = 0; $i < scalar (@inst); $i++)
+ {
+ my $inst = $inst[$i];
+ my $color = $colors[$i];
+ my $type = ($i == 0) ? 'AREA' : 'STACK';
+
+ if (length ($inst) > 8)
+ {
+ $inst = substr ($inst, 0, 6) . '..';
+ }
+ else
+ {
+ $inst = sprintf ('%-8s', $inst);
+ }
+
+ push (@ret,
+ "$type:avg_$i#$color:$inst",
+ "GPRINT:min_$i:MIN:%9.3lf Min,",
+ "GPRINT:avg_$i:AVERAGE:%9.3lf Avg,",
+ "GPRINT:max_$i:MAX:%9.3lf Max,",
+ "GPRINT:avg_$i:LAST:%9.3lf Last\\l");
+ }
+
+ return (@ret);
+}
sub output_graph
{
die unless (defined ($GraphDefs->{$Type}));
;;
restart|reload)
stop
+ sleep 1
start
;;
condrestart)
.
This package contains the development files needed to create your own
plugins.
-
collectd_SOURCES = collectd.c collectd.h \
utils_debug.c utils_debug.h \
utils_mount.c utils_mount.h \
+ utils_ignorelist.c utils_ignorelist.h \
common.c common.h \
network.c network.h \
plugin.c plugin.h \
collectd_DEPENDENCIES += disk.la
endif
+if BUILD_MODULE_DNS
+pkglib_LTLIBRARIES += dns.la
+dns_la_SOURCES = dns.c utils_dns.c utils_dns.h
+dns_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBPCAP
+dns_la_LDFLAGS += -lpcap
+endif
+if BUILD_WITH_LIBPTHREAD
+dns_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" dns.la
+collectd_DEPENDENCIES += dns.la
+endif
+
+if BUILD_MODULE_EMAIL
+pkglib_LTLIBRARIES += email.la
+email_la_SOURCES = email.c
+email_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBPTHREAD
+email_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" email.la
+collectd_DEPENDENCIES += email.la
+endif
+
#if BUILD_MODULE_QUOTA
#pkglib_LTLIBRARIES += quota.la
#quota_la_SOURCES = quota_plugin.c quota_plugin.h
endif
endif
+if BUILD_MODULE_MULTIMETER
+pkglib_LTLIBRARIES += multimeter.la
+multimeter_la_SOURCES = multimeter.c
+multimeter_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" multimeter.la
+collectd_DEPENDENCIES += multimeter.la
+endif
+
if BUILD_MODULE_MYSQL
pkglib_LTLIBRARIES += mysql.la
mysql_la_SOURCES = mysql.c
$(mkinstalldirs) $(DESTDIR)$(sysconfdir)
if test -e $(DESTDIR)$(sysconfdir)/collectd.conf; \
then \
- $(INSTALL_DATA) collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \
+ $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \
else \
- $(INSTALL_DATA) collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
+ $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
fi
#if HAVE_LIBCURL
static CURL *curl = NULL;
-static char apache_buffer[4096];
+#define ABUFFER_SIZE 16384
+static char apache_buffer[ABUFFER_SIZE];
static int apache_buffer_len = 0;
static char apache_curl_error[CURL_ERROR_SIZE];
#endif /* HAVE_LIBCURL */
{
size_t len = size * nmemb;
- if ((apache_buffer_len + len) >= 4096)
+ if ((apache_buffer_len + len) >= ABUFFER_SIZE)
{
- len = 4095 - apache_buffer_len;
+ len = (ABUFFER_SIZE - 1) - apache_buffer_len;
}
if (len <= 0)
double value;
static int sockfd = -1;
- static unsigned int complain = 0;
+ static complain_t compl;
#if APCMAIN
# define PRINT_VALUE(name, val) printf(" Found property: name = %s; value = %f;\n", name, val)
{
if ((sockfd = net_open (host, NULL, port)) < 0)
{
- /* Complain once every six hours. */
- int complain_step = 21600 / atoi (COLLECTD_STEP);
-
- if ((complain % complain_step) == 0)
- syslog (LOG_ERR, "apcups plugin: Connecting to the apcupsd failed.");
- complain++;
-
+ plugin_complain (LOG_ERR, &compl, "apcups plugin: "
+ "Connecting to the apcupsd failed.");
return (-1);
}
- else if (complain > 1)
+ else
{
- syslog (LOG_NOTICE, "apcups plugin: Connection re-established to the apcupsd.");
- complain = 0;
+ plugin_relief (LOG_NOTICE, &compl, "apcups plugin: "
+ "Connection re-established to the apcupsd.");
}
}
" General:\n"
" -C <file> Configuration file.\n"
" Default: "CONFIGFILE"\n"
+ " -P <file> PID-file.\n"
+ " Default: "PIDFILE"\n"
#if COLLECT_DAEMON
" -f Don't fork to the background.\n"
#endif
curtime = time (NULL);
/* Issue all plugins */
- plugin_read_all ();
+ plugin_read_all (&loop);
if (gettimeofday (&tv_now, NULL) < 0)
{
continue;
}
- while (nanosleep (&ts_wait, &ts_wait) == -1)
+ while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) == -1))
{
if (errno != EINTR)
{
char *configfile = CONFIGFILE;
#if COLLECT_DAEMON
struct sigaction sigChldAction;
- char *pidfile = PIDFILE;
+ char *pidfile = NULL;
pid_t pid;
int daemonize = 1;
#endif
c = getopt (argc, argv, "hC:"
#if COLLECT_DAEMON
- "f"
+ "fP:"
#endif
);
configfile = optarg;
break;
#if COLLECT_DAEMON
+ case 'P':
+ pidfile = optarg;
+ break;
case 'f':
daemonize = 0;
break;
sigChldAction.sa_handler = SIG_IGN;
sigaction (SIGCHLD, &sigChldAction, NULL);
- if ((pidfile = cf_get_option ("PIDFile", PIDFILE)) == NULL)
+ if ((pidfile == NULL)
+ && ((pidfile = cf_get_option ("PIDFile", PIDFILE)) == NULL))
{
fprintf (stderr, "Cannot obtain pidfile. This shoud not happen. Ever.");
return (1);
#endif
start_client ();
+ plugin_shutdown_all ();
+
#if COLLECT_DEBUG
if (logfile != NULL)
DBG_STOPFILE("debug file closed.");
@BUILD_MODULE_CPUFREQ_TRUE@LoadPlugin cpufreq
@BUILD_MODULE_DF_TRUE@LoadPlugin df
@BUILD_MODULE_DISK_TRUE@LoadPlugin disk
+@BUILD_MODULE_DNS_TRUE@LoadPlugin dns
@BUILD_MODULE_HDDTEMP_TRUE@LoadPlugin hddtemp
@BUILD_MODULE_LOAD_TRUE@LoadPlugin load
@BUILD_MODULE_MEMORY_TRUE@LoadPlugin memory
+@BUILD_MODULE_MULTIMETER_TRUE@LoadPlugin multimeter
@BUILD_MODULE_MYSQL_TRUE@LoadPlugin mysql
@BUILD_MODULE_NFS_TRUE@LoadPlugin nfs
@BUILD_MODULE_NTPD_TRUE@LoadPlugin ntpd
# Port 3551
#</Plugin>
+#<Plugin dns>
+# Interface eth0
+# IgnoreSource 192.168.0.1
+#</Plugin>
+
#<Plugin hddtemp>
# Host 127.0.0.1
# Port 7634
# Host host.foo.bar
#</Plugin>
+#<Plugin processes>
+# Process name
+#</Plugin>
+
+## There are two different naming schemes for the sensors plugin, to preserver
+## backwards compatibility. The `extended naming' is prefered, but not the
+## default for this reason. See `collectd.conf(5)' for more information.
+#<Plugin sensors>
+# ExtendedSensorNaming false
+# Sensor it8712-temp1
+# Sensor it8712-fan3
+# Sensor it8712-in8
+## -- OR --
+# ExtendedSensorNaming true
+# Sensor it8712-isa-0290/temperature-temp1
+# Sensor it8712-isa-0290/fanspeed-fan3
+# Sensor it8712-isa-0290/voltage-in8
+#
+# IgnoreSelected false
+#</Plugin>
+
#<Plugin traffic>
# Interface eth0
# IgnoreSelected false
=item B<PIDFile> I<File>
Sets where to write the PID file to. This file is overwritten when it exists
-and deleted when the program ist stopped. Available in B<all modes>.
+and deleted when the program ist stopped. Some init-scripts might override this
+setting using the B<-P> commandline option. Available in B<all modes>.
=item B<DataDir> I<Directory>
=back
+=head2 Plugin C<df>
+
+=over 4
+
+=item B<Device> I<Device>
+
+Select partitions based on the devicename.
+
+=item B<MountPoint> I<Directory>
+
+Select partitions based on the mountpoint.
+
+=item B<FSType> I<FSType>
+
+Select partitions based on the filesystem type.
+
+=item B<IgnoreSelected> I<true>|I<false>
+
+Invert the selection: If set to true, all partitions B<except> the ones that
+match any one of the criteria are collected. By default only selected
+partitions are collected if a selection is made. If no selection is conifured
+at all, B<all> partitions are selected.
+
+=back
+
+=head2 Plugin C<dns>
+
+=over 4
+
+=item B<Interface> I<Interface>
+
+The dns plugin uses B<libpcap> to capture dns traffic and analyses it. This
+option sets the interface that should be used. If this option is not set, or
+set to "any", the plugin will try to get packets from B<all> interfaces. This
+may not work on certain platforms, such as MacE<nbsp>OSE<nbsp>X.
+
+=item B<IgnoreSource> I<IP-address>
+
+Ignore packets that originate from this address.
+
+=back
+
+=head2 Plugin C<email>
+
+=over 4
+
+=item B<SocketGroup> I<Group>
+
+If running as root change the group of the UNIX-socket after it has been
+created. Defaults to B<collectd>.
+
+=item B<SocketPerms> I<Permissions>
+
+Change the file permissions of the UNIX-socket after it has been created. The
+permissions must be given as a numeric, octal value as you would pass to
+L<chmod(1)>. Defaults to B<0770>.
+
+=item B<MaxConns> I<Number>
+
+Sets the maximum number of connections that can be handled in parallel. Since
+this many threads will be started immediately setting this to a very high
+value will waste valuable resources. Defaults to B<5> and will be forced to be
+at most B<16384> to prevent typos and dumb mistakes.
+
+=back
+
=head2 Plugin C<hddtemp>
=over 4
=back
+=head2 Plugin C<sensors>
+
+=over 4
+
+=item B<ExtendedSensorNaming> I<true>|I<false>
+
+If set to I<true> this option switches on the extended sensors and RRD-files
+naming. This option exists to preserve backwards compatibility. It is
+recommended that you set this option to I<true>. The default is I<false> to
+maintain compatibility only.
+
+Sensors get names like I<chip-bus-address/type-feature> (e.g.
+I<it8712-isa-0290/voltage-in1>) and RRD files are therefore stored in a
+standalone directory inside the B<DataDir> directory and get names like
+I<lm_sensors-chip-bus-address/type-feature.rrd> (e.g.
+I<lm_sensors-it8712-isa-0290/voltage-in1.rrd>).
+
+The B<ExtendedSensorNaming> option breaks the compatibility with previous
+sensors and RRD files naming and the place where RRDs are stored. If you turn
+it on, the plugin will create new RRD files in a standalone directory inside
+the B<DataDir> directory and without previous history. You can rename ``old''
+RRD-files to preserve already collected statistics, because the file layout
+hasn't changed. If you have two chips of the same type, you need to use
+B<ExtendedSensorNaming> in order to collect information from both chips.
+
+If not set or set to I<false>, the extended naming is not active. Sensors get
+names like I<chip-feature> (e.g. I<it8712-in1>) and RRD files are stored in the
+main B<DataDir> directory and get names like I<sensors-chip-feature.rrd> (e.g.
+I<sensors-it8712-in1.rrd>). You simply continue using the plugin the old way
+and additionally also getting data for newly added sensors in this mode.
+
+=item B<Sensor> I<chip-feature> or B<Sensor> I<chip-bus-address/type-feature>
+
+Both option modes select the name of the sensor which you want to collect.
+The naming scheme is dependent on the state of the B<ExtendedSensorNaming>
+option (see previous option). Both option modes can also deselect the
+sensor according to the B<IgnoreSelected> option (see below).
+
+For example the option "B<Sensor> I<it8712-in1>" will cause the collectd
+to gather data for the voltage sensor I<in1> of the I<it8712> chip in case
+of the B<ExtendedSensorNaming> option is set to I<false>.
+
+And likewise the option "B<Sensor> I<it8712-isa-0290/voltage-in1>" will
+cause the collectd to gather data for the voltage sensor I<in1> of the I<it8712>
+on the isa bus at the address 0290 in case of the B<ExtendedSensorNaming>
+option set to I<true>.
+
+=item B<IgnoreSelected> I<true>|I<false>
+
+If no configuration if given, the B<sensors>-plugin will collect data from
+all sensors. This may not be practical, especially for uninteresting sensors.
+Thus, you can use the B<Sensor>-option to pick the sensors you're
+interested in. Sometimes, however, it's easier/prefered to collect all
+sensors I<except> a few ones. This option enables you to
+do that: By setting B<IgnoreSelected> to I<true> the effect of
+B<Sensor> is inversed: All selected sensors are ignored and all
+other sensors are collected.
+
+back
+
+=back
+
=head2 Plugin C<traffic>
=over 4
#define MODE_LOCAL 0x04
#define MODE_LOG 0x08
+#ifndef COLLECTD_GRP_NAME
+# define COLLECTD_GRP_NAME "collectd"
+#endif
+
#ifndef COLLECTD_STEP
# define COLLECTD_STEP "10"
#endif
=item
+DNS traffic (I<dns>)
+
+=item
+
+Email usage (I<email>)
+
+=item
+
Harddisk temperatures (I<hddtemp>)
=item
change B<collectd>'s behavior. The path may be relative to the current working
directory.
+=item B<-P> I<E<lt>pid-fileE<gt>>
+
+Specify an alternative pid file. This overwrites any settings in the config
+file. This is thought for init-scripts that require the PID-file in a certain
+directory to work correctly. For everyday-usage use the B<PIDFile>
+config-option.
+
=item B<-f>
Don't fork to the background. I<collectd> will also B<not> close standard file
similar tool is installed and an "cpu governor" (that's kernel module) is
loaded.
+=head2 email
+
+This plugin collects data indirectly by providing a UNIX socket that external
+programs can connect to. A simple line based protocol is used to communicate
+with the plugin:
+
+E-Mail type (e.g. "ham", "spam", "virus", ...) and size (bytes):
+
+ e:<type>:<size>
+
+Spam score:
+
+ s:<value>
+
+Successful spam checks (e.g. "BAYES_99", "SUBJECT_DRUG_GAP_C", ...):
+
+ c:<type1>[,<type2>,...]
+
+Each line is limited to 256 characters (including the newline character).
+Longer lines will be ignored.
+
=head2 mysql
Requires B<mysqlclient> to be installed. It connects to the database when
DS:wcount:COUNTER:HEARTBEAT:0:U
DS:wbytes:COUNTER:HEARTBEAT:0:U
+=item E-Mail count (F<email/email-I<E<lt>typeE<gt>>.rrd>)
+
+ DS:count:GAUGE:HEARTBEAT:0:U
+
+=item E-Mail size (F<email/email_size-I<E<lt>typeE<gt>>.rrd>)
+
+ DS:size:GAUGE:HEARTBEAT:0:U
+
=item HDD Temperature (F<hddtemp-I<E<lt>majorE<gt>>-I<E<lt>minorE<gt>>.rrd>)
DS:value:GAUGE:HEARTBEAT:U:U
DS:paging:GAUGE:HEARTBEAT:0:65535
DS:blocked:GAUGE:HEARTBEAT:0:65535
-=item lm_sensors (F<sensors-I<E<lt>chipE<gt>>-I<E<lt>featureE<gt>>.rrd>)
+=item lm_sensors fanspeed sensor (F<sensors-I<E<lt>instE<gt>>.rrd> or F<lm_sensors-I<E<lt>chipE<gt>>/fanspeed-I<E<lt>instE<gt>>.rrd>)
+
+ DS:value:GAUGE:HEARTBEAT:U:U
+
+=item lm_sensors temperature sensor (F<sensors-I<E<lt>instE<gt>>.rrd> or F<lm_sensors-I<E<lt>chipE<gt>>/temperature-I<E<lt>instE<gt>>.rrd>)
DS:value:GAUGE:HEARTBEAT:U:U
+=item lm_sensors voltage sensor (F<sensors-I<E<lt>instE<gt>>.rrd> or F<lm_sensors-I<E<lt>chipE<gt>>/voltage-I<E<lt>instE<gt>>.rrd>)
+
+ DS:voltage:GAUGE:HEARTBEAT:U:U
+
=item Serial port traffic (F<serial-I<E<lt>numE<gt>>.rrd>)
DS:incoming:COUNTER:HEARTBEAT:0:U
DS:outgoing:COUNTER:HEARTBEAT:0:U
+=item Spam score (F<email/spam_score.rrd>)
+
+ DS:score:GAUGE:HEARTBEAT:0:U
+
+=item Spam checks (F<email/spam_check-I<E<lt>typeE<gt>>.rrd>)
+
+ DS:hits:GAUGE:HEARTBEAT:0:U
+
=item Swap usage (F<swap.rrd>)
DS:used:GAUGE:HEARTBEAT:0:1099511627776
char *fields[9];
int numfields;
+ static complain_t complain_obj;
+
if ((fh = fopen ("/proc/stat", "r")) == NULL)
{
- syslog (LOG_WARNING, "cpu: fopen: %s", strerror (errno));
+ plugin_complain (LOG_ERR, &complain_obj, "cpu plugin: "
+ "fopen (/proc/stat) failed: %s",
+ strerror (errno));
return;
}
+ plugin_relief (LOG_NOTICE, &complain_obj, "cpu plugin: "
+ "fopen (/proc/stat) succeeded.");
+
while (fgets (buf, BUFSIZE, fh) != NULL)
{
if (strncmp (buf, "cpu", 3))
long cpuinfo[CPUSTATES];
size_t cpuinfo_size;
+ static complain_t complain_obj;
+
cpuinfo_size = sizeof (cpuinfo);
if (sysctlbyname("kern.cp_time", &cpuinfo, &cpuinfo_size, NULL, 0) < 0)
{
- syslog (LOG_WARNING, "cpu: sysctlbyname: %s", strerror (errno));
+ plugin_complain (LOG_ERR, &complain_obj, "cpu plugin: "
+ "sysctlbyname failed: %s.",
+ strerror (errno));
return;
}
+ plugin_relief (LOG_NOTICE, &complain_obj, "cpu plugin: "
+ "sysctlbyname succeeded.");
+
cpuinfo[CP_SYS] += cpuinfo[CP_INTR];
/* FIXME: Instance is always `0' */
#include "collectd.h"
#include "common.h"
#include "plugin.h"
+#include "configfile.h"
#include "utils_mount.h"
+#include "utils_ignorelist.h"
#define MODULE_NAME "df"
};
static int ds_num = 2;
+static char *config_keys[] =
+{
+ "Device",
+ "MountPoint",
+ "FSType",
+ "IgnoreSelected",
+ NULL
+};
+static int config_keys_num = 4;
+
+static ignorelist_t *il_device = NULL;
+static ignorelist_t *il_mountpoint = NULL;
+static ignorelist_t *il_fstype = NULL;
+
#define BUFSIZE 512
static void df_init (void)
{
+ if (il_device == NULL)
+ il_device = ignorelist_create (1);
+ if (il_mountpoint == NULL)
+ il_mountpoint = ignorelist_create (1);
+ if (il_fstype == NULL)
+ il_fstype = ignorelist_create (1);
+
return;
}
+static int df_config (char *key, char *value)
+{
+ df_init ();
+
+ if (strcasecmp (key, "Device") == 0)
+ {
+ if (ignorelist_add (il_device, value))
+ return (1);
+ return (0);
+ }
+ else if (strcasecmp (key, "MountPoint") == 0)
+ {
+ if (ignorelist_add (il_mountpoint, value))
+ return (1);
+ return (0);
+ }
+ else if (strcasecmp (key, "FSType") == 0)
+ {
+ if (ignorelist_add (il_fstype, value))
+ return (1);
+ return (0);
+ }
+ else if (strcasecmp (key, "IgnoreSelected") == 0)
+ {
+ if ((strcasecmp (value, "True") == 0)
+ || (strcasecmp (value, "Yes") == 0)
+ || (strcasecmp (value, "On") == 0))
+ {
+ ignorelist_set_invert (il_device, 0);
+ ignorelist_set_invert (il_mountpoint, 0);
+ ignorelist_set_invert (il_fstype, 0);
+ }
+ else
+ {
+ ignorelist_set_invert (il_device, 1);
+ ignorelist_set_invert (il_mountpoint, 1);
+ ignorelist_set_invert (il_fstype, 1);
+ }
+ return (0);
+ }
+
+ return (-1);
+}
+
static void df_write (char *host, char *inst, char *val)
{
char file[BUFSIZE];
mnt_name[i] = '-';
}
+ if (ignorelist_match (il_device,
+ (mnt_ptr->spec_device != NULL)
+ ? mnt_ptr->spec_device
+ : mnt_ptr->device))
+ continue;
+ if (ignorelist_match (il_mountpoint, mnt_ptr->dir))
+ continue;
+ if (ignorelist_match (il_fstype, mnt_ptr->type))
+ continue;
+
df_submit (mnt_name, df_used, df_free);
}
void module_register (void)
{
plugin_register (MODULE_NAME, df_init, df_read, df_write);
+ cf_register (MODULE_NAME, df_config, config_keys, config_keys_num);
}
#undef BUFSIZE
int disk_minor;
char disk_name[64];
+ static complain_t complain_obj;
+
/* Get the list of all disk objects. */
if (IOServiceGetMatchingServices (io_master_port,
IOServiceMatching (kIOBlockStorageDriverClass),
&disk_list) != kIOReturnSuccess)
{
- syslog (LOG_ERR, "disk-plugin: IOServiceGetMatchingServices failed.");
+ plugin_complain (LOG_ERR, &complain_obj, "disk plugin: "
+ "IOServiceGetMatchingServices failed.");
return;
}
+ else if (complain_obj.interval != 0)
+ {
+ plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: "
+ "IOServiceGetMatchingServices succeeded.");
+ }
while ((disk = IOIteratorNext (disk_list)) != 0)
{
diskstats_t *ds, *pre_ds;
+ static complain_t complain_obj;
+
if ((fh = fopen ("/proc/diskstats", "r")) == NULL)
{
if ((fh = fopen ("/proc/partitions", "r")) == NULL)
+ {
+ plugin_complain (LOG_ERR, &complain_obj, "disk plugin: Failed to open /proc/{diskstats,partitions}.");
return;
+ }
/* Kernel is 2.4.* */
fieldshift = 1;
}
+ plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: Succeeded to open /proc/{diskstats,partitions}.");
+
while (fgets (buffer, 1024, fh) != NULL)
{
numfields = strsplit (buffer, fields, 32);
--- /dev/null
+/**
+ * collectd - src/dns.c
+ * Copyright (C) 2006 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_debug.h"
+#include "utils_dns.h"
+
+#define MODULE_NAME "dns"
+
+#if HAVE_LIBPCAP && HAVE_LIBPTHREAD
+# include <pthread.h>
+# include <pcap.h>
+# include <sys/poll.h>
+# define DNS_HAVE_READ 1
+#else
+# define DNS_HAVE_READ 0
+#endif
+
+/*
+ * Private data types
+ */
+#if DNS_HAVE_READ
+struct counter_list_s
+{
+ unsigned int key;
+ unsigned int value;
+ struct counter_list_s *next;
+};
+typedef struct counter_list_s counter_list_t;
+#endif
+
+/*
+ * Private variables
+ */
+static char *traffic_file = "dns/dns_traffic.rrd";
+static char *qtype_file = "dns/qtype-%s.rrd";
+static char *opcode_file = "dns/opcode-%s.rrd";
+static char *rcode_file = "dns/rcode-%s.rrd";
+
+static char *traffic_ds_def[] =
+{
+ /* Limit to 1GBit/s */
+ "DS:queries:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
+ "DS:responses:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
+ NULL
+};
+static int traffic_ds_num = 2;
+
+static char *qtype_ds_def[] =
+{
+ "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
+ NULL
+};
+static int qtype_ds_num = 1;
+
+static char *opcode_ds_def[] =
+{
+ "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
+ NULL
+};
+static int opcode_ds_num = 1;
+
+static char *rcode_ds_def[] =
+{
+ "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
+ NULL
+};
+static int rcode_ds_num = 1;
+
+#if DNS_HAVE_READ
+static char *config_keys[] =
+{
+ "Interface",
+ "IgnoreSource",
+ NULL
+};
+static int config_keys_num = 2;
+
+#define PCAP_SNAPLEN 1460
+static char *pcap_device = NULL;
+
+static unsigned int tr_queries;
+static unsigned int tr_responses;
+static counter_list_t *qtype_list;
+static counter_list_t *opcode_list;
+static counter_list_t *rcode_list;
+
+static pthread_t listen_thread;
+static int listen_thread_init = 0;
+/* The `traffic' mutex if for `tr_queries' and `tr_responses' */
+static pthread_mutex_t traffic_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t qtype_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t opcode_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t rcode_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif /* DNS_HAVE_READ */
+
+/*
+ * Private functions
+ */
+#if DNS_HAVE_READ
+static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
+{
+ counter_list_t *entry;
+
+ DBG ("counter_list_search (list = %p, key = %u)",
+ (void *) *list, key);
+
+ for (entry = *list; entry != NULL; entry = entry->next)
+ if (entry->key == key)
+ break;
+
+ DBG ("return (%p)", (void *) entry);
+ return (entry);
+}
+
+static counter_list_t *counter_list_create (counter_list_t **list,
+ unsigned int key, unsigned int value)
+{
+ counter_list_t *entry;
+
+ DBG ("counter_list_create (list = %p, key = %u, value = %u)",
+ (void *) *list, key, value);
+
+ entry = (counter_list_t *) malloc (sizeof (counter_list_t));
+ if (entry == NULL)
+ return (NULL);
+
+ memset (entry, 0, sizeof (counter_list_t));
+ entry->key = key;
+ entry->value = value;
+
+ if (*list == NULL)
+ {
+ *list = entry;
+ }
+ else
+ {
+ counter_list_t *last;
+
+ last = *list;
+ while (last->next != NULL)
+ last = last->next;
+
+ last->next = entry;
+ }
+
+ DBG ("return (%p)", (void *) entry);
+ return (entry);
+}
+
+static void counter_list_add (counter_list_t **list,
+ unsigned int key, unsigned int increment)
+{
+ counter_list_t *entry;
+
+ DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
+ (void *) *list, key, increment);
+
+ entry = counter_list_search (list, key);
+
+ if (entry != NULL)
+ {
+ entry->value += increment;
+ }
+ else
+ {
+ counter_list_create (list, key, increment);
+ }
+ DBG ("return ()");
+}
+
+static int dns_config (char *key, char *value)
+{
+ if (strcasecmp (key, "Interface") == 0)
+ {
+ if (pcap_device != NULL)
+ free (pcap_device);
+ if ((pcap_device = strdup (value)) == NULL)
+ return (1);
+ }
+ else if (strcasecmp (key, "IgnoreSource") == 0)
+ {
+ if (value != NULL)
+ ignore_list_add_name (value);
+ }
+ else
+ {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void dns_child_callback (const rfc1035_header_t *dns)
+{
+ if (dns->qr == 0)
+ {
+ /* This is a query */
+ pthread_mutex_lock (&traffic_mutex);
+ tr_queries += dns->length;
+ pthread_mutex_unlock (&traffic_mutex);
+
+ pthread_mutex_lock (&qtype_mutex);
+ counter_list_add (&qtype_list, dns->qtype, 1);
+ pthread_mutex_unlock (&qtype_mutex);
+ }
+ else
+ {
+ /* This is a reply */
+ pthread_mutex_lock (&traffic_mutex);
+ tr_responses += dns->length;
+ pthread_mutex_unlock (&traffic_mutex);
+
+ pthread_mutex_lock (&rcode_mutex);
+ counter_list_add (&rcode_list, dns->rcode, 1);
+ pthread_mutex_unlock (&rcode_mutex);
+ }
+
+ /* FIXME: Are queries, replies or both interesting? */
+ pthread_mutex_lock (&opcode_mutex);
+ counter_list_add (&opcode_list, dns->opcode, 1);
+ pthread_mutex_unlock (&opcode_mutex);
+}
+
+static void *dns_child_loop (void *dummy)
+{
+ pcap_t *pcap_obj;
+ char pcap_error[PCAP_ERRBUF_SIZE];
+ struct bpf_program fp;
+
+ int status;
+
+ /* Don't block any signals */
+ {
+ sigset_t sigmask;
+ sigemptyset (&sigmask);
+ pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
+ }
+
+ /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
+ DBG ("Creating PCAP object..");
+ pcap_obj = pcap_open_live (pcap_device,
+ PCAP_SNAPLEN,
+ 0 /* Not promiscuous */,
+ atoi (COLLECTD_STEP),
+ pcap_error);
+ if (pcap_obj == NULL)
+ {
+ syslog (LOG_ERR, "dns plugin: Opening interface `%s' "
+ "failed: %s",
+ (pcap_device != NULL) ? pcap_device : "any",
+ pcap_error);
+ return (NULL);
+ }
+
+ memset (&fp, 0, sizeof (fp));
+ if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
+ {
+ DBG ("pcap_compile failed");
+ syslog (LOG_ERR, "dns plugin: pcap_compile failed");
+ return (NULL);
+ }
+ if (pcap_setfilter (pcap_obj, &fp) < 0)
+ {
+ DBG ("pcap_setfilter failed");
+ syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
+ return (NULL);
+ }
+
+ DBG ("PCAP object created.");
+
+ dnstop_set_pcap_obj (pcap_obj);
+ dnstop_set_callback (dns_child_callback);
+
+ status = pcap_loop (pcap_obj,
+ -1 /* loop forever */,
+ handle_pcap /* callback */,
+ NULL /* Whatever this means.. */);
+ if (status < 0)
+ syslog (LOG_ERR, "dns plugin: Listener thread is exiting "
+ "abnormally: %s", pcap_geterr (pcap_obj));
+
+ DBG ("child is exiting");
+
+ pcap_close (pcap_obj);
+ listen_thread_init = 0;
+ pthread_exit (NULL);
+
+ return (NULL);
+} /* static void dns_child_loop (void) */
+#endif /* DNS_HAVE_READ */
+
+static void dns_init (void)
+{
+#if DNS_HAVE_READ
+ /* clean up an old thread */
+ int status;
+
+ pthread_mutex_lock (&traffic_mutex);
+ tr_queries = 0;
+ tr_responses = 0;
+ pthread_mutex_unlock (&traffic_mutex);
+
+ if (listen_thread_init != 0)
+ return;
+
+ status = pthread_create (&listen_thread, NULL, dns_child_loop,
+ (void *) 0);
+ if (status != 0)
+ {
+ syslog (LOG_ERR, "dns plugin: pthread_create failed: %s",
+ strerror (status));
+ return;
+ }
+
+ listen_thread_init = 1;
+#endif /* DNS_HAVE_READ */
+}
+
+static void traffic_write (char *host, char *inst, char *val)
+{
+ rrd_update_file (host, traffic_file, val,
+ traffic_ds_def, traffic_ds_num);
+}
+
+static void qtype_write (char *host, char *inst, char *val)
+{
+ char file[512];
+ int status;
+
+ status = snprintf (file, 512, qtype_file, inst);
+ if (status < 1)
+ return;
+ else if (status >= 512)
+ return;
+
+ rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
+}
+
+static void rcode_write (char *host, char *inst, char *val)
+{
+ char file[512];
+ int status;
+
+ status = snprintf (file, 512, rcode_file, inst);
+ if (status < 1)
+ return;
+ else if (status >= 512)
+ return;
+
+ rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
+}
+
+static void opcode_write (char *host, char *inst, char *val)
+{
+ char file[512];
+ int status;
+
+ status = snprintf (file, 512, opcode_file, inst);
+ if (status < 1)
+ return;
+ else if (status >= 512)
+ return;
+
+ rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
+}
+
+#if DNS_HAVE_READ
+static void traffic_submit (unsigned int queries, unsigned int replies)
+{
+ char buffer[64];
+ int status;
+
+ status = snprintf (buffer, 64, "N:%u:%u", queries, replies);
+ if ((status < 1) || (status >= 64))
+ return;
+
+ plugin_submit ("dns_traffic", "-", buffer);
+}
+
+static void qtype_submit (int qtype, unsigned int counter)
+{
+ char inst[32];
+ char buffer[32];
+ int status;
+
+ strncpy (inst, qtype_str (qtype), 32);
+ inst[31] = '\0';
+
+ status = snprintf (buffer, 32, "N:%u", counter);
+ if ((status < 1) || (status >= 32))
+ return;
+
+ plugin_submit ("dns_qtype", inst, buffer);
+}
+
+static void rcode_submit (int rcode, unsigned int counter)
+{
+ char inst[32];
+ char buffer[32];
+ int status;
+
+ strncpy (inst, rcode_str (rcode), 32);
+ inst[31] = '\0';
+
+ status = snprintf (buffer, 32, "N:%u", counter);
+ if ((status < 1) || (status >= 32))
+ return;
+
+ plugin_submit ("dns_rcode", inst, buffer);
+}
+
+static void opcode_submit (int opcode, unsigned int counter)
+{
+ char inst[32];
+ char buffer[32];
+ int status;
+
+ strncpy (inst, opcode_str (opcode), 32);
+ inst[31] = '\0';
+
+ status = snprintf (buffer, 32, "N:%u", counter);
+ if ((status < 1) || (status >= 32))
+ return;
+
+ plugin_submit ("dns_opcode", inst, buffer);
+}
+
+static void dns_read (void)
+{
+ unsigned int keys[T_MAX];
+ unsigned int values[T_MAX];
+ int len;
+ int i;
+
+ counter_list_t *ptr;
+
+ pthread_mutex_lock (&traffic_mutex);
+ values[0] = tr_queries;
+ values[1] = tr_responses;
+ pthread_mutex_unlock (&traffic_mutex);
+ traffic_submit (values[0], values[1]);
+
+ pthread_mutex_lock (&qtype_mutex);
+ for (ptr = qtype_list, len = 0;
+ (ptr != NULL) && (len < T_MAX);
+ ptr = ptr->next, len++)
+ {
+ keys[len] = ptr->key;
+ values[len] = ptr->value;
+ }
+ pthread_mutex_unlock (&qtype_mutex);
+
+ for (i = 0; i < len; i++)
+ {
+ DBG ("qtype = %u; counter = %u;", keys[i], values[i]);
+ qtype_submit (keys[i], values[i]);
+ }
+
+ pthread_mutex_lock (&opcode_mutex);
+ for (ptr = opcode_list, len = 0;
+ (ptr != NULL) && (len < T_MAX);
+ ptr = ptr->next, len++)
+ {
+ keys[len] = ptr->key;
+ values[len] = ptr->value;
+ }
+ pthread_mutex_unlock (&opcode_mutex);
+
+ for (i = 0; i < len; i++)
+ {
+ DBG ("opcode = %u; counter = %u;", keys[i], values[i]);
+ opcode_submit (keys[i], values[i]);
+ }
+
+ pthread_mutex_lock (&rcode_mutex);
+ for (ptr = rcode_list, len = 0;
+ (ptr != NULL) && (len < T_MAX);
+ ptr = ptr->next, len++)
+ {
+ keys[len] = ptr->key;
+ values[len] = ptr->value;
+ }
+ pthread_mutex_unlock (&rcode_mutex);
+
+ for (i = 0; i < len; i++)
+ {
+ DBG ("rcode = %u; counter = %u;", keys[i], values[i]);
+ rcode_submit (keys[i], values[i]);
+ }
+}
+#else /* if !DNS_HAVE_READ */
+# define dns_read NULL
+#endif
+
+void module_register (void)
+{
+ plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
+ plugin_register ("dns_traffic", NULL, NULL, traffic_write);
+ plugin_register ("dns_qtype", NULL, NULL, qtype_write);
+ plugin_register ("dns_rcode", NULL, NULL, rcode_write);
+ plugin_register ("dns_opcode", NULL, NULL, opcode_write);
+#if DNS_HAVE_READ
+ cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
+#endif
+}
+
+#undef MODULE_NAME
--- /dev/null
+/**
+ * collectd - src/email.c
+ * Copyright (C) 2006 Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Sebastian Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This plugin communicates with a spam filter, a virus scanner or similar
+ * software using a UNIX socket and a very simple protocol:
+ *
+ * e-mail type (e.g. ham, spam, virus, ...) and size
+ * e:<type>:<bytes>
+ *
+ * spam score
+ * s:<value>
+ *
+ * successful spam checks
+ * c:<type1>[,<type2>,...]
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "configfile.h"
+
+#if HAVE_LIBPTHREAD
+# include <pthread.h>
+# define EMAIL_HAVE_READ 1
+#else
+# define EMAIL_HAVE_READ 0
+#endif
+
+#if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif /* HAVE_SYS_SELECT_H */
+
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+
+/* *sigh* glibc does not define UNIX_PATH_MAX in sys/un.h ... */
+#if HAVE_LINUX_UN_H
+# include <linux/un.h>
+#elif HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif /* HAVE_LINUX_UN_H | HAVE_SYS_UN_H */
+
+/* some systems (e.g. Darwin) seem to not define UNIX_PATH_MAX at all */
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
+#endif /* UNIX_PATH_MAX */
+
+#if HAVE_GRP_H
+# include <grp.h>
+#endif /* HAVE_GRP_H */
+
+#define MODULE_NAME "email"
+
+/* 256 bytes ought to be enough for anybody ;-) */
+#define BUFSIZE 256
+
+#ifndef COLLECTD_SOCKET_PREFIX
+# define COLLECTD_SOCKET_PREFIX "/tmp/.collectd-"
+#endif /* COLLECTD_SOCKET_PREFIX */
+
+#define SOCK_PATH COLLECTD_SOCKET_PREFIX"email"
+#define MAX_CONNS 5
+#define MAX_CONNS_LIMIT 16384
+
+#define log_err(...) syslog (LOG_ERR, MODULE_NAME": "__VA_ARGS__)
+#define log_warn(...) syslog (LOG_WARNING, MODULE_NAME": "__VA_ARGS__)
+
+/*
+ * Private data structures
+ */
+#if EMAIL_HAVE_READ
+/* linked list of email and check types */
+typedef struct type {
+ char *name;
+ int value;
+ struct type *next;
+} type_t;
+
+typedef struct {
+ type_t *head;
+ type_t *tail;
+} type_list_t;
+
+/* collector thread control information */
+typedef struct collector {
+ pthread_t thread;
+
+ /* socket descriptor of the current/last connection */
+ int socket;
+} collector_t;
+
+/* linked list of pending connections */
+typedef struct conn {
+ /* socket to read data from */
+ int socket;
+
+ /* buffer to read data to */
+ char *buffer;
+ int idx; /* current write position in buffer */
+ int length; /* length of the current line, i.e. index of '\0' */
+
+ struct conn *next;
+} conn_t;
+
+typedef struct {
+ conn_t *head;
+ conn_t *tail;
+} conn_list_t;
+#endif /* EMAIL_HAVE_READ */
+
+/*
+ * Private variables
+ */
+#if EMAIL_HAVE_READ
+/* valid configuration file keys */
+static char *config_keys[] =
+{
+ "SocketGroup",
+ "SocketPerms",
+ "MaxConns",
+ NULL
+};
+static int config_keys_num = 3;
+
+/* socket configuration */
+static char *sock_group = COLLECTD_GRP_NAME;
+static int sock_perms = S_IRWXU | S_IRWXG;
+static int max_conns = MAX_CONNS;
+
+/* state of the plugin */
+static int disabled = 0;
+
+/* thread managing "client" connections */
+static pthread_t connector;
+static int connector_socket;
+
+/* tell the collector threads that a new connection is available */
+static pthread_cond_t conn_available = PTHREAD_COND_INITIALIZER;
+
+/* connections that are waiting to be processed */
+static pthread_mutex_t conns_mutex = PTHREAD_MUTEX_INITIALIZER;
+static conn_list_t conns;
+
+/* tell the connector thread that a collector is available */
+static pthread_cond_t collector_available = PTHREAD_COND_INITIALIZER;
+
+/* collector threads */
+static collector_t **collectors;
+
+static pthread_mutex_t available_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int available_collectors;
+
+static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t count;
+
+static pthread_mutex_t size_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t size;
+
+static pthread_mutex_t score_mutex = PTHREAD_MUTEX_INITIALIZER;
+static double score;
+static int score_count;
+
+static pthread_mutex_t check_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t check;
+#endif /* EMAIL_HAVE_READ */
+
+#define COUNT_FILE "email/email-%s.rrd"
+static char *count_ds_def[] =
+{
+ "DS:count:GAUGE:"COLLECTD_HEARTBEAT":0:U",
+ NULL
+};
+static int count_ds_num = 1;
+
+#define SIZE_FILE "email/email_size-%s.rrd"
+static char *size_ds_def[] =
+{
+ "DS:size:GAUGE:"COLLECTD_HEARTBEAT":0:U",
+ NULL
+};
+static int size_ds_num = 1;
+
+#define SCORE_FILE "email/spam_score.rrd"
+static char *score_ds_def[] =
+{
+ "DS:score:GAUGE:"COLLECTD_HEARTBEAT":U:U",
+ NULL
+};
+static int score_ds_num = 1;
+
+#define CHECK_FILE "email/spam_check-%s.rrd"
+static char *check_ds_def[] =
+{
+ "DS:hits:GAUGE:"COLLECTD_HEARTBEAT":0:U",
+ NULL
+};
+static int check_ds_num = 1;
+
+#if EMAIL_HAVE_READ
+static int email_config (char *key, char *value)
+{
+ if (0 == strcasecmp (key, "SocketGroup")) {
+ sock_group = sstrdup (value);
+ }
+ else if (0 == strcasecmp (key, "SocketPerms")) {
+ /* the user is responsible for providing reasonable values */
+ sock_perms = (int)strtol (value, NULL, 8);
+ }
+ else if (0 == strcasecmp (key, "MaxConns")) {
+ long int tmp = strtol (value, NULL, 0);
+
+ if (tmp < 1) {
+ fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
+ "value %li, will use default %i.\n",
+ tmp, MAX_CONNS);
+ max_conns = MAX_CONNS;
+ }
+ else if (tmp > MAX_CONNS_LIMIT) {
+ fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
+ "value %li, will use hardcoded limit %i.\n",
+ tmp, MAX_CONNS_LIMIT);
+ max_conns = MAX_CONNS_LIMIT;
+ }
+ else {
+ max_conns = (int)tmp;
+ }
+ }
+ else {
+ return -1;
+ }
+ return 0;
+} /* static int email_config (char *, char *) */
+
+/* Increment the value of the given name in the given list by incr. */
+static void type_list_incr (type_list_t *list, char *name, int incr)
+{
+ if (NULL == list->head) {
+ list->head = (type_t *)smalloc (sizeof (type_t));
+
+ list->head->name = sstrdup (name);
+ list->head->value = incr;
+ list->head->next = NULL;
+
+ list->tail = list->head;
+ }
+ else {
+ type_t *ptr;
+
+ for (ptr = list->head; NULL != ptr; ptr = ptr->next) {
+ if (0 == strcmp (name, ptr->name))
+ break;
+ }
+
+ if (NULL == ptr) {
+ list->tail->next = (type_t *)smalloc (sizeof (type_t));
+ list->tail = list->tail->next;
+
+ list->tail->name = sstrdup (name);
+ list->tail->value = incr;
+ list->tail->next = NULL;
+ }
+ else {
+ ptr->value += incr;
+ }
+ }
+ return;
+} /* static void type_list_incr (type_list_t *, char *) */
+
+/* Read a single character from the socket. If an error occurs or end-of-file
+ * is reached return '\0'. */
+static char read_char (conn_t *src)
+{
+ char ret = '\0';
+
+ fd_set fdset;
+
+ FD_ZERO (&fdset);
+ FD_SET (src->socket, &fdset);
+
+ if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
+ log_err ("select() failed: %s", strerror (errno));
+ return '\0';
+ }
+
+ assert (FD_ISSET (src->socket, &fdset));
+
+ do {
+ ssize_t len = 0;
+
+ errno = 0;
+ if (0 > (len = read (src->socket, (void *)&ret, 1))) {
+ if (EINTR != errno) {
+ log_err ("read() failed: %s", strerror (errno));
+ return '\0';
+ }
+ }
+
+ if (0 == len)
+ return '\0';
+ } while (EINTR == errno);
+ return ret;
+} /* static char read_char (conn_t *) */
+
+/* Read a single line (terminated by '\n') from the the socket.
+ *
+ * The return value is zero terminated and does not contain any newline
+ * characters.
+ *
+ * If an error occurs or end-of-file is reached return NULL.
+ *
+ * IMPORTANT NOTE: If there is no newline character found in BUFSIZE
+ * characters of the input stream, the line will will be ignored! By
+ * definition we should not get any longer input lines, thus this is
+ * acceptable in this case ;-) */
+static char *read_line (conn_t *src)
+{
+ int i = 0;
+
+ assert ((BUFSIZE >= src->idx) && (src->idx >= 0));
+ assert ((src->idx > src->length) || (src->length == 0));
+
+ if (src->length > 0) { /* remove old line */
+ src->idx -= (src->length + 1);
+ memmove (src->buffer, src->buffer + src->length + 1, src->idx);
+ src->length = 0;
+ }
+
+ for (i = 0; i < src->idx; ++i) {
+ if ('\n' == src->buffer[i])
+ break;
+ }
+
+ if (i == src->idx) {
+ fd_set fdset;
+
+ ssize_t len = 0;
+
+ FD_ZERO (&fdset);
+ FD_SET (src->socket, &fdset);
+
+ if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
+ log_err ("select() failed: %s", strerror (errno));
+ return NULL;
+ }
+
+ assert (FD_ISSET (src->socket, &fdset));
+
+ do {
+ errno = 0;
+ if (0 > (len = read (src->socket,
+ (void *)(src->buffer + src->idx),
+ BUFSIZE - src->idx))) {
+ if (EINTR != errno) {
+ log_err ("read() failed: %s", strerror (errno));
+ return NULL;
+ }
+ }
+
+ if (0 == len)
+ return NULL;
+ } while (EINTR == errno);
+
+ src->idx += len;
+
+ for (i = src->idx - len; i < src->idx; ++i) {
+ if ('\n' == src->buffer[i])
+ break;
+ }
+
+ if (i == src->idx) {
+ src->length = 0;
+
+ if (BUFSIZE == src->idx) { /* no space left in buffer */
+ while ('\n' != read_char (src))
+ /* ignore complete line */;
+
+ src->idx = 0;
+ }
+ return read_line (src);
+ }
+ }
+
+ src->buffer[i] = '\0';
+ src->length = i;
+
+ return src->buffer;
+} /* static char *read_line (conn_t *) */
+
+static void *collect (void *arg)
+{
+ collector_t *this = (collector_t *)arg;
+
+ char *buffer = (char *)smalloc (BUFSIZE);
+
+ while (1) {
+ int loop = 1;
+
+ conn_t *connection;
+
+ pthread_mutex_lock (&conns_mutex);
+
+ while (NULL == conns.head) {
+ pthread_cond_wait (&conn_available, &conns_mutex);
+ }
+
+ connection = conns.head;
+ conns.head = conns.head->next;
+
+ if (NULL == conns.head) {
+ conns.tail = NULL;
+ }
+
+ this->socket = connection->socket;
+
+ pthread_mutex_unlock (&conns_mutex);
+
+ connection->buffer = buffer;
+ connection->idx = 0;
+ connection->length = 0;
+
+ { /* put the socket in non-blocking mode */
+ int flags = 0;
+
+ errno = 0;
+ if (-1 == fcntl (connection->socket, F_GETFL, &flags)) {
+ log_err ("fcntl() failed: %s", strerror (errno));
+ loop = 0;
+ }
+
+ errno = 0;
+ if (-1 == fcntl (connection->socket, F_SETFL, flags | O_NONBLOCK)) {
+ log_err ("fcntl() failed: %s", strerror (errno));
+ loop = 0;
+ }
+ }
+
+ while (loop) {
+ char *line = read_line (connection);
+
+ if (NULL == line) {
+ loop = 0;
+ break;
+ }
+
+ if (':' != line[1]) {
+ log_err ("syntax error in line '%s'", line);
+ continue;
+ }
+
+ if ('e' == line[0]) { /* e:<type>:<bytes> */
+ char *ptr = NULL;
+ char *type = strtok_r (line + 2, ":", &ptr);
+ char *tmp = strtok_r (NULL, ":", &ptr);
+ int bytes = 0;
+
+ if (NULL == tmp) {
+ log_err ("syntax error in line '%s'", line);
+ continue;
+ }
+
+ bytes = atoi (tmp);
+
+ pthread_mutex_lock (&count_mutex);
+ type_list_incr (&count, type, 1);
+ pthread_mutex_unlock (&count_mutex);
+
+ pthread_mutex_lock (&size_mutex);
+ type_list_incr (&size, type, bytes);
+ pthread_mutex_unlock (&size_mutex);
+ }
+ else if ('s' == line[0]) { /* s:<value> */
+ pthread_mutex_lock (&score_mutex);
+ score = (score * (double)score_count + atof (line + 2))
+ / (double)(score_count + 1);
+ ++score_count;
+ pthread_mutex_unlock (&score_mutex);
+ }
+ else if ('c' == line[0]) { /* c:<type1>[,<type2>,...] */
+ char *ptr = NULL;
+ char *type = strtok_r (line + 2, ",", &ptr);
+
+ do {
+ pthread_mutex_lock (&check_mutex);
+ type_list_incr (&check, type, 1);
+ pthread_mutex_unlock (&check_mutex);
+ } while (NULL != (type = strtok_r (NULL, ",", &ptr)));
+ }
+ else {
+ log_err ("unknown type '%c'", line[0]);
+ }
+ } /* while (loop) */
+
+ close (connection->socket);
+
+ free (connection);
+
+ pthread_mutex_lock (&available_mutex);
+ ++available_collectors;
+ pthread_mutex_unlock (&available_mutex);
+
+ pthread_cond_signal (&collector_available);
+ } /* while (1) */
+
+ free (buffer);
+ pthread_exit ((void *)0);
+} /* static void *collect (void *) */
+
+static void *open_connection (void *arg)
+{
+ struct sockaddr_un addr;
+
+ /* create UNIX socket */
+ errno = 0;
+ if (-1 == (connector_socket = socket (PF_UNIX, SOCK_STREAM, 0))) {
+ disabled = 1;
+ log_err ("socket() failed: %s", strerror (errno));
+ pthread_exit ((void *)1);
+ }
+
+ addr.sun_family = AF_UNIX;
+
+ strncpy (addr.sun_path, SOCK_PATH, (size_t)(UNIX_PATH_MAX - 1));
+ addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
+ unlink (addr.sun_path);
+
+ errno = 0;
+ if (-1 == bind (connector_socket, (struct sockaddr *)&addr,
+ offsetof (struct sockaddr_un, sun_path)
+ + strlen(addr.sun_path))) {
+ disabled = 1;
+ log_err ("bind() failed: %s", strerror (errno));
+ pthread_exit ((void *)1);
+ }
+
+ errno = 0;
+ if (-1 == listen (connector_socket, 5)) {
+ disabled = 1;
+ log_err ("listen() failed: %s", strerror (errno));
+ pthread_exit ((void *)1);
+ }
+
+ if ((uid_t)0 == geteuid ()) {
+ struct group *grp;
+
+ errno = 0;
+ if (NULL != (grp = getgrnam (sock_group))) {
+ errno = 0;
+ if (0 != chown (SOCK_PATH, (uid_t)-1, grp->gr_gid)) {
+ log_warn ("chown() failed: %s", strerror (errno));
+ }
+ }
+ else {
+ log_warn ("getgrnam() failed: %s", strerror (errno));
+ }
+ }
+ else {
+ log_warn ("not running as root");
+ }
+
+ errno = 0;
+ if (0 != chmod (SOCK_PATH, sock_perms)) {
+ log_warn ("chmod() failed: %s", strerror (errno));
+ }
+
+ { /* initialize collector threads */
+ int i = 0;
+ int err = 0;
+
+ pthread_attr_t ptattr;
+
+ conns.head = NULL;
+ conns.tail = NULL;
+
+ pthread_attr_init (&ptattr);
+ pthread_attr_setdetachstate (&ptattr, PTHREAD_CREATE_DETACHED);
+
+ available_collectors = max_conns;
+
+ collectors =
+ (collector_t **)smalloc (max_conns * sizeof (collector_t *));
+
+ for (i = 0; i < max_conns; ++i) {
+ collectors[i] = (collector_t *)smalloc (sizeof (collector_t));
+ collectors[i]->socket = 0;
+
+ if (0 != (err = pthread_create (&collectors[i]->thread, &ptattr,
+ collect, collectors[i]))) {
+ log_err ("pthread_create() failed: %s", strerror (err));
+ }
+ }
+
+ pthread_attr_destroy (&ptattr);
+ }
+
+ while (1) {
+ int remote = 0;
+
+ conn_t *connection;
+
+ pthread_mutex_lock (&available_mutex);
+
+ while (0 == available_collectors) {
+ pthread_cond_wait (&collector_available, &available_mutex);
+ }
+
+ --available_collectors;
+
+ pthread_mutex_unlock (&available_mutex);
+
+ do {
+ errno = 0;
+ if (-1 == (remote = accept (connector_socket, NULL, NULL))) {
+ if (EINTR != errno) {
+ disabled = 1;
+ log_err ("accept() failed: %s", strerror (errno));
+ pthread_exit ((void *)1);
+ }
+ }
+ } while (EINTR == errno);
+
+ connection = (conn_t *)smalloc (sizeof (conn_t));
+
+ connection->socket = remote;
+ connection->next = NULL;
+
+ pthread_mutex_lock (&conns_mutex);
+
+ if (NULL == conns.head) {
+ conns.head = connection;
+ conns.tail = connection;
+ }
+ else {
+ conns.tail->next = connection;
+ conns.tail = conns.tail->next;
+ }
+
+ pthread_mutex_unlock (&conns_mutex);
+
+ pthread_cond_signal (&conn_available);
+ }
+ pthread_exit ((void *)0);
+} /* static void *open_connection (void *) */
+#endif /* EMAIL_HAVE_READ */
+
+static void email_init (void)
+{
+#if EMAIL_HAVE_READ
+ int err = 0;
+
+ if (0 != (err = pthread_create (&connector, NULL,
+ open_connection, NULL))) {
+ disabled = 1;
+ log_err ("pthread_create() failed: %s", strerror (err));
+ return;
+ }
+#endif /* EMAIL_HAVE_READ */
+ return;
+} /* static void email_init (void) */
+
+#if EMAIL_HAVE_READ
+static void email_shutdown (void)
+{
+ int i = 0;
+
+ if (disabled)
+ return;
+
+ pthread_kill (connector, SIGTERM);
+ close (connector_socket);
+
+ /* don't allow any more connections to be processed */
+ pthread_mutex_lock (&conns_mutex);
+
+ for (i = 0; i < max_conns; ++i) {
+ pthread_kill (collectors[i]->thread, SIGTERM);
+ close (collectors[i]->socket);
+ }
+
+ pthread_mutex_unlock (&conns_mutex);
+
+ unlink (SOCK_PATH);
+ return;
+} /* static void email_shutdown (void) */
+#endif /* EMAIL_HAVE_READ */
+
+static void count_write (char *host, char *inst, char *val)
+{
+ char file[BUFSIZE] = "";
+ int len = 0;
+
+ len = snprintf (file, BUFSIZE, COUNT_FILE, inst);
+ if ((len < 0) || (len >= BUFSIZE))
+ return;
+
+ rrd_update_file (host, file, val, count_ds_def, count_ds_num);
+ return;
+} /* static void email_write (char *host, char *inst, char *val) */
+
+static void size_write (char *host, char *inst, char *val)
+{
+ char file[BUFSIZE] = "";
+ int len = 0;
+
+ len = snprintf (file, BUFSIZE, SIZE_FILE, inst);
+ if ((len < 0) || (len >= BUFSIZE))
+ return;
+
+ rrd_update_file (host, file, val, size_ds_def, size_ds_num);
+ return;
+} /* static void size_write (char *host, char *inst, char *val) */
+
+static void score_write (char *host, char *inst, char *val)
+{
+ rrd_update_file (host, SCORE_FILE, val, score_ds_def, score_ds_num);
+ return;
+} /* static void score_write (char *host, char *inst, char *val) */
+
+static void check_write (char *host, char *inst, char *val)
+{
+ char file[BUFSIZE] = "";
+ int len = 0;
+
+ len = snprintf (file, BUFSIZE, CHECK_FILE, inst);
+ if ((len < 0) || (len >= BUFSIZE))
+ return;
+
+ rrd_update_file (host, file, val, check_ds_def, check_ds_num);
+ return;
+} /* static void check_write (char *host, char *inst, char *val) */
+
+#if EMAIL_HAVE_READ
+static void type_submit (char *plugin, char *inst, int value)
+{
+ char buf[BUFSIZE] = "";
+ int len = 0;
+
+ len = snprintf (buf, BUFSIZE, "%u:%i", (unsigned int)curtime, value);
+ if ((len < 0) || (len >= BUFSIZE))
+ return;
+
+ plugin_submit (plugin, inst, buf);
+ return;
+} /* static void type_submit (char *, char *, int) */
+
+static void score_submit (double value)
+{
+ char buf[BUFSIZE] = "";
+ int len = 0;
+
+ len = snprintf (buf, BUFSIZE, "%u:%.2f", (unsigned int)curtime, value);
+ if ((len < 0) || (len >= BUFSIZE))
+ return;
+
+ plugin_submit ("email_spam_score", NULL, buf);
+ return;
+} /* static void score_submit (double) */
+
+static void email_read (void)
+{
+ type_t *ptr;
+
+ if (disabled)
+ return;
+
+ pthread_mutex_lock (&count_mutex);
+
+ for (ptr = count.head; NULL != ptr; ptr = ptr->next) {
+ type_submit ("email_count", ptr->name, ptr->value);
+ ptr->value = 0;
+ }
+
+ pthread_mutex_unlock (&count_mutex);
+
+ pthread_mutex_lock (&size_mutex);
+
+ for (ptr = size.head; NULL != ptr; ptr = ptr->next) {
+ type_submit ("email_size", ptr->name, ptr->value);
+ ptr->value = 0;
+ }
+
+ pthread_mutex_unlock (&size_mutex);
+
+ pthread_mutex_lock (&score_mutex);
+
+ score_submit (score);
+ score = 0.0;
+ score_count = 0;
+
+ pthread_mutex_unlock (&score_mutex);
+
+ pthread_mutex_lock (&check_mutex);
+
+ for (ptr = check.head; NULL != ptr; ptr = ptr->next) {
+ type_submit ("email_spam_check", ptr->name, ptr->value);
+ ptr->value = 0;
+ }
+
+ pthread_mutex_unlock (&check_mutex);
+ return;
+} /* static void read (void) */
+#else /* if !EMAIL_HAVE_READ */
+# define email_read NULL
+#endif
+
+void module_register (void)
+{
+ plugin_register (MODULE_NAME, email_init, email_read, NULL);
+ plugin_register ("email_count", NULL, NULL, count_write);
+ plugin_register ("email_size", NULL, NULL, size_write);
+ plugin_register ("email_spam_score", NULL, NULL, score_write);
+ plugin_register ("email_spam_check", NULL, NULL, check_write);
+#if EMAIL_HAVE_READ
+ plugin_register_shutdown (MODULE_NAME, email_shutdown);
+ cf_register (MODULE_NAME, email_config, config_keys, config_keys_num);
+#endif /* EMAIL_HAVE_READ */
+ return;
+} /* void module_register (void) */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
(struct sockaddr *) ph->addr, ph->addrlen);
if (ret < 0)
+ {
+#if defined(EHOSTUNREACH)
+ if (errno == EHOSTUNREACH)
+ return (0);
+#endif
+#if defined(ENETUNREACH)
+ if (errno == ENETUNREACH)
+ return (0);
+#endif
ping_set_error (obj, "sendto", strerror (errno));
+ }
return (ret);
}
--- /dev/null
+/**
+ * collectd - src/multimeter.c
+ * Copyright (C) 2005,2006 Peter Holik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Peter Holik <peter at holik.at>
+ *
+ * Used multimeter: Metex M-4650CR
+ *
+ **/
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#define MODULE_NAME "multimeter"
+
+static char *multimeter_file = "multimeter.rrd";
+
+static char *ds_def[] =
+{
+ "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
+ NULL
+};
+static int ds_num = 1;
+
+static int fd = -1;
+
+static int multimeter_timeval_sub (struct timeval *tv1, struct timeval *tv2,
+ struct timeval *res)
+{
+ if ((tv1->tv_sec < tv2->tv_sec) ||
+ ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec < tv2->tv_usec)))
+ return (-1);
+
+ res->tv_sec = tv1->tv_sec - tv2->tv_sec;
+ res->tv_usec = tv1->tv_usec - tv2->tv_usec;
+
+ assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec > 0)));
+
+ while (res->tv_usec < 0)
+ {
+ res->tv_usec += 1000000;
+ res->tv_sec--;
+ }
+ return (0);
+}
+#define LINE_LENGTH 14
+static int multimeter_read_value(double *value)
+{
+ int retry = 3; /* sometimes we receive garbadge */
+
+ do
+ {
+ struct timeval time_end;
+
+ tcflush(fd, TCIFLUSH);
+
+ if (gettimeofday (&time_end, NULL) < 0)
+ {
+ syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s",
+ strerror (errno));
+ return (-1);
+ }
+ time_end.tv_sec++;
+
+ while (1)
+ {
+ char buf[LINE_LENGTH];
+ char *range;
+ int status;
+ fd_set rfds;
+ struct timeval timeout;
+ struct timeval time_now;
+
+ write(fd, "D", 1);
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ if (gettimeofday (&time_now, NULL) < 0)
+ {
+ syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s",
+ strerror (errno));
+ return (-1);
+ }
+ if (multimeter_timeval_sub (&time_end, &time_now, &timeout) == -1)
+ break;
+
+ status = select(fd+1, &rfds, NULL, NULL, &timeout);
+
+ if (status > 0) /* usually we succeed */
+ {
+ status = read(fd, buf, LINE_LENGTH);
+
+ if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+ continue;
+
+ /* Format: "DC 00.000mV \r" */
+ if (status > 0 && status == LINE_LENGTH)
+ {
+ *value = strtod(buf + 2, &range);
+
+ if ( range > (buf + 6) )
+ {
+ range = buf + 9;
+
+ switch ( *range )
+ {
+ case 'p': *value *= 1.0E-12; break;
+ case 'n': *value *= 1.0E-9; break;
+ case 'u': *value *= 1.0E-6; break;
+ case 'm': *value *= 1.0E-3; break;
+ case 'k': *value *= 1.0E3; break;
+ case 'M': *value *= 1.0E6; break;
+ case 'G': *value *= 1.0E9; break;
+ }
+ }
+ else
+ return (-1); /* Overflow */
+
+ return (0); /* value received */
+ }
+ else break;
+ }
+ else if (!status) /* Timeout */
+ {
+ break;
+ }
+ else if ((status == -1) && ((errno == EAGAIN) || (errno == EINTR)))
+ {
+ continue;
+ }
+ else /* status == -1 */
+ {
+ syslog (LOG_ERR, MODULE_NAME": select failed: %s",
+ strerror (errno));
+ break;
+ }
+ }
+ } while (--retry);
+
+ return (-2); /* no value received */
+}
+
+static void multimeter_init (void)
+{
+ int i;
+ char device[] = "/dev/ttyS ";
+
+ for (i = 0; i < 10; i++)
+ {
+ device[strlen(device)-1] = i + '0';
+
+ if ((fd = open(device, O_RDWR | O_NOCTTY)) > 0)
+ {
+ struct termios tios;
+ int rts = TIOCM_RTS;
+ double value;
+
+ tios.c_cflag = B1200 | CS7 | CSTOPB | CREAD | CLOCAL;
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VTIME] = 3;
+ tios.c_cc[VMIN] = LINE_LENGTH;
+
+ tcflush(fd, TCIFLUSH);
+ tcsetattr(fd, TCSANOW, &tios);
+ ioctl(fd, TIOCMBIC, &rts);
+
+ if (multimeter_read_value(&value) < -1)
+ {
+ close(fd);
+ fd = -1;
+ }
+ else
+ {
+ syslog (LOG_INFO, MODULE_NAME" found (%s)", device);
+ return;
+ }
+ }
+ }
+ syslog (LOG_ERR, MODULE_NAME" not found");
+}
+#undef LINE_LENGTH
+
+static void multimeter_write (char *host, char *inst, char *val)
+{
+ rrd_update_file (host, multimeter_file, val, ds_def, ds_num);
+}
+#define BUFSIZE 128
+static void multimeter_submit (double *value)
+{
+ char buf[BUFSIZE];
+
+ if (snprintf (buf, BUFSIZE, "%u:%f", (unsigned int) curtime, *value) >= BUFSIZE)
+ return;
+
+ plugin_submit (MODULE_NAME, NULL, buf);
+}
+#undef BUFSIZE
+
+static void multimeter_read (void)
+{
+ double value;
+
+ if (fd > -1 && !(multimeter_read_value(&value)))
+ multimeter_submit (&value);
+}
+
+void module_register (void)
+{
+ plugin_register (MODULE_NAME, multimeter_init, multimeter_read, multimeter_write);
+}
+
+#undef MODULE_NAME
void (*init) (void);
void (*read) (void);
void (*write) (char *host, char *inst, char *val);
+ void (*shutdown) (void);
struct plugin *next;
} plugin_t;
/*
* Call `read' on all plugins (if given)
*/
-void plugin_read_all (void)
+void plugin_read_all (const int *loop)
{
plugin_t *p;
- for (p = first_plugin; p != NULL; p = p->next)
+ for (p = first_plugin; (*loop == 0) && (p != NULL); p = p->next)
if (p->read != NULL)
(*p->read) ();
}
/*
+ * Call `shutdown' on all plugins (if given)
+ */
+void plugin_shutdown_all (void)
+{
+ plugin_t *p;
+
+ for (p = first_plugin; NULL != p; p = p->next)
+ if (NULL != p->shutdown)
+ (*p->shutdown) ();
+ return;
+}
+
+/*
* Add plugin to the linked list of registered plugins.
*/
void plugin_register (char *type,
p->read = read;
p->write = write;
+ p->shutdown = NULL;
+
p->next = first_plugin;
first_plugin = p;
}
/*
+ * Register the shutdown function (optional).
+ */
+int plugin_register_shutdown (char *type, void (*shutdown) (void))
+{
+ plugin_t *p = plugin_search (type);
+
+ if (NULL == p)
+ return -1;
+
+ p->shutdown = shutdown;
+ return 0;
+}
+
+/*
* Send received data back to the plugin/module which will append DS
* definitions and pass it on to ``rrd_update_file''.
*/
else
plugin_write (NULL, type, inst, val);
}
+
+void plugin_complain (int level, complain_t *c, const char *format, ...)
+{
+ char message[512];
+ va_list ap;
+ int step;
+
+ if (c->delay > 0)
+ {
+ c->delay--;
+ return;
+ }
+
+ step = atoi (COLLECTD_STEP);
+ assert (step > 0);
+
+ if (c->interval < step)
+ c->interval = step;
+ else
+ c->interval *= 2;
+
+ if (c->interval > 86400)
+ c->interval = 86400;
+
+ c->delay = c->interval / step;
+
+ va_start (ap, format);
+ vsnprintf (message, 512, format, ap);
+ message[511] = '\0';
+ va_end (ap);
+
+ syslog (level, message);
+}
+
+void plugin_relief (int level, complain_t *c, const char *format, ...)
+{
+ char message[512];
+ va_list ap;
+
+ if (c->interval == 0)
+ return;
+
+ c->interval = 0;
+
+ va_start (ap, format);
+ vsnprintf (message, 512, format, ap);
+ message[511] = '\0';
+ va_end (ap);
+
+ syslog (level, message);
+}
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
/**
* collectd - src/plugin.h
* Copyright (C) 2005,2006 Florian octo Forster
* Florian octo Forster <octo at verplant.org>
**/
-#ifndef PLUGIN_H
-#define PLUGIN_H
+/*
+ *
+ */
+typedef struct complain_s
+{
+ unsigned int interval; /* how long we wait for reporting this error again */
+ unsigned int delay; /* how many more iterations we still need to wait */
+} complain_t;
/*
* NAME
int plugin_load_all (char *dir);
void plugin_init_all (void);
-void plugin_read_all (void);
+void plugin_read_all (const int *loop);
+
+void plugin_shutdown_all (void);
void plugin_register (char *type,
void (*init) (void),
void (*read) (void),
void (*write) (char *, char *, char *));
+int plugin_register_shutdown (char *, void (*) (void));
+
/*
* NAME
* plugin_write
void plugin_submit (char *type, char *inst, char *val);
+
+void plugin_complain (int level, complain_t *c, const char *format, ...);
+void plugin_relief (int level, complain_t *c, const char *format, ...);
+
#endif /* PLUGIN_H */
#include "common.h"
#include "plugin.h"
#include "utils_debug.h"
+#include "configfile.h"
/* Include header files for the mach system, if they exist.. */
-#if HAVE_MACH_MACH_INIT_H
-# include <mach/mach_init.h>
-#endif
-#if HAVE_MACH_HOST_PRIV_H
-# include <mach/host_priv.h>
-#endif
-#if HAVE_MACH_MACH_ERROR_H
-# include <mach/mach_error.h>
-#endif
-#if HAVE_MACH_MACH_HOST_H
-# include <mach/mach_host.h>
-#endif
-#if HAVE_MACH_MACH_PORT_H
-# include <mach/mach_port.h>
-#endif
-#if HAVE_MACH_MACH_TYPES_H
-# include <mach/mach_types.h>
-#endif
-#if HAVE_MACH_MESSAGE_H
-# include <mach/message.h>
-#endif
-#if HAVE_MACH_PROCESSOR_SET_H
-# include <mach/processor_set.h>
-#endif
-#if HAVE_MACH_TASK_H
-# include <mach/task.h>
-#endif
-#if HAVE_MACH_THREAD_ACT_H
-# include <mach/thread_act.h>
-#endif
-#if HAVE_MACH_VM_REGION_H
-# include <mach/vm_region.h>
-#endif
-#if HAVE_MACH_VM_MAP_H
-# include <mach/vm_map.h>
-#endif
-#if HAVE_MACH_VM_PROT_H
-# include <mach/vm_prot.h>
-#endif
+#if HAVE_THREAD_INFO
+# if HAVE_MACH_MACH_INIT_H
+# include <mach/mach_init.h>
+# endif
+# if HAVE_MACH_HOST_PRIV_H
+# include <mach/host_priv.h>
+# endif
+# if HAVE_MACH_MACH_ERROR_H
+# include <mach/mach_error.h>
+# endif
+# if HAVE_MACH_MACH_HOST_H
+# include <mach/mach_host.h>
+# endif
+# if HAVE_MACH_MACH_PORT_H
+# include <mach/mach_port.h>
+# endif
+# if HAVE_MACH_MACH_TYPES_H
+# include <mach/mach_types.h>
+# endif
+# if HAVE_MACH_MESSAGE_H
+# include <mach/message.h>
+# endif
+# if HAVE_MACH_PROCESSOR_SET_H
+# include <mach/processor_set.h>
+# endif
+# if HAVE_MACH_TASK_H
+# include <mach/task.h>
+# endif
+# if HAVE_MACH_THREAD_ACT_H
+# include <mach/thread_act.h>
+# endif
+# if HAVE_MACH_VM_REGION_H
+# include <mach/vm_region.h>
+# endif
+# if HAVE_MACH_VM_MAP_H
+# include <mach/vm_map.h>
+# endif
+# if HAVE_MACH_VM_PROT_H
+# include <mach/vm_prot.h>
+# endif
+# if HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+# endif
+/* #endif HAVE_THREAD_INFO */
+
+#elif KERNEL_LINUX
+# if HAVE_LINUX_CONFIG_H
+# include <linux/config.h>
+# endif
+# ifndef CONFIG_HZ
+# define CONFIG_HZ 100
+# endif
+#endif /* KERNEL_LINUX */
#define MODULE_NAME "processes"
#define BUFSIZE 256
-static char *ps_file = "processes.rrd";
-
-static char *ds_def[] =
+static char *processes_file = "processes.rrd";
+static char *processes_ds_def[] =
{
"DS:running:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
"DS:sleeping:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
"DS:blocked:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
NULL
};
-static int ds_num = 6;
+static int processes_ds_num = 6;
+
+static char *ps_rss_file = "processes/ps_rss-%s.rrd";
+static char *ps_rss_ds_def[] =
+{
+ /* max = 2^63 - 1 */
+ "DS:byte:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
+ NULL
+};
+static int ps_rss_ds_num = 1;
+
+static char *ps_cputime_file = "processes/ps_cputime-%s.rrd";
+static char *ps_cputime_ds_def[] =
+{
+ /* 1 second in user-mode per second ought to be enough.. */
+ "DS:user:COUNTER:"COLLECTD_HEARTBEAT":0:1000000",
+ "DS:syst:COUNTER:"COLLECTD_HEARTBEAT":0:1000000",
+ NULL
+};
+static int ps_cputime_ds_num = 2;
+
+static char *ps_count_file = "processes/ps_count-%s.rrd";
+static char *ps_count_ds_def[] =
+{
+ "DS:processes:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
+ "DS:threads:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
+ NULL
+};
+static int ps_count_ds_num = 2;
+
+static char *ps_pagefaults_file = "processes/ps_pagefaults-%s.rrd";
+static char *ps_pagefaults_ds_def[] =
+{
+ /* max = 2^63 - 1 */
+ "DS:minflt:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
+ "DS:majflt:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
+ NULL
+};
+static int ps_pagefaults_ds_num = 2;
+
+static char *config_keys[] =
+{
+ "Process",
+ NULL
+};
+static int config_keys_num = 1;
+
+typedef struct procstat_entry_s
+{
+ unsigned long id;
+ unsigned long age;
+
+ unsigned long num_proc;
+ unsigned long num_lwp;
+ unsigned long vmem_rss;
+
+ unsigned long vmem_minflt;
+ unsigned long vmem_majflt;
+ unsigned long vmem_minflt_counter;
+ unsigned long vmem_majflt_counter;
+
+ unsigned long cpu_user;
+ unsigned long cpu_system;
+ unsigned long cpu_user_counter;
+ unsigned long cpu_system_counter;
+
+ struct procstat_entry_s *next;
+} procstat_entry_t;
+
+#define PROCSTAT_NAME_LEN 256
+typedef struct procstat
+{
+ char name[PROCSTAT_NAME_LEN];
+
+ unsigned long num_proc;
+ unsigned long num_lwp;
+ unsigned long vmem_rss;
+
+ unsigned long vmem_minflt_counter;
+ unsigned long vmem_majflt_counter;
+
+ unsigned long cpu_user_counter;
+ unsigned long cpu_system_counter;
+
+ struct procstat *next;
+ struct procstat_entry_s *instances;
+} procstat_t;
+
+static procstat_t *list_head_g = NULL;
#if HAVE_THREAD_INFO
static mach_port_t port_host_self;
/* #endif HAVE_THREAD_INFO */
#elif KERNEL_LINUX
-/* No global variables */
+static long pagesize_g;
#endif /* KERNEL_LINUX */
+#if HAVE_THREAD_INFO | KERNEL_LINUX
+static void ps_list_register (const char *name)
+{
+ procstat_t *new;
+ procstat_t *ptr;
+
+ if ((new = (procstat_t *) malloc (sizeof (procstat_t))) == NULL)
+ return;
+ memset (new, 0, sizeof (procstat_t));
+ strncpy (new->name, name, PROCSTAT_NAME_LEN);
+
+ for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
+ {
+ if (strcmp (ptr->name, name) == 0)
+ return;
+ if (ptr->next == NULL)
+ break;
+ }
+
+ if (ptr == NULL)
+ list_head_g = new;
+ else
+ ptr->next = new;
+}
+
+static procstat_t *ps_list_search (const char *name)
+{
+ procstat_t *ptr;
+
+ for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
+ if (strcmp (ptr->name, name) == 0)
+ break;
+
+ return (ptr);
+}
+
+static void ps_list_add (const char *name, procstat_entry_t *entry)
+{
+ procstat_t *ps;
+ procstat_entry_t *pse;
+
+ if (entry->id == 0)
+ return;
+
+ if ((ps = ps_list_search (name)) == NULL)
+ return;
+
+ for (pse = ps->instances; pse != NULL; pse = pse->next)
+ if ((pse->id == entry->id) || (pse->next == NULL))
+ break;
+
+ if ((pse == NULL) || (pse->id != entry->id))
+ {
+ procstat_entry_t *new;
+
+ new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
+ if (new == NULL)
+ return;
+ memset (new, 0, sizeof (procstat_entry_t));
+ new->id = entry->id;
+
+ if (pse == NULL)
+ ps->instances = new;
+ else
+ pse->next = new;
+
+ pse = new;
+ }
+
+ pse->age = 0;
+ pse->num_proc = entry->num_proc;
+ pse->num_lwp = entry->num_lwp;
+ pse->vmem_rss = entry->vmem_rss;
+
+ ps->num_proc += pse->num_proc;
+ ps->num_lwp += pse->num_lwp;
+ ps->vmem_rss += pse->vmem_rss;
+
+ if ((entry->vmem_minflt_counter == 0)
+ && (entry->vmem_majflt_counter == 0))
+ {
+ pse->vmem_minflt_counter += entry->vmem_minflt;
+ pse->vmem_minflt = entry->vmem_minflt;
+
+ pse->vmem_majflt_counter += entry->vmem_majflt;
+ pse->vmem_majflt = entry->vmem_majflt;
+ }
+ else
+ {
+ if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
+ {
+ pse->vmem_minflt = entry->vmem_minflt_counter
+ + (ULONG_MAX - pse->vmem_minflt_counter);
+ }
+ else
+ {
+ pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
+ }
+ pse->vmem_minflt_counter = entry->vmem_minflt_counter;
+
+ if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
+ {
+ pse->vmem_majflt = entry->vmem_majflt_counter
+ + (ULONG_MAX - pse->vmem_majflt_counter);
+ }
+ else
+ {
+ pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
+ }
+ pse->vmem_majflt_counter = entry->vmem_majflt_counter;
+ }
+
+ ps->vmem_minflt_counter += pse->vmem_minflt;
+ ps->vmem_majflt_counter += pse->vmem_majflt;
+
+ if ((entry->cpu_user_counter == 0)
+ && (entry->cpu_system_counter == 0))
+ {
+ pse->cpu_user_counter += entry->cpu_user;
+ pse->cpu_user = entry->cpu_user;
+
+ pse->cpu_system_counter += entry->cpu_system;
+ pse->cpu_system = entry->cpu_system;
+ }
+ else
+ {
+ if (entry->cpu_user_counter < pse->cpu_user_counter)
+ {
+ pse->cpu_user = entry->cpu_user_counter
+ + (ULONG_MAX - pse->cpu_user_counter);
+ }
+ else
+ {
+ pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
+ }
+ pse->cpu_user_counter = entry->cpu_user_counter;
+
+ if (entry->cpu_system_counter < pse->cpu_system_counter)
+ {
+ pse->cpu_system = entry->cpu_system_counter
+ + (ULONG_MAX - pse->cpu_system_counter);
+ }
+ else
+ {
+ pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
+ }
+ pse->cpu_system_counter = entry->cpu_system_counter;
+ }
+
+ ps->cpu_user_counter += pse->cpu_user;
+ ps->cpu_system_counter += pse->cpu_system;
+}
+
+static void ps_list_reset (void)
+{
+ procstat_t *ps;
+ procstat_entry_t *pse;
+ procstat_entry_t *pse_prev;
+
+ for (ps = list_head_g; ps != NULL; ps = ps->next)
+ {
+ ps->num_proc = 0;
+ ps->num_lwp = 0;
+ ps->vmem_rss = 0;
+
+ pse_prev = NULL;
+ pse = ps->instances;
+ while (pse != NULL)
+ {
+ if (pse->age > 10)
+ {
+ DBG ("Removing this procstat entry cause it's too old: "
+ "id = %lu; name = %s;",
+ pse->id, ps->name);
+
+ if (pse_prev == NULL)
+ {
+ ps->instances = pse->next;
+ free (pse);
+ pse = ps->instances;
+ }
+ else
+ {
+ pse_prev->next = pse->next;
+ free (pse);
+ pse = pse_prev->next;
+ }
+ }
+ else
+ {
+ pse->age++;
+ pse_prev = pse;
+ pse = pse->next;
+ }
+ } /* while (pse != NULL) */
+ } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
+}
+#endif /* HAVE_THREAD_INFO | KERNEL_LINUX */
+
+static int ps_config (char *key, char *value)
+{
+ if (strcasecmp (key, "Process") == 0)
+ {
+ ps_list_register (value);
+ }
+ else
+ {
+ return (-1);
+ }
+
+ return (0);
+}
+
static void ps_init (void)
{
#if HAVE_THREAD_INFO
/* #endif HAVE_THREAD_INFO */
#elif KERNEL_LINUX
- /* No init */
+ pagesize_g = sysconf(_SC_PAGESIZE);
+ DBG ("pagesize_g = %li; CONFIG_HZ = %i;",
+ pagesize_g, CONFIG_HZ);
#endif /* KERNEL_LINUX */
return;
static void ps_write (char *host, char *inst, char *val)
{
- rrd_update_file (host, ps_file, val, ds_def, ds_num);
+ rrd_update_file (host, processes_file, val,
+ processes_ds_def, processes_ds_num);
+}
+
+static void ps_rss_write (char *host, char *inst, char *val)
+{
+ char filename[256];
+ int status;
+
+ status = snprintf (filename, 256, ps_rss_file, inst);
+ if ((status < 1) || (status >= 256))
+ return;
+
+ rrd_update_file (host, filename, val, ps_rss_ds_def, ps_rss_ds_num);
+}
+
+static void ps_cputime_write (char *host, char *inst, char *val)
+{
+ char filename[256];
+ int status;
+
+ status = snprintf (filename, 256, ps_cputime_file, inst);
+ if ((status < 1) || (status >= 256))
+ return;
+
+ DBG ("host = %s; filename = %s; val = %s;",
+ host, filename, val);
+ rrd_update_file (host, filename, val,
+ ps_cputime_ds_def, ps_cputime_ds_num);
+}
+
+static void ps_count_write (char *host, char *inst, char *val)
+{
+ char filename[256];
+ int status;
+
+ status = snprintf (filename, 256, ps_count_file, inst);
+ if ((status < 1) || (status >= 256))
+ return;
+
+ DBG ("host = %s; filename = %s; val = %s;",
+ host, filename, val);
+ rrd_update_file (host, filename, val,
+ ps_count_ds_def, ps_count_ds_num);
+}
+
+static void ps_pagefaults_write (char *host, char *inst, char *val)
+{
+ char filename[256];
+ int status;
+
+ status = snprintf (filename, 256, ps_pagefaults_file, inst);
+ if ((status < 1) || (status >= 256))
+ return;
+
+ DBG ("host = %s; filename = %s; val = %s;",
+ host, filename, val);
+ rrd_update_file (host, filename, val,
+ ps_pagefaults_ds_def, ps_pagefaults_ds_num);
}
#if PROCESSES_HAVE_READ
plugin_submit (MODULE_NAME, "-", buf);
}
+static void ps_submit_proc_list (procstat_t *ps)
+{
+ char buffer[64];
+
+ if (ps == NULL)
+ return;
+
+ snprintf (buffer, 64, "%u:%lu",
+ (unsigned int) curtime,
+ ps->vmem_rss);
+ buffer[63] = '\0';
+ plugin_submit ("ps_rss", ps->name, buffer);
+
+ snprintf (buffer, 64, "%u:%u:%u",
+ (unsigned int) curtime,
+ /* Make the counter overflow */
+ (unsigned int) (ps->cpu_user_counter & 0xFFFFFFFF),
+ (unsigned int) (ps->cpu_system_counter & 0xFFFFFFFF));
+ buffer[63] = '\0';
+ plugin_submit ("ps_cputime", ps->name, buffer);
+
+ snprintf (buffer, 64, "%u:%lu:%lu",
+ (unsigned int) curtime,
+ ps->num_proc, ps->num_lwp);
+ buffer[63] = '\0';
+ plugin_submit ("ps_count", ps->name, buffer);
+
+ snprintf (buffer, 64, "%u:%lu:%lu",
+ (unsigned int) curtime,
+ ps->vmem_minflt_counter, ps->vmem_majflt_counter);
+ buffer[63] = '\0';
+ plugin_submit ("ps_pagefaults", ps->name, buffer);
+
+ DBG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
+ "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
+ "cpu_user_counter = %lu; cpu_system_counter = %lu;",
+ ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
+ ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
+ ps->cpu_system_counter);
+
+}
+
+#if KERNEL_LINUX
+static int *ps_read_tasks (int pid)
+{
+ int *list = NULL;
+ int list_size = 1; /* size of allocated space, in elements */
+ int list_len = 0; /* number of currently used elements */
+
+ char dirname[64];
+ DIR *dh;
+ struct dirent *ent;
+
+ snprintf (dirname, 64, "/proc/%i/task", pid);
+ dirname[63] = '\0';
+
+ if ((dh = opendir (dirname)) == NULL)
+ {
+ DBG ("Failed to open directory `%s'", dirname);
+ return (NULL);
+ }
+
+ while ((ent = readdir (dh)) != NULL)
+ {
+ if (!isdigit (ent->d_name[0]))
+ continue;
+
+ if ((list_len + 1) >= list_size)
+ {
+ int *new_ptr;
+ int new_size = 2 * list_size;
+ /* Comes in sizes: 2, 4, 8, 16, ... */
+
+ new_ptr = (int *) realloc (list, (size_t) (sizeof (int) * new_size));
+ if (new_ptr == NULL)
+ {
+ if (list != NULL)
+ free (list);
+ syslog (LOG_ERR, "processes plugin: "
+ "Failed to allocate more memory.");
+ return (NULL);
+ }
+
+ list = new_ptr;
+ list_size = new_size;
+
+ memset (list + list_len, 0, sizeof (int) * (list_size - list_len));
+ }
+
+ list[list_len] = atoi (ent->d_name);
+ if (list[list_len] != 0)
+ list_len++;
+ }
+
+ closedir (dh);
+
+ assert (list_len < list_size);
+ assert (list[list_len] == 0);
+
+ return (list);
+}
+
+int ps_read_process (int pid, procstat_t *ps, char *state)
+{
+ char filename[64];
+ char buffer[1024];
+ FILE *fh;
+
+ char *fields[64];
+ char fields_len;
+
+ int *tasks;
+ int i;
+
+ int ppid;
+ int name_len;
+
+ long long unsigned cpu_user_counter;
+ long long unsigned cpu_system_counter;
+ long long unsigned vmem_rss;
+
+ memset (ps, 0, sizeof (procstat_t));
+
+ snprintf (filename, 64, "/proc/%i/stat", pid);
+ filename[63] = '\0';
+
+ if ((fh = fopen (filename, "r")) == NULL)
+ return (-1);
+
+ if (fgets (buffer, 1024, fh) == NULL)
+ {
+ fclose (fh);
+ return (-1);
+ }
+
+ fclose (fh);
+
+ fields_len = strsplit (buffer, fields, 64);
+ if (fields_len < 24)
+ {
+ DBG ("`%s' has only %i fields..",
+ filename, fields_len);
+ return (-1);
+ }
+ else if (fields_len != 41)
+ {
+ DBG ("WARNING: (fields_len = %i) != 41", fields_len);
+ }
+
+ /* copy the name, strip brackets in the process */
+ name_len = strlen (fields[1]) - 2;
+ if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
+ {
+ DBG ("No brackets found in process name: `%s'", fields[1]);
+ return (-1);
+ }
+ fields[1] = fields[1] + 1;
+ fields[1][name_len] = '\0';
+ strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
+
+ ppid = atoi (fields[3]);
+
+ if ((tasks = ps_read_tasks (pid)) == NULL)
+ {
+ /* This happends for zombied, e.g. */
+ DBG ("ps_read_tasks (%i) failed.", pid);
+ *state = 'Z';
+ ps->num_lwp = 0;
+ ps->num_proc = 0;
+ }
+ else
+ {
+ *state = '\0';
+ ps->num_lwp = 0;
+ ps->num_proc = 1;
+ for (i = 0; tasks[i] != 0; i++)
+ ps->num_lwp++;
+
+ free (tasks);
+ tasks = NULL;
+ }
+
+ /* Leave the rest at zero if this is only an LWP */
+ if (ps->num_proc == 0)
+ {
+ DBG ("This is only an LWP: pid = %i; name = %s;",
+ pid, ps->name);
+ return (0);
+ }
+
+ cpu_user_counter = atoll (fields[13]);
+ cpu_system_counter = atoll (fields[14]);
+ vmem_rss = atoll (fields[23]);
+ ps->vmem_minflt_counter = atol (fields[9]);
+ ps->vmem_majflt_counter = atol (fields[11]);
+
+ /* Convert jiffies to useconds */
+ cpu_user_counter = cpu_user_counter * 1000000 / CONFIG_HZ;
+ cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
+ vmem_rss = vmem_rss * pagesize_g;
+
+ ps->cpu_user_counter = (unsigned long) cpu_user_counter;
+ ps->cpu_system_counter = (unsigned long) cpu_system_counter;
+ ps->vmem_rss = (unsigned long) vmem_rss;
+
+ *state = fields[2][0];
+
+ /* success */
+ return (0);
+} /* int ps_read_process (...) */
+#endif /* KERNEL_LINUX */
+
+#if HAVE_THREAD_INFO
+static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
+{
+ int mib[4];
+
+ struct kinfo_proc kp;
+ size_t kp_size;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+
+ if (pid_for_task (t, pid) != KERN_SUCCESS)
+ return (-1);
+ mib[3] = *pid;
+
+ kp_size = sizeof (kp);
+ if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
+ return (-1);
+
+ if (name_max_len > (MAXCOMLEN + 1))
+ name_max_len = MAXCOMLEN + 1;
+
+ strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
+ name[name_max_len - 1] = '\0';
+
+ DBG ("pid = %i; name = %s;", *pid, name);
+
+ /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
+ * `top' does it, because it is a lot of work and only used when
+ * debugging. -octo */
+
+ return (0);
+}
+#endif /* HAVE_THREAD_INFO */
+
static void ps_read (void)
{
#if HAVE_THREAD_INFO
task_array_t task_list;
mach_msg_type_number_t task_list_len;
+ int task_pid;
+ char task_name[MAXCOMLEN + 1];
+
int thread;
thread_act_array_t thread_list;
mach_msg_type_number_t thread_list_len;
int stopped = 0;
int blocked = 0;
+ procstat_t *ps;
+ procstat_entry_t pse;
+
+ ps_list_reset ();
+
/*
* The Mach-concept is a little different from the traditional UNIX
* concept: All the work is done in threads. Threads are contained in
for (task = 0; task < task_list_len; task++)
{
+ ps = NULL;
+ if (mach_get_task_name (task_list[task],
+ &task_pid,
+ task_name, PROCSTAT_NAME_LEN) == 0)
+ ps = ps_list_search (task_name);
+
+ /* Collect more detailed statistics for this process */
+ if (ps != NULL)
+ {
+ task_basic_info_data_t task_basic_info;
+ mach_msg_type_number_t task_basic_info_len;
+ task_events_info_data_t task_events_info;
+ mach_msg_type_number_t task_events_info_len;
+ task_absolutetime_info_data_t task_absolutetime_info;
+ mach_msg_type_number_t task_absolutetime_info_len;
+
+ memset (&pse, '\0', sizeof (pse));
+ pse.id = task_pid;
+
+ task_basic_info_len = TASK_BASIC_INFO_COUNT;
+ status = task_info (task_list[task],
+ TASK_BASIC_INFO,
+ (task_info_t) &task_basic_info,
+ &task_basic_info_len);
+ if (status != KERN_SUCCESS)
+ {
+ syslog (LOG_ERR, "task_info failed: %s",
+ mach_error_string (status));
+ continue; /* with next thread_list */
+ }
+
+ task_events_info_len = TASK_EVENTS_INFO_COUNT;
+ status = task_info (task_list[task],
+ TASK_EVENTS_INFO,
+ (task_info_t) &task_events_info,
+ &task_events_info_len);
+ if (status != KERN_SUCCESS)
+ {
+ syslog (LOG_ERR, "task_info failed: %s",
+ mach_error_string (status));
+ continue; /* with next thread_list */
+ }
+
+ task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
+ status = task_info (task_list[task],
+ TASK_ABSOLUTETIME_INFO,
+ (task_info_t) &task_absolutetime_info,
+ &task_absolutetime_info_len);
+ if (status != KERN_SUCCESS)
+ {
+ syslog (LOG_ERR, "task_info failed: %s",
+ mach_error_string (status));
+ continue; /* with next thread_list */
+ }
+
+ pse.num_proc++;
+ pse.vmem_rss = task_basic_info.resident_size;
+
+ pse.vmem_minflt_counter = task_events_info.cow_faults;
+ pse.vmem_majflt_counter = task_events_info.faults;
+
+ pse.cpu_user_counter = task_absolutetime_info.total_user;
+ pse.cpu_system_counter = task_absolutetime_info.total_system;
+ }
+
status = task_threads (task_list[task], &thread_list,
&thread_list_len);
if (status != KERN_SUCCESS)
&thread_data_len);
if (status != KERN_SUCCESS)
{
- syslog (LOG_ERR, "thread_info failed: %s\n",
+ syslog (LOG_ERR, "thread_info failed: %s",
mach_error_string (status));
if (task_list[task] != port_task_self)
mach_port_deallocate (port_task_self,
continue; /* with next thread_list */
}
+ if (ps != NULL)
+ pse.num_lwp++;
+
switch (thread_data.run_state)
{
case TH_STATE_RUNNING:
syslog (LOG_ERR, "mach_port_deallocate failed: %s",
mach_error_string (status));
}
+
+ if (ps != NULL)
+ ps_list_add (task_name, &pse);
} /* for (task_list) */
if ((status = vm_deallocate (port_task_self,
} /* for (pset_list) */
ps_submit (running, sleeping, zombies, stopped, -1, blocked);
+
+ for (ps = list_head_g; ps != NULL; ps = ps->next)
+ ps_submit_proc_list (ps);
/* #endif HAVE_THREAD_INFO */
#elif KERNEL_LINUX
int paging = 0;
int blocked = 0;
- char buf[BUFSIZE];
- char filename[20]; /* need 17 bytes */
- char *fields[BUFSIZE];
-
struct dirent *ent;
- DIR *proc;
- FILE *fh;
+ DIR *proc;
+ int pid;
+
+ int status;
+ procstat_t ps;
+ procstat_entry_t pse;
+ char state;
+
+ procstat_t *ps_ptr;
running = sleeping = zombies = stopped = paging = blocked = 0;
+ ps_list_reset ();
if ((proc = opendir ("/proc")) == NULL)
{
if (!isdigit (ent->d_name[0]))
continue;
- if (snprintf (filename, 20, "/proc/%s/stat", ent->d_name) >= 20)
+ if ((pid = atoi (ent->d_name)) < 1)
continue;
- if ((fh = fopen (filename, "r")) == NULL)
+ status = ps_read_process (pid, &ps, &state);
+ if (status != 0)
{
- syslog (LOG_NOTICE, "Cannot open `%s': %s", filename,
- strerror (errno));
+ DBG ("ps_read_process failed: %i", status);
continue;
}
- if (fgets (buf, BUFSIZE, fh) == NULL)
- {
- syslog (LOG_NOTICE, "Unable to read from `%s': %s",
- filename, strerror (errno));
- fclose (fh);
- continue;
- }
+ pse.id = pid;
+ pse.age = 0;
- fclose (fh);
+ pse.num_proc = ps.num_proc;
+ pse.num_lwp = ps.num_lwp;
+ pse.vmem_rss = ps.vmem_rss;
- if (strsplit (buf, fields, BUFSIZE) < 3)
- {
- DBG ("Line has less than three fields.");
- continue;
- }
+ 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;
- switch (fields[2][0])
+ switch (state)
{
case 'R': running++; break;
case 'S': sleeping++; break;
case 'T': stopped++; break;
case 'W': paging++; break;
}
+
+ ps_list_add (ps.name, &pse);
}
closedir (proc);
ps_submit (running, sleeping, zombies, stopped, paging, blocked);
+
+ for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
+ ps_submit_proc_list (ps_ptr);
#endif /* KERNEL_LINUX */
}
#else
void module_register (void)
{
plugin_register (MODULE_NAME, ps_init, ps_read, ps_write);
+ plugin_register ("ps_rss", NULL, NULL, ps_rss_write);
+ plugin_register ("ps_cputime", NULL, NULL, ps_cputime_write);
+ plugin_register ("ps_count", NULL, NULL, ps_count_write);
+ plugin_register ("ps_pagefaults", NULL, NULL, ps_pagefaults_write);
+ cf_register (MODULE_NAME, ps_config, config_keys, config_keys_num);
}
#undef BUFSIZE
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ *
+ * Lubos Stanek <lubek at users.sourceforge.net> Wed Oct 27, 2006
+ * - config ExtendedSensorNaming option
+ * - precise sensor feature selection (chip-bus-address/type-feature)
+ * with ExtendedSensorNaming
+ * - more sensor features (finite list)
+ * - honor sensors.conf's ignored
+ * - config Sensor option
+ * - config IgnoreSelected option
**/
#include "collectd.h"
#include "common.h"
#include "plugin.h"
+#include "configfile.h"
+#include "utils_ignorelist.h"
+#include "utils_debug.h"
#define MODULE_NAME "sensors"
+#define MODULE_NAME_VOLTAGE MODULE_NAME"_voltage"
#if defined(HAVE_SENSORS_SENSORS_H)
# include <sensors/sensors.h>
#define BUFSIZE 512
-static char *filename_format = "sensors-%s.rrd";
-
+/* temperature and fan sensors */
static char *ds_def[] =
{
"DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
};
static int ds_num = 1;
-#ifdef HAVE_LIBSENSORS
+/* voltage sensors */
+static char *sensor_voltage_ds_def[] =
+{
+ "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
+ NULL
+};
+static int sensor_voltage_ds_num = 1;
+
+/* old naming */
+static char *old_filename_format = "sensors-%s.rrd";
+/* end old naming */
+
+/* new naming <chip-bus-address/type-feature */
+static char *extended_filename_format = "lm_sensors-%s.rrd";
+
+#define SENSOR_TYPE_UNKNOWN 0
+#define SENSOR_TYPE_VOLTAGE 1
+#define SENSOR_TYPE_FANSPEED 2
+#define SENSOR_TYPE_TEMPERATURE 3
+
+#if SENSORS_HAVE_READ
+static char *sensor_type_prefix[] =
+{
+ "unknown",
+ "voltage",
+ "fanspeed",
+ "temperature",
+ NULL
+};
+#endif
+
+typedef struct sensors_labeltypes {
+ char *label;
+ int type;
+} sensors_labeltypes;
+
+/*
+ * finite list of known labels extracted from lm_sensors
+ */
+#if SENSORS_HAVE_READ
+static sensors_labeltypes known_features[] =
+{
+ { "fan1", SENSOR_TYPE_FANSPEED },
+ { "fan2", SENSOR_TYPE_FANSPEED },
+ { "fan3", SENSOR_TYPE_FANSPEED },
+ { "fan4", SENSOR_TYPE_FANSPEED },
+ { "fan5", SENSOR_TYPE_FANSPEED },
+ { "fan6", SENSOR_TYPE_FANSPEED },
+ { "fan7", SENSOR_TYPE_FANSPEED },
+ { "AIN2", SENSOR_TYPE_VOLTAGE },
+ { "AIN1", SENSOR_TYPE_VOLTAGE },
+ { "in10", SENSOR_TYPE_VOLTAGE },
+ { "in9", SENSOR_TYPE_VOLTAGE },
+ { "in8", SENSOR_TYPE_VOLTAGE },
+ { "in7", SENSOR_TYPE_VOLTAGE },
+ { "in6", SENSOR_TYPE_VOLTAGE },
+ { "in5", SENSOR_TYPE_VOLTAGE },
+ { "in4", SENSOR_TYPE_VOLTAGE },
+ { "in3", SENSOR_TYPE_VOLTAGE },
+ { "in2", SENSOR_TYPE_VOLTAGE },
+ { "in0", SENSOR_TYPE_VOLTAGE },
+ { "CPU_Temp", SENSOR_TYPE_TEMPERATURE },
+ { "remote_temp", SENSOR_TYPE_TEMPERATURE },
+ { "temp1", SENSOR_TYPE_TEMPERATURE },
+ { "temp2", SENSOR_TYPE_TEMPERATURE },
+ { "temp3", SENSOR_TYPE_TEMPERATURE },
+ { "temp4", SENSOR_TYPE_TEMPERATURE },
+ { "temp5", SENSOR_TYPE_TEMPERATURE },
+ { "temp6", SENSOR_TYPE_TEMPERATURE },
+ { "temp7", SENSOR_TYPE_TEMPERATURE },
+ { "temp", SENSOR_TYPE_TEMPERATURE },
+ { "Vccp2", SENSOR_TYPE_VOLTAGE },
+ { "Vccp1", SENSOR_TYPE_VOLTAGE },
+ { "vdd", SENSOR_TYPE_VOLTAGE },
+ { "vid5", SENSOR_TYPE_VOLTAGE },
+ { "vid4", SENSOR_TYPE_VOLTAGE },
+ { "vid3", SENSOR_TYPE_VOLTAGE },
+ { "vid2", SENSOR_TYPE_VOLTAGE },
+ { "vid1", SENSOR_TYPE_VOLTAGE },
+ { "vid", SENSOR_TYPE_VOLTAGE },
+ { "vin4", SENSOR_TYPE_VOLTAGE },
+ { "vin3", SENSOR_TYPE_VOLTAGE },
+ { "vin2", SENSOR_TYPE_VOLTAGE },
+ { "vin1", SENSOR_TYPE_VOLTAGE },
+ { "voltbatt", SENSOR_TYPE_VOLTAGE },
+ { "volt12", SENSOR_TYPE_VOLTAGE },
+ { "volt5", SENSOR_TYPE_VOLTAGE },
+ { "vrm", SENSOR_TYPE_VOLTAGE },
+ { "5.0V", SENSOR_TYPE_VOLTAGE },
+ { "5V", SENSOR_TYPE_VOLTAGE },
+ { "3.3V", SENSOR_TYPE_VOLTAGE },
+ { "2.5V", SENSOR_TYPE_VOLTAGE },
+ { "2.0V", SENSOR_TYPE_VOLTAGE },
+ { "12V", SENSOR_TYPE_VOLTAGE },
+ { 0, -1 }
+};
+#endif
+/* end new naming */
+
+static char *config_keys[] =
+{
+ "Sensor",
+ "IgnoreSelected",
+ "ExtendedSensorNaming",
+ NULL
+};
+static int config_keys_num = 3;
+
+static ignorelist_t *sensor_list;
+
+/*
+ * sensor_extended_naming:
+ * 0 => default is to create chip-feature
+ * 1 => use new naming scheme chip-bus-address/type-feature
+ */
+static int sensor_extended_naming = 0;
+
+#if SENSORS_HAVE_READ
typedef struct featurelist
{
const sensors_chip_name *chip;
const sensors_feature_data *data;
+ int type;
struct featurelist *next;
} featurelist_t;
featurelist_t *first_feature = NULL;
-#endif /* defined (HAVE_LIBSENSORS) */
+#endif /* if SENSORS_HAVE_READ */
+
+static int sensors_config (char *key, char *value)
+{
+ if (sensor_list == NULL)
+ sensor_list = ignorelist_create (1);
+
+ if (strcasecmp (key, "Sensor") == 0)
+ {
+ if (ignorelist_add (sensor_list, value))
+ {
+ syslog (LOG_EMERG, "Cannot add value to ignorelist.");
+ return (1);
+ }
+ }
+ else if (strcasecmp (key, "IgnoreSelected") == 0)
+ {
+ ignorelist_set_invert (sensor_list, 1);
+ if ((strcasecmp (value, "True") == 0)
+ || (strcasecmp (value, "Yes") == 0)
+ || (strcasecmp (value, "On") == 0))
+ ignorelist_set_invert (sensor_list, 0);
+ }
+ else if (strcasecmp (key, "ExtendedSensorNaming") == 0)
+ {
+ if ((strcasecmp (value, "True") == 0)
+ || (strcasecmp (value, "Yes") == 0)
+ || (strcasecmp (value, "On") == 0))
+ sensor_extended_naming = 1;
+ else
+ sensor_extended_naming = 0;
+ }
+ else
+ {
+ return (-1);
+ }
+
+ return (0);
+}
static void collectd_sensors_init (void)
{
-#ifdef HAVE_LIBSENSORS
+#if SENSORS_HAVE_READ
FILE *fh;
featurelist_t *last_feature = NULL;
featurelist_t *new_feature;
if (sensors_init (fh))
{
fclose (fh);
- syslog (LOG_ERR, "sensors: Cannot initialize sensors. Data will not be collected.");
+ syslog (LOG_ERR, "sensors: Cannot initialize sensors. "
+ "Data will not be collected.");
return;
}
data = NULL;
data_num0 = data_num1 = 0;
- while ((data = sensors_get_all_features (*chip, &data_num0, &data_num1)) != NULL)
+ while ((data = sensors_get_all_features (*chip, &data_num0, &data_num1))
+ != NULL)
{
+ int i;
+
/* "master features" only */
if (data->mapping != SENSORS_NO_MAPPING)
continue;
- /* Only temperature for now.. */
- if (strncmp (data->name, "temp", 4)
- && strncmp (data->name, "fan", 3))
- continue;
-
- if ((new_feature = (featurelist_t *) malloc (sizeof (featurelist_t))) == NULL)
+ /* Only known features */
+ for (i = 0; known_features[i].type >= 0; i++)
{
- perror ("malloc");
- continue;
- }
+ if (strcmp (data->name, known_features[i].label) != 0)
+ continue;
+
+ /* skip ignored in sensors.conf */
+ if (sensors_get_ignored (*chip, data->number) == 0)
+ break;
+
+ DBG ("Adding feature: %s-%s-%s",
+ chip->prefix,
+ sensor_type_prefix[known_features[i].type],
+ data->name);
+
+ if ((new_feature = (featurelist_t *) malloc (sizeof (featurelist_t))) == NULL)
+ {
+ DBG ("sensors plugin: malloc: %s",
+ strerror (errno));
+ syslog (LOG_ERR, "sensors plugin: malloc: %s",
+ strerror (errno));
+ break;
+ }
+
+ new_feature->chip = chip;
+ new_feature->data = data;
+ new_feature->type = known_features[i].type;
+ new_feature->next = NULL;
+
+ if (first_feature == NULL)
+ {
+ first_feature = new_feature;
+ last_feature = new_feature;
+ }
+ else
+ {
+ last_feature->next = new_feature;
+ last_feature = new_feature;
+ }
+
+ /* stop searching known features at first found */
+ break;
+ } /* for i */
+ } /* while sensors_get_all_features */
+ } /* while sensors_get_detected_chips */
- /*
- syslog (LOG_INFO, "sensors: Adding feature: %s/%s", chip->prefix, data->name);
- */
+ if (first_feature == NULL)
+ sensors_cleanup ();
+#endif /* if SENSORS_HAVE_READ */
- new_feature->chip = chip;
- new_feature->data = data;
- new_feature->next = NULL;
+ return;
+}
- if (first_feature == NULL)
- {
- first_feature = new_feature;
- last_feature = new_feature;
- }
- else
- {
- last_feature->next = new_feature;
- last_feature = new_feature;
- }
- }
+static void sensors_shutdown (void)
+{
+#if SENSORS_HAVE_READ
+ featurelist_t *thisft = first_feature;
+ featurelist_t *nextft;
+
+ while (thisft != NULL)
+ {
+ nextft = thisft->next;
+ sfree (thisft);
+ thisft = nextft;
}
- if (first_feature == NULL)
- sensors_cleanup ();
-#endif /* defined(HAVE_LIBSENSORS) */
+ sensors_cleanup ();
+#endif /* if SENSORS_HAVE_READ */
- return;
+ ignorelist_free (sensor_list);
+}
+
+static void sensors_voltage_write (char *host, char *inst, char *val)
+{
+ char file[BUFSIZE];
+ int status;
+
+ /* skip ignored in our config */
+ if (ignorelist_match (sensor_list, inst))
+ return;
+
+ /* extended sensor naming */
+ if(sensor_extended_naming)
+ status = snprintf (file, BUFSIZE, extended_filename_format, inst);
+ else
+ status = snprintf (file, BUFSIZE, old_filename_format, inst);
+
+ if ((status < 1) || (status >= BUFSIZE))
+ return;
+
+ rrd_update_file (host, file, val, sensor_voltage_ds_def, sensor_voltage_ds_num);
}
static void sensors_write (char *host, char *inst, char *val)
char file[BUFSIZE];
int status;
- status = snprintf (file, BUFSIZE, filename_format, inst);
- if (status < 1)
+ /* skip ignored in our config */
+ if (ignorelist_match (sensor_list, inst))
return;
- else if (status >= BUFSIZE)
+
+ /* extended sensor naming */
+ if (sensor_extended_naming)
+ status = snprintf (file, BUFSIZE, extended_filename_format, inst);
+ else
+ status = snprintf (file, BUFSIZE, old_filename_format, inst);
+
+ if ((status < 1) || (status >= BUFSIZE))
return;
rrd_update_file (host, file, val, ds_def, ds_num);
}
#if SENSORS_HAVE_READ
-static void sensors_submit (const char *feat_name, const char *chip_prefix, double value)
+static void sensors_submit (const char *feat_name,
+ const char *chip_prefix, double value, int type)
{
char buf[BUFSIZE];
char inst[BUFSIZE];
- if (snprintf (buf, BUFSIZE, "%u:%.3f", (unsigned int) curtime, value) >= BUFSIZE)
+ if (snprintf (inst, BUFSIZE, "%s-%s", chip_prefix, feat_name)
+ >= BUFSIZE)
return;
- if (snprintf (inst, BUFSIZE, "%s-%s", chip_prefix, feat_name) >= BUFSIZE)
+ /* skip ignored in our config */
+ if (ignorelist_match (sensor_list, inst))
return;
- plugin_submit (MODULE_NAME, inst, buf);
+ if (snprintf (buf, BUFSIZE, "%u:%.3f", (unsigned int) curtime,
+ value) >= BUFSIZE)
+ return;
+
+ if (type == SENSOR_TYPE_VOLTAGE)
+ {
+ DBG ("%s: %s/%s, %s", MODULE_NAME_VOLTAGE,
+ sensor_type_prefix[type], inst, buf);
+ plugin_submit (MODULE_NAME_VOLTAGE, inst, buf);
+ }
+ else
+ {
+ DBG ("%s: %s/%s, %s", MODULE_NAME,
+ sensor_type_prefix[type], inst, buf);
+ plugin_submit (MODULE_NAME, inst, buf);
+ }
}
static void sensors_read (void)
{
featurelist_t *feature;
double value;
+ char chip_fullprefix[BUFSIZE];
for (feature = first_feature; feature != NULL; feature = feature->next)
{
if (sensors_get_feature (*feature->chip, feature->data->number, &value) < 0)
continue;
- sensors_submit (feature->data->name, feature->chip->prefix, value);
+ if (sensor_extended_naming)
+ {
+ /* full chip name logic borrowed from lm_sensors */
+ if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
+ {
+ if (snprintf (chip_fullprefix, BUFSIZE, "%s-isa-%04x/%s",
+ feature->chip->prefix,
+ feature->chip->addr,
+ sensor_type_prefix[feature->type])
+ >= BUFSIZE)
+ continue;
+ }
+ else if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY)
+ {
+ if (snprintf (chip_fullprefix, BUFSIZE, "%s-%s-%04x/%s",
+ feature->chip->prefix,
+ feature->chip->busname,
+ feature->chip->addr,
+ sensor_type_prefix[feature->type])
+ >= BUFSIZE)
+ continue;
+ }
+ else
+ {
+ if (snprintf (chip_fullprefix, BUFSIZE, "%s-i2c-%d-%02x/%s",
+ feature->chip->prefix,
+ feature->chip->bus,
+ feature->chip->addr,
+ sensor_type_prefix[feature->type])
+ >= BUFSIZE)
+ continue;
+ }
+
+ sensors_submit (feature->data->name,
+ chip_fullprefix,
+ value, feature->type);
+ }
+ else
+ {
+ sensors_submit (feature->data->name,
+ feature->chip->prefix,
+ value, feature->type);
+ }
}
}
#else
void module_register (void)
{
plugin_register (MODULE_NAME, collectd_sensors_init, sensors_read, sensors_write);
+ plugin_register (MODULE_NAME_VOLTAGE, NULL, NULL, sensors_voltage_write);
+ plugin_register_shutdown (MODULE_NAME, sensors_shutdown);
+ cf_register (MODULE_NAME, sensors_config, config_keys, config_keys_num);
}
#undef BUFSIZE
--- /dev/null
+/*
+ * collectd - src/utils_dns.c
+ * Modifications Copyright (C) 2006 Florian octo Forster
+ * Copyright (C) 2002 The Measurement Factory, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ * The Measurement Factory, Inc. <http://www.measurement-factory.com/>
+ * Florian octo Forster <octo at verplant.org>
+ */
+
+#include "collectd.h"
+
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_PCAP_H
+# include <pcap.h>
+#endif
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#if HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#elif HAVE_ARPA_NAMESER_COMPAT_H
+# include <arpa/nameser_compat.h>
+#endif
+
+#if HAVE_NET_IF_ARP_H
+# include <net/if_arp.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#if HAVE_NETINET_IF_ETHER_H
+# include <netinet/if_ether.h>
+#endif
+#if HAVE_NET_IF_PPP_H
+# include <net/if_ppp.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+# include <netdb.h>
+#endif
+
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+#ifdef HAVE_NETINET_IP_VAR_H
+# include <netinet/ip_var.h>
+#endif
+#if HAVE_NETINET_IP6_H
+# include <netinet/ip6.h>
+#endif
+#if HAVE_NETINET_UDP_H
+# include <netinet/udp.h>
+#endif
+
+#define PCAP_SNAPLEN 1460
+#ifndef ETHER_HDR_LEN
+#define ETHER_ADDR_LEN 6
+#define ETHER_TYPE_LEN 2
+#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
+#endif
+#ifndef ETHERTYPE_8021Q
+# define ETHERTYPE_8021Q 0x8100
+#endif
+#ifndef ETHERTYPE_IPV6
+# define ETHERTYPE_IPV6 0x86DD
+#endif
+
+#ifndef PPP_ADDRESS_VAL
+# define PPP_ADDRESS_VAL 0xff /* The address byte value */
+#endif
+#ifndef PPP_CONTROL_VAL
+# define PPP_CONTROL_VAL 0x03 /* The control byte value */
+#endif
+
+#ifdef __linux__
+#define uh_sport source
+#define uh_dport dest
+#endif
+
+#include "utils_dns.h"
+
+/*
+ * Type definitions
+ */
+struct ip_list_s
+{
+ struct in6_addr addr;
+ void *data;
+ struct ip_list_s *next;
+};
+typedef struct ip_list_s ip_list_t;
+
+typedef int (printer)(const char *, ...);
+
+/*
+ * flags/features for non-interactive mode
+ */
+
+#ifndef T_A6
+#define T_A6 38
+#endif
+#ifndef T_SRV
+#define T_SRV 33
+#endif
+
+/*
+ * Global variables
+ */
+int qtype_counts[T_MAX];
+int opcode_counts[OP_MAX];
+int qclass_counts[C_MAX];
+
+#if HAVE_PCAP_H
+static pcap_t *pcap_obj = NULL;
+#endif
+
+static ip_list_t *IgnoreList = NULL;
+
+#if HAVE_PCAP_H
+static void (*Callback) (const rfc1035_header_t *) = NULL;
+
+static int query_count_intvl = 0;
+static int query_count_total = 0;
+# ifdef __OpenBSD__
+static struct bpf_timeval last_ts;
+# else
+static struct timeval last_ts;
+# endif /* __OpenBSD__ */
+#endif /* HAVE_PCAP_H */
+
+static int cmp_in6_addr (const struct in6_addr *a,
+ const struct in6_addr *b)
+{
+ int i;
+
+ assert (sizeof (struct in6_addr) == 16);
+
+ for (i = 0; i < 16; i++)
+ if (a->s6_addr[i] != b->s6_addr[i])
+ break;
+
+ if (i >= 16)
+ return (0);
+
+ return (a->s6_addr[i] > b->s6_addr[i] ? 1 : -1);
+} /* int cmp_addrinfo */
+
+static inline int ignore_list_match (const struct in6_addr *addr)
+{
+ ip_list_t *ptr;
+
+ for (ptr = IgnoreList; ptr != NULL; ptr = ptr->next)
+ if (cmp_in6_addr (addr, &ptr->addr) == 0)
+ return (1);
+ return (0);
+} /* int ignore_list_match */
+
+static void ignore_list_add (const struct in6_addr *addr)
+{
+ ip_list_t *new;
+
+ if (ignore_list_match (addr) != 0)
+ return;
+
+ new = malloc (sizeof (ip_list_t));
+ if (new == NULL)
+ {
+ perror ("malloc");
+ return;
+ }
+
+ memcpy (&new->addr, addr, sizeof (struct in6_addr));
+ new->next = IgnoreList;
+
+ IgnoreList = new;
+} /* void ignore_list_add */
+
+void ignore_list_add_name (const char *name)
+{
+ struct addrinfo *ai_list;
+ struct addrinfo *ai_ptr;
+ struct in6_addr addr;
+ int status;
+
+ status = getaddrinfo (name, NULL, NULL, &ai_list);
+ if (status != 0)
+ return;
+
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ if (ai_ptr->ai_family == AF_INET)
+ {
+ memset (&addr, '\0', sizeof (addr));
+ addr.s6_addr[10] = 0xFF;
+ addr.s6_addr[11] = 0xFF;
+ memcpy (addr.s6_addr + 12, &((struct sockaddr_in *) ai_ptr->ai_addr)->sin_addr, 4);
+
+ ignore_list_add (&addr);
+ }
+ else
+ {
+ ignore_list_add (&((struct sockaddr_in6 *) ai_ptr->ai_addr)->sin6_addr);
+ }
+ } /* for */
+
+ freeaddrinfo (ai_list);
+}
+
+#if HAVE_PCAP_H
+static void in6_addr_from_buffer (struct in6_addr *ia,
+ const void *buf, size_t buf_len,
+ int family)
+{
+ memset (ia, 0, sizeof (struct in6_addr));
+ if ((AF_INET == family) && (sizeof (uint32_t) == buf_len))
+ {
+ ia->s6_addr[10] = 0xFF;
+ ia->s6_addr[11] = 0xFF;
+ memcpy (ia->s6_addr + 12, buf, buf_len);
+ }
+ else if ((AF_INET6 == family) && (sizeof (struct in6_addr) == buf_len))
+ {
+ memcpy (ia, buf, buf_len);
+ }
+} /* void in6_addr_from_buffer */
+
+void dnstop_set_pcap_obj (pcap_t *po)
+{
+ pcap_obj = po;
+}
+
+void dnstop_set_callback (void (*cb) (const rfc1035_header_t *))
+{
+ Callback = cb;
+}
+
+#define RFC1035_MAXLABELSZ 63
+static int
+rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
+)
+{
+ off_t no = 0;
+ unsigned char c;
+ size_t len;
+ assert(ns > 0);
+ do {
+ if ((*off) >= sz)
+ break;
+ c = *(buf + (*off));
+ if (c > 191) {
+ /* blasted compression */
+ unsigned short s;
+ off_t ptr;
+ memcpy(&s, buf + (*off), sizeof(s));
+ s = ntohs(s);
+ (*off) += sizeof(s);
+ /* Sanity check */
+ if ((*off) >= sz)
+ return 1;
+ ptr = s & 0x3FFF;
+ /* Make sure the pointer is inside this message */
+ if (ptr >= sz)
+ return 2;
+ return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
+ } else if (c > RFC1035_MAXLABELSZ) {
+ /*
+ * "(The 10 and 01 combinations are reserved for future use.)"
+ */
+ break;
+ return 3;
+ } else {
+ (*off)++;
+ len = (size_t) c;
+ if (len == 0)
+ break;
+ if (len > (ns - 1))
+ len = ns - 1;
+ if ((*off) + len > sz) /* message is too short */
+ return 4;
+ memcpy(name + no, buf + (*off), len);
+ (*off) += len;
+ no += len;
+ *(name + (no++)) = '.';
+ }
+ } while (c > 0);
+ *(name + no - 1) = '\0';
+ /* make sure we didn't allow someone to overflow the name buffer */
+ assert(no <= ns);
+ return 0;
+}
+
+static int
+handle_dns(const char *buf, int len,
+ const struct in6_addr *s_addr,
+ const struct in6_addr *d_addr)
+{
+ rfc1035_header_t qh;
+ uint16_t us;
+ off_t offset;
+ char *t;
+ int x;
+
+ /* The DNS header is 12 bytes long */
+ if (len < 12)
+ return 0;
+
+ memcpy(&us, buf + 0, 2);
+ qh.id = ntohs(us);
+
+ memcpy(&us, buf + 2, 2);
+ us = ntohs(us);
+ fprintf (stderr, "Bytes 0, 1: 0x%04hx\n", us);
+ qh.qr = (us >> 15) & 0x01;
+ qh.opcode = (us >> 11) & 0x0F;
+ qh.aa = (us >> 10) & 0x01;
+ qh.tc = (us >> 9) & 0x01;
+ qh.rd = (us >> 8) & 0x01;
+ qh.ra = (us >> 7) & 0x01;
+ qh.z = (us >> 6) & 0x01;
+ qh.ad = (us >> 5) & 0x01;
+ qh.cd = (us >> 4) & 0x01;
+ qh.rcode = us & 0x0F;
+
+ memcpy(&us, buf + 4, 2);
+ qh.qdcount = ntohs(us);
+
+ memcpy(&us, buf + 6, 2);
+ qh.ancount = ntohs(us);
+
+ memcpy(&us, buf + 8, 2);
+ qh.nscount = ntohs(us);
+
+ memcpy(&us, buf + 10, 2);
+ qh.arcount = ntohs(us);
+
+ offset = 12;
+ memset(qh.qname, '\0', MAX_QNAME_SZ);
+ x = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
+ if (0 != x)
+ return 0;
+ if ('\0' == qh.qname[0])
+ strcpy(qh.qname, ".");
+ while ((t = strchr(qh.qname, '\n')))
+ *t = ' ';
+ while ((t = strchr(qh.qname, '\r')))
+ *t = ' ';
+ for (t = qh.qname; *t; t++)
+ *t = tolower(*t);
+
+ memcpy(&us, buf + offset, 2);
+ qh.qtype = ntohs(us);
+ memcpy(&us, buf + offset + 2, 2);
+ qh.qclass = ntohs(us);
+
+ qh.length = (uint16_t) len;
+
+ /* gather stats */
+ qtype_counts[qh.qtype]++;
+ qclass_counts[qh.qclass]++;
+ opcode_counts[qh.opcode]++;
+
+ if (Callback != NULL)
+ Callback (&qh);
+
+ return 1;
+}
+
+static int
+handle_udp(const struct udphdr *udp, int len,
+ const struct in6_addr *s_addr,
+ const struct in6_addr *d_addr)
+{
+ char buf[PCAP_SNAPLEN];
+ if ((ntohs (udp->uh_dport) != 53)
+ && (ntohs (udp->uh_sport) != 53))
+ return 0;
+ memcpy(buf, udp + 1, len - sizeof(*udp));
+ if (0 == handle_dns(buf, len - sizeof(*udp), s_addr, d_addr))
+ return 0;
+ return 1;
+}
+
+static int
+handle_ipv6 (struct ip6_hdr *ipv6, int len)
+{
+ char buf[PCAP_SNAPLEN];
+ int offset;
+ int nexthdr;
+
+ struct in6_addr s_addr;
+ struct in6_addr d_addr;
+ uint16_t payload_len;
+
+ offset = sizeof (struct ip6_hdr);
+ nexthdr = ipv6->ip6_nxt;
+ s_addr = ipv6->ip6_src;
+ d_addr = ipv6->ip6_dst;
+ payload_len = ntohs (ipv6->ip6_plen);
+
+ if (ignore_list_match (&s_addr))
+ return (0);
+
+ /* Parse extension headers. This only handles the standard headers, as
+ * defined in RFC 2460, correctly. Fragments are discarded. */
+ while ((IPPROTO_ROUTING == nexthdr) /* routing header */
+ || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */
+ || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */
+ || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
+ || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
+ || (IPPROTO_AH == nexthdr) /* destination options. */
+ || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */
+ {
+ struct ip6_ext ext_hdr;
+ uint16_t ext_hdr_len;
+
+ /* Catch broken packets */
+ if ((offset + sizeof (struct ip6_ext)) > len)
+ return (0);
+
+ /* Cannot handle fragments. */
+ if (IPPROTO_FRAGMENT == nexthdr)
+ return (0);
+
+ memcpy (&ext_hdr, (char *) ipv6 + offset, sizeof (struct ip6_ext));
+ nexthdr = ext_hdr.ip6e_nxt;
+ ext_hdr_len = (8 * (ntohs (ext_hdr.ip6e_len) + 1));
+
+ /* This header is longer than the packets payload.. WTF? */
+ if (ext_hdr_len > payload_len)
+ return (0);
+
+ offset += ext_hdr_len;
+ payload_len -= ext_hdr_len;
+ } /* while */
+
+ /* Catch broken and empty packets */
+ if (((offset + payload_len) > len)
+ || (payload_len == 0)
+ || (payload_len > PCAP_SNAPLEN))
+ return (0);
+
+ if (IPPROTO_UDP != nexthdr)
+ return (0);
+
+ memcpy (buf, (char *) ipv6 + offset, payload_len);
+ if (handle_udp ((struct udphdr *) buf, payload_len, &s_addr, &d_addr) == 0)
+ return (0);
+
+ return (1); /* Success */
+} /* int handle_ipv6 */
+
+static int
+handle_ip(const struct ip *ip, int len)
+{
+ char buf[PCAP_SNAPLEN];
+ int offset = ip->ip_hl << 2;
+ struct in6_addr s_addr;
+ struct in6_addr d_addr;
+
+ if (ip->ip_v == 6)
+ return (handle_ipv6 ((struct ip6_hdr *) ip, len));
+
+ in6_addr_from_buffer (&s_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET);
+ in6_addr_from_buffer (&d_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET);
+ if (ignore_list_match (&s_addr))
+ return (0);
+ if (IPPROTO_UDP != ip->ip_p)
+ return 0;
+ memcpy(buf, (void *) ip + offset, len - offset);
+ if (0 == handle_udp((struct udphdr *) buf, len - offset, &s_addr, &d_addr))
+ return 0;
+ return 1;
+}
+
+#if HAVE_NET_IF_PPP_H
+static int
+handle_ppp(const u_char * pkt, int len)
+{
+ char buf[PCAP_SNAPLEN];
+ unsigned short us;
+ unsigned short proto;
+ if (len < 2)
+ return 0;
+ if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
+ pkt += 2; /* ACFC not used */
+ len -= 2;
+ }
+ if (len < 2)
+ return 0;
+ if (*pkt % 2) {
+ proto = *pkt; /* PFC is used */
+ pkt++;
+ len--;
+ } else {
+ memcpy(&us, pkt, sizeof(us));
+ proto = ntohs(us);
+ pkt += 2;
+ len -= 2;
+ }
+ if (ETHERTYPE_IP != proto && PPP_IP != proto)
+ return 0;
+ memcpy(buf, pkt, len);
+ return handle_ip((struct ip *) buf, len);
+}
+#endif /* HAVE_NET_IF_PPP_H */
+
+static int
+handle_null(const u_char * pkt, int len)
+{
+ unsigned int family;
+ memcpy(&family, pkt, sizeof(family));
+ if (AF_INET != family)
+ return 0;
+ return handle_ip((struct ip *) (pkt + 4), len - 4);
+}
+
+#ifdef DLT_LOOP
+static int
+handle_loop(const u_char * pkt, int len)
+{
+ unsigned int family;
+ memcpy(&family, pkt, sizeof(family));
+ if (AF_INET != ntohl(family))
+ return 0;
+ return handle_ip((struct ip *) (pkt + 4), len - 4);
+}
+
+#endif
+
+#ifdef DLT_RAW
+static int
+handle_raw(const u_char * pkt, int len)
+{
+ return handle_ip((struct ip *) pkt, len);
+}
+
+#endif
+
+static int
+handle_ether(const u_char * pkt, int len)
+{
+ char buf[PCAP_SNAPLEN];
+ struct ether_header *e = (void *) pkt;
+ unsigned short etype = ntohs(e->ether_type);
+ if (len < ETHER_HDR_LEN)
+ return 0;
+ pkt += ETHER_HDR_LEN;
+ len -= ETHER_HDR_LEN;
+ if (ETHERTYPE_8021Q == etype) {
+ etype = ntohs(*(unsigned short *) (pkt + 2));
+ pkt += 4;
+ len -= 4;
+ }
+ if ((ETHERTYPE_IP != etype)
+ && (ETHERTYPE_IPV6 != etype))
+ return 0;
+ memcpy(buf, pkt, len);
+ if (ETHERTYPE_IPV6 == etype)
+ return (handle_ipv6 ((struct ip6_hdr *) buf, len));
+ else
+ return handle_ip((struct ip *) buf, len);
+}
+
+#ifdef DLT_LINUX_SLL
+static int
+handle_linux_sll (const u_char *pkt, int len)
+{
+ struct sll_header
+ {
+ uint16_t pkt_type;
+ uint16_t dev_type;
+ uint16_t addr_len;
+ uint8_t addr[8];
+ uint16_t proto_type;
+ } *hdr;
+ uint16_t etype;
+
+ if (len < sizeof (struct sll_header))
+ return (0);
+
+ hdr = (struct sll_header *) pkt;
+ pkt = (u_char *) (hdr + 1);
+ len -= sizeof (struct sll_header);
+
+ etype = ntohs (hdr->proto_type);
+
+ if ((ETHERTYPE_IP != etype)
+ && (ETHERTYPE_IPV6 != etype))
+ return 0;
+
+ if (ETHERTYPE_IPV6 == etype)
+ return (handle_ipv6 ((struct ip6_hdr *) pkt, len));
+ else
+ return handle_ip((struct ip *) pkt, len);
+}
+#endif /* DLT_LINUX_SLL */
+
+/* public function */
+void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
+{
+ int status;
+
+ fprintf (stderr, "handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n",
+ (void *) udata, (void *) hdr, (void *) pkt,
+ hdr->caplen);
+
+ if (hdr->caplen < ETHER_HDR_LEN)
+ return;
+
+ switch (pcap_datalink (pcap_obj))
+ {
+ case DLT_EN10MB:
+ status = handle_ether (pkt, hdr->caplen);
+ break;
+#if HAVE_NET_IF_PPP_H
+ case DLT_PPP:
+ status = handle_ppp (pkt, hdr->caplen);
+ break;
+#endif
+#ifdef DLT_LOOP
+ case DLT_LOOP:
+ status = handle_loop (pkt, hdr->caplen);
+ break;
+#endif
+#ifdef DLT_RAW
+ case DLT_RAW:
+ status = handle_raw (pkt, hdr->caplen);
+ break;
+#endif
+#ifdef DLT_LINUX_SLL
+ case DLT_LINUX_SLL:
+ status = handle_linux_sll (pkt, hdr->caplen);
+ break;
+#endif
+ case DLT_NULL:
+ status = handle_null (pkt, hdr->caplen);
+ break;
+
+ default:
+ fprintf (stderr, "unsupported data link type %d\n",
+ pcap_datalink(pcap_obj));
+ status = 0;
+ break;
+ } /* switch (pcap_datalink(pcap_obj)) */
+
+ if (0 == status)
+ return;
+
+ query_count_intvl++;
+ query_count_total++;
+ last_ts = hdr->ts;
+}
+#endif /* HAVE_PCAP_H */
+
+const char *qtype_str(int t)
+{
+ static char buf[32];
+ switch (t) {
+ case ns_t_a: return ("A");
+ case ns_t_ns: return ("NS");
+ case ns_t_md: return ("MD");
+ case ns_t_mf: return ("MF");
+ case ns_t_cname: return ("CNAME");
+ case ns_t_soa: return ("SOA");
+ case ns_t_mb: return ("MB");
+ case ns_t_mg: return ("MG");
+ case ns_t_mr: return ("MR");
+ case ns_t_null: return ("NULL");
+ case ns_t_wks: return ("WKS");
+ case ns_t_ptr: return ("PTR");
+ case ns_t_hinfo: return ("HINFO");
+ case ns_t_minfo: return ("MINFO");
+ case ns_t_mx: return ("MX");
+ case ns_t_txt: return ("TXT");
+ case ns_t_rp: return ("RP");
+ case ns_t_afsdb: return ("AFSDB");
+ case ns_t_x25: return ("X25");
+ case ns_t_isdn: return ("ISDN");
+ case ns_t_rt: return ("RT");
+ case ns_t_nsap: return ("NSAP");
+ case ns_t_nsap_ptr: return ("NSAP-PTR");
+ case ns_t_sig: return ("SIG");
+ case ns_t_key: return ("KEY");
+ case ns_t_px: return ("PX");
+ case ns_t_gpos: return ("GPOS");
+ case ns_t_aaaa: return ("AAAA");
+ case ns_t_loc: return ("LOC");
+ case ns_t_nxt: return ("NXT");
+ case ns_t_eid: return ("EID");
+ case ns_t_nimloc: return ("NIMLOC");
+ case ns_t_srv: return ("SRV");
+ case ns_t_atma: return ("ATMA");
+ case ns_t_naptr: return ("NAPTR");
+ case ns_t_kx: return ("KX");
+ case ns_t_cert: return ("CERT");
+ case ns_t_a6: return ("A6");
+ case ns_t_dname: return ("DNAME");
+ case ns_t_sink: return ("SINK");
+ case ns_t_opt: return ("OPT");
+ case ns_t_tsig: return ("TSIG");
+ case ns_t_ixfr: return ("IXFR");
+ case ns_t_axfr: return ("AXFR");
+ case ns_t_mailb: return ("MAILB");
+ case ns_t_maila: return ("MAILA");
+ case ns_t_any: return ("ANY");
+ case ns_t_zxfr: return ("ZXFR");
+ default:
+ snprintf (buf, 32, "#%i", t);
+ buf[31] = '\0';
+ return (buf);
+ }; /* switch (t) */
+ /* NOTREACHED */
+ return (NULL);
+}
+
+const char *opcode_str (int o)
+{
+ static char buf[30];
+ switch (o) {
+ case 0:
+ return "Query";
+ break;
+ case 1:
+ return "Iquery";
+ break;
+ case 2:
+ return "Status";
+ break;
+ case 4:
+ return "Notify";
+ break;
+ case 5:
+ return "Update";
+ break;
+ default:
+ snprintf(buf, 30, "Opcode%d", o);
+ return buf;
+ }
+ /* NOTREACHED */
+}
+
+const char *rcode_str (int rcode)
+{
+ static char buf[32];
+ switch (rcode)
+ {
+ case ns_r_noerror: return ("NOERROR");
+ case ns_r_formerr: return ("FORMERR");
+ case ns_r_servfail: return ("SERVFAIL");
+ case ns_r_nxdomain: return ("NXDOMAIN");
+ case ns_r_notimpl: return ("NOTIMPL");
+ case ns_r_refused: return ("REFUSED");
+ case ns_r_yxdomain: return ("YXDOMAIN");
+ case ns_r_yxrrset: return ("YXRRSET");
+ case ns_r_nxrrset: return ("NXRRSET");
+ case ns_r_notauth: return ("NOTAUTH");
+ case ns_r_notzone: return ("NOTZONE");
+ case ns_r_max: return ("MAX");
+ case ns_r_badsig: return ("BADSIG");
+ case ns_r_badkey: return ("BADKEY");
+ case ns_r_badtime: return ("BADTIME");
+ default:
+ snprintf (buf, 32, "RCode%i", rcode);
+ buf[31] = '\0';
+ return (buf);
+ }
+ /* Never reached */
+ return (NULL);
+} /* const char *rcode_str (int rcode) */
+
+#if 0
+static int
+main(int argc, char *argv[])
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ int x;
+ struct stat sb;
+ int readfile_state = 0;
+ struct bpf_program fp;
+
+ port53 = htons(53);
+ SubReport = Sources_report;
+ ignore_addr.s_addr = 0;
+ progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
+ srandom(time(NULL));
+ ResetCounters();
+
+ while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
+ switch (x) {
+ case 'a':
+ anon_flag = 1;
+ break;
+ case 's':
+ sld_flag = 1;
+ break;
+ case 't':
+ nld_flag = 1;
+ break;
+ case 'p':
+ promisc_flag = 0;
+ break;
+ case 'b':
+ bpf_program_str = strdup(optarg);
+ break;
+ case 'i':
+ ignore_addr.s_addr = inet_addr(optarg);
+ break;
+ case 'f':
+ set_filter(optarg);
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+ device = strdup(argv[0]);
+
+ if (0 == stat(device, &sb))
+ readfile_state = 1;
+ if (readfile_state) {
+ pcap_obj = pcap_open_offline(device, errbuf);
+ } else {
+ pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
+ }
+ if (NULL == pcap_obj) {
+ fprintf(stderr, "pcap_open_*: %s\n", errbuf);
+ exit(1);
+ }
+
+ if (0 == isatty(1)) {
+ if (0 == readfile_state) {
+ fprintf(stderr, "Non-interactive mode requires savefile argument\n");
+ exit(1);
+ }
+ interactive = 0;
+ print_func = printf;
+ }
+
+ memset(&fp, '\0', sizeof(fp));
+ x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0);
+ if (x < 0) {
+ fprintf(stderr, "pcap_compile failed\n");
+ exit(1);
+ }
+ x = pcap_setfilter(pcap_obj, &fp);
+ if (x < 0) {
+ fprintf(stderr, "pcap_setfilter failed\n");
+ exit(1);
+ }
+
+ /*
+ * non-blocking call added for Mac OS X bugfix. Sent by Max Horn.
+ * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
+ */
+ x = pcap_setnonblock(pcap_obj, 1, errbuf);
+ if (x < 0) {
+ fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
+ exit(1);
+ }
+
+ switch (pcap_datalink(pcap_obj)) {
+ case DLT_EN10MB:
+ handle_datalink = handle_ether;
+ break;
+#if HAVE_NET_IF_PPP_H
+ case DLT_PPP:
+ handle_datalink = handle_ppp;
+ break;
+#endif
+#ifdef DLT_LOOP
+ case DLT_LOOP:
+ handle_datalink = handle_loop;
+ break;
+#endif
+#ifdef DLT_RAW
+ case DLT_RAW:
+ handle_datalink = handle_raw;
+ break;
+#endif
+ case DLT_NULL:
+ handle_datalink = handle_null;
+ break;
+ default:
+ fprintf(stderr, "unsupported data link type %d\n",
+ pcap_datalink(pcap_obj));
+ return 1;
+ break;
+ }
+ if (interactive) {
+ init_curses();
+ while (0 == Quit) {
+ if (readfile_state < 2) {
+ /*
+ * On some OSes select() might return 0 even when
+ * there are packets to process. Thus, we always
+ * ignore its return value and just call pcap_dispatch()
+ * anyway.
+ */
+ if (0 == readfile_state) /* interactive */
+ pcap_select(pcap_obj, 1, 0);
+ x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL);
+ }
+ if (0 == x && 1 == readfile_state) {
+ /* block on keyboard until user quits */
+ readfile_state++;
+ nodelay(w, 0);
+ }
+ keyboard();
+ cron_pre();
+ report();
+ cron_post();
+ }
+ endwin(); /* klin, Thu Nov 28 08:56:51 2002 */
+ } else {
+ while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL))
+ (void) 0;
+ cron_pre();
+ Sources_report(); print_func("\n");
+ Destinatioreport(); print_func("\n");
+ Qtypes_report(); print_func("\n");
+ Opcodes_report(); print_func("\n");
+ Tld_report(); print_func("\n");
+ Sld_report(); print_func("\n");
+ Nld_report(); print_func("\n");
+ SldBySource_report();
+ }
+
+ pcap_close(pcap_obj);
+ return 0;
+} /* static int main(int argc, char *argv[]) */
+#endif
+/*
+ * vim:shiftwidth=4:tabstop=8:softtabstop=4
+ */
--- /dev/null
+#ifndef COLLECTD_UTILS_DNS_H
+#define COLLECTD_UTILS_DNS_H 1
+/*
+ * collectd - src/utils_dns.h
+ * Copyright (C) 2006 Florian octo Forster
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ */
+
+#include "config.h"
+
+#include <arpa/nameser.h>
+
+#if HAVE_PCAP_H
+# include <pcap.h>
+#endif
+
+#define T_MAX 65536
+#define OP_MAX 16
+#define C_MAX 65536
+#define MAX_QNAME_SZ 512
+
+struct rfc1035_header_s {
+ uint16_t id;
+ unsigned int qr:1;
+ unsigned int opcode:4;
+ unsigned int aa:1;
+ unsigned int tc:1;
+ unsigned int rd:1;
+ unsigned int ra:1;
+ unsigned int z:1;
+ unsigned int ad:1;
+ unsigned int cd:1;
+ unsigned int rcode:4;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+ uint16_t qtype;
+ uint16_t qclass;
+ char qname[MAX_QNAME_SZ];
+ uint16_t length;
+};
+typedef struct rfc1035_header_s rfc1035_header_t;
+
+extern int qtype_counts[T_MAX];
+extern int opcode_counts[OP_MAX];
+extern int qclass_counts[C_MAX];
+
+#if HAVE_PCAP_H
+void dnstop_set_pcap_obj (pcap_t *po);
+#endif
+void dnstop_set_callback (void (*cb) (const rfc1035_header_t *));
+
+void ignore_list_add_name (const char *name);
+#if HAVE_PCAP_H
+void handle_pcap (u_char * udata, const struct pcap_pkthdr *hdr, const u_char * pkt);
+#endif
+
+const char *qtype_str(int t);
+const char *opcode_str(int o);
+const char *rcode_str (int r);
+
+#endif /* !COLLECTD_UTILS_DNS_H */
--- /dev/null
+/**
+ * collectd - src/utils_ignorelist.c
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * Licence along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ * Authors:
+ * Lubos Stanek <lubek at users.sourceforge.net>
+ **/
+/**
+ * ignorelist handles plugin's list of configured collectable
+ * entries with global ignore action
+ **/
+/**
+ * Usage:
+ *
+ * Define plugin's global pointer variable of type ignorelist_t:
+ * ignorelist_t *myconfig_ignore;
+ * If you know the state of the global ignore (IgnoreSelected),
+ * allocate the variable with:
+ * myconfig_ignore = ignorelist_create (YourKnownIgnore);
+ * If you do not know the state of the global ignore,
+ * initialize the global variable and set the ignore flag later:
+ * myconfig_ignore = ignorelist_init ();
+ * Append single entries in your cf_register'ed callback function:
+ * ignorelist_add (myconfig_ignore, newentry);
+ * When you hit the IgnoreSelected config option,
+ * offer it to the list:
+ * ignorelist_ignore (myconfig_ignore, instantly_got_value_of_ignore);
+ * That is all for the ignorelist initialization.
+ * Later during read and write (plugin's registered functions) get
+ * the information whether this entry would be collected or not:
+ * if (ignorelist_match (myconfig_ignore, thisentry))
+ * return;
+ **/
+
+#include "common.h"
+#include "utils_debug.h"
+#include "utils_ignorelist.h"
+
+/*
+ * private prototypes
+ */
+struct ignorelist_item_s
+{
+#if HAVE_REGEX_H
+ regex_t *rmatch; /* regular expression entry identification */
+#endif
+ char *smatch; /* string entry identification */
+ struct ignorelist_item_s *next;
+};
+typedef struct ignorelist_item_s ignorelist_item_t;
+
+struct ignorelist_s
+{
+ int ignore; /* ignore entries */
+ ignorelist_item_t *head; /* pointer to the first entry */
+};
+
+/* *** *** *** ********************************************* *** *** *** */
+/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */
+/* *** *** *** ********************************************* *** *** *** */
+
+static inline void ignorelist_append (ignorelist_t *il, ignorelist_item_t *item)
+{
+ assert ((il != NULL) && (item != NULL));
+
+ item->next = il->head;
+ il->head = item;
+}
+
+#if HAVE_REGEX_H
+static int ignorelist_append_regex(ignorelist_t *il, const char *entry)
+{
+ int rcompile;
+ regex_t *regtemp;
+ int errsize;
+ char *regerr = NULL;
+ ignorelist_item_t *new;
+
+ /* create buffer */
+ if ((regtemp = malloc(sizeof(regex_t))) == NULL)
+ {
+ syslog (LOG_ERR, "cannot allocate new config entry");
+ return (1);
+ }
+ memset (regtemp, '\0', sizeof(regex_t));
+
+ /* compile regex */
+ if ((rcompile = regcomp (regtemp, entry, REG_EXTENDED)) != 0)
+ {
+ /* prepare message buffer */
+ errsize = regerror(rcompile, regtemp, NULL, 0);
+ if (errsize)
+ regerr = smalloc(errsize);
+ /* get error message */
+ if (regerror (rcompile, regtemp, regerr, errsize))
+ {
+ fprintf (stderr, "Cannot compile regex %s: %i/%s",
+ entry, rcompile, regerr);
+ syslog (LOG_ERR, "Cannot compile regex %s: %i/%s",
+ entry, rcompile, regerr);
+ }
+ else
+ {
+ fprintf (stderr, "Cannot compile regex %s: %i",
+ entry, rcompile);
+ syslog (LOG_ERR, "Cannot compile regex %s: %i",
+ entry, rcompile);
+ }
+
+ if (errsize)
+ sfree (regerr);
+ regfree (regtemp);
+ return (1);
+ }
+ DBG("regex compiled: %s - %i", entry, rcompile);
+
+ /* create new entry */
+ if ((new = malloc(sizeof(ignorelist_item_t))) == NULL)
+ {
+ syslog (LOG_ERR, "cannot allocate new config entry");
+ regfree (regtemp);
+ return (1);
+ }
+ memset (new, '\0', sizeof(ignorelist_item_t));
+ new->rmatch = regtemp;
+
+ /* append new entry */
+ ignorelist_append (il, new);
+
+ return (0);
+} /* int ignorelist_append_regex(ignorelist_t *il, const char *entry) */
+#endif
+
+static int ignorelist_append_string(ignorelist_t *il, const char *entry)
+{
+ ignorelist_item_t *new;
+
+ /* create new entry */
+ if ((new = malloc(sizeof(ignorelist_item_t))) == NULL )
+ {
+ syslog (LOG_ERR, "cannot allocate new entry");
+ return (1);
+ }
+ memset (new, '\0', sizeof(ignorelist_item_t));
+ new->smatch = sstrdup(entry);
+
+ /* append new entry */
+ ignorelist_append (il, new);
+
+ return (0);
+} /* int ignorelist_append_string(ignorelist_t *il, const char *entry) */
+
+#if HAVE_REGEX_H
+/*
+ * check list for entry regex match
+ * return 1 if found
+ */
+static int ignorelist_match_regex (ignorelist_item_t *item, const char *entry)
+{
+ assert ((item != NULL) && (item->rmatch != NULL)
+ && (entry != NULL) && (strlen (entry) > 0));
+
+ /* match regex */
+ if (regexec (item->rmatch, entry, 0, NULL, 0) == 0)
+ return (1);
+
+ return (0);
+} /* int ignorelist_match_regex (ignorelist_item_t *item, const char *entry) */
+#endif
+
+/*
+ * check list for entry string match
+ * return 1 if found
+ */
+static int ignorelist_match_string (ignorelist_item_t *item, const char *entry)
+{
+ assert ((item != NULL) && (item->smatch != NULL)
+ && (entry != NULL) && (strlen (entry) > 0));
+
+ if (strcmp (entry, item->smatch) == 0)
+ return (1);
+
+ return (0);
+} /* int ignorelist_match_string (ignorelist_item_t *item, const char *entry) */
+
+
+/* *** *** *** ******************************************** *** *** *** */
+/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */
+/* *** *** *** ******************************************** *** *** *** */
+
+/*
+ * create the ignorelist_t with known ignore state
+ * return pointer to ignorelist_t
+ */
+ignorelist_t *ignorelist_create (int invert)
+{
+ ignorelist_t *il;
+
+ /* smalloc exits if it failes */
+ il = (ignorelist_t *) smalloc (sizeof (ignorelist_t));
+ DBG("Ignorelist created 0x%p, default is %s",
+ (void *) il,
+ invert ? "collect" : "ignore");
+
+ memset (il, '\0', sizeof (ignorelist_t));
+
+ /*
+ * ->ignore == 0 => collect
+ * ->ignore == 1 => ignore
+ */
+ il->ignore = invert ? 0 : 1;
+
+ return (il);
+} /* ignorelist_t *ignorelist_create (int ignore) */
+
+/*
+ * free memory used by ignorelist_t
+ */
+void ignorelist_free (ignorelist_t *il)
+{
+ ignorelist_item_t *this;
+ ignorelist_item_t *next;
+
+ DBG ("(il = 0x%p)", (void *) il);
+
+ if (il == NULL)
+ return;
+
+ for (this = il->head; this != NULL; this = next)
+ {
+ next = this->next;
+#if HAVE_REGEX_H
+ if (this->rmatch != NULL)
+ {
+ regfree (this->rmatch);
+ this->rmatch = NULL;
+ }
+#endif
+ if (this->smatch != NULL)
+ {
+ sfree (this->smatch);
+ this->smatch = NULL;
+ }
+ sfree (this);
+ }
+
+ sfree (il);
+ il = NULL;
+} /* void ignorelist_destroy (ignorelist_t *il) */
+
+/*
+ * set ignore state of the ignorelist_t
+ */
+void ignorelist_set_invert (ignorelist_t *il, int invert)
+{
+ if (il == NULL)
+ {
+ DBG("ignore call with ignorelist_t == NULL");
+ return;
+ }
+
+ il->ignore = invert ? 0 : 1;
+} /* void ignorelist_set_invert (ignorelist_t *il, int ignore) */
+
+/*
+ * append entry into ignorelist_t
+ * return 1 for success
+ */
+int ignorelist_add (ignorelist_t *il, const char *entry)
+{
+ int ret;
+ size_t entry_len;
+
+ if (il == NULL)
+ {
+ DBG ("add called with ignorelist_t == NULL");
+ return (1);
+ }
+
+ entry_len = strlen (entry);
+
+ /* append nothing */
+ if (entry_len == 0)
+ {
+ DBG("not appending: empty entry");
+ return (1);
+ }
+
+#if HAVE_REGEX_H
+ /* regex string is enclosed in "/.../" */
+ if ((entry_len > 2) && (entry[0] == '/') && entry[entry_len - 1] == '/')
+ {
+ char *entry_copy;
+
+ /* We need to copy `entry' since it's const */
+ entry_copy = smalloc (entry_len);
+ memset (entry_copy, '\0', entry_len);
+ strncpy (entry_copy, entry + 1, entry_len - 2);
+
+ DBG("I'm about to add regex entry: %s", entry_copy);
+ ret = ignorelist_append_regex(il, entry_copy);
+ sfree (entry_copy);
+ }
+ else
+#endif
+ {
+ DBG("to add entry: %s", entry);
+ ret = ignorelist_append_string(il, entry);
+ }
+
+ return (ret);
+} /* int ignorelist_add (ignorelist_t *il, const char *entry) */
+
+/*
+ * check list for entry
+ * return 1 for ignored entry
+ */
+int ignorelist_match (ignorelist_t *il, const char *entry)
+{
+ ignorelist_item_t *traverse;
+
+ assert (il != NULL);
+
+ /* if no entries, collect all */
+ if (il->head == NULL)
+ return (0);
+
+ if ((entry == NULL) || (strlen (entry) == 0))
+ return (0);
+
+ /* traverse list and check entries */
+ for (traverse = il->head; traverse != NULL; traverse = traverse->next)
+ {
+#if HAVE_REGEX_H
+ if (traverse->rmatch != NULL)
+ {
+ if (ignorelist_match_regex (traverse, entry))
+ return (il->ignore);
+ }
+ else
+#endif
+ {
+ if (ignorelist_match_string (traverse, entry))
+ return (il->ignore);
+ }
+ } /* for traverse */
+
+ return (1 - il->ignore);
+} /* int ignorelist_match (ignorelist_t *il, const char *entry) */
+
--- /dev/null
+/**
+ * collectd - src/utils_ignorelist.h
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * Licence along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ * Authors:
+ * Lubos Stanek <lubek at users.sourceforge.net>
+ **/
+/**
+ * ignorelist handles plugin's list of configured collectable
+ * entries with global ignore action
+ **/
+
+#ifndef UTILS_IGNORELIST_H
+#define UTILS_IGNORELIST_H 1
+
+#include "collectd.h"
+
+#if HAVE_REGEX_H
+# include <regex.h>
+#endif
+
+/* public prototypes */
+
+struct ignorelist_s;
+typedef struct ignorelist_s ignorelist_t;
+
+/*
+ * create the ignorelist_t with known ignore state
+ * return pointer to ignorelist_t
+ */
+ignorelist_t *ignorelist_create (int invert);
+
+/*
+ * free memory used by ignorelist_t
+ */
+void ignorelist_free (ignorelist_t *il);
+
+/*
+ * set ignore state of the ignorelist_t
+ */
+void ignorelist_set_invert (ignorelist_t *il, int invert);
+
+/*
+ * append entry to ignorelist_t
+ * returns zero on success, non-zero upon failure.
+ */
+int ignorelist_add (ignorelist_t *il, const char *entry);
+
+/*
+ * check list for entry
+ * return 1 for ignored entry
+ */
+int ignorelist_match (ignorelist_t *il, const char *entry);
+
+#endif /* UTILS_IGNORELIST_H */
DIR *proc;
struct dirent *dent; /* 42 */
+ static complain_t complain_obj;
+
errno = 0;
- if (NULL == (proc = opendir (PROCDIR))) {
- syslog (LOG_ERR, "Cannot open '%s': %s", PROCDIR, strerror (errno));
+ if (NULL == (proc = opendir (PROCDIR)))
+ {
+ plugin_complain (LOG_ERR, &complain_obj, "vserver plugin: "
+ "fopen (%s) failed: %s", PROCDIR, strerror (errno));
return;
}
+ plugin_relief (LOG_NOTICE, &complain_obj, "vserver plugin: "
+ "fopen (%s) succeeded.", PROCDIR);
while (NULL != (dent = readdir (proc))) {
int len;