MOON
Server: Apache/2.2.31 (Unix) mod_ssl/2.2.31 OpenSSL/0.9.8e-fips-rhel5 mod_bwlimited/1.4
System: Linux csr818.wilogic.com 2.6.18-419.el5xen #1 SMP Fri Feb 24 22:50:37 UTC 2017 x86_64
User: digitals (531)
PHP: 5.4.45
Disabled: NONE
Upload Files
File: //proc/self/root/scripts.20110531.215904.25158/AcctLock.pm
package AcctLock;

# cpanel - AcctLock.pm                       Copyright(c) 1999-2008 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use strict;
use Fcntl          ();
use cPScript::Logger ();
use cPScript::WHMAPI ();

my $logger = cPScript::Logger->new();

# Accounting lock file location, not used or specified anywhere else
my $accounting_lock_file = '/var/cpanel/acct.lock';
my $lock_expire_seconds  = 100;                        # Wait time for lock expiration before stealing lock
my $loop_check           = 0;                          # Internal counter to prevent deep recursion

#
# is it currently locked?
# externally no need to know
# the exact name of the file
#
sub is_acctlock {
    return 1 if (-e $accounting_lock_file);
    return 0;
} # end of is_acctlock


sub acctlock {
    $loop_check++;

    # Terminate recursion if already failed 10 times
    # Yes, but the caller's aren't checking for return
    # status so what they do is unsafe by any measure
    if ( $loop_check >= 10 ) {
        $logger->warn('acctlock failed for 10 iterations');
        return 0;
    }

    # first see if we can assert it right now
    if (! -e $accounting_lock_file) {
        if (assert_lock()) {
            # and make sure we got it
            my ($pid, $pname) = lock_details();
            return 1 if ($pid == $$ && $pname eq $0);
        }
    }

    # Check for an existing lock file
    if ( -e $accounting_lock_file ) {
        my $lock_time   = 0;
        my $mtime       = 0;
        my $start_mtime = ( stat(_) )[9];

        # Wait for lock to expire
      LOCKWAIT:
        while ($start_mtime && -e $accounting_lock_file) {
            $mtime = ( stat(_) )[9];

            if ($mtime) {
                # lock file has aged
                if ( $mtime == $start_mtime ) {
                    my $will_expire_seconds = ( $lock_expire_seconds - $lock_time );
                    $lock_time++;
                    _showstatus("Waiting $will_expire_seconds seconds for accounting lock ...");
                }

                # there is a new lock file, reset
                else {
                    $start_mtime = $mtime;
                    $lock_time   = 0;
                }
            }

            # Lock file removed by holding process
            else {
                last LOCKWAIT;
            }

            # lock file aged 100 sec
            if ( $lock_time == $lock_expire_seconds ) {
                if (-e $accounting_lock_file) {
                    my ($pid, $pname) = lock_details();
                    if ( $pid && $pname ) {
                    _showstatus("Stealing dead lock from process: $pid ($pname)");
                    }
                    else {
                        $logger->warn("Invalid accounting lock file $accounting_lock_file encountered");
                        if ( -s $accounting_lock_file ) {
                            $logger->warn("Lock file is not empty. Will not proceed.");
                            return;
                        }
                    }

                    # Only attempt to remove a lock file that can be read
                    unlink $accounting_lock_file or $logger->warn("Failed to unlink expired lock file $accounting_lock_file: $!");
                    last LOCKWAIT;
                }
                else {
                    $logger->warn("Lock file $accounting_lock_file has expired, but unable to read status: $!");
                }
            }
            # now sleep if need be
            sleep 1;
        } # while
    }

    if (assert_lock()) {
        # and make sure we got it
        my ($pid, $pname) = lock_details();
        return 1 if ($pid == $$ && $pname eq $0);
    }
    else {
        $logger->info("Failed to grab exclusive lock: $!");
        # okay this is where the recursion for $loop_check comes in
        acctlock();
    }
} # end of acctlock


sub assert_lock {
    if ( sysopen( my $lock_fh, $accounting_lock_file, &Fcntl::O_WRONLY | &Fcntl::O_CREAT | &Fcntl::O_EXCL, 0600 ) ) {
        print {$lock_fh} $$ . "\n";
        print {$lock_fh} $0 . "\n";
        close $lock_fh;
        _clearstatus();
        return 1;
    }
    return 0;
} # end of assert_lock


sub acctunlock {
    if ( -e $accounting_lock_file ) {
        if ( open my $acct_fh, '<', $accounting_lock_file ) {
            my @info = <$acct_fh>;
            close($acct_fh);
            chomp(@info);
            close $acct_fh;
            # Rely upon unlink to return success/failure
            # ours? just unlnk and get out of here
            if ($info[0] == $$) {
                my $rtn_val = unlink $accounting_lock_file;
                return $rtn_val if ( $rtn_val );
                $logger->warn("Failed to unlink $accounting_lock_file: $!");
            }
            if ( int $info[0] != $$ ) {
                if ( @info == 2 ) {
                    $logger->warn("Account Lock stolen from process $info[1] with PID $info[0]");
                }
                else {
                    $logger->warn('Account Lock stolen from invalid locking process');
                }
            }
        }
        else {
            $logger->warn("Unable to unlock accounting lock, failed to read $accounting_lock_file: $!");
            return;
        }
    }
    return 1;
} # end of acctunlock


sub _showstatus {
    my ($message) = @_;
    return if !$message;
    if ( -t STDIN ) {
        print $message . "\n";
    }
    else {
        cPScript::WHMAPI::setstatus($message);
    }
} # end of _showstatus


sub _clearstatus {
    $loop_check = 0;
    if ( !-t STDIN ) {
        cPScript::WHMAPI::clearstatus();
    }
} # end of _clearstatus


sub lockacct   { goto &acctlock; }
sub unacctlock { goto &acctunlock; }


#
# maybe just for debug
# else the caller's lvalue list is undefined
#
sub lock_details {
    if (-e $accounting_lock_file) {
        my $fh;
        open($fh, '<', $accounting_lock_file);
        my @file = <$fh>;
        close($fh);
        chomp(@file);
        return @file;
    }
} # end of lock_details


#
# a variation of lock_details
# does the check here
#
sub owned_lock {
    my ($pid, $pname) = lock_details();
    return 1 if ($pid && $pname && $pid == $$ && $pname eq $0);
    return 0;
} # end of owned_lock

1;