139 lines
5.6 KiB
Bash
139 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# توقف همهٔ سرویسهای محلی کتیبه (web / worker / scanner) که با ./run.sh بالا آمدهاند.
|
|
#
|
|
# استفاده:
|
|
# ./stop.sh توقف همه + گزارش
|
|
# ./stop.sh --quiet بدون خروجی (برای صدا زدن از run.sh)
|
|
#
|
|
# ترتیب کار: اول PIDهای ثبتشده در .run/ (با SIGTERM و مهلت، سپس SIGKILL)،
|
|
# بعد پاکسازی هر پروسهٔ جامانده با الگوی manage.py — تا اگر pidfile گم/کهنه شده بود
|
|
# هم چیزی روی پورت ۸۰۰۰ نماند.
|
|
set -uo pipefail
|
|
|
|
cd "$(dirname "$(readlink -f "$0")")"
|
|
|
|
RUN_DIR=".run"
|
|
WEB_PORT="8000"
|
|
QUIET=0
|
|
[ "${1:-}" = "--quiet" ] && QUIET=1
|
|
|
|
say() { [ "$QUIET" = "1" ] || echo "$@"; }
|
|
|
|
# الگوی پروسههای همین پروژه. عمداً فقط روی manage.py و زیرفرمانهایش قفل میشود:
|
|
# cmdline این اسکریپت («bash ./stop.sh») با آن مطابقت نمیکند، پس خودش را نمیکشد.
|
|
PATTERN='manage\.py (runserver|sms_worker|sms_scan)'
|
|
|
|
# پاکسازی جانماندههای سمت ویندوز.
|
|
#
|
|
# چرا لازم است: از WSL، فایل ``.venv/Scripts/python.exe`` یک باینری ویندوزی است و از طریق
|
|
# WSL interop اجرا میشود. اگر والدِ interop از بین برود، پروسهٔ ویندوزی یتیم میشود و دیگر
|
|
# در ``ps`` داخل WSL دیده نمیشود، ولی پورت ۸۰۰۰ را همچنان گرفته نگه میدارد (نتیجه: سرور
|
|
# بعدی bind نمیشود یا نسخهٔ قدیمی کد سرو میشود). پس صریحاً با ابزار ویندوز پاک میشوند.
|
|
#
|
|
# فیلتر محافظتی: فقط پروسههای ``python`` که ``manage.py`` اجرا میکنند و *یا* از مسیر همین
|
|
# پروژه/venv آمدهاند *یا* پورت ۸۰۰۰ را اشغال کردهاند. هیچ پروژهٔ دیگری آسیب نمیبیند.
|
|
windows_cleanup() {
|
|
command -v powershell.exe >/dev/null 2>&1 || return 0
|
|
command -v taskkill.exe >/dev/null 2>&1 || return 0
|
|
|
|
local pids
|
|
pids="$(powershell.exe -NoProfile -Command "
|
|
\$port = $WEB_PORT
|
|
\$listen = @()
|
|
try { \$listen = @((Get-NetTCPConnection -LocalPort \$port -State Listen -ErrorAction Stop).OwningProcess) } catch { \$listen = @() }
|
|
Get-CimInstance Win32_Process -Filter \"Name='python.exe' or Name='pythonw.exe'\" |
|
|
Where-Object {
|
|
\$_.CommandLine -match 'manage\.py' -and (
|
|
\$_.ExecutablePath -like '*khatibi*' -or
|
|
\$_.CommandLine -like '*khatibi*' -or
|
|
(\$listen -contains \$_.ProcessId)
|
|
)
|
|
} | ForEach-Object { \$_.ProcessId }
|
|
" 2>/dev/null | tr -d '\r' | grep -E '^[0-9]+$' || true)"
|
|
|
|
[ -n "$pids" ] || return 0
|
|
|
|
local p
|
|
for p in $pids; do
|
|
taskkill.exe /F /PID "$p" >/dev/null 2>&1 || true
|
|
done
|
|
say " پروسههای ویندوزی پاک شدند: $(echo "$pids" | tr '\n' ' ')"
|
|
}
|
|
|
|
is_our_process() {
|
|
local pid="$1" cmd
|
|
[ -r "/proc/$pid/cmdline" ] || return 1
|
|
cmd="$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null || true)"
|
|
case "$cmd" in *manage.py*) return 0 ;; *) return 1 ;; esac
|
|
}
|
|
|
|
stop_pid() {
|
|
local pid="$1" name="$2"
|
|
[ -n "$pid" ] || return 0
|
|
kill -0 "$pid" 2>/dev/null || return 0
|
|
|
|
# محافظت در برابر PID کهنه: اگر این PID دیگر مال پروژه نیست، دست نزن.
|
|
if ! is_our_process "$pid"; then
|
|
say " $name: pid $pid دیگر مربوط به این پروژه نیست؛ رد شد"
|
|
return 0
|
|
fi
|
|
|
|
kill -TERM "$pid" 2>/dev/null || true
|
|
for _ in $(seq 1 20); do
|
|
kill -0 "$pid" 2>/dev/null || { say " $name (pid $pid) متوقف شد"; return 0; }
|
|
sleep 0.25
|
|
done
|
|
kill -KILL "$pid" 2>/dev/null || true
|
|
say " $name (pid $pid) پس از مهلت مجبور به توقف شد"
|
|
}
|
|
|
|
say "==> توقف سرویسهای محلی"
|
|
|
|
stopped_any=0
|
|
if [ -d "$RUN_DIR" ]; then
|
|
shopt -s nullglob
|
|
for pidfile in "$RUN_DIR"/*.pid; do
|
|
name="$(basename "$pidfile" .pid)"
|
|
pid="$(cat "$pidfile" 2>/dev/null || true)"
|
|
[ -n "$pid" ] && stopped_any=1
|
|
stop_pid "$pid" "$name"
|
|
rm -f "$pidfile"
|
|
done
|
|
shopt -u nullglob
|
|
fi
|
|
|
|
# پاکسازی باقیماندهها (pidfile گمشده، اجرای دستی، کرش قبلی)
|
|
leftover=0
|
|
if command -v pkill >/dev/null 2>&1; then
|
|
if pgrep -f "$PATTERN" >/dev/null 2>&1; then
|
|
leftover=1
|
|
pkill -TERM -f "$PATTERN" 2>/dev/null || true
|
|
for _ in $(seq 1 20); do
|
|
pgrep -f "$PATTERN" >/dev/null 2>&1 || break
|
|
sleep 0.25
|
|
done
|
|
pkill -KILL -f "$PATTERN" 2>/dev/null || true
|
|
say " پروسههای جامانده پاک شدند"
|
|
fi
|
|
fi
|
|
|
|
if [ "$stopped_any" = "0" ] && [ "$leftover" = "0" ]; then
|
|
say " چیزی برای متوقفکردن نبود."
|
|
fi
|
|
|
|
windows_cleanup
|
|
|
|
# تأیید نهایی: پورت ۸۰۰۰ آزاد باشد.
|
|
# --noproxy اجباری است: در این محیط یک پروکسی محلی (127.0.0.1:10808) هست که درخواست به
|
|
# localhost را میگیرد و 503 برمیگرداند؛ بدون دور زدنش این بررسی false positive میدهد.
|
|
if command -v curl >/dev/null 2>&1; then
|
|
code="$(curl -s --noproxy '*' -o /dev/null -w '%{http_code}' --max-time 3 \
|
|
"http://127.0.0.1:$WEB_PORT/" 2>/dev/null || true)"
|
|
if [ -n "$code" ] && [ "$code" != "000" ]; then
|
|
say " هشدار: هنوز چیزی روی پورت $WEB_PORT پاسخ میدهد (HTTP $code) — سروری خارج از این اسکریپت."
|
|
fi
|
|
fi
|
|
|
|
say "✅ پایان."
|