# # Copyright (c) 2005 XenSource Ltd. # Copyright (c) 2007 Red Hat # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Serialisation # LOCK_BASEDIR=/var/run/xen-hotplug _setlockfd() { local i for ((i = 0; i < ${#_lockdict}; i++)) do [ -z "${_lockdict[$i]}" -o "${_lockdict[$i]}" = "$1" ] && break done _lockdict[$i]="$1" let _lockfd=200+i _lockfile="$LOCK_BASEDIR/$1" } claim_lock() { mkdir -p "$LOCK_BASEDIR" _setlockfd $1 # The locking strategy is identical to that from with-lock-ex(1) # from chiark-utils, except using flock. It has the benefit of # it being possible to safely remove the lockfile when done. # See below for a correctness proof. local rightfile while true; do eval "exec $_lockfd<>$_lockfile" flock -x $_lockfd || return $? # We can't just stat /dev/stdin or /proc/self/fd/$_lockfd or # use bash's test -ef because those all go through what is # actually a synthetic symlink in /proc and we aren't # guaranteed that our stat(2) won't lose the race with an # rm(1) between reading the synthetic link and traversing the # file system to find the inum. Perl is very fast so use that. rightfile=$( perl -e ' open STDIN, "<&'$_lockfd'" or die $!; my $fd_inum = (stat STDIN)[1]; die $! unless defined $fd_inum; my $file_inum = (stat $ARGV[0])[1]; print "y\n" if $fd_inum eq $file_inum; ' "$_lockfile" ) if [ x$rightfile = xy ]; then break; fi # Some versions of bash appear to be buggy if the same # $_lockfile is opened repeatedly. Close the current fd here. eval "exec $_lockfd<&-" done } release_lock() { _setlockfd $1 rm "$_lockfile" } # Protocol and correctness proof: # # * The lock is owned not by a process but by an open-file (informally # an fd). Any process with an fd onto this open-file is a # lockholder and may perform the various operations; such a process # should only do so when its co-lockholder processes expect. Ie, we # will treat all processes holding fds onto the open-file as acting # in concert and not distinguish between them. # # * You are a lockholder if # - You have an fd onto an open-file which # currently holds an exclusive flock lock on its inum # - and that inum is currently linked at the lockfile path # # * The rules are: # - No-one but a lockholder may unlink the lockfile path # (or otherwise cause it to stop referring to a file it # refers to). # - Anyone may open the lockfile with O_CREAT # # * The protocol for locking is: # - Open the file (O_CREAT) # - flock it # - fstat the fd you have open # - stat the lockfile path # - if both are equal you have the lock, otherwise try again. # # * Informal proof of exclusivity: # - No two open-files can hold an fcntl lock onto the same file # at the same time # - No two files can have the same name at the same time # # * Informal proof of correctness of locking protocol: # - After you call flock successfully no-one other than you # (someone with the same open-file) can stop you having # that flock lock. # - Obviously the inum you get from the fstat is fixed # - At the point where you call stat there are two # possibilities: # (i) the lockfile path referred to some other inum # in which case you have failed # (ii) the lockfile path referred to the same file # in which case at that point you were the # lockholder (by definition). # # * Informal proof that no-one else can steal the lock: # - After you call flock successfully no-one other than you # can stop you having that flock lock # - No-one other than the lockholder is permitted to stop # the path referring to a particular inum. So if you # hold the lock then only you are allowed to stop the # path referring to the file whose flock you hold; so # it will continue to refer to that file. # That's both the conditions for being the lockholder. # # Thus once you hold the lock at any instant, you will # continue to do so until you voluntarily stop doing so # (eg by unlinking the lockfile or closing the fd).