1 /* 2 * Options Visitor 3 * 4 * Copyright Red Hat, Inc. 2012-2016 5 * 6 * Author: Laszlo Ersek <lersek@redhat.com> 7 * 8 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 9 * See the COPYING.LIB file in the top-level directory. 10 * 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qapi/error.h" 15 #include "qemu/cutils.h" 16 #include "qapi/qmp/qerror.h" 17 #include "qapi/opts-visitor.h" 18 #include "qemu/queue.h" 19 #include "qemu/option_int.h" 20 #include "qapi/visitor-impl.h" 21 22 23 enum ListMode 24 { 25 LM_NONE, /* not traversing a list of repeated options */ 26 27 LM_IN_PROGRESS, /* 28 * opts_next_list() ready to be called. 29 * 30 * Generating the next list link will consume the most 31 * recently parsed QemuOpt instance of the repeated 32 * option. 33 * 34 * Parsing a value into the list link will examine the 35 * next QemuOpt instance of the repeated option, and 36 * possibly enter LM_SIGNED_INTERVAL or 37 * LM_UNSIGNED_INTERVAL. 38 */ 39 40 LM_SIGNED_INTERVAL, /* 41 * opts_next_list() has been called. 42 * 43 * Generating the next list link will consume the most 44 * recently stored element from the signed interval, 45 * parsed from the most recent QemuOpt instance of the 46 * repeated option. This may consume QemuOpt itself 47 * and return to LM_IN_PROGRESS. 48 * 49 * Parsing a value into the list link will store the 50 * next element of the signed interval. 51 */ 52 53 LM_UNSIGNED_INTERVAL, /* Same as above, only for an unsigned interval. */ 54 55 LM_TRAVERSED /* 56 * opts_next_list() has been called. 57 * 58 * No more QemuOpt instance in the list. 59 * The traversal has been completed. 60 */ 61 }; 62 63 typedef enum ListMode ListMode; 64 65 struct OptsVisitor 66 { 67 Visitor visitor; 68 69 /* Ownership remains with opts_visitor_new()'s caller. */ 70 const QemuOpts *opts_root; 71 72 unsigned depth; 73 74 /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value 75 * is a non-empty GQueue, enumerating all QemuOpt occurrences with that 76 * name. */ 77 GHashTable *unprocessed_opts; 78 79 /* The list currently being traversed with opts_start_list() / 80 * opts_next_list(). The list must have a struct element type in the 81 * schema, with a single mandatory scalar member. */ 82 ListMode list_mode; 83 GQueue *repeated_opts; 84 85 /* When parsing a list of repeating options as integers, values of the form 86 * "a-b", representing a closed interval, are allowed. Elements in the 87 * range are generated individually. 88 */ 89 union { 90 int64_t s; 91 uint64_t u; 92 } range_next, range_limit; 93 94 /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for 95 * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does 96 * not survive or escape the OptsVisitor object. 97 */ 98 QemuOpt *fake_id_opt; 99 }; 100 101 102 static OptsVisitor *to_ov(Visitor *v) 103 { 104 return container_of(v, OptsVisitor, visitor); 105 } 106 107 108 static void 109 destroy_list(gpointer list) 110 { 111 g_queue_free(list); 112 } 113 114 115 static void 116 opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) 117 { 118 GQueue *list; 119 120 list = g_hash_table_lookup(unprocessed_opts, opt->name); 121 if (list == NULL) { 122 list = g_queue_new(); 123 124 /* GHashTable will never try to free the keys -- we supply NULL as 125 * "key_destroy_func" in opts_start_struct(). Thus cast away key 126 * const-ness in order to suppress gcc's warning. 127 */ 128 g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list); 129 } 130 131 /* Similarly, destroy_list() doesn't call g_queue_free_full(). */ 132 g_queue_push_tail(list, (gpointer)opt); 133 } 134 135 136 static bool 137 opts_start_struct(Visitor *v, const char *name, void **obj, 138 size_t size, Error **errp) 139 { 140 OptsVisitor *ov = to_ov(v); 141 const QemuOpt *opt; 142 143 if (obj) { 144 *obj = g_malloc0(size); 145 } 146 if (ov->depth++ > 0) { 147 return true; 148 } 149 150 ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, 151 NULL, &destroy_list); 152 QTAILQ_FOREACH(opt, &ov->opts_root->head, next) { 153 /* ensured by qemu-option.c::opts_do_parse() */ 154 assert(strcmp(opt->name, "id") != 0); 155 156 opts_visitor_insert(ov->unprocessed_opts, opt); 157 } 158 159 if (ov->opts_root->id != NULL) { 160 ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); 161 162 ov->fake_id_opt->name = g_strdup("id"); 163 ov->fake_id_opt->str = g_strdup(ov->opts_root->id); 164 opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt); 165 } 166 return true; 167 } 168 169 170 static bool 171 opts_check_struct(Visitor *v, Error **errp) 172 { 173 OptsVisitor *ov = to_ov(v); 174 GHashTableIter iter; 175 GQueue *any; 176 177 if (ov->depth > 1) { 178 return true; 179 } 180 181 /* we should have processed all (distinct) QemuOpt instances */ 182 g_hash_table_iter_init(&iter, ov->unprocessed_opts); 183 if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) { 184 const QemuOpt *first; 185 186 first = g_queue_peek_head(any); 187 error_setg(errp, QERR_INVALID_PARAMETER, first->name); 188 return false; 189 } 190 return true; 191 } 192 193 194 static void 195 opts_end_struct(Visitor *v, void **obj) 196 { 197 OptsVisitor *ov = to_ov(v); 198 199 if (--ov->depth > 0) { 200 return; 201 } 202 203 g_hash_table_destroy(ov->unprocessed_opts); 204 ov->unprocessed_opts = NULL; 205 if (ov->fake_id_opt) { 206 g_free(ov->fake_id_opt->name); 207 g_free(ov->fake_id_opt->str); 208 g_free(ov->fake_id_opt); 209 } 210 ov->fake_id_opt = NULL; 211 } 212 213 214 static GQueue * 215 lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) 216 { 217 GQueue *list; 218 219 list = g_hash_table_lookup(ov->unprocessed_opts, name); 220 if (!list) { 221 error_setg(errp, QERR_MISSING_PARAMETER, name); 222 } 223 return list; 224 } 225 226 227 static bool 228 opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size, 229 Error **errp) 230 { 231 OptsVisitor *ov = to_ov(v); 232 233 /* we can't traverse a list in a list */ 234 assert(ov->list_mode == LM_NONE); 235 /* we don't support visits without a list */ 236 assert(list); 237 ov->repeated_opts = lookup_distinct(ov, name, errp); 238 if (!ov->repeated_opts) { 239 *list = NULL; 240 return false; 241 } 242 ov->list_mode = LM_IN_PROGRESS; 243 *list = g_malloc0(size); 244 return true; 245 } 246 247 248 static GenericList * 249 opts_next_list(Visitor *v, GenericList *tail, size_t size) 250 { 251 OptsVisitor *ov = to_ov(v); 252 253 switch (ov->list_mode) { 254 case LM_TRAVERSED: 255 return NULL; 256 case LM_SIGNED_INTERVAL: 257 case LM_UNSIGNED_INTERVAL: 258 if (ov->list_mode == LM_SIGNED_INTERVAL) { 259 if (ov->range_next.s < ov->range_limit.s) { 260 ++ov->range_next.s; 261 break; 262 } 263 } else if (ov->range_next.u < ov->range_limit.u) { 264 ++ov->range_next.u; 265 break; 266 } 267 ov->list_mode = LM_IN_PROGRESS; 268 /* range has been completed, fall through in order to pop option */ 269 270 case LM_IN_PROGRESS: { 271 const QemuOpt *opt; 272 273 opt = g_queue_pop_head(ov->repeated_opts); 274 if (g_queue_is_empty(ov->repeated_opts)) { 275 g_hash_table_remove(ov->unprocessed_opts, opt->name); 276 ov->repeated_opts = NULL; 277 ov->list_mode = LM_TRAVERSED; 278 return NULL; 279 } 280 break; 281 } 282 283 default: 284 abort(); 285 } 286 287 tail->next = g_malloc0(size); 288 return tail->next; 289 } 290 291 292 static bool 293 opts_check_list(Visitor *v, Error **errp) 294 { 295 /* 296 * Unvisited list elements will be reported later when checking 297 * whether unvisited struct members remain. 298 */ 299 return true; 300 } 301 302 303 static void 304 opts_end_list(Visitor *v, void **obj) 305 { 306 OptsVisitor *ov = to_ov(v); 307 308 assert(ov->list_mode == LM_IN_PROGRESS || 309 ov->list_mode == LM_SIGNED_INTERVAL || 310 ov->list_mode == LM_UNSIGNED_INTERVAL || 311 ov->list_mode == LM_TRAVERSED); 312 ov->repeated_opts = NULL; 313 ov->list_mode = LM_NONE; 314 } 315 316 317 static const QemuOpt * 318 lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp) 319 { 320 if (ov->list_mode == LM_NONE) { 321 GQueue *list; 322 323 /* the last occurrence of any QemuOpt takes effect when queried by name 324 */ 325 list = lookup_distinct(ov, name, errp); 326 return list ? g_queue_peek_tail(list) : NULL; 327 } 328 if (ov->list_mode == LM_TRAVERSED) { 329 error_setg(errp, "Fewer list elements than expected"); 330 return NULL; 331 } 332 assert(ov->list_mode == LM_IN_PROGRESS); 333 return g_queue_peek_head(ov->repeated_opts); 334 } 335 336 337 static void 338 processed(OptsVisitor *ov, const char *name) 339 { 340 if (ov->list_mode == LM_NONE) { 341 g_hash_table_remove(ov->unprocessed_opts, name); 342 return; 343 } 344 assert(ov->list_mode == LM_IN_PROGRESS); 345 /* do nothing */ 346 } 347 348 349 static bool 350 opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) 351 { 352 OptsVisitor *ov = to_ov(v); 353 const QemuOpt *opt; 354 355 opt = lookup_scalar(ov, name, errp); 356 if (!opt) { 357 *obj = NULL; 358 return false; 359 } 360 *obj = g_strdup(opt->str ? opt->str : ""); 361 /* Note that we consume a string even if this is called as part of 362 * an enum visit that later fails because the string is not a 363 * valid enum value; this is harmless because tracking what gets 364 * consumed only matters to visit_end_struct() as the final error 365 * check if there were no other failures during the visit. */ 366 processed(ov, name); 367 return true; 368 } 369 370 371 /* mimics qemu-option.c::parse_option_bool() */ 372 static bool 373 opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) 374 { 375 OptsVisitor *ov = to_ov(v); 376 const QemuOpt *opt; 377 378 opt = lookup_scalar(ov, name, errp); 379 if (!opt) { 380 return false; 381 } 382 383 if (opt->str) { 384 if (strcmp(opt->str, "on") == 0 || 385 strcmp(opt->str, "yes") == 0 || 386 strcmp(opt->str, "y") == 0) { 387 *obj = true; 388 } else if (strcmp(opt->str, "off") == 0 || 389 strcmp(opt->str, "no") == 0 || 390 strcmp(opt->str, "n") == 0) { 391 *obj = false; 392 } else { 393 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 394 "on|yes|y|off|no|n"); 395 return false; 396 } 397 } else { 398 *obj = true; 399 } 400 401 processed(ov, name); 402 return true; 403 } 404 405 406 static bool 407 opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) 408 { 409 OptsVisitor *ov = to_ov(v); 410 const QemuOpt *opt; 411 const char *str; 412 long long val; 413 char *endptr; 414 415 if (ov->list_mode == LM_SIGNED_INTERVAL) { 416 *obj = ov->range_next.s; 417 return true; 418 } 419 420 opt = lookup_scalar(ov, name, errp); 421 if (!opt) { 422 return false; 423 } 424 str = opt->str ? opt->str : ""; 425 426 /* we've gotten past lookup_scalar() */ 427 assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 428 429 errno = 0; 430 val = strtoll(str, &endptr, 0); 431 if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) { 432 if (*endptr == '\0') { 433 *obj = val; 434 processed(ov, name); 435 return true; 436 } 437 if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 438 long long val2; 439 440 str = endptr + 1; 441 val2 = strtoll(str, &endptr, 0); 442 if (errno == 0 && endptr > str && *endptr == '\0' && 443 INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 && 444 (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX || 445 val2 < val + OPTS_VISITOR_RANGE_MAX)) { 446 ov->range_next.s = val; 447 ov->range_limit.s = val2; 448 ov->list_mode = LM_SIGNED_INTERVAL; 449 450 /* as if entering on the top */ 451 *obj = ov->range_next.s; 452 return true; 453 } 454 } 455 } 456 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 457 (ov->list_mode == LM_NONE) ? "an int64 value" : 458 "an int64 value or range"); 459 return false; 460 } 461 462 463 static bool 464 opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) 465 { 466 OptsVisitor *ov = to_ov(v); 467 const QemuOpt *opt; 468 const char *str; 469 unsigned long long val; 470 char *endptr; 471 472 if (ov->list_mode == LM_UNSIGNED_INTERVAL) { 473 *obj = ov->range_next.u; 474 return true; 475 } 476 477 opt = lookup_scalar(ov, name, errp); 478 if (!opt) { 479 return false; 480 } 481 str = opt->str; 482 483 /* we've gotten past lookup_scalar() */ 484 assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 485 486 if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { 487 if (*endptr == '\0') { 488 *obj = val; 489 processed(ov, name); 490 return true; 491 } 492 if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 493 unsigned long long val2; 494 495 str = endptr + 1; 496 if (parse_uint_full(str, &val2, 0) == 0 && 497 val2 <= UINT64_MAX && val <= val2 && 498 val2 - val < OPTS_VISITOR_RANGE_MAX) { 499 ov->range_next.u = val; 500 ov->range_limit.u = val2; 501 ov->list_mode = LM_UNSIGNED_INTERVAL; 502 503 /* as if entering on the top */ 504 *obj = ov->range_next.u; 505 return true; 506 } 507 } 508 } 509 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 510 (ov->list_mode == LM_NONE) ? "a uint64 value" : 511 "a uint64 value or range"); 512 return false; 513 } 514 515 516 static bool 517 opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) 518 { 519 OptsVisitor *ov = to_ov(v); 520 const QemuOpt *opt; 521 int err; 522 523 opt = lookup_scalar(ov, name, errp); 524 if (!opt) { 525 return false; 526 } 527 528 err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj); 529 if (err < 0) { 530 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 531 "a size value"); 532 return false; 533 } 534 535 processed(ov, name); 536 return true; 537 } 538 539 540 static void 541 opts_optional(Visitor *v, const char *name, bool *present) 542 { 543 OptsVisitor *ov = to_ov(v); 544 545 /* we only support a single mandatory scalar field in a list node */ 546 assert(ov->list_mode == LM_NONE); 547 *present = (lookup_distinct(ov, name, NULL) != NULL); 548 } 549 550 551 static void 552 opts_free(Visitor *v) 553 { 554 OptsVisitor *ov = to_ov(v); 555 556 if (ov->unprocessed_opts != NULL) { 557 g_hash_table_destroy(ov->unprocessed_opts); 558 } 559 g_free(ov->fake_id_opt); 560 g_free(ov); 561 } 562 563 564 Visitor * 565 opts_visitor_new(const QemuOpts *opts) 566 { 567 OptsVisitor *ov; 568 569 assert(opts); 570 ov = g_malloc0(sizeof *ov); 571 572 ov->visitor.type = VISITOR_INPUT; 573 574 ov->visitor.start_struct = &opts_start_struct; 575 ov->visitor.check_struct = &opts_check_struct; 576 ov->visitor.end_struct = &opts_end_struct; 577 578 ov->visitor.start_list = &opts_start_list; 579 ov->visitor.next_list = &opts_next_list; 580 ov->visitor.check_list = &opts_check_list; 581 ov->visitor.end_list = &opts_end_list; 582 583 ov->visitor.type_int64 = &opts_type_int64; 584 ov->visitor.type_uint64 = &opts_type_uint64; 585 ov->visitor.type_size = &opts_type_size; 586 ov->visitor.type_bool = &opts_type_bool; 587 ov->visitor.type_str = &opts_type_str; 588 589 /* type_number() is not filled in, but this is not the first visitor to 590 * skip some mandatory methods... */ 591 592 ov->visitor.optional = &opts_optional; 593 ov->visitor.free = opts_free; 594 595 ov->opts_root = opts; 596 597 return &ov->visitor; 598 } 599