|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # <@LICENSE>  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Licensed to the Apache Software Foundation (ASF) under one or more  | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # contributor license agreements.  See the NOTICE file distributed with  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # this work for additional information regarding copyright ownership.  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # The ASF licenses this file to you under the Apache License, Version 2.0  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # (the "License"); you may not use this file except in compliance with  | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # the License.  You may obtain a copy of the License at:  | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     http://www.apache.org/licenses/LICENSE-2.0  | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Unless required by applicable law or agreed to in writing, software  | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # distributed under the License is distributed on an "AS IS" BASIS,  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # See the License for the specific language governing permissions and  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # limitations under the License.  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # </@LICENSE>  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use strict;  | 
| 
20
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
166
 | 
 use warnings;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
880
 | 
    | 
| 
21
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
138
 | 
 # use bytes;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1285
 | 
    | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use re 'taint';  | 
| 
23
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
142
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1256
 | 
    | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use Mail::SpamAssassin;  | 
| 
25
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
139
 | 
 use Mail::SpamAssassin::Locker;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
719
 | 
    | 
| 
26
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
8949
 | 
 use Mail::SpamAssassin::Util;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
769
 | 
    | 
| 
27
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
155
 | 
 use Mail::SpamAssassin::Logger;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1375
 | 
    | 
| 
28
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
129
 | 
 use File::Spec;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1690
 | 
    | 
| 
29
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
162
 | 
 use Time::Local;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
751
 | 
    | 
| 
30
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
122
 | 
 use Fcntl qw(:DEFAULT :flock);  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2172
 | 
    | 
| 
31
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
143
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12822
 | 
    | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our @ISA = qw(Mail::SpamAssassin::Locker);  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################################  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $class = shift;  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $self = $class->SUPER::new(@_);  | 
| 
38
 | 
92
 | 
 
 | 
 
 | 
  
92
  
 | 
  
0
  
 | 
322
 | 
   $self;  | 
| 
39
 | 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
607
 | 
 }  | 
| 
40
 | 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2530
 | 
    | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###########################################################################  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # NFS-safe locking (I hope!):  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Attempt to create a file lock, using NFS-safe locking techniques.  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Locking code adapted from code by Alexis Rosen <alexis@panix.com>  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # by Kelsey Cummings <kgc@sonic.net>, with mods by jm and quinlan  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # A good implementation of Alexis' code, for reference, is here:  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # http://mail-index.netbsd.org/netbsd-bugs/1996/04/17/0002.html  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use constant LOCK_MAX_AGE => 600;	# seconds   | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
53
 | 
26
 | 
 
 | 
 
 | 
  
26
  
 | 
 
 | 
177
 | 
   my ($self, $path, $max_retries, $mode) = @_;  | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
    | 
| 
 
 | 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30266
 | 
    | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $is_locked = 0;  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my @stat;  | 
| 
56
 | 
36
 | 
 
 | 
 
 | 
  
36
  
 | 
  
0
  
 | 
141
 | 
    | 
| 
57
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
   $max_retries ||= 30;  | 
| 
58
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
   $mode ||= "0700";  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $mode = (oct $mode) & 0666;  | 
| 
60
 | 
36
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
110
 | 
   dbg ("locker: mode is $mode");  | 
| 
61
 | 
36
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
108
 | 
    | 
| 
62
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
130
 | 
   my $lock_file = "$path.lock";  | 
| 
63
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
237
 | 
   my $hname = Mail::SpamAssassin::Util::fq_hostname();  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $lock_tmp = Mail::SpamAssassin::Util::untaint_file_path  | 
| 
65
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
99
 | 
                                       ($path.".lock.".$hname.".".$$);  | 
| 
66
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
271
 | 
    | 
| 
67
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
298
 | 
   # keep this for unlocking  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $self->{lock_tmp} = $lock_tmp;  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $umask = umask(~$mode);  | 
| 
71
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
215
 | 
   if (!open(LTMP, ">$lock_tmp")) {  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       umask $umask; # just in case  | 
| 
73
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
289
 | 
       die "locker: safe_lock: cannot create tmp lockfile $lock_tmp for $lock_file: $!\n";  | 
| 
74
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
3433
 | 
   }  | 
| 
75
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   umask $umask;  | 
| 
76
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   autoflush LTMP 1;  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   dbg("locker: safe_lock: created $lock_tmp");  | 
| 
78
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
272
 | 
    | 
| 
79
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
432
 | 
   for (my $retries = 0; $retries < $max_retries; $retries++) {  | 
| 
80
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3480
 | 
     if ($retries > 0) { $self->jittery_one_second_sleep(); }  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print LTMP "$hname.$$\n"  or warn "Error writing to $lock_tmp: $!";  | 
| 
82
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
180
 | 
     dbg("locker: safe_lock: trying to get lock on $path with $retries retries");  | 
| 
83
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
130
 | 
     if (link($lock_tmp, $lock_file)) {  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
84
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1918
 | 
       dbg("locker: safe_lock: link to $lock_file: link ok");  | 
| 
85
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
318
 | 
       $is_locked = 1;  | 
| 
86
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1273
 | 
       last;  | 
| 
87
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
256
 | 
     }  | 
| 
88
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
69
 | 
     # link _may_ return false even if the link _is_ created  | 
| 
89
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
113
 | 
     @stat = lstat($lock_tmp);  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     @stat  or warn "locker: error accessing $lock_tmp: $!";  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (defined $stat[3] && $stat[3] > 1) {  | 
| 
92
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       dbg("locker: safe_lock: link to $lock_file: stat ok");  | 
| 
93
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $is_locked = 1;  | 
| 
94
 | 
  
0
  
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
       last;  | 
| 
95
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     }  | 
| 
96
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     # check age of lockfile ctime  | 
| 
97
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $now = ($#stat < 11 ? undef : $stat[10]);  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     @stat = lstat($lock_file);  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     @stat  or warn "locker: error accessing $lock_file: $!";  | 
| 
100
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $lock_age = ($#stat < 11 ? undef : $stat[10]);  | 
| 
101
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     if (defined($lock_age) && defined($now) && ($now - $lock_age) > LOCK_MAX_AGE)  | 
| 
102
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     {  | 
| 
103
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       # we got a stale lock, break it  | 
| 
104
 | 
  
0
  
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
       dbg("locker: safe_lock: breaking stale $lock_file: age=" .  | 
| 
 
 | 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  (defined $lock_age ? $lock_age : "undef") . " now=$now");  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       unlink($lock_file)  | 
| 
107
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         or warn "locker: safe_lock: unlink of lock file $lock_file failed: $!\n";  | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
109
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   }  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   close LTMP  or die "error closing $lock_tmp: $!";  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   unlink($lock_tmp)  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     or warn "locker: safe_lock: unlink of temp lock $lock_tmp failed: $!\n";  | 
| 
114
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
594
 | 
    | 
| 
115
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1184
 | 
   # record this for safe unlocking  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if ($is_locked) {  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     @stat = lstat($lock_file);  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     @stat  or warn "locker: error accessing $lock_file: $!";  | 
| 
119
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
168
 | 
     my $lock_ctime = ($#stat < 11 ? undef : $stat[10]);  | 
| 
120
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
742
 | 
    | 
| 
121
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
167
 | 
     $self->{lock_ctimes} ||= { };  | 
| 
122
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
158
 | 
     $self->{lock_ctimes}->{$path} = $lock_ctime;  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
124
 | 
36
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
242
 | 
    | 
| 
125
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
120
 | 
   return $is_locked;  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
128
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
210
 | 
 ###########################################################################  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my ($self, $path) = @_;  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $lock_file = "$path.lock";  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $lock_tmp = $self->{lock_tmp};  | 
| 
134
 | 
36
 | 
 
 | 
 
 | 
  
36
  
 | 
  
0
  
 | 
120
 | 
   if (!$lock_tmp) {  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     dbg("locker: safe_unlock: $path.lock never locked");  | 
| 
136
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
126
 | 
     return;  | 
| 
137
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
92
 | 
   }  | 
| 
138
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
104
 | 
    | 
| 
139
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   # 1. Build a temp file and stat that to get an idea of what the server  | 
| 
140
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   # thinks the current time is (our_tmp.st_ctime).  note: do not use time()  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # directly because the server's clock may be out of sync with the client's.  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my @stat_ourtmp;  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (!defined sysopen(LTMP, $lock_tmp, O_CREAT|O_WRONLY|O_EXCL, 0700)) {  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     warn "locker: safe_unlock: failed to create lock tmpfile $lock_tmp: $!";  | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return;  | 
| 
147
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
79
 | 
   } else {  | 
| 
148
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
3160
 | 
     autoflush LTMP 1;  | 
| 
149
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     print LTMP "\n"  or warn "Error writing to $lock_tmp: $!";  | 
| 
150
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (!(@stat_ourtmp = stat(LTMP)) || (scalar(@stat_ourtmp) < 11)) {  | 
| 
152
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
386
 | 
       @stat_ourtmp  or warn "locker: error accessing $lock_tmp: $!";  | 
| 
153
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
3479
 | 
       warn "locker: safe_unlock: failed to create lock tmpfile $lock_tmp";  | 
| 
154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       close LTMP  or die "error closing $lock_tmp: $!";  | 
| 
155
 | 
36
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
753
 | 
       unlink($lock_tmp)  | 
| 
156
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         or warn "locker: safe_lock: unlink of lock file failed: $!\n";  | 
| 
157
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return;  | 
| 
158
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     }  | 
| 
159
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   }  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
161
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my $ourtmp_ctime = $stat_ourtmp[10]; # paranoia  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (!defined $ourtmp_ctime) {  | 
| 
163
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     die "locker: safe_unlock: stat failed on $lock_tmp";  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
165
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
151
 | 
    | 
| 
166
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
124
 | 
   close LTMP  or die "error closing $lock_tmp: $!";  | 
| 
167
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   unlink($lock_tmp)  | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     or warn "locker: safe_lock: unlink of lock file failed: $!\n";  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
170
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
438
 | 
   # 2. If the ctime hasn't been modified, unlink the file and return. If the  | 
| 
171
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
2045
 | 
   # lock has expired, sleep the usual random interval before returning. If we  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # didn't sleep, there could be a race if the caller immediately tries to  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # relock the file.  | 
| 
174
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $lock_ctime = $self->{lock_ctimes}->{$path};  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (!defined $lock_ctime) {  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     warn "locker: safe_unlock: no ctime recorded for $lock_file";  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return;  | 
| 
179
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
186
 | 
   }  | 
| 
180
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
125
 | 
    | 
| 
181
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my @stat_lock = lstat($lock_file);  | 
| 
182
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   @stat_lock  or warn "locker: error accessing $lock_file: $!";  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $now_ctime = $stat_lock[10];  | 
| 
185
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
499
 | 
    | 
| 
186
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
170
 | 
   if (defined $now_ctime && $now_ctime == $lock_ctime)   | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   {  | 
| 
188
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
99
 | 
     # things are good: the ctimes match so it was our lock  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     unlink($lock_file)  | 
| 
190
 | 
36
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
219
 | 
       or warn "locker: safe_unlock: unlink failed: $lock_file\n";  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     dbg("locker: safe_unlock: unlink $lock_file");  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
193
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1260
 | 
     if ($ourtmp_ctime >= $lock_ctime + LOCK_MAX_AGE) {  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # the lock has expired, so sleep a bit; use some randomness  | 
| 
195
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
294
 | 
       # to avoid race conditions.  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       dbg("locker: safe_unlock: lock expired on $lock_file expired safely; sleeping");  | 
| 
197
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
149
 | 
       my $i; for ($i = 0; $i < 5; $i++) {  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->jittery_one_second_sleep();  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
200
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     }  | 
| 
201
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return;  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
202
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   }  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # 4. Either ctime has been modified, or the entire lock file is missing.  | 
| 
205
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
169
 | 
   # If the lock should still be ours, based on the ctime of the temp  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # file, warn it was stolen. If not, then our lock is expired and  | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # someone else has grabbed the file, so warn it was lost.  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if ($ourtmp_ctime < $lock_ctime + LOCK_MAX_AGE) {  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     warn "locker: safe_unlock: lock on $lock_file was stolen";  | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   } else {  | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     warn "locker: safe_unlock: lock on $lock_file was lost due to expiry";  | 
| 
212
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   }  | 
| 
213
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 }  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
215
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 ###########################################################################  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my($self, $path) = @_;  | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   return unless $path;  | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # this could arguably read the lock and make sure the same process  | 
| 
222
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
0
  
 | 
16
 | 
   # owns it, but this shouldn't, in theory, be an issue.  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # TODO: in NFS, it definitely may be one :(  | 
| 
224
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $lock_file = "$path.lock";  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   utime time, time, $lock_file;  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # update the lock_ctimes entry  | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my @stat = lstat($lock_file);  | 
| 
230
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
   @stat  or warn "locker: error accessing $lock_file: $!";  | 
| 
231
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
163
 | 
    | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my $lock_ctime = ($#stat < 11 ? undef : $stat[10]);  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $self->{lock_ctimes}->{$path} = $lock_ctime;  | 
| 
234
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75
 | 
    | 
| 
235
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
36
 | 
   dbg("locker: refresh_lock: refresh $path.lock");  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
237
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
238
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 ###########################################################################  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
240
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
29
 | 
 1;  |