virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def,
                              xmlNodePtr cur, unsigned int flags,
                              virDomainChrDefPtr chr_def,
                              xmlXPathContextPtr ctxt)
{
    bool logParsed = false;
    bool protocolParsed = false;
    int sourceParsed = 0;

    for (; cur; cur = cur->next) {
        if (cur->type != XML_ELEMENT_NODE)
            continue;

        if (virXMLNodeNameEqual(cur, "source")) {
            /* Parse only the first source element since only one is used
             * for chardev devices, the only exception is UDP type, where
             * user can specify two source elements. */
            if (sourceParsed >= 1 && def->type != VIR_DOMAIN_CHR_TYPE_UDP) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("only one source element is allowed for "
                                 "character device"));
                goto error;
            } else if (sourceParsed >= 2) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("only two source elements are allowed for "
                                 "character device"));
                goto error;
            }
            sourceParsed++;

            switch ((virDomainChrType) def->type) {
            case VIR_DOMAIN_CHR_TYPE_FILE:
                if (virDomainChrSourceDefParseFile(def, cur) < 0)
                    goto error;
                break;

            case VIR_DOMAIN_CHR_TYPE_PTY:
                /* PTY path is only parsed from live xml.  */
                if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
                    def->data.file.path = virXMLPropString(cur, "path");
                break;

            case VIR_DOMAIN_CHR_TYPE_DEV:
            case VIR_DOMAIN_CHR_TYPE_PIPE:
                def->data.file.path = virXMLPropString(cur, "path");
                break;

            case VIR_DOMAIN_CHR_TYPE_UNIX:
                if (virDomainChrSourceDefParseUnix(def, cur, ctxt) < 0)
                    goto error;
                break;

            case VIR_DOMAIN_CHR_TYPE_UDP:
                if (virDomainChrSourceDefParseUDP(def, cur) < 0)
                    goto error;
                break;

            case VIR_DOMAIN_CHR_TYPE_TCP:
                if (virDomainChrSourceDefParseTCP(def, cur, ctxt, flags) < 0)
                    goto error;
                break;

            case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
                def->data.spiceport.channel = virXMLPropString(cur, "channel");
                break;

            case VIR_DOMAIN_CHR_TYPE_NMDM:
                def->data.nmdm.master = virXMLPropString(cur, "master");
                def->data.nmdm.slave = virXMLPropString(cur, "slave");
                break;

            case VIR_DOMAIN_CHR_TYPE_LAST:
            case VIR_DOMAIN_CHR_TYPE_NULL:
            case VIR_DOMAIN_CHR_TYPE_VC:
            case VIR_DOMAIN_CHR_TYPE_STDIO:
            case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
                break;
            }

            /* Check for an optional seclabel override in <source/>. */
            if (chr_def) {
                xmlNodePtr saved_node = ctxt->node;
                ctxt->node = cur;
                if (virSecurityDeviceLabelDefParseXML(&def->seclabels,
                                                      &def->nseclabels,
                                                      ctxt,
                                                      flags) < 0) {
                    ctxt->node = saved_node;
                    goto error;
                }
                ctxt->node = saved_node;
            }
        } else if (virXMLNodeNameEqual(cur, "log")) {
            if (logParsed) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("only one protocol element is allowed for "
                                 "character device"));
                goto error;
            }
            logParsed = true;
            if (virDomainChrSourceDefParseLog(def, cur) < 0)
                goto error;
        } else if (virXMLNodeNameEqual(cur, "protocol")) {
            if (protocolParsed) {
                virReportError(VIR_ERR_XML_ERROR, "%s",
                               _("only one log element is allowed for "
                                 "character device"));
                goto error;
            }
            protocolParsed = true;
            if (virDomainChrSourceDefParseProtocol(def, cur) < 0)
                goto error;
        }
    }

    return 0;

 error:
    virDomainChrSourceDefClear(def);
    return -1;
}