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