-
Notifications
You must be signed in to change notification settings - Fork 11
/
bashcached
executable file
·99 lines (93 loc) · 6.11 KB
/
bashcached
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env bash
# bashcached - memcached built on bash + socat
# (C) TSUYUSATO "MakeNowJust" Kitsune 2016-2020 <[email protected]>
#
# USAGE: bashcached [--help] [--version] [--license] [--protocol=tcp|unix] [--port=PORT] [--check=CHECK]
#
# OPTIONS:
# --protocol=tcp|unix protocol name to bind and listen (default: tcp)
# --port=PORT port (or filename) to bind and listen (default: 25252)
# --check=CHECK interval to check each cache's expire (default: 60)
# --help show this help
# --version show bashcached's version
# --license show bashcached's license
IFS=$' \t\r' VERSION=5.2.0-bashcached; export LANG=C
trap 'exit 0' INT TERM
if [[ "$SOCAT_VERSION" ]]; then
send="$(mktemp -u)"; mkfifo -m 600 "$send"
recv="$(mktemp -u)"; mkfifo -m 600 "$recv"; while [[ -p "$recv" ]]; do cat "$recv" 2>/dev/null; done &
trap 'rm -f "$recv" "$send"' EXIT; while read -ra cmd; do case ${cmd[0]} in
set|add|replace|append|prepend|cas)
while true; do printf 'recv=%q send=%q cmd=%q\n' "$recv" "$send" "${cmd[*]}" >"$BASHCACHED_PIPE" 2>/dev/null && break
done; head -c "${cmd[4]-0}" >"$send";;
quit) exit;; '') ;; *) while true; do
printf 'recv=%q send=%q cmd=%q\n' "$recv" "$send" "${cmd[*]}" >"$BASHCACHED_PIPE" 2>/dev/null && break; done;;
esac; done
else
help() { tail -n+2 <"$0" | head -n12 | cut -c3-; exit; }; version() { echo $VERSION; exit; }
license() { curl -s 'https://raw.githubusercontent.com/MakeNowJust/bashcached/master/LICENSE.md'; echo;
curl -s 'https://raw.githubusercontent.com/MakeNowJust/bashcached/master/LICENSE.%F0%9F%8D%A3.md'; exit; }
for v in "$@"; do [[ $v == --* ]] && eval "${v:2}"; done
# global variables
unique=1 before=$(printf '%(%s)T' -1)
declare -A flags=() exptime=() casUnique=() data=()
# cache operator
cache_has() { t=${exptime[$1]} && [[ $t && ( $t -eq 0 || $t -gt $time ) ]]; }
cache_update() { data[$1]=$2; [[ $3 ]] && casUnique[$1]="$3" || casUnique[$1]=$((unique++)); }
cache_set() { flags[$1]=$2 exptime[$1]=$((0 < $3 && $3 <= 2592000 ? $3 + time : $3)); cache_update "$1" "$4" "$5"; }
cache_get() { cache_has "$1" && d="${data[$1]}" && printf $'VALUE %s %s %s%s\r\n' \
"$1" "${flags[$1]}" "$(echo -n "$d" | base64 -d | wc -c)" "$([[ $2 ]] && echo " ${casUnique[$1]}")" &&
echo -n "$d" | base64 -d && echo -e '\r'; }
cache_delete() { unset "flags[$1]" "exptime[$1]" "casUnique[$1]" "data[$1]"; }
# utils
read_data() { d="$(head -c "$1" "$send" | base64)"; }
base64_cat() { cat <(echo -n "$1" | base64 -d) <(echo -n "$2" | base64 -d) | base64; }
BASHCACHED_PIPE="$(mktemp -u)"; export BASHCACHED_PIPE; mkfifo -m 600 "$BASHCACHED_PIPE"
trap 'rm -f "$BASHCACHED_PIPE"' EXIT
(( ${check-60} != 0 )) && while [[ -p "$BASHCACHED_PIPE" ]]; do echo; sleep "${check-60}"; done >"$BASHCACHED_PIPE" &
while [[ -p "$BASHCACHED_PIPE" ]]; do cat "$BASHCACHED_PIPE" 2>/dev/null; done | while read -r line; do
cmd=() recv='' send=; eval "$line"; read -ra cmd<<<"${cmd[*]}"; time=$(printf '%(%s)T' -1)
(( time - before >= ${check-60} )) && for k in "${!exptime[@]}"; do
! cache_has $k && cache_delete $k; done && before=$time
[[ ! -p $recv ]] && continue
case ${cmd[0]} in
set) read_data "${cmd[4]}" || return 1; cache_set "${cmd[1]}" "${cmd[2]}" "${cmd[3]}" "$d"
[[ "${cmd[5]}" != noreply ]] && echo -e 'STORED\r'>"$recv";;
add) read_data "${cmd[4]}" || return 1; ! cache_has "${cmd[1]}" && cache_set "${cmd[1]}" "${cmd[2]}" "${cmd[3]}" "$d" &&
result=STORED || result=NOT_STORED
[[ "${cmd[5]}" != noreply ]] && echo -e "$result\\r">"$recv";;
replace) read_data "${cmd[4]}" || return 1; cache_has "${cmd[1]}" && cache_set "${cmd[1]}" "${cmd[2]}" "${cmd[3]}" "$d" &&
result=STORED || result=NOT_STORED
[[ "${cmd[5]}" != noreply ]] && echo -e "$result\\r">"$recv";;
append) read_data "${cmd[4]}" || return 1; cache_has "${cmd[1]}" && cache_update "${cmd[1]}" \
"$(base64_cat "${data[${cmd[1]}]}" "$d")" && result=STORED || result=NOT_STORED
[[ "${cmd[5]}" != noreply ]] && echo -e "$result\\r">"$recv";;
prepend) read_data "${cmd[4]}" || return 1; cache_has "${cmd[1]}" && cache_update "${cmd[1]}" \
"$(base64_cat "$d" "${data[${cmd[1]}]}")" && result=STORED || result=NOT_STORED
[[ "${cmd[5]}" != noreply ]] && echo -e "$result\\r">"$recv";;
cas) read_data "${cmd[4]}" || return 1; if ! cache_has "${cmd[1]}"; then result=NOT_FOUND
else [[ "${casUnique[${cmd[1]}]}" -eq "${cmd[5]}" ]] && cache_set "${cmd[1]}" "${cmd[2]}" "${cmd[3]}" "$d" &&
result=STORED || result=EXISTS; fi
[[ "${cmd[6]}" != noreply ]] && echo -e "$result\\r">"$recv";;
get) (for ((i=1; i < ${#cmd[@]}; i++)); do cache_get "${cmd[$i]}"; done
echo -e 'END\r')>"$recv";;
gets) (for ((i=1; i < ${#cmd[@]}; i++)); do cache_get "${cmd[$i]}" 1; done
echo -e 'END\r')>"$recv";;
delete) cache_has "${cmd[1]}" && cache_delete "${cmd[1]}" &&
result=DELETED || result=NOT_FOUND
[[ "${cmd[2]}" != noreply ]] && echo -e "$result\\r">"$recv";;
incr) cache_has "${cmd[1]}" && result=$(($(echo -n "${data[${cmd[1]}]}" | base64 -d) + ${cmd[2]-0})) &&
cache_update "${cmd[1]}" "$(echo -n "$result" | base64)" || result=NOT_FOUND
[[ "${cmd[3]}" != noreply ]] && echo -e "$result\\r">"$recv";;
decr) cache_has "${cmd[1]}" && result=$(($(echo -n "${data[${cmd[1]}]}" | base64 -d) - ${cmd[2]-0})) &&
cache_update "${cmd[1]}" "$(echo -n $result | base64)" || result=NOT_FOUND
[[ "${cmd[3]}" != noreply ]] && echo -e "$result\\r">"$recv";;
touch) cache_has "${cmd[1]}" &&
cache_set "${cmd[1]}" "${flags[${cmd[1]}]}" "${cmd[2]}" "${data[${cmd[1]}]}" "${casUnique[${cmd[1]}]}" &&
result=TOUCHED || result=NOT_FOUND
[[ "${cmd[3]}" != noreply ]] && echo -e "$result\\r">"$recv";;
flush_all) for k in "${!exptime[@]}"; do exptime[$k]=$((time + ${cmd[1]-0})); done
[[ "${cmd[-1]}" != noreply ]] && echo -e 'OK\r'>"$recv";;
version) echo -e "VERSION $VERSION\\r">"$recv" &;;
esac; done &
socat "${protocol-tcp}-listen:${port-25252},reuseaddr,fork" system:"$0"; fi