On 2015-9-22 17:16 , Steffen Kaiser wrote: > I had SpamAssassin rules allocating about 100MB, the forked children > only shared the C libraries after some time. That's a problem of > Perl's way to handle rereferences to data.
It might help not to integrate SpamAssassin, but to use spamc to communicate
with spamd. However, that only saves memory if you have other stuff in your
filter rules that take time processing messages, like DNS blacklist checks,
virus scanners, SPF/DKIM/DMARC processing, SMTP forward lookups, etc.
That way, perl processes doing those other things do not have a big lump of
SpamAssassin rules sitting in memory (which is usually quite a lot of memory
due to the way spamassassin works). You'd generally need fewer spamassassin
slaves than mimedefang slaves (if you don't, then this won't save memory but
eat a bit more memory instead because of the extra perl processes involved).
On the other hand, it does make things a bit more complex because you have to
manage another daemon, monitor it, restart when rules change, maintain configs,
etc. Per-recipient rules might be somewhat harder.
Oh, and stock mimedefang doesn't support it. I've attached the SpamC.pm that we
use for spamd communication. Also make sure that you set
$Features{"SpamAssassin"} = 0 in your filter, to prevent Mail::SpamAssassin
from loading (otherwise your mimedefang slaves would still eat memory for
spamassassin).
You will need to modify this SpamC.pm as it uses a modular Mimedefang.pm, but
the changes should be trivial.
--
Jan-Pieter Cornet <[email protected]>
"Any sufficiently advanced incompetence is indistinguishable from malice."
- Grey's Law
package MailFilter::SpamC;
# provide spamc interface to spamassassin, call-compatible with mimedefang
# API
# ... mostly. It actually only provides spam_assassin_check().
use Mimedefang qw(gen_msgid_header synthesize_received_header
:global :logging :config);
use IPC::Open2;
use base Exporter;
our @SpamAssassinExtraHeaders;
our @EXPORT_OK = qw(
spam_assassin_check
@SpamAssassinExtraHeaders
);
my $spamc = "/usr/bin/spamc";
my @spamc_opts = qw(-F /etc/spamd/spamc.conf);
sub spam_assassin_check {
### open communications to spamc
my $in;
unless ( open $in, "<", "./INPUTMSG" ) {
md_syslog('err', "$MsgID: Spamc error: Cannot read INPUTMSG: $!");
return;
}
my($sprd, $spwr);
my $sp_pid = open2($sprd, $spwr, $spamc, @spamc_opts);
unless ( $sp_pid ) {
md_syslog('err', "$MsgID: Spamc error: Cannot fork $spamc: $!");
return;
}
### note: the lines below duplicate the effect in the real
### spam_assassin_check somewhat
### build complete headers
my $hdrs = "Return-Path: $Sender\n" .
synthesize_received_header();
$hdrs .= gen_msgid_header() if ($MessageID eq "NOQUEUE");
### get message headers, remember if we had a "To:" header
my($seen_to, $seen_eoh);
while ( <$in> ) {
if ( /^$/ ) {
$seen_eoh++;
last;
}
$seen_to++ if /^To:/i;
$hdrs .= $_;
}
$hdrs .= "To: undisclosed-recipients:;\n" if !$seen_to;
if ( $AddApparentlyToForSpamAssassin and @Recipients ) {
$hdrs .= "Apparently-To: " . join(", ", @Recipients) . "\n";
}
$hdrs .= join("", @SpamAssassinExtraHeaders);
### add header-body separation line that we ate in the loop above
$hdrs .= "\n";
### $hdrs now contains the complete headers as sent to spamc
### send headers to spamc
print $spwr $hdrs;
### send rest of message (if there was any left)
if ( $seen_eoh ) {
print $spwr $_ while <$in>;
}
close $spwr;
### wait for result
my $output = join("", <$sprd>);
close $sprd;
waitpid($sp_pid, 0);
if ( $? ) {
md_syslog('err', "$MsgID: spamc returned non-zero exit code: $?\n");
return;
}
my($hits, $req, $names, $report, %sa_tags);
### first line is hits/req
if ( $output =~ s!\A(-?\d+(?:\.\d+)?)/(-?\d+(?:\.\d+)?)\r?\n!! ) {
($hits, $req) = ($1, $2);
} else {
my $sample = $output;
if ( length($sample) > 80 ) {
$sample = substr($sample, 0, 80) . "...";
}
$sample =~ s{[^ -~]}{sprintf("\\x%02x", ord $1)}ge;
md_syslog('err',
"$MsgID: Error: spamc returned invalid output: $sample");
return;
}
### process rest of output
while ( $output =~ s/\A(\w+):\s+(.*)\r?\n// ) {
my($k,$v) = ($1,$2);
$hits = $v, next if $k eq "Score";
$req = $v, next if $k eq "Required";
$names = $v, next if $k eq "Tests";
$sa_tags{$k} = $v;
}
### anything that is left now is the full report
$output =~ s/^\s+//;
$report = $output;
return($hits, $req, $names, $report, \%sa_tags);
}
1;
signature.asc
Description: OpenPGP digital signature
_______________________________________________ NOTE: If there is a disclaimer or other legal boilerplate in the above message, it is NULL AND VOID. You may ignore it. Visit http://www.mimedefang.org and http://www.roaringpenguin.com MIMEDefang mailing list [email protected] http://lists.roaringpenguin.com/mailman/listinfo/mimedefang

