From 9d82ef1a225c7c35053d632eeac9bc66c7187915 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 19 Jul 2025 14:23:22 +0800 Subject: [PATCH] remove terminal.md --- terminal.md | 521 ---------------------------------------------------- 1 file changed, 521 deletions(-) delete mode 100644 terminal.md diff --git a/terminal.md b/terminal.md deleted file mode 100644 index 92a0973cb..000000000 --- a/terminal.md +++ /dev/null @@ -1,521 +0,0 @@ -# RustDesk Terminal Service Implementation - -## Overview - -The RustDesk terminal service provides remote terminal/shell access with support for multiple concurrent terminal sessions per connection. It features persistence support, allowing terminal sessions to survive connection drops and be resumed later. - -## Architecture - -### Client-Side (Flutter) - -#### Terminal Connection Management -- **TerminalConnectionManager** (`flutter/lib/desktop/pages/terminal_connection_manager.dart`) - - Manages one FFI instance per peer (shared across all terminal tabs) - - Tracks persistence settings per peer - - Handles connection reference counting - -#### Terminal Models -- **TerminalModel** (`flutter/lib/models/terminal_model.dart`) - - One instance per terminal tab - - Handles terminal I/O and display using xterm package - - Manages terminal state (opened, size, buffer) - -#### UI Components -- **TerminalTabPage** (`flutter/lib/desktop/pages/terminal_tab_page.dart`) - - Manages multiple terminal tabs - - Right-click menu for persistence toggle - - Keyboard shortcuts (Cmd/Ctrl+Shift+T for new terminal) - -### Server-Side (Rust) - -#### Terminal Service Structure -```rust -TerminalService { - conn_id: i32, - service_id: String, // "tmp_{uuid}" or "persist_{uuid}" - persist: bool, -} - -PersistentTerminalService { - service_id: String, - sessions: HashMap, // terminal_id -> session - next_terminal_id: i32, - created_at: Instant, - last_activity: Instant, -} - -TerminalSession { - terminal_id: i32, - pty_pair: PtyPair, - child: Box, - writer: Box, - reader: Box, - output_buffer: OutputBuffer, // For reconnection - rows: u16, - cols: u16, -} -``` - -## Message Protocol - -### Client → Server Messages - -1. **Open Terminal** -```protobuf -TerminalAction { - open: OpenTerminal { - terminal_id: i32, - rows: u32, - cols: u32, - } -} -``` - -2. **Send Input** -```protobuf -TerminalAction { - data: TerminalData { - terminal_id: i32, - data: bytes, - } -} -``` - -3. **Resize Terminal** -```protobuf -TerminalAction { - resize: ResizeTerminal { - terminal_id: i32, - rows: u32, - cols: u32, - } -} -``` - -4. **Close Terminal** -```protobuf -TerminalAction { - close: CloseTerminal { - terminal_id: i32, - force: bool, - } -} -``` - -### Server → Client Messages - -1. **Terminal Opened** -```protobuf -TerminalResponse { - opened: TerminalOpened { - terminal_id: i32, - success: bool, - message: string, - pid: u32, - } -} -``` - -2. **Terminal Output** -```protobuf -TerminalResponse { - data: TerminalData { - terminal_id: i32, - data: bytes, // Base64 encoded in Flutter - } -} -``` - -3. **Terminal Closed** -```protobuf -TerminalResponse { - closed: TerminalClosed { - terminal_id: i32, - exit_code: i32, - } -} -``` - -## Persistence Design - -### Service ID Convention -- **Temporary**: `"tmp_{uuid}"` - Cleaned up after idle timeout -- **Persistent**: `"persist_{uuid}"` - Survives disconnections - -### Persistence Flow -1. User right-clicks terminal tab → "Enable terminal persistence" -2. Client stores persistence preference in `TerminalConnectionManager` -3. New terminals created with appropriate service ID prefix -4. Service ID saved for future reconnection (TODO: implement storage) - -### Cleanup Rules -- **Temporary services (`tmp_`)**: - - Removed after 1 hour idle time - - Immediately removed when service loop exits - -- **Persistent services**: - - Removed after 2 hours idle time IF empty - - Survive connection drops - - Can be reconnected using saved service ID - -### Cleanup Implementation - -#### 1. **Automatic Background Cleanup** -```rust -// Runs every 5 minutes -fn ensure_cleanup_task() { - tokio::spawn(async { - let mut interval = tokio::time::interval(Duration::from_secs(300)); - loop { - interval.tick().await; - cleanup_inactive_services(); - } - }); -} -``` - -#### 2. **Cleanup Logic** -```rust -fn cleanup_inactive_services() { - let now = Instant::now(); - - for (service_id, service) in services.iter() { - // Temporary services: clean up after 1 hour idle - if service_id.starts_with("tmp_") && - now.duration_since(svc.last_activity) > SERVICE_IDLE_TIMEOUT { - to_remove.push(service_id); - } - // Persistent services: clean up after 2 hours IF empty - else if !service_id.starts_with("tmp_") && - svc.sessions.is_empty() && - now.duration_since(svc.last_activity) > SERVICE_IDLE_TIMEOUT * 2 { - to_remove.push(service_id); - } - } -} -``` - -#### 3. **Service Loop Exit Cleanup** -```rust -fn run(sp: EmptyExtraFieldService, _conn_id: i32, service_id: String) { - // Service loop - while sp.ok() { - // Read and send terminal outputs... - } - - // Clean up temporary services immediately on exit - if service_id.starts_with("tmp_") { - remove_service(&service_id); - } -} -``` - -#### 4. **Session Cleanup Within Service** -When a terminal is closed: -- PTY process is terminated -- Terminal session removed from service's HashMap -- Resources (file descriptors, buffers) are freed -- Service continues running for other terminals - -#### 5. **Connection Drop Behavior** -```rust -impl Drop for Connection { - fn drop(&mut self) { - if self.terminal { - // Unsubscribe from terminal service - server.subscribe(&service_name, self.inner.clone(), false); - } - } -} -``` -- Connection unsubscribes from service -- Service loop continues if other subscribers exist -- If no subscribers remain, `sp.ok()` returns false → service loop exits - -#### 6. **Activity Tracking** -`last_activity` is updated when: -- New terminal opened -- Input sent to terminal -- Terminal resized -- Output read from terminal -- Any terminal operation occurs - -#### 7. **Two-Phase Cleanup Process** -```rust -// Collect services to remove (while holding lock) -let mut to_remove = Vec::new(); -for (id, service) in services.iter() { - if should_remove(service) { - to_remove.push(id); - } -} - -// Remove services (after releasing lock) -drop(services); -for id in to_remove { - remove_service(&id); -} -``` -This prevents deadlock when removing services. - -## Key Features - -### Multiple Terminals per Connection -- Single FFI connection shared by all terminal tabs -- Each terminal has unique ID within the service -- Independent PTY sessions per terminal - -### Output Buffering -- Last 1MB of output buffered per terminal -- Allows showing recent history on reconnection -- Ring buffer with line-based storage - -### Cross-Platform Support -- **Unix/Linux/macOS**: Uses default shell from `$SHELL` or `/bin/bash` -- **Windows**: Uses `%COMSPEC%` or `cmd.exe` -- PTY implementation via `portable_pty` crate - -### Non-Blocking I/O -- PTY readers set to non-blocking mode (Unix) -- Output polled at ~33fps for responsive display -- Prevents blocking when no data available - -## Current Limitations - -1. **Service ID Storage**: Client doesn't persist service IDs yet -2. **Reconnection UI**: No UI to recover previous sessions -3. **Authentication**: No per-service authentication for reconnection -4. **Resource Limits**: No configurable limits on terminals per service - -## Future Enhancements - -1. **Proper Reconnection Flow**: - - Store service IDs in peer config - - UI to list and recover previous sessions - - Show buffered output on reconnection - -2. **Security**: - - Authentication token for service recovery - - Encryption of buffered output - - Access control per terminal - -3. **Advanced Features**: - - Terminal sharing between users - - Session recording/playback - - File transfer via terminal - - Custom shell/command configuration - -## Code Locations - -- **Server Implementation**: `src/server/terminal_service.rs` -- **Connection Handler**: `src/server/connection.rs` (handle_terminal_action) -- **Client Interface**: `src/ui_session_interface.rs` (terminal methods) -- **Flutter FFI**: `src/flutter_ffi.rs` (session_open_terminal, etc.) -- **Flutter Models**: `flutter/lib/models/terminal_model.dart` -- **Flutter UI**: `flutter/lib/desktop/pages/terminal_*.dart` - -## Usage - -1. **Start Terminal Session**: - - Click terminal icon or use Ctrl/Cmd+Shift+T - - Terminal opens with default shell - -2. **Enable Persistence**: - - Right-click any terminal tab - - Select "Enable terminal persistence" - - All terminals for that peer become persistent - -3. **Multiple Terminals**: - - Click "+" button or Ctrl/Cmd+Shift+T - - Each terminal is independent - -4. **Reconnection** (TODO): - - Connect to same peer - - Previous terminals automatically restored - - Recent output displayed - -## Implementation Issues & TODOs - -### Critical Missing Features - -1. **Service ID Storage & Recovery** - - Need to store service_id in peer config when persistence enabled - - Pass service_id in LoginRequest for reconnection - - Handle service_id in server login flow - - Return terminal list in LoginResponse - -2. **Protocol Extensions Needed** - ```protobuf - // In LoginRequest - message Terminal { - string service_id = 1; // For reconnection - bool persistent = 2; // Request persistence - } - - // In LoginResponse - message TerminalServiceInfo { - string service_id = 1; - repeated TerminalSessionInfo sessions = 2; - } - ``` - -3. **Terminal Recovery Flow** - - Add RecoverTerminal action to restore specific terminal - - Send buffered output on reconnection - - Handle terminal size on recovery - - UI to show available terminals - -### Current Design Issues - -1. **Service Pattern Mismatch** - - Terminal service forced into broadcast service pattern - - Should be direct connection resource, not shared service - - Complex routing through service registry unnecessary - -2. **Global State Management** - - TERMINAL_SERVICES static HashMap may cause issues - - No proper service discovery mechanism - - Cleanup task is global, not per-connection - -3. **Resource Limits Missing** - - No limit on terminals per service - - No limit on buffer size per terminal - - No limit on total services - - Could lead to resource exhaustion - -4. **Security Concerns** - - No authentication for service recovery - - Service IDs are predictable (just UUID) - - No encryption of buffered terminal output - - No access control between users - -### Performance Optimizations Needed - -1. **Output Reading** - - Currently polls at 33fps regardless of activity - - Should use event-driven I/O (epoll/kqueue) - - Batch small outputs to reduce messages - -2. **Buffer Management** - - Ring buffer could be more efficient - - Consider compression for stored output - - Implement smart truncation (keep last N complete lines) - -3. **Message Overhead** - - Each output chunk creates new protobuf message - - Could batch multiple terminal outputs - - Consider streaming protocol for continuous output - -### Platform-Specific Issues - -1. **Windows** - - ConPTY support needs testing - - Non-blocking I/O handled differently - - Shell detection could be improved - -2. **Mobile (Android/iOS)** - - Terminal feature disabled by conditional compilation - - Need to evaluate mobile terminal support - - Touch keyboard integration needed - -### Testing Requirements - -1. **Unit Tests Needed** - - Terminal service lifecycle - - Cleanup logic edge cases - - Buffer management - - Message serialization - -2. **Integration Tests** - - Multi-terminal scenarios - - Reconnection flows - - Cleanup timing - - Resource limits - -3. **Stress Tests** - - Many terminals per connection - - Large output volumes - - Rapid connect/disconnect - - Long-running sessions - -### Alternative Designs to Consider - -1. **Direct Terminal Management** - ```rust - // In Connection struct - terminals: HashMap, - - // No service pattern, direct management - async fn handle_terminal_action(&mut self, action) { - match action { - Open => self.open_terminal(), - Data => self.terminal_input(), - // etc - } - } - ``` - -2. **Actor-Based Design** - - Each terminal as an actor - - Message passing for I/O - - Better isolation and error handling - -3. **Session Manager Service** - - One global terminal manager - - Connections request terminals from manager - - Cleaner separation of concerns - -### Documentation Gaps - -1. **API Documentation** - - Document all public methods - - Add examples for common operations - - Document error conditions - -2. **Configuration** - - Document all timeouts and limits - - How to configure shell/terminal - - Platform-specific settings - -3. **Troubleshooting Guide** - - Common issues and solutions - - Debug logging interpretation - - Performance tuning - -### Future Feature Ideas - -1. **Advanced Terminal Features** - - Terminal sharing (multiple users, one terminal) - - Session recording and playback - - File transfer through terminal (zmodem) - - Custom color schemes - - Font configuration - -2. **Integration Features** - - SSH key forwarding - - Environment variable injection - - Working directory synchronization - - Shell integration (prompt markers, etc) - -3. **Management Features** - - Terminal session monitoring - - Usage statistics - - Audit logging - - Rate limiting - -### Refactoring Suggestions - -1. **Separate Concerns** - - Split terminal_service.rs into multiple files - - Separate PTY management from service logic - - Extract buffer management to own module - -2. **Improve Error Handling** - - Use proper error types, not strings - - Add error recovery mechanisms - - Better error reporting to client - -3. **Configuration Management** - - Make timeouts configurable - - Add feature flags for experimental features - - Environment-based configuration \ No newline at end of file