v1.8.69: smart X display auto-detection and xauth fix

Automatically finds active display from X11 sockets and processes,
locates .Xauthority from display owner in /home/*, and merges
xauth cookies so root can use another user's X session.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
cosmic-frost-8b10
2026-02-28 13:29:21 +00:00
parent 8169e3e137
commit d51c0aa09a
3 changed files with 81 additions and 22 deletions

83
main.py
View File

@@ -11,31 +11,90 @@ import subprocess
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def _find_active_xauthority():
"""Find .Xauthority from the user who owns the active X display."""
# 1. Already set and works
if os.environ.get("XAUTHORITY") and os.path.isfile(os.environ["XAUTHORITY"]):
return os.environ["XAUTHORITY"]
# 2. Search /home/*/.Xauthority for all users
try:
for entry in os.scandir("/home"):
if entry.is_dir():
xauth = os.path.join(entry.path, ".Xauthority")
if os.path.isfile(xauth):
return xauth
except PermissionError:
pass
# 3. Check /root/.Xauthority
root_xauth = "/root/.Xauthority"
if os.path.isfile(root_xauth):
return root_xauth
return None
def _find_active_display():
"""Detect active X display from running sessions."""
display = os.environ.get("DISPLAY")
if display:
return display
# Parse from /tmp/.X11-unix sockets
x11_dir = "/tmp/.X11-unix"
if os.path.isdir(x11_dir):
try:
sockets = sorted(os.listdir(x11_dir))
for s in sockets:
if s.startswith("X"):
return ":" + s[1:]
except PermissionError:
pass
# Parse from active X/xrdp/vnc processes
try:
result = subprocess.run(
["ps", "-eo", "args"], capture_output=True, text=True, timeout=3
)
for line in result.stdout.splitlines():
# Xorg :50 ... or Xvnc :1 ...
for prefix in ("Xorg ", "Xvnc ", "Xtigervnc ", "xrdp-sesman"):
if prefix in line:
for part in line.split():
if part.startswith(":") and part[1:].split(".")[0].isdigit():
return part
except (FileNotFoundError, subprocess.TimeoutExpired):
pass
return ":0"
def _ensure_display_access(): def _ensure_display_access():
"""Auto-fix X display authorization for root and non-owner users.""" """Auto-fix X display authorization for root and non-owner users."""
display = os.environ.get("DISPLAY") # Step 1: Find active display
if not display: display = _find_active_display()
# Try common displays os.environ["DISPLAY"] = display
for d in (":0", ":1", ":0.0"):
os.environ["DISPLAY"] = d # Step 2: Find and set XAUTHORITY from display owner
display = d xauth = _find_active_xauthority()
break if xauth:
os.environ["XAUTHORITY"] = xauth
# Step 3: Try xhost to allow local connections
try: try:
subprocess.run(["xhost", "+local:"], stdout=subprocess.DEVNULL, subprocess.run(["xhost", "+local:"], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, timeout=3) stderr=subprocess.DEVNULL, timeout=3)
except (FileNotFoundError, subprocess.TimeoutExpired): except (FileNotFoundError, subprocess.TimeoutExpired):
pass pass
# Fallback: generate xauth cookie if xhost not available
if display: # Step 4: Verify display works, try xauth merge if not
try: try:
subprocess.run(["xdpyinfo"], stdout=subprocess.DEVNULL, subprocess.run(["xdpyinfo"], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, timeout=3, check=True) stderr=subprocess.DEVNULL, timeout=3, check=True)
return # Works!
except (FileNotFoundError, subprocess.TimeoutExpired, subprocess.CalledProcessError): except (FileNotFoundError, subprocess.TimeoutExpired, subprocess.CalledProcessError):
pass
# Step 5: Last resort — copy xauth cookies from display owner
if xauth and xauth != os.path.expanduser("~/.Xauthority"):
try: try:
xauth_file = os.path.expanduser("~/.Xauthority")
os.environ.setdefault("XAUTHORITY", xauth_file)
subprocess.run( subprocess.run(
["xauth", "generate", display, ".", "trusted"], ["xauth", "merge", xauth],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5 stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5
) )
except (FileNotFoundError, subprocess.TimeoutExpired): except (FileNotFoundError, subprocess.TimeoutExpired):

Binary file not shown.

View File

@@ -1,6 +1,6 @@
"""Version info for ServerManager.""" """Version info for ServerManager."""
__version__ = "1.8.68" __version__ = "1.8.69"
__app_name__ = "ServerManager" __app_name__ = "ServerManager"
__author__ = "aibot777" __author__ = "aibot777"
__description__ = "Desktop GUI for managing remote servers" __description__ = "Desktop GUI for managing remote servers"