Great Circle Associates Majordomo-Users
(February 1993)
 

Indexed By Date: [Previous] [Next] Indexed By Thread: [Previous] [Next]

Subject: Big news: found the 14-character Majordomo deletion bug!
From: Brent Chapman <brent @ GreatCircle . COM>
Date: Fri, 26 Feb 93 09:26:16 -0800
To: majordomo-users @ GreatCircle . COM
Cc: kls @ ditka . chicago . com

Karl Swartz (kls@ditka.chicago.com) just figured out the bug that's
causing Majordomo files to get deleted: it was a bug in "shlock".

shlock would create lock files by appending ".lock" to the file name.
If the file name was already 14 characters or longer, the lock file would
end up having the same name as the original file; thus the original file
would get over-written by the lock file, and then deleted when the lock
was deleted.

A fixed version of "shlock.pl" is appended.  It will be part of the next
Majordomo release.

Thanks, Karl!


-Brent
--
Brent Chapman                                   Great Circle Associates
Brent@GreatCircle.COM                           1057 West Dana Street
+1 415 962 0841                                 Mountain View, CA  94041


# PERL implementation of Erik E. Fair's 'shlock' (from the NNTP distribution)
# Ported by Brent Chapman <Brent@GreatCircle.COM>


# $Source: /mycroft/brent/majordomo/RCS/shlock.pl,v $
# $Revision: 1.4 $
# $Date: 1993/02/26 17:24:56 $
# $Author: brent $
# $State: Exp $
#
# $Locker:  $
# 
# $Log: shlock.pl,v $
# Revision 1.4  1993/02/26  17:24:56  brent
# Fixed bug that was causing files to get deleted on systems with 14-char
# filename limits.  $file.lock would end up same as $file if $file was
# already 14 characters or longer.  Changed from $file.lock to L.$file.
# Thanks to Karl Swartz <kls@ditka.chicago.com> for chasing this down.
#
# Revision 1.3  1993/01/03  04:18:18  brent
# Added "lreopen" function.  -Brent
#
# Revision 1.2  1992/12/25  00:48:53  brent
# Moved "lopen" and "lclose" functions from "majordomo.pl" to "shlock.pl".
#
# Revision 1.1  1992/06/24  01:53:45  brent
# Initial revision
#
#

package shlock;

$shlock_debug = 0;

$EPERM = 1;
$ESRCH = 3;
$EEXIST = 17;

sub main'shlock	## Public
{
    local($file) = shift;
    local($tmp);
    local($retcode) = 0;
    local($redo_loop);

    print STDERR "trying lock \"$file\" for pid $$\n" if $shlock_debug;
    if (!($tmp = &xtmpfile($file))) {
	return(undef);
    }

    do {
	$redo_loop = 0;
	if (! link($tmp, $file)) {
	    if ($! == $EEXIST) {
		print STDERR "lock \"$file\" already exists\n" if $shlock_debug;
		$redo_loop = 1;
		if (&cklock($file)) {
		    print STDERR "extant lock is valid\n" if $shlock_debug;
		    $redo_loop = 0;
		} else {
		    print STDERR "lock is invalid; removing\n" if $shlock_debug;
		    if (unlink($file) <= 0) {
			warn("shlock: unlink(\"$file\"): $!");
		    }
		}
	    } else {
		warn("shlock: link(\"$tmp\", \"$file\"): $!");
	    }
	} else {
	    print STDERR "got lock \"$file\"\n" if $shlock_debug;
	    $retcode = 1;
	}
    } while ($redo_loop);

    if (unlink($tmp) <= 0) {
	warn("shlock: unlink(\"$file\"): $!");
    }
    return($retcode);
}

sub p_exists {
    local($pid) = shift;

    print STDERR "process $pid is " if $shlock_debug;
    if ($pid <= 0) {
	print STDERR "invalid\n" if $shlock_debug;
	return(0);
    }
    if (kill(0, $pid) <= 0) {
	if ($! == $ESRCH)
	    { print STDERR "dead\n" if $shlock_debug; return 0; }
	elsif ($! == $EPERM)
	    { print STDERR "alive\n" if $shlock_debug; return 1; }
	else
	    { print STDERR "state unknown: $!\n" if $shlock_debug; return 1; }
    }
    print "alive\n" if $shlock_debug;
    return 1;
}

sub cklock {
    local($file) = shift;
    local(*FILE, $len, $pid, $buf);

    print STDERR "checking extant lock \"$file\"\n" if $shlock_debug;
    if (!open(FILE, "$file")) {
	warn("shlock: open(\"$file\"): $!");
	return 1;
    }

    $buf = <FILE>;

    if (int($buf) <= 0) {
	close(FILE);
	print STDERR "lock file format error\n" if $shlock_debug;
	return 0;
    }
    close(FILE);
    return(&p_exists(int($buf)));
}

sub xtmpfile {
    local($file) = shift;
    local(*FILE);
    local($tempname);
    local($redo_loop);

    $tempname = $file;
    if ($tempname =~ /\//) {
	$tempname =~ s,/[^\/]*$,/,;
	$tempname .= "shlock$$";
    } else {
	$tempname = "shlock$$";
    }
    print STDERR "temporary filename \"$tempname\"\n" if $shlock_debug;

    do {
	$redo_loop = 0;
	if (!open(FILE, ">$tempname")) {
	    if ($! == $EEXIST) {
		print STDERR "file \"$tempname\" exists\n" if $shlock_debug;
		if (unlink($tempname) <= 0) {
		    warn("shlock: unlink(\"$tempname\"): $!");
		    return(undef);
		}
		$redo_loop = 1;
	    } else {
		warn("shlock: open(\">$tempname\"): $!");
		return(undef);
	    }
	}
    } while ($redo_loop);

    if (! print FILE "$$\n") {
	warn("shlock: write(\"$tempfile\", \"$$\"): $!");
	close(FILE);
	unlink($tempname) || warn("shlock: unlink(\"$tempname\"): $!");
	return(undef);
    }

    close(FILE);
    return($tempname);
}

# open a file locked for exclusive access; we remember the name of the lock
# file, so that we can delete it when we close the file

sub main'lopen {
    local($FH) = shift;
    local($mode) = shift;
    local($file) = shift;
    # $fm is what will actually get passed to open()
    local($fm) = "$mode$file";
    local($status);
    local($tries);

    # create name for lock file
    local($lockfile) = $file;
    $lockfile =~ s,([^/]*)$,L.$1,;

    # force unqualified filehandles into callers' package
    local($package) = caller;
    $FH =~ s/^[^']+$/$package'$&/;

    for ($tries = 0 ; $tries < 600 ; $tries++) {
	# Try to obtain the lock 600 times, waiting 1 second after each try
	if (&main'shlock("$lockfile")) {
	    # Got the lock; now try to open the file
	    $status = open($FH, $fm);
	    if (defined($status)) {
		# File successfully opened; remember the lock file for deletion
		$lock_files[fileno($FH)] = "$lockfile";
	    } else {
		# File wasn't successfully opened; delete the lock
		unlink("$lockfile");
	    }
	    # return the success or failure of the open
	    return($status);
	} else {
	    # didn't get the lock; wait 1 second and try again.
	    sleep(1);
	}
    }
    # If we get this far, we ran out of tries on the lock.
    return undef;
}

# reopen a file already opened and locked (probably to change read/write mode).
# We remember the name of the lock file, so that we can delete it when
# we close the file

sub main'lreopen {
    local($FH) = shift;
    local($mode) = shift;
    local($file) = shift;
    # $fm is what will actually get passed to open()
    local($fm) = "$mode$file";

    # create name for lock file
    local($lockfile) = $file;
    $lockfile =~ s,([^/]*)$,L.$1,;

    # force unqualified filehandles into callers' package
    local($package) = caller;
    $FH =~ s/^[^']+$/$package'$&/;

    # close the old file handle, and delete the lock reference
    if ($lock_files[fileno($FH)]) {
	undef($lock_files[fileno($FH)]);
	close($FH);
    } else {
	# the file wasn't already locked
	unlink("$lockfile");
	return(undef);
    }

    # We've already got the lock; now try to open the file
    $status = open($FH, $fm);
    if (defined($status)) {
	# File successfully opened; remember the lock file for deletion
	$lock_files[fileno($FH)] = "$lockfile";
    } else {
	# File wasn't successfully opened; delete the lock
	unlink("$lockfile");
    }
    # return the success or failure of the open
    return($status);
}


# Close a locked file, deleting the corresponding .lock file.
sub main'lclose {
    local($FH) = shift;

    # force unqualified filehandles into callers' package
    local($package) = caller;
    $FH =~ s/^[^']+$/$package'$&/;

    unlink($lock_files[fileno($FH)]);
    close($FH);
}

1;



Follow-Ups:
Indexed By Date Previous: Re: Where is "digest"?
From: Brent Chapman <brent@GreatCircle.COM>
Next: Re: Big news: found the 14-character Majordomo deletion bug!
From: randy@psg.com (Randy Bush)
Indexed By Thread Previous: Re: Where is "digest"?
From: Brent Chapman <brent@GreatCircle.COM>
Next: Re: Big news: found the 14-character Majordomo deletion bug!
From: randy@psg.com (Randy Bush)

Google
 
Search Internet Search www.greatcircle.com