#!/usr/bin/perl # $Id: qpsmtpd-plugin-file_connection,v 1.1 2013/02/02 00:59:45 slords Exp $ =head1 NAME file_connection - Simple per session log-to-file logging for qpsmtpd =head1 DESCRIPTION The 'file_connection' logging plugin for qpsmtpd records qpsmtpd log messages into a file (or a named pipe, if you prefer.) The file is reopened for each connection. To facilitate automatic logfile switching the filename can contain strftime conversion specifiers, which are expanded immediately before opening the file. This ensures that a single connection is never split across logfiles. The list of supported conversion specifiers depends on the strftime implementation of your C library. See strftime(3) for details. Additionally, %i exands to a (hopefully) unique session-id. =head1 CONFIGURATION To enable the logging plugin, add a line of this form to the qpsmtpd plugins configuration file: =over logging/file_connection [loglevel I] I For example: logging/file_connection loglevel LOGINFO /var/log/qpsmtpd/%Y-%m-%d =back Multiple instances of the plugin can be configured by appending :I for any integer(s) I, to log to multiple files simultaneously, e.g. to log critical errors and normally verbose logs elsewhere. The following optional configuration setting can be supplied: =over =item loglevel I The internal log level below which messages will be logged. The I given should be chosen from this list. Priorities count downward (for example, if LOGWARN were selected, LOGERROR, LOGCRIT and LOGEMERG messages would be logged as well): =over =item B =item B =item B =item B =item B =item B =item B =item B =back =back The chosen I should be writable by the user running qpsmtpd; it will be created it did not already exist, and appended to otherwise. =head1 AUTHOR Peter J. Holzer , based on a plugin by Devin Carraway =head1 LICENSE Copyright (c) 2005, Devin Carraway. This plugin is licensed under the same terms as the qpsmtpd package itself. Please see the LICENSE file included with qpsmtpd for details. =cut use strict; use warnings; use IO::File; #use Sys::Hostname; use POSIX qw(strftime); sub register { my ($self, $qp, @args) = @_; my %args; $self->{_loglevel} = LOGWARN; while (1) { last if !@args; if (lc $args[0] eq 'loglevel') { shift @args; my $ll = shift @args; if (!defined $ll) { warn "Malformed arguments to logging/file_connection plugin"; return; } if ($ll =~ /^(\d+)$/) { $self->{_loglevel} = $1; } elsif ($ll =~ /^(LOG\w+)$/) { $self->{_loglevel} = log_level($1); defined $self->{_loglevel} or $self->{_loglevel} = LOGWARN; } } else { last } } unless (@args && $args[0]) { warn "Malformed arguments to syslog plugin"; return; } $self->{_logfile} = join(' ', @args); $self->{_log_session_id_prefix} = sprintf("%08x%04x", time(), $$); $self->{_log_session_id_counter} = 0; $self->register_hook('logging', 'write_log'); $self->register_hook('pre-connection', 'open_log'); $self->open_log($qp); } sub open_log { my ($self, $qp) = @_; my $output = $self->{_logfile}; $self->{_log_session_id} = $self->{_log_session_id_prefix} . "." . ++$self->{_log_session_id_counter}; $output =~ s/%i/$self->{_log_session_id}/; $output = strftime($output, localtime); #print STDERR "open_log: output=$output, uid=$>\n"; if ($output =~ /^\s*\|(.*)/) { unless ($self->{_f} = new IO::File "|$1") { warn "Error opening log output to command $1: $!"; return; } } elsif ($output =~ /^(.*)/) { # detaint unless ($self->{_f} = new IO::File ">>$1") { warn "Error opening log output to path $1: $!"; return; } } $self->{_f}->autoflush(1); return DECLINED; } sub write_log { my ($self, $txn, $trace, $hook, $plugin, @log) = @_; return DECLINED if $trace > $self->{_loglevel}; return DECLINED if defined $plugin and $plugin eq $self->plugin_name; $self->open_log unless($self->{_f}); my $f = $self->{_f}; print STDERR "no open file\n" unless (defined $f); print $f join(" ", strftime("%Y-%m-%dT%H:%M:%S%z", localtime), $self->{_log_session_id}, (defined $plugin ? " $plugin plugin:" : defined $hook ? " running plugin ($hook):" : ""), @log), "\n"; return DECLINED; } # vi: tabstop=4 shiftwidth=4 expandtab: