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