opts-visitor.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*
  2. * Options Visitor
  3. *
  4. * Copyright Red Hat, Inc. 2012
  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. #include "opts-visitor.h"
  13. #include "qemu-queue.h"
  14. #include "qemu-option-internal.h"
  15. #include "qapi-visit-impl.h"
  16. struct OptsVisitor
  17. {
  18. Visitor visitor;
  19. /* Ownership remains with opts_visitor_new()'s caller. */
  20. const QemuOpts *opts_root;
  21. unsigned depth;
  22. /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value
  23. * is a non-empty GQueue, enumerating all QemuOpt occurrences with that
  24. * name. */
  25. GHashTable *unprocessed_opts;
  26. /* The list currently being traversed with opts_start_list() /
  27. * opts_next_list(). The list must have a struct element type in the
  28. * schema, with a single mandatory scalar member. */
  29. GQueue *repeated_opts;
  30. bool repeated_opts_first;
  31. /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for
  32. * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does
  33. * not survive or escape the OptsVisitor object.
  34. */
  35. QemuOpt *fake_id_opt;
  36. };
  37. static void
  38. destroy_list(gpointer list)
  39. {
  40. g_queue_free(list);
  41. }
  42. static void
  43. opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
  44. {
  45. GQueue *list;
  46. list = g_hash_table_lookup(unprocessed_opts, opt->name);
  47. if (list == NULL) {
  48. list = g_queue_new();
  49. /* GHashTable will never try to free the keys -- we supply NULL as
  50. * "key_destroy_func" in opts_start_struct(). Thus cast away key
  51. * const-ness in order to suppress gcc's warning.
  52. */
  53. g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list);
  54. }
  55. /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
  56. g_queue_push_tail(list, (gpointer)opt);
  57. }
  58. static void
  59. opts_start_struct(Visitor *v, void **obj, const char *kind,
  60. const char *name, size_t size, Error **errp)
  61. {
  62. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  63. const QemuOpt *opt;
  64. *obj = g_malloc0(size > 0 ? size : 1);
  65. if (ov->depth++ > 0) {
  66. return;
  67. }
  68. ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
  69. NULL, &destroy_list);
  70. QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
  71. /* ensured by qemu-option.c::opts_do_parse() */
  72. assert(strcmp(opt->name, "id") != 0);
  73. opts_visitor_insert(ov->unprocessed_opts, opt);
  74. }
  75. if (ov->opts_root->id != NULL) {
  76. ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
  77. ov->fake_id_opt->name = "id";
  78. ov->fake_id_opt->str = ov->opts_root->id;
  79. opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
  80. }
  81. }
  82. static gboolean
  83. ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
  84. {
  85. return TRUE;
  86. }
  87. static void
  88. opts_end_struct(Visitor *v, Error **errp)
  89. {
  90. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  91. GQueue *any;
  92. if (--ov->depth > 0) {
  93. return;
  94. }
  95. /* we should have processed all (distinct) QemuOpt instances */
  96. any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
  97. if (any) {
  98. const QemuOpt *first;
  99. first = g_queue_peek_head(any);
  100. error_set(errp, QERR_INVALID_PARAMETER, first->name);
  101. }
  102. g_hash_table_destroy(ov->unprocessed_opts);
  103. ov->unprocessed_opts = NULL;
  104. g_free(ov->fake_id_opt);
  105. ov->fake_id_opt = NULL;
  106. }
  107. static GQueue *
  108. lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
  109. {
  110. GQueue *list;
  111. list = g_hash_table_lookup(ov->unprocessed_opts, name);
  112. if (!list) {
  113. error_set(errp, QERR_MISSING_PARAMETER, name);
  114. }
  115. return list;
  116. }
  117. static void
  118. opts_start_list(Visitor *v, const char *name, Error **errp)
  119. {
  120. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  121. /* we can't traverse a list in a list */
  122. assert(ov->repeated_opts == NULL);
  123. ov->repeated_opts = lookup_distinct(ov, name, errp);
  124. ov->repeated_opts_first = (ov->repeated_opts != NULL);
  125. }
  126. static GenericList *
  127. opts_next_list(Visitor *v, GenericList **list, Error **errp)
  128. {
  129. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  130. GenericList **link;
  131. if (ov->repeated_opts_first) {
  132. ov->repeated_opts_first = false;
  133. link = list;
  134. } else {
  135. const QemuOpt *opt;
  136. opt = g_queue_pop_head(ov->repeated_opts);
  137. if (g_queue_is_empty(ov->repeated_opts)) {
  138. g_hash_table_remove(ov->unprocessed_opts, opt->name);
  139. return NULL;
  140. }
  141. link = &(*list)->next;
  142. }
  143. *link = g_malloc0(sizeof **link);
  144. return *link;
  145. }
  146. static void
  147. opts_end_list(Visitor *v, Error **errp)
  148. {
  149. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  150. ov->repeated_opts = NULL;
  151. }
  152. static const QemuOpt *
  153. lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
  154. {
  155. if (ov->repeated_opts == NULL) {
  156. GQueue *list;
  157. /* the last occurrence of any QemuOpt takes effect when queried by name
  158. */
  159. list = lookup_distinct(ov, name, errp);
  160. return list ? g_queue_peek_tail(list) : NULL;
  161. }
  162. return g_queue_peek_head(ov->repeated_opts);
  163. }
  164. static void
  165. processed(OptsVisitor *ov, const char *name)
  166. {
  167. if (ov->repeated_opts == NULL) {
  168. g_hash_table_remove(ov->unprocessed_opts, name);
  169. }
  170. }
  171. static void
  172. opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
  173. {
  174. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  175. const QemuOpt *opt;
  176. opt = lookup_scalar(ov, name, errp);
  177. if (!opt) {
  178. return;
  179. }
  180. *obj = g_strdup(opt->str ? opt->str : "");
  181. processed(ov, name);
  182. }
  183. /* mimics qemu-option.c::parse_option_bool() */
  184. static void
  185. opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
  186. {
  187. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  188. const QemuOpt *opt;
  189. opt = lookup_scalar(ov, name, errp);
  190. if (!opt) {
  191. return;
  192. }
  193. if (opt->str) {
  194. if (strcmp(opt->str, "on") == 0 ||
  195. strcmp(opt->str, "yes") == 0 ||
  196. strcmp(opt->str, "y") == 0) {
  197. *obj = true;
  198. } else if (strcmp(opt->str, "off") == 0 ||
  199. strcmp(opt->str, "no") == 0 ||
  200. strcmp(opt->str, "n") == 0) {
  201. *obj = false;
  202. } else {
  203. error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
  204. "on|yes|y|off|no|n");
  205. return;
  206. }
  207. } else {
  208. *obj = true;
  209. }
  210. processed(ov, name);
  211. }
  212. static void
  213. opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
  214. {
  215. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  216. const QemuOpt *opt;
  217. const char *str;
  218. long long val;
  219. char *endptr;
  220. opt = lookup_scalar(ov, name, errp);
  221. if (!opt) {
  222. return;
  223. }
  224. str = opt->str ? opt->str : "";
  225. errno = 0;
  226. val = strtoll(str, &endptr, 0);
  227. if (*str != '\0' && *endptr == '\0' && errno == 0 && INT64_MIN <= val &&
  228. val <= INT64_MAX) {
  229. *obj = val;
  230. processed(ov, name);
  231. return;
  232. }
  233. error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, "an int64 value");
  234. }
  235. static void
  236. opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
  237. {
  238. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  239. const QemuOpt *opt;
  240. const char *str;
  241. opt = lookup_scalar(ov, name, errp);
  242. if (!opt) {
  243. return;
  244. }
  245. str = opt->str;
  246. if (str != NULL) {
  247. while (isspace((unsigned char)*str)) {
  248. ++str;
  249. }
  250. if (*str != '-' && *str != '\0') {
  251. unsigned long long val;
  252. char *endptr;
  253. /* non-empty, non-negative subject sequence */
  254. errno = 0;
  255. val = strtoull(str, &endptr, 0);
  256. if (*endptr == '\0' && errno == 0 && val <= UINT64_MAX) {
  257. *obj = val;
  258. processed(ov, name);
  259. return;
  260. }
  261. }
  262. }
  263. error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
  264. "an uint64 value");
  265. }
  266. static void
  267. opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
  268. {
  269. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  270. const QemuOpt *opt;
  271. int64_t val;
  272. char *endptr;
  273. opt = lookup_scalar(ov, name, errp);
  274. if (!opt) {
  275. return;
  276. }
  277. val = strtosz_suffix(opt->str ? opt->str : "", &endptr,
  278. STRTOSZ_DEFSUFFIX_B);
  279. if (val != -1 && *endptr == '\0') {
  280. *obj = val;
  281. processed(ov, name);
  282. return;
  283. }
  284. error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
  285. "a size value representible as a non-negative int64");
  286. }
  287. static void
  288. opts_start_optional(Visitor *v, bool *present, const char *name,
  289. Error **errp)
  290. {
  291. OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
  292. /* we only support a single mandatory scalar field in a list node */
  293. assert(ov->repeated_opts == NULL);
  294. *present = (lookup_distinct(ov, name, NULL) != NULL);
  295. }
  296. OptsVisitor *
  297. opts_visitor_new(const QemuOpts *opts)
  298. {
  299. OptsVisitor *ov;
  300. ov = g_malloc0(sizeof *ov);
  301. ov->visitor.start_struct = &opts_start_struct;
  302. ov->visitor.end_struct = &opts_end_struct;
  303. ov->visitor.start_list = &opts_start_list;
  304. ov->visitor.next_list = &opts_next_list;
  305. ov->visitor.end_list = &opts_end_list;
  306. /* input_type_enum() covers both "normal" enums and union discriminators.
  307. * The union discriminator field is always generated as "type"; it should
  308. * match the "type" QemuOpt child of any QemuOpts.
  309. *
  310. * input_type_enum() will remove the looked-up key from the
  311. * "unprocessed_opts" hash even if the lookup fails, because the removal is
  312. * done earlier in opts_type_str(). This should be harmless.
  313. */
  314. ov->visitor.type_enum = &input_type_enum;
  315. ov->visitor.type_int = &opts_type_int;
  316. ov->visitor.type_uint64 = &opts_type_uint64;
  317. ov->visitor.type_size = &opts_type_size;
  318. ov->visitor.type_bool = &opts_type_bool;
  319. ov->visitor.type_str = &opts_type_str;
  320. /* type_number() is not filled in, but this is not the first visitor to
  321. * skip some mandatory methods... */
  322. ov->visitor.start_optional = &opts_start_optional;
  323. ov->opts_root = opts;
  324. return ov;
  325. }
  326. void
  327. opts_visitor_cleanup(OptsVisitor *ov)
  328. {
  329. if (ov->unprocessed_opts != NULL) {
  330. g_hash_table_destroy(ov->unprocessed_opts);
  331. }
  332. g_free(ov->fake_id_opt);
  333. g_free(ov);
  334. }
  335. Visitor *
  336. opts_get_visitor(OptsVisitor *ov)
  337. {
  338. return &ov->visitor;
  339. }