runtime-lib.sh 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #!/usr/bin/env bash
  2. require_root() {
  3. if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
  4. echo "need root"
  5. exit 1
  6. fi
  7. }
  8. ensure_runtime_user() {
  9. local user_name="$1"
  10. id -u "$user_name" >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin "$user_name"
  11. }
  12. ensure_log_file() {
  13. local log_file="$1"
  14. local owner="$2"
  15. mkdir -p "$(dirname "$log_file")"
  16. touch "$log_file"
  17. chown "$owner":"$owner" "$log_file"
  18. }
  19. pid_matches_marker() {
  20. local pid="$1"
  21. local marker="$2"
  22. [[ "$pid" =~ ^[0-9]+$ ]] || return 1
  23. [[ -r "/proc/$pid/cmdline" ]] || return 1
  24. local cmdline
  25. cmdline="$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null || true)"
  26. [[ "$cmdline" == *"$marker"* ]]
  27. }
  28. find_pids_by_marker() {
  29. local marker="$1"
  30. local proc_path
  31. for proc_path in /proc/[0-9]*; do
  32. local pid="${proc_path##*/}"
  33. if pid_matches_marker "$pid" "$marker"; then
  34. printf '%s\n' "$pid"
  35. fi
  36. done
  37. }
  38. stop_pid() {
  39. local pid="$1"
  40. kill "$pid" 2>/dev/null || true
  41. for _ in {1..30}; do
  42. if ! kill -0 "$pid" 2>/dev/null; then
  43. return 0
  44. fi
  45. sleep 0.1
  46. done
  47. if kill -0 "$pid" 2>/dev/null; then
  48. kill -9 "$pid" 2>/dev/null || true
  49. fi
  50. }
  51. stop_pid_file() {
  52. local pid_file="$1"
  53. local marker="$2"
  54. local pid=""
  55. if [[ -f "$pid_file" ]]; then
  56. pid="$(cat "$pid_file" 2>/dev/null || true)"
  57. fi
  58. if pid_matches_marker "$pid" "$marker"; then
  59. stop_pid "$pid"
  60. else
  61. while IFS= read -r matched_pid; do
  62. [[ -n "$matched_pid" ]] || continue
  63. stop_pid "$matched_pid"
  64. done < <(find_pids_by_marker "$marker")
  65. fi
  66. rm -f "$pid_file"
  67. }
  68. wait_for_tcp_listen() {
  69. local host="$1"
  70. local port="$2"
  71. local timeout_sec="${3:-15}"
  72. local deadline=$((SECONDS + timeout_sec))
  73. local escaped_host="${host//./\\.}"
  74. local pattern="${escaped_host}:${port}( |$)"
  75. while (( SECONDS < deadline )); do
  76. if ss -H -lnt 2>/dev/null | grep -qE "$pattern"; then
  77. return 0
  78. fi
  79. sleep 0.2
  80. done
  81. return 1
  82. }
  83. start_python_service() {
  84. local runtime_user="$1"
  85. local workdir="$2"
  86. local pythonpath="$3"
  87. local log_file="$4"
  88. local pid_file="$5"
  89. local marker="$6"
  90. local argv_json="$7"
  91. local launcher='
  92. import json
  93. import os
  94. import subprocess
  95. import sys
  96. runtime_user, workdir, pythonpath, log_file, pid_file, marker, argv_json = sys.argv[1:]
  97. argv = json.loads(argv_json)
  98. env = os.environ.copy()
  99. env["PYTHONUNBUFFERED"] = "1"
  100. env["PYTHONPATH"] = pythonpath
  101. with open(log_file, "ab", buffering=0) as handle:
  102. proc = subprocess.Popen(
  103. argv,
  104. cwd=workdir,
  105. env=env,
  106. stdin=subprocess.DEVNULL,
  107. stdout=handle,
  108. stderr=subprocess.STDOUT,
  109. start_new_session=True,
  110. )
  111. print(proc.pid)
  112. '
  113. local pid
  114. pid="$(
  115. runuser -u "$runtime_user" -- python3 -c "$launcher" \
  116. "$runtime_user" "$workdir" "$pythonpath" "$log_file" "$pid_file" "$marker" "$argv_json"
  117. )"
  118. printf '%s\n' "$pid" > "$pid_file"
  119. printf '%s\n' "$pid"
  120. }
  121. ensure_nat_jump_absent() {
  122. local cmd="$1"
  123. local table="$2"
  124. local chain="$3"
  125. shift 3
  126. while "$cmd" -t "$table" -C "$chain" "$@" >/dev/null 2>&1; do
  127. "$cmd" -t "$table" -D "$chain" "$@" >/dev/null 2>&1 || break
  128. done
  129. }
  130. ensure_nat_chain_absent() {
  131. local cmd="$1"
  132. local chain="$2"
  133. if ! command -v "$cmd" >/dev/null 2>&1; then
  134. return 0
  135. fi
  136. if ! "$cmd" -t nat -S >/dev/null 2>&1; then
  137. return 0
  138. fi
  139. "$cmd" -t nat -F "$chain" 2>/dev/null || true
  140. "$cmd" -t nat -X "$chain" 2>/dev/null || true
  141. }