1 |
stephdl |
1.1 |
#!/usr/bin/perl |
2 |
|
|
# $Id: qpsmtpd-plugin-file_connection,v 1.1 2013/02/02 00:59:45 slords Exp $ |
3 |
|
|
|
4 |
|
|
=head1 NAME |
5 |
|
|
|
6 |
|
|
file_connection - Simple per session log-to-file logging for qpsmtpd |
7 |
|
|
|
8 |
|
|
=head1 DESCRIPTION |
9 |
|
|
|
10 |
|
|
The 'file_connection' logging plugin for qpsmtpd records qpsmtpd log messages into a |
11 |
|
|
file (or a named pipe, if you prefer.) |
12 |
|
|
|
13 |
|
|
The file is reopened for each connection. To facilitate automatic |
14 |
|
|
logfile switching the filename can contain strftime conversion |
15 |
|
|
specifiers, which are expanded immediately before opening the file. This |
16 |
|
|
ensures that a single connection is never split across logfiles. |
17 |
|
|
|
18 |
|
|
The list of supported conversion specifiers depends on the strftime |
19 |
|
|
implementation of your C library. See strftime(3) for details. |
20 |
|
|
Additionally, %i exands to a (hopefully) unique session-id. |
21 |
|
|
|
22 |
|
|
|
23 |
|
|
=head1 CONFIGURATION |
24 |
|
|
|
25 |
|
|
To enable the logging plugin, add a line of this form to the qpsmtpd plugins |
26 |
|
|
configuration file: |
27 |
|
|
|
28 |
|
|
=over |
29 |
|
|
|
30 |
|
|
logging/file_connection [loglevel I<level>] I<path> |
31 |
|
|
|
32 |
|
|
For example: |
33 |
|
|
|
34 |
|
|
logging/file_connection loglevel LOGINFO /var/log/qpsmtpd/%Y-%m-%d |
35 |
|
|
|
36 |
|
|
=back |
37 |
|
|
|
38 |
|
|
Multiple instances of the plugin can be configured by appending :I<N> for any |
39 |
|
|
integer(s) I<N>, to log to multiple files simultaneously, e.g. to log critical |
40 |
|
|
errors and normally verbose logs elsewhere. |
41 |
|
|
|
42 |
|
|
The following optional configuration setting can be supplied: |
43 |
|
|
|
44 |
|
|
=over |
45 |
|
|
|
46 |
|
|
=item loglevel I<loglevel> |
47 |
|
|
|
48 |
|
|
The internal log level below which messages will be logged. The I<loglevel> |
49 |
|
|
given should be chosen from this list. Priorities count downward (for example, |
50 |
|
|
if LOGWARN were selected, LOGERROR, LOGCRIT and LOGEMERG messages would be |
51 |
|
|
logged as well): |
52 |
|
|
|
53 |
|
|
=over |
54 |
|
|
|
55 |
|
|
=item B<LOGDEBUG> |
56 |
|
|
|
57 |
|
|
=item B<LOGINFO> |
58 |
|
|
|
59 |
|
|
=item B<LOGNOTICE> |
60 |
|
|
|
61 |
|
|
=item B<LOGWARN> |
62 |
|
|
|
63 |
|
|
=item B<LOGERROR> |
64 |
|
|
|
65 |
|
|
=item B<LOGCRIT> |
66 |
|
|
|
67 |
|
|
=item B<LOGALERT> |
68 |
|
|
|
69 |
|
|
=item B<LOGEMERG> |
70 |
|
|
|
71 |
|
|
=back |
72 |
|
|
|
73 |
|
|
=back |
74 |
|
|
|
75 |
|
|
|
76 |
|
|
The chosen I<path> should be writable by the user running qpsmtpd; it will be |
77 |
|
|
created it did not already exist, and appended to otherwise. |
78 |
|
|
|
79 |
|
|
=head1 AUTHOR |
80 |
|
|
|
81 |
|
|
Peter J. Holzer <hjp@hjp.at>, based on a plugin by |
82 |
|
|
Devin Carraway <qpsmtpd@devin.com> |
83 |
|
|
|
84 |
|
|
=head1 LICENSE |
85 |
|
|
|
86 |
|
|
Copyright (c) 2005, Devin Carraway. |
87 |
|
|
|
88 |
|
|
This plugin is licensed under the same terms as the qpsmtpd package itself. |
89 |
|
|
Please see the LICENSE file included with qpsmtpd for details. |
90 |
|
|
|
91 |
|
|
=cut |
92 |
|
|
|
93 |
|
|
use strict; |
94 |
|
|
use warnings; |
95 |
|
|
|
96 |
|
|
use IO::File; |
97 |
|
|
#use Sys::Hostname; |
98 |
|
|
use POSIX qw(strftime); |
99 |
|
|
|
100 |
|
|
sub register { |
101 |
|
|
my ($self, $qp, @args) = @_; |
102 |
|
|
my %args; |
103 |
|
|
|
104 |
|
|
$self->{_loglevel} = LOGWARN; |
105 |
|
|
|
106 |
|
|
while (1) { |
107 |
|
|
last if !@args; |
108 |
|
|
if (lc $args[0] eq 'loglevel') { |
109 |
|
|
shift @args; |
110 |
|
|
my $ll = shift @args; |
111 |
|
|
if (!defined $ll) { |
112 |
|
|
warn "Malformed arguments to logging/file_connection plugin"; |
113 |
|
|
return; |
114 |
|
|
} |
115 |
|
|
if ($ll =~ /^(\d+)$/) { |
116 |
|
|
$self->{_loglevel} = $1; |
117 |
|
|
} |
118 |
|
|
elsif ($ll =~ /^(LOG\w+)$/) { |
119 |
|
|
$self->{_loglevel} = log_level($1); |
120 |
|
|
defined $self->{_loglevel} or $self->{_loglevel} = LOGWARN; |
121 |
|
|
} |
122 |
|
|
} |
123 |
|
|
else { last } |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
unless (@args && $args[0]) { |
127 |
|
|
warn "Malformed arguments to syslog plugin"; |
128 |
|
|
return; |
129 |
|
|
} |
130 |
|
|
|
131 |
|
|
$self->{_logfile} = join(' ', @args); |
132 |
|
|
$self->{_log_session_id_prefix} = sprintf("%08x%04x", time(), $$); |
133 |
|
|
$self->{_log_session_id_counter} = 0; |
134 |
|
|
|
135 |
|
|
$self->register_hook('logging', 'write_log'); |
136 |
|
|
$self->register_hook('pre-connection', 'open_log'); |
137 |
|
|
$self->open_log($qp); |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
sub open_log { |
141 |
|
|
my ($self, $qp) = @_; |
142 |
|
|
my $output = $self->{_logfile}; |
143 |
|
|
$self->{_log_session_id} = |
144 |
|
|
$self->{_log_session_id_prefix} . "." . |
145 |
|
|
++$self->{_log_session_id_counter}; |
146 |
|
|
|
147 |
|
|
$output =~ s/%i/$self->{_log_session_id}/; |
148 |
|
|
$output = strftime($output, localtime); |
149 |
|
|
#print STDERR "open_log: output=$output, uid=$>\n"; |
150 |
|
|
if ($output =~ /^\s*\|(.*)/) { |
151 |
|
|
unless ($self->{_f} = new IO::File "|$1") { |
152 |
|
|
warn "Error opening log output to command $1: $!"; |
153 |
|
|
return; |
154 |
|
|
} |
155 |
|
|
} elsif ($output =~ /^(.*)/) { # detaint |
156 |
|
|
unless ($self->{_f} = new IO::File ">>$1") { |
157 |
|
|
warn "Error opening log output to path $1: $!"; |
158 |
|
|
return; |
159 |
|
|
} |
160 |
|
|
} |
161 |
|
|
$self->{_f}->autoflush(1); |
162 |
|
|
|
163 |
|
|
return DECLINED; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
sub write_log { |
167 |
|
|
my ($self, $txn, $trace, $hook, $plugin, @log) = @_; |
168 |
|
|
|
169 |
|
|
return DECLINED if $trace > $self->{_loglevel}; |
170 |
|
|
return DECLINED if defined $plugin and $plugin eq $self->plugin_name; |
171 |
|
|
$self->open_log unless($self->{_f}); |
172 |
|
|
|
173 |
|
|
my $f = $self->{_f}; |
174 |
|
|
print STDERR "no open file\n" unless (defined $f); |
175 |
|
|
print $f join(" ", |
176 |
|
|
strftime("%Y-%m-%dT%H:%M:%S%z", localtime), $self->{_log_session_id}, |
177 |
|
|
(defined $plugin ? " $plugin plugin:" : |
178 |
|
|
defined $hook ? " running plugin ($hook):" : ""), |
179 |
|
|
@log), "\n"; |
180 |
|
|
return DECLINED; |
181 |
|
|
} |
182 |
|
|
|
183 |
|
|
# vi: tabstop=4 shiftwidth=4 expandtab: |
184 |
|
|
|