#!/usr/bin/perl -w
# $Header: /global/src/usr.bin/src/RCS/lockfile.pl,v 1.2 1998/07/24 20:12:35 pb Exp $
# Based on ~pb22/pl/fcntl
# Use "pod2text" for info

=head1 NAME

cl-lockfile - take a lock on a file and run a command

=head1 SYNOPSIS

B<cl-lockfile> [-v] [-h] [-w] [-n]  [--fcntl|--flock|--dot] [--file $file] [$file] $command $args ...

=head1 OPTIONS

=over 4

=item B<-v|--verbose>

show what it's doing

=item B<-h|--help>

generate help info.

=item B<--fcntl>

use fcntl locking (default)

=item B<--flock>

use flock locking (not considered reliable over NFS)

=item B<--dot>

use dot file locking

=item B<--wait>

wait for the lock

=item B<--file> I<filename>

the file to lock

=item B<-n>

do not actually run the command

=back

=head1 DESCRIPTION

Take a lock on I<$filename> and run I<$command>

=cut

use Getopt::Long;
use Getopt::Long qw(:config require_order);
use Pod::Usage;
use Fcntl;

# ($name = $0) =~ s=.*/==;

%opts = (
	"verbose|v"	=> \$verbose,
	"flock"		=> \$O_flock,
	"fcntl"		=> \$O_fcntl,
	"dot"		=> \$O_dot,
	"wait|w"	=> \$waitforlock,
	"file:s"	=> \$file,
        "help|h"	=> \$help,
	"n"		=> \$n,		# do not actually do anything
);

GetOptions (%opts) || pod2usage();
pod2usage(-verbose => 2) if $help;

if ($O_flock) {
	die("$0: only set one lock type\n") if $O_fcntl || $O_dot;
} elsif ($O_dot) {
	die("$0: only set one lock type\n") if $O_fcntl;
} else { $O_fcntl = 1; }

# Lock a file (using fcntl or flock) and run a command

$LOCK_EX = 2;
$LOCK_NB = 4;

#	elsif ($ARGV[0] eq "-1")	{ shift; $file = "$pwdir/lock1" }
#	elsif ($ARGV[0] eq "-2")	{ shift; $file = "$pwdir/lock2" }

$file = shift unless defined($file);

$LOCKEDFILE = $ENV{'LOCKEDFILE'};
unless (defined($LOCKEDFILE) && $LOCKEDFILE eq $file) {
	if ($O_dot) {
		$dotlock = "$file.lock";
		$tmpfile = "$file.lock.$$";
		open(FILE, ">>$tmpfile") || die "Can't open $tmpfile: $!\n";
		close(FILE);
		if (link $tmpfile,  $dotlock) { unlink $tmpfile; }
		else {	unlink $tmpfile;
			print STDERR "Failed to link to $dotlock: $!\n" if $verbose;
			exit 1;
		}
	} else {
	    open(FILE, ">>$file") || die "Can't open $file: $!\n";

	    $lockstruct = pack("sslli", F_WRLCK, 0, 0, 0, 0) if $O_fcntl;
	    unless (($O_fcntl) ?
		fcntl(FILE, ($waitforlock) ? F_SETLKW : F_SETLK, $lockstruct) :
		flock(FILE, $LOCK_EX |($waitforlock?0:$LOCK_NB))) {
        	print STDERR "file $file already locked ($!) -- abandoning\n" if $verbose;
        	exit 1;
	    }
	}
}

$ENV{'LOCKEDFILE'} = $file;
print STDERR "Locked $file, so running \"@ARGV\" ...\n" if $verbose;
$rc = system(@ARGV) unless $n;
print STDERR "@ARGV gave $rc\n" if $rc && $verbose;
unlink($dotlock) if $O_dot;
exit $rc / 256;
