#!/bin/sh -u # /usr/local/bin sh-lock-utils -ci "-vD" -s "sh-lock-file sh-unlock-file" -- \ # sh-lock-utils -- provides functions for locking files slu_version(){ cat < This is free software; see the GNU General Public License version 2 or later for copying conditions. There is NO warranty. VRS false; slu_chk; } SLU_VERSION='$Revision: 1.43 $'; # AVAILABLE at: http://www.dma.org/~rhawes/programs/sh-lock-file #sh-lock-utils -- source file for shell scripts to use functions # The two other commands are symbolic links to sh-lock-utils slu_help() { cat <&2; slu_err_get; } slu_error_usage() { slu_error $SLU_E_USAGE "$PGM: USAGE: $*, use -H for help"; } slu_lock_all() { local lock lockfile locklist= lock_next next recurse=0 ret=0; while [ $# -gt 0 ]&& { lock="$1"; shift;}; do if [ "$lock" ]; then slu_lock_it "$lock" 0|| slu_chk; if slu_err_get; then locklist="$locklist $lock"; else [ "$locklist" ]&& echo "$locklist"| slu_unlock_file $vb -p $pid -; break; fi fi done slu_err_get } slu_lock_dir() { local dir lock; while [ $# -gt 0 ]&& { lock="$1"; shift;}; do if [ "$lock" ]&& dir=$(dirname "$lock"); then if [ -d "$dir" ]; then [ -r "$dir" ]|| slu_abort "$dir is unreadable"; [ -w "$dir" ]|| slu_abort "$dir is unwriteable"; else slu_abort "$dir is not a directory"; fi fi done slu_err_get; } slu_lock_file() { # make symbolic link to it's own process id in /proc local cum=0 errno=0 inc=1 link lock option pid= PGM=sh-lock-file ret=0\ retry=0 retry_max=-1 timer=8 vb= OPTARG OPTIND=1; while getopts ":HVXi:p:r:t:v" option;do case "$option" in H) slu_help_lock;; V) slu_version $SLU_VERSION;; X) set -x;; i) slu_chk_min inc 0 $OPTARG;; p) slu_chk_min pid 1 $OPTARG;; r) slu_chk_min retry_max -1 $OPTARG;; t) slu_chk_min timer 1 $OPTARG;; v) vb=-v;; \?) slu_error_usage "Opton: '$OPTARG' is invalid";; :) slu_error_usage "Opton: '$OPTARG' is missing an argument";; *) ;; esac|| break; done shift $(($OPTIND-1)); if [ $# -gt 0 ]&& [ "$1" = "-" ]; then shift; while read -r lock; do [ "$lock" ]&& set -- "$@" "$lock"; done fi ! slu_err_get|| { [ $(echo "$@" | wc -w) -gt 0 ]|| slu_error_usage "no lockfiles specified"; [ $(echo "$@" | wc -l) -eq 1 ]|| slu_error_usage "lockfile name has a new line character"; slu_lock_dir "$@"; link=/proc/$pid/status; [ -f "$link" ]|| slu_abort "$link does not exist"; } ! slu_err_get|| until slu_lock_ok_all "$@"&& slu_lock_all "$@" do retry=$(($retry + 1)); [ $retry_max -gt 0 ]&& { [ $retry -le $retry_max ]|| slu_abort "retry lock exceeded count"; } slu_err_get|| break; [ -z "$vb" ]|| { cum=$(($cum + $timer)); echo "retry $retry sleep $timer cum $cum pid $pid"; } sleep $timer|| slu_chk|| break; timer=$(($timer + $inc)); done slu_err_get; } slu_lock_fix() { # remove an invalid lock local lockfile lock_next next recurse=$1; slu_lock_init; # make certain only a single process tries this # this prevents possible accidental removal of valid locks if slu_lock_it "$lock" $next; then if slu_lock_invalid $recurse; then rm $vb -f "$lockfile"|| slu_abort "cannot remove invalid lockfile: $lockfile"; fi rm $vb -f "$lock_next" fi slu_err_get; } slu_lock_init() { next=$(($recurse + 1)); lockfile="${lock}.$recurse"; lock_next="${lock}.$next"; } slu_lock_invalid() { # true if invalid local lockfile recurse=$1 w; lockfile="${lock}.$recurse"; if [ -L "$lockfile" ]; then [ ! -f "$lockfile" ]|| ! w=$(slu_which_locked "$lockfile")|| [ -z "$w" ]|| [ "$w" -eq $pid ]|| [ "$( ps --no-headers -o state $w )" = Z ]; else [ -e "$lockfile" ]; fi } slu_lock_it() { # level# lockname local lock="$1" lockfile lock_next next recurse=$2; slu_lock_init; slu_lock_ok&& ln -s $vb $link "$lockfile" 2>&3|| false; # or another process beat us } slu_lock_ok() { # true if ok to lock level recurse local ret=1; if slu_lock_invalid $next; then slu_lock_fix $next; fi if [ ! -L "$lock_next" ]; then if slu_lock_invalid $recurse; then slu_lock_fix $recurse; fi [ -L "$lockfile" ]|| ret=0; fi slu_err_get|| ret=$?; return $ret; } slu_lock_ok_all() { local lock lockfile lock_next next recurse=0 ret=0; while [ $# -gt 0 ]&& { lock="$1"; shift;}; do if [ "$lock" ]; then slu_lock_init; slu_lock_ok; ret=$?; [ $ret -eq 0 ]|| break; fi done return $ret; } slu_pid_locked() { [ -L "$2" ]&& ls -l "$2" 2>&3| grep -q " /proc/$1/status$";} slu_unlock_file() { # safely remove lock files in signal handlers set by trap local errno=0 pid=0 lock option PGM=sh-unlock-file vb= OPTARG OPTIND=1; while getopts ":HVXp:v" option;do case "$option" in H) slu_help_unlock;; V) slu_version $SLU_VERSION;; X) set -x;; p) slu_chk_min pid 1 $OPTARG;; v) vb=-v;; \?) slu_error_usage "Opton: '$OPTARG' is invalid";; :) slu_error_usage "Opton: '$OPTARG' is missing an argument";; *) ;; esac|| break; done shift $(($OPTIND-1)); if [ $# -gt 0 ]&& [ "$1" = "-" ]; then shift; while read -r lock; do [ "$lock" ]&& set -- "$@" "$lock"; done fi # search for all recursive locks ! slu_err_get|| slu_unlock_it "$@" slu_err_get; } slu_unlock_it() { local file lock; while [ $# -gt 0 ]&& { lock="$1"; shift;}; do [ "$lock" ]&& ls "${lock}."[0-9]* 2>&3| grep "^.*\.[0-9]\+$"| { while read -r file; do [ "$file" ]&& slu_pid_locked $pid "$file"&& rm -f $vb "$file"; done } done slu_err_get; } slu_which_locked() { # get pid in symbolic link [ -L "$1" ]&& ls -l "$1" 2>&3| sed -n 's%.* /proc/\([0-9]\+\)/status$%\1%p' } #efficiency exec 3<>/dev/null #check PATH hash cat dirname echo grep ln ls ps rm sed sleep wc&& case "$0" in */sh-lock-file) slu_lock_file "$@";; */sh-unlock-file) slu_unlock_file "$@";; *) ;; esac