import subprocess import threading import time import pymysql import logging import configparser import psutil CONFIG_PATH = "/app/config.ini" config = configparser.ConfigParser() config.read(CONFIG_PATH) loglevel_str = config["log"]["level"].upper() loglevel = getattr(logging, loglevel_str, logging.INFO) logging.basicConfig(level=loglevel) DB_CONFIG = { 'host': config["database"]["host"], 'user': config["database"]["user"], 'password': config["database"]["password"], 'database': config["database"]["database"] } running_services = {} def get_process_on_port(port): for conn in psutil.net_connections(kind='inet'): if conn.laddr.port == port and conn.status == psutil.CONN_LISTEN: pid = conn.pid if pid is None: continue try: proc = psutil.Process(pid) return { 'pid': pid, 'name': proc.name(), 'cmdline': proc.cmdline() } except (psutil.NoSuchProcess, psutil.AccessDenied): continue return None def initialize_running_services_from_db(db_config): try: conn = pymysql.connect(**db_config) cursor = conn.cursor(pymysql.cursors.DictCursor) cursor.execute("SELECT name, tcpport FROM receivers;") rows = cursor.fetchall() for row in rows: name = row['name'] port = row['tcpport'] proc_info = get_process_on_port(port) if proc_info: logging.info(f"[INIT] Found existing service '{name}' on port {port}, PID={proc_info['pid']}, Cmd={proc_info['cmdline']}") running_services[name] = { "pid": proc_info["pid"], "cmdline": proc_info["cmdline"] } cursor.close() conn.close() except Exception as e: logging.exception("[INIT] Failed to detect existing services") def watchdog(db_config): global running_services while True: try: thread_conn = pymysql.connect(**db_config) cursor = thread_conn.cursor(pymysql.cursors.DictCursor) cursor.execute("SELECT name, type, tcpport, enabled FROM receivers;") rows = cursor.fetchall() cursor.close() thread_conn.close() for row in rows: name = row["name"] receiver_type = row["type"] port = row["tcpport"] enabled = row["enabled"] if receiver_type != "SIA-DC09": continue if enabled == 1 and name not in running_services: binary_path = "/app/tcp_sia_server.bin" logging.info(f"[WATCHDOG] Starting receiver '{name}' on port {port}") start_service(name, binary_path, port) elif enabled == 0: proc_info = running_services.get(name) if proc_info and isinstance(proc_info, subprocess.Popen): logging.info(f"[WATCHDOG] Stopping disabled receiver '{name}' (started by script)") proc_info.terminate() proc_info.wait() del running_services[name] else: # Kill external process using psutil port = row["tcpport"] try: for conn in psutil.net_connections(kind='inet'): if conn.laddr.port == port and conn.status == psutil.CONN_LISTEN: pid = conn.pid if pid: logging.warning( f"[WATCHDOG] Force killing external process on port {port} (PID {pid}) for receiver '{name}'") psutil.Process(pid).kill() if name in running_services: del running_services[name] except Exception as e: logging.exception(f"[WATCHDOG] Failed to kill external process for receiver '{name}'") log_thread_status() time.sleep(3) except Exception as e: logging.exception(f"[WATCHDOG] Error during check: {e}") def start_service(name, binary_path, port=None): def run(): cmd = [binary_path] if port: cmd.append(str(port)) logging.info(f"[WATCHDOG] Starting {name} on port {port}...") process = subprocess.Popen(cmd) running_services[name] = { "popen": process } process.wait() logging.warning(f"[WATCHDOG] {name} exited.") thread = threading.Thread(target=run, daemon=True) thread.start() def log_thread_status(): running = list(running_services.keys()) logging.debug(f"[STATUS] Running services: {running if running else 'None'}") if __name__ == "__main__": initialize_running_services_from_db(DB_CONFIG) logging.debug(f"[INIT] Active running_services: {list(running_services.keys())}") watchdog(DB_CONFIG)