/** * Main server application */ import { networkInterfaces } from 'node:os'; import { loadServerConfig } from '../config/manager.js'; import { TCPServer } from '../protocol/socket.js'; import { verifySystemRequirements } from '../system/permissions.js'; import { LogLevel, logger } from '../utils/logger.js'; import { ConnectionManager } from './connection-manager.js'; import { DeviceManager } from './device-manager.js'; /** * Get the local IP address (prefer non-loopback IPv4) */ function getLocalIPAddress(): string { const nets = networkInterfaces(); for (const name of Object.keys(nets)) { const netInfo = nets[name]; if (!netInfo) continue; for (const net of netInfo) { // Skip loopback and non-IPv4 addresses if (net.family === 'IPv4' && !net.internal) { return net.address; } } } return '127.0.0.1'; } /** * Start the USB/IP supervisor server */ export async function startServer(configPath?: string): Promise { try { logger.info('Starting USB/IP Supervisor Server...'); // Verify system requirements const requirements = await verifySystemRequirements(); if (!requirements.usbipInstalled) { logger.error('Cannot start server: usbip is not installed'); process.exit(1); } // Load configuration const config = await loadServerConfig(configPath); // Set log level if (config.logLevel) { const levelMap = { debug: LogLevel.DEBUG, info: LogLevel.INFO, warn: LogLevel.WARN, error: LogLevel.ERROR, }; logger.setLevel(levelMap[config.logLevel]); } // Set log file if configured if (config.logFile) { logger.setLogFile(config.logFile); } logger.info(`Loaded configuration with ${config.devices.length} devices`); // Initialize TCP server const tcpServer = new TCPServer(config.listenAddress, config.listenPort); await tcpServer.start(); // Initialize device manager const deviceManager = new DeviceManager(config.devices); // Determine server address for clients to connect to // If listening on 0.0.0.0, clients need the actual server IP // For now, use listenAddress if specific, otherwise let clients use their configured address const serverAddress = config.listenAddress === '0.0.0.0' ? getLocalIPAddress() : config.listenAddress; // Initialize connection manager BEFORE starting device manager // This ensures state change handlers are registered before devices are bound const connectionManager = new ConnectionManager(tcpServer, deviceManager, serverAddress); // Now start device manager - this will trigger state changes that connectionManager can handle await deviceManager.start(); // Start usbipd daemon to expose bound devices const { startUsbipd } = await import('../usbip/commands.js'); const usbipdStarted = await startUsbipd(); if (!usbipdStarted) { logger.warn('Failed to start usbipd daemon - clients will not be able to attach devices'); } logger.info('✓ Server started successfully'); logger.info(`Listening on ${config.listenAddress}:${config.listenPort}`); logger.info(`Managing ${config.devices.length} device(s)`); // Handle shutdown gracefully const shutdown = async () => { logger.info('Shutting down server...'); await deviceManager.stop(); await tcpServer.stop(); // Stop usbipd daemon const { stopUsbipd } = await import('../usbip/commands.js'); await stopUsbipd(); logger.info('Server stopped'); process.exit(0); }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); } catch (error) { logger.error(`Failed to start server: ${error}`); process.exit(1); } }