Server IP : 85.214.239.14 / Your IP : 3.144.104.175 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/3/task/3/cwd/proc/2/root/usr/share/perl5/Mail/SpamAssassin/Locker/ |
Upload File : |
# <@LICENSE> # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to you under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # </@LICENSE> package Mail::SpamAssassin::Locker::Flock; use strict; use warnings; # use bytes; use re 'taint'; use Mail::SpamAssassin; use Mail::SpamAssassin::Locker; use Mail::SpamAssassin::Util; use Mail::SpamAssassin::Logger; use File::Spec; use IO::File; use Fcntl qw(:DEFAULT :flock); our @ISA = qw(Mail::SpamAssassin::Locker); ########################################################################### sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self; } ########################################################################### # Attempt to create a file lock, using NFS-UNsafe locking techniques. sub safe_lock { my ($self, $path, $max_retries, $mode) = @_; my $is_locked = 0; my @stat; $max_retries ||= 30; $mode ||= "0700"; $mode = (oct $mode) & 0666; dbg ("locker: mode is $mode"); my $lock_file = "$path.mutex"; my $umask = umask(~$mode); my $fh = IO::File->new; if (!$fh->open ($lock_file, O_RDWR|O_CREAT)) { umask $umask; # just in case die "locker: safe_lock: cannot create lockfile $lock_file: $!\n"; } umask $umask; # we've created the file, so reset umask dbg("locker: safe_lock: created $lock_file"); my $unalarmed = 0; my $oldalarm = 0; # use a SIGALRM-based timer -- more efficient than second-by-second # sleeps my $eval_stat; eval { local $SIG{ALRM} = sub { die "alarm\n" }; dbg("locker: safe_lock: trying to get lock on $path with $max_retries timeout"); # max_retries is basically seconds! so use it for the timeout $oldalarm = alarm $max_retries; # HELLO!?! IO::File doesn't have a flock() method?! if (!flock($fh, LOCK_EX)) { warn "locker: safe_lock: cannot obtain a lock on log file: $!"; } else { alarm $oldalarm; $unalarmed = 1; # avoid calling alarm(0) twice dbg("locker: safe_lock: link to $lock_file: link ok"); $is_locked = 1; # just to be nice: let people know when it was locked $fh->print("$$\n") or die "error writing to lock file: $!"; $fh->flush or die "cannot flush lock file: $!"; # keep the FD around - we need to keep the lockfile open or the lock # is unlocked! $self->{lock_fhs} ||= { }; $self->{lock_fhs}->{$path} = $fh; } 1; } or do { $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; }; $unalarmed or alarm $oldalarm; # if we die'd above, need to reset here if (defined $eval_stat) { if ($eval_stat =~ /alarm/) { dbg("locker: safe_lock: timed out after $max_retries seconds"); } else { die "locker: safe_lock: $eval_stat\n"; } } return $is_locked; } ########################################################################### sub safe_unlock { my ($self, $path) = @_; if (!exists $self->{lock_fhs} || !defined $self->{lock_fhs}->{$path}) { dbg("locker: safe_unlock: no lock handle for $path - already unlocked?"); return; } my $fh = $self->{lock_fhs}->{$path}; delete $self->{lock_fhs}->{$path}; flock($fh, LOCK_UN) or die "cannot unlock a log file: $!"; $fh->close or die "error closing a lock file: $!"; dbg("locker: safe_unlock: unlocked $path.mutex"); # do NOT unlink! this would open a race, whereby: # # procA: ....unlock (unlocked lockfile) # procB: lock (gets lock on lockfile) # procA: unlink (deletes lockfile) # (procB's lock is now deleted as well!) # procC: create, lock (gets lock on new file) # # both procB and procC would then think they had locks, and both # would write to the database file. this is bad. # # unlink ("$path.mutex"); # # side-effect: we leave a .mutex file around. but hey! } ########################################################################### sub refresh_lock { my($self, $path) = @_; return unless $path; if (!exists $self->{lock_fhs} || !defined $self->{lock_fhs}->{$path}) { warn "locker: refresh_lock: no lock handle for $path\n"; return; } my $fh = $self->{lock_fhs}->{$path}; $fh->print("$$\n") or die "error writing to lock file: $!"; $fh->flush or die "cannot flush lock file: $!"; dbg("locker: refresh_lock: refresh $path.mutex"); } ########################################################################### 1;