mail.py: add changesource for SVN's commit-email.pl script, contributed by Justin...
[buildbot.git] / buildbot / test / mail / svn-commit.2
blobeeef001ce5271ac3e7dd8ebdbd549a8e27e556c5
1 X-Original-To: jm@jmason.org
2 Delivered-To: jm@dogma.boxhost.net
3 Received: from localhost [127.0.0.1]
4         by localhost with IMAP (fetchmail-6.2.5)
5         for jm@localhost (single-drop); Thu, 09 Mar 2006 21:44:57 +0000 (GMT)
6 Received: from minotaur.apache.org (minotaur.apache.org [209.237.227.194])
7         by dogma.boxhost.net (Postfix) with SMTP id 0D3463105BF
8         for <jm@jmason.org>; Thu,  9 Mar 2006 19:52:50 +0000 (GMT)
9 Received: (qmail 30661 invoked by uid 1833); 9 Mar 2006 19:52:44 -0000
10 Delivered-To: jm@locus.apache.org
11 Received: (qmail 30451 invoked from network); 9 Mar 2006 19:52:38 -0000
12 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199)
13   by minotaur.apache.org with SMTP; 9 Mar 2006 19:52:38 -0000
14 Received: (qmail 97860 invoked by uid 500); 9 Mar 2006 19:52:29 -0000
15 Delivered-To: apmail-jm@apache.org
16 Received: (qmail 97837 invoked by uid 500); 9 Mar 2006 19:52:28 -0000
17 Mailing-List: contact commits-help@spamassassin.apache.org; run by ezmlm
18 Precedence: bulk
19 list-help: <mailto:commits-help@spamassassin.apache.org>
20 list-unsubscribe: <mailto:commits-unsubscribe@spamassassin.apache.org>
21 List-Post: <mailto:commits@spamassassin.apache.org>
22 Reply-To: "SpamAssassin Dev" <dev@spamassassin.apache.org>
23 List-Id: <commits.spamassassin.apache.org>
24 Delivered-To: mailing list commits@spamassassin.apache.org
25 Received: (qmail 97826 invoked by uid 99); 9 Mar 2006 19:52:28 -0000
26 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49)
27     by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 09 Mar 2006 11:52:28 -0800
28 X-ASF-Spam-Status: No, hits=-9.4 required=10.0
29         tests=ALL_TRUSTED,NO_REAL_NAME
30 Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194)
31     by apache.org (qpsmtpd/0.29) with SMTP; Thu, 09 Mar 2006 11:52:26 -0800
32 Received: (qmail 29644 invoked by uid 65534); 9 Mar 2006 19:52:06 -0000
33 Message-ID: <20060309195206.29643.qmail@minotaur.apache.org>
34 Content-Type: text/plain; charset="utf-8"
35 MIME-Version: 1.0
36 Content-Transfer-Encoding: 7bit
37 Subject: svn commit: r384590 - in /spamassassin/branches/3.1: ./
38  lib/Mail/SpamAssassin/ lib/Mail/SpamAssassin/Plugin/ spamd/
39 Date: Thu, 09 Mar 2006 19:52:02 -0000
40 To: commits@spamassassin.apache.org
41 From: sidney@apache.org
42 X-Mailer: svnmailer-1.0.7
43 X-Virus-Checked: Checked by ClamAV on apache.org
44 Status: O
45 X-UID: 60795
46 X-Keywords:                                                                                                    
48 Author: sidney
49 Date: Thu Mar  9 11:51:59 2006
50 New Revision: 384590
52 URL: http://svn.apache.org/viewcvs?rev=384590&view=rev
53 Log:
54 Bug 4696: consolidated fixes for timeout bugs
56 Added:
57     spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm
58 Modified:
59     spamassassin/branches/3.1/MANIFEST
60     spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm
61     spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm
62     spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm
63     spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm
64     spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm
65     spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm
66     spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm
67     spamassassin/branches/3.1/spamd/spamd.raw
69 Modified: spamassassin/branches/3.1/MANIFEST
70 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/MANIFEST?rev=384590&r1=384589&r2=384590&view=diff
71 ==============================================================================
72 --- spamassassin/branches/3.1/MANIFEST (original)
73 +++ spamassassin/branches/3.1/MANIFEST Thu Mar  9 11:51:59 2006
74 @@ -89,6 +89,7 @@
75  lib/Mail/SpamAssassin/SQLBasedAddrList.pm
76  lib/Mail/SpamAssassin/SpamdForkScaling.pm
77  lib/Mail/SpamAssassin/SubProcBackChannel.pm
78 +lib/Mail/SpamAssassin/Timeout.pm
79  lib/Mail/SpamAssassin/Util.pm
80  lib/Mail/SpamAssassin/Util/DependencyInfo.pm
81  lib/Mail/SpamAssassin/Util/Progress.pm
83 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm
84 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm?rev=384590&r1=384589&r2=384590&view=diff
85 ==============================================================================
86 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm (original)
87 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm Thu Mar  9 11:51:59 2006
88 @@ -142,7 +142,7 @@
90    if ($level eq "error") {
91      # don't log alarm timeouts or broken pipes of various plugins' network checks
92 -    return if ($message[0] =~ /__(?:alarm|brokenpipe)__ignore__/);
93 +    return if ($message[0] =~ /__ignore__/);
95      # dos: we can safely ignore any die's that we eval'd in our own modules so
96      # don't log them -- this is caller 0, the use'ing package is 1, the eval is 2
98 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm
99 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm?rev=384590&r1=384589&r2=384590&view=diff
100 ==============================================================================
101 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm (original)
102 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DCC.pm Thu Mar  9 11:51:59 2006
103 @@ -44,6 +44,7 @@
105  use Mail::SpamAssassin::Plugin;
106  use Mail::SpamAssassin::Logger;
107 +use Mail::SpamAssassin::Timeout;
108  use IO::Socket;
109  use strict;
110  use warnings;
111 @@ -375,15 +376,10 @@
113    $permsgstatus->enter_helper_run_mode();
115 -  my $oldalarm = 0;
116 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
117 +  my $err = $timer->run_and_catch(sub {
119 -  eval {
120 -    # safe to use $SIG{ALRM} here instead of Util::trap_sigalrm_fully(),
121 -    # since there are no killer regexp hang dangers here
122 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
123 -    local $SIG{__DIE__};   # bug 4631
125 -    $oldalarm = alarm $timeout;
126 +    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
128      my $sock = IO::Socket::UNIX->new(Type => SOCK_STREAM,
129        Peer => $sockpath) || dbg("dcc: failed to open socket") && die;
130 @@ -419,28 +415,20 @@
131      }
133      dbg("dcc: dccifd got response: $response");
134 +  
135 +  });
137 -    if (defined $oldalarm) {
138 -      alarm $oldalarm; $oldalarm = undef;
139 -    }
140 -  };
141 +  $permsgstatus->leave_helper_run_mode();
143 -  my $err = $@;
144 -  if (defined $oldalarm) {
145 -    alarm $oldalarm; $oldalarm = undef;
146 +  if ($timer->timed_out()) {
147 +    dbg("dcc: dccifd check timed out after $timeout secs.");
148 +    return 0;
149    }
150 -  $permsgstatus->leave_helper_run_mode();
152    if ($err) {
153      chomp $err;
154 -    $response = undef;
155 -    if ($err eq "__alarm__ignore__") {
156 -      dbg("dcc: dccifd check timed out after $timeout secs.");
157 -      return 0;
158 -    } else {
159 -      warn("dcc: dccifd -> check skipped: $! $err");
160 -      return 0;
161 -    }
162 +    warn("dcc: dccifd -> check skipped: $! $err");
163 +    return 0;
164    }
166    if (!defined $response || $response !~ /^X-DCC/) {
167 @@ -494,17 +482,12 @@
169    # use a temp file here -- open2() is unreliable, buffering-wise, under spamd
170    my $tmpf = $permsgstatus->create_fulltext_tmpfile($fulltext);
171 -  my $oldalarm = 0;
173    my $pid;
174 -  eval {
175 -    # safe to use $SIG{ALRM} here instead of Util::trap_sigalrm_fully(),
176 -    # since there are no killer regexp hang dangers here
177 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
178 -    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
179 -    local $SIG{__DIE__};   # bug 4631
181 -    $oldalarm = alarm $timeout;
182 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
183 +  my $err = $timer->run_and_catch(sub {
185 +    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
187      # note: not really tainted, this came from system configuration file
188      my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{dcc_path});
189 @@ -542,17 +525,7 @@
191      dbg("dcc: got response: $response");
193 -    # note: this must be called BEFORE leave_helper_run_mode()
194 -    # $self->cleanup_kids($pid);
195 -    if (defined $oldalarm) {
196 -      alarm $oldalarm; $oldalarm = undef;
197 -    }
198 -  };
200 -  my $err = $@;
201 -  if (defined $oldalarm) {
202 -    alarm $oldalarm; $oldalarm = undef;
203 -  }
204 +  });
206    if (defined(fileno(*DCC))) {  # still open
207      if ($pid) {
208 @@ -564,11 +537,14 @@
209    }
210    $permsgstatus->leave_helper_run_mode();
212 +  if ($timer->timed_out()) {
213 +    dbg("dcc: check timed out after $timeout seconds");
214 +    return 0;
215 +  }
217    if ($err) {
218      chomp $err;
219 -    if ($err eq "__alarm__ignore__") {
220 -      dbg("dcc: check timed out after $timeout seconds");
221 -    } elsif ($err eq "__brokenpipe__ignore__") {
222 +    if ($err eq "__brokenpipe__ignore__") {
223        dbg("dcc: check failed: broken pipe");
224      } elsif ($err eq "no response") {
225        dbg("dcc: check failed: no response");
226 @@ -645,47 +621,37 @@
227    my ($self, $options, $tmpf) = @_;
228    my $timeout = $options->{report}->{conf}->{dcc_timeout};
230 -  $options->{report}->enter_helper_run_mode();
231 +  # note: not really tainted, this came from system configuration file
232 +  my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{dcc_path});
234 -  my $oldalarm = 0;
235 +  my $opts = $options->{report}->{conf}->{dcc_options} || '';
237 -  eval {
238 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
239 -    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
240 -    local $SIG{__DIE__};   # bug 4631
241 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
243 -    $oldalarm = alarm $timeout;
245 -    # note: not really tainted, this came from system configuration file
246 -    my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{dcc_path});
247 +  $options->{report}->enter_helper_run_mode();
248 +  my $err = $timer->run_and_catch(sub {
250 -    my $opts = $options->{report}->{conf}->{dcc_options} || '';
251 +    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
253      my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*DCC,
254 -       $tmpf, 1, $path, "-t", "many", split(' ', $opts));
255 +        $tmpf, 1, $path, "-t", "many", split(' ', $opts));
256      $pid or die "$!\n";
258      my @ignored = <DCC>;
259      $options->{report}->close_pipe_fh(\*DCC);
261      waitpid ($pid, 0);
262 -    if (defined $oldalarm) {
263 -      alarm $oldalarm; $oldalarm = undef;
264 -    }
265 -  };
266 +  
267 +  });
268 +  $options->{report}->leave_helper_run_mode();
270 -  my $err = $@;
271 -  if (defined $oldalarm) {
272 -    alarm $oldalarm; $oldalarm = undef;
273 +  if ($timer->timed_out()) {
274 +    dbg("reporter: DCC report timed out after $timeout seconds");
275 +    return 0;
276    }
278 -  $options->{report}->leave_helper_run_mode();
280    if ($err) {
281      chomp $err;
282 -    if ($err eq "__alarm__ignore__") {
283 -      dbg("reporter: DCC report timed out after $timeout seconds");
284 -    } elsif ($err eq "__brokenpipe__ignore__") {
285 +    if ($err eq "__brokenpipe__ignore__") {
286        dbg("reporter: DCC report failed: broken pipe");
287      } else {
288        warn("reporter: DCC report failed: $err\n");
290 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm
291 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm?rev=384590&r1=384589&r2=384590&view=diff
292 ==============================================================================
293 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm (original)
294 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm Thu Mar  9 11:51:59 2006
295 @@ -34,6 +34,8 @@
297  use Mail::SpamAssassin::Plugin;
298  use Mail::SpamAssassin::Logger;
299 +use Mail::SpamAssassin::Timeout;
301  use strict;
302  use warnings;
303  use bytes;
304 @@ -165,30 +167,22 @@
305    }
307    my $timeout = $scan->{conf}->{domainkeys_timeout};
308 -  my $oldalarm = 0;
310 -  eval {
311 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
312 -    local $SIG{__DIE__};   # bug 4631
313 -    $oldalarm = alarm($timeout);
314 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
315 +  my $err = $timer->run_and_catch(sub {
317      $self->_dk_lookup_trapped($scan, $message, $domain);
318 -    if (defined $oldalarm) {
319 -      alarm $oldalarm; $oldalarm = undef;
320 -    }
321 -  };
323 -  my $err = $@;
324 -  if (defined $oldalarm) {
325 -    alarm $oldalarm; $oldalarm = undef;
327 +  });
329 +  if ($timer->timed_out()) {
330 +    dbg("dk: lookup timed out after $timeout seconds");
331 +    return 0;
332    }
334    if ($err) {
335      chomp $err;
336 -    if ($err eq "__alarm__ignore__") {
337 -      dbg("dk: lookup timed out after $timeout seconds");
338 -    } else {
339 -      warn("dk: lookup failed: $err\n");
340 -    }
341 +    warn("dk: lookup failed: $err\n");
342      return 0;
343    }
346 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm
347 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm?rev=384590&r1=384589&r2=384590&view=diff
348 ==============================================================================
349 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm (original)
350 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Pyzor.pm Thu Mar  9 11:51:59 2006
351 @@ -35,6 +35,7 @@
353  use Mail::SpamAssassin::Plugin;
354  use Mail::SpamAssassin::Logger;
355 +use Mail::SpamAssassin::Timeout;
356  use strict;
357  use warnings;
358  use bytes;
359 @@ -229,27 +230,22 @@
361    $pyzor_count = 0;
362    $pyzor_whitelisted = 0;
364 -  $permsgstatus->enter_helper_run_mode();
365 +  my $pid;
367    # use a temp file here -- open2() is unreliable, buffering-wise, under spamd
368    my $tmpf = $permsgstatus->create_fulltext_tmpfile($fulltext);
369 -  my $oldalarm = 0;
371 -  my $pid;
372 -  eval {
373 -    # safe to use $SIG{ALRM} here instead of Util::trap_sigalrm_fully(),
374 -    # since there are no killer regexp hang dangers here
375 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
376 -    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
377 -    local $SIG{__DIE__};   # bug 4631
378 +  # note: not really tainted, this came from system configuration file
379 +  my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{pyzor_path});
381 +  my $opts = $self->{main}->{conf}->{pyzor_options} || '';
383 -    $oldalarm = alarm $timeout;
384 +  $permsgstatus->enter_helper_run_mode();
386 -    # note: not really tainted, this came from system configuration file
387 -    my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{pyzor_path});
388 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
389 +  my $err = $timer->run_and_catch(sub {
391 -    my $opts = $self->{main}->{conf}->{pyzor_options} || '';
392 +    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
393   
394      dbg("pyzor: opening pipe: " . join(' ', $path, $opts, "check", "< $tmpf"));
396 @@ -273,21 +269,7 @@
397        die("internal error\n");
398      }
400 -    # note: this must be called BEFORE leave_helper_run_mode()
401 -    # $self->cleanup_kids($pid);
403 -    # attempt to call this inside the eval, as leaving this scope is
404 -    # a slow operation and timing *that* out is pointless
405 -    if (defined $oldalarm) { 
406 -      alarm $oldalarm; $oldalarm = undef;
407 -    }
408 -  };
410 -  # clear the alarm before doing lots of time-consuming hard work
411 -  my $err = $@;
412 -  if (defined $oldalarm) { 
413 -    alarm $oldalarm; $oldalarm = undef;
414 -  }
415 +  });
417    if (defined(fileno(*PYZOR))) {  # still open
418      if ($pid) {
419 @@ -299,11 +281,14 @@
420    }
421    $permsgstatus->leave_helper_run_mode();
423 +  if ($timer->timed_out()) {
424 +    dbg("pyzor: check timed out after $timeout seconds");
425 +    return 0;
426 +  }
428    if ($err) {
429      chomp $err;
430 -    if ($err eq "__alarm__ignore__") {
431 -      dbg("pyzor: check timed out after $timeout seconds");
432 -    } elsif ($err eq "__brokenpipe__ignore__") {
433 +    if ($err eq "__brokenpipe__ignore__") {
434        dbg("pyzor: check failed: broken pipe");
435      } elsif ($err eq "no response") {
436        dbg("pyzor: check failed: no response");
437 @@ -364,23 +349,19 @@
439  sub pyzor_report {
440    my ($self, $options, $tmpf) = @_;
442 +  # note: not really tainted, this came from system configuration file
443 +  my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{pyzor_path});
445 +  my $opts = $options->{report}->{conf}->{pyzor_options} || '';
446    my $timeout = $self->{main}->{conf}->{pyzor_timeout};
448    $options->{report}->enter_helper_run_mode();
450 -  my $oldalarm = 0;
451 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
452 +  my $err = $timer->run_and_catch(sub {
454 -  eval {
455 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
456      local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
457 -    local $SIG{__DIE__};   # bug 4631
459 -    $oldalarm = alarm $timeout;
461 -    # note: not really tainted, this came from system configuration file
462 -    my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{pyzor_path});
464 -    my $opts = $options->{report}->{conf}->{pyzor_options} || '';
466      dbg("pyzor: opening pipe: " . join(' ', $path, $opts, "report", "< $tmpf"));
468 @@ -391,23 +372,19 @@
469      my @ignored = <PYZOR>;
470      $options->{report}->close_pipe_fh(\*PYZOR);
472 -    if (defined $oldalarm) { 
473 -      alarm $oldalarm; $oldalarm = undef;
474 -    }
475      waitpid ($pid, 0);
476 -  };
477 +  });
479 -  my $err = $@;
480 -  if (defined $oldalarm) { 
481 -    alarm $oldalarm; $oldalarm = undef;
482 -  }
483    $options->{report}->leave_helper_run_mode();
485 +  if ($timer->timed_out()) {
486 +    dbg("reporter: pyzor report timed out after $timeout seconds");
487 +    return 0;
488 +  }
490    if ($err) {
491      chomp $err;
492 -    if ($err eq '__alarm__ignore__') {
493 -      dbg("reporter: pyzor report timed out after $timeout seconds");
494 -    } elsif ($err eq '__brokenpipe__ignore__') {
495 +    if ($err eq '__brokenpipe__ignore__') {
496        dbg("reporter: pyzor report failed: broken pipe");
497      } else {
498        warn("reporter: pyzor report failed: $err\n");
500 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm
501 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm?rev=384590&r1=384589&r2=384590&view=diff
502 ==============================================================================
503 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm (original)
504 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/Razor2.pm Thu Mar  9 11:51:59 2006
505 @@ -143,14 +143,11 @@
506    }
508    Mail::SpamAssassin::PerMsgStatus::enter_helper_run_mode($self);
509 -  my $oldalarm = 0;
511 -  eval {
512 -    local ($^W) = 0;    # argh, warnings in Razor
513 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
514 +  my $err = $timer->run_and_catch(sub {
516 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
517 -    local $SIG{__DIE__};   # bug 4631
518 -    $oldalarm = alarm $timeout;
519 +    local ($^W) = 0;    # argh, warnings in Razor
521      # everything's in the module!
522      my $rc = Razor2::Client::Agent->new("razor-$type");
523 @@ -184,7 +181,7 @@
524        # let's reset the alarm since get_server_info() calls
525        # nextserver() which calls discover() which very likely will
526        # reset the alarm for us ... how polite.  :(
527 -      alarm $timeout;
528 +      $timer->reset();
530        # no facility prefix on this die
531        my $sigs = $rc->compute_sigs($objects)
532 @@ -219,100 +216,96 @@
533           my $error = $rc->errprefix("$debug: spamassassin") || "$debug: razor2 had unknown error during disconnect";
534           die $error;
535         }
536 +      }
538 -       # if we got here, we're done doing remote stuff, abort the alert
539 -        if (defined $oldalarm) {
540 -          alarm $oldalarm; $oldalarm = undef;
541 -        }
543 -       # Razor 2.14 says that if we get here, we did ok.
544 -       $return = 1;
545 +      # Razor 2.14 says that if we get here, we did ok.
546 +      $return = 1;
548 -       # figure out if we have a log file we need to close...
549 -       if (ref($rc->{logref}) && exists $rc->{logref}->{fd}) {
550 -         # the fd can be stdout or stderr, so we need to find out if it is
551 -         # so we don't close them by accident.  Note: we can't just
552 -         # undef the fd here (like the IO::Handle manpage says we can)
553 -         # because it won't actually close, unfortunately. :(
554 -         my $untie = 1;
555 -         foreach my $log (*STDOUT{IO}, *STDERR{IO}) {
556 -           if ($log == $rc->{logref}->{fd}) {
557 -             $untie = 0;
558 -             last;
559 -           }
560 -         }
561 -         close $rc->{logref}->{fd} if ($untie);
562 -       }
564 -       if ($type eq 'check') {
565 -         # so $objects->[0] is the first (only) message, and ->{spam} is a general yes/no
566 -         push(@results, { result => $objects->[0]->{spam} });
567 +      # figure out if we have a log file we need to close...
568 +      if (ref($rc->{logref}) && exists $rc->{logref}->{fd}) {
569 +        # the fd can be stdout or stderr, so we need to find out if it is
570 +        # so we don't close them by accident.  Note: we can't just
571 +        # undef the fd here (like the IO::Handle manpage says we can)
572 +        # because it won't actually close, unfortunately. :(
573 +        my $untie = 1;
574 +        foreach my $log (*STDOUT{IO}, *STDERR{IO}) {
575 +          if ($log == $rc->{logref}->{fd}) {
576 +            $untie = 0;
577 +            last;
578 +          }
579 +        }
580 +        close $rc->{logref}->{fd} if ($untie);
581 +      }
583 -         # great for debugging, but leave this off!
584 -         #use Data::Dumper;
585 -         #print Dumper($objects),"\n";
587 -         # ->{p} is for each part of the message
588 -         # so go through each part, taking the highest cf we find
589 -         # of any part that isn't contested (ct).  This helps avoid false
590 -         # positives.  equals logic_method 4.
591 -         #
592 -         # razor-agents < 2.14 have a different object format, so we now support both.
593 -         # $objects->[0]->{resp} vs $objects->[0]->{p}->[part #]->{resp}
594 -         my $part = 0;
595 -         my $arrayref = $objects->[0]->{p} || $objects;
596 -         if (defined $arrayref) {
597 -           foreach my $cf (@{$arrayref}) {
598 -             if (exists $cf->{resp}) {
599 -               for (my $response=0; $response<@{$cf->{resp}}; $response++) {
600 -                 my $tmp = $cf->{resp}->[$response];
601 -                 my $tmpcf = $tmp->{cf}; # Part confidence
602 -                 my $tmpct = $tmp->{ct}; # Part contested?
603 -                 my $engine = $cf->{sent}->[$response]->{e};
605 -                 # These should always be set, but just in case ...
606 -                 $tmpcf = 0 unless defined $tmpcf;
607 -                 $tmpct = 0 unless defined $tmpct;
608 -                 $engine = 0 unless defined $engine;
610 -                 push(@results,
611 -                      { part => $part, engine => $engine, contested => $tmpct, confidence => $tmpcf });
612 -               }
613 -             }
614 -             else {
615 -               push(@results, { part => $part, noresponse => 1 });
616 -             }
617 -             $part++;
618 -           }
619 -         }
620 -         else {
621 -           # If we have some new $objects format that isn't close to
622 -           # the current razor-agents 2.x version, we won't FP but we
623 -           # should alert in debug.
624 -           dbg("$debug: it looks like the internal Razor object has changed format!");
625 -         }
626 -       }
627 +      if ($type eq 'check') {
628 +        # so $objects->[0] is the first (only) message, and ->{spam} is a general yes/no
629 +        push(@results, { result => $objects->[0]->{spam} });
631 +        # great for debugging, but leave this off!
632 +        #use Data::Dumper;
633 +        #print Dumper($objects),"\n";
635 +        # ->{p} is for each part of the message
636 +        # so go through each part, taking the highest cf we find
637 +        # of any part that isn't contested (ct).  This helps avoid false
638 +        # positives.  equals logic_method 4.
639 +        #
640 +        # razor-agents < 2.14 have a different object format, so we now support both.
641 +        # $objects->[0]->{resp} vs $objects->[0]->{p}->[part #]->{resp}
642 +        my $part = 0;
643 +        my $arrayref = $objects->[0]->{p} || $objects;
644 +        if (defined $arrayref) {
645 +          foreach my $cf (@{$arrayref}) {
646 +            if (exists $cf->{resp}) {
647 +              for (my $response=0; $response<@{$cf->{resp}}; $response++) {
648 +                my $tmp = $cf->{resp}->[$response];
649 +                my $tmpcf = $tmp->{cf}; # Part confidence
650 +                my $tmpct = $tmp->{ct}; # Part contested?
651 +                my $engine = $cf->{sent}->[$response]->{e};
653 +                # These should always be set, but just in case ...
654 +                $tmpcf = 0 unless defined $tmpcf;
655 +                $tmpct = 0 unless defined $tmpct;
656 +                $engine = 0 unless defined $engine;
658 +                push(@results,
659 +                      { part => $part, engine => $engine, contested => $tmpct, confidence => $tmpcf });
660 +              }
661 +            }
662 +            else {
663 +              push(@results, { part => $part, noresponse => 1 });
664 +            }
665 +            $part++;
666 +          }
667 +        }
668 +        else {
669 +          # If we have some new $objects format that isn't close to
670 +          # the current razor-agents 2.x version, we won't FP but we
671 +          # should alert in debug.
672 +          dbg("$debug: it looks like the internal Razor object has changed format!");
673 +        }
674        }
675      }
676      else {
677        warn "$debug: undefined Razor2::Client::Agent\n";
678      }
679    
680 -    if (defined $oldalarm) {
681 -      alarm $oldalarm; $oldalarm = undef;
682 -    }
683 -  };
684 +  });
686 +  # OK, that's enough Razor stuff. now, reset all that global
687 +  # state it futzes with :(
688 +  # work around serious brain damage in Razor2 (constant seed)
689 +  srand;
691 -  my $err = $@;
692 -  if (defined $oldalarm) {
693 -    alarm $oldalarm; $oldalarm = undef;
694 +  Mail::SpamAssassin::PerMsgStatus::leave_helper_run_mode($self);
696 +  if ($timer->timed_out()) {
697 +    dbg("$debug: razor2 $type timed out after $timeout seconds");
698    }
700    if ($err) {
701      chomp $err;
702 -    if ($err eq "__alarm__ignore__") {
703 -      dbg("$debug: razor2 $type timed out after $timeout seconds");
704 -    } elsif ($err =~ /(?:could not connect|network is unreachable)/) {
705 +    if ($err =~ /(?:could not connect|network is unreachable)/) {
706        # make this a dbg(); SpamAssassin will still continue,
707        # but without Razor checking.  otherwise there may be
708        # DSNs and errors in syslog etc., yuck
709 @@ -323,11 +316,6 @@
710        warn("$debug: razor2 $type failed: $! $err");
711      }
712    }
714 -  # work around serious brain damage in Razor2 (constant seed)
715 -  srand;
717 -  Mail::SpamAssassin::PerMsgStatus::leave_helper_run_mode($self);
719    # razor also debugs to stdout. argh. fix it to stderr...
720    if (would_log('dbg', $debug)) {
722 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm
723 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm?rev=384590&r1=384589&r2=384590&view=diff
724 ==============================================================================
725 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm (original)
726 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/SPF.pm Thu Mar  9 11:51:59 2006
727 @@ -34,6 +34,7 @@
729  use Mail::SpamAssassin::Plugin;
730  use Mail::SpamAssassin::Logger;
731 +use Mail::SpamAssassin::Timeout;
732  use strict;
733  use warnings;
734  use bytes;
735 @@ -300,30 +301,17 @@
737    my ($result, $comment);
738    my $timeout = $scanner->{conf}->{spf_timeout};
739 -  my $oldalarm = 0;
741 -  eval {
742 -    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
743 -    local $SIG{__DIE__};   # bug 4631
744 -    $oldalarm = alarm($timeout);
745 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
746 +  my $err = $timer->run_and_catch(sub {
748      ($result, $comment) = $query->result();
749 -    if (defined $oldalarm) {
750 -      alarm $oldalarm; $oldalarm = undef;
751 -    }
752 -  };
754 -  my $err = $@;
755 -  if (defined $oldalarm) {
756 -    alarm $oldalarm; $oldalarm = undef;
757 -  }
758 +  });
760    if ($err) {
761      chomp $err;
762 -    if ($err eq "__alarm__ignore__") {
763 -      dbg("spf: lookup timed out after $timeout seconds");
764 -    } else {
765 -      warn("spf: lookup failed: $err\n");
766 -    }
767 +    warn("spf: lookup failed: $err\n");
768      return 0;
769    }
772 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm
773 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm?rev=384590&r1=384589&r2=384590&view=diff
774 ==============================================================================
775 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm (original)
776 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm Thu Mar  9 11:51:59 2006
777 @@ -25,6 +25,7 @@
779  use Mail::SpamAssassin::Util;
780  use Mail::SpamAssassin::Logger;
781 +use Mail::SpamAssassin::Timeout;
783  use vars qw {
784    @PFSTATE_VARS %EXPORT_TAGS @EXPORT_OK
785 @@ -109,6 +110,9 @@
787    delete $self->{kids}->{$pid};
789 +  # note this for the select()-caller's benefit
790 +  $self->{child_just_exited} = 1;
792    # remove the child from the backchannel list, too
793    $self->{backchannel}->delete_socket_for_child($pid);
795 @@ -188,24 +192,63 @@
796      vec($rin, $self->{server_fileno}, 1) = 0;
797    }
799 -  my ($rout, $eout, $nfound, $timeleft);
800 +  my ($rout, $eout, $nfound, $timeleft, $selerr);
802 +  # use alarm to back up select()'s built-in alarm, to debug Theo's bug.
803 +  # not that I can remember what Theo's bug was, but hey ;)    A good
804 +  # 60 seconds extra on the alarm() should make that quite rare...
806 +  my $timer = Mail::SpamAssassin::Timeout->new({ secs => ($tout*2) + 60 });
808 -  # use alarm to back up select()'s built-in alarm, to debug theo's bug
809 -  eval {
810 -    Mail::SpamAssassin::Util::trap_sigalrm_fully(sub { die "tcp timeout"; });
811 -    alarm ($tout*2) if ($tout);
812 +  $timer->run(sub {
814 +    $self->{child_just_exited} = 0;
815      ($nfound, $timeleft) = select($rout=$rin, undef, $eout=$rin, $tout);
816 -  };
817 -  alarm 0;
818 +    $selerr = $!;
820 -  if ($@) {
821 -    warn "prefork: select timeout failed! recovering\n";
822 -    sleep 1;        # avoid overload
823 -    return;
824 -  }
825 +  });
827 +  # bug 4696: under load, the process can go for such a long time without
828 +  # being context-switched in, that when it does return the alarm() fires
829 +  # before the select() timeout does.   Treat this as a select() timeout
830 +  if ($timer->timed_out) {
831 +    dbg("prefork: select timed out (via alarm)");
832 +    $nfound = 0;
833 +    $timeleft = 0;
834 +  }
836 +  # errors; handle undef *or* -1 returned.  do this before "errors on
837 +  # the handle" below, since an error condition is signalled both via
838 +  # a -1 return and a $eout bit.
839 +  if (!defined $nfound || $nfound < 0)
840 +  {
841 +    if (exists &Errno::EINTR && $selerr == &Errno::EINTR)
842 +    {
843 +      # this happens if the process is signalled during the select(),
844 +      # for example if someone sends SIGHUP to reload the configuration.
845 +      # just return inmmediately
846 +      dbg("prefork: select returned err $selerr, probably signalled");
847 +      return;
848 +    }
850 +    # if a child exits during that select() call, it generates a spurious
851 +    # error, like this:
852 +    #
853 +    # Jan 29 12:53:17 dogma spamd[18518]: prefork: child states: BI
854 +    # Jan 29 12:53:17 dogma spamd[18518]: spamd: handled cleanup of child pid 13101 due to SIGCHLD
855 +    # Jan 29 12:53:17 dogma spamd[18518]: prefork: select returned -1! recovering:
856 +    #
857 +    # avoid by setting a boolean in the child_exited() callback and checking
858 +    # it here.  log $! just in case, though.
859 +    if ($self->{child_just_exited} && $nfound == -1) {
860 +      dbg("prefork: select returned -1 due to child exiting, ignored ($selerr)");
861 +      return;
862 +    }
864 +    warn "prefork: select returned ".
865 +            (defined $nfound ? $nfound : "undef").
866 +            "! recovering: $selerr\n";
868 -  if (!defined $nfound) {
869 -    warn "prefork: select returned undef! recovering\n";
870      sleep 1;        # avoid overload
871      return;
872    }
873 @@ -213,7 +256,7 @@
874    # errors on the handle?
875    # return them immediately, they may be from a SIGHUP restart signal
876    if (vec ($eout, $self->{server_fileno}, 1)) {
877 -    warn "prefork: select returned error on server filehandle: $!\n";
878 +    warn "prefork: select returned error on server filehandle: $selerr $!\n";
879      return;
880    }
882 @@ -282,7 +325,7 @@
884    my ($sock, $kid);
885    while (($kid, $sock) = each %{$self->{backchannel}->{kids}}) {
886 -    $self->syswrite_with_retry($sock, PF_PING_ORDER) and next;
887 +    $self->syswrite_with_retry($sock, PF_PING_ORDER, $kid, 3) and next;
889      warn "prefork: write of ping failed to $kid fd=".$sock->fileno.": ".$!;
891 @@ -353,7 +396,7 @@
892        return $self->order_idle_child_to_accept();
893      }
895 -    if (!$self->syswrite_with_retry($sock, PF_ACCEPT_ORDER))
896 +    if (!$self->syswrite_with_retry($sock, PF_ACCEPT_ORDER, $kid))
897      {
898        # failure to write to the child; bad news.  call it dead
899        warn "prefork: killing rogue child $kid, failed to write on fd ".$sock->fileno.": $!\n";
900 @@ -396,7 +439,7 @@
901    my ($self, $kid) = @_;
902    if ($self->{waiting_for_idle_child}) {
903      my $sock = $self->{backchannel}->get_socket_for_child($kid);
904 -    $self->syswrite_with_retry($sock, PF_ACCEPT_ORDER)
905 +    $self->syswrite_with_retry($sock, PF_ACCEPT_ORDER, $kid)
906          or die "prefork: $kid claimed it was ready, but write failed on fd ".
907                              $sock->fileno.": ".$!;
908      $self->{waiting_for_idle_child} = 0;
909 @@ -426,7 +469,7 @@
910  sub report_backchannel_socket {
911    my ($self, $str) = @_;
912    my $sock = $self->{backchannel}->get_parent_socket();
913 -  $self->syswrite_with_retry($sock, $str)
914 +  $self->syswrite_with_retry($sock, $str, 'parent')
915          or write "syswrite() to parent failed: $!";
918 @@ -537,12 +580,31 @@
921  sub syswrite_with_retry {
922 -  my ($self, $sock, $buf) = @_;
923 +  my ($self, $sock, $buf, $targetname, $numretries) = @_;
924 +  $numretries ||= 10;       # default 10 retries
926    my $written = 0;
927 +  my $try = 0;
929  retry_write:
931 +  $try++;
932 +  if ($try > 1) {
933 +    warn "prefork: syswrite(".$sock->fileno.") to $targetname failed on try $try";
934 +    if ($try > $numretries) {
935 +      warn "prefork: giving up";
936 +      return undef;
937 +    }
938 +    else {
939 +      # give it 1 second to recover.  we retry indefinitely.
940 +      my $rout = '';
941 +      vec($rout, $sock->fileno, 1) = 1;
942 +      select(undef, $rout, undef, 1);
943 +    }
944 +  }
946    my $nbytes = $sock->syswrite($buf);
948    if (!defined $nbytes) {
949      unless ((exists &Errno::EAGAIN && $! == &Errno::EAGAIN)
950          || (exists &Errno::EWOULDBLOCK && $! == &Errno::EWOULDBLOCK))
951 @@ -551,13 +613,7 @@
952        return undef;
953      }
955 -    warn "prefork: syswrite(".$sock->fileno.") failed, retrying...";
957 -    # give it 5 seconds to recover.  we retry indefinitely.
958 -    my $rout = '';
959 -    vec($rout, $sock->fileno, 1) = 1;
960 -    select(undef, $rout, undef, 5);
962 +    warn "prefork: retrying syswrite(): $!";
963      goto retry_write;
964    }
965    else {
966 @@ -568,7 +624,8 @@
967        return $written;      # it's complete, we can return
968      }
969      else {
970 -      warn "prefork: partial write of $nbytes, towrite=".length($buf).
971 +      warn "prefork: partial write of $nbytes to ".
972 +            $targetname.", towrite=".length($buf).
973              " sofar=".$written." fd=".$sock->fileno.", recovering";
974        goto retry_write;
975      }
977 Added: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm
978 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm?rev=384590&view=auto
979 ==============================================================================
980 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm (added)
981 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm Thu Mar  9 11:51:59 2006
982 @@ -0,0 +1,215 @@
983 +# <@LICENSE>
984 +# Copyright 2004 Apache Software Foundation
985 +# 
986 +# Licensed under the Apache License, Version 2.0 (the "License");
987 +# you may not use this file except in compliance with the License.
988 +# You may obtain a copy of the License at
989 +# 
990 +#     http://www.apache.org/licenses/LICENSE-2.0
991 +# 
992 +# Unless required by applicable law or agreed to in writing, software
993 +# distributed under the License is distributed on an "AS IS" BASIS,
994 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
995 +# See the License for the specific language governing permissions and
996 +# limitations under the License.
997 +# </@LICENSE>
999 +=head1 NAME
1001 +Mail::SpamAssassin::Timeout - safe, reliable timeouts in perl
1003 +=head1 SYNOPSIS
1005 +    # non-timeout code...
1007 +    my $t = Mail::SpamAssassin::Timeout->new({ secs => 5 });
1008 +    
1009 +    $t->run(sub {
1010 +        # code to run with a 5-second timeout...
1011 +    });
1013 +    if ($t->timed_out()) {
1014 +        # do something...
1015 +    }
1017 +    # more non-timeout code...
1019 +=head1 DESCRIPTION
1021 +This module provides a safe, reliable and clean API to provide
1022 +C<alarm(2)>-based timeouts for perl code.
1024 +Note that C<$SIG{ALRM}> is used to provide the timeout, so this will not
1025 +interrupt out-of-control regular expression matches.
1027 +Nested timeouts are supported.
1029 +=head1 PUBLIC METHODS
1031 +=over 4
1033 +=cut
1035 +package Mail::SpamAssassin::Timeout;
1037 +use strict;
1038 +use warnings;
1039 +use bytes;
1041 +use vars qw{
1042 +  @ISA
1045 +@ISA = qw();
1047 +###########################################################################
1049 +=item my $t = Mail::SpamAssassin::Timeout->new({ ... options ... });
1051 +Constructor.  Options include:
1053 +=over 4
1055 +=item secs => $seconds
1057 +timeout, in seconds.  Optional; if not specified, no timeouts will be applied.
1059 +=back
1061 +=cut
1063 +sub new {
1064 +  my ($class, $opts) = @_;
1065 +  $class = ref($class) || $class;
1066 +  my %selfval = $opts ? %{$opts} : ();
1067 +  my $self = \%selfval;
1069 +  bless ($self, $class);
1070 +  $self;
1073 +###########################################################################
1075 +=item $t->run($coderef)
1077 +Run a code reference within the currently-defined timeout.
1079 +The timeout is as defined by the B<secs> parameter to the constructor.
1081 +Returns whatever the subroutine returns, or C<undef> on timeout.
1082 +If the timer times out, C<$t-<gt>timed_out()> will return C<1>.
1084 +Time elapsed is not cumulative; multiple runs of C<run> will restart the
1085 +timeout from scratch.
1087 +=item $t->run_and_catch($coderef)
1089 +Run a code reference, as per C<$t-<gt>run()>, but also catching any
1090 +C<die()> calls within the code reference.
1092 +Returns C<undef> if no C<die()> call was executed and C<$@> was unset, or the
1093 +value of C<$@> if it was set.  (The timeout event doesn't count as a C<die()>.)
1095 +=cut
1097 +sub run { $_[0]->_run($_[1], 0); }
1099 +sub run_and_catch { $_[0]->_run($_[1], 1); }
1101 +sub _run {      # private
1102 +  my ($self, $sub, $and_catch) = @_;
1104 +  delete $self->{timed_out};
1106 +  if (!$self->{secs}) { # no timeout!  just call the sub and return.
1107 +    return &$sub;
1108 +  }
1110 +  # assertion
1111 +  if ($self->{secs} < 0) {
1112 +    die "Mail::SpamAssassin::Timeout: oops? neg value for 'secs': $self->{secs}";
1113 +  }
1115 +  my $oldalarm = 0;
1116 +  my $ret;
1118 +  eval {
1119 +    # note use of local to ensure closed scope here
1120 +    local $SIG{ALRM} = sub { die "__alarm__ignore__\n" };
1121 +    local $SIG{__DIE__};   # bug 4631
1123 +    $oldalarm = alarm($self->{secs});
1125 +    $ret = &$sub;
1127 +    # Unset the alarm() before we leave eval{ } scope, as that stack-pop
1128 +    # operation can take a second or two under load. Note: previous versions
1129 +    # restored $oldalarm here; however, that is NOT what we want to do, since
1130 +    # it creates a new race condition, namely that an old alarm could then fire
1131 +    # while the stack-pop was underway, thereby appearing to be *this* timeout
1132 +    # timing out. In terms of how we might possibly have nested timeouts in
1133 +    # SpamAssassin, this is an academic issue with little impact, but it's
1134 +    # still worth avoiding anyway.
1136 +    alarm 0;
1137 +  };
1139 +  my $err = $@;
1141 +  if (defined $oldalarm) {
1142 +    # now, we could have died from a SIGALRM == timed out.  if so,
1143 +    # restore the previously-active one, or zero all timeouts if none
1144 +    # were previously active.
1145 +    alarm $oldalarm;
1146 +  }
1148 +  if ($err) {
1149 +    if ($err =~ /__alarm__ignore__/) {
1150 +      $self->{timed_out} = 1;
1151 +    } else {
1152 +      if ($and_catch) {
1153 +        return $@;
1154 +      } else {
1155 +        die $@;             # propagate any "real" errors
1156 +      }
1157 +    }
1158 +  }
1160 +  if ($and_catch) {
1161 +    return;                 # undef
1162 +  } else {
1163 +    return $ret;
1164 +  }
1167 +###########################################################################
1169 +=item $t->timed_out()
1171 +Returns C<1> if the most recent code executed in C<run()> timed out, or
1172 +C<undef> if it did not.
1174 +=cut
1176 +sub timed_out {
1177 +  my ($self) = @_;
1178 +  return $self->{timed_out};
1181 +###########################################################################
1183 +=item $t->reset()
1185 +If called within a C<run()> code reference, causes the current alarm timer to
1186 +be reset to its starting value.
1188 +=cut
1190 +sub reset {
1191 +  my ($self) = @_;
1192 +  alarm($self->{secs});
1195 +###########################################################################
1199 Modified: spamassassin/branches/3.1/spamd/spamd.raw
1200 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/spamd/spamd.raw?rev=384590&r1=384589&r2=384590&view=diff
1201 ==============================================================================
1202 --- spamassassin/branches/3.1/spamd/spamd.raw (original)
1203 +++ spamassassin/branches/3.1/spamd/spamd.raw Thu Mar  9 11:51:59 2006
1204 @@ -2049,6 +2049,9 @@
1205    foreach (keys %children) {
1206      kill 'INT' => $_;
1207      my $pid = waitpid($_, 0);
1208 +    if ($scaling) {
1209 +      $scaling->child_exited($pid);
1210 +    }
1211      info("spamd: child $pid killed successfully");
1212    }
1213    %children = ();
1218