diff options
Diffstat (limited to 'buildbot/buildbot/test/mail/svn-commit.2')
-rw-r--r-- | buildbot/buildbot/test/mail/svn-commit.2 | 1218 |
1 files changed, 1218 insertions, 0 deletions
diff --git a/buildbot/buildbot/test/mail/svn-commit.2 b/buildbot/buildbot/test/mail/svn-commit.2 new file mode 100644 index 0000000..eeef001 --- /dev/null +++ b/buildbot/buildbot/test/mail/svn-commit.2 @@ -0,0 +1,1218 @@ +X-Original-To: jm@jmason.org +Delivered-To: jm@dogma.boxhost.net +Received: from localhost [127.0.0.1] + by localhost with IMAP (fetchmail-6.2.5) + for jm@localhost (single-drop); Thu, 09 Mar 2006 21:44:57 +0000 (GMT) +Received: from minotaur.apache.org (minotaur.apache.org [209.237.227.194]) + by dogma.boxhost.net (Postfix) with SMTP id 0D3463105BF + for <jm@jmason.org>; Thu, 9 Mar 2006 19:52:50 +0000 (GMT) +Received: (qmail 30661 invoked by uid 1833); 9 Mar 2006 19:52:44 -0000 +Delivered-To: jm@locus.apache.org +Received: (qmail 30451 invoked from network); 9 Mar 2006 19:52:38 -0000 +Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) + by minotaur.apache.org with SMTP; 9 Mar 2006 19:52:38 -0000 +Received: (qmail 97860 invoked by uid 500); 9 Mar 2006 19:52:29 -0000 +Delivered-To: apmail-jm@apache.org +Received: (qmail 97837 invoked by uid 500); 9 Mar 2006 19:52:28 -0000 +Mailing-List: contact commits-help@spamassassin.apache.org; run by ezmlm +Precedence: bulk +list-help: <mailto:commits-help@spamassassin.apache.org> +list-unsubscribe: <mailto:commits-unsubscribe@spamassassin.apache.org> +List-Post: <mailto:commits@spamassassin.apache.org> +Reply-To: "SpamAssassin Dev" <dev@spamassassin.apache.org> +List-Id: <commits.spamassassin.apache.org> +Delivered-To: mailing list commits@spamassassin.apache.org +Received: (qmail 97826 invoked by uid 99); 9 Mar 2006 19:52:28 -0000 +Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) + by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 09 Mar 2006 11:52:28 -0800 +X-ASF-Spam-Status: No, hits=-9.4 required=10.0 + tests=ALL_TRUSTED,NO_REAL_NAME +Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) + by apache.org (qpsmtpd/0.29) with SMTP; Thu, 09 Mar 2006 11:52:26 -0800 +Received: (qmail 29644 invoked by uid 65534); 9 Mar 2006 19:52:06 -0000 +Message-ID: <20060309195206.29643.qmail@minotaur.apache.org> +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: svn commit: r384590 - in /spamassassin/branches/3.1: ./ + lib/Mail/SpamAssassin/ lib/Mail/SpamAssassin/Plugin/ spamd/ +Date: Thu, 09 Mar 2006 19:52:02 -0000 +To: commits@spamassassin.apache.org +From: sidney@apache.org +X-Mailer: svnmailer-1.0.7 +X-Virus-Checked: Checked by ClamAV on apache.org +Status: O +X-UID: 60795 +X-Keywords: + +Author: sidney +Date: Thu Mar 9 11:51:59 2006 +New Revision: 384590 + +URL: http://svn.apache.org/viewcvs?rev=384590&view=rev +Log: +Bug 4696: consolidated fixes for timeout bugs + +Added: + spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm +Modified: + spamassassin/branches/3.1/MANIFEST + spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm + spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm + spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm + spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm + spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm + spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm + spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm + spamassassin/branches/3.1/spamd/spamd.raw + +Modified: spamassassin/branches/3.1/MANIFEST +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/MANIFEST?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/MANIFEST (original) ++++ spamassassin/branches/3.1/MANIFEST Thu Mar 9 11:51:59 2006 +@@ -89,6 +89,7 @@ + lib/Mail/SpamAssassin/SQLBasedAddrList.pm + lib/Mail/SpamAssassin/SpamdForkScaling.pm + lib/Mail/SpamAssassin/SubProcBackChannel.pm ++lib/Mail/SpamAssassin/Timeout.pm + lib/Mail/SpamAssassin/Util.pm + lib/Mail/SpamAssassin/Util/DependencyInfo.pm + lib/Mail/SpamAssassin/Util/Progress.pm + +Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm (original) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm Thu Mar 9 11:51:59 2006 +@@ -142,7 +142,7 @@ + + if ($level eq "error") { + # don't log alarm timeouts or broken pipes of various plugins' network checks +- return if ($message[0] =~ /__(?:alarm|brokenpipe)__ignore__/); ++ return if ($message[0] =~ /__ignore__/); + + # dos: we can safely ignore any die's that we eval'd in our own modules so + # don't log them -- this is caller 0, the use'ing package is 1, the eval is 2 + +Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm (original) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm Thu Mar 9 11:51:59 2006 +@@ -44,6 +44,7 @@ + + use Mail::SpamAssassin::Plugin; + use Mail::SpamAssassin::Logger; ++use Mail::SpamAssassin::Timeout; + use IO::Socket; + use strict; + use warnings; +@@ -375,15 +376,10 @@ + + $permsgstatus->enter_helper_run_mode(); + +- my $oldalarm = 0; ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); ++ my $err = $timer->run_and_catch(sub { + +- eval { +- # safe to use $SIG{ALRM} here instead of Util::trap_sigalrm_fully(), +- # since there are no killer regexp hang dangers here +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 +- +- $oldalarm = alarm $timeout; ++ local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; + + my $sock = IO::Socket::UNIX->new(Type => SOCK_STREAM, + Peer => $sockpath) || dbg("dcc: failed to open socket") && die; +@@ -419,28 +415,20 @@ + } + + dbg("dcc: dccifd got response: $response"); ++ ++ }); + +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- }; ++ $permsgstatus->leave_helper_run_mode(); + +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; ++ if ($timer->timed_out()) { ++ dbg("dcc: dccifd check timed out after $timeout secs."); ++ return 0; + } +- $permsgstatus->leave_helper_run_mode(); + + if ($err) { + chomp $err; +- $response = undef; +- if ($err eq "__alarm__ignore__") { +- dbg("dcc: dccifd check timed out after $timeout secs."); +- return 0; +- } else { +- warn("dcc: dccifd -> check skipped: $! $err"); +- return 0; +- } ++ warn("dcc: dccifd -> check skipped: $! $err"); ++ return 0; + } + + if (!defined $response || $response !~ /^X-DCC/) { +@@ -494,17 +482,12 @@ + + # use a temp file here -- open2() is unreliable, buffering-wise, under spamd + my $tmpf = $permsgstatus->create_fulltext_tmpfile($fulltext); +- my $oldalarm = 0; +- + my $pid; +- eval { +- # safe to use $SIG{ALRM} here instead of Util::trap_sigalrm_fully(), +- # since there are no killer regexp hang dangers here +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; +- local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 + +- $oldalarm = alarm $timeout; ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); ++ my $err = $timer->run_and_catch(sub { ++ ++ local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; + + # note: not really tainted, this came from system configuration file + my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{dcc_path}); +@@ -542,17 +525,7 @@ + + dbg("dcc: got response: $response"); + +- # note: this must be called BEFORE leave_helper_run_mode() +- # $self->cleanup_kids($pid); +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- }; +- +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } ++ }); + + if (defined(fileno(*DCC))) { # still open + if ($pid) { +@@ -564,11 +537,14 @@ + } + $permsgstatus->leave_helper_run_mode(); + ++ if ($timer->timed_out()) { ++ dbg("dcc: check timed out after $timeout seconds"); ++ return 0; ++ } ++ + if ($err) { + chomp $err; +- if ($err eq "__alarm__ignore__") { +- dbg("dcc: check timed out after $timeout seconds"); +- } elsif ($err eq "__brokenpipe__ignore__") { ++ if ($err eq "__brokenpipe__ignore__") { + dbg("dcc: check failed: broken pipe"); + } elsif ($err eq "no response") { + dbg("dcc: check failed: no response"); +@@ -645,47 +621,37 @@ + my ($self, $options, $tmpf) = @_; + my $timeout = $options->{report}->{conf}->{dcc_timeout}; + +- $options->{report}->enter_helper_run_mode(); ++ # note: not really tainted, this came from system configuration file ++ my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{dcc_path}); + +- my $oldalarm = 0; ++ my $opts = $options->{report}->{conf}->{dcc_options} || ''; + +- eval { +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; +- local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + +- $oldalarm = alarm $timeout; +- +- # note: not really tainted, this came from system configuration file +- my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{dcc_path}); ++ $options->{report}->enter_helper_run_mode(); ++ my $err = $timer->run_and_catch(sub { + +- my $opts = $options->{report}->{conf}->{dcc_options} || ''; ++ local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; + + my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*DCC, +- $tmpf, 1, $path, "-t", "many", split(' ', $opts)); ++ $tmpf, 1, $path, "-t", "many", split(' ', $opts)); + $pid or die "$!\n"; + + my @ignored = <DCC>; + $options->{report}->close_pipe_fh(\*DCC); +- + waitpid ($pid, 0); +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- }; ++ ++ }); ++ $options->{report}->leave_helper_run_mode(); + +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; ++ if ($timer->timed_out()) { ++ dbg("reporter: DCC report timed out after $timeout seconds"); ++ return 0; + } + +- $options->{report}->leave_helper_run_mode(); +- + if ($err) { + chomp $err; +- if ($err eq "__alarm__ignore__") { +- dbg("reporter: DCC report timed out after $timeout seconds"); +- } elsif ($err eq "__brokenpipe__ignore__") { ++ if ($err eq "__brokenpipe__ignore__") { + dbg("reporter: DCC report failed: broken pipe"); + } else { + warn("reporter: DCC report failed: $err\n"); + +Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm (original) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm Thu Mar 9 11:51:59 2006 +@@ -34,6 +34,8 @@ + + use Mail::SpamAssassin::Plugin; + use Mail::SpamAssassin::Logger; ++use Mail::SpamAssassin::Timeout; ++ + use strict; + use warnings; + use bytes; +@@ -165,30 +167,22 @@ + } + + my $timeout = $scan->{conf}->{domainkeys_timeout}; +- my $oldalarm = 0; + +- eval { +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 +- $oldalarm = alarm($timeout); ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); ++ my $err = $timer->run_and_catch(sub { ++ + $self->_dk_lookup_trapped($scan, $message, $domain); +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- }; +- +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; ++ ++ }); ++ ++ if ($timer->timed_out()) { ++ dbg("dk: lookup timed out after $timeout seconds"); ++ return 0; + } + + if ($err) { + chomp $err; +- if ($err eq "__alarm__ignore__") { +- dbg("dk: lookup timed out after $timeout seconds"); +- } else { +- warn("dk: lookup failed: $err\n"); +- } ++ warn("dk: lookup failed: $err\n"); + return 0; + } + + +Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm (original) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm Thu Mar 9 11:51:59 2006 +@@ -35,6 +35,7 @@ + + use Mail::SpamAssassin::Plugin; + use Mail::SpamAssassin::Logger; ++use Mail::SpamAssassin::Timeout; + use strict; + use warnings; + use bytes; +@@ -229,27 +230,22 @@ + + $pyzor_count = 0; + $pyzor_whitelisted = 0; +- +- $permsgstatus->enter_helper_run_mode(); ++ my $pid; + + # use a temp file here -- open2() is unreliable, buffering-wise, under spamd + my $tmpf = $permsgstatus->create_fulltext_tmpfile($fulltext); +- my $oldalarm = 0; + +- my $pid; +- eval { +- # safe to use $SIG{ALRM} here instead of Util::trap_sigalrm_fully(), +- # since there are no killer regexp hang dangers here +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; +- local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 ++ # note: not really tainted, this came from system configuration file ++ my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{pyzor_path}); ++ ++ my $opts = $self->{main}->{conf}->{pyzor_options} || ''; + +- $oldalarm = alarm $timeout; ++ $permsgstatus->enter_helper_run_mode(); + +- # note: not really tainted, this came from system configuration file +- my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{pyzor_path}); ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); ++ my $err = $timer->run_and_catch(sub { + +- my $opts = $self->{main}->{conf}->{pyzor_options} || ''; ++ local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; + + dbg("pyzor: opening pipe: " . join(' ', $path, $opts, "check", "< $tmpf")); + +@@ -273,21 +269,7 @@ + die("internal error\n"); + } + +- # note: this must be called BEFORE leave_helper_run_mode() +- # $self->cleanup_kids($pid); +- +- # attempt to call this inside the eval, as leaving this scope is +- # a slow operation and timing *that* out is pointless +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- }; +- +- # clear the alarm before doing lots of time-consuming hard work +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } ++ }); + + if (defined(fileno(*PYZOR))) { # still open + if ($pid) { +@@ -299,11 +281,14 @@ + } + $permsgstatus->leave_helper_run_mode(); + ++ if ($timer->timed_out()) { ++ dbg("pyzor: check timed out after $timeout seconds"); ++ return 0; ++ } ++ + if ($err) { + chomp $err; +- if ($err eq "__alarm__ignore__") { +- dbg("pyzor: check timed out after $timeout seconds"); +- } elsif ($err eq "__brokenpipe__ignore__") { ++ if ($err eq "__brokenpipe__ignore__") { + dbg("pyzor: check failed: broken pipe"); + } elsif ($err eq "no response") { + dbg("pyzor: check failed: no response"); +@@ -364,23 +349,19 @@ + + sub pyzor_report { + my ($self, $options, $tmpf) = @_; ++ ++ # note: not really tainted, this came from system configuration file ++ my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{pyzor_path}); ++ ++ my $opts = $options->{report}->{conf}->{pyzor_options} || ''; + my $timeout = $self->{main}->{conf}->{pyzor_timeout}; + + $options->{report}->enter_helper_run_mode(); + +- my $oldalarm = 0; ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); ++ my $err = $timer->run_and_catch(sub { + +- eval { +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; + local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 +- +- $oldalarm = alarm $timeout; +- +- # note: not really tainted, this came from system configuration file +- my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{pyzor_path}); +- +- my $opts = $options->{report}->{conf}->{pyzor_options} || ''; + + dbg("pyzor: opening pipe: " . join(' ', $path, $opts, "report", "< $tmpf")); + +@@ -391,23 +372,19 @@ + my @ignored = <PYZOR>; + $options->{report}->close_pipe_fh(\*PYZOR); + +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } + waitpid ($pid, 0); +- }; ++ }); + +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } + $options->{report}->leave_helper_run_mode(); + ++ if ($timer->timed_out()) { ++ dbg("reporter: pyzor report timed out after $timeout seconds"); ++ return 0; ++ } ++ + if ($err) { + chomp $err; +- if ($err eq '__alarm__ignore__') { +- dbg("reporter: pyzor report timed out after $timeout seconds"); +- } elsif ($err eq '__brokenpipe__ignore__') { ++ if ($err eq '__brokenpipe__ignore__') { + dbg("reporter: pyzor report failed: broken pipe"); + } else { + warn("reporter: pyzor report failed: $err\n"); + +Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm (original) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm Thu Mar 9 11:51:59 2006 +@@ -143,14 +143,11 @@ + } + + Mail::SpamAssassin::PerMsgStatus::enter_helper_run_mode($self); +- my $oldalarm = 0; + +- eval { +- local ($^W) = 0; # argh, warnings in Razor ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); ++ my $err = $timer->run_and_catch(sub { + +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 +- $oldalarm = alarm $timeout; ++ local ($^W) = 0; # argh, warnings in Razor + + # everything's in the module! + my $rc = Razor2::Client::Agent->new("razor-$type"); +@@ -184,7 +181,7 @@ + # let's reset the alarm since get_server_info() calls + # nextserver() which calls discover() which very likely will + # reset the alarm for us ... how polite. :( +- alarm $timeout; ++ $timer->reset(); + + # no facility prefix on this die + my $sigs = $rc->compute_sigs($objects) +@@ -219,100 +216,96 @@ + my $error = $rc->errprefix("$debug: spamassassin") || "$debug: razor2 had unknown error during disconnect"; + die $error; + } ++ } + +- # if we got here, we're done doing remote stuff, abort the alert +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- +- # Razor 2.14 says that if we get here, we did ok. +- $return = 1; ++ # Razor 2.14 says that if we get here, we did ok. ++ $return = 1; + +- # figure out if we have a log file we need to close... +- if (ref($rc->{logref}) && exists $rc->{logref}->{fd}) { +- # the fd can be stdout or stderr, so we need to find out if it is +- # so we don't close them by accident. Note: we can't just +- # undef the fd here (like the IO::Handle manpage says we can) +- # because it won't actually close, unfortunately. :( +- my $untie = 1; +- foreach my $log (*STDOUT{IO}, *STDERR{IO}) { +- if ($log == $rc->{logref}->{fd}) { +- $untie = 0; +- last; +- } +- } +- close $rc->{logref}->{fd} if ($untie); +- } +- +- if ($type eq 'check') { +- # so $objects->[0] is the first (only) message, and ->{spam} is a general yes/no +- push(@results, { result => $objects->[0]->{spam} }); ++ # figure out if we have a log file we need to close... ++ if (ref($rc->{logref}) && exists $rc->{logref}->{fd}) { ++ # the fd can be stdout or stderr, so we need to find out if it is ++ # so we don't close them by accident. Note: we can't just ++ # undef the fd here (like the IO::Handle manpage says we can) ++ # because it won't actually close, unfortunately. :( ++ my $untie = 1; ++ foreach my $log (*STDOUT{IO}, *STDERR{IO}) { ++ if ($log == $rc->{logref}->{fd}) { ++ $untie = 0; ++ last; ++ } ++ } ++ close $rc->{logref}->{fd} if ($untie); ++ } + +- # great for debugging, but leave this off! +- #use Data::Dumper; +- #print Dumper($objects),"\n"; +- +- # ->{p} is for each part of the message +- # so go through each part, taking the highest cf we find +- # of any part that isn't contested (ct). This helps avoid false +- # positives. equals logic_method 4. +- # +- # razor-agents < 2.14 have a different object format, so we now support both. +- # $objects->[0]->{resp} vs $objects->[0]->{p}->[part #]->{resp} +- my $part = 0; +- my $arrayref = $objects->[0]->{p} || $objects; +- if (defined $arrayref) { +- foreach my $cf (@{$arrayref}) { +- if (exists $cf->{resp}) { +- for (my $response=0; $response<@{$cf->{resp}}; $response++) { +- my $tmp = $cf->{resp}->[$response]; +- my $tmpcf = $tmp->{cf}; # Part confidence +- my $tmpct = $tmp->{ct}; # Part contested? +- my $engine = $cf->{sent}->[$response]->{e}; +- +- # These should always be set, but just in case ... +- $tmpcf = 0 unless defined $tmpcf; +- $tmpct = 0 unless defined $tmpct; +- $engine = 0 unless defined $engine; +- +- push(@results, +- { part => $part, engine => $engine, contested => $tmpct, confidence => $tmpcf }); +- } +- } +- else { +- push(@results, { part => $part, noresponse => 1 }); +- } +- $part++; +- } +- } +- else { +- # If we have some new $objects format that isn't close to +- # the current razor-agents 2.x version, we won't FP but we +- # should alert in debug. +- dbg("$debug: it looks like the internal Razor object has changed format!"); +- } +- } ++ if ($type eq 'check') { ++ # so $objects->[0] is the first (only) message, and ->{spam} is a general yes/no ++ push(@results, { result => $objects->[0]->{spam} }); ++ ++ # great for debugging, but leave this off! ++ #use Data::Dumper; ++ #print Dumper($objects),"\n"; ++ ++ # ->{p} is for each part of the message ++ # so go through each part, taking the highest cf we find ++ # of any part that isn't contested (ct). This helps avoid false ++ # positives. equals logic_method 4. ++ # ++ # razor-agents < 2.14 have a different object format, so we now support both. ++ # $objects->[0]->{resp} vs $objects->[0]->{p}->[part #]->{resp} ++ my $part = 0; ++ my $arrayref = $objects->[0]->{p} || $objects; ++ if (defined $arrayref) { ++ foreach my $cf (@{$arrayref}) { ++ if (exists $cf->{resp}) { ++ for (my $response=0; $response<@{$cf->{resp}}; $response++) { ++ my $tmp = $cf->{resp}->[$response]; ++ my $tmpcf = $tmp->{cf}; # Part confidence ++ my $tmpct = $tmp->{ct}; # Part contested? ++ my $engine = $cf->{sent}->[$response]->{e}; ++ ++ # These should always be set, but just in case ... ++ $tmpcf = 0 unless defined $tmpcf; ++ $tmpct = 0 unless defined $tmpct; ++ $engine = 0 unless defined $engine; ++ ++ push(@results, ++ { part => $part, engine => $engine, contested => $tmpct, confidence => $tmpcf }); ++ } ++ } ++ else { ++ push(@results, { part => $part, noresponse => 1 }); ++ } ++ $part++; ++ } ++ } ++ else { ++ # If we have some new $objects format that isn't close to ++ # the current razor-agents 2.x version, we won't FP but we ++ # should alert in debug. ++ dbg("$debug: it looks like the internal Razor object has changed format!"); ++ } + } + } + else { + warn "$debug: undefined Razor2::Client::Agent\n"; + } + +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- }; ++ }); ++ ++ # OK, that's enough Razor stuff. now, reset all that global ++ # state it futzes with :( ++ # work around serious brain damage in Razor2 (constant seed) ++ srand; + +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; ++ Mail::SpamAssassin::PerMsgStatus::leave_helper_run_mode($self); ++ ++ if ($timer->timed_out()) { ++ dbg("$debug: razor2 $type timed out after $timeout seconds"); + } + + if ($err) { + chomp $err; +- if ($err eq "__alarm__ignore__") { +- dbg("$debug: razor2 $type timed out after $timeout seconds"); +- } elsif ($err =~ /(?:could not connect|network is unreachable)/) { ++ if ($err =~ /(?:could not connect|network is unreachable)/) { + # make this a dbg(); SpamAssassin will still continue, + # but without Razor checking. otherwise there may be + # DSNs and errors in syslog etc., yuck +@@ -323,11 +316,6 @@ + warn("$debug: razor2 $type failed: $! $err"); + } + } +- +- # work around serious brain damage in Razor2 (constant seed) +- srand; +- +- Mail::SpamAssassin::PerMsgStatus::leave_helper_run_mode($self); + + # razor also debugs to stdout. argh. fix it to stderr... + if (would_log('dbg', $debug)) { + +Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm (original) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm Thu Mar 9 11:51:59 2006 +@@ -34,6 +34,7 @@ + + use Mail::SpamAssassin::Plugin; + use Mail::SpamAssassin::Logger; ++use Mail::SpamAssassin::Timeout; + use strict; + use warnings; + use bytes; +@@ -300,30 +301,17 @@ + + my ($result, $comment); + my $timeout = $scanner->{conf}->{spf_timeout}; +- my $oldalarm = 0; + +- eval { +- local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; +- local $SIG{__DIE__}; # bug 4631 +- $oldalarm = alarm($timeout); ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); ++ my $err = $timer->run_and_catch(sub { ++ + ($result, $comment) = $query->result(); +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } +- }; + +- my $err = $@; +- if (defined $oldalarm) { +- alarm $oldalarm; $oldalarm = undef; +- } ++ }); + + if ($err) { + chomp $err; +- if ($err eq "__alarm__ignore__") { +- dbg("spf: lookup timed out after $timeout seconds"); +- } else { +- warn("spf: lookup failed: $err\n"); +- } ++ warn("spf: lookup failed: $err\n"); + return 0; + } + + +Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm (original) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm Thu Mar 9 11:51:59 2006 +@@ -25,6 +25,7 @@ + + use Mail::SpamAssassin::Util; + use Mail::SpamAssassin::Logger; ++use Mail::SpamAssassin::Timeout; + + use vars qw { + @PFSTATE_VARS %EXPORT_TAGS @EXPORT_OK +@@ -109,6 +110,9 @@ + + delete $self->{kids}->{$pid}; + ++ # note this for the select()-caller's benefit ++ $self->{child_just_exited} = 1; ++ + # remove the child from the backchannel list, too + $self->{backchannel}->delete_socket_for_child($pid); + +@@ -188,24 +192,63 @@ + vec($rin, $self->{server_fileno}, 1) = 0; + } + +- my ($rout, $eout, $nfound, $timeleft); ++ my ($rout, $eout, $nfound, $timeleft, $selerr); ++ ++ # use alarm to back up select()'s built-in alarm, to debug Theo's bug. ++ # not that I can remember what Theo's bug was, but hey ;) A good ++ # 60 seconds extra on the alarm() should make that quite rare... ++ ++ my $timer = Mail::SpamAssassin::Timeout->new({ secs => ($tout*2) + 60 }); + +- # use alarm to back up select()'s built-in alarm, to debug theo's bug +- eval { +- Mail::SpamAssassin::Util::trap_sigalrm_fully(sub { die "tcp timeout"; }); +- alarm ($tout*2) if ($tout); ++ $timer->run(sub { ++ ++ $self->{child_just_exited} = 0; + ($nfound, $timeleft) = select($rout=$rin, undef, $eout=$rin, $tout); +- }; +- alarm 0; ++ $selerr = $!; + +- if ($@) { +- warn "prefork: select timeout failed! recovering\n"; +- sleep 1; # avoid overload +- return; +- } ++ }); ++ ++ # bug 4696: under load, the process can go for such a long time without ++ # being context-switched in, that when it does return the alarm() fires ++ # before the select() timeout does. Treat this as a select() timeout ++ if ($timer->timed_out) { ++ dbg("prefork: select timed out (via alarm)"); ++ $nfound = 0; ++ $timeleft = 0; ++ } ++ ++ # errors; handle undef *or* -1 returned. do this before "errors on ++ # the handle" below, since an error condition is signalled both via ++ # a -1 return and a $eout bit. ++ if (!defined $nfound || $nfound < 0) ++ { ++ if (exists &Errno::EINTR && $selerr == &Errno::EINTR) ++ { ++ # this happens if the process is signalled during the select(), ++ # for example if someone sends SIGHUP to reload the configuration. ++ # just return inmmediately ++ dbg("prefork: select returned err $selerr, probably signalled"); ++ return; ++ } ++ ++ # if a child exits during that select() call, it generates a spurious ++ # error, like this: ++ # ++ # Jan 29 12:53:17 dogma spamd[18518]: prefork: child states: BI ++ # Jan 29 12:53:17 dogma spamd[18518]: spamd: handled cleanup of child pid 13101 due to SIGCHLD ++ # Jan 29 12:53:17 dogma spamd[18518]: prefork: select returned -1! recovering: ++ # ++ # avoid by setting a boolean in the child_exited() callback and checking ++ # it here. log $! just in case, though. ++ if ($self->{child_just_exited} && $nfound == -1) { ++ dbg("prefork: select returned -1 due to child exiting, ignored ($selerr)"); ++ return; ++ } ++ ++ warn "prefork: select returned ". ++ (defined $nfound ? $nfound : "undef"). ++ "! recovering: $selerr\n"; + +- if (!defined $nfound) { +- warn "prefork: select returned undef! recovering\n"; + sleep 1; # avoid overload + return; + } +@@ -213,7 +256,7 @@ + # errors on the handle? + # return them immediately, they may be from a SIGHUP restart signal + if (vec ($eout, $self->{server_fileno}, 1)) { +- warn "prefork: select returned error on server filehandle: $!\n"; ++ warn "prefork: select returned error on server filehandle: $selerr $!\n"; + return; + } + +@@ -282,7 +325,7 @@ + + my ($sock, $kid); + while (($kid, $sock) = each %{$self->{backchannel}->{kids}}) { +- $self->syswrite_with_retry($sock, PF_PING_ORDER) and next; ++ $self->syswrite_with_retry($sock, PF_PING_ORDER, $kid, 3) and next; + + warn "prefork: write of ping failed to $kid fd=".$sock->fileno.": ".$!; + +@@ -353,7 +396,7 @@ + return $self->order_idle_child_to_accept(); + } + +- if (!$self->syswrite_with_retry($sock, PF_ACCEPT_ORDER)) ++ if (!$self->syswrite_with_retry($sock, PF_ACCEPT_ORDER, $kid)) + { + # failure to write to the child; bad news. call it dead + warn "prefork: killing rogue child $kid, failed to write on fd ".$sock->fileno.": $!\n"; +@@ -396,7 +439,7 @@ + my ($self, $kid) = @_; + if ($self->{waiting_for_idle_child}) { + my $sock = $self->{backchannel}->get_socket_for_child($kid); +- $self->syswrite_with_retry($sock, PF_ACCEPT_ORDER) ++ $self->syswrite_with_retry($sock, PF_ACCEPT_ORDER, $kid) + or die "prefork: $kid claimed it was ready, but write failed on fd ". + $sock->fileno.": ".$!; + $self->{waiting_for_idle_child} = 0; +@@ -426,7 +469,7 @@ + sub report_backchannel_socket { + my ($self, $str) = @_; + my $sock = $self->{backchannel}->get_parent_socket(); +- $self->syswrite_with_retry($sock, $str) ++ $self->syswrite_with_retry($sock, $str, 'parent') + or write "syswrite() to parent failed: $!"; + } + +@@ -537,12 +580,31 @@ + } + + sub syswrite_with_retry { +- my ($self, $sock, $buf) = @_; ++ my ($self, $sock, $buf, $targetname, $numretries) = @_; ++ $numretries ||= 10; # default 10 retries + + my $written = 0; ++ my $try = 0; + + retry_write: ++ ++ $try++; ++ if ($try > 1) { ++ warn "prefork: syswrite(".$sock->fileno.") to $targetname failed on try $try"; ++ if ($try > $numretries) { ++ warn "prefork: giving up"; ++ return undef; ++ } ++ else { ++ # give it 1 second to recover. we retry indefinitely. ++ my $rout = ''; ++ vec($rout, $sock->fileno, 1) = 1; ++ select(undef, $rout, undef, 1); ++ } ++ } ++ + my $nbytes = $sock->syswrite($buf); ++ + if (!defined $nbytes) { + unless ((exists &Errno::EAGAIN && $! == &Errno::EAGAIN) + || (exists &Errno::EWOULDBLOCK && $! == &Errno::EWOULDBLOCK)) +@@ -551,13 +613,7 @@ + return undef; + } + +- warn "prefork: syswrite(".$sock->fileno.") failed, retrying..."; +- +- # give it 5 seconds to recover. we retry indefinitely. +- my $rout = ''; +- vec($rout, $sock->fileno, 1) = 1; +- select(undef, $rout, undef, 5); +- ++ warn "prefork: retrying syswrite(): $!"; + goto retry_write; + } + else { +@@ -568,7 +624,8 @@ + return $written; # it's complete, we can return + } + else { +- warn "prefork: partial write of $nbytes, towrite=".length($buf). ++ warn "prefork: partial write of $nbytes to ". ++ $targetname.", towrite=".length($buf). + " sofar=".$written." fd=".$sock->fileno.", recovering"; + goto retry_write; + } + +Added: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm?rev=384590&view=auto +============================================================================== +--- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm (added) ++++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm Thu Mar 9 11:51:59 2006 +@@ -0,0 +1,215 @@ ++# <@LICENSE> ++# Copyright 2004 Apache Software Foundation ++# ++# 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. ++# </@LICENSE> ++ ++=head1 NAME ++ ++Mail::SpamAssassin::Timeout - safe, reliable timeouts in perl ++ ++=head1 SYNOPSIS ++ ++ # non-timeout code... ++ ++ my $t = Mail::SpamAssassin::Timeout->new({ secs => 5 }); ++ ++ $t->run(sub { ++ # code to run with a 5-second timeout... ++ }); ++ ++ if ($t->timed_out()) { ++ # do something... ++ } ++ ++ # more non-timeout code... ++ ++=head1 DESCRIPTION ++ ++This module provides a safe, reliable and clean API to provide ++C<alarm(2)>-based timeouts for perl code. ++ ++Note that C<$SIG{ALRM}> is used to provide the timeout, so this will not ++interrupt out-of-control regular expression matches. ++ ++Nested timeouts are supported. ++ ++=head1 PUBLIC METHODS ++ ++=over 4 ++ ++=cut ++ ++package Mail::SpamAssassin::Timeout; ++ ++use strict; ++use warnings; ++use bytes; ++ ++use vars qw{ ++ @ISA ++}; ++ ++@ISA = qw(); ++ ++########################################################################### ++ ++=item my $t = Mail::SpamAssassin::Timeout->new({ ... options ... }); ++ ++Constructor. Options include: ++ ++=over 4 ++ ++=item secs => $seconds ++ ++timeout, in seconds. Optional; if not specified, no timeouts will be applied. ++ ++=back ++ ++=cut ++ ++sub new { ++ my ($class, $opts) = @_; ++ $class = ref($class) || $class; ++ my %selfval = $opts ? %{$opts} : (); ++ my $self = \%selfval; ++ ++ bless ($self, $class); ++ $self; ++} ++ ++########################################################################### ++ ++=item $t->run($coderef) ++ ++Run a code reference within the currently-defined timeout. ++ ++The timeout is as defined by the B<secs> parameter to the constructor. ++ ++Returns whatever the subroutine returns, or C<undef> on timeout. ++If the timer times out, C<$t-<gt>timed_out()> will return C<1>. ++ ++Time elapsed is not cumulative; multiple runs of C<run> will restart the ++timeout from scratch. ++ ++=item $t->run_and_catch($coderef) ++ ++Run a code reference, as per C<$t-<gt>run()>, but also catching any ++C<die()> calls within the code reference. ++ ++Returns C<undef> if no C<die()> call was executed and C<$@> was unset, or the ++value of C<$@> if it was set. (The timeout event doesn't count as a C<die()>.) ++ ++=cut ++ ++sub run { $_[0]->_run($_[1], 0); } ++ ++sub run_and_catch { $_[0]->_run($_[1], 1); } ++ ++sub _run { # private ++ my ($self, $sub, $and_catch) = @_; ++ ++ delete $self->{timed_out}; ++ ++ if (!$self->{secs}) { # no timeout! just call the sub and return. ++ return &$sub; ++ } ++ ++ # assertion ++ if ($self->{secs} < 0) { ++ die "Mail::SpamAssassin::Timeout: oops? neg value for 'secs': $self->{secs}"; ++ } ++ ++ my $oldalarm = 0; ++ my $ret; ++ ++ eval { ++ # note use of local to ensure closed scope here ++ local $SIG{ALRM} = sub { die "__alarm__ignore__\n" }; ++ local $SIG{__DIE__}; # bug 4631 ++ ++ $oldalarm = alarm($self->{secs}); ++ ++ $ret = &$sub; ++ ++ # Unset the alarm() before we leave eval{ } scope, as that stack-pop ++ # operation can take a second or two under load. Note: previous versions ++ # restored $oldalarm here; however, that is NOT what we want to do, since ++ # it creates a new race condition, namely that an old alarm could then fire ++ # while the stack-pop was underway, thereby appearing to be *this* timeout ++ # timing out. In terms of how we might possibly have nested timeouts in ++ # SpamAssassin, this is an academic issue with little impact, but it's ++ # still worth avoiding anyway. ++ ++ alarm 0; ++ }; ++ ++ my $err = $@; ++ ++ if (defined $oldalarm) { ++ # now, we could have died from a SIGALRM == timed out. if so, ++ # restore the previously-active one, or zero all timeouts if none ++ # were previously active. ++ alarm $oldalarm; ++ } ++ ++ if ($err) { ++ if ($err =~ /__alarm__ignore__/) { ++ $self->{timed_out} = 1; ++ } else { ++ if ($and_catch) { ++ return $@; ++ } else { ++ die $@; # propagate any "real" errors ++ } ++ } ++ } ++ ++ if ($and_catch) { ++ return; # undef ++ } else { ++ return $ret; ++ } ++} ++ ++########################################################################### ++ ++=item $t->timed_out() ++ ++Returns C<1> if the most recent code executed in C<run()> timed out, or ++C<undef> if it did not. ++ ++=cut ++ ++sub timed_out { ++ my ($self) = @_; ++ return $self->{timed_out}; ++} ++ ++########################################################################### ++ ++=item $t->reset() ++ ++If called within a C<run()> code reference, causes the current alarm timer to ++be reset to its starting value. ++ ++=cut ++ ++sub reset { ++ my ($self) = @_; ++ alarm($self->{secs}); ++} ++ ++########################################################################### ++ ++1; + +Modified: spamassassin/branches/3.1/spamd/spamd.raw +URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/spamd/spamd.raw?rev=384590&r1=384589&r2=384590&view=diff +============================================================================== +--- spamassassin/branches/3.1/spamd/spamd.raw (original) ++++ spamassassin/branches/3.1/spamd/spamd.raw Thu Mar 9 11:51:59 2006 +@@ -2049,6 +2049,9 @@ + foreach (keys %children) { + kill 'INT' => $_; + my $pid = waitpid($_, 0); ++ if ($scaling) { ++ $scaling->child_exited($pid); ++ } + info("spamd: child $pid killed successfully"); + } + %children = (); + + + + +
\ No newline at end of file |