Advertisement
Guest User

Lock library

a guest
Jun 25th, 2022
23
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 8.13 KB | None | 0 0
  1. #!/usr/bin/env sh
  2.  
  3. # NAME          lock
  4. # PURPOSE       library for provides two types of lock and a related exit routine.
  5. # FROM          Parts adapted from:
  6. #               www.kfirlavi.com/blog/2012/11/06/elegant-locking-of-bash-program
  7. # NEEDS         ** dash **
  8. # USAGE         See separate (documentation) file.
  9.  
  10. readonly lock_logLvl=0
  11. # ^ 0: off. 1: minimal. 2: full. Some errors get logged even if the value is 0.
  12. readonly lock_lockFile_dir='/tmp'
  13. # ^ Setting this to a *subfolder* of tmp stops things working, for some reason.
  14. readonly lock_fd=9
  15. # ^ Highest value of lock_fd supported by *Dash* is 9 (but 200 for Bash?).
  16.  
  17. lock_file_lock_core() {
  18.     lock_isUnset=${lock_lockFile_handle:-}
  19.     if [ -z "${lock_isUnset}" ]; then
  20.         lock_log 'Handle variable is undefined!'
  21.         return 1
  22.     fi
  23.     [ ${lock_logLvl} -gt 1 ] &&
  24.         lock_log "Trying to lock with file <${lock_lockFile}>: creating file .."
  25.     # The stuff below somehow 'uses an open file by its file descriptor number'
  26.     # - according to 'man flock'.
  27.     lock_loop=1
  28.     lock_loopMax=3
  29.     while [ ${lock_loop} -le ${lock_loopMax} ]; do
  30.         /usr/bin/flock -n "${lock_lockFile_handle}" >/dev/null 2>&1 &&
  31.             return 0
  32.         sleep 0.25
  33.         lock_loop=$((lock_loop + 1))
  34.     done
  35.     [ -e "${lock_lockFile}" ] && return 0
  36.     case ${lock_logLvl} in
  37.     0)
  38.         lock_log \
  39.             "Problem: Could not create lock file <${lock_lockFile}>, though file does not exist already."
  40.         ;;
  41.     *)
  42.         lock_log \
  43.             "Problem: Could not create lock file <${lock_lockFile}>; and lock file exists already."
  44.         ;;
  45.     esac
  46.     return 1
  47. }
  48.  
  49. # ${1}
  50. lock_log() {
  51.     case $# in
  52.     1)
  53.         lock_msg="Lock library: ${1}"
  54.         ;;
  55.     *)
  56.         lock_msg="Function <lock_log> needs exactly one argument but $# received"
  57.         ;;
  58.     esac
  59.     logger -t "${lock_progName}" "${lock_msg}"
  60.     return 0
  61. }
  62.  
  63. # ${1}
  64. lock_log_error() {
  65.     case $# in
  66.     1)
  67.         lock_msg="Lock library: ERROR - ${1}"
  68.         ;;
  69.     *)
  70.         lock_msg="META-ERROR: function <lock_log_error> needs exactly one argument but $# received"
  71.         ;;
  72.     esac
  73.     logger -t "${lock_progName}" "${lock_msg}"
  74.     return 0
  75. }
  76.  
  77. # On some stuff in here, see:
  78. # https://old.reddit.com/r/commandline/comments/9lwvu1/a_few_lines_to_convert_from_bash_to_dash/
  79. # https://unix.stackexchange.com/questions/368159/why-flock-doesnt-clean-the-lock-file
  80. lock_file_lock() {
  81.     case ${lock_logLvl} in
  82.     0) ;;
  83.     *)
  84.         [ ${lock_logLvl} -eq 2 ] && lock_log 'function <lock_file_lock> - starting ..'
  85.         lock_log \
  86.             "function <lock_file_lock> - lock_lockFile_dir is <${lock_lockFile_dir}>. lock_userName is <${lock_userName}>. lock_progName is <${lock_progName}>"
  87.         ;;
  88.     esac
  89.     # Create lock file
  90.     lock_lockFile="${lock_lockFile_dir}/${lock_userName}-${lock_progName}.lock"
  91.     lock_lockFile_handle="${lock_fd}"
  92.     if eval "exec ${lock_lockFile_handle}> \"\${lock_lockFile}\""; then
  93.         chmod 777 "${lock_lockFile}"
  94.         lock_file_lock_core ||
  95.             return 1
  96.         # NB: 'lock_file_lock_core || return 1' is vital: it is what tells the calling script that the file is locked.
  97.         [ "${lock_logLvl}" -gt 1 ] &&
  98.             lock_log \
  99.                 "function <lock_file_lock> - finished creating lock file <${lock_lockFile}>"
  100.         return 0
  101.     fi
  102.     lock_log \
  103.         'function <lock_file_lock> - failed to create lock-file'
  104.     return 1
  105. }
  106.  
  107. lock_file_unlock() {
  108.     lock_isUnset=${lock_lockFile_handle:-}
  109.     if [ -z "${lock_isUnset}" ]; then
  110.         lock_log \
  111.             'function <lock_file_unlock> - lock_lockFile_handle unset!'
  112.         return 1
  113.     fi
  114.     # Try to unlock the file.
  115.     if /usr/bin/flock -u "${lock_lockFile_handle}" >/dev/null 2>&1; then
  116.         [ ${lock_logLvl} -gt 0 ] &&
  117.             lock_log \
  118.                 "function <lock_file_unlock> - unlocked <${lock_lockFile}>."
  119.         return 0
  120.     fi
  121.     # Else .. try again.
  122.     sleep 0.3
  123.     if /usr/bin/flock -u "${lock_lockFile_handle}" >/dev/null 2>&1; then
  124.         [ ${lock_logLvl} -gt 0 ] &&
  125.             lock_log \
  126.                 "function <lock_file_unlock> - unlocked <${lock_lockFile}> (on second attempt)"
  127.         return 0
  128.     fi
  129.     lock_log_error 'function <lock_file_unlock> - failed to unlock!'
  130.     return 1
  131. }
  132.  
  133. lock_file_exitCleanly() {
  134.     lock_file_unlock
  135.     exit 0
  136. }
  137.  
  138. lock_file_exitDirtily() {
  139.     lock_file_unlock
  140.     exit 1
  141. }
  142.  
  143. isInteger() {
  144.     case ${1#[+-]} in
  145.     *[!0123456789]*) return 1 ;;
  146.     '') return 1 ;;
  147.     *) return 0 ;;
  148.     esac
  149. }
  150.  
  151. # $1: exit code
  152. lock_file_exit_withCode() {
  153.     case $# in
  154.     1)
  155.         # shellcheck disable=2086
  156.         isInteger "${1}" &&
  157.             exit ${1}
  158.         lock_log_error \
  159.             'function <lock_file_exit_withCode> - non-integer exit-code received'
  160.         exit 1
  161.         ;;
  162.     *)
  163.         lock_log_error \
  164.             "function <lock_file_exit_withCode> needs exactly one argument but $# received"
  165.         lock_file_unlock
  166.         exit 1
  167.         ;;
  168.     esac
  169. }
  170.  
  171. # $1: time period in seconds.
  172. # If the lock file was created fewer than $1 seconds from now, then
  173. #   the returns true (/0).
  174. # If the lock file was NOT created fewer than $1 seconds from now, or does not exist,
  175. #   then the returns false (/1).
  176. lock_time_ageIsLessThan() {
  177.     case $# in
  178.     1) ;;
  179.     *)
  180.         lock_log_error \
  181.             "function <lock_time_ageIsLessThan> needs exactly one argument but $# received"
  182.         return 0
  183.         ;;
  184.     esac
  185.     if ! isInteger "${1}"; then
  186.         lock_log_error \
  187.             'function <lock_time_ageIsLessThan> - non-integer argument. Exiting'
  188.         exit 1
  189.     fi
  190.     lock_timeLock_seconds=${1}
  191.     # If use 'lock_timeLock_seconds', get a warning about bash.
  192.     # DO THE LOCK CHECK
  193.     lock_timeLock_file_fullPath="${lock_lockFile_dir}/${lock_userName}-${lock_progName}.timelock"
  194.     # If the file does not exist, return false (i.e. 1)
  195.     # which, here is the 'proceed' value.
  196.     if ! [ -e "${lock_timeLock_file_fullPath}" ]; then
  197.         lock_log_error \
  198.             "function <lock_time_ageIsLessThan> - file <${lock_timeLock_file_fullPath}> does not exist"
  199.         return 1
  200.     fi
  201.     # File does exist; get its date.
  202.     if ! lock_time_timeStamp=$(stat -c%Y "${lock_timeLock_file_fullPath}"); then
  203.         lock_log_error \
  204.             "function <lock_time_ageIsLessThan> - could not stat file <${lock_timeLock_file_fullPath}>"
  205.         return 1
  206.     fi
  207.     # Now calculate the file's age.
  208.     lock_timeLock_now=$(date '+%s')
  209.     if [ $((lock_timeLock_now - lock_time_timeStamp)) -lt "${lock_timeLock_seconds}" ]; then
  210.         [ "${lock_logLvl}" -gt 1 ] &&
  211.             lock_log \
  212.                 "function <lock_time_ageIsLessThan> - queried <${lock_timeLock_file_fullPath}> and found it fewer than <${lock_timeLock_seconds}> seconds old"
  213.         # Return 0 / true
  214.         return 0
  215.     fi
  216.     # Otherwise
  217.     [ ${lock_logLvl} -gt 1 ] &&
  218.         lock_log \
  219.             "function <lock_time_ageIsLessThan> - queried ${lock_timeLock_file_fullPath}: it is at least <${lock_timeLock_seconds}> seconds old"
  220.     if rm -f "${lock_timeLock_file_fullPath}" >/dev/null 2>&1; then
  221.         [ ${lock_logLvl} -gt 0 ] &&
  222.             lock_log \
  223.                 "function <lock_time_ageIsLessThan> - deleted <${lock_timeLock_file_fullPath}>"
  224.         return 1
  225.     fi
  226.     lock_log_error \
  227.         "function <lock_time_ageIsLessThan> - problem deleting <${lock_timeLock_file_fullPath}>"
  228.     return 1
  229. }
  230.  
  231. lock_time_lock() {
  232.     lock_lockFile="${lock_lockFile_dir}/${lock_userName}-${lock_progName}.lock"
  233.     lock_timeLock_file_fullPath="${lock_lockFile_dir}/${lock_userName}-${lock_progName}.timelock"
  234.     if ! touch "${lock_timeLock_file_fullPath}" >/dev/null 2>&1; then
  235.         lock_log_error \
  236.             "function <lock_time_lock> - problem creating <${lock_timeLock_file_fullPath}>"
  237.         return 1
  238.     fi
  239.     [ ${lock_logLvl} -gt 1 ] &&
  240.         lock_log \
  241.             "function <lock_time_lock> - created or updated <${lock_timeLock_file_fullPath}>"
  242.     chmod 777 "${lock_timeLock_file_fullPath}" >/dev/null 2>&1 &&
  243.         return 0
  244.     lock_log_error \
  245.         "function <lock_time_lock> - problem setting permissions for <${lock_timeLock_file_fullPath}>"
  246.     return 1
  247. }
  248.  
  249. lock_time_file_delete() {
  250.     if rm -f --one-file-system -- "${lock_timeLock_file_fullPath}" >/dev/null 2>&1; then
  251.         [ ${lock_logLvl} -gt 0 ] &&
  252.             lock_log \
  253.                 "function <lock_time_file_delete> - ${lock_progName} deleted <${lock_timeLock_file_fullPath}>"
  254.         return 0
  255.     fi
  256.     lock_log_error \
  257.         "function <lock_time_file_delete> - problem deleting <${lock_timeLock_file_fullPath}>"
  258.     return 1
  259. }
  260.  
  261. lock_time_unlock() {
  262.     vars_set_timeLockVar &&
  263.         lock_time_file_delete &&
  264.         return 0
  265.     return 1
  266. }
  267.  
  268. # AUTOEXECUTE
  269. lock_progName=$(basename "${0}") || lock_progName='unknown'
  270. if /usr/bin/test "$(id -u)" -eq "0"; then
  271.     lock_userName='root'
  272. else
  273.     lock_isUnset=${USER:-}
  274.     if [ -z "${lock_isUnset}" ]; then
  275.         lock_userName='unknownUser'
  276.     else
  277.         lock_userName="${USER}"
  278.     fi
  279. fi
  280.  
  281. # EOF
  282.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement