diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 0671bdc852..f6d8ef46a1 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -79,6 +79,9 @@ struct _qemuMonitor { bool json; bool waitGreeting; + + /* cache of query-command-line-options results */ + virJSONValuePtr options; }; static virClassPtr qemuMonitorClass; @@ -243,6 +246,7 @@ static void qemuMonitorDispose(void *obj) (mon->cb->destroy)(mon, mon->vm); virCondDestroy(&mon->notify); VIR_FREE(mon->buffer); + virJSONValueFree(mon->options); } @@ -912,6 +916,18 @@ cleanup: } +virJSONValuePtr +qemuMonitorGetOptions(qemuMonitorPtr mon) +{ + return mon->options; +} + +void +qemuMonitorSetOptions(qemuMonitorPtr mon, virJSONValuePtr options) +{ + mon->options = options; +} + int qemuMonitorHMPCommandWithFd(qemuMonitorPtr mon, const char *cmd, int scm_fd, @@ -3334,6 +3350,31 @@ int qemuMonitorGetEvents(qemuMonitorPtr mon, } +/* Collect the parameters associated with a given command line option. + * Return count of known parameters or -1 on error. */ +int +qemuMonitorGetCommandLineOptionParameters(qemuMonitorPtr mon, + const char *option, + char ***params) +{ + VIR_DEBUG("mon=%p option=%s params=%p", mon, option, params); + + 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 qemuMonitorJSONGetCommandLineOptionParameters(mon, option, params); +} + + int qemuMonitorGetKVMState(qemuMonitorPtr mon, bool *enabled, bool *present) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 527da0a8a2..8f9c182db7 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -162,12 +162,16 @@ int qemuMonitorSetCapabilities(qemuMonitorPtr mon); int qemuMonitorSetLink(qemuMonitorPtr mon, const char *name, - enum virDomainNetInterfaceLinkState state) ; + enum virDomainNetInterfaceLinkState state); /* These APIs are for use by the internal Text/JSON monitor impl code only */ char *qemuMonitorNextCommandID(qemuMonitorPtr mon); int qemuMonitorSend(qemuMonitorPtr mon, qemuMonitorMessagePtr msg); +virJSONValuePtr qemuMonitorGetOptions(qemuMonitorPtr mon) + ATTRIBUTE_NONNULL(1); +void qemuMonitorSetOptions(qemuMonitorPtr mon, virJSONValuePtr options) + ATTRIBUTE_NONNULL(1); int qemuMonitorHMPCommandWithFd(qemuMonitorPtr mon, const char *cmd, int scm_fd, @@ -664,6 +668,9 @@ int qemuMonitorGetCommands(qemuMonitorPtr mon, char ***commands); int qemuMonitorGetEvents(qemuMonitorPtr mon, char ***events); +int qemuMonitorGetCommandLineOptionParameters(qemuMonitorPtr mon, + const char *option, + char ***params); int qemuMonitorGetKVMState(qemuMonitorPtr mon, bool *enabled, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 341b2958e1..0c011d31b0 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4275,6 +4275,126 @@ cleanup: } +int +qemuMonitorJSONGetCommandLineOptionParameters(qemuMonitorPtr mon, + const char *option, + char ***params) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + virJSONValuePtr data = NULL; + virJSONValuePtr array = NULL; + char **paramlist = NULL; + int n = 0; + size_t i; + + *params = NULL; + + /* query-command-line-options has fixed output for a given qemu + * binary; but since callers want to query parameters for one + * option at a time, we cache the option list from qemu. */ + if (!(array = qemuMonitorGetOptions(mon))) { + if (!(cmd = qemuMonitorJSONMakeCommand("query-command-line-options", + NULL))) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + if (qemuMonitorJSONHasError(reply, "CommandNotFound")) + goto cleanup; + ret = qemuMonitorJSONCheckError(cmd, reply); + } + + if (ret < 0) + goto cleanup; + + if (virJSONValueObjectRemoveKey(reply, "return", &array) <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-command-line-options reply was missing " + "return data")); + goto cleanup; + } + qemuMonitorSetOptions(mon, array); + } + + ret = -1; + + if ((n = virJSONValueArraySize(array)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-command-line-options reply data was not " + "an array")); + goto cleanup; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(array, i); + const char *tmp; + + if (!(tmp = virJSONValueObjectGetString(child, "option"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-command-line-options reply data was " + "missing 'option'")); + goto cleanup; + } + if (STREQ(tmp, option)) { + data = virJSONValueObjectGet(child, "parameters"); + break; + } + } + + if (!data) { + /* Option not found; return 0 parameters rather than an error. */ + ret = 0; + goto cleanup; + } + + if ((n = virJSONValueArraySize(data)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-command-line-options parameter data was not " + "an array")); + goto cleanup; + } + + /* null-terminated list */ + if (VIR_ALLOC_N(paramlist, n + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(data, i); + const char *tmp; + + if (!(tmp = virJSONValueObjectGetString(child, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-command-line-options parameter data was " + "missing 'name'")); + goto cleanup; + } + + if (VIR_STRDUP(paramlist[i], tmp) < 0) + goto cleanup; + } + + ret = n; + *params = paramlist; + +cleanup: + /* If we failed before getting the JSON array of options, we (try) + * to cache an empty array to speed up the next failure. */ + if (!qemuMonitorGetOptions(mon)) + qemuMonitorSetOptions(mon, virJSONValueNewArray()); + + if (ret < 0) + virStringFreeList(paramlist); + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon, bool *enabled, bool *present) diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 5ee0d84fc3..74e2476b72 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -319,6 +319,10 @@ int qemuMonitorJSONGetCommands(qemuMonitorPtr mon, int qemuMonitorJSONGetEvents(qemuMonitorPtr mon, char ***events) ATTRIBUTE_NONNULL(2); +int qemuMonitorJSONGetCommandLineOptionParameters(qemuMonitorPtr mon, + const char *option, + char ***params) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon, bool *enabled, diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 6f42598fda..acc94ca611 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -493,6 +493,108 @@ cleanup: } +static int +testQemuMonitorJSONGetCommandLineOptionParameters(const void *data) +{ + const virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data; + qemuMonitorTestPtr test = qemuMonitorTestNew(true, xmlopt); + int ret = -1; + char **params = NULL; + int nparams = 0; + + if (!test) + return -1; + + if (qemuMonitorTestAddItem(test, "query-command-line-options", + "{ " + " \"return\": [ " + " {\"parameters\": [], \"option\": \"acpi\" }," + " {\"parameters\": [" + " {\"name\": \"romfile\", " + " \"type\": \"string\"}, " + " {\"name\": \"bootindex\", " + " \"type\": \"number\"}], " + " \"option\": \"option-rom\"}" + " ]" + "}") < 0) + goto cleanup; + + /* present with params */ + if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test), + "option-rom", + ¶ms)) < 0) + goto cleanup; + + if (nparams != 2) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "nparams was %d, expected 2", nparams); + goto cleanup; + } + +#define CHECK(i, wantname) \ + do { \ + if (STRNEQ(params[i], (wantname))) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, \ + "name was %s, expected %s", \ + params[i], (wantname)); \ + goto cleanup; \ + } \ + } while (0) + + CHECK(0, "romfile"); + CHECK(1, "bootindex"); + +#undef CHECK + + virStringFreeList(params); + params = NULL; + + /* present but empty */ + if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test), + "acpi", + ¶ms)) < 0) + goto cleanup; + + if (nparams != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "nparams was %d, expected 0", nparams); + goto cleanup; + } + if (params && params[0]) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "unexpected array contents"); + goto cleanup; + } + + virStringFreeList(params); + params = NULL; + + /* no such option */ + if ((nparams = qemuMonitorGetCommandLineOptionParameters(qemuMonitorTestGetMonitor(test), + "foobar", + ¶ms)) < 0) + goto cleanup; + + if (nparams != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "nparams was %d, expected 0", nparams); + goto cleanup; + } + if (params && params[0]) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "unexpected array contents"); + goto cleanup; + } + + ret = 0; + +cleanup: + qemuMonitorTestFree(test); + virStringFreeList(params); + return ret; +} + + static int mymain(void) { @@ -520,6 +622,7 @@ mymain(void) DO_TEST(GetCPUDefinitions); DO_TEST(GetCommands); DO_TEST(GetTPMModels); + DO_TEST(GetCommandLineOptionParameters); virObjectUnref(xmlopt);