/ pidfile
pidfile
1 #!/bin/bash 2 3 #pidfile.getpid PARA pidfile_getpid; getpid; return the locking pid 4 #pidfile.getpid 5 #pidfile.getpid pidfile_getpid <pidfile> 6 #pidfile.getpid 7 #pidfile.getpid returns the pid of the current locker of the given pidfile. 8 #pidfile.getpid 9 function pidfile_getpid() 10 { 11 head -1 "$1" 2>/dev/null 12 } 13 14 15 #pidfile.sigwait PARA pidfile_sigwait; sigwait; wait a given time for a signal 16 #pidfile.sigwait 17 #pidfile.sigwait pidfile_sigwait [timeout [signal]} 18 #pidfile.sigwait 19 #pidfile.sigwait Waits 'timeout' (default: 1) seconds for 'signal' (default: SIGALRM) 20 #pidfile.sigwait 21 function pidfile_sigwait() 22 { 23 local save_traps=$(trap) 24 sleep "${1:-1}" & 25 trap "kill $! >&/dev/null" ${2:-ALRM} 26 wait $! || true 27 eval "$save_traps" 28 } 29 30 31 #pidfile.trylock PARA pidfile_trylock; trylock; try to get a lockfile 32 #pidfile.trylock 33 #pidfile.trylock pidfile_trylock <pidfile> [timeout [tries]] 34 #pidfile.trylock 35 #pidfile.trylock appends the process pid to a given file and then checks 36 #pidfile.trylock if the first line contains the process pid, if yes, we got the lock, if not 37 #pidfile.trylock it fails. Optional timeout and tries parameters define the waiting behaviour 38 #pidfile.trylock on the pidfile. Locks are not recursive! Locks are tried to be served in fifo 39 #pidfile.trylock order but this is not guranteed in all cases. s. queuelock below. 40 #pidfile.trylock The default for 'timeout' (0) and 'tries' (1) constitute just one try else fail to 41 #pidfile.trylock lock. 42 #pidfile.trylock 43 function pidfile_trylock 44 { 45 local timeout="${2:-0}" 46 local tries="${3:-1}" 47 48 local save_traps=$(trap) 49 trap "" ALRM 50 51 for (( ; tries>0; tries-- )); do 52 echo "$$" >>"$1" 53 local pid="$(pidfile_getpid "$1")" 54 if [[ "$pid" -eq "$$" ]]; then 55 eval "$save_traps" 56 return 0 57 else 58 if ! kill -0 "$pid" >&/dev/null; then 59 echo "$$" >"$1" 60 if [[ "$(pidfile_getpid "$1")" -eq "$$" ]]; then 61 eval "$save_traps" 62 return 0 63 fi 64 fi 65 fi 66 pidfile_sigwait "$timeout" 67 done 68 eval "$save_traps" 69 return 1 70 } 71 72 73 #pidfile.queuelock PARA pidfile_queuelock; queuelock; queues a lock 74 #pidfile.queuelock 75 #pidfile.queuelock pidfile_queuelock <pidfile> [timeout [tries]] 76 #pidfile.queuelock 77 #pidfile.queuelock Like 'pidfile_trylock' but locks are queud in order which is guranteed. 78 #pidfile.queuelock The defaults for 'timeout' (10) and 'tries' (2000000000) are effectively 79 #pidfile.queuelock ethernal (1361 years). Queued locks must be released with pidfile_unqueue. 80 #pidfile.queuelock 81 function pidfile_queuelock 82 { 83 local timeout="${2:-10}" 84 local tries="${3:-2000000000}" 85 local pid 86 87 pidfile_trylock "$1,lock" 1 2000000000 88 89 local save_traps=$(trap) 90 trap "" ALRM 91 92 if ! grep "^$$\$" "$1" >&/dev/null; then 93 echo "$$" >>"$1" 94 fi 95 pidfile_unlock "$1,lock" 96 97 for (( ; tries>0; tries-- )); do 98 99 pidfile_trylock "$1,lock" 1 2000000000 100 pid="$(pidfile_getpid "$1")" 101 pidfile_unlock "$1,lock" 102 103 if [[ "$pid" -eq "$$" ]]; then 104 eval "$save_traps" 105 return 0 106 else 107 if ! kill -0 "$pid" >&/dev/null; then 108 109 pidfile_trylock "$1,lock" 1 2000000000 110 echo "$$" >"$1" 111 pid="$(pidfile_getpid "$1")" 112 pidfile_unlock "$1,lock" 113 114 if [[ "$pid" -eq "$$" ]]; then 115 eval "$save_traps" 116 return 0 117 fi 118 fi 119 fi 120 pidfile_sigwait "$timeout" 121 done 122 eval "$save_traps" 123 return 1 124 } 125 126 127 #pidfile.unqueuelock PARA pidfile_unqueue; unqueue; removes process from queue 128 #pidfile.unqueuelock 129 #pidfile.unqueuelock pidfile_queuelock <pidfile> 130 #pidfile.unqueuelock 131 #pidfile.unqueuelock Removes the current process from the lockqueue, must be used 132 #pidfile.unqueuelock after a 'pidfile_queuelock' to release the lock even if the queuelock 133 #pidfile.unqueuelock attempt was unsuccessful. 134 #pidfile.unqueuelock 135 function pidfile_unqueue 136 { 137 pidfile_trylock "$1,lock" 1 2000000000 138 139 local pid="$(pidfile_getpid "$1")" 140 141 grep -v "^$$\$" "$1" >"$1,tmp" 142 mv "$1,tmp" "$1" 143 144 if [[ "$pid" -eq "$$" ]]; then 145 local wakeup="$(pidfile_getpid "$1")" 146 if [[ "$wakeup" ]]; then 147 pidfile_unlock "$1,lock" 148 kill -ALRM "$wakeup" 149 else 150 rm "$1" 151 pidfile_unlock "$1,lock" 152 fi 153 else 154 pidfile_unlock "$1,lock" 155 fi 156 } 157 158 #pidfile.havelock PARA pidfile_havelock; havelock; check for lockfile ownership 159 #pidfile.havelock 160 #pidfile.havelock pidfile_havelock <pidfile> 161 #pidfile.havelock 162 #pidfile.havelock checks if the caller holds the lock, to be used to implement 163 #pidfile.havelock client side recursive locking. 164 #pidfile.havelock 165 function pidfile_havelock 166 { 167 local pid="$(pidfile_getpid "$1")" 168 if [[ $? -eq 0 && "$pid" -eq "$$" ]]; then 169 return 0 170 else 171 return 1 172 fi 173 } 174 175 176 #pidfile.unlock PARA pidfile_unlock; unlock; release a lockfile 177 #pidfile.unlock 178 #pidfile.unlock pidfile_unlock <pidfile> 179 #pidfile.unlock 180 #pidfile.unlock checks that we have the lock on the given pidfile and then deletes it. 181 #pidfile.unlock It is safe to attempt to unlock a pidfile when we don't own the lock. 182 #pidfile.unlock 183 function pidfile_unlock 184 { 185 local pid="$(pidfile_getpid "$1")" 186 if [[ "$pid" -eq "$$" ]]; then 187 grep -v "^$$\$" "$1" >"$1,tmp" 188 local wakeup=$(head -1 "$1,tmp") 189 if [[ "$wakeup" ]]; then 190 mv "$1,tmp" "$1" 191 kill -ALRM "$wakeup" 192 else 193 rm "$1,tmp" "$1" 194 fi 195 fi 196 } 197 198 199 #pidfile.kill PARA pidfile_kill; kill; sends a signal to the lock holder 200 #pidfile.kill 201 #pidfile.kill pidfile_kill <pidfile> [signal] 202 #pidfile.kill 203 #pidfile.kill Sends the given signal or 'SIGTERM' by default to the pid 204 #pidfile.kill holding the given pidfile. Does not kill itself. 205 #pidfile.kill 206 function pidfile_kill 207 { 208 local pid="$(pidfile_getpid "$1")" 209 if [[ "$pid" != "$$" ]]; then 210 kill -${2:-SIGTERM} "$pid" >&/dev/null 211 fi 212 } 213 214 215 #pidfile.takeover PARA pidfile_takeover; takeover; kills existing process, takes over the lock 216 #pidfile.takeover 217 #pidfile.takeover pidfile_takeover <pidfile> [signal] [timeout] [tries] 218 #pidfile.takeover 219 #pidfile.takeover Sends the given signal or 'SIGTERM' by default to the pid 220 #pidfile.takeover holding the given pidfile and locks the pidfile by the caller. 221 #pidfile.takeover 222 function pidfile_takeover 223 { 224 local timeout="${3:-1}" 225 local tries="${4:-3}" 226 227 for (( ; tries>0; tries-- )); do 228 while ! pidfile_trylock "$1" "$timeout"; do 229 pidfile_kill "$1" "${2:-SIGTERM}" 230 done 231 if pidfile_havelock "$1"; then 232 return 0 233 fi 234 done 235 236 return 1 237 } 238