Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 1 | /* |
Philippe Mathieu-Daudé | 32cad1f | 2024-12-03 15:20:13 +0100 | [diff] [blame] | 2 | * QMP commands related to the monitor (common to system and tools) |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 3 | * |
| 4 | * Copyright (c) 2003-2004 Fabrice Bellard |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | |
| 25 | #include "qemu/osdep.h" |
| 26 | |
| 27 | #include "monitor-internal.h" |
| 28 | #include "qemu-version.h" |
Markus Armbruster | 624fa80 | 2021-03-18 16:55:14 +0100 | [diff] [blame] | 29 | #include "qapi/compat-policy.h" |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 30 | #include "qapi/error.h" |
| 31 | #include "qapi/qapi-commands-control.h" |
Markus Armbruster | 624fa80 | 2021-03-18 16:55:14 +0100 | [diff] [blame] | 32 | #include "qapi/qapi-commands-introspect.h" |
Kevin Wolf | 7d3f505 | 2020-01-29 11:22:39 +0100 | [diff] [blame] | 33 | #include "qapi/qapi-introspect.h" |
Markus Armbruster | 624fa80 | 2021-03-18 16:55:14 +0100 | [diff] [blame] | 34 | #include "qapi/qapi-visit-introspect.h" |
| 35 | #include "qapi/qobject-input-visitor.h" |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 36 | |
| 37 | /* |
| 38 | * Accept QMP capabilities in @list for @mon. |
| 39 | * On success, set mon->qmp.capab[], and return true. |
| 40 | * On error, set @errp, and return false. |
| 41 | */ |
| 42 | static bool qmp_caps_accept(MonitorQMP *mon, QMPCapabilityList *list, |
| 43 | Error **errp) |
| 44 | { |
| 45 | GString *unavailable = NULL; |
| 46 | bool capab[QMP_CAPABILITY__MAX]; |
| 47 | |
| 48 | memset(capab, 0, sizeof(capab)); |
| 49 | |
| 50 | for (; list; list = list->next) { |
| 51 | if (!mon->capab_offered[list->value]) { |
| 52 | if (!unavailable) { |
| 53 | unavailable = g_string_new(QMPCapability_str(list->value)); |
| 54 | } else { |
| 55 | g_string_append_printf(unavailable, ", %s", |
| 56 | QMPCapability_str(list->value)); |
| 57 | } |
| 58 | } |
| 59 | capab[list->value] = true; |
| 60 | } |
| 61 | |
| 62 | if (unavailable) { |
| 63 | error_setg(errp, "Capability %s not available", unavailable->str); |
| 64 | g_string_free(unavailable, true); |
| 65 | return false; |
| 66 | } |
| 67 | |
| 68 | memcpy(mon->capab, capab, sizeof(capab)); |
| 69 | return true; |
| 70 | } |
| 71 | |
| 72 | void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, |
| 73 | Error **errp) |
| 74 | { |
Kevin Wolf | 947e474 | 2020-10-05 17:58:44 +0200 | [diff] [blame] | 75 | Monitor *cur_mon = monitor_cur(); |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 76 | MonitorQMP *mon; |
| 77 | |
| 78 | assert(monitor_is_qmp(cur_mon)); |
| 79 | mon = container_of(cur_mon, MonitorQMP, common); |
| 80 | |
| 81 | if (mon->commands == &qmp_commands) { |
| 82 | error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, |
| 83 | "Capabilities negotiation is already complete, command " |
| 84 | "ignored"); |
| 85 | return; |
| 86 | } |
| 87 | |
| 88 | if (!qmp_caps_accept(mon, enable, errp)) { |
| 89 | return; |
| 90 | } |
| 91 | |
| 92 | mon->commands = &qmp_commands; |
| 93 | } |
| 94 | |
| 95 | VersionInfo *qmp_query_version(Error **errp) |
| 96 | { |
| 97 | VersionInfo *info = g_new0(VersionInfo, 1); |
| 98 | |
| 99 | info->qemu = g_new0(VersionTriple, 1); |
| 100 | info->qemu->major = QEMU_VERSION_MAJOR; |
| 101 | info->qemu->minor = QEMU_VERSION_MINOR; |
| 102 | info->qemu->micro = QEMU_VERSION_MICRO; |
| 103 | info->package = g_strdup(QEMU_PKGVERSION); |
| 104 | |
| 105 | return info; |
| 106 | } |
| 107 | |
Marc-André Lureau | f0ccc00 | 2020-03-16 18:18:24 +0100 | [diff] [blame] | 108 | static void query_commands_cb(const QmpCommand *cmd, void *opaque) |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 109 | { |
Eric Blake | 240ee8b | 2021-01-13 16:10:10 -0600 | [diff] [blame] | 110 | CommandInfo *info; |
| 111 | CommandInfoList **list = opaque; |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 112 | |
| 113 | if (!cmd->enabled) { |
| 114 | return; |
| 115 | } |
| 116 | |
| 117 | info = g_malloc0(sizeof(*info)); |
Eric Blake | 240ee8b | 2021-01-13 16:10:10 -0600 | [diff] [blame] | 118 | info->name = g_strdup(cmd->name); |
| 119 | QAPI_LIST_PREPEND(*list, info); |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | CommandInfoList *qmp_query_commands(Error **errp) |
| 123 | { |
| 124 | CommandInfoList *list = NULL; |
Kevin Wolf | 947e474 | 2020-10-05 17:58:44 +0200 | [diff] [blame] | 125 | Monitor *cur_mon = monitor_cur(); |
Kevin Wolf | 5676281 | 2020-01-29 11:22:38 +0100 | [diff] [blame] | 126 | MonitorQMP *mon; |
| 127 | |
| 128 | assert(monitor_is_qmp(cur_mon)); |
| 129 | mon = container_of(cur_mon, MonitorQMP, common); |
| 130 | |
| 131 | qmp_for_each_command(mon->commands, query_commands_cb, &list); |
| 132 | |
| 133 | return list; |
| 134 | } |
| 135 | |
Markus Armbruster | 2df68d7 | 2021-03-18 16:55:15 +0100 | [diff] [blame] | 136 | static void *split_off_generic_list(void *list, |
| 137 | bool (*splitp)(void *elt), |
| 138 | void **part) |
| 139 | { |
| 140 | GenericList *keep = NULL, **keep_tailp = &keep; |
| 141 | GenericList *split = NULL, **split_tailp = &split; |
| 142 | GenericList *tail; |
| 143 | |
| 144 | for (tail = list; tail; tail = tail->next) { |
| 145 | if (splitp(tail)) { |
| 146 | *split_tailp = tail; |
| 147 | split_tailp = &tail->next; |
| 148 | } else { |
| 149 | *keep_tailp = tail; |
| 150 | keep_tailp = &tail->next; |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | *keep_tailp = *split_tailp = NULL; |
| 155 | *part = split; |
| 156 | return keep; |
| 157 | } |
| 158 | |
| 159 | static bool is_in(const char *s, strList *list) |
| 160 | { |
| 161 | strList *tail; |
| 162 | |
| 163 | for (tail = list; tail; tail = tail->next) { |
| 164 | if (!strcmp(tail->value, s)) { |
| 165 | return true; |
| 166 | } |
| 167 | } |
| 168 | return false; |
| 169 | } |
| 170 | |
| 171 | static bool is_entity_deprecated(void *link) |
| 172 | { |
| 173 | return is_in("deprecated", ((SchemaInfoList *)link)->value->features); |
| 174 | } |
| 175 | |
| 176 | static bool is_member_deprecated(void *link) |
| 177 | { |
| 178 | return is_in("deprecated", |
| 179 | ((SchemaInfoObjectMemberList *)link)->value->features); |
| 180 | } |
| 181 | |
| 182 | static SchemaInfoList *zap_deprecated(SchemaInfoList *schema) |
| 183 | { |
| 184 | void *to_zap; |
| 185 | SchemaInfoList *tail; |
| 186 | SchemaInfo *ent; |
| 187 | |
| 188 | schema = split_off_generic_list(schema, is_entity_deprecated, &to_zap); |
| 189 | qapi_free_SchemaInfoList(to_zap); |
| 190 | |
| 191 | for (tail = schema; tail; tail = tail->next) { |
| 192 | ent = tail->value; |
| 193 | if (ent->meta_type == SCHEMA_META_TYPE_OBJECT) { |
| 194 | ent->u.object.members |
| 195 | = split_off_generic_list(ent->u.object.members, |
| 196 | is_member_deprecated, &to_zap); |
| 197 | qapi_free_SchemaInfoObjectMemberList(to_zap); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | return schema; |
| 202 | } |
| 203 | |
Markus Armbruster | 624fa80 | 2021-03-18 16:55:14 +0100 | [diff] [blame] | 204 | SchemaInfoList *qmp_query_qmp_schema(Error **errp) |
Kevin Wolf | 7d3f505 | 2020-01-29 11:22:39 +0100 | [diff] [blame] | 205 | { |
Markus Armbruster | 624fa80 | 2021-03-18 16:55:14 +0100 | [diff] [blame] | 206 | QObject *obj = qobject_from_qlit(&qmp_schema_qlit); |
| 207 | Visitor *v = qobject_input_visitor_new(obj); |
| 208 | SchemaInfoList *schema = NULL; |
| 209 | |
| 210 | /* test_visitor_in_qmp_introspect() ensures this can't fail */ |
| 211 | visit_type_SchemaInfoList(v, NULL, &schema, &error_abort); |
| 212 | g_assert(schema); |
| 213 | |
| 214 | qobject_unref(obj); |
| 215 | visit_free(v); |
| 216 | |
Markus Armbruster | 2df68d7 | 2021-03-18 16:55:15 +0100 | [diff] [blame] | 217 | if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) { |
| 218 | return zap_deprecated(schema); |
| 219 | } |
Markus Armbruster | 624fa80 | 2021-03-18 16:55:14 +0100 | [diff] [blame] | 220 | return schema; |
Kevin Wolf | 7d3f505 | 2020-01-29 11:22:39 +0100 | [diff] [blame] | 221 | } |