mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 19:06:07 +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
|
||||
# ============================================================================
|
||||
|
||||
GOTELEGRAM_VERSION = "2.4.2"
|
||||
GOTELEGRAM_VERSION = "2.4.3"
|
||||
GOTELEGRAM_CONFIG = "/opt/gotelegram/config.json"
|
||||
TELEMT_CONFIG = "/etc/telemt/config.toml"
|
||||
TELEMT_SERVICE = "telemt"
|
||||
|
||||
39
install.sh
39
install.sh
@@ -1323,8 +1323,45 @@ bot_action_change_lite_domain() {
|
||||
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() {
|
||||
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
|
||||
for arg in "$@"; do
|
||||
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
|
||||
|
||||
# ── Version ───────────────────────────────────────────────────────────────────
|
||||
GOTELEGRAM_VERSION="2.4.2"
|
||||
GOTELEGRAM_VERSION="2.4.3"
|
||||
GOTELEGRAM_NAME="GoTelegram"
|
||||
|
||||
# ── Пути ──────────────────────────────────────────────────────────────────────
|
||||
@@ -277,6 +277,7 @@ apt_pkg_for_cmd() {
|
||||
host) echo "dnsutils" ;;
|
||||
ss) echo "iproute2" ;;
|
||||
netstat) echo "net-tools" ;;
|
||||
flock) echo "util-linux" ;;
|
||||
*) echo "$1" ;; # команда == имя пакета
|
||||
esac
|
||||
}
|
||||
@@ -287,13 +288,17 @@ dnf_pkg_for_cmd() {
|
||||
xxd) echo "vim-common" ;;
|
||||
ss) echo "iproute" ;;
|
||||
netstat) echo "net-tools" ;;
|
||||
flock) echo "util-linux" ;;
|
||||
*) echo "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
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, устанавливать всё равно, но не падать если не смогли
|
||||
local optional=(qrencode bc)
|
||||
|
||||
@@ -386,7 +391,7 @@ ensure_deps() {
|
||||
# main() чтобы не дёргать apt-get update при каждом запуске меню.
|
||||
check_deps_present() {
|
||||
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
|
||||
done
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user