# -*- Perl -*-
#
# Copyright (C) 1996 Craig Ruefenacht <ruefenac@cs.utah.edu> and the
#                    University of Utah Computer Science Department
#
# $Id: Lock.pm,v 1.18 1997/04/16 20:18:29 ruefenac Exp $
#
# This is part of RUST-1.0 Beta 6
#
# Abstract: This library contains a class defination to use to
#           deal with file locking.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with the source distribution of RUST, in the license
# directory. If you don't have a copy of it, write to: 
#
# Free Software Foundation, Inc.
# 675 Mass Ave
# Cambridge, MA 02139, USA.

{

package Lock;
use Rust_Debug;
use Rust_Lib;

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub new {
    # 
    # Abstract: Create a new hash to store the names of locked
    #           files in
    #

    my ($type) = shift;
    my ($self) = {};
    
    bless $self, $type;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub add {
    #
    # Abstract: Lock a file and, if successful, add the name of the
    #           locked file to the hash
    
    my ($self) = shift;
    my($file_name, $hostname, $caller, $tries) = @_;
    my (@keys) = keys %$self;
    my ($i) = 0;
    my ($file_name_lock) = "$file_name.lock";

    # Untaint the lock filename here just in case....
    $file_name_lock = Rust_Lib::untaint_obj($file_name_lock);

    # If we are already locked, then no need to re-lock the file

    foreach $key (@keys) {
	if($key eq $file_name_lock) { 
	    Rust_Debug::debug("$file_name already locked by this process...continuing\n");
	    return 1; 
	}
    }

    # It wasn't locked, so try and lock it

    do {

        # Try $tries times to lock the file if there is a lock file
	# that exists.  This means that some other process is
	# using the file at this instance, but trying to lock the
	# file $tries times allows plenty of time for the other process
	# to finish with the file and unlock it so we can use it.
	# If filelocking fails after $tries tries, return a fail value of 0

	$i = 0;
        while(-f $file_name_lock) { 
	    if(++$i >= $tries) { return 28; }
	    warn "Try # $i to lock file $file_name_lock...\n";
            sleep(5);
	}
 
	# We were successful in making the lock file.  Now, put some stuff

        Rust_Debug::debug("Attempting to lock $file_name\n");
        open(LOCK_FILE, ">$file_name_lock");
        print LOCK_FILE "$file_name:$$:$hostname:$caller\n";
        close(LOCK_FILE);
        
        sleep(1);
        
	# To make sure the lock file has correct content, re-open the file
	# and extract the pid# from it.  Make sure that the pid number
	# extracted matches the pid that this script is using.  If they
	# match, then the lock file is not corrupt.  If these do not match,
	# then keep trying until we get it right.  It is important to get
	# it right, so that we make sure we don't unlock wrong files when
	# we exit, only unlock the ones that were locked with this pid.

        open(LOCK_FILE, "<$file_name_lock");
        $contents = <LOCK_FILE>;
        close(LOCK_FILE);
        
	$process = (split(":",$contents))[1];

    } while($process != $$);
    
    Rust_Debug::debug("File locking of $file_name was successful\n");
    $self->{$file_name_lock} = 1;
    return 1;
}

#-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
sub unlock_all {
    #
    # Abstract: Unlock all files that are in the hash so that we
    #           can exit and not leave any behind
    #

    my ($self) = shift;
    my (@keys) = keys %$self;

    # As a double check, check which pid has the file locked.  If
    # it is the same pid that this script is running under, then
    # unlock the file.  If the pid in the lockfile and the pid
    # that runs this script DOES NOT match, then DON'T unlock it.
    #
    # See the "add" subroutine above for further detail on this.

    foreach $key (@keys) {
	open(LOCK_FILE, "<$key");
        $contents = <LOCK_FILE>;
        close(LOCK_FILE);
	
	$process = (split(":",$contents))[1];

        unlink($key) if($process == $$) ;
	$self->{$key} = 0
    }

}
}

1;

# EOF

