gitoriaLog in with ident

antcolony

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commitc613d26bc613d26btemplates: bridges to external components (login.js for ident's selector) are allowed (creator 2026-09-27)mrec613d26b/lib/cleanup.sh

4.0 KB

  1. #!/bin/bash
  2. # cleanup.sh <from> <to> <since-ms> <session> — mission 026 (ticket antcolony#1): after a worker / controller / finalize
  3. # session ended (any outcome), stop what it left behind: every process that
  4. # * LISTENS (TCP) on a port of the session's range <from>-<to>, AND
  5. # * STARTED after the session part began (<since-ms>, epoch ms; + 50 ms margin): start = now − (uptime − starttime)
  6. # (/proc/uptime and /proc/<pid>/stat both count from boot — btime + starttime was up to 1 s off on Loreana), AND
  7. # * belongs to this user, is not this script's own ancestor (the scheduler, its wrapper), AND
  8. # * does not carry another colony session in its environment (COLONY_SESSION=<other>; the scheduler gives every
  9. # session COLONY_SESSION=<its id>, which the worker's servers inherit).
  10. # Never others. SIGTERM, up to 5 s, then SIGKILL. One line per process on stdout:
  11. # stopped pid <p> · port <n> · started <ISO> · SIGTERM|SIGKILL · <command line>
  12. # skipped pid <p> · port <n> · <why> · <command line> (or `skipped port <n> · no pid visible (another user)`)
  13. # nothing listening on <from>-<to> started after the session (when nothing was found at all)
  14. set -f # no globbing: `set -- $line` must not expand an ss address like `*:8781`
  15. from=$1; to=$2; since_ms=$3; session=$4
  16. case "$from$to$since_ms" in ''|*[!0-9]*) echo "error: usage: cleanup.sh <from> <to> <since-ms> <session>"; exit 0;; esac
  17. me=$(id -u)
  18. hz=$(getconf CLK_TCK)
  19. # the age of a process = uptime − its starttime; wall clock and uptime read together once
  20. now_ms=$(date +%s%3N)
  21. up=$(cut -d' ' -f1 /proc/uptime)
  22. up_ms=$(( ${up%.*} * 1000 + 10#${up#*.} * 10 ))
  23. # ancestors of this script — never touched
  24. anc=" "
  25. p=$$
  26. while [ -n "$p" ] && [ "$p" -gt 1 ] 2>/dev/null; do
  27. anc="$anc$p "
  28. s=$(cat "/proc/$p/stat" 2>/dev/null) || break
  29. s=${s##*) }
  30. set -- $s
  31. p=$2
  32. done
  33. iso() { date -u -d "@$(( $1 / 1000 )).$(printf %03d $(( $1 % 1000 )))" +%Y-%m-%dT%H:%M:%S.%3NZ; }
  34. # alive and not a zombie
  35. alive() { local t; t=$(cat "/proc/$1/stat" 2>/dev/null) || return 1; t=${t##*) }; [ "${t%% *}" != Z ]; }
  36. cmdof() { tr '\0' ' ' < "/proc/$1/cmdline" 2>/dev/null | cut -c1-160; }
  37. declare -A seen seenport
  38. n=0
  39. while read -r line; do
  40. [ -z "$line" ] && continue
  41. set -- $line
  42. local_addr=$4
  43. port=${local_addr##*:}
  44. case $port in ''|*[!0-9]*) continue;; esac
  45. if [ "$port" -lt "$from" ] || [ "$port" -gt "$to" ]; then continue; fi
  46. pids=$(printf '%s\n' "$line" | grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -un)
  47. if [ -z "$pids" ]; then
  48. [ -n "${seenport[$port]}" ] && continue
  49. seenport[$port]=1
  50. echo "skipped port $port · no pid visible (another user's process)"
  51. n=$((n + 1))
  52. continue
  53. fi
  54. for pid in $pids; do
  55. [ -n "${seen[$pid]}" ] && continue
  56. seen[$pid]=1
  57. n=$((n + 1))
  58. [ -d "/proc/$pid" ] || { echo "skipped pid $pid · port $port · gone already"; continue; }
  59. cmd=$(cmdof "$pid")
  60. owner=$(stat -c %u "/proc/$pid" 2>/dev/null)
  61. if [ "$owner" != "$me" ]; then echo "skipped pid $pid · port $port · another user ($owner) · $cmd"; continue; fi
  62. case "$anc" in *" $pid "*) echo "skipped pid $pid · port $port · the scheduler itself · $cmd"; continue;; esac
  63. s=$(cat "/proc/$pid/stat" 2>/dev/null) || { echo "skipped pid $pid · port $port · gone already"; continue; }
  64. s=${s##*) }
  65. set -- $s
  66. st=${20}
  67. start_ms=$(( now_ms - (up_ms - st * 1000 / hz) ))
  68. if [ "$start_ms" -lt $(( since_ms + 50 )) ]; then echo "skipped pid $pid · port $port · started $(iso "$start_ms"), before the session · $cmd"; continue; fi
  69. other=$(tr '\0' '\n' < "/proc/$pid/environ" 2>/dev/null | grep -m1 '^COLONY_SESSION=' | cut -d= -f2-)
  70. if [ -n "$other" ] && [ "$other" != "$session" ]; then echo "skipped pid $pid · port $port · belongs to colony session $other · $cmd"; continue; fi
  71. kill -TERM "$pid" 2>/dev/null
  72. sig=SIGTERM
  73. i=0
  74. while alive "$pid" && [ $i -lt 50 ]; do sleep 0.1; i=$((i + 1)); done
  75. if alive "$pid"; then kill -KILL "$pid" 2>/dev/null; sig=SIGKILL; fi
  76. echo "stopped pid $pid · port $port · started $(iso "$start_ms") · $sig · $cmd"
  77. done
  78. done < <(ss -ltnpH 2>/dev/null)
  79. [ "$n" -eq 0 ] && echo "nothing listening on $from-$to"
  80. exit 0

Branches

Latest commits

  • c613d26btemplates: bridges to external components (login.js for ident's selector) are allowed (creator 2026-09-27)mre
  • 9062978ctracker: worker box sees /media/STORAGE/projects/old-tracker read-only (tracker#2 source data)mre
  • 7f9660eeState of 2026-09-27, before the move to gitoriamre