From c60eba13521b003dbe547faae2ec7f0385832f66 Mon Sep 17 00:00:00 2001 From: alexveley Date: Wed, 24 Jun 2026 01:57:11 -0400 Subject: [PATCH] Update pve RustDesk GA sync scripts --- blair-sync-rustdesk-via-ga.sh | 150 +++++++++++++++++++++++++--------- install.sh | 2 +- 2 files changed, 113 insertions(+), 39 deletions(-) diff --git a/blair-sync-rustdesk-via-ga.sh b/blair-sync-rustdesk-via-ga.sh index 160af0f..168938e 100644 --- a/blair-sync-rustdesk-via-ga.sh +++ b/blair-sync-rustdesk-via-ga.sh @@ -25,18 +25,50 @@ SHARE = os.environ.get('BLAIR_SHARE', '/tank/blair-share') REGISTRY = os.path.join(SHARE, 'fleet', 'registry.json') NODE = os.environ.get('BLAIR_PVE_NODE', 'pve') DRY_RUN = os.environ.get('BLAIR_DRY_RUN', '') in ('1', 'true', 'yes') +VERBOSE = os.environ.get('BLAIR_VERBOSE', '') in ('1', 'true', 'yes') VMIDS_ENV = os.environ.get('BLAIR_VMIDS', '').strip() -RUSTDESK_CFG = ( - r'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml' -) -PS_GET_ID = ( - r"$p='C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml'; " - r"if (-not (Test-Path -LiteralPath $p)) { exit 1 }; " - r"$t=Get-Content -Raw -LiteralPath $p; " - r"if ($t -match '(?m)^\s*id\s*=\s*''?(\d{6,12})') { Write-Output $Matches[1]; exit 0 }; " - r"exit 1" +GUEST_READ_PATHS = [ + r'C:\ProgramData\Blair\rustdesk-report.json', + r'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml', + r'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk.toml', + r'C:\Windows\System32\config\systemprofile\AppData\Roaming\RustDesk\config\RustDesk2.toml', + r'C:\ProgramData\RustDesk\config\RustDesk2.toml', +] + +PS_READ_ID = r''' +$paths = @( + 'C:\ProgramData\Blair\rustdesk-report.json', + 'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml', + 'C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk.toml', + 'C:\Windows\System32\config\systemprofile\AppData\Roaming\RustDesk\config\RustDesk2.toml', + 'C:\ProgramData\RustDesk\config\RustDesk2.toml' ) +foreach ($p in $paths) { + if (-not (Test-Path -LiteralPath $p)) { continue } + $t = Get-Content -Raw -LiteralPath $p + if ($p -like '*.json') { + try { + $j = $t | ConvertFrom-Json + if ($j.rustdesk_id -match '^\d{6,12}$') { Write-Output $j.rustdesk_id; exit 0 } + } catch {} + } + if ($t -match '(?m)^\s*id\s*=\s*[''"]?(\d{6,12})') { Write-Output $Matches[1]; exit 0 } +} +foreach ($rp in @( + 'HKLM:\SOFTWARE\RustDesk\Host', + 'HKLM:\SOFTWARE\RustDesk', + 'HKLM:\SOFTWARE\WOW6432Node\RustDesk\Host', + 'HKLM:\SOFTWARE\WOW6432Node\RustDesk' +)) { + if (-not (Test-Path $rp)) { continue } + foreach ($n in @('id', 'Id', 'rustdesk_id', 'peer_id')) { + $v = (Get-ItemProperty -Path $rp -Name $n -ErrorAction SilentlyContinue).$n + if ($v -match '^\d{6,12}$') { Write-Output $v; exit 0 } + } +} +exit 1 +''' def log(msg: str) -> None: @@ -142,40 +174,81 @@ def guest_exec_text(vmid: int, command: list[str], timeout: int = 90) -> tuple[s def guest_file_read(vmid: int, guest_path: str) -> tuple[str | None, str | None]: - proc = subprocess.run( - ['pvesh', 'get', f'/nodes/{NODE}/qemu/{vmid}/agent/file-read', '--file', guest_path], - capture_output=True, text=True, check=False, + for path in {guest_path, guest_path.replace('\\', '/')}: + proc = subprocess.run( + [ + 'pvesh', 'get', f'/nodes/{NODE}/qemu/{vmid}/agent/file-read', + '--file', path, '--output-format', 'json', + ], + capture_output=True, text=True, check=False, + ) + if proc.returncode != 0: + continue + try: + payload = json.loads(proc.stdout) + except json.JSONDecodeError: + continue + data = payload.get('data', payload) + content_b64 = data.get('content') if isinstance(data, dict) else None + if not content_b64: + continue + return base64.b64decode(content_b64).decode('utf-8', errors='replace'), None + return None, f'file-read failed for {guest_path}' + + +def parse_id_from_content(text: str, path: str) -> str | None: + if path.lower().endswith('.json'): + try: + rid = str(json.loads(text).get('rustdesk_id', '') or '').strip() + if re.fullmatch(r'\d{6,12}', rid): + return rid + except json.JSONDecodeError: + pass + for pattern in ( + r'(?m)^\s*id\s*=\s*["\']?(\d{6,12})', + r'(?m)rustdesk_id\s*=\s*["\']?(\d{6,12})', + ): + match = re.search(pattern, text) + if match: + return match.group(1) + return None + + +def powershell_encoded(script: str) -> str: + return base64.b64encode(script.strip().encode('utf-16-le')).decode('ascii') + + +def guest_exec_powershell(vmid: int) -> tuple[str | None, str | None]: + enc = powershell_encoded(PS_READ_ID) + return guest_exec_text( + vmid, + ['powershell.exe', '-NoProfile', '-EncodedCommand', enc], + timeout=120, ) - if proc.returncode != 0: - msg = proc.stderr.strip() or proc.stdout.strip() or f'file-read rc={proc.returncode}' - return None, msg - try: - payload = json.loads(proc.stdout) - except json.JSONDecodeError: - return None, f'file-read json error: {proc.stdout[:160]!r}' - data = payload.get('data', payload) - content_b64 = data.get('content') if isinstance(data, dict) else None - if not content_b64: - return None, 'file-read empty content' - return base64.b64decode(content_b64).decode('utf-8', errors='replace'), None - - -def parse_rustdesk_id_from_toml(text: str) -> str | None: - match = re.search(r'(?m)^\s*id\s*=\s*["\']?(\d{6,12})', text) - return match.group(1) if match else None def read_rustdesk_id(vmid: int) -> tuple[str | None, str | None]: - text, err = guest_file_read(vmid, RUSTDESK_CFG) - if text: - rid = parse_rustdesk_id_from_toml(text) + errors: list[str] = [] + for path in GUEST_READ_PATHS: + text, err = guest_file_read(vmid, path) + if not text: + if VERBOSE: + errors.append(f'{path}: {err}') + continue + rid = parse_id_from_content(text, path) if rid: + if VERBOSE: + log(f'VM {vmid}: ID {rid} from file-read {path}') return rid, None - err = 'id not found in RustDesk2.toml' - return guest_exec_text( - vmid, - ['powershell.exe', '-NoProfile', '-Command', PS_GET_ID], - ) + errors.append(f'{path}: no id in file') + + rid, err = guest_exec_powershell(vmid) + if rid: + if VERBOSE: + log(f'VM {vmid}: ID {rid} from guest exec') + return rid, None + errors.append(f'exec: {err}') + return None, errors[-1] if errors else 'unknown' def qm_description(vmid: int) -> str: @@ -224,7 +297,8 @@ def main() -> int: new_id, err = read_rustdesk_id(vmid) if not new_id: - log(f'VM {vmid}: skip (could not read RustDesk ID: {err})') + hint = ' (try BLAIR_VERBOSE=1)' if not VERBOSE else '' + log(f'VM {vmid}: skip (could not read RustDesk ID: {err}){hint}') continue old_registry_id = registry_old_id(registry, vmid) diff --git a/install.sh b/install.sh index 134c422..0e69262 100644 --- a/install.sh +++ b/install.sh @@ -27,7 +27,7 @@ chmod 644 "$CRON" cat "$CRON" echo "=== dry run ===" -"$TARGET" --dry-run +BLAIR_VERBOSE=1 "$TARGET" --dry-run echo "=== live run ===" "$TARGET"