#
# Copyright (c) 1991,1992 The Ohio State University.
# All rights reserved.
#
# Redistribution and use in source and binary forms are permitted
# provided that: (1) source distributions retain this entire copyright
# notice and comment, and (2) distributions including binaries display
# the following acknowledgement:  ``This product includes software
# developed by The Ohio State University and its contributors''
# in the documentation or other materials provided with the distribution
# and in all advertising materials mentioning features or use of this
# software. Neither the name of the University nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#

$LOCK_OK = 0;
$LOCK_NOT_OK = 1;

#
# Heck, we almost always need the darned hostname, might as well get it 
# once and for all...
#
if (! defined($hostname)) {
    open(HOST, "$hostname_prog |") || die "can't get the hostname";
    chop($hostname=<HOST>);
    close(HOST);

    ($hostname) = split(/\./, $hostname);
}

sub get_lock_names {
    local($name) = @_;
    local($full, $tmp);

    $name =~ s/.lock$//;

    if (index($name, "/") == -1) {
	$full = $lock_dir . "/" . $name;
    } else {
	$full = $name;
    }
    $tmp = "$full.$hostname.$$.lock";
    $full = "$full.lock";

    return($full, $tmp);
}

#
# Give us an exclusive lock on a file.  It would be nice if we had 
# shared locks also.
#

sub lock {
    local($name, $timeout) = @_;
    local($fullname, $tmpfile, $start_time, $lock_age);

    $start_time = time;

    printf(stderr "$hostname,$0 requests lock on '$name'\n") if $opt_d & $DEBUG_LOCK;

    ($fullname, $tmpname) = &get_lock_names($name);

    if (! defined($timeout)) {
	$timeout = 600;
    }

    if (-e $tmpname) {
	unlink($tmpname);
    }

    while ((time - $start_time) < $timeout) {
	#
	# Create the target for the link if it doesn't exist.  This needs
	# to be within the loop in case we're stuck on a lock and someone
	# removes *lock in the database directory, nuking the target files 
	# with the locks.
	#
	if (! -e $tmpname) {
	    open(TMPFILE, ">$tmpname");
	    print TMPFILE "program $0 host $hostname pid $$\n";
	    close(TMPFILE);
	}

	#
	# link worked, got the lock
	#
        if (link($tmpname, $fullname)) {
	    print stderr "$hostname,$0 got lock on '$name'\n"
	      if $opt_d & $DEBUG_LOCK;
	
	    $my_lock_list{$name} = 1;

	    return($LOCK_OK);
	}

	#
	# major league paranoia here
	#
	if ((lstat($tmpname))[3] == 2) {
	    &make_log_entry("program $0 host $hostname - J's paranoia paid off on $fullname");
	    print stderr "$hostname,$0 paranoia paid off on '$name'\n"
	      if $opt_d & $DEBUG_LOCK;

	    print stderr "$hostname,$0 got lock on '$name'\n"
	      if $opt_d & $DEBUG_LOCK;
	
	    $my_lock_list{$name} = 1;

	    return($LOCK_OK);
	}

	if (-f $fullname) {
	    $lock_age = time - (stat($fullname))[10];

	    if ($lock_age > $long_timeout) {
		open(FOO, "<$fullname");
		chop($what = <FOO>);
		close(FOO);

		unlink($fullname);
		&make_log_entry("$0 on $hostname broke long lock on $fullname '$what' after $lock_age seconds");
		print stderr "$hostname,$0 broke long lock '$name'\n"
		  if $opt_d & $DEBUG_LOCK;
		next;
	    }
	}

	print stderr "$hostname,$0 couldn't get lock on '$name', sleeping\n"
	  if $opt_d & $DEBUG_LOCK;
	
	sleep(15);
    }

    print stderr "$hostname,$0 lock request on $fullname timed out\n"
      if $opt_d & $DEBUG_LOCK;

    return($LOCK_NOT_OK);
}

sub unlock {
    local($name) = @_;
    local($fullname, $tmpname);

    ($fullname, $tmpname) = &get_lock_names($name);

    print stderr "$hostname,$0 unlock '$name'\n" if $opt_d & $DEBUG_LOCK;

    unlink($fullname) ||
	   print stderr "$hostname,$0 unlock for '$fullname' failed - lock file gone!\n";
    unlink($tmpname) ||
	   print stderr "$hostname,$0 unlock for '$tmpname' failed - lock file gone!\n";

    delete $my_lock_list{$name};
}

#
# Unlock all locked locks.
#
sub unlock_all {
    local($key);

    foreach $key (keys %my_lock_list) {
	&unlock($key);
    }
}
	
#
# Clean up and exit.
#
sub handle_interrupts {
    local($sig) = @_;

    print stderr "caught a SIG$sig, cleaning up locks...\n";
    &unlock_all();
    exit 0;
}

1;
