#!/bin/bash

[ "${__DEBUG:-}" = TRUE ] \
	&& { set +x; __DEBUG=FALSE; __SILENT=FALSE; . /opt/fox_utils/crab_sys.sh; }

set -eu
### --help Info: Атомарная блокировка с ожиданием 10
### --help Usage: . __lock $lockfile [$pid|""] [timeout=10]
### --help Example: __lock /tmp/myname.lock $$
[ "${1:---help}" = '--help' ] && { fgrep '##''# --help' $0 ; exit 255; }
LOCK=${1//.lock/}
LOCK=${LOCK}.lock
LOCK_DIR=${LOCK%/*}
LOCK_PID=${2:-$PPID}
[ -z "$LOCK_PID" ] && LOCK_PID=$PPID
TIMEOUT=${3:-10}
# TODO4 read from proc
USER_HZ=100
if [ ! -d "$LOCK_DIR" ]; then
	echo "Error: lock dir $LOCK_DIR not found" >&2
	exit 255
fi
if [ ! -d /proc/$LOCK_PID ]; then
	echo "LOCK_PID not exists /proc/$LOCK_PID" >&2
	exit 255
fi
# дополнительный файл для атомарной работы с основным файлом
FLOCK="${LOCK/.lock/.atomic.tmp}"
touch $FLOCK
exec 9>&-
exec 9>>$FLOCK

# цикл ожидания захвата лока, блокировка хранится в файле
# правки файла только через flock - для атомарности
i=0
while true; do
	if ! flock -w 3 -x 9; then
		# никто не должен на долго захватывать flock
		echo "Cannot flock $FLOCK file" >&2
		rm -f $FLOCK
		exit 255
	fi
	[ ! -f "$LOCK" ] && break
	# файл пустой можно захватывать
	[ ! -s "$LOCK" ] && break
	old_pid=; old_jstart=; old_cmdline=; proc_cmdline=
	read -r old_pid old_jstart old_cmdline < $LOCK || true
	# процесса захватившего lock нет уже, можно захватывать
	[ ! -d /proc/${old_pid:-none}/ ] && break
	# глючит read -r proc_cmdline </proc/$old_pid/cmdline || true
	proc_cmdline=$( cat /proc/$old_pid/cmdline | tr '[:cntrl:]' ' ') || true
	proc_stat_a[21]=0
	read -ra proc_stat_a  </proc/$old_pid/stat || true
	# время старта процесса не совпадает, случайно совпал pid, можно захватывать
	[ "$old_jstart" != "${proc_stat_a[21]}" ] && break
	IFS=. read -r uptime tmp </proc/uptime || true
	old_uptime=$((uptime-${proc_stat_a[21]}/USER_HZ))
	ps aux | grep " $old_pid " >&2
	echo "$LOCK try=$i<$TIMEOUT Already locked pid=$old_pid age=$old_uptime ${proc_cmdline}" >&2
	tmp_pid="$LOCK_PID"
	if [ "$LOCK_PID" = "$old_pid" ]; then
		echo "Warning i am self $old_pid already lock $LOCK" >&2
		exit 0
	fi
	# -n "$tmp_pid" лечит какой то баг
	while [ "$tmp_pid" != 1 -a -n "$tmp_pid" ]; do
		tmp_pid="$(grep PPid /proc/$tmp_pid/status | awk '{ print $2 }' )"
		if [ "$tmp_pid" = "$old_pid" ]; then
			echo "Warning my parent $old_pid already lock $LOCK" >&2
			exit 0
		fi
	done
	i=$((i+1))
	# разблокируемся на время sleep'а чтоб другие могли захватить
	flock -u 9
	[ $i -gt $TIMEOUT ] && { echo "$LOCK Cannot lock, TIMEOUT=$TIMEOUT" >&2; exit 255; }
	sleep 1
done
read -r lock_cmdline </proc/$LOCK_PID/cmdline || true
read -ra lock_stat_a  </proc/$LOCK_PID/stat || true

echo "$LOCK_PID ${lock_stat_a[21]} $lock_cmdline" > $LOCK
flock -u 9

### echo "$0 $@ [$$] SUCCESS" >&2
exit 0
