Merge branch 'master' of octo@verplant.org:/var/lib/git/collectd
authorFlorian Forster <octo@crystal.wlan.home.verplant.org>
Wed, 6 Dec 2006 08:59:20 +0000 (09:59 +0100)
committerFlorian Forster <octo@crystal.wlan.home.verplant.org>
Wed, 6 Dec 2006 08:59:20 +0000 (09:59 +0100)
README
contrib/SpamAssassin/Collectd.pm [new file with mode: 0644]
contrib/SpamAssassin/example.cf [new file with mode: 0644]
contrib/collection.cgi
src/collectd.conf.pod
src/collectd.pod
src/email.c
src/sensors.c
src/utils_dns.c

diff --git a/README b/README
index ac1ba15..3665099 100644 (file)
--- a/README
+++ b/README
@@ -39,6 +39,9 @@ Features
     - Disk utilization
       (Sectors read/written, number of read/write actions, time spent doing IO)
 
+    - DNS traffic
+      (query types, response codes, opcodes and traffic)
+
     - Harddisk temperatures
       (Uhm, yeah, temperature of harddisks that is ;)
 
diff --git a/contrib/SpamAssassin/Collectd.pm b/contrib/SpamAssassin/Collectd.pm
new file mode 100644 (file)
index 0000000..d3b103e
--- /dev/null
@@ -0,0 +1,168 @@
+#!/usr/bin/perl
+# $Id: Collectd.pm 4 2006-12-02 15:18:14Z formorer $
+
+=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 
+=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> 
+ Licensed under the Apache License,  Version 2.0 (the "License"); 
+ you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required 
+ by applicable law or agreed to in writing, software distributed 
+ under the License is distributed on an "AS IS" BASIS, WITHOUT 
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ See the License for the specific language governing permissions 
+ and limitations under the License.
+
+=cut
+
+package Mail::SpamAssassin::Plugin::Collectd;
+
+use Mail::SpamAssassin::Plugin;
+use Mail::SpamAssassin::Logger;
+use strict;
+use bytes; 
+use warnings;
+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,
+    });
+
+    $conf->{parser}->register_commands(\@cmds);
+}
+
+sub check_end {
+    my ($self, $params) = @_;
+    my $message_status = $params->{permsgstatus};
+       #create  new connection to our socket
+       my $sock = new IO::Socket::UNIX ( $self->{main}->{conf}->{collectd_socket});
+       # debug some informations if collectd is not running or anything else went
+       # wrong
+       if ( ! $sock ) {
+               dbg("collect: could not connect to " .
+                       $self->{main}->{conf}->{collectd_socket} . ": $! - collectd plugin
+                       disabled"); 
+               return 0; 
+       }
+       $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); 
+}
+
+1;
+
+# vim: syntax=perl sw=4 ts=4 noet shiftround
diff --git a/contrib/SpamAssassin/example.cf b/contrib/SpamAssassin/example.cf
new file mode 100644 (file)
index 0000000..31e763c
--- /dev/null
@@ -0,0 +1,2 @@
+collectd_buffersize 256
+collectd_socket /tmp/.collectd-email
index ddf4465..7494202 100755 (executable)
@@ -653,6 +653,18 @@ our $GraphDefs;
                        '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",
@@ -1132,6 +1144,7 @@ our $GraphArgs =
        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'],
index f521571..3e544e1 100644 (file)
@@ -160,6 +160,23 @@ TCP-Port to connect to. Defaults to B<3551>.
 
 =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<df>
 
 =over 4
index 5bd8278..b0af26d 100644 (file)
@@ -40,6 +40,10 @@ Disk and partition usage/throughput (I<disk>)
 
 =item
 
+DNS traffic (I<dns>)
+
+=item
+
 Harddisk temperatures (I<hddtemp>)
 
 =item
index 404a20a..9fd05ba 100644 (file)
@@ -144,6 +144,7 @@ static int disabled = 0;
 
 /* thread managing "client" connections */
 static pthread_t connector;
+static int connector_socket;
 
 /* tell the connector thread that a collector is available */
 static pthread_cond_t collector_available = PTHREAD_COND_INITIALIZER;
@@ -533,13 +534,11 @@ static void *collect (void *arg)
 
 static void *open_connection (void *arg)
 {
-       int local = 0;
-
        struct sockaddr_un addr;
 
        /* create UNIX socket */
        errno = 0;
-       if (-1 == (local = socket (PF_UNIX, SOCK_STREAM, 0))) {
+       if (-1 == (connector_socket = socket (PF_UNIX, SOCK_STREAM, 0))) {
                disabled = 1;
                syslog (LOG_ERR, "socket() failed: %s", strerror (errno));
                pthread_exit ((void *)1);
@@ -552,7 +551,7 @@ static void *open_connection (void *arg)
        unlink (addr.sun_path);
 
        errno = 0;
-       if (-1 == bind (local, (struct sockaddr *)&addr,
+       if (-1 == bind (connector_socket, (struct sockaddr *)&addr,
                                offsetof (struct sockaddr_un, sun_path)
                                        + strlen(addr.sun_path))) {
                disabled = 1;
@@ -561,7 +560,7 @@ static void *open_connection (void *arg)
        }
 
        errno = 0;
-       if (-1 == listen (local, 5)) {
+       if (-1 == listen (connector_socket, 5)) {
                disabled = 1;
                syslog (LOG_ERR, "listen() failed: %s", strerror (errno));
                pthread_exit ((void *)1);
@@ -628,7 +627,7 @@ static void *open_connection (void *arg)
 
                do {
                        errno = 0;
-                       if (-1 == (remote = accept (local, NULL, NULL))) {
+                       if (-1 == (remote = accept (connector_socket, NULL, NULL))) {
                                if (EINTR != errno) {
                                        disabled = 1;
                                        syslog (LOG_ERR, "accept() failed: %s", strerror (errno));
@@ -713,6 +712,31 @@ static void email_init (void)
        return;
 } /* static void email_init (void) */
 
+#if EMAIL_HAVE_READ
+static void email_shutdown (void)
+{
+       collector_t *ptr;
+
+       if (disabled)
+               return;
+
+       close (connector_socket);
+       pthread_kill (connector, SIGTERM);
+
+       pthread_mutex_lock (&active_mutex);
+
+       for (ptr = active.head; NULL != ptr; ptr = ptr->next) {
+               close (ptr->socket);
+               pthread_kill (ptr->thread, SIGTERM);
+       }
+
+       pthread_mutex_unlock (&active_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] = "";
@@ -846,6 +870,7 @@ void module_register (void)
        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;
index 7cf787c..ff8b580 100644 (file)
@@ -178,7 +178,7 @@ static ignorelist_t *sensor_list;
  */
 static int sensor_extended_naming = 0;
 
-#ifdef HAVE_LIBSENSORS
+#if SENSORS_HAVE_READ
 typedef struct featurelist
 {
        const sensors_chip_name    *chip;
@@ -188,7 +188,7 @@ typedef struct featurelist
 } featurelist_t;
 
 featurelist_t *first_feature = NULL;
-#endif /* defined (HAVE_LIBSENSORS) */
+#endif /* if SENSORS_HAVE_READ */
 
 static int sensors_config (char *key, char *value)
 {
@@ -230,7 +230,7 @@ static int sensors_config (char *key, char *value)
 
 static void collectd_sensors_init (void)
 {
-#ifdef HAVE_LIBSENSORS
+#if SENSORS_HAVE_READ
        FILE *fh;
        featurelist_t *last_feature = NULL;
        featurelist_t *new_feature;
@@ -330,11 +330,30 @@ static void collectd_sensors_init (void)
 
        if (first_feature == NULL)
                sensors_cleanup ();
-#endif /* defined(HAVE_LIBSENSORS) */
+#endif /* if SENSORS_HAVE_READ */
 
        return;
 }
 
+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;
+       }
+
+       sensors_cleanup ();
+#endif /* if SENSORS_HAVE_READ */
+
+       ignorelist_free (sensor_list);
+}
+
 static void sensors_voltage_write (char *host, char *inst, char *val)
 {
        char file[BUFSIZE];
@@ -474,6 +493,7 @@ 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);
 }
 
index 221bac4..e9aec38 100644 (file)
@@ -599,6 +599,40 @@ handle_ether(const u_char * pkt, int len)
        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)
 {
@@ -631,6 +665,11 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt
            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;