qmp.py 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. #!/usr/bin/env python3
  2. """Minimal QMP client for the PCL PoC VM."""
  3. import json
  4. import socket
  5. class QMPError(RuntimeError):
  6. pass
  7. class QMP:
  8. def __init__(self, path, timeout=10.0):
  9. self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  10. self.sock.settimeout(timeout)
  11. self.sock.connect(path)
  12. self.f = self.sock.makefile("rwb", buffering=0)
  13. greeting = self._read()
  14. if "QMP" not in greeting:
  15. raise QMPError("not a QMP greeting: %r" % (greeting,))
  16. self._send({"execute": "qmp_capabilities"})
  17. self._read()
  18. def _read(self):
  19. line = self.f.readline()
  20. if not line:
  21. raise EOFError("QMP closed")
  22. return json.loads(line.decode("utf-8", "replace"))
  23. def _send(self, obj):
  24. self.f.write((json.dumps(obj) + "\n").encode("utf-8"))
  25. def cmd(self, name, args=None, timeout=15.0):
  26. self.sock.settimeout(timeout)
  27. request = {"execute": name}
  28. if args is not None:
  29. request["arguments"] = args
  30. self._send(request)
  31. while True:
  32. message = self._read()
  33. if "return" in message or "error" in message:
  34. return message
  35. def sendkey(self, key="spc"):
  36. return self.cmd(
  37. "human-monitor-command", {"command-line": "sendkey %s" % key}
  38. )
  39. def screendump(self, path):
  40. return self.cmd("screendump", {"filename": path})
  41. def status(self):
  42. return self.cmd("query-status", timeout=3.0)
  43. def quit(self):
  44. try:
  45. self.cmd("quit", timeout=3.0)
  46. except Exception:
  47. pass
  48. def close(self):
  49. try:
  50. self.f.close()
  51. except Exception:
  52. pass
  53. try:
  54. self.sock.close()
  55. except Exception:
  56. pass