1# 2# Copyright (c) 2005 XenSource Ltd. 3# Copyright (c) 2007 Red Hat 4# 5# This library is free software; you can redistribute it and/or 6# modify it under the terms of version 2.1 of the GNU Lesser General Public 7# License as published by the Free Software Foundation. 8# 9# This library is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12# Lesser General Public License for more details. 13# 14# You should have received a copy of the GNU Lesser General Public 15# License along with this library; If not, see <http://www.gnu.org/licenses/>. 16# 17 18# 19# Serialisation 20# 21 22LOCK_BASEDIR=/var/run/xen-hotplug 23 24_setlockfd() 25{ 26 local i 27 for ((i = 0; i < ${#_lockdict}; i++)) 28 do [ -z "${_lockdict[$i]}" -o "${_lockdict[$i]}" = "$1" ] && break 29 done 30 _lockdict[$i]="$1" 31 let _lockfd=200+i 32 _lockfile="$LOCK_BASEDIR/$1" 33} 34 35 36claim_lock() 37{ 38 mkdir -p "$LOCK_BASEDIR" 39 _setlockfd $1 40 # The locking strategy is identical to that from with-lock-ex(1) 41 # from chiark-utils, except using flock. It has the benefit of 42 # it being possible to safely remove the lockfile when done. 43 # See below for a correctness proof. 44 local stat 45 while true; do 46 eval "exec $_lockfd<>$_lockfile" 47 flock -x $_lockfd || return $? 48 # Although /dev/stdin (i.e. /proc/self/fd/0) looks like a symlink, 49 # stat(2) bypasses the synthetic symlink and directly accesses the 50 # underlying open-file. So this works correctly even if the file 51 # has been renamed or unlinked. stat will output two lines like: 52 # WW.XXX 53 # YY.ZZZ 54 # which need to be separated and compared. 55 if stat=$( stat -L -c '%D.%i' /dev/stdin $_lockfile 0<&$_lockfd 2>/dev/null ) 56 then 57 local file_stat 58 local fd_stat 59 60 # match on literal newline 61 fd_stat=${stat% 62*} 63 file_stat=${stat#* 64} 65 if [ "$fd_stat" = "$file_stat" ] ; then break; fi 66 fi 67 # Some versions of bash appear to be buggy if the same 68 # $_lockfile is opened repeatedly. Close the current fd here. 69 eval "exec $_lockfd<&-" 70 done 71} 72 73 74release_lock() 75{ 76 _setlockfd $1 77 rm "$_lockfile" 78} 79 80# Protocol and correctness proof: 81# 82# * The lock is owned not by a process but by an open-file (informally 83# an fd). Any process with an fd onto this open-file is a 84# lockholder and may perform the various operations; such a process 85# should only do so when its co-lockholder processes expect. Ie, we 86# will treat all processes holding fds onto the open-file as acting 87# in concert and not distinguish between them. 88# 89# * You are a lockholder if 90# - You have an fd onto an open-file which 91# currently holds an exclusive flock lock on its inum 92# - and that inum is currently linked at the lockfile path 93# 94# * The rules are: 95# - No-one but a lockholder may unlink the lockfile path 96# (or otherwise cause it to stop referring to a file it 97# refers to). 98# - Anyone may open the lockfile with O_CREAT 99# 100# * The protocol for locking is: 101# - Open the file (O_CREAT) 102# - flock it 103# - fstat the fd you have open 104# - stat the lockfile path 105# - if both are equal you have the lock, otherwise try again. 106# 107# * Informal proof of exclusivity: 108# - No two open-files can hold an fcntl lock onto the same file 109# at the same time 110# - No two files can have the same name at the same time 111# 112# * Informal proof of correctness of locking protocol: 113# - After you call flock successfully no-one other than you 114# (someone with the same open-file) can stop you having 115# that flock lock. 116# - Obviously the inum you get from the fstat is fixed 117# - At the point where you call stat there are two 118# possibilities: 119# (i) the lockfile path referred to some other inum 120# in which case you have failed 121# (ii) the lockfile path referred to the same file 122# in which case at that point you were the 123# lockholder (by definition). 124# 125# * Informal proof that no-one else can steal the lock: 126# - After you call flock successfully no-one other than you 127# can stop you having that flock lock 128# - No-one other than the lockholder is permitted to stop 129# the path referring to a particular inum. So if you 130# hold the lock then only you are allowed to stop the 131# path referring to the file whose flock you hold; so 132# it will continue to refer to that file. 133# That's both the conditions for being the lockholder. 134# 135# Thus once you hold the lock at any instant, you will 136# continue to do so until you voluntarily stop doing so 137# (eg by unlinking the lockfile or closing the fd). 138