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