xref: /openbmc/qemu/util/qemu-config.c (revision 7a5951f6)
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/qerror.h"
6 #include "qapi/qmp/qdict.h"
7 #include "qapi/qmp/qlist.h"
8 #include "qemu/error-report.h"
9 #include "qemu/option.h"
10 #include "qemu/config-file.h"
11 #include "hw/boards.h"
12 
13 static QemuOptsList *vm_config_groups[48];
14 static QemuOptsList *drive_config_groups[5];
15 
16 static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
17                                Error **errp)
18 {
19     int i;
20 
21     qemu_load_module_for_opts(group);
22     for (i = 0; lists[i] != NULL; i++) {
23         if (strcmp(lists[i]->name, group) == 0)
24             break;
25     }
26     if (lists[i] == NULL) {
27         error_setg(errp, "There is no option group '%s'", group);
28     }
29     return lists[i];
30 }
31 
32 QemuOptsList *qemu_find_opts(const char *group)
33 {
34     QemuOptsList *ret;
35     Error *local_err = NULL;
36 
37     ret = find_list(vm_config_groups, group, &local_err);
38     if (local_err) {
39         error_report_err(local_err);
40     }
41 
42     return ret;
43 }
44 
45 QemuOpts *qemu_find_opts_singleton(const char *group)
46 {
47     QemuOptsList *list;
48     QemuOpts *opts;
49 
50     list = qemu_find_opts(group);
51     assert(list);
52     opts = qemu_opts_find(list, NULL);
53     if (!opts) {
54         opts = qemu_opts_create(list, NULL, 0, &error_abort);
55     }
56     return opts;
57 }
58 
59 static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc)
60 {
61     CommandLineParameterInfoList *param_list = NULL;
62     CommandLineParameterInfo *info;
63     int i;
64 
65     for (i = 0; desc[i].name != NULL; i++) {
66         info = g_malloc0(sizeof(*info));
67         info->name = g_strdup(desc[i].name);
68 
69         switch (desc[i].type) {
70         case QEMU_OPT_STRING:
71             info->type = COMMAND_LINE_PARAMETER_TYPE_STRING;
72             break;
73         case QEMU_OPT_BOOL:
74             info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN;
75             break;
76         case QEMU_OPT_NUMBER:
77             info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER;
78             break;
79         case QEMU_OPT_SIZE:
80             info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE;
81             break;
82         }
83 
84         info->help = g_strdup(desc[i].help);
85         info->q_default = g_strdup(desc[i].def_value_str);
86 
87         QAPI_LIST_PREPEND(param_list, info);
88     }
89 
90     return param_list;
91 }
92 
93 /* remove repeated entry from the info list */
94 static void cleanup_infolist(CommandLineParameterInfoList *head)
95 {
96     CommandLineParameterInfoList *pre_entry, *cur, *del_entry;
97 
98     cur = head;
99     while (cur->next) {
100         pre_entry = head;
101         while (pre_entry != cur->next) {
102             if (!strcmp(pre_entry->value->name, cur->next->value->name)) {
103                 del_entry = cur->next;
104                 cur->next = cur->next->next;
105                 del_entry->next = NULL;
106                 qapi_free_CommandLineParameterInfoList(del_entry);
107                 break;
108             }
109             pre_entry = pre_entry->next;
110         }
111         cur = cur->next;
112     }
113 }
114 
115 /* merge the description items of two parameter infolists */
116 static void connect_infolist(CommandLineParameterInfoList *head,
117                              CommandLineParameterInfoList *new)
118 {
119     CommandLineParameterInfoList *cur;
120 
121     cur = head;
122     while (cur->next) {
123         cur = cur->next;
124     }
125     cur->next = new;
126 }
127 
128 /* access all the local QemuOptsLists for drive option */
129 static CommandLineParameterInfoList *get_drive_infolist(void)
130 {
131     CommandLineParameterInfoList *head = NULL, *cur;
132     int i;
133 
134     for (i = 0; drive_config_groups[i] != NULL; i++) {
135         if (!head) {
136             head = query_option_descs(drive_config_groups[i]->desc);
137         } else {
138             cur = query_option_descs(drive_config_groups[i]->desc);
139             connect_infolist(head, cur);
140         }
141     }
142     cleanup_infolist(head);
143 
144     return head;
145 }
146 
147 static CommandLineParameterInfo *objprop_to_cmdline_prop(ObjectProperty *prop)
148 {
149     CommandLineParameterInfo *info;
150 
151     info = g_malloc0(sizeof(*info));
152     info->name = g_strdup(prop->name);
153 
154     if (g_str_equal(prop->type, "bool") || g_str_equal(prop->type, "OnOffAuto")) {
155         info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN;
156     } else if (g_str_equal(prop->type, "int")) {
157         info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER;
158     } else if (g_str_equal(prop->type, "size")) {
159         info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE;
160     } else {
161         info->type = COMMAND_LINE_PARAMETER_TYPE_STRING;
162     }
163 
164     if (prop->description) {
165         info->help = g_strdup(prop->description);
166     }
167 
168     return info;
169 }
170 
171 static CommandLineParameterInfoList *query_all_machine_properties(void)
172 {
173     CommandLineParameterInfoList *params = NULL, *clpiter;
174     CommandLineParameterInfo *info;
175     GSList *machines, *curr_mach;
176     ObjectPropertyIterator op_iter;
177     ObjectProperty *prop;
178     bool is_new;
179 
180     machines = object_class_get_list(TYPE_MACHINE, false);
181     assert(machines);
182 
183     /* Loop over all machine classes */
184     for (curr_mach = machines; curr_mach; curr_mach = curr_mach->next) {
185         object_class_property_iter_init(&op_iter, curr_mach->data);
186         /* ... and over the properties of each machine: */
187         while ((prop = object_property_iter_next(&op_iter))) {
188             if (!prop->set) {
189                 continue;
190             }
191             /*
192              * Check whether the property has already been put into the list
193              * (via another machine class)
194              */
195             is_new = true;
196             for (clpiter = params; clpiter != NULL; clpiter = clpiter->next) {
197                 if (g_str_equal(clpiter->value->name, prop->name)) {
198                     is_new = false;
199                     break;
200                 }
201             }
202             /* If it hasn't been added before, add it now to the list */
203             if (is_new) {
204                 info = objprop_to_cmdline_prop(prop);
205                 QAPI_LIST_PREPEND(params, info);
206             }
207         }
208     }
209 
210     g_slist_free(machines);
211 
212     /* Add entry for the "type" parameter */
213     info = g_malloc0(sizeof(*info));
214     info->name = g_strdup("type");
215     info->type = COMMAND_LINE_PARAMETER_TYPE_STRING;
216     info->help = g_strdup("machine type");
217     QAPI_LIST_PREPEND(params, info);
218 
219     return params;
220 }
221 
222 CommandLineOptionInfoList *qmp_query_command_line_options(const char *option,
223                                                           Error **errp)
224 {
225     CommandLineOptionInfoList *conf_list = NULL;
226     CommandLineOptionInfo *info;
227     int i;
228 
229     for (i = 0; vm_config_groups[i] != NULL; i++) {
230         if (!option || !strcmp(option, vm_config_groups[i]->name)) {
231             info = g_malloc0(sizeof(*info));
232             info->option = g_strdup(vm_config_groups[i]->name);
233             if (!strcmp("drive", vm_config_groups[i]->name)) {
234                 info->parameters = get_drive_infolist();
235             } else {
236                 info->parameters =
237                     query_option_descs(vm_config_groups[i]->desc);
238             }
239             QAPI_LIST_PREPEND(conf_list, info);
240         }
241     }
242 
243     if (!option || !strcmp(option, "machine")) {
244         info = g_malloc0(sizeof(*info));
245         info->option = g_strdup("machine");
246         info->parameters = query_all_machine_properties();
247         QAPI_LIST_PREPEND(conf_list, info);
248     }
249 
250     if (conf_list == NULL) {
251         error_setg(errp, "invalid option name: %s", option);
252     }
253 
254     return conf_list;
255 }
256 
257 QemuOptsList *qemu_find_opts_err(const char *group, Error **errp)
258 {
259     return find_list(vm_config_groups, group, errp);
260 }
261 
262 void qemu_add_drive_opts(QemuOptsList *list)
263 {
264     int entries, i;
265 
266     entries = ARRAY_SIZE(drive_config_groups);
267     entries--; /* keep list NULL terminated */
268     for (i = 0; i < entries; i++) {
269         if (drive_config_groups[i] == NULL) {
270             drive_config_groups[i] = list;
271             return;
272         }
273     }
274     fprintf(stderr, "ran out of space in drive_config_groups");
275     abort();
276 }
277 
278 void qemu_add_opts(QemuOptsList *list)
279 {
280     int entries, i;
281 
282     entries = ARRAY_SIZE(vm_config_groups);
283     entries--; /* keep list NULL terminated */
284     for (i = 0; i < entries; i++) {
285         if (vm_config_groups[i] == NULL) {
286             vm_config_groups[i] = list;
287             return;
288         }
289     }
290     fprintf(stderr, "ran out of space in vm_config_groups");
291     abort();
292 }
293 
294 /* Returns number of config groups on success, -errno on error */
295 static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque,
296                                const char *fname, Error **errp)
297 {
298     ERRP_GUARD();
299     char line[1024], prev_group[64], group[64], arg[64], value[1024];
300     Location loc;
301     QDict *qdict = NULL;
302     int res = -EINVAL, lno = 0;
303     int count = 0;
304 
305     loc_push_none(&loc);
306     while (fgets(line, sizeof(line), fp) != NULL) {
307         ++lno;
308         if (line[0] == '\n') {
309             /* skip empty lines */
310             continue;
311         }
312         if (line[0] == '#') {
313             /* comment */
314             continue;
315         }
316         if (line[0] == '[') {
317             QDict *prev = qdict;
318             if (sscanf(line, "[%63s \"%63[^\"]\"]", group, value) == 2) {
319                 qdict = qdict_new();
320                 qdict_put_str(qdict, "id", value);
321                 count++;
322             } else if (sscanf(line, "[%63[^]]]", group) == 1) {
323                 qdict = qdict_new();
324                 count++;
325             }
326             if (qdict != prev) {
327                 if (prev) {
328                     cb(prev_group, prev, opaque, errp);
329                     qobject_unref(prev);
330                     if (*errp) {
331                         goto out;
332                     }
333                 }
334                 strcpy(prev_group, group);
335                 continue;
336             }
337         }
338         loc_set_file(fname, lno);
339         value[0] = '\0';
340         if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2 ||
341             sscanf(line, " %63s = \"\"", arg) == 1) {
342             /* arg = value */
343             if (qdict == NULL) {
344                 error_setg(errp, "no group defined");
345                 goto out;
346             }
347             qdict_put_str(qdict, arg, value);
348             continue;
349         }
350         error_setg(errp, "parse error");
351         goto out;
352     }
353     if (ferror(fp)) {
354         loc_pop(&loc);
355         error_setg_errno(errp, errno, "Cannot read config file");
356         goto out_no_loc;
357     }
358     res = count;
359     if (qdict) {
360         cb(group, qdict, opaque, errp);
361     }
362 out:
363     loc_pop(&loc);
364 out_no_loc:
365     qobject_unref(qdict);
366     return res;
367 }
368 
369 void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp)
370 {
371     QemuOptsList **lists = opaque;
372     QemuOptsList *list;
373 
374     list = find_list(lists, group, errp);
375     if (!list) {
376         return;
377     }
378 
379     qemu_opts_from_qdict(list, qdict, errp);
380 }
381 
382 int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)
383 {
384     return qemu_config_foreach(fp, qemu_config_do_parse, lists, fname, errp);
385 }
386 
387 int qemu_read_config_file(const char *filename, QEMUConfigCB *cb, Error **errp)
388 {
389     FILE *f = fopen(filename, "r");
390     int ret;
391 
392     if (f == NULL) {
393         error_setg_file_open(errp, errno, filename);
394         return -errno;
395     }
396 
397     ret = qemu_config_foreach(f, cb, vm_config_groups, filename, errp);
398     fclose(f);
399     return ret;
400 }
401 
402 static bool config_parse_qdict_section(QDict *options, QemuOptsList *opts,
403                                        Error **errp)
404 {
405     QemuOpts *subopts;
406     g_autoptr(QDict) subqdict = NULL;
407     g_autoptr(QList) list = NULL;
408     size_t orig_size, enum_size;
409     char *prefix;
410 
411     prefix = g_strdup_printf("%s.", opts->name);
412     qdict_extract_subqdict(options, &subqdict, prefix);
413     g_free(prefix);
414     orig_size = qdict_size(subqdict);
415     if (!orig_size) {
416         return true;
417     }
418 
419     subopts = qemu_opts_create(opts, NULL, 0, errp);
420     if (!subopts) {
421         return false;
422     }
423 
424     if (!qemu_opts_absorb_qdict(subopts, subqdict, errp)) {
425         return false;
426     }
427 
428     enum_size = qdict_size(subqdict);
429     if (enum_size < orig_size && enum_size) {
430         error_setg(errp, "Unknown option '%s' for [%s]",
431                    qdict_first(subqdict)->key, opts->name);
432         return false;
433     }
434 
435     if (enum_size) {
436         /* Multiple, enumerated sections */
437         QListEntry *list_entry;
438         unsigned i = 0;
439 
440         /* Not required anymore */
441         qemu_opts_del(subopts);
442 
443         qdict_array_split(subqdict, &list);
444         if (qdict_size(subqdict)) {
445             error_setg(errp, "Unused option '%s' for [%s]",
446                        qdict_first(subqdict)->key, opts->name);
447             return false;
448         }
449 
450         QLIST_FOREACH_ENTRY(list, list_entry) {
451             QDict *section = qobject_to(QDict, qlist_entry_obj(list_entry));
452             char *opt_name;
453 
454             if (!section) {
455                 error_setg(errp, "[%s] section (index %u) does not consist of "
456                            "keys", opts->name, i);
457                 return false;
458             }
459 
460             opt_name = g_strdup_printf("%s.%u", opts->name, i++);
461             subopts = qemu_opts_create(opts, opt_name, 1, errp);
462             g_free(opt_name);
463             if (!subopts) {
464                 return false;
465             }
466 
467             if (!qemu_opts_absorb_qdict(subopts, section, errp)) {
468                 qemu_opts_del(subopts);
469                 return false;
470             }
471 
472             if (qdict_size(section)) {
473                 error_setg(errp, "[%s] section doesn't support the option '%s'",
474                            opts->name, qdict_first(section)->key);
475                 qemu_opts_del(subopts);
476                 return false;
477             }
478         }
479     }
480 
481     return true;
482 }
483 
484 bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists,
485                              Error **errp)
486 {
487     int i;
488 
489     for (i = 0; lists[i]; i++) {
490         if (!config_parse_qdict_section(options, lists[i], errp)) {
491             return false;
492         }
493     }
494 
495     return true;
496 }
497