[Libvir] [PATCH] sound support for qemu and xen

Jim Meyering jim at meyering.net
Tue Apr 29 10:53:01 UTC 2008


Cole Robinson <crobinso at redhat.com> wrote:
> Cole Robinson wrote:
>> The patch below adds xml support for the soundhw option to qemu
>> and xen. The new xml element takes the form:
...
> Again, this needs to be rediff'd around recent commits (virBuffer
> changes, probably others), which I will do next round after any
> feedback.

Hi Cole,

Yes, parsing in C is no fun...

> diff --git a/src/qemu_conf.c b/src/qemu_conf.c
> index d9b82b2..1b68806 100644
> --- a/src/qemu_conf.c
> +++ b/src/qemu_conf.c
> @@ -1011,6 +1011,64 @@ static int qemudParseInputXML(virConnectPtr conn,
>      return -1;
>  }
>
> +/* Sound device helper functions */
> +static int qemudSoundModelFromString(virConnectPtr conn,
> +                                      const char *model) {

Please don't require a "conn" argument in functions like this.
Instead, let the caller handle any diagnostic.
For an example where this would be useful, see below.

> +    if (STREQ(model, "sb16")) {
> +        return QEMU_SOUND_SB16;
> +    } else if (STREQ(model, "es1370")) {
> +        return QEMU_SOUND_ES1370;
> +    } else if (STREQ(model, "pcspk")) {
> +        return QEMU_SOUND_PCSPK;
> +    }
> +
> +    qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_ARG,
> +                     _("invalid sound model '%s'"), model);
> +    return -1;
> +}
> +
> +static const char *qemudSoundModelToString(virConnectPtr conn,
> +                                            const int model) {

Likewise.

> +    if (model == QEMU_SOUND_SB16) {
> +        return "sb16";
> +    } else if (model == QEMU_SOUND_ES1370) {
> +        return "es1370";
> +    } else if (model == QEMU_SOUND_PCSPK) {
> +        return "pcspk";
> +    }
> +
> +    qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
> +                     _("invalid sound model '%d'"), model);
> +    return NULL;
> +}
> +
> +
> +static int qemudParseSoundXML(virConnectPtr conn,
> +                              struct qemud_vm_sound_def *sound,
> +                              xmlNodePtr node) {

Can this be "const"?  It looks like it should be.

> +                              const xmlNodePtr node) {

Maybe omit "conn" here too.  Maybe.
Callers could say "missing or invalid sound model", but that's
slightly degraded from the current "invalid sound model '%s'".

> +    xmlChar *model = NULL;
> +    model = xmlGetProp(node, BAD_CAST "model");
> +
> +    if (!model) {
> +        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
> +                         "%s", _("missing sound model"));
> +        goto error;
> +    }
> +    if ((sound->model = qemudSoundModelFromString(conn, (char *) model)) < 0)
> +        goto error;
> +
> +    if (model)
> +        xmlFree(model);
> +    return 0;
> +
> + error:
> +    if (model)
> +        xmlFree(model);
> +    return -1;

It's better to record "int err = -1;" initially, and set
err = 0 if all goes well.  Then you can have a single point
of return and avoid the duplicate xmlFree, e.g.,

    err = 0;

 error:
    xmlFree(model);
    return err;

...
> +    /* Add sound hardware */
> +    if (sound) {
> +        int size = 100;
> +        char *modstr = calloc(1, size+1);
> +        if (!modstr)
> +            goto no_memory;
> +        if (!((*argv)[++n] = strdup("-soundhw")))
> +            goto no_memory;
> +
> +        while(sound && size > 0) {
> +            const char *model = qemudSoundModelToString(conn, sound->model);
> +            strncat(modstr, model, size);

qemudSoundModelToString can return NULL, so you won't want to
use strncat or strlen on that.

...
> +char *sound_string_to_xml(const char *sound) {
> +
> +    char *comma, *model, *dupe;
> +    virBuffer buf;
> +    int collision, modelsize;

Most of the above (but e.g., not "buf") declarations
can be moved "down" into the scope where they're used.

> +    if (!(buf.content = calloc(1, 1024)))
> +        return NULL;
> +    buf.size = 1024;
> +    buf.use = 0;
> +
> +    while (sound) {
> +
> +        collision = 0;
> +        model = NULL;
> +        modelsize = strlen(sound);
> +        if ((comma = strchr(sound, ','))) {
> +            modelsize -= strlen(comma);
> +        }

slightly more efficient, and clearer to me:

    comma = strchr(sound, ',');
    modelsize = comma ? comma - sound : strlen (sound);

> +
> +        // Parse out first element up to comma
> +        if (!strncmp(sound, "sb16", modelsize)) {
> +            model = strdup("sb16");
> +        } else if (!strncmp(sound, "es1370", modelsize)) {
> +            model = strdup("es1370");
> +        } else if (!strncmp(sound, "pcspk", modelsize)) {
> +            model = strdup("pcspk");
> +        }

You can avoid enumerating the types here.  E.g.,
  model = strndup(sound, modelsize);
  if (!model)
      continue;

  if ((m = qemudSoundModelFromString(model)) < 0) {
      free(model);
      continue;
  }

The duplicate detection below misses when a real match is obscured by a
partial one, e.g., a string like "es1370.,es1370" where strstr matches,
but a subsequent word-boundary test fails.
How about a cheaper test, e.g., (with this declaration and initialization
above: char seenSoundModel[20]; memset(seenSoundModel,0,sizeof seenSoundModel);

  assert (m < sizeof seenSoundModel);
  if (seenSoundModel[m])
      collision = 1;
  seenSoundModel[m] = 1;

> +        // Check that model is not already in remaining soundstr
> +        if (comma && model && (dupe = strstr(comma, model))) {
> +            if (( (dupe == sound) ||                //(Start of line |
> +                  (*(dupe - 1) == ',') ) &&         // Preceded by comma) &
> +                ( (dupe[strlen(model)] == ',') ||   //(Ends with comma |
> +                  (dupe[strlen(model)] == '\0') ))  // Ends whole string)
> +                collision = 1;
> +        }
...
>      /* HVM guests, or old PV guests use this config format */
> @@ -1040,6 +1052,10 @@ char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf) {
>      buf->content = NULL;
>      virBufferFree(buf);
>      return (xml);
> +
> +  error:
> +    virBufferFree(buf);
> +    return (NULL);

You can avoid this duplication, too.
As suggested above except here with "char *ret = NULL;", etc.
and "return xml;"

>  }
...
> +char * virBuildSoundStringFromXML(virConnectPtr conn,
> +                                  xmlXPathContextPtr ctxt) {
> +
> +    int nb_nodes, size = 256;
> +    char *dupe, *sound;
> +    xmlNodePtr *nodes = NULL;
> +
> +    if (!(sound = calloc(1, size+1))) {
> +        virXMLError(conn, VIR_ERR_NO_MEMORY,
> +                    _("failed to allocate sound string"), 0);
> +        return NULL;
> +    }
> +
> +    nb_nodes = virXPathNodeSet("/domain/devices/sound", ctxt, &nodes);
> +    if (nb_nodes > 0) {
> +        int i;
> +        for (i = 0; i < nb_nodes && size > 0; i++) {
> +            char *model = NULL;
> +            int collision = 0;
> +
> +            model = (char *) xmlGetProp(nodes[i], (xmlChar *) "model");
> +            if (!model) {
> +                virXMLError(conn, VIR_ERR_XML_ERROR,
> +                            _("no model for sound device"), 0);
> +                goto error;
> +            }
> +
> +            if (!(STREQ(model, "pcspk")||
> +                  STREQ(model, "sb16") ||
> +                  STREQ(model, "es1370"))) {

Don't repeat this list of literal strings.
Imagine having to add a new model type.
You'll want that change to require modification of a minimum number of
places in the code (probably just 2: convert from string to int and int
to string).

Instead, just test qemudSoundModelFromString(model) < 0

> +                virXMLError(conn, VIR_ERR_XML_ERROR,
> +                            _("unknown sound model type"), 0);
> +                free(model);
> +                goto error;
> +            }
> +
> +            // Check for duplicates in currently built string
> +            if (*sound && (dupe = strstr(sound, model))) {

This code looks way too similar to the dup-detecting code above.
Once the above is in a shape you like, consider factoring it
out into a function and reusing that function here.

> +                if (( (dupe == sound) ||                //(Start of line |
> +                      (*(dupe - 1) == ',') ) &&         // Preceded by comma) &
> +                    ( (dupe[strlen(model)] == ',') ||   //(Ends with comma |
> +                      (dupe[strlen(model)] == '\0') ))  // Ends whole string)
> +                    collision = 1;
> +            }
> +
> +            // If no collision, add to string
> +            if (!collision) {
> +                if (*sound && (size >= (strlen(model) + 1))) {
> +                    strncat(sound, ",", size--);
> +                } else if (*sound || size < strlen(model)) {
> +                    free(model);
> +                    continue;
> +                }
> +                strncat(sound, model, size);
> +                size -= strlen(model);
...




More information about the libvir-list mailing list