J2897

Connect-RustDesk-AfterLinuxLogin.ps1

Aug 20th, 2026 (edited)
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#!
  2. .SYNOPSIS
  3. Connects to a Linux computer in RustDesk and reconnects after its graphical-login handoff.
  4.  
  5. .DESCRIPTION
  6. RustDesk may initially connect to a LightDM (or similar) login screen. When the
  7. user signs in, the remote RustDesk server can be replaced by the per-user server.
  8. Its built-in retry can occur before that replacement server is ready.
  9.  
  10. This wrapper opens the normal RustDesk connection, watches the remote machine's
  11. loginctl sessions over key-based SSH, and issues one fresh RustDesk connection
  12. after the requested user becomes active on the specified graphical seat.
  13.  
  14. .NOTES
  15. Requires Windows PowerShell 5.1 or later, RustDesk, OpenSSH's ssh.exe, and
  16. non-interactive/key-based SSH access to the remote machine.
  17. #>
  18.  
  19. [CmdletBinding()]
  20. param(
  21.     # RustDesk ID, relay address, or direct address such as 192.168.1.25:21118.
  22.     [Parameter(Mandatory = $true)]
  23.     [ValidateNotNullOrEmpty()]
  24.     [string]$RustDeskTarget,
  25.  
  26.     # An SSH host alias, hostname, or user@hostname accepted by ssh.exe.
  27.     [Parameter(Mandatory = $true)]
  28.     [ValidateNotNullOrEmpty()]
  29.     [string]$SshTarget,
  30.  
  31.     # Defaults to the current Windows username, normalised for typical Linux use.
  32.     [ValidateNotNullOrEmpty()]
  33.     [string]$SessionUser = $(if ($env:USERNAME) { $env:USERNAME.ToLowerInvariant() } else { '' }),
  34.  
  35.     [ValidateNotNullOrEmpty()]
  36.     [string]$Seat = 'seat0',
  37.  
  38.     [ValidateScript({ Test-Path -LiteralPath $_ -PathType Leaf })]
  39.     [string]$RustDeskPath = (Join-Path $env:ProgramFiles 'RustDesk\RustDesk.exe'),
  40.  
  41.     [ValidateRange(30, 900)]
  42.     [int]$LoginWatchWindowSeconds = 300,
  43.  
  44.     [ValidateRange(1, 30)]
  45.     [int]$PollIntervalSeconds = 3,
  46.  
  47.     [ValidateRange(1, 60)]
  48.     [int]$SessionStartupGraceSeconds = 7,
  49.  
  50.     # Optional plain-text diagnostic log path. It contains timestamps only.
  51.     [string]$LogPath
  52. )
  53.  
  54. $sshCommand = Get-Command ssh.exe -ErrorAction Stop
  55. $seatPattern = '^\s*\S+\s+\d+\s+' + [regex]::Escape($SessionUser) + '\s+' + [regex]::Escape($Seat) + '\s'
  56.  
  57. function Write-ConnectionLog {
  58.     param([Parameter(Mandatory = $true)][string]$Message)
  59.  
  60.     if (-not $LogPath) {
  61.         return
  62.     }
  63.  
  64.     try {
  65.         Add-Content -LiteralPath $LogPath -Value ('{0:yyyy-MM-dd HH:mm:ss} {1}' -f (Get-Date), $Message)
  66.     }
  67.     catch {
  68.         # Logging is diagnostic only and must not prevent a remote connection.
  69.     }
  70. }
  71.  
  72. function Test-RemoteDesktopSession {
  73.     # SSH-created sessions have no seat. A graphical Linux desktop session is
  74.     # explicitly associated with a seat, normally seat0.
  75.     $sessions = & $sshCommand.Source -o BatchMode=yes -o ConnectTimeout=3 -o LogLevel=ERROR $SshTarget 'loginctl list-sessions --no-legend' 2>$null
  76.     $matchingSessions = @($sessions | Where-Object { $_ -match $seatPattern })
  77.     return ($LASTEXITCODE -eq 0 -and $matchingSessions.Count -gt 0)
  78. }
  79.  
  80. function Start-RustDeskConnection {
  81.     Start-Process -FilePath $RustDeskPath -ArgumentList "--connect $RustDeskTarget"
  82. }
  83.  
  84. $wasOnDesktop = Test-RemoteDesktopSession
  85. $deadline = [DateTime]::UtcNow.AddSeconds($LoginWatchWindowSeconds)
  86. $reconnectIssued = $false
  87.  
  88. Write-ConnectionLog "Opened initial connection to $RustDeskTarget; waiting for $SessionUser on $Seat."
  89. Start-RustDeskConnection
  90.  
  91. # A connection made while the desktop is already signed in does not cross the
  92. # display-manager-to-user-session boundary. The initial RustDesk connection is
  93. # already the right one, so exit instead of keeping a watcher alive.
  94. if ($wasOnDesktop) {
  95.     Write-ConnectionLog "$SessionUser was already active on $Seat; no post-login reconnection is needed."
  96.     return
  97. }
  98.  
  99. while ([DateTime]::UtcNow -lt $deadline) {
  100.     $isOnDesktop = Test-RemoteDesktopSession
  101.  
  102.     if (-not $wasOnDesktop -and $isOnDesktop) {
  103.         Write-ConnectionLog "Detected $SessionUser on $Seat; waiting $SessionStartupGraceSeconds seconds for the user-session RustDesk server."
  104.         Start-Sleep -Seconds $SessionStartupGraceSeconds
  105.         Start-RustDeskConnection
  106.         Write-ConnectionLog 'Issued post-login RustDesk reconnection.'
  107.         $reconnectIssued = $true
  108.         break
  109.     }
  110.  
  111.     $wasOnDesktop = $isOnDesktop
  112.     Start-Sleep -Seconds $PollIntervalSeconds
  113. }
  114.  
  115. if (-not $reconnectIssued) {
  116.     Write-ConnectionLog 'Stopped watching without detecting a new graphical-session login.'
  117. }
  118.  
Add Comment
Please, Sign In to add comment