1 | #include "qemu/osdep.h" |
2 | #include "block/qdict.h" /* for qdict_extract_subqdict() */ |
3 | #include "qapi/error.h" |
4 | #include "qapi/qapi-commands-misc.h" |
5 | #include "qapi/qmp/qdict.h" |
6 | #include "qapi/qmp/qlist.h" |
7 | #include "qemu/error-report.h" |
8 | #include "qemu/option.h" |
9 | #include "qemu/config-file.h" |
10 | |
11 | static QemuOptsList *vm_config_groups[48]; |
12 | static QemuOptsList *drive_config_groups[5]; |
13 | |
14 | static QemuOptsList *find_list(QemuOptsList **lists, const char *group, |
15 | Error **errp) |
16 | { |
17 | int i; |
18 | |
19 | for (i = 0; lists[i] != NULL; i++) { |
20 | if (strcmp(lists[i]->name, group) == 0) |
21 | break; |
22 | } |
23 | if (lists[i] == NULL) { |
24 | error_setg(errp, "There is no option group '%s'" , group); |
25 | } |
26 | return lists[i]; |
27 | } |
28 | |
29 | QemuOptsList *qemu_find_opts(const char *group) |
30 | { |
31 | QemuOptsList *ret; |
32 | Error *local_err = NULL; |
33 | |
34 | ret = find_list(vm_config_groups, group, &local_err); |
35 | if (local_err) { |
36 | error_report_err(local_err); |
37 | } |
38 | |
39 | return ret; |
40 | } |
41 | |
42 | QemuOpts *qemu_find_opts_singleton(const char *group) |
43 | { |
44 | QemuOptsList *list; |
45 | QemuOpts *opts; |
46 | |
47 | list = qemu_find_opts(group); |
48 | assert(list); |
49 | opts = qemu_opts_find(list, NULL); |
50 | if (!opts) { |
51 | opts = qemu_opts_create(list, NULL, 0, &error_abort); |
52 | } |
53 | return opts; |
54 | } |
55 | |
56 | static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc) |
57 | { |
58 | CommandLineParameterInfoList *param_list = NULL, *entry; |
59 | CommandLineParameterInfo *info; |
60 | int i; |
61 | |
62 | for (i = 0; desc[i].name != NULL; i++) { |
63 | info = g_malloc0(sizeof(*info)); |
64 | info->name = g_strdup(desc[i].name); |
65 | |
66 | switch (desc[i].type) { |
67 | case QEMU_OPT_STRING: |
68 | info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; |
69 | break; |
70 | case QEMU_OPT_BOOL: |
71 | info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; |
72 | break; |
73 | case QEMU_OPT_NUMBER: |
74 | info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; |
75 | break; |
76 | case QEMU_OPT_SIZE: |
77 | info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; |
78 | break; |
79 | } |
80 | |
81 | if (desc[i].help) { |
82 | info->has_help = true; |
83 | info->help = g_strdup(desc[i].help); |
84 | } |
85 | if (desc[i].def_value_str) { |
86 | info->has_q_default = true; |
87 | info->q_default = g_strdup(desc[i].def_value_str); |
88 | } |
89 | |
90 | entry = g_malloc0(sizeof(*entry)); |
91 | entry->value = info; |
92 | entry->next = param_list; |
93 | param_list = entry; |
94 | } |
95 | |
96 | return param_list; |
97 | } |
98 | |
99 | /* remove repeated entry from the info list */ |
100 | static void cleanup_infolist(CommandLineParameterInfoList *head) |
101 | { |
102 | CommandLineParameterInfoList *pre_entry, *cur, *del_entry; |
103 | |
104 | cur = head; |
105 | while (cur->next) { |
106 | pre_entry = head; |
107 | while (pre_entry != cur->next) { |
108 | if (!strcmp(pre_entry->value->name, cur->next->value->name)) { |
109 | del_entry = cur->next; |
110 | cur->next = cur->next->next; |
111 | del_entry->next = NULL; |
112 | qapi_free_CommandLineParameterInfoList(del_entry); |
113 | break; |
114 | } |
115 | pre_entry = pre_entry->next; |
116 | } |
117 | cur = cur->next; |
118 | } |
119 | } |
120 | |
121 | /* merge the description items of two parameter infolists */ |
122 | static void connect_infolist(CommandLineParameterInfoList *head, |
123 | CommandLineParameterInfoList *new) |
124 | { |
125 | CommandLineParameterInfoList *cur; |
126 | |
127 | cur = head; |
128 | while (cur->next) { |
129 | cur = cur->next; |
130 | } |
131 | cur->next = new; |
132 | } |
133 | |
134 | /* access all the local QemuOptsLists for drive option */ |
135 | static CommandLineParameterInfoList *get_drive_infolist(void) |
136 | { |
137 | CommandLineParameterInfoList *head = NULL, *cur; |
138 | int i; |
139 | |
140 | for (i = 0; drive_config_groups[i] != NULL; i++) { |
141 | if (!head) { |
142 | head = query_option_descs(drive_config_groups[i]->desc); |
143 | } else { |
144 | cur = query_option_descs(drive_config_groups[i]->desc); |
145 | connect_infolist(head, cur); |
146 | } |
147 | } |
148 | cleanup_infolist(head); |
149 | |
150 | return head; |
151 | } |
152 | |
153 | /* restore machine options that are now machine's properties */ |
154 | static QemuOptsList machine_opts = { |
155 | .merge_lists = true, |
156 | .head = QTAILQ_HEAD_INITIALIZER(machine_opts.head), |
157 | .desc = { |
158 | { |
159 | .name = "type" , |
160 | .type = QEMU_OPT_STRING, |
161 | .help = "emulated machine" |
162 | },{ |
163 | .name = "accel" , |
164 | .type = QEMU_OPT_STRING, |
165 | .help = "accelerator list" , |
166 | },{ |
167 | .name = "kernel_irqchip" , |
168 | .type = QEMU_OPT_BOOL, |
169 | .help = "use KVM in-kernel irqchip" , |
170 | },{ |
171 | .name = "kvm_shadow_mem" , |
172 | .type = QEMU_OPT_SIZE, |
173 | .help = "KVM shadow MMU size" , |
174 | },{ |
175 | .name = "kernel" , |
176 | .type = QEMU_OPT_STRING, |
177 | .help = "Linux kernel image file" , |
178 | },{ |
179 | .name = "initrd" , |
180 | .type = QEMU_OPT_STRING, |
181 | .help = "Linux initial ramdisk file" , |
182 | },{ |
183 | .name = "append" , |
184 | .type = QEMU_OPT_STRING, |
185 | .help = "Linux kernel command line" , |
186 | },{ |
187 | .name = "dtb" , |
188 | .type = QEMU_OPT_STRING, |
189 | .help = "Linux kernel device tree file" , |
190 | },{ |
191 | .name = "dumpdtb" , |
192 | .type = QEMU_OPT_STRING, |
193 | .help = "Dump current dtb to a file and quit" , |
194 | },{ |
195 | .name = "phandle_start" , |
196 | .type = QEMU_OPT_NUMBER, |
197 | .help = "The first phandle ID we may generate dynamically" , |
198 | },{ |
199 | .name = "dt_compatible" , |
200 | .type = QEMU_OPT_STRING, |
201 | .help = "Overrides the \"compatible\" property of the dt root node" , |
202 | },{ |
203 | .name = "dump-guest-core" , |
204 | .type = QEMU_OPT_BOOL, |
205 | .help = "Include guest memory in a core dump" , |
206 | },{ |
207 | .name = "mem-merge" , |
208 | .type = QEMU_OPT_BOOL, |
209 | .help = "enable/disable memory merge support" , |
210 | },{ |
211 | .name = "usb" , |
212 | .type = QEMU_OPT_BOOL, |
213 | .help = "Set on/off to enable/disable usb" , |
214 | },{ |
215 | .name = "firmware" , |
216 | .type = QEMU_OPT_STRING, |
217 | .help = "firmware image" , |
218 | },{ |
219 | .name = "iommu" , |
220 | .type = QEMU_OPT_BOOL, |
221 | .help = "Set on/off to enable/disable Intel IOMMU (VT-d)" , |
222 | },{ |
223 | .name = "suppress-vmdesc" , |
224 | .type = QEMU_OPT_BOOL, |
225 | .help = "Set on to disable self-describing migration" , |
226 | },{ |
227 | .name = "aes-key-wrap" , |
228 | .type = QEMU_OPT_BOOL, |
229 | .help = "enable/disable AES key wrapping using the CPACF wrapping key" , |
230 | },{ |
231 | .name = "dea-key-wrap" , |
232 | .type = QEMU_OPT_BOOL, |
233 | .help = "enable/disable DEA key wrapping using the CPACF wrapping key" , |
234 | },{ |
235 | .name = "loadparm" , |
236 | .type = QEMU_OPT_STRING, |
237 | .help = "Up to 8 chars in set of [A-Za-z0-9. ](lower case chars" |
238 | " converted to upper case) to pass to machine" |
239 | " loader, boot manager, and guest kernel" , |
240 | }, |
241 | { /* End of list */ } |
242 | } |
243 | }; |
244 | |
245 | CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, |
246 | const char *option, |
247 | Error **errp) |
248 | { |
249 | CommandLineOptionInfoList *conf_list = NULL, *entry; |
250 | CommandLineOptionInfo *info; |
251 | int i; |
252 | |
253 | for (i = 0; vm_config_groups[i] != NULL; i++) { |
254 | if (!has_option || !strcmp(option, vm_config_groups[i]->name)) { |
255 | info = g_malloc0(sizeof(*info)); |
256 | info->option = g_strdup(vm_config_groups[i]->name); |
257 | if (!strcmp("drive" , vm_config_groups[i]->name)) { |
258 | info->parameters = get_drive_infolist(); |
259 | } else if (!strcmp("machine" , vm_config_groups[i]->name)) { |
260 | info->parameters = query_option_descs(machine_opts.desc); |
261 | } else { |
262 | info->parameters = |
263 | query_option_descs(vm_config_groups[i]->desc); |
264 | } |
265 | entry = g_malloc0(sizeof(*entry)); |
266 | entry->value = info; |
267 | entry->next = conf_list; |
268 | conf_list = entry; |
269 | } |
270 | } |
271 | |
272 | if (conf_list == NULL) { |
273 | error_setg(errp, "invalid option name: %s" , option); |
274 | } |
275 | |
276 | return conf_list; |
277 | } |
278 | |
279 | QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) |
280 | { |
281 | return find_list(vm_config_groups, group, errp); |
282 | } |
283 | |
284 | void qemu_add_drive_opts(QemuOptsList *list) |
285 | { |
286 | int entries, i; |
287 | |
288 | entries = ARRAY_SIZE(drive_config_groups); |
289 | entries--; /* keep list NULL terminated */ |
290 | for (i = 0; i < entries; i++) { |
291 | if (drive_config_groups[i] == NULL) { |
292 | drive_config_groups[i] = list; |
293 | return; |
294 | } |
295 | } |
296 | fprintf(stderr, "ran out of space in drive_config_groups" ); |
297 | abort(); |
298 | } |
299 | |
300 | void qemu_add_opts(QemuOptsList *list) |
301 | { |
302 | int entries, i; |
303 | |
304 | entries = ARRAY_SIZE(vm_config_groups); |
305 | entries--; /* keep list NULL terminated */ |
306 | for (i = 0; i < entries; i++) { |
307 | if (vm_config_groups[i] == NULL) { |
308 | vm_config_groups[i] = list; |
309 | return; |
310 | } |
311 | } |
312 | fprintf(stderr, "ran out of space in vm_config_groups" ); |
313 | abort(); |
314 | } |
315 | |
316 | int qemu_set_option(const char *str) |
317 | { |
318 | Error *local_err = NULL; |
319 | char group[64], id[64], arg[64]; |
320 | QemuOptsList *list; |
321 | QemuOpts *opts; |
322 | int rc, offset; |
323 | |
324 | rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n" , group, id, arg, &offset); |
325 | if (rc < 3 || str[offset] != '=') { |
326 | error_report("can't parse: \"%s\"" , str); |
327 | return -1; |
328 | } |
329 | |
330 | list = qemu_find_opts(group); |
331 | if (list == NULL) { |
332 | return -1; |
333 | } |
334 | |
335 | opts = qemu_opts_find(list, id); |
336 | if (!opts) { |
337 | error_report("there is no %s \"%s\" defined" , |
338 | list->name, id); |
339 | return -1; |
340 | } |
341 | |
342 | qemu_opt_set(opts, arg, str + offset + 1, &local_err); |
343 | if (local_err) { |
344 | error_report_err(local_err); |
345 | return -1; |
346 | } |
347 | return 0; |
348 | } |
349 | |
350 | struct ConfigWriteData { |
351 | QemuOptsList *list; |
352 | FILE *fp; |
353 | }; |
354 | |
355 | static int config_write_opt(void *opaque, const char *name, const char *value, |
356 | Error **errp) |
357 | { |
358 | struct ConfigWriteData *data = opaque; |
359 | |
360 | fprintf(data->fp, " %s = \"%s\"\n" , name, value); |
361 | return 0; |
362 | } |
363 | |
364 | static int config_write_opts(void *opaque, QemuOpts *opts, Error **errp) |
365 | { |
366 | struct ConfigWriteData *data = opaque; |
367 | const char *id = qemu_opts_id(opts); |
368 | |
369 | if (id) { |
370 | fprintf(data->fp, "[%s \"%s\"]\n" , data->list->name, id); |
371 | } else { |
372 | fprintf(data->fp, "[%s]\n" , data->list->name); |
373 | } |
374 | qemu_opt_foreach(opts, config_write_opt, data, NULL); |
375 | fprintf(data->fp, "\n" ); |
376 | return 0; |
377 | } |
378 | |
379 | void qemu_config_write(FILE *fp) |
380 | { |
381 | struct ConfigWriteData data = { .fp = fp }; |
382 | QemuOptsList **lists = vm_config_groups; |
383 | int i; |
384 | |
385 | fprintf(fp, "# qemu config file\n\n" ); |
386 | for (i = 0; lists[i] != NULL; i++) { |
387 | data.list = lists[i]; |
388 | qemu_opts_foreach(data.list, config_write_opts, &data, NULL); |
389 | } |
390 | } |
391 | |
392 | /* Returns number of config groups on success, -errno on error */ |
393 | int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) |
394 | { |
395 | char line[1024], group[64], id[64], arg[64], value[1024]; |
396 | Location loc; |
397 | QemuOptsList *list = NULL; |
398 | Error *local_err = NULL; |
399 | QemuOpts *opts = NULL; |
400 | int res = -EINVAL, lno = 0; |
401 | int count = 0; |
402 | |
403 | loc_push_none(&loc); |
404 | while (fgets(line, sizeof(line), fp) != NULL) { |
405 | loc_set_file(fname, ++lno); |
406 | if (line[0] == '\n') { |
407 | /* skip empty lines */ |
408 | continue; |
409 | } |
410 | if (line[0] == '#') { |
411 | /* comment */ |
412 | continue; |
413 | } |
414 | if (sscanf(line, "[%63s \"%63[^\"]\"]" , group, id) == 2) { |
415 | /* group with id */ |
416 | list = find_list(lists, group, &local_err); |
417 | if (local_err) { |
418 | error_report_err(local_err); |
419 | goto out; |
420 | } |
421 | opts = qemu_opts_create(list, id, 1, NULL); |
422 | count++; |
423 | continue; |
424 | } |
425 | if (sscanf(line, "[%63[^]]]" , group) == 1) { |
426 | /* group without id */ |
427 | list = find_list(lists, group, &local_err); |
428 | if (local_err) { |
429 | error_report_err(local_err); |
430 | goto out; |
431 | } |
432 | opts = qemu_opts_create(list, NULL, 0, &error_abort); |
433 | count++; |
434 | continue; |
435 | } |
436 | value[0] = '\0'; |
437 | if (sscanf(line, " %63s = \"%1023[^\"]\"" , arg, value) == 2 || |
438 | sscanf(line, " %63s = \"\"" , arg) == 1) { |
439 | /* arg = value */ |
440 | if (opts == NULL) { |
441 | error_report("no group defined" ); |
442 | goto out; |
443 | } |
444 | qemu_opt_set(opts, arg, value, &local_err); |
445 | if (local_err) { |
446 | error_report_err(local_err); |
447 | goto out; |
448 | } |
449 | continue; |
450 | } |
451 | error_report("parse error" ); |
452 | goto out; |
453 | } |
454 | if (ferror(fp)) { |
455 | error_report("error reading file" ); |
456 | goto out; |
457 | } |
458 | res = count; |
459 | out: |
460 | loc_pop(&loc); |
461 | return res; |
462 | } |
463 | |
464 | int qemu_read_config_file(const char *filename) |
465 | { |
466 | FILE *f = fopen(filename, "r" ); |
467 | int ret; |
468 | |
469 | if (f == NULL) { |
470 | return -errno; |
471 | } |
472 | |
473 | ret = qemu_config_parse(f, vm_config_groups, filename); |
474 | fclose(f); |
475 | return ret; |
476 | } |
477 | |
478 | static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, |
479 | Error **errp) |
480 | { |
481 | QemuOpts *subopts; |
482 | QDict *subqdict; |
483 | QList *list = NULL; |
484 | Error *local_err = NULL; |
485 | size_t orig_size, enum_size; |
486 | char *prefix; |
487 | |
488 | prefix = g_strdup_printf("%s." , opts->name); |
489 | qdict_extract_subqdict(options, &subqdict, prefix); |
490 | g_free(prefix); |
491 | orig_size = qdict_size(subqdict); |
492 | if (!orig_size) { |
493 | goto out; |
494 | } |
495 | |
496 | subopts = qemu_opts_create(opts, NULL, 0, &local_err); |
497 | if (local_err) { |
498 | error_propagate(errp, local_err); |
499 | goto out; |
500 | } |
501 | |
502 | qemu_opts_absorb_qdict(subopts, subqdict, &local_err); |
503 | if (local_err) { |
504 | error_propagate(errp, local_err); |
505 | goto out; |
506 | } |
507 | |
508 | enum_size = qdict_size(subqdict); |
509 | if (enum_size < orig_size && enum_size) { |
510 | error_setg(errp, "Unknown option '%s' for [%s]" , |
511 | qdict_first(subqdict)->key, opts->name); |
512 | goto out; |
513 | } |
514 | |
515 | if (enum_size) { |
516 | /* Multiple, enumerated sections */ |
517 | QListEntry *list_entry; |
518 | unsigned i = 0; |
519 | |
520 | /* Not required anymore */ |
521 | qemu_opts_del(subopts); |
522 | |
523 | qdict_array_split(subqdict, &list); |
524 | if (qdict_size(subqdict)) { |
525 | error_setg(errp, "Unused option '%s' for [%s]" , |
526 | qdict_first(subqdict)->key, opts->name); |
527 | goto out; |
528 | } |
529 | |
530 | QLIST_FOREACH_ENTRY(list, list_entry) { |
531 | QDict *section = qobject_to(QDict, qlist_entry_obj(list_entry)); |
532 | char *opt_name; |
533 | |
534 | if (!section) { |
535 | error_setg(errp, "[%s] section (index %u) does not consist of " |
536 | "keys" , opts->name, i); |
537 | goto out; |
538 | } |
539 | |
540 | opt_name = g_strdup_printf("%s.%u" , opts->name, i++); |
541 | subopts = qemu_opts_create(opts, opt_name, 1, &local_err); |
542 | g_free(opt_name); |
543 | if (local_err) { |
544 | error_propagate(errp, local_err); |
545 | goto out; |
546 | } |
547 | |
548 | qemu_opts_absorb_qdict(subopts, section, &local_err); |
549 | if (local_err) { |
550 | error_propagate(errp, local_err); |
551 | qemu_opts_del(subopts); |
552 | goto out; |
553 | } |
554 | |
555 | if (qdict_size(section)) { |
556 | error_setg(errp, "[%s] section doesn't support the option '%s'" , |
557 | opts->name, qdict_first(section)->key); |
558 | qemu_opts_del(subopts); |
559 | goto out; |
560 | } |
561 | } |
562 | } |
563 | |
564 | out: |
565 | qobject_unref(subqdict); |
566 | qobject_unref(list); |
567 | } |
568 | |
569 | void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, |
570 | Error **errp) |
571 | { |
572 | int i; |
573 | Error *local_err = NULL; |
574 | |
575 | for (i = 0; lists[i]; i++) { |
576 | config_parse_qdict_section(options, lists[i], &local_err); |
577 | if (local_err) { |
578 | error_propagate(errp, local_err); |
579 | return; |
580 | } |
581 | } |
582 | } |
583 | |