/* qmp.c — AF_UNIX QMP client: connect + capability handshake, line-based recv * with a poll timeout, and synchronous command execution. */ #include "qmp.h" #include #include #include #include #include #include #define QMP_TIMEOUT_MS 5000 #define QMP_BUF_SIZE 4096 struct qmp_conn { int fd; }; static int recv_line(int fd, char* buf, size_t cap) { size_t n = 0; while (n + 1 < cap) { struct pollfd pfd = { .fd = fd, .events = POLLIN }; if (poll(&pfd, 1, QMP_TIMEOUT_MS) <= 0) return -1; char c; if (read(fd, &c, 1) != 1) return -1; buf[n++] = c; if (c == '\n') break; } buf[n] = '\0'; return (int)n; } static int send_all(int fd, const char* s, size_t len) { while (len > 0) { ssize_t w = write(fd, s, len); if (w <= 0) return -1; s += w; len -= (size_t)w; } return 0; } qmp_conn* qmp_connect(const char* sock_path) { int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) return NULL; struct sockaddr_un addr; memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock_path, sizeof addr.sun_path - 1); if (connect(fd, (struct sockaddr*)&addr, sizeof addr) < 0) { close(fd); return NULL; } char buf[QMP_BUF_SIZE]; if (recv_line(fd, buf, sizeof buf) < 0) { close(fd); return NULL; } const char* cap_cmd = "{\"execute\":\"qmp_capabilities\"}\r\n"; if (send_all(fd, cap_cmd, strlen(cap_cmd)) < 0) { close(fd); return NULL; } if (recv_line(fd, buf, sizeof buf) < 0) { close(fd); return NULL; } qmp_conn* c = malloc(sizeof *c); if (!c) { close(fd); return NULL; } c->fd = fd; return c; } void qmp_disconnect(qmp_conn* c) { if (!c) return; close(c->fd); free(c); } int qmp_exec(qmp_conn* c, const char* cmd, char* resp, size_t cap) { size_t cmdlen = strlen(cmd); if (send_all(c->fd, cmd, cmdlen) < 0) return -1; if (send_all(c->fd, "\r\n", 2) < 0) return -1; char line[QMP_BUF_SIZE]; for (;;) { if (recv_line(c->fd, line, sizeof line) < 0) return -1; if (strstr(line, "\"return\"")) { if (resp && cap > 0) { strncpy(resp, line, cap - 1); resp[cap - 1] = '\0'; } return 0; } if (strstr(line, "\"error\"")) { if (resp && cap > 0) { strncpy(resp, line, cap - 1); resp[cap - 1] = '\0'; } return -1; } } }