清理:移除 RDP 遗留资料并纳管产品文档与运维工具

变更详情:清理 RDP 遗留文档与临时文件,纳管产品文档与运维工具脚本
This commit is contained in:
songyiming committed 2026-10-29 17:44:25 +08:00
1 parent 26fde0369e
commit 7fdcb53dd9
14 files changed
+759 -100

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
set -u
LOG_DIR="$HOME/vdi-terminal/logs"
LOG_FILE="$LOG_DIR/overnight-$(date +%Y%m%d).log"
mkdir -p "$LOG_DIR"
log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S %z')" "$*" >> "$LOG_FILE"; }
log "monitor_start pid=$$ interval=60s"
while :; do
now=$(date '+%Y-%m-%d %H:%M:%S %z')
shell_pid=$(pgrep -f '^/home/armbian/vdi-terminal/bin/kiosk-shell$' | head -n 1 || true)
monitor_pid=$(pgrep -f '^/home/armbian/vdi-terminal/bin/vdi-kiosk-monitor' | head -n 1 || true)
if [ -n "$shell_pid" ]; then
ps_line=$(ps -o pcpu=,pmem=,rss=,etime= -p "$shell_pid" | tr '\n' ' ')
else
ps_line="missing"
fi
temp=$(awk '{printf "%.1f", $1/1000}' /sys/class/thermal/thermal_zone0/temp 2>/dev/null || printf "unknown")
mem=$(awk '/MemTotal:|MemAvailable:|SwapFree:/{printf "%s=%sKB ",$1,$2}' /proc/meminfo 2>/dev/null || true)
heartbeat="missing"
if [ -f /tmp/vdi-kiosk.heartbeat ]; then
heartbeat=$(stat -c '%Y' /tmp/vdi-kiosk.heartbeat 2>/dev/null || printf 'unknown')
fi
rdp=$(ss -tnp 2>/dev/null | grep '192.168.1.70:3389' | tr '\n' ';' || true)
starts=$(grep -c '主进程已启动' /tmp/vdi-kiosk-monitor.log 2>/dev/null || printf 0)
exits=$(grep -c '主进程已退出' /tmp/vdi-kiosk-monitor.log 2>/dev/null || printf 0)
crashes=$(grep -ciE 'segmentation fault|bus error|double free|invalid pointer|aborted|terminate called' /tmp/vdi-kiosk-monitor.log 2>/dev/null || printf 0)
log "sample now=$now shell_pid=${shell_pid:-missing} monitor_pid=${monitor_pid:-missing} ps=[$ps_line] temp_c=$temp heartbeat_epoch=$heartbeat rdp=[$rdp] starts=$starts exits=$exits crashes=$crashes $mem"
sleep 60
done
+89
View File
@@ -0,0 +1,89 @@
"""Run a command on the PVE node via the same VNC term channel the web console uses.
Usage: python3 pve_term_exec.py "<command>"
Reads ticket from /tmp/pve_t.txt (refreshed automatically if expired).
Prints the command output captured from the terminal stream.
"""
import base64
import json
import ssl
import struct
import sys
import time
import urllib.parse
import urllib.request
HOST = "https://192.168.1.51:8006"
NODE = "starhe"
CTX = ssl.create_default_context()
CTX.check_hostname = False
CTX.verify_mode = ssl.CERT_NONE
def http(path, data=None, ticket=None, csrf=None):
body = urllib.parse.urlencode(data).encode() if data is not None else None
req = urllib.request.Request(HOST + path, body)
if ticket:
req.add_header("Cookie", "PVEAuthCookie=" + ticket)
if csrf:
req.add_header("CSRFPreventionToken", csrf)
with urllib.request.urlopen(req, context=CTX, timeout=30) as r:
raw = r.read()
return json.loads(raw)["data"] if raw else None
def login():
d = http("/api2/json/access/ticket",
{"username": "root@pam", "password": "yx66668888"})
return d["ticket"], d["CSRFPreventionToken"]
def run_cmd(command, timeout=60):
ticket, csrf = login()
# 1. open a term proxy (VNC channel to node shell)
r = http("/api2/json/nodes/%s/termproxy" % NODE, {}, ticket, csrf)
port, ticket_vnc = r["port"], r["ticket"]
user, realm_sep, _ = "root", "@", "pam"
import socket
sock = socket.create_connection(("192.168.1.51", port), timeout=10)
# PVE handshake: username@realm\n ticket\n binary handshake
sock.sendall(b"root@pam\n" + ticket_vnc.encode() + b"\n")
gh = sock.recv(16)
maj, mino = struct.unpack("BB", gh[0:2])
said = sock.recv(16 * 4)
# client sends its own handshake
ch = struct.pack("BB", maj, mino) + b"PVEVNC Client 00" + b"\x00" * 0
sock.sendall(ch)
# 2. run the command in the shell by typing it and reading output
marker = "PVEDONE%s" % int(time.time())
payload = command + " ; echo " + marker + "\n"
time.sleep(0.5)
sock.sendall(payload.encode())
buf = b""
deadline = time.time() + timeout
while time.time() < deadline:
try:
sock.settimeout(2)
chunk = sock.recv(65536)
if not chunk:
break
buf += chunk
if marker.encode() in buf.replace(b"\x1b", b"").replace(b"[?2004l", b""):
break
except socket.timeout:
if marker.encode() in buf:
break
continue
sock.close()
# strip terminal control chars roughly and the echoed command
text = buf.decode("utf-8", "replace")
text = text.replace(marker, "")
lines = [l for l in text.split("\r\n")]
return "\n".join(lines)
if __name__ == "__main__":
print(run_cmd(sys.argv[1]))
+55
View File
@@ -0,0 +1,55 @@
import json
import ssl
import sys
import urllib.parse
import urllib.request
HOST = "https://192.168.1.51:8006"
CTX = ssl.create_default_context()
CTX.check_hostname = False
CTX.verify_mode = ssl.CERT_NONE
def api(path, data=None, ticket=None, csrf=None, json_body=None):
body = None
headers = {}
if json_body is not None:
body = json.dumps(json_body).encode()
headers["Content-Type"] = "application/json"
elif data is not None:
body = urllib.parse.urlencode(data).encode()
req = urllib.request.Request(HOST + path, body, headers)
if ticket:
req.add_header("Cookie", "PVEAuthCookie=" + ticket)
if csrf:
req.add_header("CSRFPreventionToken", csrf)
with urllib.request.urlopen(req, context=CTX, timeout=40) as r:
raw = r.read()
return json.loads(raw)["data"] if raw else None
def agent(ticket, csrf, command, timeout=90):
r = api("/api2/json/nodes/starhe/qemu/100/agent/exec",
json_body={"command": command}, ticket=ticket, csrf=csrf)
pid = r["pid"]
for _ in range(timeout // 2):
import time
time.sleep(2)
st = api("/api2/json/nodes/starhe/qemu/100/agent/exec-status?pid=%d" % pid,
ticket=ticket)
if st.get("exited"):
return st.get("out-data") or "", st.get("err-data") or ""
return "TIMEOUT", ""
if __name__ == "__main__":
d = api("/api2/json/access/ticket",
{"username": "root@pam", "password": "yx66668888"})
ticket, csrf = d["ticket"], d["CSRFPreventionToken"]
ps = sys.argv[1]
out, err = agent(ticket, csrf, [
"C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe",
"-NoProfile", "-Command", ps])
print(out)
if err:
print("ERR:", err[:300])
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Convert an XWD dump (from xwd) into a PNG, without external image libraries.
Usage: xwd -root -silent | python3 xwd2png.py out.png
"""
import struct
import sys
import zlib
def read_header(data):
fields = struct.unpack(">25I", data[:100])
names = [
"header_size", "file_version", "pixmap_format", "pixmap_depth",
"pixmap_width", "pixmap_height", "xoffset", "byte_order",
"bitmap_unit", "bitmap_bit_order", "bitmap_pad", "bits_per_pixel",
"bytes_per_line", "visual_class", "red_mask", "green_mask",
"blue_mask", "bits_per_rgb", "colormap_entries", "ncolors",
"window_width", "window_height", "window_x", "window_y",
"window_bdrwidth",
]
return dict(zip(names, fields))
def write_png(path, width, height, rgb_rows):
raw = b"".join(b"\x00" + row for row in rgb_rows)
def chunk(tag, payload):
return (struct.pack(">I", len(payload)) + tag + payload +
struct.pack(">I", zlib.crc32(tag + payload) & 0xFFFFFFFF))
header = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
png = (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", header) +
chunk(b"IDAT", zlib.compress(raw, 6)) + chunk(b"IEND", b""))
with open(path, "wb") as handle:
handle.write(png)
def main():
out_path = sys.argv[1] if len(sys.argv) > 1 else "out.png"
data = sys.stdin.buffer.read()
head = read_header(data)
width = head["pixmap_width"]
height = head["pixmap_height"]
bpp = head["bits_per_pixel"]
stride = head["bytes_per_line"]
offset = head["header_size"] + head["ncolors"] * 12
pixels = data[offset:]
if bpp == 32:
rows = []
for y in range(height):
line = pixels[y * stride:y * stride + width * 4]
out = bytearray(width * 3)
for x in range(width):
src = x * 4
dst = x * 3
out[dst] = line[src + 2]
out[dst + 1] = line[src + 1]
out[dst + 2] = line[src]
rows.append(bytes(out))
elif bpp == 24:
rows = []
for y in range(height):
line = pixels[y * stride:y * stride + width * 3]
out = bytearray(width * 3)
for x in range(width):
src = x * 3
dst = x * 3
out[dst] = line[src + 2]
out[dst + 1] = line[src + 1]
out[dst + 2] = line[src]
rows.append(bytes(out))
else:
sys.stderr.write("unsupported bits_per_pixel=%s\n" % bpp)
return 2
write_png(out_path, width, height, rows)
print("%s %dx%d" % (out_path, width, height))
return 0
if __name__ == "__main__":
sys.exit(main())