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=$XEN_LOCK_DIR/xen-hotplug
23
24_setlockfd()
25{
26    _lockfd=9
27    _lockfile="$LOCK_BASEDIR/$1"
28}
29
30
31claim_lock()
32{
33    mkdir -p "$LOCK_BASEDIR"
34    _setlockfd $1
35    # The locking strategy is identical to that from with-lock-ex(1)
36    # from chiark-utils, except using flock.  It has the benefit of
37    # it being possible to safely remove the lockfile when done.
38    # See below for a correctness proof.
39    local stat
40    while true; do
41        eval "exec $_lockfd<> $_lockfile"
42	# we can't flock $_lockfd here, as the shell closes it on exec.
43	# Workaround by redirecting to 0 for the command, and flock 0 instead.
44        flock -v -x 0  0<& $_lockfd|| exit 1
45        local file_stat
46        local fd_stat
47        if fd_stat=$(stat -f '%d.%i' 0<&$_lockfd 2>/dev/null) && file_stat=$(stat -f '%d.%i' $_lockfile 2>/dev/null )
48        then
49            if [ "$fd_stat" = "$file_stat" ] ; then break; fi
50        fi
51        # Some versions of bash appear to be buggy if the same
52        # $_lockfile is opened repeatedly. Close the current fd here.
53        eval "exec $_lockfd<&-"
54    done
55}
56
57
58release_lock()
59{
60    _setlockfd $1
61    rm "$_lockfile"
62}
63
64# Protocol and correctness proof:
65#
66# * The lock is owned not by a process but by an open-file (informally
67#   an fd).  Any process with an fd onto this open-file is a
68#   lockholder and may perform the various operations; such a process
69#   should only do so when its co-lockholder processes expect.  Ie, we
70#   will treat all processes holding fds onto the open-file as acting
71#   in concert and not distinguish between them.
72#
73# * You are a lockholder if
74#     - You have an fd onto an open-file which
75#       currently holds an exclusive flock lock on its inum
76#     - and that inum is currently linked at the lockfile path
77#
78# * The rules are:
79#     - No-one but a lockholder may unlink the lockfile path
80#       (or otherwise cause it to stop referring to a file it
81#       refers to).
82#     - Anyone may open the lockfile with O_CREAT
83#
84# * The protocol for locking is:
85#     - Open the file (O_CREAT)
86#     - flock it
87#     - fstat the fd you have open
88#     - stat the lockfile path
89#     - if both are equal you have the lock, otherwise try again.
90#
91# * Informal proof of exclusivity:
92#     - No two open-files can hold an fcntl lock onto the same file
93#       at the same time
94#     - No two files can have the same name at the same time
95#
96# * Informal proof of correctness of locking protocol:
97#     - After you call flock successfully no-one other than you
98#       (someone with the same open-file) can stop you having
99#       that flock lock.
100#     - Obviously the inum you get from the fstat is fixed
101#     - At the point where you call stat there are two
102#       possibilities:
103#         (i) the lockfile path referred to some other inum
104#             in which case you have failed
105#         (ii) the lockfile path referred to the same file
106#             in which case at that point you were the
107#             lockholder (by definition).
108#
109# * Informal proof that no-one else can steal the lock:
110#     - After you call flock successfully no-one other than you
111#       can stop you having that flock lock
112#     - No-one other than the lockholder is permitted to stop
113#       the path referring to a particular inum.  So if you
114#       hold the lock then only you are allowed to stop the
115#       path referring to the file whose flock you hold; so
116#       it will continue to refer to that file.
117#   That's both the conditions for being the lockholder.
118#
119#   Thus once you hold the lock at any instant, you will
120#   continue to do so until you voluntarily stop doing so
121#   (eg by unlinking the lockfile or closing the fd).
122