- 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 ;)
--- /dev/null
+#!/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
--- /dev/null
+collectd_buffersize 256
+collectd_socket /tmp/.collectd-email
'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",
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'],
=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
=item
+DNS traffic (I<dns>)
+
+=item
+
Harddisk temperatures (I<hddtemp>)
=item
/* 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;
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);
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;
}
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);
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));
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] = "";
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;
*/
static int sensor_extended_naming = 0;
-#ifdef HAVE_LIBSENSORS
+#if SENSORS_HAVE_READ
typedef struct featurelist
{
const sensors_chip_name *chip;
} featurelist_t;
featurelist_t *first_feature = NULL;
-#endif /* defined (HAVE_LIBSENSORS) */
+#endif /* if SENSORS_HAVE_READ */
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;
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];
{
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);
}
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)
{
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;