/ 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