v1.8.5: restore sudo auto-password + fix sudo SFTP operations
Terminal: - Auto-detect [sudo] password prompts in interactive shell output - Auto-send server password when sudo prompt detected - Reset detection flag on new command (Enter key) SSH client (SFTPSession): - Fix exec_command() sudo password timing (0.1s delay for prompt) - Fix listdir_attr_sudo() ls output parsing with proper maxsplit - Handle filenames with spaces, symlinks, and varied ls formats Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -465,6 +465,8 @@ class SFTPSession:
|
||||
full_cmd = cmd
|
||||
stdin, stdout, stderr = self._client.exec_command(full_cmd, timeout=30)
|
||||
if self.sudo_mode and user != "root" and password:
|
||||
# Wait briefly for sudo prompt to appear before sending password
|
||||
time.sleep(0.1)
|
||||
stdin.write(password + "\n")
|
||||
stdin.flush()
|
||||
return stdout.read().decode("utf-8", errors="replace")
|
||||
@@ -476,29 +478,35 @@ class SFTPSession:
|
||||
for line in output.strip().splitlines():
|
||||
if line.startswith("total "):
|
||||
continue
|
||||
parts = line.split(None, 7)
|
||||
# With --time-style=+%s the columns are:
|
||||
# perms links owner group size mtime name [-> target if symlink]
|
||||
# Use maxsplit=8 to preserve spaces in filenames, giving us:
|
||||
# [0=perms, 1=links, 2=owner, 3=group, 4=size, 5=mtime, 6=name...]
|
||||
parts = line.split(None, 8)
|
||||
if len(parts) < 7:
|
||||
continue
|
||||
perms = parts[0]
|
||||
if perms.startswith("d") or perms.startswith("l") or perms.startswith("-"):
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
size_str = parts[4]
|
||||
mtime_str = parts[5]
|
||||
# parts[6] may be time or name depending on format
|
||||
# With --time-style=+%s: perms links owner group size epoch name
|
||||
name = parts[6] if len(parts) == 7 else parts[7]
|
||||
if name in (".", ".."):
|
||||
if not (perms.startswith("d") or perms.startswith("l") or perms.startswith("-")):
|
||||
continue
|
||||
try:
|
||||
size = int(size_str)
|
||||
size = int(parts[4])
|
||||
except ValueError:
|
||||
size = 0
|
||||
continue
|
||||
try:
|
||||
mtime = int(mtime_str)
|
||||
mtime = int(parts[5])
|
||||
except ValueError:
|
||||
mtime = 0
|
||||
continue
|
||||
name = parts[6]
|
||||
# Handle cases where name contains " -> " (symlinks) or has spaces
|
||||
if len(parts) > 7:
|
||||
# This means the filename itself contained spaces and was split
|
||||
name = parts[6] + " " + parts[7]
|
||||
# Strip symlink target (e.g. "name -> target")
|
||||
name = name.split(" -> ")[0].strip()
|
||||
if name in (".", ".."):
|
||||
continue
|
||||
mode = _parse_ls_perms(perms)
|
||||
entry = _SudoFileAttr(name, size, mtime, mode)
|
||||
results.append(entry)
|
||||
|
||||
@@ -37,6 +37,10 @@ class TerminalTab(ctk.CTkFrame):
|
||||
# Thread-safe data queue
|
||||
self._data_queue: queue.Queue[bytes] = queue.Queue()
|
||||
|
||||
# Sudo auto-password detection
|
||||
self._sudo_buffer = b"" # Buffer for detecting sudo prompts
|
||||
self._sudo_sent = False # Prevent sending password twice for same prompt
|
||||
|
||||
def set_server(self, alias: str | None):
|
||||
if alias == self._current_alias:
|
||||
return
|
||||
@@ -151,7 +155,20 @@ class TerminalTab(ctk.CTkFrame):
|
||||
except queue.Empty:
|
||||
pass
|
||||
if chunks:
|
||||
self._terminal.feed(b"".join(chunks))
|
||||
combined = b"".join(chunks)
|
||||
self._sudo_buffer += combined
|
||||
# Keep only last 200 bytes for pattern matching
|
||||
self._sudo_buffer = self._sudo_buffer[-200:]
|
||||
|
||||
if not self._sudo_sent:
|
||||
buf_str = self._sudo_buffer.decode("utf-8", errors="replace").lower()
|
||||
if "[sudo] password for" in buf_str or buf_str.rstrip().endswith("password:"):
|
||||
server = self.store.get_server(self._current_alias)
|
||||
if server and server.get("password"):
|
||||
self._session.send(server["password"].encode() + b"\n")
|
||||
self._sudo_sent = True
|
||||
|
||||
self._terminal.feed(combined)
|
||||
|
||||
def _on_disconnected(self):
|
||||
"""Called from SSH read thread."""
|
||||
@@ -189,6 +206,9 @@ class TerminalTab(ctk.CTkFrame):
|
||||
session = self._session # local ref for thread safety
|
||||
if session and session.connected:
|
||||
session.send(data)
|
||||
# Reset sudo sent flag when user sends a new command (detects \r or \n)
|
||||
if b'\r' in data or b'\n' in data:
|
||||
self._sudo_sent = False
|
||||
elif self._current_alias and not self._intentional_disconnect and self._reconnect_count == 0:
|
||||
# Session dead, no reconnect in progress — trigger one attempt
|
||||
self._on_disconnected()
|
||||
|
||||
BIN
releases/ServerManager-v1.8.5-win-x64.exe
Normal file
BIN
releases/ServerManager-v1.8.5-win-x64.exe
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
"""Version info for ServerManager."""
|
||||
|
||||
__version__ = "1.8.4"
|
||||
__version__ = "1.8.5"
|
||||
__app_name__ = "ServerManager"
|
||||
__author__ = "aibot777"
|
||||
__description__ = "Desktop GUI for managing remote servers"
|
||||
|
||||
Reference in New Issue
Block a user