import subprocess import threading import time import requests import os import pymysql import logging import bcrypt import configparser from functools import partial CONFIG_PATH = "/app/config.ini" #CONFIG_PATH = "../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"] } LICENSE_SERVER = "https://sikkerdata.com/validate_license.php" LICENSE_KEY = config["license"]["license_key"] with open("/etc/machine-id", "r") as f: get_HWID = f.read().strip() def validate_license(): try: hwid = get_HWID response = requests.post(LICENSE_SERVER, json={ "license_key": LICENSE_KEY, "hwid": hwid }, timeout=5) logging.info(f"[MAIN] License server response: {response.text}") if response.status_code == 200: result = response.json() if result.get("status") == "valid": logging.info("[MAIN] License validated") return True else: logging.info(f"[MAIN] License check failed: {result.get('message')}") return False else: logging.info(f"[MAIN] License server error: {response.status_code}") return False except Exception as e: logging.info(f"[MAIN] License check exception: {e}") return False def setup_database(db_config): conn = pymysql.connect(**db_config) cursor = conn.cursor() def table_exists(name): cursor.execute("SHOW TABLES LIKE %s", (name,)) return cursor.fetchone() is not None # Create `users` table if missing if not table_exists('users'): print("[DB] Creating 'users' table...") cursor.execute(""" CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, full_name VARCHAR(100) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, phone VARCHAR(20), password_hash VARCHAR(255) NOT NULL, totp_secret VARCHAR(64), user_role ENUM('Admin','Operator','Installer','Client') DEFAULT 'Client', access_group VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; """) # Create `signals` table if missing if not table_exists('signals'): print("[DB] Creating 'signals' table...") cursor.execute(""" CREATE TABLE signals ( id INT AUTO_INCREMENT PRIMARY KEY, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, protocol VARCHAR(10) NOT NULL, raw_message TEXT NOT NULL, account VARCHAR(20), sequence VARCHAR(10), line_number VARCHAR(10), event_code VARCHAR(10), partition VARCHAR(10), zone VARCHAR(20), signal_time DATETIME, source_ip VARCHAR(45), signal_text VARCHAR(255), v TEXT, x TEXT, y TEXT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; """) if not table_exists('receivers'): print("[DB] Creating 'receivers' table...") cursor.execute(""" CREATE TABLE receivers ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, type ENUM('SIA-DC09') NOT NULL DEFAULT 'SIA-DC09', tcpport INT NOT NULL, enabled BOOLEAN DEFAULT 1 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; """) cursor.execute("""INSERT INTO receivers (name, type, tcpport, enabled) VALUES ('Main Receiver', 'SIA-DC09', 9000, 1);""") # If no users exist, create default admin cursor.execute("SELECT COUNT(*) FROM users") (user_count,) = cursor.fetchone() if user_count == 0: print("[DB] Inserting default admin user...") default_password = bcrypt.hashpw("admin123".encode(), bcrypt.gensalt()).decode('utf-8') cursor.execute(""" INSERT INTO users (username, full_name, email, password_hash, user_role) VALUES (%s, %s, %s, %s, %s) """, ("admin", "Admin", "admin@example.com", default_password, "Admin")) print("[DB] Admin created with username 'admin' and password 'admin123'") conn.commit() cursor.close() conn.close() running_services = {} def watchdog(): bin_path = "/app/watchdog.bin" def run_watchdog(): while True: cmd = [bin_path] logging.debug(f"[MAIN] Starting Watchdog...") process = subprocess.Popen(cmd) running_services["Watchdog"] = process process.wait() logging.debug(f"[MAIN] Watchdog finished. Restarting in 5s...") time.sleep(5) thread = threading.Thread(target=run_watchdog, name="Watchdog", daemon=True) thread.start() def is_watchdog_running(): return any(thread.name == "Watchdog" and thread.is_alive() for thread in threading.enumerate()) def main(): logging.info("[MAIN] Starting main function") try: if not validate_license(): logging.info("[MAIN] Shutting down due to invalid license.") return setup_thread = threading.Thread(target=partial(setup_database, DB_CONFIG), daemon=True) setup_thread.start() setup_thread.join() # Wait for DB setup before continuing while True: try: logging.debug(f"[MAIN] Main is running") if is_watchdog_running(): logging.debug("[MAIN] Watchdog thread is already running") else: watchdog() time.sleep(10) except Exception as e: logging.exception(f"[MAIN] Exception while logging thread status: {e}") except KeyboardInterrupt: logging.info("Graceful shutdown requested") def wait_for_mariadb(host, port, user, password, database, timeout=5): logging.info(f"[WAIT] Waiting for MariaDB at {host}:{port} to become ready...") while True: try: conn = pymysql.connect( host=host, port=port, user=user, password=password, database=database, connect_timeout=timeout ) with conn.cursor() as cursor: cursor.execute("SELECT 1;") result = cursor.fetchone() if result: logging.info("[WAIT] MariaDB is ready.") conn.close() break except pymysql.MySQLError as e: logging.warning(f"[WAIT] MariaDB not ready yet: {e}") time.sleep(timeout) if __name__ == "__main__": wait_for_mariadb( host=config["database"]["host"], port=3306, user=config["database"]["user"], password=config["database"]["password"], database=config["database"]["database"] ) main()