1 /* 2 * Input Visitor 3 * 4 * Copyright (C) 2012-2016 Red Hat, Inc. 5 * Copyright IBM, Corp. 2011 6 * 7 * Authors: 8 * Anthony Liguori <aliguori@us.ibm.com> 9 * 10 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 11 * See the COPYING.LIB file in the top-level directory. 12 * 13 */ 14 15 #include "qemu/osdep.h" 16 #include "qapi/error.h" 17 #include "qapi/qobject-input-visitor.h" 18 #include "qapi/visitor-impl.h" 19 #include "qemu/queue.h" 20 #include "qemu-common.h" 21 #include "qapi/qmp/types.h" 22 #include "qapi/qmp/qerror.h" 23 24 typedef struct StackObject { 25 const char *name; /* Name of @obj in its parent, if any */ 26 QObject *obj; /* QDict or QList being visited */ 27 void *qapi; /* sanity check that caller uses same pointer */ 28 29 GHashTable *h; /* If @obj is QDict: unvisited keys */ 30 const QListEntry *entry; /* If @obj is QList: unvisited tail */ 31 unsigned index; /* If @obj is QList: list index of @entry */ 32 33 QSLIST_ENTRY(StackObject) node; /* parent */ 34 } StackObject; 35 36 struct QObjectInputVisitor { 37 Visitor visitor; 38 39 /* Root of visit at visitor creation. */ 40 QObject *root; 41 42 /* Stack of objects being visited (all entries will be either 43 * QDict or QList). */ 44 QSLIST_HEAD(, StackObject) stack; 45 46 GString *errname; /* Accumulator for full_name() */ 47 }; 48 49 static QObjectInputVisitor *to_qiv(Visitor *v) 50 { 51 return container_of(v, QObjectInputVisitor, visitor); 52 } 53 54 static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, 55 int n) 56 { 57 StackObject *so; 58 char buf[32]; 59 60 if (qiv->errname) { 61 g_string_truncate(qiv->errname, 0); 62 } else { 63 qiv->errname = g_string_new(""); 64 } 65 66 QSLIST_FOREACH(so , &qiv->stack, node) { 67 if (n) { 68 n--; 69 } else if (qobject_type(so->obj) == QTYPE_QDICT) { 70 g_string_prepend(qiv->errname, name ?: "<anonymous>"); 71 g_string_prepend_c(qiv->errname, '.'); 72 } else { 73 snprintf(buf, sizeof(buf), "[%u]", so->index); 74 g_string_prepend(qiv->errname, buf); 75 } 76 name = so->name; 77 } 78 assert(!n); 79 80 if (name) { 81 g_string_prepend(qiv->errname, name); 82 } else if (qiv->errname->str[0] == '.') { 83 g_string_erase(qiv->errname, 0, 1); 84 } else if (!qiv->errname->str[0]) { 85 return "<anonymous>"; 86 } 87 88 return qiv->errname->str; 89 } 90 91 static const char *full_name(QObjectInputVisitor *qiv, const char *name) 92 { 93 return full_name_nth(qiv, name, 0); 94 } 95 96 static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, 97 const char *name, 98 bool consume) 99 { 100 StackObject *tos; 101 QObject *qobj; 102 QObject *ret; 103 104 if (QSLIST_EMPTY(&qiv->stack)) { 105 /* Starting at root, name is ignored. */ 106 assert(qiv->root); 107 return qiv->root; 108 } 109 110 /* We are in a container; find the next element. */ 111 tos = QSLIST_FIRST(&qiv->stack); 112 qobj = tos->obj; 113 assert(qobj); 114 115 if (qobject_type(qobj) == QTYPE_QDICT) { 116 assert(name); 117 ret = qdict_get(qobject_to_qdict(qobj), name); 118 if (tos->h && consume && ret) { 119 bool removed = g_hash_table_remove(tos->h, name); 120 assert(removed); 121 } 122 } else { 123 assert(qobject_type(qobj) == QTYPE_QLIST); 124 assert(!name); 125 if (tos->entry) { 126 ret = qlist_entry_obj(tos->entry); 127 if (consume) { 128 tos->entry = qlist_next(tos->entry); 129 } 130 } else { 131 ret = NULL; 132 } 133 if (consume) { 134 tos->index++; 135 } 136 } 137 138 return ret; 139 } 140 141 static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, 142 const char *name, 143 bool consume, Error **errp) 144 { 145 QObject *obj = qobject_input_try_get_object(qiv, name, consume); 146 147 if (!obj) { 148 error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); 149 } 150 return obj; 151 } 152 153 static void qdict_add_key(const char *key, QObject *obj, void *opaque) 154 { 155 GHashTable *h = opaque; 156 g_hash_table_insert(h, (gpointer) key, NULL); 157 } 158 159 static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, 160 const char *name, 161 QObject *obj, void *qapi) 162 { 163 GHashTable *h; 164 StackObject *tos = g_new0(StackObject, 1); 165 166 assert(obj); 167 tos->name = name; 168 tos->obj = obj; 169 tos->qapi = qapi; 170 171 if (qobject_type(obj) == QTYPE_QDICT) { 172 h = g_hash_table_new(g_str_hash, g_str_equal); 173 qdict_iter(qobject_to_qdict(obj), qdict_add_key, h); 174 tos->h = h; 175 } else { 176 assert(qobject_type(obj) == QTYPE_QLIST); 177 tos->entry = qlist_first(qobject_to_qlist(obj)); 178 tos->index = -1; 179 } 180 181 QSLIST_INSERT_HEAD(&qiv->stack, tos, node); 182 return tos->entry; 183 } 184 185 186 static void qobject_input_check_struct(Visitor *v, Error **errp) 187 { 188 QObjectInputVisitor *qiv = to_qiv(v); 189 StackObject *tos = QSLIST_FIRST(&qiv->stack); 190 GHashTableIter iter; 191 const char *key; 192 193 assert(tos && !tos->entry); 194 195 g_hash_table_iter_init(&iter, tos->h); 196 if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { 197 error_setg(errp, "Parameter '%s' is unexpected", 198 full_name(qiv, key)); 199 } 200 } 201 202 static void qobject_input_stack_object_free(StackObject *tos) 203 { 204 if (tos->h) { 205 g_hash_table_unref(tos->h); 206 } 207 208 g_free(tos); 209 } 210 211 static void qobject_input_pop(Visitor *v, void **obj) 212 { 213 QObjectInputVisitor *qiv = to_qiv(v); 214 StackObject *tos = QSLIST_FIRST(&qiv->stack); 215 216 assert(tos && tos->qapi == obj); 217 QSLIST_REMOVE_HEAD(&qiv->stack, node); 218 qobject_input_stack_object_free(tos); 219 } 220 221 static void qobject_input_start_struct(Visitor *v, const char *name, void **obj, 222 size_t size, Error **errp) 223 { 224 QObjectInputVisitor *qiv = to_qiv(v); 225 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 226 227 if (obj) { 228 *obj = NULL; 229 } 230 if (!qobj) { 231 return; 232 } 233 if (qobject_type(qobj) != QTYPE_QDICT) { 234 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 235 full_name(qiv, name), "object"); 236 return; 237 } 238 239 qobject_input_push(qiv, name, qobj, obj); 240 241 if (obj) { 242 *obj = g_malloc0(size); 243 } 244 } 245 246 247 static void qobject_input_start_list(Visitor *v, const char *name, 248 GenericList **list, size_t size, 249 Error **errp) 250 { 251 QObjectInputVisitor *qiv = to_qiv(v); 252 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 253 const QListEntry *entry; 254 255 if (list) { 256 *list = NULL; 257 } 258 if (!qobj) { 259 return; 260 } 261 if (qobject_type(qobj) != QTYPE_QLIST) { 262 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 263 full_name(qiv, name), "array"); 264 return; 265 } 266 267 entry = qobject_input_push(qiv, name, qobj, list); 268 if (entry && list) { 269 *list = g_malloc0(size); 270 } 271 } 272 273 static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, 274 size_t size) 275 { 276 QObjectInputVisitor *qiv = to_qiv(v); 277 StackObject *tos = QSLIST_FIRST(&qiv->stack); 278 279 assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST); 280 281 if (!tos->entry) { 282 return NULL; 283 } 284 tail->next = g_malloc0(size); 285 return tail->next; 286 } 287 288 static void qobject_input_check_list(Visitor *v, Error **errp) 289 { 290 QObjectInputVisitor *qiv = to_qiv(v); 291 StackObject *tos = QSLIST_FIRST(&qiv->stack); 292 293 assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST); 294 295 if (tos->entry) { 296 error_setg(errp, "Only %u list elements expected in %s", 297 tos->index + 1, full_name_nth(qiv, NULL, 1)); 298 } 299 } 300 301 302 static void qobject_input_start_alternate(Visitor *v, const char *name, 303 GenericAlternate **obj, size_t size, 304 bool promote_int, Error **errp) 305 { 306 QObjectInputVisitor *qiv = to_qiv(v); 307 QObject *qobj = qobject_input_get_object(qiv, name, false, errp); 308 309 if (!qobj) { 310 *obj = NULL; 311 return; 312 } 313 *obj = g_malloc0(size); 314 (*obj)->type = qobject_type(qobj); 315 if (promote_int && (*obj)->type == QTYPE_QINT) { 316 (*obj)->type = QTYPE_QFLOAT; 317 } 318 } 319 320 static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, 321 Error **errp) 322 { 323 QObjectInputVisitor *qiv = to_qiv(v); 324 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 325 QInt *qint; 326 327 if (!qobj) { 328 return; 329 } 330 qint = qobject_to_qint(qobj); 331 if (!qint) { 332 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 333 full_name(qiv, name), "integer"); 334 return; 335 } 336 337 *obj = qint_get_int(qint); 338 } 339 340 static void qobject_input_type_uint64(Visitor *v, const char *name, 341 uint64_t *obj, Error **errp) 342 { 343 /* FIXME: qobject_to_qint mishandles values over INT64_MAX */ 344 QObjectInputVisitor *qiv = to_qiv(v); 345 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 346 QInt *qint; 347 348 if (!qobj) { 349 return; 350 } 351 qint = qobject_to_qint(qobj); 352 if (!qint) { 353 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 354 full_name(qiv, name), "integer"); 355 return; 356 } 357 358 *obj = qint_get_int(qint); 359 } 360 361 static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj, 362 Error **errp) 363 { 364 QObjectInputVisitor *qiv = to_qiv(v); 365 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 366 QBool *qbool; 367 368 if (!qobj) { 369 return; 370 } 371 qbool = qobject_to_qbool(qobj); 372 if (!qbool) { 373 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 374 full_name(qiv, name), "boolean"); 375 return; 376 } 377 378 *obj = qbool_get_bool(qbool); 379 } 380 381 static void qobject_input_type_str(Visitor *v, const char *name, char **obj, 382 Error **errp) 383 { 384 QObjectInputVisitor *qiv = to_qiv(v); 385 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 386 QString *qstr; 387 388 *obj = NULL; 389 if (!qobj) { 390 return; 391 } 392 qstr = qobject_to_qstring(qobj); 393 if (!qstr) { 394 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 395 full_name(qiv, name), "string"); 396 return; 397 } 398 399 *obj = g_strdup(qstring_get_str(qstr)); 400 } 401 402 static void qobject_input_type_number(Visitor *v, const char *name, double *obj, 403 Error **errp) 404 { 405 QObjectInputVisitor *qiv = to_qiv(v); 406 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 407 QInt *qint; 408 QFloat *qfloat; 409 410 if (!qobj) { 411 return; 412 } 413 qint = qobject_to_qint(qobj); 414 if (qint) { 415 *obj = qint_get_int(qobject_to_qint(qobj)); 416 return; 417 } 418 419 qfloat = qobject_to_qfloat(qobj); 420 if (qfloat) { 421 *obj = qfloat_get_double(qobject_to_qfloat(qobj)); 422 return; 423 } 424 425 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 426 full_name(qiv, name), "number"); 427 } 428 429 static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, 430 Error **errp) 431 { 432 QObjectInputVisitor *qiv = to_qiv(v); 433 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 434 435 *obj = NULL; 436 if (!qobj) { 437 return; 438 } 439 440 qobject_incref(qobj); 441 *obj = qobj; 442 } 443 444 static void qobject_input_type_null(Visitor *v, const char *name, Error **errp) 445 { 446 QObjectInputVisitor *qiv = to_qiv(v); 447 QObject *qobj = qobject_input_get_object(qiv, name, true, errp); 448 449 if (!qobj) { 450 return; 451 } 452 453 if (qobject_type(qobj) != QTYPE_QNULL) { 454 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, 455 full_name(qiv, name), "null"); 456 } 457 } 458 459 static void qobject_input_optional(Visitor *v, const char *name, bool *present) 460 { 461 QObjectInputVisitor *qiv = to_qiv(v); 462 QObject *qobj = qobject_input_try_get_object(qiv, name, false); 463 464 if (!qobj) { 465 *present = false; 466 return; 467 } 468 469 *present = true; 470 } 471 472 static void qobject_input_free(Visitor *v) 473 { 474 QObjectInputVisitor *qiv = to_qiv(v); 475 476 while (!QSLIST_EMPTY(&qiv->stack)) { 477 StackObject *tos = QSLIST_FIRST(&qiv->stack); 478 479 QSLIST_REMOVE_HEAD(&qiv->stack, node); 480 qobject_input_stack_object_free(tos); 481 } 482 483 qobject_decref(qiv->root); 484 if (qiv->errname) { 485 g_string_free(qiv->errname, TRUE); 486 } 487 g_free(qiv); 488 } 489 490 Visitor *qobject_input_visitor_new(QObject *obj) 491 { 492 QObjectInputVisitor *v; 493 494 assert(obj); 495 v = g_malloc0(sizeof(*v)); 496 497 v->visitor.type = VISITOR_INPUT; 498 v->visitor.start_struct = qobject_input_start_struct; 499 v->visitor.check_struct = qobject_input_check_struct; 500 v->visitor.end_struct = qobject_input_pop; 501 v->visitor.start_list = qobject_input_start_list; 502 v->visitor.next_list = qobject_input_next_list; 503 v->visitor.check_list = qobject_input_check_list; 504 v->visitor.end_list = qobject_input_pop; 505 v->visitor.start_alternate = qobject_input_start_alternate; 506 v->visitor.type_int64 = qobject_input_type_int64; 507 v->visitor.type_uint64 = qobject_input_type_uint64; 508 v->visitor.type_bool = qobject_input_type_bool; 509 v->visitor.type_str = qobject_input_type_str; 510 v->visitor.type_number = qobject_input_type_number; 511 v->visitor.type_any = qobject_input_type_any; 512 v->visitor.type_null = qobject_input_type_null; 513 v->visitor.optional = qobject_input_optional; 514 v->visitor.free = qobject_input_free; 515 516 v->root = obj; 517 qobject_incref(obj); 518 519 return &v->visitor; 520 } 521