[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