From 30ec739e8a6846486c4e6b8626fd856408e08581 Mon Sep 17 00:00:00 2001 From: kalzu rekku Date: Mon, 5 Aug 2024 22:12:43 +0300 Subject: [PATCH] POC state of core_daemon. With to example modules. --- .gitignore | 1 + core_daemon.py | 109 ++++++++++++++++++++++++++++++++++++++ daemon_cli.py | 95 +++++++++++++++++++++++++++++++++ modules/extra_commands.py | 25 +++++++++ modules/http_service.py | 37 +++++++++++++ 5 files changed, 267 insertions(+) create mode 100644 .gitignore create mode 100644 core_daemon.py create mode 100644 daemon_cli.py create mode 100644 modules/extra_commands.py create mode 100644 modules/http_service.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/core_daemon.py b/core_daemon.py new file mode 100644 index 0000000..b84b5e7 --- /dev/null +++ b/core_daemon.py @@ -0,0 +1,109 @@ +import os +import sys +import importlib +import socket +import json +import threading + +# Add the modules folder to the Python path +modules_path = os.path.join(os.path.dirname(__file__), 'modules') +sys.path.append(modules_path) + +class ModuleManager: + def __init__(self): + self.loaded_modules = {} + self.extra_commands = {} + + def load_module(self, module_name): + try: + # Try to import from the modules folder + module = importlib.import_module(f'modules.{module_name}') + self.loaded_modules[module_name] = module + if hasattr(module, 'initialize'): + module.initialize() + if hasattr(module, 'get_commands'): + new_commands = module.get_commands() + self.extra_commands.update(new_commands) + return True, f"Module '{module_name}' loaded and initialized successfully." + except ImportError as e: + return False, f"Error: Unable to load module '{module_name}'. {str(e)}" + + def unload_module(self, module_name): + if module_name in self.loaded_modules: + module = self.loaded_modules[module_name] + if hasattr(module, 'shutdown'): + module.shutdown() + if hasattr(module, 'get_commands'): + commands_to_remove = module.get_commands().keys() + for cmd in commands_to_remove: + self.extra_commands.pop(cmd, None) + del self.loaded_modules[module_name] + return True, f"Module '{module_name}' unloaded and shut down." + return False, f"Module '{module_name}' is not loaded." + + def list_modules(self): + return list(self.loaded_modules.keys()) + + def list_commands(self): + return list(self.extra_commands.keys()) + + def execute_command(self, command, args): + if command in self.extra_commands: + return True, self.extra_commands[command](args) + return False, "Command not found" + +class CoreDaemon: + def __init__(self, host='localhost', port=9999): + self.host = host + self.port = port + self.module_manager = ModuleManager() + + def start(self): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind((self.host, self.port)) + s.listen() + print(f"Core daemon listening on {self.host}:{self.port}") + while True: + conn, addr = s.accept() + threading.Thread(target=self.handle_client, args=(conn,)).start() + + def handle_client(self, conn): + with conn: + while True: + data = conn.recv(1024) + if not data: + break + command = json.loads(data.decode()) + response = self.process_command(command) + conn.sendall(json.dumps(response).encode()) + + def upgrade(self): + print("Upgrading core daemon...") + # Here you would typically download or copy the new version + # For this example, we'll just restart the script + os.execv(sys.executable, ['python'] + sys.argv) + + def process_command(self, command): + action = command.get('action') + if action == 'load': + success, message = self.module_manager.load_module(command.get('module')) + elif action == 'unload': + success, message = self.module_manager.unload_module(command.get('module')) + elif action == 'list': + modules = self.module_manager.list_modules() + success, message = True, modules + elif action == 'execute': + success, message = self.module_manager.execute_command(command.get('command'), command.get('args')) + elif action == 'upgrade': + self.upgrade() + success, message = True, "Upgrade initiated" + elif action == 'list_commands': + commands = self.module_manager.list_commands() + success, message = True, commands + else: + success, message = False, "Unknown command" + return {'success': success, 'message': message} + +if __name__ == "__main__": + daemon = CoreDaemon() + daemon.start() diff --git a/daemon_cli.py b/daemon_cli.py new file mode 100644 index 0000000..1da63e1 --- /dev/null +++ b/daemon_cli.py @@ -0,0 +1,95 @@ +import cmd +import socket +import json +import os + +class ModuleShell(cmd.Cmd): + intro = "Welcome to the Module Shell. Type 'help' for commands." + prompt = "(module_shell) " + + def __init__(self, host='localhost', port=9999): + super().__init__() + self.host = host + self.port = port + self.update_commands() + + def send_command(self, command): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((self.host, self.port)) + s.sendall(json.dumps(command).encode()) + return json.loads(s.recv(1024).decode()) + + def update_commands(self): + response = self.send_command({'action': 'list_commands'}) + if response['success']: + self.dynamic_commands = response['message'] + else: + self.dynamic_commands = [] + + def default(self, line): + parts = line.split() + command = parts[0] + args = ' '.join(parts[1:]) + response = self.send_command({'action': 'execute', 'command': command, 'args': args}) + if response['success']: + print(response['message']) + else: + print(f"Error: {response['message']}") + + def completenames(self, text, *ignored): + dotext = 'do_'+text + return [a[3:] for a in self.get_names() if a.startswith(dotext)] + \ + [cmd for cmd in self.dynamic_commands if cmd.startswith(text)] + + def do_load(self, arg): + """Load a module: load """ + response = self.send_command({'action': 'load', 'module': arg}) + print(response['message']) + self.update_commands() # Update commands after loading a new module + + def do_unload(self, arg): + """Unload a module: unload """ + response = self.send_command({'action': 'unload', 'module': arg}) + print(response['message']) + self.update_commands() # Update commands after unloading a module + + def do_list(self, arg): + """List all loaded modules""" + response = self.send_command({'action': 'list'}) + modules = response['message'] + if modules: + print("Loaded modules:") + for module in modules: + print(f"- {module}") + else: + print("No modules are currently loaded.") + + def do_list_available(self, arg): + """List available modules in the modules folder""" + modules_path = os.path.join(os.path.dirname(__file__), 'modules') + available_modules = [f[:-3] for f in os.listdir(modules_path) if f.endswith('.py') and not f.startswith('__')] + print("Available modules:") + for module in available_modules: + print(f"- {module}") + + def complete_load(self, text, line, begidx, endidx): + modules_path = os.path.join(os.path.dirname(__file__), 'modules') + available_modules = [f[:-3] for f in os.listdir(modules_path) if f.endswith('.py') and not f.startswith('__')] + return [m for m in available_modules if m.startswith(text)] + + + def do_upgrade(self, arg): + """Upgrade the core daemon""" + response = self.send_command({'action': 'upgrade'}) + print(response['message']) + + def do_exit(self, arg): + """Exit the shell""" + print("Exiting...") + return True + +def main(): + ModuleShell().cmdloop() + +if __name__ == "__main__": + main() diff --git a/modules/extra_commands.py b/modules/extra_commands.py new file mode 100644 index 0000000..9a45647 --- /dev/null +++ b/modules/extra_commands.py @@ -0,0 +1,25 @@ +class ExtraCommands: + @staticmethod + def do_greet(args): + """Greet someone: greet """ + name = args if args else "World" + return f"Hello, {name}!" + + @staticmethod + def do_reverse(args): + """Reverse a string: reverse """ + return args[::-1] + + @staticmethod + def do_count(args): + """Count words in a string: count """ + return str(len(args.split())) + +commands = { + 'greet': ExtraCommands.do_greet, + 'reverse': ExtraCommands.do_reverse, + 'count': ExtraCommands.do_count, +} + +def get_commands(): + return commands \ No newline at end of file diff --git a/modules/http_service.py b/modules/http_service.py new file mode 100644 index 0000000..7b36a94 --- /dev/null +++ b/modules/http_service.py @@ -0,0 +1,37 @@ +from http.server import HTTPServer, BaseHTTPRequestHandler +import threading + +class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-type', 'text/plain') + self.end_headers() + self.wfile.write(b'Hello, World!') + +class HTTPService: + def __init__(self, host='localhost', port=8000): + self.host = host + self.port = port + self.server = None + self.thread = None + + def start(self): + self.server = HTTPServer((self.host, self.port), SimpleHTTPRequestHandler) + self.thread = threading.Thread(target=self.server.serve_forever) + self.thread.start() + print(f"HTTP service started on http://{self.host}:{self.port}") + + def stop(self): + if self.server: + self.server.shutdown() + self.server.server_close() + self.thread.join() + print("HTTP service stopped") + +http_service = HTTPService() + +def initialize(): + http_service.start() + +def shutdown(): + http_service.stop()