[libvirt] [PATCH 2/3] bhyve: implement bhyve argument parser
Fabian Freyer
fabian.freyer at physik.tu-berlin.de
Wed Jun 1 10:03:01 UTC 2016
On 01/06/16 10:21, Fabian Freyer wrote:
> A simpe getopt-based argument parser is added for the /usr/sbin/bhyve command,
> loosely based on its argument parser, which reads the following from the bhyve
> command line string:
getopt is not thread safe, so can't use that here. There are a number of possible
solutions:
- use gnulib's getopt which provides a reentrant interface:
https://lists.gnu.org/archive/html/bug-gnulib/2004-03/msg00018.html
- use string comparisons
I'll fix that in a v2.
>
> * vm name
> * number of vcpus
> * memory size
> * the time offset (UTC or localtime). This includes a capability check to see
> if this is actually supported by the bhyve version.
> * features:
> * acpi
> * ioapic: While this flag is deprecated in FreeBSD r257423, keep checking for
> it for backwards compatibiility.
> * the domain UUID; if not explicitely given, one will be generated.
> * lpc devices: for now only the com1 and com2 are supported. It is required for
> these to be /dev/nmdm[\d+][AB], and the slave devices are automatically
> inferred from these to be the corresponding end of the virtual null-modem
> cable: /dev/nmdm<N>A <-> /dev/nmdm<N>B
> * PCI devices:
> * Disks: these are numbered in the order they are found, for virtio and ahci
> disks separately. The destination is set to sdX or vdX with X='a'+index;
> therefore only 'z'-'a' disks are supported.
> Disks are considered to be block devices if the path
> starts with /dev, otherwise they are considered to be files.
> * Networks: only tap devices are supported. Since it isn't possible to tell
> the type of the network, VIR_DOMAIN_NET_TYPE_ETHERNET is assumed, since it
> is the most generic. If no mac is specified, one will be generated.
>
> Signed-off-by: Fabian Freyer <fabian.freyer at physik.tu-berlin.de>
> ---
> src/bhyve/bhyve_parse_command.c | 491 +++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 489 insertions(+), 2 deletions(-)
>
> diff --git a/src/bhyve/bhyve_parse_command.c b/src/bhyve/bhyve_parse_command.c
> index 72367bb..0fadb6a 100644
> --- a/src/bhyve/bhyve_parse_command.c
> +++ b/src/bhyve/bhyve_parse_command.c
> @@ -23,6 +23,7 @@
> */
>
> #include <config.h>
> +#include <getopt.h>
>
> #include "bhyve_capabilities.h"
> #include "bhyve_command.h"
> @@ -225,10 +226,493 @@ bhyveCommandLine2argv(const char *nativeConfig,
> return -1;
> }
>
> +static int
> +bhyveParseBhyveLPCArg(virDomainDefPtr def,
> + unsigned caps ATTRIBUTE_UNUSED,
> + const char *arg)
> +{
> + /* -l emulation[,config] */
> + const char *separator = NULL;
> + const char *param = NULL;
> + size_t last = 0;
> + virDomainChrDefPtr chr = NULL;
> + char *type = NULL;
> +
> + separator = strchr(arg, ',');
> + param = separator + 1;
> +
> + if (!separator)
> + goto error;
> +
> + if (VIR_STRNDUP(type, arg, separator - arg) < 0)
> + goto error;
> +
> + /* Only support com%d */
> + if (STRPREFIX(type, "com") && type[4] == 0) {
> + if (!(chr = virDomainChrDefNew()))
> + goto error;
> +
> + chr->source.type = VIR_DOMAIN_CHR_TYPE_NMDM;
> + chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
> +
> + if (!STRPREFIX(param, "/dev/nmdm")) {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to set com port %s: does not start with "
> + "'/dev/nmdm'."), type);
> + goto error;
> + }
> +
> + if (VIR_STRDUP(chr->source.data.file.path, param) < 0) {
> + virDomainChrDefFree(chr);
> + goto error;
> + }
> +
> + if (VIR_STRDUP(chr->source.data.nmdm.slave, chr->source.data.file.path)
> + < 0) {
> + virDomainChrDefFree(chr);
> + goto error;
> + }
> +
> + /* If the last character of the master is 'A', the slave will be 'B'
> + * and vice versa */
> + last = strlen(chr->source.data.file.path) - 1;
> + switch (chr->source.data.file.path[last]) {
> + case 'A':
> + chr->source.data.file.path[last] = 'B';
> + break;
> + case 'B':
> + chr->source.data.file.path[last] = 'A';
> + break;
> + default:
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to set slave for %s: last letter not "
> + "'A' or 'B'"),
> + chr->source.data.file.path);
> + goto error;
> + }
> +
> + switch (type[3]-'0') {
> + case 1:
> + case 2:
> + chr->target.port = type[3] - '1';
> + break;
> + default:
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to parse %s: only com1 and com2"
> + "supported."), type);
> + virDomainChrDefFree(chr);
> + goto error;
> + break;
> + }
> +
> + if (VIR_APPEND_ELEMENT(def->serials, def->nserials, chr) < 0) {
> + virDomainChrDefFree(chr);
> + goto error;
> + }
> + }
> +
> + VIR_FREE(type);
> + return 0;
> +
> +error:
> + VIR_FREE(chr);
> + VIR_FREE(type);
> + return -1;
> +}
> +
> +static int
> +bhyveParsePCISlot(const char *slotdef,
> + unsigned *pcislot,
> + unsigned *bus,
> + unsigned *function)
> +{
> + /* slot[:function] | bus:slot:function */
> + const char *curr = NULL;
> + const char *next = NULL;
> + unsigned values[3];
> + int i;
> +
> + curr = slotdef;
> + for (i = 0; i < 3; i++) {
> + char *val = NULL;
> +
> + next = strchr(curr, ':');
> +
> + if (VIR_STRNDUP(val, curr, next? next - curr : -1) < 0)
> + goto error;
> +
> + if (virStrToLong_ui(val, NULL, 10, &values[i]) < 0)
> + goto error;
> +
> + VIR_FREE(val);
> +
> + if (!next)
> + break;
> +
> + curr = next +1;
> + }
> +
> + *bus = 0;
> + *pcislot = 0;
> + *function = 0;
> +
> + switch (i + 1) {
> + case 2:
> + /* pcislot[:function] */
> + *function = values[1];
> + case 1:
> + *pcislot = values[0];
> + break;
> + case 3:
> + /* bus:pcislot:function */
> + *bus = values[0];
> + *pcislot = values[1];
> + *function = values[2];
> + break;
> + }
> +
> + return 0;
> +error:
> + return -1;
> +}
> +
> +static int
> +bhyveParsePCIDisk(virDomainDefPtr def,
> + unsigned caps ATTRIBUTE_UNUSED,
> + unsigned pcislot,
> + unsigned pcibus,
> + unsigned function,
> + int bus,
> + int device,
> + unsigned *nvirtiodisk,
> + unsigned *nahcidisk,
> + char *config)
> +{
> + /* -s slot,virtio-blk|ahci-cd|ahci-hd,/path/to/file */
> + const char *separator = NULL;
> + int index = -1;
> + virDomainDiskDefPtr disk = NULL;
> +
> + if (VIR_ALLOC(disk) < 0)
> + goto cleanup;
> + if (VIR_ALLOC(disk->src) < 0)
> + goto error;
> +
> + disk->bus = bus;
> + disk->device = device;
> +
> + disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
> + disk->info.addr.pci.slot = pcislot;
> + disk->info.addr.pci.bus = pcibus;
> + disk->info.addr.pci.function = function;
> +
> + if (STRPREFIX(config, "/dev/"))
> + disk->src->type = VIR_STORAGE_TYPE_BLOCK;
> + else
> + disk->src->type = VIR_STORAGE_TYPE_FILE;
> +
> + if (!config)
> + goto error;
> +
> + separator = strchr(config, ',');
> + if (VIR_STRNDUP(disk->src->path, config,
> + separator? separator - config : -1) < 0)
> + goto error;
> +
> + if (bus == VIR_DOMAIN_DISK_BUS_VIRTIO) {
> + index = *nvirtiodisk++;
> + if (VIR_STRDUP(disk->dst, "vda") < 0)
> + goto error;
> + }
> +
> + else if (bus == VIR_DOMAIN_DISK_BUS_SATA) {
> + index = *nahcidisk++;
> + if (VIR_STRDUP(disk->dst, "sda") < 0)
> + goto error;
> + }
> +
> + if (index > 'z' - 'a')
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("too many disks"));
> +
> + disk->dst[2] = 'a' + index;
> +
> + if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
> + goto error;
> +
> +cleanup:
> + return 0;
> +
> +error:
> + virDomainDiskDefFree(disk);
> + return -1;
> +}
> +
> +static int
> +bhyveParsePCINet(virDomainDefPtr def,
> + virDomainXMLOptionPtr xmlopt,
> + unsigned caps ATTRIBUTE_UNUSED,
> + unsigned pcislot,
> + unsigned pcibus,
> + unsigned function,
> + const char *config)
> +{
> + /* -s slot,virtio-net,tapN[,mac=xx:xx:xx:xx:xx:xx] */
> +
> + virDomainNetDefPtr net = NULL;
> + const char *separator = NULL;
> + const char *mac = NULL;
> +
> + if (VIR_ALLOC(net) < 0)
> + goto cleanup;
> +
> + /* Let's just assume it is VIR_DOMAIN_NET_TYPE_ETHERNET, it could also be
> + * a bridge, but this is the most generic option. */
> + net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
> +
> + net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
> + net->info.addr.pci.slot = pcislot;
> + net->info.addr.pci.bus = pcibus;
> + net->info.addr.pci.function = function;
> +
> + if (!config)
> + goto error;
> +
> + if (!STRPREFIX(config, "tap")) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("Only tap devices supported."));
> + goto error;
> + }
> +
> + separator = strchr(config, ',');
> + if (VIR_STRNDUP(net->ifname, config,
> + separator? separator - config : -1) < 0)
> + goto error;
> +
> + if (!separator)
> + goto cleanup;
> +
> + if (!STRPREFIX(++separator, "mac=")) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("Only mac option can be specified for virt-net"));
> + goto error;
> + }
> + mac = separator + 4;
> +
> + if (virMacAddrParse(mac, &net->mac) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("unable to parse mac address '%s'"),
> + mac);
> + goto cleanup;
> + }
> +
> +cleanup:
> + if (!mac)
> + virDomainNetGenerateMAC(xmlopt, &net->mac);
> +
> + if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0)
> + goto error;
> + return 0;
> +
> +error:
> + virDomainNetDefFree(net);
> + return -1;
> +}
> +
> +static int
> +bhyveParseBhyvePCIArg(virDomainDefPtr def,
> + virDomainXMLOptionPtr xmlopt,
> + unsigned caps,
> + unsigned *nvirtiodisk,
> + unsigned *nahcidisk,
> + const char *arg)
> +{
> + /* -s slot,emulation[,conf] */
> + const char *separator = NULL;
> + char *slotdef = NULL;
> + char *emulation = NULL;
> + char *conf = NULL;
> + unsigned pcislot, bus, function;
> +
> + separator = strchr(arg, ',');
> +
> + if (!separator)
> + goto error;
> + else
> + separator++; /* Skip comma */
> +
> + if (VIR_STRNDUP(slotdef, arg, separator - arg - 1) < 0)
> + goto error;
> +
> + conf = strchr(separator+1, ',');
> + if (conf)
> + conf++; /* Skip initial comma */
> +
> + if (VIR_STRNDUP(emulation, separator, conf? conf - separator - 1 : -1) < 0)
> + goto error;
> +
> + if (bhyveParsePCISlot(slotdef, &pcislot, &bus, &function) < 0)
> + goto error;
> +
> + if (STREQ(emulation, "ahci-cd"))
> + bhyveParsePCIDisk(def, caps, pcislot, bus, function,
> + VIR_DOMAIN_DISK_BUS_SATA,
> + VIR_DOMAIN_DISK_DEVICE_CDROM,
> + nvirtiodisk,
> + nahcidisk,
> + conf);
> + else if (STREQ(emulation, "ahci-hd"))
> + bhyveParsePCIDisk(def, caps, pcislot, bus, function,
> + VIR_DOMAIN_DISK_BUS_SATA,
> + VIR_DOMAIN_DISK_DEVICE_DISK,
> + nvirtiodisk,
> + nahcidisk,
> + conf);
> + else if (STREQ(emulation, "virtio-blk"))
> + bhyveParsePCIDisk(def, caps, pcislot, bus, function,
> + VIR_DOMAIN_DISK_BUS_VIRTIO,
> + VIR_DOMAIN_DISK_DEVICE_DISK,
> + nvirtiodisk,
> + nahcidisk,
> + conf);
> + else if (STREQ(emulation, "virtio-net"))
> + bhyveParsePCINet(def, xmlopt, caps, pcislot, bus, function, conf);
> +
> + VIR_FREE(emulation);
> + VIR_FREE(slotdef);
> + return 0;
> +error:
> + VIR_FREE(emulation);
> + VIR_FREE(slotdef);
> + return -1;
> +}
> +
> +/*
> + * Parse the /usr/sbin/bhyve command line.
> + */
> +static int
> +bhyveParseBhyveCommandLine(virDomainDefPtr def,
> + virDomainXMLOptionPtr xmlopt,
> + unsigned caps,
> + int argc, char **argv)
> +{
> + int c;
> + const char optstr[] = "abehuwxACHIPSWYp:g:c:s:m:l:U:";
> + int vcpus = 1;
> + size_t memory = 0;
> + unsigned nahcidisks = 0;
> + unsigned nvirtiodisks = 0;
> + int opterr_saved = opterr;
> +
> + if (!argv)
> + goto error;
> +
> + optind = 1;
> + opterr = 0;
> + while ((c = getopt(argc, argv, optstr)) != -1) {
> + switch (c) {
> + case 'A':
> + def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ON;
> + break;
> + case 'c':
> + if (virStrToLong_i(optarg, NULL, 10, &vcpus) < 0) {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to parse number of vCPUs."));
> + goto error;
> + }
> + if (virDomainDefSetVcpusMax(def, vcpus) < 0)
> + goto error;
> + if (virDomainDefSetVcpus(def, vcpus) < 0)
> + goto error;
> + break;
> + case 'l':
> + if (bhyveParseBhyveLPCArg(def, caps, optarg))
> + goto error;
> + break;
> + case 's':
> + if (bhyveParseBhyvePCIArg(def,
> + xmlopt,
> + caps,
> + &nahcidisks,
> + &nvirtiodisks,
> + optarg))
> + goto error;
> + break;
> + case 'm':
> + if (virStrToLong_ul(optarg, NULL, 10, &memory) < 0) {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to parse Memory."));
> + goto error;
> + }
> + /* For compatibility reasons, assume memory is givin in MB
> + * when < 1024, otherwise it is given in bytes */
> + if (memory < 1024)
> + memory *= 1024;
> + else
> + memory /= 1024UL;
> + if (def->mem.cur_balloon != 0 && def->mem.cur_balloon != memory) {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to parse Memory: Memory size mismatch."));
> + goto error;
> + }
> + def->mem.cur_balloon = memory;
> + virDomainDefSetMemoryTotal(def, memory);
> + break;
> + break;
> + case 'I':
> + /* While this flag was deprecated in FreeBSD r257423, keep checking
> + * for it for backwards compatibility. */
> + def->features[VIR_DOMAIN_FEATURE_APIC] = VIR_TRISTATE_SWITCH_ON;
> + break;
> + case 'u':
> + if ((caps & BHYVE_CAP_RTC_UTC) != 0) {
> + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC;
> + } else {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("Installed bhyve binary does not support "
> + "UTC clock"));
> + goto error;
> + }
> + break;
> + case 'U':
> + if (virUUIDParse(optarg, def->uuid) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, \
> + _("cannot parse UUID '%s'"), optarg);
> + goto error;
> + }
> + break;
> + }
> + }
> +
> + if (argc != optind) {
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to parse arguments for bhyve command."));
> + goto error;
> + }
> +
> + if (def->name == NULL) {
> + if (VIR_STRDUP(def->name, argv[argc]) < 0)
> + goto error;
> + }
> + else if (STRNEQ(def->name, argv[argc])) {
> + /* the vm name of the loader and the bhyverun command differ, throw an
> + * error here */
> + virReportError(VIR_ERR_OPERATION_FAILED,
> + _("Failed to parse arguments: VM name mismatch."));
> + goto error;
> + }
> +
> + opterr = opterr_saved;
> + return 0;
> +error:
> + opterr = opterr_saved;
> + return -1;
> +}
> +
> virDomainDefPtr
> bhyveParseCommandLineString(const char* nativeConfig,
> - unsigned caps ATTRIBUTE_UNUSED,
> - virDomainXMLOptionPtr xmlopt ATTRIBUTE_UNUSED)
> + unsigned caps,
> + virDomainXMLOptionPtr xmlopt)
> {
> virDomainDefPtr def = NULL;
> int bhyve_argc = 0;
> @@ -256,6 +740,9 @@ bhyveParseCommandLineString(const char* nativeConfig,
> goto cleanup;
> }
>
> + if (bhyveParseBhyveCommandLine(def, xmlopt, caps, bhyve_argc, bhyve_argv))
> + goto cleanup;
> +
> cleanup:
> virStringFreeList(loader_argv);
> virStringFreeList(bhyve_argv);
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 842 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20160601/b5369cd5/attachment-0001.sig>
More information about the libvir-list
mailing list