core_daemon/core_daemon.py

196 lines
8.3 KiB
Python
Raw Normal View History

import os
import time
import sys
import importlib
import socket
import json
import threading
import logging
import traceback
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class ModuleManager:
def __init__(self, module_dirs):
self.module_dirs = module_dirs
self.loaded_modules = {}
self.extra_commands = {}
self._update_sys_path()
def _update_sys_path(self):
for dir in self.module_dirs:
full_path = os.path.abspath(dir)
if full_path not in sys.path:
sys.path.append(full_path)
logging.info(f"Added {full_path} to sys.path")
def add_module_dir(self, new_dir):
try:
full_path = os.path.abspath(new_dir)
if full_path not in self.module_dirs:
self.module_dirs.append(full_path)
self._update_sys_path()
return True, f"Added module directory: {full_path}"
return False, f"Module directory already exists: {full_path}"
except Exception as e:
logging.error(f"Error adding module directory: {str(e)}")
return False, f"Error adding module directory: {str(e)}"
def load_module(self, module_name):
for dir in self.module_dirs:
try:
module = importlib.import_module(f'{os.path.basename(dir)}.{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)
logging.info(f"Module '{module_name}' loaded successfully from {dir}.")
return True, f"Module '{module_name}' loaded and initialized successfully from {dir}."
except ImportError:
continue
except Exception as e:
logging.error(f"Error loading module '{module_name}' from {dir}: {str(e)}")
return False, f"Error loading module '{module_name}' from {dir}: {str(e)}"
return False, f"Error: Unable to load module '{module_name}' from any of the module directories."
def unload_module(self, module_name):
if module_name in self.loaded_modules:
try:
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]
logging.info(f"Module '{module_name}' unloaded successfully.")
return True, f"Module '{module_name}' unloaded and shut down."
except Exception as e:
logging.error(f"Error unloading module '{module_name}': {str(e)}")
return False, f"Error unloading module '{module_name}': {str(e)}"
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:
try:
result = self.extra_commands[command](args)
return True, result
except Exception as e:
logging.error(f"Error executing command '{command}': {str(e)}")
return False, f"Error executing command '{command}': {str(e)}"
return False, "Command not found"
class CoreDaemon:
def __init__(self, host='localhost', port=9999, module_dirs=None):
self.host = host
self.port = port
self.module_dirs = module_dirs or ['modules']
self.module_manager = ModuleManager(self.module_dirs)
def start(self):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((self.host, self.port))
s.listen()
logging.info(f"Core daemon listening on {self.host}:{self.port}")
while True:
conn, addr = s.accept()
threading.Thread(target=self.handle_client, args=(conn,)).start()
except Exception as e:
logging.error(f"Error starting core daemon: {str(e)}")
sys.exit(1)
def shutdown(self):
logging.info("Shutting down core daemon...")
return {'success': True, 'message': 'Core daemon is shutting down'}
def _delayed_shutdown(self):
time.sleep(1) # Give time for the response to be sent
os._exit(0)
def handle_client(self, conn):
with conn:
while True:
try:
data = conn.recv(1024)
if not data:
break
command = json.loads(data.decode())
response = self.process_command(command)
conn.sendall(json.dumps(response).encode())
except json.JSONDecodeError:
logging.error("Received invalid JSON data")
conn.sendall(json.dumps({'success': False, 'message': 'Invalid command format'}).encode())
except Exception as e:
logging.error(f"Error handling client request: {str(e)}")
conn.sendall(json.dumps({'success': False, 'message': 'Internal server error'}).encode())
def upgrade(self):
logging.info("Upgrading core daemon...")
try:
# 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)
except Exception as e:
logging.error(f"Error during upgrade: {str(e)}")
return False, f"Upgrade failed: {str(e)}"
def process_command(self, command):
action = command.get('action')
try:
if action == 'load':
return self.module_manager.load_module(command.get('module'))
elif action == 'unload':
return self.module_manager.unload_module(command.get('module'))
elif action == 'list':
modules = self.module_manager.list_modules()
return True, modules
elif action == 'execute':
return self.module_manager.execute_command(command.get('command'), command.get('args'))
elif action == 'upgrade':
return self.upgrade()
elif action == 'list_commands':
commands = self.module_manager.list_commands()
return True, commands
elif action == 'add_module_dir':
return self.module_manager.add_module_dir(command.get('dir'))
elif action == 'shutdown':
response = self.shutdown()
# After sending the response, exit the program
threading.Thread(target=self._delayed_shutdown).start()
return response
else:
return False, "Unknown command"
except Exception as e:
logging.error(f"Error processing command {action}: {str(e)}")
return False, f"Error processing command {action}: {str(e)}"
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Core Daemon with dynamic module loading")
parser.add_argument('--host', default='localhost', help='Host to bind the daemon to')
parser.add_argument('--port', type=int, default=9999, help='Port to bind the daemon to')
parser.add_argument('--module-dirs', nargs='*', default=['modules'], help='Directories to load modules from')
args = parser.parse_args()
try:
daemon = CoreDaemon(host=args.host, port=args.port, module_dirs=args.module_dirs)
daemon.start()
except Exception as e:
logging.critical(f"Critical error in core daemon: {str(e)}")
logging.debug(traceback.format_exc())
sys.exit(1)