OLD | NEW |
| (Empty) |
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:
| |
47 | |
48 Author: sidney | |
49 Date: Thu Mar 9 11:51:59 2006 | |
50 New Revision: 384590 | |
51 | |
52 URL: http://svn.apache.org/viewcvs?rev=384590&view=rev | |
53 Log: | |
54 Bug 4696: consolidated fixes for timeout bugs | |
55 | |
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 | |
68 | |
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 | |
82 | |
83 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Logger.pm | |
84 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssass
in/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:5
9 2006 | |
88 @@ -142,7 +142,7 @@ | |
89 | |
90 if ($level eq "error") { | |
91 # don't log alarm timeouts or broken pipes of various plugins' network chec
ks | |
92 - return if ($message[0] =~ /__(?:alarm|brokenpipe)__ignore__/); | |
93 + return if ($message[0] =~ /__ignore__/); | |
94 | |
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 | |
97 | |
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/SpamAssass
in/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 @@ | |
104 | |
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 @@ | |
112 | |
113 $permsgstatus->enter_helper_run_mode(); | |
114 | |
115 - my $oldalarm = 0; | |
116 + my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); | |
117 + my $err = $timer->run_and_catch(sub { | |
118 | |
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 | |
124 - | |
125 - $oldalarm = alarm $timeout; | |
126 + local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; | |
127 | |
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 } | |
132 | |
133 dbg("dcc: dccifd got response: $response"); | |
134 + | |
135 + }); | |
136 | |
137 - if (defined $oldalarm) { | |
138 - alarm $oldalarm; $oldalarm = undef; | |
139 - } | |
140 - }; | |
141 + $permsgstatus->leave_helper_run_mode(); | |
142 | |
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(); | |
151 | |
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 } | |
165 | |
166 if (!defined $response || $response !~ /^X-DCC/) { | |
167 @@ -494,17 +482,12 @@ | |
168 | |
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; | |
172 - | |
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 | |
180 | |
181 - $oldalarm = alarm $timeout; | |
182 + my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); | |
183 + my $err = $timer->run_and_catch(sub { | |
184 + | |
185 + local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; | |
186 | |
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 @@ | |
190 | |
191 dbg("dcc: got response: $response"); | |
192 | |
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 - }; | |
199 - | |
200 - my $err = $@; | |
201 - if (defined $oldalarm) { | |
202 - alarm $oldalarm; $oldalarm = undef; | |
203 - } | |
204 + }); | |
205 | |
206 if (defined(fileno(*DCC))) { # still open | |
207 if ($pid) { | |
208 @@ -564,11 +537,14 @@ | |
209 } | |
210 $permsgstatus->leave_helper_run_mode(); | |
211 | |
212 + if ($timer->timed_out()) { | |
213 + dbg("dcc: check timed out after $timeout seconds"); | |
214 + return 0; | |
215 + } | |
216 + | |
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}; | |
229 | |
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}->{c
onf}->{dcc_path}); | |
233 | |
234 - my $oldalarm = 0; | |
235 + my $opts = $options->{report}->{conf}->{dcc_options} || ''; | |
236 | |
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 }); | |
242 | |
243 - $oldalarm = alarm $timeout; | |
244 - | |
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 { | |
249 | |
250 - my $opts = $options->{report}->{conf}->{dcc_options} || ''; | |
251 + local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; | |
252 | |
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"; | |
257 | |
258 my @ignored = <DCC>; | |
259 $options->{report}->close_pipe_fh(\*DCC); | |
260 - | |
261 waitpid ($pid, 0); | |
262 - if (defined $oldalarm) { | |
263 - alarm $oldalarm; $oldalarm = undef; | |
264 - } | |
265 - }; | |
266 + | |
267 + }); | |
268 + $options->{report}->leave_helper_run_mode(); | |
269 | |
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 } | |
277 | |
278 - $options->{report}->leave_helper_run_mode(); | |
279 - | |
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"); | |
289 | |
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/SpamAssass
in/Plugin/DomainKeys.pm?rev=384590&r1=384589&r2=384590&view=diff | |
292 ============================================================================== | |
293 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm (origin
al) | |
294 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/Plugin/DomainKeys.pm Thu Mar
9 11:51:59 2006 | |
295 @@ -34,6 +34,8 @@ | |
296 | |
297 use Mail::SpamAssassin::Plugin; | |
298 use Mail::SpamAssassin::Logger; | |
299 +use Mail::SpamAssassin::Timeout; | |
300 + | |
301 use strict; | |
302 use warnings; | |
303 use bytes; | |
304 @@ -165,30 +167,22 @@ | |
305 } | |
306 | |
307 my $timeout = $scan->{conf}->{domainkeys_timeout}; | |
308 - my $oldalarm = 0; | |
309 | |
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 { | |
316 + | |
317 $self->_dk_lookup_trapped($scan, $message, $domain); | |
318 - if (defined $oldalarm) { | |
319 - alarm $oldalarm; $oldalarm = undef; | |
320 - } | |
321 - }; | |
322 - | |
323 - my $err = $@; | |
324 - if (defined $oldalarm) { | |
325 - alarm $oldalarm; $oldalarm = undef; | |
326 + | |
327 + }); | |
328 + | |
329 + if ($timer->timed_out()) { | |
330 + dbg("dk: lookup timed out after $timeout seconds"); | |
331 + return 0; | |
332 } | |
333 | |
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 } | |
344 | |
345 | |
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/SpamAssass
in/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 1
1:51:59 2006 | |
351 @@ -35,6 +35,7 @@ | |
352 | |
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 @@ | |
360 | |
361 $pyzor_count = 0; | |
362 $pyzor_whitelisted = 0; | |
363 - | |
364 - $permsgstatus->enter_helper_run_mode(); | |
365 + my $pid; | |
366 | |
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; | |
370 | |
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}); | |
380 + | |
381 + my $opts = $self->{main}->{conf}->{pyzor_options} || ''; | |
382 | |
383 - $oldalarm = alarm $timeout; | |
384 + $permsgstatus->enter_helper_run_mode(); | |
385 | |
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 { | |
390 | |
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")); | |
395 | |
396 @@ -273,21 +269,7 @@ | |
397 die("internal error\n"); | |
398 } | |
399 | |
400 - # note: this must be called BEFORE leave_helper_run_mode() | |
401 - # $self->cleanup_kids($pid); | |
402 - | |
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 - }; | |
409 - | |
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 + }); | |
416 | |
417 if (defined(fileno(*PYZOR))) { # still open | |
418 if ($pid) { | |
419 @@ -299,11 +281,14 @@ | |
420 } | |
421 $permsgstatus->leave_helper_run_mode(); | |
422 | |
423 + if ($timer->timed_out()) { | |
424 + dbg("pyzor: check timed out after $timeout seconds"); | |
425 + return 0; | |
426 + } | |
427 + | |
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 @@ | |
438 | |
439 sub pyzor_report { | |
440 my ($self, $options, $tmpf) = @_; | |
441 + | |
442 + # note: not really tainted, this came from system configuration file | |
443 + my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{c
onf}->{pyzor_path}); | |
444 + | |
445 + my $opts = $options->{report}->{conf}->{pyzor_options} || ''; | |
446 my $timeout = $self->{main}->{conf}->{pyzor_timeout}; | |
447 | |
448 $options->{report}->enter_helper_run_mode(); | |
449 | |
450 - my $oldalarm = 0; | |
451 + my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); | |
452 + my $err = $timer->run_and_catch(sub { | |
453 | |
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 | |
458 - | |
459 - $oldalarm = alarm $timeout; | |
460 - | |
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}); | |
463 - | |
464 - my $opts = $options->{report}->{conf}->{pyzor_options} || ''; | |
465 | |
466 dbg("pyzor: opening pipe: " . join(' ', $path, $opts, "report", "< $tmpf"))
; | |
467 | |
468 @@ -391,23 +372,19 @@ | |
469 my @ignored = <PYZOR>; | |
470 $options->{report}->close_pipe_fh(\*PYZOR); | |
471 | |
472 - if (defined $oldalarm) { | |
473 - alarm $oldalarm; $oldalarm = undef; | |
474 - } | |
475 waitpid ($pid, 0); | |
476 - }; | |
477 + }); | |
478 | |
479 - my $err = $@; | |
480 - if (defined $oldalarm) { | |
481 - alarm $oldalarm; $oldalarm = undef; | |
482 - } | |
483 $options->{report}->leave_helper_run_mode(); | |
484 | |
485 + if ($timer->timed_out()) { | |
486 + dbg("reporter: pyzor report timed out after $timeout seconds"); | |
487 + return 0; | |
488 + } | |
489 + | |
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"); | |
499 | |
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/SpamAssass
in/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 } | |
507 | |
508 Mail::SpamAssassin::PerMsgStatus::enter_helper_run_mode($self); | |
509 - my $oldalarm = 0; | |
510 | |
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 { | |
515 | |
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 | |
520 | |
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(); | |
529 | |
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 + } | |
537 | |
538 - # if we got here, we're done doing remote stuff, abort the alert | |
539 - if (defined $oldalarm) { | |
540 - alarm $oldalarm; $oldalarm = undef; | |
541 - } | |
542 - | |
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; | |
547 | |
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 - } | |
563 - | |
564 - if ($type eq 'check') { | |
565 - # so $objects->[0] is the first (only) message, and ->{spam} is a gene
ral 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 + } | |
582 | |
583 - # great for debugging, but leave this off! | |
584 - #use Data::Dumper; | |
585 - #print Dumper($objects),"\n"; | |
586 - | |
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 suppor
t 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}; | |
604 - | |
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; | |
609 - | |
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 for
mat!"); | |
625 - } | |
626 - } | |
627 + if ($type eq 'check') { | |
628 + # so $objects->[0] is the first (only) message, and ->{spam} is a gener
al yes/no | |
629 + push(@results, { result => $objects->[0]->{spam} }); | |
630 + | |
631 + # great for debugging, but leave this off! | |
632 + #use Data::Dumper; | |
633 + #print Dumper($objects),"\n"; | |
634 + | |
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}; | |
652 + | |
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; | |
657 + | |
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 form
at!"); | |
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 + }); | |
685 + | |
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; | |
690 | |
691 - my $err = $@; | |
692 - if (defined $oldalarm) { | |
693 - alarm $oldalarm; $oldalarm = undef; | |
694 + Mail::SpamAssassin::PerMsgStatus::leave_helper_run_mode($self); | |
695 + | |
696 + if ($timer->timed_out()) { | |
697 + dbg("$debug: razor2 $type timed out after $timeout seconds"); | |
698 } | |
699 | |
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 } | |
713 - | |
714 - # work around serious brain damage in Razor2 (constant seed) | |
715 - srand; | |
716 - | |
717 - Mail::SpamAssassin::PerMsgStatus::leave_helper_run_mode($self); | |
718 | |
719 # razor also debugs to stdout. argh. fix it to stderr... | |
720 if (would_log('dbg', $debug)) { | |
721 | |
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/SpamAssass
in/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 @@ | |
728 | |
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 @@ | |
736 | |
737 my ($result, $comment); | |
738 my $timeout = $scanner->{conf}->{spf_timeout}; | |
739 - my $oldalarm = 0; | |
740 | |
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 { | |
747 + | |
748 ($result, $comment) = $query->result(); | |
749 - if (defined $oldalarm) { | |
750 - alarm $oldalarm; $oldalarm = undef; | |
751 - } | |
752 - }; | |
753 | |
754 - my $err = $@; | |
755 - if (defined $oldalarm) { | |
756 - alarm $oldalarm; $oldalarm = undef; | |
757 - } | |
758 + }); | |
759 | |
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 } | |
770 | |
771 | |
772 Modified: spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm | |
773 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssass
in/SpamdForkScaling.pm?rev=384590&r1=384589&r2=384590&view=diff | |
774 ============================================================================== | |
775 --- spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm (origina
l) | |
776 +++ spamassassin/branches/3.1/lib/Mail/SpamAssassin/SpamdForkScaling.pm Thu Mar
9 11:51:59 2006 | |
777 @@ -25,6 +25,7 @@ | |
778 | |
779 use Mail::SpamAssassin::Util; | |
780 use Mail::SpamAssassin::Logger; | |
781 +use Mail::SpamAssassin::Timeout; | |
782 | |
783 use vars qw { | |
784 @PFSTATE_VARS %EXPORT_TAGS @EXPORT_OK | |
785 @@ -109,6 +110,9 @@ | |
786 | |
787 delete $self->{kids}->{$pid}; | |
788 | |
789 + # note this for the select()-caller's benefit | |
790 + $self->{child_just_exited} = 1; | |
791 + | |
792 # remove the child from the backchannel list, too | |
793 $self->{backchannel}->delete_socket_for_child($pid); | |
794 | |
795 @@ -188,24 +192,63 @@ | |
796 vec($rin, $self->{server_fileno}, 1) = 0; | |
797 } | |
798 | |
799 - my ($rout, $eout, $nfound, $timeleft); | |
800 + my ($rout, $eout, $nfound, $timeleft, $selerr); | |
801 + | |
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... | |
805 + | |
806 + my $timer = Mail::SpamAssassin::Timeout->new({ secs => ($tout*2) + 60 }); | |
807 | |
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 { | |
813 + | |
814 + $self->{child_just_exited} = 0; | |
815 ($nfound, $timeleft) = select($rout=$rin, undef, $eout=$rin, $tout); | |
816 - }; | |
817 - alarm 0; | |
818 + $selerr = $!; | |
819 | |
820 - if ($@) { | |
821 - warn "prefork: select timeout failed! recovering\n"; | |
822 - sleep 1; # avoid overload | |
823 - return; | |
824 - } | |
825 + }); | |
826 + | |
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 + } | |
835 + | |
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 + } | |
849 + | |
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 1
3101 due to SIGCHLD | |
855 + # Jan 29 12:53:17 dogma spamd[18518]: prefork: select returned -1! recoveri
ng: | |
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 + } | |
863 + | |
864 + warn "prefork: select returned ". | |
865 + (defined $nfound ? $nfound : "undef"). | |
866 + "! recovering: $selerr\n"; | |
867 | |
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 } | |
881 | |
882 @@ -282,7 +325,7 @@ | |
883 | |
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; | |
888 | |
889 warn "prefork: write of ping failed to $kid fd=".$sock->fileno.": ".$!; | |
890 | |
891 @@ -353,7 +396,7 @@ | |
892 return $self->order_idle_child_to_accept(); | |
893 } | |
894 | |
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->f
ileno.": $!\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: $!"; | |
916 } | |
917 | |
918 @@ -537,12 +580,31 @@ | |
919 } | |
920 | |
921 sub syswrite_with_retry { | |
922 - my ($self, $sock, $buf) = @_; | |
923 + my ($self, $sock, $buf, $targetname, $numretries) = @_; | |
924 + $numretries ||= 10; # default 10 retries | |
925 | |
926 my $written = 0; | |
927 + my $try = 0; | |
928 | |
929 retry_write: | |
930 + | |
931 + $try++; | |
932 + if ($try > 1) { | |
933 + warn "prefork: syswrite(".$sock->fileno.") to $targetname failed on try $tr
y"; | |
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 + } | |
945 + | |
946 my $nbytes = $sock->syswrite($buf); | |
947 + | |
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 } | |
954 | |
955 - warn "prefork: syswrite(".$sock->fileno.") failed, retrying..."; | |
956 - | |
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); | |
961 - | |
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 } | |
976 | |
977 Added: spamassassin/branches/3.1/lib/Mail/SpamAssassin/Timeout.pm | |
978 URL: http://svn.apache.org/viewcvs/spamassassin/branches/3.1/lib/Mail/SpamAssass
in/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> | |
998 + | |
999 +=head1 NAME | |
1000 + | |
1001 +Mail::SpamAssassin::Timeout - safe, reliable timeouts in perl | |
1002 + | |
1003 +=head1 SYNOPSIS | |
1004 + | |
1005 + # non-timeout code... | |
1006 + | |
1007 + my $t = Mail::SpamAssassin::Timeout->new({ secs => 5 }); | |
1008 + | |
1009 + $t->run(sub { | |
1010 + # code to run with a 5-second timeout... | |
1011 + }); | |
1012 + | |
1013 + if ($t->timed_out()) { | |
1014 + # do something... | |
1015 + } | |
1016 + | |
1017 + # more non-timeout code... | |
1018 + | |
1019 +=head1 DESCRIPTION | |
1020 + | |
1021 +This module provides a safe, reliable and clean API to provide | |
1022 +C<alarm(2)>-based timeouts for perl code. | |
1023 + | |
1024 +Note that C<$SIG{ALRM}> is used to provide the timeout, so this will not | |
1025 +interrupt out-of-control regular expression matches. | |
1026 + | |
1027 +Nested timeouts are supported. | |
1028 + | |
1029 +=head1 PUBLIC METHODS | |
1030 + | |
1031 +=over 4 | |
1032 + | |
1033 +=cut | |
1034 + | |
1035 +package Mail::SpamAssassin::Timeout; | |
1036 + | |
1037 +use strict; | |
1038 +use warnings; | |
1039 +use bytes; | |
1040 + | |
1041 +use vars qw{ | |
1042 + @ISA | |
1043 +}; | |
1044 + | |
1045 +@ISA = qw(); | |
1046 + | |
1047 +########################################################################### | |
1048 + | |
1049 +=item my $t = Mail::SpamAssassin::Timeout->new({ ... options ... }); | |
1050 + | |
1051 +Constructor. Options include: | |
1052 + | |
1053 +=over 4 | |
1054 + | |
1055 +=item secs => $seconds | |
1056 + | |
1057 +timeout, in seconds. Optional; if not specified, no timeouts will be applied. | |
1058 + | |
1059 +=back | |
1060 + | |
1061 +=cut | |
1062 + | |
1063 +sub new { | |
1064 + my ($class, $opts) = @_; | |
1065 + $class = ref($class) || $class; | |
1066 + my %selfval = $opts ? %{$opts} : (); | |
1067 + my $self = \%selfval; | |
1068 + | |
1069 + bless ($self, $class); | |
1070 + $self; | |
1071 +} | |
1072 + | |
1073 +########################################################################### | |
1074 + | |
1075 +=item $t->run($coderef) | |
1076 + | |
1077 +Run a code reference within the currently-defined timeout. | |
1078 + | |
1079 +The timeout is as defined by the B<secs> parameter to the constructor. | |
1080 + | |
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>. | |
1083 + | |
1084 +Time elapsed is not cumulative; multiple runs of C<run> will restart the | |
1085 +timeout from scratch. | |
1086 + | |
1087 +=item $t->run_and_catch($coderef) | |
1088 + | |
1089 +Run a code reference, as per C<$t-<gt>run()>, but also catching any | |
1090 +C<die()> calls within the code reference. | |
1091 + | |
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()>.) | |
1094 + | |
1095 +=cut | |
1096 + | |
1097 +sub run { $_[0]->_run($_[1], 0); } | |
1098 + | |
1099 +sub run_and_catch { $_[0]->_run($_[1], 1); } | |
1100 + | |
1101 +sub _run { # private | |
1102 + my ($self, $sub, $and_catch) = @_; | |
1103 + | |
1104 + delete $self->{timed_out}; | |
1105 + | |
1106 + if (!$self->{secs}) { # no timeout! just call the sub and return. | |
1107 + return &$sub; | |
1108 + } | |
1109 + | |
1110 + # assertion | |
1111 + if ($self->{secs} < 0) { | |
1112 + die "Mail::SpamAssassin::Timeout: oops? neg value for 'secs': $self->{secs}
"; | |
1113 + } | |
1114 + | |
1115 + my $oldalarm = 0; | |
1116 + my $ret; | |
1117 + | |
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 | |
1122 + | |
1123 + $oldalarm = alarm($self->{secs}); | |
1124 + | |
1125 + $ret = &$sub; | |
1126 + | |
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. | |
1135 + | |
1136 + alarm 0; | |
1137 + }; | |
1138 + | |
1139 + my $err = $@; | |
1140 + | |
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 + } | |
1147 + | |
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 + } | |
1159 + | |
1160 + if ($and_catch) { | |
1161 + return; # undef | |
1162 + } else { | |
1163 + return $ret; | |
1164 + } | |
1165 +} | |
1166 + | |
1167 +########################################################################### | |
1168 + | |
1169 +=item $t->timed_out() | |
1170 + | |
1171 +Returns C<1> if the most recent code executed in C<run()> timed out, or | |
1172 +C<undef> if it did not. | |
1173 + | |
1174 +=cut | |
1175 + | |
1176 +sub timed_out { | |
1177 + my ($self) = @_; | |
1178 + return $self->{timed_out}; | |
1179 +} | |
1180 + | |
1181 +########################################################################### | |
1182 + | |
1183 +=item $t->reset() | |
1184 + | |
1185 +If called within a C<run()> code reference, causes the current alarm timer to | |
1186 +be reset to its starting value. | |
1187 + | |
1188 +=cut | |
1189 + | |
1190 +sub reset { | |
1191 + my ($self) = @_; | |
1192 + alarm($self->{secs}); | |
1193 +} | |
1194 + | |
1195 +########################################################################### | |
1196 + | |
1197 +1; | |
1198 | |
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 = (); | |
1214 | |
1215 | |
1216 | |
1217 | |
1218 | |
OLD | NEW |