188 lines
No EOL
6.7 KiB
Python
188 lines
No EOL
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
apt-ostree Daemon - New dbus-next implementation with proper decoupling
|
|
Atomic package management system for Debian/Ubuntu inspired by rpm-ostree
|
|
"""
|
|
|
|
import asyncio
|
|
import argparse
|
|
import logging
|
|
import signal
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
# Add the parent directory to Python path for imports
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from core.daemon import AptOstreeDaemon
|
|
from daemon.interfaces.sysroot_interface import AptOstreeSysrootInterface, AptOstreeOSInterface
|
|
from dbus_next.aio import MessageBus
|
|
from dbus_next import BusType
|
|
|
|
class AptOstreeNewDaemon:
|
|
"""New daemon using dbus-next implementation with proper decoupling"""
|
|
|
|
def __init__(self, pid_file: str = None, config: dict = None):
|
|
self.pid_file = pid_file
|
|
self.config = config or {}
|
|
self.running = False
|
|
self.logger = logging.getLogger('daemon')
|
|
|
|
# Core daemon instance (pure Python, no D-Bus dependencies)
|
|
self.core_daemon: Optional[AptOstreeDaemon] = None
|
|
|
|
# D-Bus components
|
|
self.bus: Optional[MessageBus] = None
|
|
self.sysroot_interface: Optional[AptOstreeSysrootInterface] = None
|
|
self.os_interface: Optional[AptOstreeOSInterface] = None
|
|
|
|
def _write_pid_file(self):
|
|
"""Write PID to file"""
|
|
if self.pid_file:
|
|
try:
|
|
with open(self.pid_file, 'w') as f:
|
|
f.write(str(os.getpid()))
|
|
os.chmod(self.pid_file, 0o644)
|
|
except Exception as e:
|
|
print(f"Failed to write PID file: {e}", file=sys.stderr)
|
|
|
|
def _remove_pid_file(self):
|
|
"""Remove PID file"""
|
|
if self.pid_file and os.path.exists(self.pid_file):
|
|
try:
|
|
os.unlink(self.pid_file)
|
|
except Exception as e:
|
|
print(f"Failed to remove PID file: {e}", file=sys.stderr)
|
|
|
|
def _setup_signal_handlers(self):
|
|
"""Setup signal handlers for graceful shutdown"""
|
|
def signal_handler(signum, frame):
|
|
self.logger.info(f"Received signal {signum}, shutting down...")
|
|
self.running = False
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
|
|
async def _setup_dbus(self):
|
|
"""Setup D-Bus connection and interfaces"""
|
|
try:
|
|
# Connect to system bus
|
|
self.bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
|
|
|
|
# Create D-Bus interfaces with core daemon instance
|
|
self.sysroot_interface = AptOstreeSysrootInterface(self.core_daemon)
|
|
self.os_interface = AptOstreeOSInterface(self.core_daemon)
|
|
|
|
# Export interfaces
|
|
self.bus.export("/org/debian/aptostree1/Sysroot", self.sysroot_interface)
|
|
self.bus.export("/org/debian/aptostree1/OS/default", self.os_interface)
|
|
|
|
# Request D-Bus name
|
|
await self.bus.request_name("org.debian.aptostree1")
|
|
|
|
self.logger.info("D-Bus interfaces exported successfully")
|
|
return True
|
|
except Exception as e:
|
|
self.logger.error(f"Failed to setup D-Bus: {e}")
|
|
return False
|
|
|
|
async def _cleanup_dbus(self):
|
|
"""Cleanup D-Bus connection"""
|
|
if self.bus:
|
|
try:
|
|
self.bus.disconnect()
|
|
self.logger.info("D-Bus connection closed")
|
|
except Exception as e:
|
|
self.logger.error(f"Error closing D-Bus connection: {e}")
|
|
|
|
async def run(self):
|
|
"""Run the daemon with proper decoupling"""
|
|
try:
|
|
# Write PID file if specified
|
|
if self.pid_file:
|
|
self._write_pid_file()
|
|
|
|
# Setup signal handlers
|
|
self._setup_signal_handlers()
|
|
|
|
self.logger.info("Starting apt-ostree daemon (dbus-next implementation)")
|
|
|
|
# Initialize core daemon (pure Python logic)
|
|
self.core_daemon = AptOstreeDaemon(self.config, self.logger)
|
|
if not await self.core_daemon.initialize():
|
|
self.logger.error("Failed to initialize core daemon")
|
|
return 1
|
|
|
|
# Setup D-Bus interfaces (thin wrapper around core daemon)
|
|
if not await self._setup_dbus():
|
|
self.logger.error("Failed to setup D-Bus interfaces")
|
|
return 1
|
|
|
|
self.running = True
|
|
self.logger.info("Daemon started successfully")
|
|
|
|
# Keep the daemon running with proper signal handling
|
|
try:
|
|
while self.running:
|
|
await asyncio.sleep(1) # Check every second instead of waiting forever
|
|
except KeyboardInterrupt:
|
|
self.logger.info("Received interrupt signal")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Daemon error: {e}")
|
|
return 1
|
|
finally:
|
|
# Cleanup in reverse order
|
|
await self._cleanup_dbus()
|
|
if self.core_daemon:
|
|
await self.core_daemon.shutdown()
|
|
self._remove_pid_file()
|
|
self.running = False
|
|
|
|
return 0
|
|
|
|
def parse_arguments():
|
|
"""Parse command line arguments"""
|
|
parser = argparse.ArgumentParser(description='apt-ostree System Management Daemon (dbus-next)')
|
|
parser.add_argument('--daemon', action='store_true',
|
|
help='Run as daemon (default behavior)')
|
|
parser.add_argument('--pid-file', type=str,
|
|
help='Write PID to specified file')
|
|
parser.add_argument('--foreground', action='store_true',
|
|
help='Run in foreground (for debugging)')
|
|
parser.add_argument('--config', type=str,
|
|
help='Path to configuration file')
|
|
return parser.parse_args()
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
args = parse_arguments()
|
|
|
|
# Check if running as root (required for system operations)
|
|
if os.geteuid() != 0:
|
|
print("apt-ostree daemon must be run as root", file=sys.stderr)
|
|
return 1
|
|
|
|
# Setup basic logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
|
|
# Load configuration (placeholder for now)
|
|
config = {
|
|
'daemon.auto_update_policy': 'none',
|
|
'daemon.idle_exit_timeout': 0,
|
|
'sysroot.path': '/',
|
|
'sysroot.test_mode': True
|
|
}
|
|
|
|
daemon = AptOstreeNewDaemon(pid_file=args.pid_file, config=config)
|
|
|
|
# Run the daemon
|
|
return asyncio.run(daemon.run())
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |