Replace with B2 archive contents
This commit is contained in:
55
scripts/kill-orphan-claude.sh
Executable file
55
scripts/kill-orphan-claude.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
# Kill orphaned Claude processes (no TTY or parent is init)
|
||||
# An orphaned Claude process is one where:
|
||||
# - TTY is "?" (no terminal attached), OR
|
||||
# - Parent PID is 1 (adopted by init)
|
||||
|
||||
LOG_TAG="orphan-claude-killer"
|
||||
|
||||
# Find all claude processes
|
||||
CLAUDE_PIDS=$(pgrep -x claude 2>/dev/null)
|
||||
|
||||
if [ -z "$CLAUDE_PIDS" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for PID in $CLAUDE_PIDS; do
|
||||
# Get TTY and PPID for this process
|
||||
PROC_INFO=$(ps -o tty=,ppid= -p "$PID" 2>/dev/null)
|
||||
|
||||
if [ -z "$PROC_INFO" ]; then
|
||||
# Process already gone
|
||||
continue
|
||||
fi
|
||||
|
||||
PROC_TTY=$(echo "$PROC_INFO" | awk '{print $1}')
|
||||
PARENT_PID=$(echo "$PROC_INFO" | awk '{print $2}')
|
||||
|
||||
ORPHANED=false
|
||||
REASON=""
|
||||
|
||||
# Check if TTY is "?" (no terminal)
|
||||
if [ "$PROC_TTY" = "?" ]; then
|
||||
ORPHANED=true
|
||||
REASON="no TTY attached"
|
||||
fi
|
||||
|
||||
# Check if parent PID is 1 (adopted by init)
|
||||
if [ "$PARENT_PID" = "1" ]; then
|
||||
ORPHANED=true
|
||||
REASON="parent is init (PPID=1)"
|
||||
fi
|
||||
|
||||
if [ "$ORPHANED" = true ]; then
|
||||
# Get process start time for logging
|
||||
START_TIME=$(ps -o lstart= -p "$PID" 2>/dev/null)
|
||||
CPU=$(ps -o %cpu= -p "$PID" 2>/dev/null)
|
||||
MEM=$(ps -o %mem= -p "$PID" 2>/dev/null)
|
||||
|
||||
logger "$LOG_TAG: Killing orphaned claude process PID=$PID ($REASON) started='$START_TIME' cpu=$CPU% mem=$MEM%"
|
||||
|
||||
# Kill the process tree (claude may have child processes)
|
||||
pkill -9 -P "$PID" 2>/dev/null
|
||||
kill -9 "$PID" 2>/dev/null
|
||||
fi
|
||||
done
|
||||
13
scripts/kill-top-cpu.sh
Executable file
13
scripts/kill-top-cpu.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# Kill the process tree using the most CPU (excluding critical ones)
|
||||
|
||||
# Find the top CPU consumer (excluding protected and transient processes)
|
||||
TOP_LINE=$(ps -eo pid,comm,%cpu --sort=-%cpu | grep -v -E '(PID|systemd|sshd|monit|earlyoom|bash|ps|awk|grep|head)' | head -1)
|
||||
TARGET_PID=$(echo "$TOP_LINE" | awk '{print $1}')
|
||||
COMM=$(echo "$TOP_LINE" | awk '{print $2}')
|
||||
|
||||
if [ -n "$TARGET_PID" ] && [ -n "$COMM" ]; then
|
||||
logger "monit cpu-killer: Killing all '$COMM' processes (detected high CPU on PID $TARGET_PID)"
|
||||
# Kill all processes with this command name
|
||||
pkill -9 -x "$COMM"
|
||||
fi
|
||||
52
scripts/status.sh
Executable file
52
scripts/status.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# Status check for runaway process killer
|
||||
|
||||
echo "============================================"
|
||||
echo "Runaway Process Killer - Status"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
echo "=== Services ==="
|
||||
echo -n "earlyoom: "
|
||||
systemctl is-active earlyoom 2>/dev/null || echo "not running"
|
||||
|
||||
echo -n "monit: "
|
||||
systemctl is-active monit 2>/dev/null || echo "not running"
|
||||
|
||||
echo ""
|
||||
echo "=== Current System Load ==="
|
||||
echo -n "CPU: "
|
||||
top -bn1 | grep "Cpu(s)" | awk '{print $2 "% user, " $4 "% system"}'
|
||||
|
||||
echo -n "RAM: "
|
||||
free -h | awk '/Mem:/ {printf "%s used / %s total (%.1f%% used)\n", $3, $2, $3/$2*100}'
|
||||
|
||||
echo -n "Swap: "
|
||||
free -h | awk '/Swap:/ {if ($2 != "0B") printf "%s used / %s total\n", $3, $2; else print "disabled"}'
|
||||
|
||||
echo ""
|
||||
echo "=== Configuration ==="
|
||||
if [ -f /etc/monit/conf.d/cpu-killer ]; then
|
||||
CYCLES=$(grep "for.*cycles" /etc/monit/conf.d/cpu-killer | grep -oP '\d+(?= cycles)')
|
||||
echo "CPU threshold: ${CYCLES} minutes at 95%+"
|
||||
else
|
||||
echo "CPU threshold: NOT CONFIGURED"
|
||||
fi
|
||||
|
||||
if [ -f /etc/default/earlyoom ]; then
|
||||
RAM_PCT=$(grep -oP '(?<=-m )\d+' /etc/default/earlyoom)
|
||||
echo "RAM threshold: <${RAM_PCT}% free memory"
|
||||
else
|
||||
echo "RAM threshold: NOT CONFIGURED"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Recent Kill Events ==="
|
||||
echo "CPU kills (last 24h):"
|
||||
journalctl --since "24 hours ago" 2>/dev/null | grep "cpu-killer" | tail -3 || echo " None"
|
||||
|
||||
echo ""
|
||||
echo "RAM kills (last 24h):"
|
||||
journalctl -u earlyoom --since "24 hours ago" 2>/dev/null | grep -E "SIGTERM|SIGKILL" | tail -3 || echo " None"
|
||||
|
||||
echo ""
|
||||
105
scripts/test.sh
Executable file
105
scripts/test.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
# Test script for runaway process killer
|
||||
# WARNING: This will stress your system!
|
||||
|
||||
set -e
|
||||
|
||||
echo "============================================"
|
||||
echo "Runaway Process Killer - Test Suite"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo "WARNING: This will temporarily stress your CPU and RAM!"
|
||||
echo ""
|
||||
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Error: Please run as root (sudo ./test.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check stress is installed
|
||||
if ! command -v stress &> /dev/null; then
|
||||
echo "Installing stress tool..."
|
||||
apt-get install -y -qq stress
|
||||
fi
|
||||
|
||||
# Backup current config
|
||||
ORIG_CYCLES=$(grep "for.*cycles" /etc/monit/conf.d/cpu-killer | grep -oP '\d+(?= cycles)')
|
||||
echo "Current CPU threshold: ${ORIG_CYCLES} cycles"
|
||||
echo "Temporarily setting to 2 cycles for testing..."
|
||||
|
||||
# Set to 2 cycles for testing
|
||||
sed -i "s/for ${ORIG_CYCLES} cycles/for 2 cycles/" /etc/monit/conf.d/cpu-killer
|
||||
monit reload
|
||||
sleep 2
|
||||
|
||||
echo ""
|
||||
echo "=== TEST 1: CPU Protection ==="
|
||||
echo "Starting CPU stress (expect kill in ~2 minutes)..."
|
||||
echo ""
|
||||
|
||||
stress --cpu 4 --timeout 300 &
|
||||
STRESS_PID=$!
|
||||
sleep 2
|
||||
|
||||
START=$(date +%s)
|
||||
while kill -0 $STRESS_PID 2>/dev/null; do
|
||||
sleep 5
|
||||
NOW=$(date +%s)
|
||||
ELAPSED=$((NOW - START))
|
||||
|
||||
if [ $ELAPSED -ge 180 ]; then
|
||||
echo "FAILED: CPU stress not killed after 3 minutes"
|
||||
kill $STRESS_PID 2>/dev/null || true
|
||||
break
|
||||
fi
|
||||
|
||||
if ! pgrep -x stress > /dev/null 2>&1; then
|
||||
echo "PASSED: CPU stress killed after ${ELAPSED}s"
|
||||
break
|
||||
fi
|
||||
|
||||
[ $((ELAPSED % 30)) -eq 0 ] && echo "[${ELAPSED}s] Stress still running..."
|
||||
done
|
||||
|
||||
sleep 2
|
||||
|
||||
echo ""
|
||||
echo "=== TEST 2: RAM Protection ==="
|
||||
echo "Starting RAM stress (expect quick kill)..."
|
||||
echo ""
|
||||
|
||||
stress --vm 4 --vm-bytes 4G --vm-keep --timeout 300 &
|
||||
STRESS_PID=$!
|
||||
sleep 2
|
||||
|
||||
START=$(date +%s)
|
||||
while pgrep -x stress > /dev/null 2>&1; do
|
||||
sleep 5
|
||||
NOW=$(date +%s)
|
||||
ELAPSED=$((NOW - START))
|
||||
|
||||
if [ $ELAPSED -ge 120 ]; then
|
||||
echo "FAILED: RAM stress not killed after 2 minutes"
|
||||
pkill stress 2>/dev/null || true
|
||||
break
|
||||
fi
|
||||
|
||||
if ! pgrep -x stress > /dev/null 2>&1; then
|
||||
echo "PASSED: RAM stress killed after ${ELAPSED}s"
|
||||
break
|
||||
fi
|
||||
|
||||
MEM_FREE=$(free -m | awk '/Mem:/ {print $7}')
|
||||
echo "[${ELAPSED}s] Stress running, ${MEM_FREE}MB free"
|
||||
done
|
||||
|
||||
# Restore original config
|
||||
echo ""
|
||||
echo "Restoring original CPU threshold (${ORIG_CYCLES} cycles)..."
|
||||
sed -i "s/for 2 cycles/for ${ORIG_CYCLES} cycles/" /etc/monit/conf.d/cpu-killer
|
||||
monit reload
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo "Tests Complete"
|
||||
echo "============================================"
|
||||
Reference in New Issue
Block a user