import rsgdnd.utils as utils import rsgdnd.server as server import threading import importlib import time import json import sys import os services = {} service_defs = {} services_mtime = 0 services_running = False services_lock = threading.Lock() def get_callback(self, in_path, response): global services_lock in_path = in_path.replace("\\", "/") utils.log("==================") utils.log(f"server: in coming path '{in_path}'") utils.log("==================") # pass through handled = [] with services_lock: for k, service in services.items(): callback = "get_callback" callback = service[callback] if callback in service else None if callback and callback(self, in_path, response): handled.append(k) if len(handled) > 1: utils.log(f"server: {in_path} handled by multiple services, is that intended? "+",".join(handled)) return len(handled) != 0 def post_callback(self, in_path, data, response): global services_lock in_path = in_path.replace("\\", "/") utils.log("==================") utils.log(f"server: in coming path '{in_path}'") utils.log("==================") # pass through handled = [] with services_lock: for k, service in services.items(): callback = "post_callback" callback = service[callback] if callback in service else None if callback and callback(self, in_path, data, response): handled.append(k) if len(handled) > 1: utils.log(f"server: {in_path} handled by multiple services, is that intended? "+",".join(handled)) return len(handled) != 0 def service_update(): global services global service_defs global services_mtime global services_running services_running = True while services_running: # check if service defs have updated if services_mtime != os.stat("services.json").st_mtime: services_mtime = os.stat("services.json").st_mtime do_update = False with open("services.json") as serv_json: try: service_defs = json.loads(serv_json.read()) do_update = True except Exception as error: utils.log("Failed trying to open parse services.json") utils.log(error) if do_update: with services_lock: # stop any services that shouldn't be running anymore removals = [] for k, v in services.items(): try: if k not in service_defs: if "stop" in v and callable(v["stop"]): utils.log(f"server: stopping '{k}'") v["stop"]() removals.append(k) except Exception as error: utils.log("Failed trying to stop services") utils.log(error) for k in removals: utils.log(f"server: removing '{k}' from the service list") del services[k] # start services for k, v in service_defs.items(): if k not in services: try: if k in sys.modules: importlib.reload(sys.modules[k]) new_service = importlib.import_module(k) services[k] = { "module": new_service } for id in v: if hasattr(new_service, v[id]): services[k][id] = getattr(new_service, v[id]) else: services[k][id] = None if not any([id in services[k] for id in ["get_callback", "post_callback", "start", "stop"]]): raise ValueError(k+" doesn't define any of the service interfaces.") for func in [services[k][id] for id in ["get_callback", "post_callback", "start", "stop"] if id in services[k]]: if not callable(func): raise ValueError(k+" '"+func.__name__+"' is not callable") utils.log(f"server: added '{k}' to the service list") if "start" in services[k] and callable(services[k]["start"]): utils.log(f"server: starting '{k}'") services[k]["start"]() except Exception as error: utils.log("Failed trying to start services") utils.log(error) if k in services: del services[k] time.sleep(1) @utils.trywrap() def main(): global services_running update = threading.Thread(target=service_update) update.start() server.run_whitelist(get_callback=get_callback, post_callback=post_callback) services_running = False update.join() pass if __name__ == "__main__": main()