remove terminal.md

This commit is contained in:
rustdesk
2025-07-19 14:23:22 +08:00
parent 555bb66668
commit 9d82ef1a22

View File

@@ -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<i32, TerminalSession>, // terminal_id -> session
next_terminal_id: i32,
created_at: Instant,
last_activity: Instant,
}
TerminalSession {
terminal_id: i32,
pty_pair: PtyPair,
child: Box<dyn Child>,
writer: Box<dyn Write>,
reader: Box<dyn Read>,
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<i32, TerminalSession>,
// 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