mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 21:16:04 +00:00
fix(v2.4.3): flock serialization for bot_action_dispatch
Iteration 3 discovered a race condition: parallel CLI invocations of change-lite-domain occasionally produced 'no secret in config' because one process read config.json while another was mid-rewrite (jq -> tmp -> mv). asyncio.Lock in bot.py only protects bot->bot races; CLI-level races and bot+CLI mixed races were still possible. - install.sh: bot_action_dispatch wraps dispatch in flock(1) on /var/lock/gotelegram-bot-action.lock, 30s timeout, EX_TEMPFAIL on timeout - common.sh: flock added to critical deps; apt_pkg_for_cmd/dnf_pkg_for_cmd map flock -> util-linux - common.sh: check_deps_present includes flock - version bumped to 2.4.3 (common.sh + bot.py)
This commit is contained in:
@@ -100,7 +100,7 @@ logger = logging.getLogger(__name__)
|
|||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
GOTELEGRAM_VERSION = "2.4.2"
|
GOTELEGRAM_VERSION = "2.4.3"
|
||||||
GOTELEGRAM_CONFIG = "/opt/gotelegram/config.json"
|
GOTELEGRAM_CONFIG = "/opt/gotelegram/config.json"
|
||||||
TELEMT_CONFIG = "/etc/telemt/config.toml"
|
TELEMT_CONFIG = "/etc/telemt/config.toml"
|
||||||
TELEMT_SERVICE = "telemt"
|
TELEMT_SERVICE = "telemt"
|
||||||
|
|||||||
39
install.sh
39
install.sh
@@ -1323,8 +1323,45 @@ bot_action_change_lite_domain() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main dispatcher — called from main() when --action=X is present
|
# Main dispatcher — called from main() when --action=X is present.
|
||||||
|
# Uses a file lock (flock) so concurrent CLI invocations (from multiple bot
|
||||||
|
# users, or from bot + manual CLI) serialize cleanly. Without this, two
|
||||||
|
# parallel `change-lite-domain` calls raced on the jq-rewrite of config.json
|
||||||
|
# and one process would see a truncated file ("no secret in config").
|
||||||
bot_action_dispatch() {
|
bot_action_dispatch() {
|
||||||
|
local lock_file="/var/lock/gotelegram-bot-action.lock"
|
||||||
|
# Make sure /var/lock exists (it does on Debian/Ubuntu; be defensive for minimal images)
|
||||||
|
[ -d /var/lock ] || mkdir -p /var/lock 2>/dev/null || true
|
||||||
|
|
||||||
|
if command -v flock >/dev/null 2>&1; then
|
||||||
|
# Wait up to 30 seconds for the lock — bot actions are fast (<5s
|
||||||
|
# typical), so 30s is plenty for legitimate serialization but short
|
||||||
|
# enough to surface a stuck process.
|
||||||
|
(
|
||||||
|
flock -w 30 9 || {
|
||||||
|
# If we time out, emit JSON error for the bot parent.
|
||||||
|
local json_out=0 a
|
||||||
|
for a in "$@"; do
|
||||||
|
[ "$a" = "--json" ] && json_out=1
|
||||||
|
done
|
||||||
|
if [ "$json_out" = "1" ]; then
|
||||||
|
bot_emit_json "error" "another action in progress (lock timeout)" "code=lock_timeout"
|
||||||
|
fi
|
||||||
|
exit 75 # EX_TEMPFAIL
|
||||||
|
}
|
||||||
|
_bot_action_dispatch_locked "$@"
|
||||||
|
) 9>"$lock_file"
|
||||||
|
return $?
|
||||||
|
else
|
||||||
|
# No flock installed — run unlocked with a warning. ensure_deps/check_deps
|
||||||
|
# normally ensures util-linux is present, so this branch is defensive.
|
||||||
|
log_warning "flock not available — bot actions not serialized"
|
||||||
|
_bot_action_dispatch_locked "$@"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_bot_action_dispatch_locked() {
|
||||||
local action="" tpl_id="" domain="" json_out=0 arg
|
local action="" tpl_id="" domain="" json_out=0 arg
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
|
|||||||
13
lib/common.sh
Executable file → Normal file
13
lib/common.sh
Executable file → Normal file
@@ -3,7 +3,7 @@
|
|||||||
# Colors, logging, spinner, system helpers, v1 compat, i18n-aware
|
# Colors, logging, spinner, system helpers, v1 compat, i18n-aware
|
||||||
|
|
||||||
# ── Version ───────────────────────────────────────────────────────────────────
|
# ── Version ───────────────────────────────────────────────────────────────────
|
||||||
GOTELEGRAM_VERSION="2.4.2"
|
GOTELEGRAM_VERSION="2.4.3"
|
||||||
GOTELEGRAM_NAME="GoTelegram"
|
GOTELEGRAM_NAME="GoTelegram"
|
||||||
|
|
||||||
# ── Пути ──────────────────────────────────────────────────────────────────────
|
# ── Пути ──────────────────────────────────────────────────────────────────────
|
||||||
@@ -277,6 +277,7 @@ apt_pkg_for_cmd() {
|
|||||||
host) echo "dnsutils" ;;
|
host) echo "dnsutils" ;;
|
||||||
ss) echo "iproute2" ;;
|
ss) echo "iproute2" ;;
|
||||||
netstat) echo "net-tools" ;;
|
netstat) echo "net-tools" ;;
|
||||||
|
flock) echo "util-linux" ;;
|
||||||
*) echo "$1" ;; # команда == имя пакета
|
*) echo "$1" ;; # команда == имя пакета
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -287,13 +288,17 @@ dnf_pkg_for_cmd() {
|
|||||||
xxd) echo "vim-common" ;;
|
xxd) echo "vim-common" ;;
|
||||||
ss) echo "iproute" ;;
|
ss) echo "iproute" ;;
|
||||||
netstat) echo "net-tools" ;;
|
netstat) echo "net-tools" ;;
|
||||||
|
flock) echo "util-linux" ;;
|
||||||
*) echo "$1" ;;
|
*) echo "$1" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_deps() {
|
ensure_deps() {
|
||||||
# Критические зависимости — без них скрипт не работает
|
# Критические зависимости — без них скрипт не работает.
|
||||||
local critical=(curl jq openssl git xxd tar dig)
|
# flock используется bot_action_dispatch для сериализации параллельных
|
||||||
|
# вызовов (иначе гонка на config.json при одновременных change-template /
|
||||||
|
# change-lite-domain из бота).
|
||||||
|
local critical=(curl jq openssl git xxd tar dig flock)
|
||||||
# Желательные — есть fallback, устанавливать всё равно, но не падать если не смогли
|
# Желательные — есть fallback, устанавливать всё равно, но не падать если не смогли
|
||||||
local optional=(qrencode bc)
|
local optional=(qrencode bc)
|
||||||
|
|
||||||
@@ -386,7 +391,7 @@ ensure_deps() {
|
|||||||
# main() чтобы не дёргать apt-get update при каждом запуске меню.
|
# main() чтобы не дёргать apt-get update при каждом запуске меню.
|
||||||
check_deps_present() {
|
check_deps_present() {
|
||||||
local cmd
|
local cmd
|
||||||
for cmd in curl jq openssl git xxd tar dig; do
|
for cmd in curl jq openssl git xxd tar dig flock; do
|
||||||
command -v "$cmd" &>/dev/null || return 1
|
command -v "$cmd" &>/dev/null || return 1
|
||||||
done
|
done
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user