From 6c12082a540dac07b5abe03dc629e4165036e795 Mon Sep 17 00:00:00 2001 From: Vyacheslav K Date: Mon, 15 Jun 2026 16:25:03 +0300 Subject: [PATCH] feat: suppress systemctl inotify noise, cache service state in menu loop --- main.sh | 48 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/main.sh b/main.sh index d45a67c..3d7a254 100644 --- a/main.sh +++ b/main.sh @@ -82,6 +82,19 @@ run_elevated() { "$ELEVATE_CMD" "$@" } +# systemctl on some systems logs "Failed to add a watch ... inotify watch limit reached" +# to stderr when it talks to PID 1. The operation itself succeeds; suppress the +# noisy line so it does not pollute the TUI. +run_systemctl() { + local line rc + while IFS= read -r line; do + [[ "$line" == *"Failed to add a watch"*"inotify watch limit reached"* ]] && continue + printf '%s\n' "$line" + done < <(systemctl "$@" 2>&1) || true + rc=${PIPESTATUS[0]} + return "$rc" +} + check_deps() { local missing=() cmd for cmd in bash curl jq systemctl date stat mktemp sha256sum cmp install dirname basename cut mkdir chmod cp cat printf; do @@ -203,7 +216,7 @@ normalize_service_name() { validate_service() { local svc="$1" - systemctl --no-ask-password cat "$svc" >/dev/null 2>&1 + run_systemctl --no-ask-password cat "$svc" >/dev/null 2>&1 } ensure_target_parent() { @@ -422,13 +435,13 @@ install_xray_config() { restart_xray_service() { local service="$1" info "Перезапускаю ${service}…" - if systemctl --no-ask-password restart "$service" 2>/dev/null; then + if run_systemctl --no-ask-password restart "$service" 2>/dev/null; then ok "Служба ${service} перезапущена." return 0 fi elevate_available || die "Не удалось перезапустить ${service}: нет прав и не найден doas/run0/pkexec/sudo." - run_elevated systemctl --no-ask-password restart "$service" + run_elevated run_systemctl --no-ask-password restart "$service" ok "Служба ${service} перезапущена." } @@ -540,12 +553,12 @@ xray_socks_endpoint() { service_state_raw() { local service="$1" - systemctl --no-ask-password is-active "$service" 2>/dev/null || true + run_systemctl --no-ask-password is-active "$service" 2>/dev/null || true } service_enabled_raw() { local service="$1" - systemctl --no-ask-password is-enabled "$service" 2>/dev/null || true + run_systemctl --no-ask-password is-enabled "$service" 2>/dev/null || true } service_state_colored() { @@ -582,7 +595,7 @@ run_service_action() { say "" info "systemctl ${action} ${service}" - if systemctl --no-ask-password "$action" "$service" 2>/dev/null; then + if run_systemctl --no-ask-password "$action" "$service" 2>/dev/null; then ok "Готово: ${service} → ${action}." pause_screen hide_cursor @@ -596,7 +609,7 @@ run_service_action() { return 1 } - if run_elevated systemctl --no-ask-password "$action" "$service"; then + if run_elevated run_systemctl --no-ask-password "$action" "$service"; then ok "Готово: ${service} → ${action}." else warn "systemctl ${action} завершился с ошибкой. Проверьте статус службы." @@ -612,7 +625,7 @@ show_service_status_screen() { clear_screen say "${C_BOLD}${C_MAGENTA}✦ Статус службы ${service}${C_RESET}" say "" - if ! systemctl --no-ask-password --no-pager --full status "$service" -n 18; then + if ! run_systemctl --no-ask-password --no-pager --full status "$service" -n 18; then warn "Не удалось прочитать статус через systemctl." fi pause_screen @@ -663,6 +676,7 @@ render_profile_row() { render_menu() { local cursor="$1" count saved_index i cache_info selected_name local service service_state service_enabled xray_config socks_endpoint + local now count="$(profile_count)" saved_index="$(cfg_get selected_index)" @@ -671,8 +685,17 @@ render_menu() { xray_config="$(cfg_get xray_config)" socks_endpoint="$(xray_socks_endpoint "$xray_config")" cache_info="$(subscription_update_info)" - service_state="$(service_state_raw "$service")" - service_enabled="$(service_enabled_raw "$service")" + + # Polling systemd on every keypress can exhaust inotify watches quickly. + # Cache the state for a short interval inside the menu loop. + now="$(date +%s)" + if [[ -z "${_MENU_STATE_CACHE_TS:-}" || $(( now - _MENU_STATE_CACHE_TS )) -gt 2 ]]; then + _MENU_STATE_CACHE_TS="$now" + _MENU_STATE_CACHE_VALUE="$(service_state_raw "$service")" + _MENU_ENABLED_CACHE_VALUE="$(service_enabled_raw "$service")" + fi + service_state="${_MENU_STATE_CACHE_VALUE}" + service_enabled="${_MENU_ENABLED_CACHE_VALUE}" clear_screen say "${C_BOLD}${C_MAGENTA}✦ Xray JSON TUI${C_RESET}" @@ -751,15 +774,19 @@ menu() { ;; s|S) run_service_action start "$service" + unset _MENU_STATE_CACHE_TS _MENU_STATE_CACHE_VALUE _MENU_ENABLED_CACHE_VALUE ;; o|O) run_service_action stop "$service" + unset _MENU_STATE_CACHE_TS _MENU_STATE_CACHE_VALUE _MENU_ENABLED_CACHE_VALUE ;; e|E) run_service_action restart "$service" + unset _MENU_STATE_CACHE_TS _MENU_STATE_CACHE_VALUE _MENU_ENABLED_CACHE_VALUE ;; v|V) show_service_status_screen "$service" + unset _MENU_STATE_CACHE_TS _MENU_STATE_CACHE_VALUE _MENU_ENABLED_CACHE_VALUE ;; q|Q) clear_screen @@ -770,6 +797,7 @@ menu() { show_cursor say "" apply_selection "$cursor" + unset _MENU_STATE_CACHE_TS _MENU_STATE_CACHE_VALUE _MENU_ENABLED_CACHE_VALUE pause_screen hide_cursor ;;