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 rightfile
45    while true; do
46        eval "exec $_lockfd<>$_lockfile"
47        flock -x $_lockfd || return $?
48        # We can't just stat /dev/stdin or /proc/self/fd/$_lockfd or
49        # use bash's test -ef because those all go through what is
50        # actually a synthetic symlink in /proc and we aren't
51        # guaranteed that our stat(2) won't lose the race with an
52        # rm(1) between reading the synthetic link and traversing the
53        # file system to find the inum.  Perl is very fast so use that.
54        rightfile=$( perl -e '
55            open STDIN, "<&'$_lockfd'" or die $!;
56            my $fd_inum = (stat STDIN)[1]; die $! unless defined $fd_inum;
57            my $file_inum = (stat $ARGV[0])[1];
58            print "y\n" if $fd_inum eq $file_inum;
59                             ' "$_lockfile" )
60        if [ x$rightfile = xy ]; then break; fi
61	# Some versions of bash appear to be buggy if the same
62	# $_lockfile is opened repeatedly. Close the current fd here.
63        eval "exec $_lockfd<&-"
64    done
65}
66
67
68release_lock()
69{
70    _setlockfd $1
71    rm "$_lockfile"
72}
73
74# Protocol and correctness proof:
75#
76# * The lock is owned not by a process but by an open-file (informally
77#   an fd).  Any process with an fd onto this open-file is a
78#   lockholder and may perform the various operations; such a process
79#   should only do so when its co-lockholder processes expect.  Ie, we
80#   will treat all processes holding fds onto the open-file as acting
81#   in concert and not distinguish between them.
82#
83# * You are a lockholder if
84#     - You have an fd onto an open-file which
85#       currently holds an exclusive flock lock on its inum
86#     - and that inum is currently linked at the lockfile path
87#
88# * The rules are:
89#     - No-one but a lockholder may unlink the lockfile path
90#       (or otherwise cause it to stop referring to a file it
91#       refers to).
92#     - Anyone may open the lockfile with O_CREAT
93#
94# * The protocol for locking is:
95#     - Open the file (O_CREAT)
96#     - flock it
97#     - fstat the fd you have open
98#     - stat the lockfile path
99#     - if both are equal you have the lock, otherwise try again.
100#
101# * Informal proof of exclusivity:
102#     - No two open-files can hold an fcntl lock onto the same file
103#       at the same time
104#     - No two files can have the same name at the same time
105#
106# * Informal proof of correctness of locking protocol:
107#     - After you call flock successfully no-one other than you
108#       (someone with the same open-file) can stop you having
109#       that flock lock.
110#     - Obviously the inum you get from the fstat is fixed
111#     - At the point where you call stat there are two
112#       possibilities:
113#         (i) the lockfile path referred to some other inum
114#             in which case you have failed
115#         (ii) the lockfile path referred to the same file
116#             in which case at that point you were the
117#             lockholder (by definition).
118#
119# * Informal proof that no-one else can steal the lock:
120#     - After you call flock successfully no-one other than you
121#       can stop you having that flock lock
122#     - No-one other than the lockholder is permitted to stop
123#       the path referring to a particular inum.  So if you
124#       hold the lock then only you are allowed to stop the
125#       path referring to the file whose flock you hold; so
126#       it will continue to refer to that file.
127#   That's both the conditions for being the lockholder.
128#
129#   Thus once you hold the lock at any instant, you will
130#   continue to do so until you voluntarily stop doing so
131#   (eg by unlinking the lockfile or closing the fd).
132