74 lines
3.6 KiB
Python
74 lines
3.6 KiB
Python
"""Scoped VM100 interactive cursor exporter. No new network listener or system cursor theme."""
|
|
import argparse
|
|
import base64
|
|
import json
|
|
from pathlib import Path
|
|
import runpy
|
|
import sys
|
|
|
|
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("action", choices=["start", "status", "export", "stop"])
|
|
parser.add_argument("--output", type=Path)
|
|
args = parser.parse_args()
|
|
root = Path(__file__).resolve().parent
|
|
t = runpy.run_path(str(root / "pve_term_exec.py"))
|
|
v = runpy.run_path(str(root / "pve_vm.py"))
|
|
ticket, csrf = t["login"]()
|
|
if v["api"]("/api2/json/nodes/starhe/qemu/100/status/current", ticket=ticket).get("status") != "running":
|
|
sys.exit("VM100 is not running")
|
|
command = r"$ErrorActionPreference='Stop'; $folder='C:\ProgramData\VDI\cursor-poc-20261103'; $task='VDICursorPOC'; "
|
|
if args.action == "start":
|
|
payload = base64.b64encode((root / "cursor-poc-host.ps1").read_bytes()).decode()
|
|
command += r"""
|
|
if(Get-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue) { throw 'Probe already exists' }
|
|
$user=(Get-CimInstance Win32_ComputerSystem).UserName
|
|
if(-not $user) { throw 'No interactive user' }
|
|
New-Item -ItemType Directory -Force $folder | Out-Null
|
|
$acl=Get-Acl $folder
|
|
$acl.SetAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($user,'Modify','ContainerInherit,ObjectInherit','None','Allow')))
|
|
Set-Acl $folder $acl
|
|
Remove-Item "$folder\error.txt","$folder\state.json","$folder\shapes.json" -ErrorAction SilentlyContinue
|
|
[IO.File]::WriteAllBytes("$folder\probe.ps1",[Convert]::FromBase64String('__PAYLOAD__'))
|
|
$a=New-ScheduledTaskAction -Execute powershell.exe -Argument "-NoProfile -STA -ExecutionPolicy Bypass -WindowStyle Hidden -File $folder\probe.ps1"
|
|
$p=New-ScheduledTaskPrincipal -UserId $user -LogonType Interactive -RunLevel Limited
|
|
$s=New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 9)
|
|
Register-ScheduledTask -TaskName $task -Action $a -Principal $p -Settings $s | Out-Null
|
|
Start-ScheduledTask -TaskName $task
|
|
Start-Sleep -Seconds 3
|
|
if(Test-Path "$folder\error.txt") { throw (Get-Content "$folder\error.txt" -Raw) }
|
|
if(-not(Test-Path "$folder\shapes.json")) { throw 'No fresh cursor export' }
|
|
'Cursor probe started'
|
|
""".replace("__PAYLOAD__", payload)
|
|
elif args.action == "export":
|
|
command += 'Get-Content "$folder\\shapes.json" -Raw'
|
|
elif args.action == "status":
|
|
command += r"""
|
|
Get-Content "$folder\state.json" -Raw
|
|
Get-Content "$folder\clicks.csv"
|
|
Get-Content "$folder\states.csv"
|
|
Get-Content "$folder\error.txt" -ErrorAction SilentlyContinue
|
|
"""
|
|
else:
|
|
command += r"""
|
|
Stop-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue
|
|
Unregister-ScheduledTask -TaskName $task -Confirm:$false -ErrorAction SilentlyContinue
|
|
if(Test-Path "$folder\pid.txt") {
|
|
$probePid=[int](Get-Content "$folder\pid.txt")
|
|
$process=Get-CimInstance Win32_Process -Filter "ProcessId=$probePid"
|
|
if($process.CommandLine -like '*cursor-poc-20261103*') { Stop-Process -Id $probePid -ErrorAction SilentlyContinue }
|
|
}
|
|
'Cursor probe stopped'
|
|
"""
|
|
out, error = v["agent"](ticket, csrf, ["powershell.exe", "-NoProfile", "-Command", command])
|
|
if error or not out or out == "TIMEOUT":
|
|
sys.exit(error or out or "Empty guest-agent result")
|
|
if args.action == "export":
|
|
data = json.loads(out.lstrip("\ufeff"))
|
|
if args.output:
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_text(json.dumps(data, separators=(",", ":")), encoding="utf-8")
|
|
print(json.dumps([{k: s[k] for k in ("name", "width", "height", "hotX", "hotY", "monochrome", "invertedPixels")} for s in data["shapes"]]))
|
|
else:
|
|
print(out)
|