xref: /illumos-kvm-cmd/qemu-config.c (revision 68396ea9)
1 #include "qemu-common.h"
2 #include "qemu-error.h"
3 #include "qemu-option.h"
4 #include "qemu-config.h"
5 #include "sysemu.h"
6 #include "hw/qdev.h"
7 
8 static QemuOptsList qemu_drive_opts = {
9     .name = "drive",
10     .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
11     .desc = {
12         {
13             .name = "bus",
14             .type = QEMU_OPT_NUMBER,
15             .help = "bus number",
16         },{
17             .name = "unit",
18             .type = QEMU_OPT_NUMBER,
19             .help = "unit number (i.e. lun for scsi)",
20         },{
21             .name = "if",
22             .type = QEMU_OPT_STRING,
23             .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
24         },{
25             .name = "index",
26             .type = QEMU_OPT_NUMBER,
27         },{
28             .name = "cyls",
29             .type = QEMU_OPT_NUMBER,
30             .help = "number of cylinders (ide disk geometry)",
31         },{
32             .name = "heads",
33             .type = QEMU_OPT_NUMBER,
34             .help = "number of heads (ide disk geometry)",
35         },{
36             .name = "secs",
37             .type = QEMU_OPT_NUMBER,
38             .help = "number of sectors (ide disk geometry)",
39         },{
40             .name = "trans",
41             .type = QEMU_OPT_STRING,
42             .help = "chs translation (auto, lba. none)",
43         },{
44             .name = "media",
45             .type = QEMU_OPT_STRING,
46             .help = "media type (disk, cdrom)",
47         },{
48             .name = "snapshot",
49             .type = QEMU_OPT_BOOL,
50         },{
51             .name = "file",
52             .type = QEMU_OPT_STRING,
53             .help = "disk image",
54         },{
55             .name = "cache",
56             .type = QEMU_OPT_STRING,
57             .help = "host cache usage (none, writeback, writethrough, unsafe)",
58         },{
59             .name = "aio",
60             .type = QEMU_OPT_STRING,
61             .help = "host AIO implementation (threads, native)",
62         },{
63             .name = "format",
64             .type = QEMU_OPT_STRING,
65             .help = "disk format (raw, qcow2, ...)",
66         },{
67             .name = "serial",
68             .type = QEMU_OPT_STRING,
69         },{
70             .name = "rerror",
71             .type = QEMU_OPT_STRING,
72         },{
73             .name = "werror",
74             .type = QEMU_OPT_STRING,
75         },{
76             .name = "addr",
77             .type = QEMU_OPT_STRING,
78             .help = "pci address (virtio only)",
79         },{
80             .name = "readonly",
81             .type = QEMU_OPT_BOOL,
82         },{
83             .name = "boot",
84             .type = QEMU_OPT_BOOL,
85             .help = "make this a boot drive",
86         },
87         { /* end of list */ }
88     },
89 };
90 
91 static QemuOptsList qemu_chardev_opts = {
92     .name = "chardev",
93     .implied_opt_name = "backend",
94     .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
95     .desc = {
96         {
97             .name = "backend",
98             .type = QEMU_OPT_STRING,
99         },{
100             .name = "path",
101             .type = QEMU_OPT_STRING,
102         },{
103             .name = "host",
104             .type = QEMU_OPT_STRING,
105         },{
106             .name = "port",
107             .type = QEMU_OPT_STRING,
108         },{
109             .name = "localaddr",
110             .type = QEMU_OPT_STRING,
111         },{
112             .name = "localport",
113             .type = QEMU_OPT_STRING,
114         },{
115             .name = "to",
116             .type = QEMU_OPT_NUMBER,
117         },{
118             .name = "ipv4",
119             .type = QEMU_OPT_BOOL,
120         },{
121             .name = "ipv6",
122             .type = QEMU_OPT_BOOL,
123         },{
124             .name = "wait",
125             .type = QEMU_OPT_BOOL,
126         },{
127             .name = "server",
128             .type = QEMU_OPT_BOOL,
129         },{
130             .name = "delay",
131             .type = QEMU_OPT_BOOL,
132         },{
133             .name = "telnet",
134             .type = QEMU_OPT_BOOL,
135         },{
136             .name = "width",
137             .type = QEMU_OPT_NUMBER,
138         },{
139             .name = "height",
140             .type = QEMU_OPT_NUMBER,
141         },{
142             .name = "cols",
143             .type = QEMU_OPT_NUMBER,
144         },{
145             .name = "rows",
146             .type = QEMU_OPT_NUMBER,
147         },{
148             .name = "mux",
149             .type = QEMU_OPT_BOOL,
150         },{
151             .name = "signal",
152             .type = QEMU_OPT_BOOL,
153         },{
154             .name = "name",
155             .type = QEMU_OPT_STRING,
156         },{
157             .name = "debug",
158             .type = QEMU_OPT_NUMBER,
159         },
160         { /* end of list */ }
161     },
162 };
163 
164 QemuOptsList qemu_fsdev_opts = {
165     .name = "fsdev",
166     .implied_opt_name = "fstype",
167     .head = QTAILQ_HEAD_INITIALIZER(qemu_fsdev_opts.head),
168     .desc = {
169         {
170             .name = "fstype",
171             .type = QEMU_OPT_STRING,
172         }, {
173             .name = "path",
174             .type = QEMU_OPT_STRING,
175         }, {
176             .name = "security_model",
177             .type = QEMU_OPT_STRING,
178         },
179         { /*End of list */ }
180     },
181 };
182 
183 QemuOptsList qemu_virtfs_opts = {
184     .name = "virtfs",
185     .implied_opt_name = "fstype",
186     .head = QTAILQ_HEAD_INITIALIZER(qemu_virtfs_opts.head),
187     .desc = {
188         {
189             .name = "fstype",
190             .type = QEMU_OPT_STRING,
191         }, {
192             .name = "path",
193             .type = QEMU_OPT_STRING,
194         }, {
195             .name = "mount_tag",
196             .type = QEMU_OPT_STRING,
197         }, {
198             .name = "security_model",
199             .type = QEMU_OPT_STRING,
200         },
201 
202         { /*End of list */ }
203     },
204 };
205 
206 static QemuOptsList qemu_device_opts = {
207     .name = "device",
208     .implied_opt_name = "driver",
209     .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
210     .desc = {
211         /*
212          * no elements => accept any
213          * sanity checking will happen later
214          * when setting device properties
215          */
216         { /* end of list */ }
217     },
218 };
219 
220 static QemuOptsList qemu_netdev_opts = {
221     .name = "netdev",
222     .implied_opt_name = "type",
223     .head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head),
224     .desc = {
225         /*
226          * no elements => accept any params
227          * validation will happen later
228          */
229         { /* end of list */ }
230     },
231 };
232 
233 static QemuOptsList qemu_net_opts = {
234     .name = "net",
235     .implied_opt_name = "type",
236     .head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head),
237     .desc = {
238         /*
239          * no elements => accept any params
240          * validation will happen later
241          */
242         { /* end of list */ }
243     },
244 };
245 
246 static QemuOptsList qemu_rtc_opts = {
247     .name = "rtc",
248     .head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head),
249     .desc = {
250         {
251             .name = "base",
252             .type = QEMU_OPT_STRING,
253         },{
254             .name = "clock",
255             .type = QEMU_OPT_STRING,
256         },{
257             .name = "driftfix",
258             .type = QEMU_OPT_STRING,
259         },
260         { /* end of list */ }
261     },
262 };
263 
264 static QemuOptsList qemu_global_opts = {
265     .name = "global",
266     .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
267     .desc = {
268         {
269             .name = "driver",
270             .type = QEMU_OPT_STRING,
271         },{
272             .name = "property",
273             .type = QEMU_OPT_STRING,
274         },{
275             .name = "value",
276             .type = QEMU_OPT_STRING,
277         },
278         { /* end of list */ }
279     },
280 };
281 
282 static QemuOptsList qemu_mon_opts = {
283     .name = "mon",
284     .implied_opt_name = "chardev",
285     .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
286     .desc = {
287         {
288             .name = "mode",
289             .type = QEMU_OPT_STRING,
290         },{
291             .name = "chardev",
292             .type = QEMU_OPT_STRING,
293         },{
294             .name = "default",
295             .type = QEMU_OPT_BOOL,
296         },{
297             .name = "pretty",
298             .type = QEMU_OPT_BOOL,
299         },
300         { /* end of list */ }
301     },
302 };
303 
304 #ifdef CONFIG_SIMPLE_TRACE
305 static QemuOptsList qemu_trace_opts = {
306     .name = "trace",
307     .implied_opt_name = "trace",
308     .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head),
309     .desc = {
310         {
311             .name = "file",
312             .type = QEMU_OPT_STRING,
313         },
314         { /* end if list */ }
315     },
316 };
317 #endif
318 
319 static QemuOptsList qemu_cpudef_opts = {
320     .name = "cpudef",
321     .head = QTAILQ_HEAD_INITIALIZER(qemu_cpudef_opts.head),
322     .desc = {
323         {
324             .name = "name",
325             .type = QEMU_OPT_STRING,
326         },{
327             .name = "level",
328             .type = QEMU_OPT_NUMBER,
329         },{
330             .name = "vendor",
331             .type = QEMU_OPT_STRING,
332         },{
333             .name = "family",
334             .type = QEMU_OPT_NUMBER,
335         },{
336             .name = "model",
337             .type = QEMU_OPT_NUMBER,
338         },{
339             .name = "stepping",
340             .type = QEMU_OPT_NUMBER,
341         },{
342             .name = "feature_edx",      /* cpuid 0000_0001.edx */
343             .type = QEMU_OPT_STRING,
344         },{
345             .name = "feature_ecx",      /* cpuid 0000_0001.ecx */
346             .type = QEMU_OPT_STRING,
347         },{
348             .name = "extfeature_edx",   /* cpuid 8000_0001.edx */
349             .type = QEMU_OPT_STRING,
350         },{
351             .name = "extfeature_ecx",   /* cpuid 8000_0001.ecx */
352             .type = QEMU_OPT_STRING,
353         },{
354             .name = "xlevel",
355             .type = QEMU_OPT_NUMBER,
356         },{
357             .name = "model_id",
358             .type = QEMU_OPT_STRING,
359         },{
360             .name = "vendor_override",
361             .type = QEMU_OPT_NUMBER,
362         },
363         { /* end of list */ }
364     },
365 };
366 
367 QemuOptsList qemu_spice_opts = {
368     .name = "spice",
369     .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
370     .desc = {
371         {
372             .name = "port",
373             .type = QEMU_OPT_NUMBER,
374         },{
375             .name = "tls-port",
376             .type = QEMU_OPT_NUMBER,
377         },{
378             .name = "addr",
379             .type = QEMU_OPT_STRING,
380         },{
381             .name = "ipv4",
382             .type = QEMU_OPT_BOOL,
383         },{
384             .name = "ipv6",
385             .type = QEMU_OPT_BOOL,
386         },{
387             .name = "password",
388             .type = QEMU_OPT_STRING,
389         },{
390             .name = "disable-ticketing",
391             .type = QEMU_OPT_BOOL,
392         },{
393             .name = "x509-dir",
394             .type = QEMU_OPT_STRING,
395         },{
396             .name = "x509-key-file",
397             .type = QEMU_OPT_STRING,
398         },{
399             .name = "x509-key-password",
400             .type = QEMU_OPT_STRING,
401         },{
402             .name = "x509-cert-file",
403             .type = QEMU_OPT_STRING,
404         },{
405             .name = "x509-cacert-file",
406             .type = QEMU_OPT_STRING,
407         },{
408             .name = "x509-dh-key-file",
409             .type = QEMU_OPT_STRING,
410         },{
411             .name = "tls-ciphers",
412             .type = QEMU_OPT_STRING,
413         },{
414             .name = "tls-channel",
415             .type = QEMU_OPT_STRING,
416         },{
417             .name = "plaintext-channel",
418             .type = QEMU_OPT_STRING,
419         },{
420             .name = "image-compression",
421             .type = QEMU_OPT_STRING,
422         },{
423             .name = "jpeg-wan-compression",
424             .type = QEMU_OPT_STRING,
425         },{
426             .name = "zlib-glz-wan-compression",
427             .type = QEMU_OPT_STRING,
428         },{
429             .name = "streaming-video",
430             .type = QEMU_OPT_STRING,
431         },{
432             .name = "agent-mouse",
433             .type = QEMU_OPT_BOOL,
434         },{
435             .name = "playback-compression",
436             .type = QEMU_OPT_BOOL,
437         },
438         { /* end if list */ }
439     },
440 };
441 
442 QemuOptsList qemu_option_rom_opts = {
443     .name = "option-rom",
444     .implied_opt_name = "romfile",
445     .head = QTAILQ_HEAD_INITIALIZER(qemu_option_rom_opts.head),
446     .desc = {
447         {
448             .name = "bootindex",
449             .type = QEMU_OPT_NUMBER,
450         }, {
451             .name = "romfile",
452             .type = QEMU_OPT_STRING,
453         },
454         { /* end if list */ }
455     },
456 };
457 
458 static QemuOptsList *vm_config_groups[32] = {
459     &qemu_drive_opts,
460     &qemu_chardev_opts,
461     &qemu_device_opts,
462     &qemu_netdev_opts,
463     &qemu_net_opts,
464     &qemu_rtc_opts,
465     &qemu_global_opts,
466     &qemu_mon_opts,
467     &qemu_cpudef_opts,
468 #ifdef CONFIG_SIMPLE_TRACE
469     &qemu_trace_opts,
470 #endif
471     &qemu_option_rom_opts,
472     NULL,
473 };
474 
find_list(QemuOptsList ** lists,const char * group)475 static QemuOptsList *find_list(QemuOptsList **lists, const char *group)
476 {
477     int i;
478 
479     for (i = 0; lists[i] != NULL; i++) {
480         if (strcmp(lists[i]->name, group) == 0)
481             break;
482     }
483     if (lists[i] == NULL) {
484         error_report("there is no option group \"%s\"", group);
485     }
486     return lists[i];
487 }
488 
qemu_find_opts(const char * group)489 QemuOptsList *qemu_find_opts(const char *group)
490 {
491     return find_list(vm_config_groups, group);
492 }
493 
qemu_add_opts(QemuOptsList * list)494 void qemu_add_opts(QemuOptsList *list)
495 {
496     int entries, i;
497 
498     entries = ARRAY_SIZE(vm_config_groups);
499     entries--; /* keep list NULL terminated */
500     for (i = 0; i < entries; i++) {
501         if (vm_config_groups[i] == NULL) {
502             vm_config_groups[i] = list;
503             return;
504         }
505     }
506     fprintf(stderr, "ran out of space in vm_config_groups");
507     abort();
508 }
509 
qemu_set_option(const char * str)510 int qemu_set_option(const char *str)
511 {
512     char group[64], id[64], arg[64];
513     QemuOptsList *list;
514     QemuOpts *opts;
515     int rc, offset;
516 
517     rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset);
518     if (rc < 3 || str[offset] != '=') {
519         error_report("can't parse: \"%s\"", str);
520         return -1;
521     }
522 
523     list = qemu_find_opts(group);
524     if (list == NULL) {
525         return -1;
526     }
527 
528     opts = qemu_opts_find(list, id);
529     if (!opts) {
530         error_report("there is no %s \"%s\" defined",
531                      list->name, id);
532         return -1;
533     }
534 
535     if (qemu_opt_set(opts, arg, str+offset+1) == -1) {
536         return -1;
537     }
538     return 0;
539 }
540 
qemu_global_option(const char * str)541 int qemu_global_option(const char *str)
542 {
543     char driver[64], property[64];
544     QemuOpts *opts;
545     int rc, offset;
546 
547     rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset);
548     if (rc < 2 || str[offset] != '=') {
549         error_report("can't parse: \"%s\"", str);
550         return -1;
551     }
552 
553     opts = qemu_opts_create(&qemu_global_opts, NULL, 0);
554     qemu_opt_set(opts, "driver", driver);
555     qemu_opt_set(opts, "property", property);
556     qemu_opt_set(opts, "value", str+offset+1);
557     return 0;
558 }
559 
560 struct ConfigWriteData {
561     QemuOptsList *list;
562     FILE *fp;
563 };
564 
config_write_opt(const char * name,const char * value,void * opaque)565 static int config_write_opt(const char *name, const char *value, void *opaque)
566 {
567     struct ConfigWriteData *data = opaque;
568 
569     fprintf(data->fp, "  %s = \"%s\"\n", name, value);
570     return 0;
571 }
572 
config_write_opts(QemuOpts * opts,void * opaque)573 static int config_write_opts(QemuOpts *opts, void *opaque)
574 {
575     struct ConfigWriteData *data = opaque;
576     const char *id = qemu_opts_id(opts);
577 
578     if (id) {
579         fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id);
580     } else {
581         fprintf(data->fp, "[%s]\n", data->list->name);
582     }
583     qemu_opt_foreach(opts, config_write_opt, data, 0);
584     fprintf(data->fp, "\n");
585     return 0;
586 }
587 
qemu_config_write(FILE * fp)588 void qemu_config_write(FILE *fp)
589 {
590     struct ConfigWriteData data = { .fp = fp };
591     QemuOptsList **lists = vm_config_groups;
592     int i;
593 
594     fprintf(fp, "# qemu config file\n\n");
595     for (i = 0; lists[i] != NULL; i++) {
596         data.list = lists[i];
597         qemu_opts_foreach(data.list, config_write_opts, &data, 0);
598     }
599 }
600 
qemu_config_parse(FILE * fp,QemuOptsList ** lists,const char * fname)601 int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
602 {
603     char line[1024], group[64], id[64], arg[64], value[1024];
604     Location loc;
605     QemuOptsList *list = NULL;
606     QemuOpts *opts = NULL;
607     int res = -1, lno = 0;
608 
609     loc_push_none(&loc);
610     while (fgets(line, sizeof(line), fp) != NULL) {
611         loc_set_file(fname, ++lno);
612         if (line[0] == '\n') {
613             /* skip empty lines */
614             continue;
615         }
616         if (line[0] == '#') {
617             /* comment */
618             continue;
619         }
620         if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) {
621             /* group with id */
622             list = find_list(lists, group);
623             if (list == NULL)
624                 goto out;
625             opts = qemu_opts_create(list, id, 1);
626             continue;
627         }
628         if (sscanf(line, "[%63[^]]]", group) == 1) {
629             /* group without id */
630             list = find_list(lists, group);
631             if (list == NULL)
632                 goto out;
633             opts = qemu_opts_create(list, NULL, 0);
634             continue;
635         }
636         if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) {
637             /* arg = value */
638             if (opts == NULL) {
639                 error_report("no group defined");
640                 goto out;
641             }
642             if (qemu_opt_set(opts, arg, value) != 0) {
643                 goto out;
644             }
645             continue;
646         }
647         error_report("parse error");
648         goto out;
649     }
650     if (ferror(fp)) {
651         error_report("error reading file");
652         goto out;
653     }
654     res = 0;
655 out:
656     loc_pop(&loc);
657     return res;
658 }
659 
qemu_read_config_file(const char * filename)660 int qemu_read_config_file(const char *filename)
661 {
662     FILE *f = fopen(filename, "r");
663     int ret;
664 
665     if (f == NULL) {
666         return -errno;
667     }
668 
669     ret = qemu_config_parse(f, vm_config_groups, filename);
670     fclose(f);
671 
672     if (ret == 0) {
673         return 0;
674     } else {
675         return -EINVAL;
676     }
677 }
678