The function being introduced is responsible for preparing and executing 'chardev-add' qemu monitor command. Moreover, in case of PTY chardev, the corresponding pty path is updated. --- src/qemu/qemu_monitor.c | 21 +++++ src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 182 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + tests/qemumonitorjsontest.c | 81 +++++++++++++++++++ 5 files changed, 290 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 067b368..c85f826 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3621,3 +3621,24 @@ int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, return qemuMonitorJSONGetTPMTypes(mon, tpmtypes); } + +int qemuMonitorAttachCharDev(qemuMonitorPtr mon, + const char *chrID, + virDomainChrSourceDefPtr chr) +{ + VIR_DEBUG("mon=%p chrID=%s chr=%p", mon, chrID, chr); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONAttachCharDev(mon, chrID, chr); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 78011ee..734e09b 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -701,6 +701,9 @@ int qemuMonitorGetTPMModels(qemuMonitorPtr mon, int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, char ***tpmtypes); +int qemuMonitorAttachCharDev(qemuMonitorPtr mon, + const char *chrID, + virDomainChrSourceDefPtr chr); /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 626db8b..8e5a67c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4763,6 +4763,29 @@ error: return NULL; } +static virJSONValuePtr +qemuMonitorJSONBuildUnixSocketAddress(const char *path) +{ + virJSONValuePtr addr = NULL; + virJSONValuePtr data = NULL; + + if (!(data = virJSONValueNewObject()) || + !(addr = virJSONValueNewObject())) + goto error; + + if (virJSONValueObjectAppendString(data, "path", path) < 0 || + virJSONValueObjectAppendString(addr, "type", "unix") < 0 || + virJSONValueObjectAppend(addr, "data", data) < 0) + goto error; + + return addr; +error: + virReportOOMError(); + virJSONValueFree(data); + virJSONValueFree(addr); + return NULL; +} + int qemuMonitorJSONNBDServerStart(qemuMonitorPtr mon, const char *host, @@ -4938,3 +4961,162 @@ int qemuMonitorJSONGetTPMTypes(qemuMonitorPtr mon, { return qemuMonitorJSONGetStringArray(mon, "query-tpm-types", tpmtypes); } + +static virJSONValuePtr +qemuMonitorJSONAttachCharDevCommand(const char *chrID, + const virDomainChrSourceDefPtr chr) +{ + virJSONValuePtr ret; + virJSONValuePtr backend; + virJSONValuePtr data = NULL; + virJSONValuePtr addr = NULL; + const char *backend_type = NULL; + bool telnet; + + if (!(backend = virJSONValueNewObject()) || + !(data = virJSONValueNewObject())) { + goto no_memory; + } + + switch ((enum virDomainChrType) chr->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + case VIR_DOMAIN_CHR_TYPE_VC: + backend_type = "null"; + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + backend_type = "pty"; + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + backend_type = "file"; + if (virJSONValueObjectAppendString(data, "out", chr->data.file.path) < 0) + goto no_memory; + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + backend_type = STRPREFIX(chrID, "parallel") ? "parallel" : "serial"; + if (virJSONValueObjectAppendString(data, "device", + chr->data.file.path) < 0) + goto no_memory; + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + backend_type = "socket"; + addr = qemuMonitorJSONBuildInetSocketAddress(chr->data.tcp.host, + chr->data.tcp.service); + if (!addr || + virJSONValueObjectAppend(data, "addr", addr) < 0) + goto no_memory; + addr = NULL; + + telnet = chr->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + + if (virJSONValueObjectAppendBoolean(data, "wait", false) < 0 || + virJSONValueObjectAppendBoolean(data, "telnet", telnet) < 0 || + virJSONValueObjectAppendBoolean(data, "server", chr->data.tcp.listen) < 0) + goto no_memory; + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + backend_type = "socket"; + addr = qemuMonitorJSONBuildInetSocketAddress(chr->data.udp.connectHost, + chr->data.udp.connectService); + if (!addr || + virJSONValueObjectAppend(data, "addr", addr) < 0) + goto no_memory; + addr = NULL; + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + backend_type = "socket"; + addr = qemuMonitorJSONBuildUnixSocketAddress(chr->data.nix.path); + + if (!addr || + virJSONValueObjectAppend(data, "addr", addr) < 0) + goto no_memory; + addr = NULL; + + if (virJSONValueObjectAppendBoolean(data, "wait", false) < 0 || + virJSONValueObjectAppendBoolean(data, "server", chr->data.nix.listen) < 0) + goto no_memory; + break; + + case VIR_DOMAIN_CHR_TYPE_SPICEVMC: + case VIR_DOMAIN_CHR_TYPE_PIPE: + case VIR_DOMAIN_CHR_TYPE_STDIO: + case VIR_DOMAIN_CHR_TYPE_LAST: + virReportError(VIR_ERR_OPERATION_FAILED, + _("Unsupported char device type '%d'"), + chr->type); + goto error; + } + + if (virJSONValueObjectAppendString(backend, "type", backend_type) < 0 || + virJSONValueObjectAppend(backend, "data", data) < 0) + goto no_memory; + data = NULL; + + if (!(ret = qemuMonitorJSONMakeCommand("chardev-add", + "s:id", chrID, + "a:backend", backend, + NULL))) + goto error; + + return ret; + +no_memory: + virReportOOMError(); +error: + virJSONValueFree(addr); + virJSONValueFree(data); + virJSONValueFree(backend); + return NULL; +} + + +int +qemuMonitorJSONAttachCharDev(qemuMonitorPtr mon, + const char *chrID, + virDomainChrSourceDefPtr chr) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (!(cmd = qemuMonitorJSONAttachCharDevCommand(chrID, chr))) + return ret; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + goto cleanup; + + if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { + virJSONValuePtr data; + const char *path; + + if (!(data = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("chardev-add reply was missing return data")); + goto cleanup; + } + + if (!(path = virJSONValueObjectGetString(data, "pty"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("chardev-add reply was missing pty path")); + goto cleanup; + } + + if (VIR_STRDUP(chr->data.file.path, path) < 0) + goto cleanup; + } + + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index d79b86b..e0a4883 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -353,4 +353,7 @@ int qemuMonitorJSONGetTPMTypes(qemuMonitorPtr mon, char ***tpmtypes) ATTRIBUTE_NONNULL(2); +int qemuMonitorJSONAttachCharDev(qemuMonitorPtr mon, + const char *chrID, + virDomainChrSourceDefPtr chr); #endif /* QEMU_MONITOR_JSON_H */ diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index acc94ca..411a7a7 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -594,6 +594,86 @@ cleanup: return ret; } +static int +testQemuMonitorJSONAttachChardev(const void *data) +{ + const virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data; + qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt); + virDomainChrSourceDef chr; + int ret = 0; + + if (!test) + return -1; + +#define DO_CHECK(chrID, reply, fail) \ + if (qemuMonitorTestAddItem(test, "chardev-add", reply) < 0) \ + goto cleanup; \ + if (qemuMonitorAttachCharDev(qemuMonitorTestGetMonitor(test), \ + chrID, &chr) < 0) \ + ret = fail ? ret : -1; \ + else \ + ret = fail ? -1 : ret; \ + +#define CHECK(chrID, reply) \ + DO_CHECK(chrID, reply, false) + +#define CHECK_FAIL(chrID, reply) \ + DO_CHECK(chrID, reply, true) + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_NULL }; + CHECK("chr_null", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type =VIR_DOMAIN_CHR_TYPE_VC }; + CHECK("chr_vc", "{\"return\": {}}"); + +#define PTY_PATH "/dev/ttyS0" + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_PTY }; + CHECK("chr_pty", "{\"return\": {\"pty\" : \"" PTY_PATH "\"}}"); + if (STRNEQ_NULLABLE(PTY_PATH, chr.data.file.path)) { + VIR_FREE(chr.data.file.path); + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected PTY path: %s got: %s", + PTY_PATH, NULLSTR(chr.data.file.path)); + ret = -1; + } + VIR_FREE(chr.data.file.path); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_PTY }; + CHECK_FAIL("chr_pty_fail", "{\"return\": {}}"); +#undef PTY_PATH + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_FILE }; + CHECK("chr_file", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_DEV }; + CHECK("chr_dev", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_TCP }; + CHECK("chr_tcp", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_UDP }; + CHECK("chr_udp", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_UNIX }; + CHECK("chr_unix", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_SPICEVMC }; + CHECK_FAIL("chr_spicevmc", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_PIPE }; + CHECK_FAIL("chr_pipe", "{\"return\": {}}"); + + chr = (virDomainChrSourceDef) { .type = VIR_DOMAIN_CHR_TYPE_STDIO }; + CHECK_FAIL("chr_stdio", "{\"return\": {}}"); + +#undef CHECK +#undef CHECK_FAIL +#undef DO_CHECK + +cleanup: + qemuMonitorTestFree(test); + return ret; +} static int mymain(void) @@ -623,6 +703,7 @@ mymain(void) DO_TEST(GetCommands); DO_TEST(GetTPMModels); DO_TEST(GetCommandLineOptionParameters); + DO_TEST(AttachChardev); virObjectUnref(xmlopt); -- 1.8.1.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list