Skip to content

Instantly share code, notes, and snippets.

@mjf
Last active December 11, 2023 16:53
Show Gist options
  • Save mjf/2134329 to your computer and use it in GitHub Desktop.
Save mjf/2134329 to your computer and use it in GitHub Desktop.
lockscript - Shell script locking template
#! /bin/sh
# lockscript - Shell script locking template
# Copyright (C) 2012-2014 Matous J. Fialka, <http://mjf.cz/>
# Released under the terms of The MIT License
# USAGE
# =====
#
# Rename, link or source this file to your desired script.
#
# ENVIRONMENT
# ===========
#
# Name Description
# ----------- -----------------------------------------------------
# LOCKTIME Time to stay locked (-1 means to never unlock)
# LOCKFILE Path to lockfile
# PIDFILE Path to file to write process ID (PID) to
# CONFIGFILE List of paths to configuration (separated by collons)
# SCRIPTFILE Path to file to include as main script
# -------------------------------------------------------------------
#
# DESCRIPTION
# ===========
#
# The script first tries to read CONFIGFILE in order, then it writes
# PIDFILE and LOCKFILE. If everything went OK it sources SCRIPTFILE.
# It also does signal trapping so that you do not have to handle it.
# The SCRIPTFILE or main part wouldn't get run again unless LOCKTIME
# expires (to reset lock use LOCKTIME=0 which means to ignore lock).
#
# <<>> BASIC INFORMATION <<>>
#
PROGRAM=`basename "$0"`
if [ "$PROGRAM" = "$__PROGRAM" ]
then
echo "Source loop detected for $__PROGRAM" 1>&2
exit 1
fi
__PROGRAM="$PROGRAM"
#
# <<>> CONFIGFILE <<>>
#
if [ "${#CONFIGFILE}" -eq 0 ]
then
CONFIGFILE="/etc/${PROGRAM}.conf:$HOME/.${PROGRAM}rc":"./.${PROGRAM}rc"
else
CONFIGFILE="/etc/${PROGRAM}.conf:$HOME/.${PROGRAM}rc":"./.${PROGRAM}rc":"$CONFIGFILE"
fi
__OFS="$IFS"
IFS=:
for __CONFIGFILE in $CONFIGFILE
do
if [ -e "$__CONFIGFILE" ]
then
if ! [ -r "$__CONFIGFILE" ]
then
echo "Could not source $__CONFIGFILE" 1>&2
exit 1
fi
source "$__CONFIGFILE"
fi
unset __CONFIGFILE
done
IFS="$__OFS"
#
# <<>> PIDFILE <<>>
#
if [ "${#PIDFILE}" -eq 0 ]
then
if [ "$USER" = 'root' ]
then
PIDFILE="/var/run/$PROGRAM.pid"
else
PIDFILE="/tmp/$PROGRAM.pid"
fi
fi
if [ -e "$PIDFILE" ]
then
echo "Concurent session is running. PID file is $PIDFILE" 1>&2
exit 1
fi
__PIDFILE_DIRNAME=`dirname "$PIDFILE"`
if [ -w "$__PIDFILE_DIRNAME" ]
then
echo "$$" > "$PIDFILE"
else
echo "Path to write PID file $PIDFILE is not writable" 1>&2
exit 1
fi
unset __PIDFILE_DIRNAME
#
# <<>> SIGNALS <<>>
#
signal_handler_general()
{
EXIT=${EXIT:-$?}
if [ -r "$PIDFILE" ]
then
if [ $(< $PIDFILE) -eq $$ ]
then
rm -f "$PIDFILE"
fi
fi
trap - 0 1 2 3 13 15
kill -2 $$
}
signal_handler_exit()
{
EXIT=${EXIT:-$?}
if [ $EXIT -eq 0 ]
then
rm -f "$LOCKFILE"
fi
signal_handler_general
}
trap signal_handler_exit 0
trap signal_handler_general 1 2 3 13 15
#
# <<>> LOCKFILE <<>>
#
if [ "${#LOCKFILE}" -eq 0 ]
then
if [ "$USER" = 'root' ]
then
LOCKFILE="/var/lock/$PROGRAM.lock"
else
LOCKFILE="/tmp/$PROGRAM.lock"
fi
fi
__LOCKFILE_DIRNAME=`dirname "$LOCKFILE"`
if [ ! -w "$__LOCKFILE_DIRNAME" ]
then
echo "Path to write lock file $LOCKFILE is not writable" 1>&2
exit 1
fi
unset __LOCKFILE_DIRNAME
LOCKTIME="${LOCKTIME:--1}"
if [ "$LOCKTIME" -lt -1 ]
then
echo 'Invalid LOCKTIME value (must be in range -1..N)' 1>&2
exit 1
fi
if [ -e "$LOCKFILE" ]
then
if [ "$LOCKTIME" -eq -1 ]
then
echo "Session is permanently locked. Lockfile is $LOCKFILE" 1>&2
exit 1
fi
if ! [ `stat -c %Z "$LOCKFILE"` -lt $((`date +%s` - $LOCKTIME + 1)) ]
then
echo "Session is temporarily locked. Lockfile is $LOCKFILE" 1>&2
exit 1
fi
fi
touch "$LOCKFILE"
#
# <<>> SCRIPTFILE <<>>
#
if [ "${#SCRIPTFILE}" -ne 0 ]
then
if [ -e "$SCRIPTFILE" ]
then
if [ -r "$SCRIPTFILE" ]
then
source "$SCRIPTFILE"
else
echo "Unable to read script file $SCRIPTFILE" 1>&2
exit 1
fi
else
echo "Unable to find script file $SCRIPTFILE" 1>&2
exit 1
fi
fi
#
# <<>> MAIN <<>>
#
@mjf
Copy link
Author

mjf commented Mar 10, 2014

USAGE

Create a demo script somewhere in the $PATH:

$ cat > /usr/local/bin/lockscript-demo
#! /bin/sh

echo -n 'Press Ctrl-C (OK) or Ctr-D (failure) or enter something (OK): '
read
echo "You have entered: '$REPLY'"

Run it via lockscript:

$ SCRIPTFILE=`which lockscript-demo` LOCKTIME=5 lockscript

If the lockscript-demo script specified in the SCRIPTFILE variable
fails then it can not be run again using the lockscript program for
at least 5 seconds specified in then LOCKTIME variable.

In case you would like to enjoy the lockscript configuration files
link it to you desired name, say test.sh:

$ ln -s `which lockscript` /usr/local/bin/test

Now you can place your configuration in CONFIGFILE paths, in order:

  1. /etc/test.conf
  2. $HOME/testrc
  3. ./testrc
  4. $CONFIGFILE

And run lockscript as test like this:

$ SCRIPTFILE=`which lockscript-demo` LOCKTIME=5 test

You can place configuration variables for the lockscript-demo in any of the
configuration files as listed above.

You can also copy the lockscript program with another name and use the last MAIN
part of the script to either place SCRIPTFILE post-run code or you can use
it to implement standalone script as well.

Of course, feel free to source this file at the beginning of any script you like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment