Fix web terminal issues: improve xterm.js configuration, fix PTY setup, and...

Fix web terminal issues: improve xterm.js configuration, fix PTY setup, and resolve output alignment

- Enhanced xterm.js terminal configuration with proper theme, cursor, and font settings
- Fixed PTY setup in wssshd.py for better subprocess connection
- Improved terminal output formatting and line ending handling
- Resolved terminal connection and data flow issues
- Added proper terminal size configuration (80x24)
- Simplified input handling for better SSH compatibility
parent 039df42f
...@@ -45,10 +45,27 @@ function connect() { ...@@ -45,10 +45,27 @@ function connect() {
return; return;
} }
// Initialize xterm // Initialize xterm with proper configuration
if (!term) { if (!term) {
term = new Terminal(); term = new Terminal({
cursorBlink: true,
cursorStyle: 'block',
fontSize: 14,
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace',
theme: {
background: '#1e1e1e',
foreground: '#f8f8f2',
cursor: '#f8f8f2',
cursorAccent: '#1e1e1e',
selection: 'rgba(248, 248, 242, 0.3)'
},
allowTransparency: true,
scrollback: 1000,
tabStopWidth: 4,
convertEol: true
});
term.open(document.getElementById('terminal')); term.open(document.getElementById('terminal'));
term.focus();
} }
term.write('Connecting to ' + username + '@{{ client_id }}...\r\n'); term.write('Connecting to ' + username + '@{{ client_id }}...\r\n');
...@@ -74,8 +91,8 @@ function connect() { ...@@ -74,8 +91,8 @@ function connect() {
term.write('Launching: ' + data.command + '\r\n'); term.write('Launching: ' + data.command + '\r\n');
} }
term.write('Connected successfully!\r\n$ '); term.write('Connected successfully!\r\n$ ');
// Start polling for data // Start polling for data with shorter interval for better responsiveness
pollInterval = setInterval(pollData, 500); pollInterval = setInterval(pollData, 100);
} else { } else {
term.write('Error: ' + data.error + '\r\n'); term.write('Error: ' + data.error + '\r\n');
disconnect(); disconnect();
...@@ -86,9 +103,11 @@ function connect() { ...@@ -86,9 +103,11 @@ function connect() {
disconnect(); disconnect();
}); });
// Handle input // Handle input - let SSH server handle echoing
term.onData(data => { term.onData(data => {
if (!connected || !requestId) return; if (!connected || !requestId) return;
// Send data to server
fetch('/terminal/{{ client_id }}/data', { fetch('/terminal/{{ client_id }}/data', {
method: 'POST', method: 'POST',
headers: { headers: {
...@@ -132,8 +151,12 @@ function pollData() { ...@@ -132,8 +151,12 @@ function pollData() {
.then(response => response.text()) .then(response => response.text())
.then(data => { .then(data => {
if (data) { if (data) {
term.write(data); // Ensure proper line ending handling
term.write(data.replace(/\n/g, '\r\n'));
} }
})
.catch(error => {
console.error('Polling error:', error);
}); });
} }
......
...@@ -31,6 +31,9 @@ import uuid ...@@ -31,6 +31,9 @@ import uuid
import subprocess import subprocess
import pty import pty
import select import select
import fcntl
import termios
import stat
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
...@@ -69,6 +72,72 @@ class User(UserMixin, db.Model): ...@@ -69,6 +72,72 @@ class User(UserMixin, db.Model):
def load_user(user_id): def load_user(user_id):
return db.session.get(User, int(user_id)) return db.session.get(User, int(user_id))
def openpty_with_fallback():
"""Open a PTY with fallback to different device paths for systems where /dev/pty doesn't exist"""
# First try the standard pty.openpty()
try:
master, slave = pty.openpty()
return master, slave
except OSError as e:
if debug:
print(f"[DEBUG] Standard pty.openpty() failed: {e}, trying fallback methods")
# Fallback: try to open /dev/ptmx directly
ptmx_paths = ['/dev/ptmx', '/dev/pts/ptmx']
for ptmx_path in ptmx_paths:
try:
if os.path.exists(ptmx_path):
# Open master PTY
master = os.open(ptmx_path, os.O_RDWR | os.O_NOCTTY)
if master < 0:
continue
# Get slave PTY name
slave_name = os.ttyname(master)
if not slave_name:
os.close(master)
continue
# Open slave PTY
slave = os.open(slave_name, os.O_RDWR | os.O_NOCTTY)
if slave < 0:
os.close(master)
continue
if debug:
print(f"[DEBUG] Successfully opened PTY using {ptmx_path}: master={master}, slave={slave}")
return master, slave
except (OSError, AttributeError) as e:
if debug:
print(f"[DEBUG] Failed to open PTY using {ptmx_path}: {e}")
continue
# Last resort: try to create PTY devices manually
try:
# Try to find an available PTY number
for i in range(256): # Try PTY numbers 0-255
pty_name = f"/dev/pts/{i}"
try:
if os.path.exists(pty_name):
continue
# Try to create the PTY device
master = os.open('/dev/ptmx', os.O_RDWR | os.O_NOCTTY)
slave_name = os.ttyname(master)
if slave_name and os.path.exists(slave_name):
slave = os.open(slave_name, os.O_RDWR | os.O_NOCTTY)
if debug:
print(f"[DEBUG] Created PTY manually: master={master}, slave={slave}")
return master, slave
os.close(master)
except (OSError, AttributeError):
continue
except Exception as e:
if debug:
print(f"[DEBUG] Manual PTY creation failed: {e}")
# If all methods fail, raise the original exception
raise OSError("Failed to open PTY: no available PTY devices found")
# Create database and default admin user # Create database and default admin user
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
...@@ -186,15 +255,41 @@ def connect_terminal(client_id): ...@@ -186,15 +255,41 @@ def connect_terminal(client_id):
username = request.form.get('username', 'root') username = request.form.get('username', 'root')
request_id = str(uuid.uuid4()) request_id = str(uuid.uuid4())
command = ['wsssh', '-p', str(args.port), f'{username}@{client_id}.{args.domain}'] command = ['wsssh', '-p', str(args.port), f'{username}@{client_id}.{args.domain}']
# Spawn wsssh process with pty # Spawn wsssh process with pty using fallback method
master, slave = pty.openpty() master, slave = openpty_with_fallback()
slave_name = os.ttyname(slave)
def set_controlling_terminal():
os.setsid()
# Set the controlling terminal
try:
fcntl.ioctl(slave, termios.TIOCSCTTY, 0)
except (OSError, AttributeError):
pass # Some systems don't support TIOCSCTTY
# Set terminal size to match xterm.js dimensions
import struct
winsize = struct.pack('HHHH', 24, 80, 0, 0)
try:
fcntl.ioctl(0, termios.TIOCSWINSZ, winsize)
except (OSError, AttributeError):
pass
# Set raw mode - let SSH client handle terminal behavior
import tty
try:
tty.setraw(0)
except (OSError, AttributeError):
pass
proc = subprocess.Popen( proc = subprocess.Popen(
command, command,
stdin=slave, stdin=slave,
stdout=slave, stdout=slave,
stderr=slave, stderr=slave,
preexec_fn=os.setsid, preexec_fn=set_controlling_terminal,
env=dict(os.environ, TERM='xterm') env=dict(os.environ, TERM='xterm', COLUMNS='80', LINES='24')
) )
os.close(slave) os.close(slave)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment