Features/QCFG: Difference between revisions

From QEMU
No edit summary
No edit summary
Line 25: Line 25:


  // qemu/qcfg-marshal.h
  // qemu/qcfg-marshal.h
  static VncConfig *qcfg_unmarshal_VncConfig(const char *value, Error **errp)
  static VncConfig *qcfg_unmarshal_VncConfig(KeyValues *kv, Error **errp)
  {
  {
     VncConfig *config;
     VncConfig *config;
     Error *local_err = NULL;
     Error *local_err = NULL;
    const char *ptr = value;
    char *key, *value;
     bool has_addr = false;
     bool has_addr = false;
   
   
     config = qmp_alloc_VncConfig();
     config = qmp_alloc_VncConfig();
   
   
     while (qcfg_strsep(value, ',', &ptr, &key, &value)) {
     for (; kv; kv = kv->next) {
         if (strequals(key, "addr")) {
         if (strequals(kv->key, "addr")) {
             has_addr = true;
             has_addr = true;
             config->addr = qcfg_unmarshal_str(value, &local_err);
             config->addr = qcfg_unmarshal_str(kv->value, &local_err);
         } else if (strequals(key, "x509")) {
         } else if (strequals(kv->key, "x509")) {
             config->has_x509 = true;
             config->has_x509 = true;
             config->x509 = qcfg_unmarshal_str(value, &local_err);
             config->x509 = qcfg_unmarshal_str(kv->value, &local_err);
         } else if (strequals(key, "x509verify")) {
         } else if (strequals(kv->key, "x509verify")) {
             config->has_x509verify = true;
             config->has_x509verify = true;
             config->x509verify = qcfg_unmarshal_str(value, &local_err);
             config->x509verify = qcfg_unmarshal_str(kv->value, &local_err);
         } else if (strequals(key, "password")) {
         } else if (strequals(kv->key, "password")) {
             config->has_password = true;
             config->has_password = true;
             config->password = qcfg_unmarshal_bool(value, &local_err);
             config->password = qcfg_unmarshal_bool(kv->value, &local_err);
         } else if (strequals(key, "tls")) {
         } else if (strequals(kv->key, "tls")) {
             config->has_tls = true;
             config->has_tls = true;
             config->tls = qcfg_unmarshal_bool(value, &local_err);
             config->tls = qcfg_unmarshal_bool(kv->value, &local_err);
         } else if (strequals(key, "sasl")) {
         } else if (strequals(kv->key, "sasl")) {
             config->has_sasl = true;
             config->has_sasl = true;
             config->sasl = qcfg_unmarshal_bool(value, &local_err);
             config->sasl = qcfg_unmarshal_bool(kv->value, &local_err);
         } else {
         } else {
             error_set(&local_err, QERR_INVALID_PARAMETER, key);
             error_set(&local_err, QERR_INVALID_PARAMETER, kv->key);
         }
         }
   
   
Line 90: Line 88:
  void qcfg_vnc_config(VncConfig * config, Error **errp);
  void qcfg_vnc_config(VncConfig * config, Error **errp);
   
   
  static void qcfg_handle_vnc(const char *value, Error **errp)
  static void qcfg_handle_vnc(KeyValues *kv, Error **errp)
  {
  {
     VncConfig *config;
     VncConfig *config;
     Error *local_err = NULL;
     Error *local_err = NULL;
   
   
     config = qcfg_unmarshal_VncConfig(value, &local_err);
     config = qcfg_unmarshal_VncConfig(kv, &local_err);
     if (local_err) {
     if (local_err) {
         error_propagate(errp, local_err);
         error_propagate(errp, local_err);
Line 142: Line 140:
  DeviceState *qdev_ctor_isa_serial(ISASerialConfig *config, Error **errp);
  DeviceState *qdev_ctor_isa_serial(ISASerialConfig *config, Error **errp);
   
   
  static DeviceState *qdev_marshal_isa_serial(const char *value, Error **errp)
  static DeviceState *qdev_marshal_isa_serial(KeyValues *kv, Error **errp)
  {
  {
     ISASerialConfig *config;
     ISASerialConfig *config;
Line 148: Line 146:
     DeviceState *dev;
     DeviceState *dev;
   
   
     config = qcfg_unmarshal_ISASerialConfig(value, &local_err);
     config = qcfg_unmarshal_ISASerialConfig(kv, &local_err);
     if (local_err) {
     if (local_err) {
         error_propagate(errp, local_err);
         error_propagate(errp, local_err);

Revision as of 03:03, 12 March 2011

Summary

Replace QemuOpts infrastructure with code generation leveraging QAPI.

Owner

Detailed Summary

Use code generator to generate demarshalling routines for command line arguments into structures. Pass structures to function calls to process QEMU command line.

For instance, consider the -vnc option. We would first add a type corresponding to the options -vnc takes:

{ 'type': 'VncConfig',
  'data': { 'addr': 'str', '*x509': 'str', '*x509verify': 'str',
            '*password': 'bool', '*tls': 'bool', '*sasl': 'bool' } }

We can generate an unmarshalling function that will convert a string of comma encoded options to a proper structure. It will generate the following code:

// qemu/qcfg-marshal.h
static VncConfig *qcfg_unmarshal_VncConfig(KeyValues *kv, Error **errp)
{
    VncConfig *config;
    Error *local_err = NULL;
    bool has_addr = false;

    config = qmp_alloc_VncConfig();

    for (; kv; kv = kv->next) {
        if (strequals(kv->key, "addr")) {
            has_addr = true;
            config->addr = qcfg_unmarshal_str(kv->value, &local_err);
        } else if (strequals(kv->key, "x509")) {
            config->has_x509 = true;
            config->x509 = qcfg_unmarshal_str(kv->value, &local_err);
        } else if (strequals(kv->key, "x509verify")) {
            config->has_x509verify = true;
            config->x509verify = qcfg_unmarshal_str(kv->value, &local_err);
        } else if (strequals(kv->key, "password")) {
            config->has_password = true;
            config->password = qcfg_unmarshal_bool(kv->value, &local_err);
        } else if (strequals(kv->key, "tls")) {
            config->has_tls = true;
            config->tls = qcfg_unmarshal_bool(kv->value, &local_err);
        } else if (strequals(kv->key, "sasl")) {
            config->has_sasl = true;
            config->sasl = qcfg_unmarshal_bool(kv->value, &local_err);
        } else {
            error_set(&local_err, QERR_INVALID_PARAMETER, kv->key);
        }

        if (local_err) {
            goto out;
        }
    }

    if (!has_addr) {
        error_set(&local_err, QERR_MISSING_PARAMETER, "addr");
        goto out;
    }

    return config;

out:
    qmp_free_vnc_config(config);
    error_propagate(errp, local_err);
    return NULL;
}

There will still be the normal code generated in qmp-types and qmp-marshal-types which means that this type can also be encoded and decode via QMP.

In order to bridge this type to a function that is called to process a command line argument, we just need to add the following stanza:

// qemu/qcfg-schema.json
{ 'config': 'vnc', 'type': 'VncConfig' }

Which generates:

// qemu/qcfg.h
void qcfg_vnc_config(VncConfig * config, Error **errp);

static void qcfg_handle_vnc(KeyValues *kv, Error **errp)
{
    VncConfig *config;
    Error *local_err = NULL;

    config = qcfg_unmarshal_VncConfig(kv, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    qcfg_vnc(config, errp);

    qmp_free_VncConfig(config);
}

void qcfg_init(void)
{
    qcfg_register_handler("vnc", &qcfg_marshal_vnc);
}

The effect is that we can call a function that takes a string argument and will decode that to a strongly typed call to a function.

This function can also be reused by QMP trivially without going through multiple levels of marshaling and unmarshaling.

This provides a consistent way to document all options including the parameters that they take. It also provides a consistent way to do introspection of command line arguments.

This mechanism can also be applied to qdev which will allow for strongly typed qdev initialization functions that can also be trivially called from within QEMU.

For instance, for the serial device, we would do:

{ 'type': 'ISASerialConfig',
  'data': { '*index': 'int', '*iobase': 'int', '*irq': 'int',
            'chardev': 'CharDriverState' } }

This would generate a standard qmp-types entry and corresponding marshalling functions. This means the type is usuable via both QMP and through the command line. To generate a device factory interface, we just use the following schema entry:

{ 'device': 'isa-serial', 'config': 'ISASerialConfig' }

This would generate the following code:

DeviceState *qdev_ctor_isa_serial(ISASerialConfig *config, Error **errp);

static DeviceState *qdev_marshal_isa_serial(KeyValues *kv, Error **errp)
{
    ISASerialConfig *config;
    Error *local_err = NULL;
    DeviceState *dev;

    config = qcfg_unmarshal_ISASerialConfig(kv, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    dev = qdev_ctor_isa_serial(config, errp);

    qmp_free_isa_serial_config(config);

    return dev;
}

This lets QMP invoke qdev constructors directly without remarshaling to strings. It also provides a convenient interface for QEMU to consume internally to create devices which is fully type safe.

It also provides a mechanism to do full introspection of qdev and a consistent way to provide documentation.

Open Questions

  • Should we use a structure or marshal parameters as function calls?