telegram-bot-bash/modules/jsonDB.sh

247 lines
6.9 KiB
Bash
Raw Normal View History

2019-05-27 12:30:21 +00:00
#!/bin/bash
# file: modules/jsshDB.sh
# do not edit, this file will be overwritten on update
# This file is public domain in the USA and all free countries.
# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying)
#
2020-05-17 11:51:32 +00:00
#### $$VERSION$$ V0.94-0-gbdb50c8
2019-05-27 12:30:21 +00:00
#
# source from commands.sh to use jsonDB functions
#
# jsonDB provides simple functions to read and store bash Arrays
# from to file in JSON.sh output format, its a simple key/value storage.
2019-05-27 12:30:21 +00:00
2020-05-15 09:53:19 +00:00
2020-05-14 17:47:37 +00:00
# source once magic, function named like file
eval "$(basename "${BASH_SOURCE[0]}")(){ :; }"
2020-05-15 09:53:19 +00:00
# new feature: serialize / atomic operations:
# updates will be done atomic with flock
# flock should flock should be availible on all system as its part of busybox
# tinybox
# lockfile filename.flock is persistent and will be testet with flock for active lock (file open)
export BASHBOT_LOCKNAME=".flock"
if _exists flock; then
###############
# we have flock
# use flock for atomic operations
# read content of a file in JSON.sh format into given ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename, must be relative to BASHBOT_ETC, and not contain '..'
jssh_readDB() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
2020-05-15 09:53:19 +00:00
# shared lock, many processes can read, maximum wait 1s
{ flock -s -w 1 200; Json2Array "$1" <"${DB}"; } 200>"${DB}${BASHBOT_LOCKNAME}"
}
2019-05-27 12:30:21 +00:00
2020-05-15 09:53:19 +00:00
# write ARRAY content to a file in JSON.sh format
# Warning: old content is overwritten
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_writeDB() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
2020-05-15 09:53:19 +00:00
# exclusive lock, no other process can read or write, maximum wait to get lock is 10s
{ flock -e -w 10 200; Array2Json "$1" >"${DB}"; } 200>"${DB}${BASHBOT_LOCKNAME}"
}
2019-05-27 12:30:21 +00:00
2020-05-15 09:53:19 +00:00
# update/write ARRAY content in file without deleting keys not in ARRAY
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_updateDB() {
# for atomic update we cant use read/writeDB
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
2020-05-14 21:04:08 +00:00
declare -n ARRAY="$1"
[ -z "${ARRAY[*]}" ] && return 1
declare -A oldARR newARR
2020-05-15 09:53:19 +00:00
# start atomic update here, exclusive max wait 10s
{ flock -e -w 10 200
Json2Array "oldARR" <"${DB}"
if [ -z "${oldARR[*]}" ]; then
# no old content
2020-05-15 09:53:19 +00:00
Array2Json "$1" >"${DB}"
else
# merge arrays
local o1 o2 n1 n2
o1="$(declare -p oldARR)"; o2="${o1#*\(}"
n1="$(declare -p ARRAY)"; n2="${n1#*\(}"
unset IFS; set -f
#shellcheck disable=SC2034,SC2190,SC2206
newARR=( ${o2:0:${#o2}-1} ${n2:0:${#n2}-1} )
set +f
2020-05-15 09:53:19 +00:00
Array2Json "newARR" >"${DB}"
fi
2020-05-15 09:53:19 +00:00
} 200>"${DB}${BASHBOT_LOCKNAME}"
}
2020-05-15 09:53:19 +00:00
# insert, update, apped key/value to jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._
# $2 key value
# $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_insertDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local key="$1" value="$2"
local DB; DB="$(jssh_checkDB "$3")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
2020-05-15 09:53:19 +00:00
# start atomic update here, exclusive max wait 2si, it's append, not overwrite
{ flock -e -w 2 200
# it's append, but last one counts, its a simple DB ...
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${value//\"/\\\"}" >>"${DB}"
} 200>"${DB}${BASHBOT_LOCKNAME}"
2020-05-15 09:53:19 +00:00
}
2020-05-15 15:45:23 +00:00
# delete key/value from jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._
# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_deleteKeyDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
2020-05-15 16:49:38 +00:00
local DB; DB="$(jssh_checkDB "$2")"
2020-05-15 15:45:23 +00:00
declare -A oldARR
# start atomic delete here, exclusive max wait 10s
{ flock -e -w 10 200
Json2Array "oldARR" <"${DB}"
2020-05-15 16:49:38 +00:00
unset oldARR["$1"]
Array2Json "oldARR" >"${DB}"
2020-05-15 15:45:23 +00:00
} 200>"${DB}${BASHBOT_LOCKNAME}"
}
2020-05-15 09:53:19 +00:00
else
#########
# we have no flock, use "old" not atomic functions
jssh_readDB() {
jssh_readDB_async "$@"
}
jssh_writeDB() {
jssh_writeDB_async "$@"
}
jssh_updateDB() {
jssh_updateDB_async "$@"
}
jssh_insertDB() {
jssh_insertDB_async "$@"
}
2020-05-15 15:45:23 +00:00
jssh_deleteKeyDB() {
jssh_deleteKeyDB_async "$@"
}
2020-05-15 09:53:19 +00:00
fi
##############
# no need for atomic
# print ARRAY content to stdout instead of file
# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront
jssh_printDB() {
Array2Json "$1"
}
# get key/value from jsshDB
# $1 key name, can onyl contain -a-zA-Z0-9,._
# $2 key value
# $3 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
# returns value
jssh_getDB() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
declare -A getARR
jssh_readDB "getARR" "$3" || return "$?"
printf '%s\n' "${getARR[${key}]}"
}
2019-05-27 12:30:21 +00:00
# $1 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..'
jssh_newDB() {
local DB; DB="$(jssh_checkDB "$1")"
[ -z "${DB}" ] && return 1
[ -f "${DB}" ] && return 2 # already exist, do not zero out
2019-05-27 12:30:21 +00:00
printf '\n' >"${DB}"
}
2019-05-30 16:02:57 +00:00
# $1 filename, check filename, it must be relative to BASHBOT_ETC, and not contain '..'
# returns real path to DB file if everything is ok
jssh_checkDB(){
[ -z "$1" ] && return 1
2019-05-30 16:02:57 +00:00
local DB="${BASHBOT_ETC:-.}/$1.jssh"
if [[ "$1" = "${BASHBOT_ETC:-.}"* ]] || [[ "$1" = "${BASHBOT_DATA:-.}"* ]]; then
DB="$1.jssh"
fi
[[ "$1" = *'..'* ]] && return 2
2019-05-30 16:02:57 +00:00
printf '%s\n' "${DB}"
}
2020-05-15 09:53:19 +00:00
######################
# "old" implementations as non atomic functions
# can be used explictitly or as fallback if flock is not availible
jssh_readDB_async() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
Json2Array "$1" <"${DB}"
}
jssh_writeDB_async() {
local DB; DB="$(jssh_checkDB "$2")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
Array2Json "$1" >"${DB}"
}
jssh_updateDB_async() {
declare -n ARRAY="$1"
[ -z "${ARRAY[*]}" ] && return 1
declare -A oldARR newARR
2020-05-15 15:45:23 +00:00
jssh_readDB_async "oldARR" "$2" || return "$?"
2020-05-15 09:53:19 +00:00
if [ -z "${oldARR[*]}" ]; then
# no old content
2020-05-15 15:45:23 +00:00
jssh_writeDB_async "$1" "$2"
2020-05-15 09:53:19 +00:00
else
# merge arrays
local o1 o2 n1 n2
o1="$(declare -p oldARR)"; o2="${o1#*\(}"
n1="$(declare -p ARRAY)"; n2="${n1#*\(}"
unset IFS; set -f
#shellcheck disable=SC2034,SC2190,SC2206
newARR=( ${o2:0:${#o2}-1} ${n2:0:${#n2}-1} )
set +f
2020-05-15 15:45:23 +00:00
jssh_writeDB_async "newARR" "$2"
2020-05-15 09:53:19 +00:00
fi
}
jssh_insertDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
local key="$1" value="$2"
local DB; DB="$(jssh_checkDB "$3")"
[ -z "${DB}" ] && return 1
[ ! -f "${DB}" ] && return 2
# its append, but last one counts, its a simple DB ...
printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${value//\"/\\\"}" >>"${DB}"
}
2020-05-15 15:45:23 +00:00
jssh_deleteKeyDB_async() {
[[ "$1" =~ ^[-a-zA-Z0-9,._]+$ ]] || return 3
2020-05-15 16:49:38 +00:00
local DB; DB="$(jssh_checkDB "$2")"
2020-05-15 15:45:23 +00:00
declare -A oldARR
jssh_readDB_async "oldARR" "$2" || return "$?"
2020-05-15 16:49:38 +00:00
unset oldARR["$1"]
jssh_writeDB_async "oldARR" "$2"
2020-05-15 15:45:23 +00:00
}