-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbash_lock.sh
More file actions
executable file
·234 lines (208 loc) · 6.29 KB
/
bash_lock.sh
File metadata and controls
executable file
·234 lines (208 loc) · 6.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#!/usr/bin/env bash
# (c) 2018, Ivan Aragones Muniesa <iaragone@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# Usage: bash_lock.sh <-m 'mutex_name'> [-s] [-r] [-p] [-n <max_queue_length>] [-ml <max_lock_time>] [-mw <max_wait_time>]
#
# Example: bash_lock.sh -m 'get_static_mutex' -s
# Example: bash_lock.sh -m 'get_static_mutex' -s -mw 30
# Example: bash_lock.sh -m 'program_end_of_static_mutex' -p -ml 30 &
# Example: bash_lock.sh -m 'release_static_mutex' -r
#
# NOTE: '-s', '-p' and '-r' options are used to set and release the mutexes manually for long running or multi-command mutex requirements.
# In that cases, the calling process is the responsible of the correct muttex release as <max_lock_time> is completely ignored.
#
# NOTE: '-p' may be usually executed in background (&), as it only programs the lock auto-release (don't execute any command).
#
# Check for the input parameters
if [ $# -eq 0 ]; then
echo ""
echo "Usage: bash_lock.sh <-m 'mutex_name'> [-s] [-r] [-p] [-n <max_queue_length>] [-ml <max_lock_time>] [-mw <max_wait_time>]"
echo ""
echo "Example: bash_lock.sh -m 'get_static_mutex' -s"
echo "Example: bash_lock.sh -m 'get_static_mutex' -s -n 3 -mw 30"
echo "Example: bash_lock.sh -m 'program_end_of_static_mutex' -p -ml 30"
echo "Example: bash_lock.sh -m 'release_static_mutex' -r"
echo ""
echo "NOTE: '-s', '-p' and '-r' options are used to set and release the mutexes manually for long running or multi-command mutex requirements."
echo "In '-s' and '-r' cases, the calling process is the responsible of the correct mutex release as <max_lock_time> is completely ignored."
echo ""
echo "NOTE: '-p' may be usually executed in background (&), as it only programs the lock auto-release as per <max_lock_time> specified."
echo ""
echo "Error codes:"
echo " 0: no errors"
echo " 101: Missing parameters"
echo " 102: Missing required parameter '-m'"
echo " 103: SIGALRM received. The command is killed"
echo " 104: <max_queue_length> queue size reached"
echo " 105: <max_wait_time> time exhausted"
echo ""
echo "Environment variables:"
echo " LOCK_PATH: Indicates where to create the mutexes (default: /var/tmp/)"
echo ""
exit 101
fi
### Variables
# Lock directory
_LOCK_PATH="${LOCK_PATH:-/var/tmp}"
_MUTEX_PREFIX="bash_lock_"
_MUTEX_NAME=""
# Maximum amount of time to maintain the lock (in seconds)
_MAX_LOCK_TIME=0
# Maximum time we'll wait for the lock (in seconds)
_MAX_WAIT_TIME=0
# By default, no limit on number of processes waiting for the mutex.
let _MAX_QUEUE_LEN=0
_ONLY_GET_LOCK=0
_PROGRAM_RELEASE=0
_RUN_RELEASE=0
# Parse arguments
if [ "${1}" == "-m" -a "${2}" == "${2#-}" ]; then
_MUTEX_NAME="${2//[[:space:]]/_}"
shift
shift
else
echo "You must give a name for the mutex (-m)"
exit 102
fi
while [ "${1#-}" != "${1}" ]; do
case "${1}" in
"-ml")
_MAX_LOCK_TIME=${2}
shift
shift
;;
"-mw")
_MAX_WAIT_TIME=${2}
shift
shift
;;
"-n")
_MAX_QUEUE_LEN=${2}
shift
shift
;;
"-s")
_ONLY_GET_LOCK=1
shift
;;
"-p")
_PROGRAM_RELEASE=1
shift
;;
"-r")
_RUN_RELEASE=1
shift
;;
esac
done
_LOCK_DIR="${_LOCK_PATH}/${_MUTEX_PREFIX}${_MUTEX_NAME}.lck"
echo "_ONLY_GET_LOCK = ${_ONLY_GET_LOCK}"
echo "_PROGRAM_RELEASE = ${_PROGRAM_RELEASE}"
echo "_RUN_RELEASE = ${_RUN_RELEASE}"
echo "_MAX_LOCK_TIME = ${_MAX_LOCK_TIME}"
echo "_MAX_WAIT_TIME = ${_MAX_WAIT_TIME}"
echo "_MAX_QUEUE_LEN = ${_MAX_QUEUE_LEN}"
# Sum the time to wait for (get the lock + being locked, added 5 seconds to let the kill )
let _SUM_LOCK_TIME=_MAX_LOCK_TIME+_MAX_WAIT_TIME+5
### Functions
function clean_exit {
# remove us from the queue
rmdir ${_LOCK_DIR}_$$ &>/dev/null
# if we got the lock, release it
if [ -f ${_LOCK_DIR}/info.txt ]; then
if [ ! -z "$(grep $$ ${_LOCK_DIR}/info.txt)" ]; then
unlock
fi
fi
exit ${1:-0}
}
# Function to get into the queue
function queue_in {
# if the queue is full, exit before try getting the lock
if [ ${_MAX_QUEUE_LEN} -ne 0 ]; then
# put ourselves in the mutex queue to write to the queue
${0%/*}/bash_mutex.sh -m 'queue' -n 0 -mw 3 -ml 3 " \
if [ \$(ls -d ${_LOCK_DIR}_* 2>/dev/null | wc -l) -lt ${_MAX_QUEUE_LEN} ]; then \
mkdir ${_LOCK_DIR}_$$ >&/dev/null; \
else \
exit 104; \
fi; \
" &>/dev/null || clean_exit 104
if [ $? -ne 0 ]; then
echo "Max queue length has been reached. No command is executed from $$"
clean_exit 104
fi
fi
}
function queue_out {
rmdir ${_LOCK_DIR}_$$ &>/dev/null
}
# Function to lock
function lock {
let _counter=1
while ! mkdir ${_LOCK_DIR} &>/dev/null; do
if [ ${_counter} -gt ${_MAX_WAIT_TIME} ]; then
echo "Could'nt get the lock: timed out..."
clean_exit 105
break
fi
let _counter+=1
sleep 1
done
# Write some usefull (debug) information to info.txt file
cat > ${_LOCK_DIR}/info.txt <<EOF
$(hostname) - PID: $$
$(date)
_Max_Lock_Time: ${_MAX_LOCK_TIME}
- Variables de entorno:
$(env | grep "BASH_LOCK")
EOF
sync
# # We are not on the queue, we got the lock
# queue_out
}
# Function to unlock
function unlock {
# get out of the queue to let another process to get the slot
rmdir ${_LOCK_DIR}_$$ &>/dev/null
# release the lock
rm -rf ${_LOCK_DIR} &>/dev/null
}
# Configure SIGALRM handler
function unlock_signal {
echo "handling the received signal: ${1}"
unlock ${1}
exit 103
}
# MAIN
trap 'unlock_signal ALRM' ALRM
trap 'clean_exit 2' INT
trap 'clean_exit 15' TERM
trap 'clean_exit 1' HUP
trap 'clean_exit 6' ABRT
if [ ${_RUN_RELEASE} -eq 1 ]; then
# If called with -r we only need to release the lock
rm -rf ${_LOCK_DIR} &> /dev/null
clean_exit 0
elif [ ${_PROGRAM_RELEASE} -eq 1 ]; then
# Write some usefull (debug) information to info.txt file
cat > ${_LOCK_DIR}/info.txt <<EOF
$(hostname) - PID: $$
$(date)
_Max_Lock_Time: ${_MAX_LOCK_TIME}
- Variables de entorno:
$(env | grep "BASH_LOCK")
EOF
sync
sleep ${_SUM_LOCK_TIME}
# Exit assuring the lock has been released
unlock
exit 0
elif [ ${_ONLY_GET_LOCK} ]; then
# If called with -s we try to get the lock
queue_in
lock
fi
# Clean exit
exit 0