virDomainDefFormatInternalSetRootName(virDomainDefPtr def,
                                      virDomainXMLOptionPtr xmlopt,
                                      virBufferPtr buf,
                                      const char *rootname,
                                      unsigned int flags)
{
    unsigned char *uuid;
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    const char *type = NULL;
    int n;
    size_t i;

    virCheckFlags(VIR_DOMAIN_DEF_FORMAT_COMMON_FLAGS |
                  VIR_DOMAIN_DEF_FORMAT_STATUS |
                  VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET |
                  VIR_DOMAIN_DEF_FORMAT_PCI_ORIG_STATES |
                  VIR_DOMAIN_DEF_FORMAT_CLOCK_ADJUST,
                  -1);

    if (!(type = virDomainVirtTypeToString(def->virtType))) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unexpected domain type %d"), def->virtType);
        goto error;
    }

    if (def->id == -1)
        flags |= VIR_DOMAIN_DEF_FORMAT_INACTIVE;

    virBufferAsprintf(buf, "<%s type='%s'", rootname, type);
    if (!(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE))
        virBufferAsprintf(buf, " id='%d'", def->id);
    if (def->namespaceData && def->ns.format)
        virXMLNamespaceFormatNS(buf, &def->ns);
    virBufferAddLit(buf, ">\n");
    virBufferAdjustIndent(buf, 2);

    virBufferEscapeString(buf, "<name>%s</name>\n", def->name);

    uuid = def->uuid;
    virUUIDFormat(uuid, uuidstr);
    virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);

    if (def->genidRequested) {
        char genidstr[VIR_UUID_STRING_BUFLEN];

        virUUIDFormat(def->genid, genidstr);
        virBufferAsprintf(buf, "<genid>%s</genid>\n", genidstr);
    }

    virBufferEscapeString(buf, "<title>%s</title>\n", def->title);

    virBufferEscapeString(buf, "<description>%s</description>\n",
                          def->description);

    if (def->metadata) {
        xmlBufferPtr xmlbuf;
        int oldIndentTreeOutput = xmlIndentTreeOutput;

        /* Indentation on output requires that we previously set
         * xmlKeepBlanksDefault to 0 when parsing; also, libxml does 2
         * spaces per level of indentation of intermediate elements,
         * but no leading indentation before the starting element.
         * Thankfully, libxml maps what looks like globals into
         * thread-local uses, so we are thread-safe.  */
        xmlIndentTreeOutput = 1;
        xmlbuf = xmlBufferCreate();
        if (xmlNodeDump(xmlbuf, def->metadata->doc, def->metadata,
                        virBufferGetIndent(buf) / 2, 1) < 0) {
            xmlBufferFree(xmlbuf);
            xmlIndentTreeOutput = oldIndentTreeOutput;
            goto error;
        }
        virBufferAsprintf(buf, "%s\n", (char *) xmlBufferContent(xmlbuf));
        xmlBufferFree(xmlbuf);
        xmlIndentTreeOutput = oldIndentTreeOutput;
    }

    if (virDomainDefHasMemoryHotplug(def)) {
        virBufferAsprintf(buf,
                          "<maxMemory slots='%u' unit='KiB'>%llu</maxMemory>\n",
                          def->mem.memory_slots, def->mem.max_memory);
    }

    virBufferAddLit(buf, "<memory");
    if (def->mem.dump_core)
        virBufferAsprintf(buf, " dumpCore='%s'",
                          virTristateSwitchTypeToString(def->mem.dump_core));
    virBufferAsprintf(buf, " unit='KiB'>%llu</memory>\n",
                      virDomainDefGetMemoryTotal(def));

    virBufferAsprintf(buf, "<currentMemory unit='KiB'>%llu</currentMemory>\n",
                      def->mem.cur_balloon);

    if (virDomainDefFormatBlkiotune(buf, def) < 0)
        goto error;

    virDomainMemtuneFormat(buf, &def->mem);
    virDomainMemorybackingFormat(buf, &def->mem);

    if (virDomainCpuDefFormat(buf, def) < 0)
        goto error;

    if (def->niothreadids > 0) {
        virBufferAsprintf(buf, "<iothreads>%zu</iothreads>\n",
                          def->niothreadids);
        if (virDomainDefIothreadShouldFormat(def)) {
            virBufferAddLit(buf, "<iothreadids>\n");
            virBufferAdjustIndent(buf, 2);
            for (i = 0; i < def->niothreadids; i++) {
                virBufferAsprintf(buf, "<iothread id='%u'/>\n",
                                  def->iothreadids[i]->iothread_id);
            }
            virBufferAdjustIndent(buf, -2);
            virBufferAddLit(buf, "</iothreadids>\n");
        }
    }

    if (virDomainCputuneDefFormat(buf, def, flags) < 0)
        goto error;

    if (virDomainNumatuneFormatXML(buf, def->numa) < 0)
        goto error;

    if (def->resource)
        virDomainResourceDefFormat(buf, def->resource);

    if (def->sysinfo)
        ignore_value(virSysinfoFormat(buf, def->sysinfo));

    if (def->os.bootloader) {
        virBufferEscapeString(buf, "<bootloader>%s</bootloader>\n",
                              def->os.bootloader);
        virBufferEscapeString(buf,
                              "<bootloader_args>%s</bootloader_args>\n",
                              def->os.bootloaderArgs);
    }

    virBufferAddLit(buf, "<os");
    if (def->os.firmware)
        virBufferAsprintf(buf, " firmware='%s'",
                          virDomainOsDefFirmwareTypeToString(def->os.firmware));
    virBufferAddLit(buf, ">\n");
    virBufferAdjustIndent(buf, 2);
    virBufferAddLit(buf, "<type");
    if (def->os.arch)
        virBufferAsprintf(buf, " arch='%s'", virArchToString(def->os.arch));
    if (def->os.machine)
        virBufferAsprintf(buf, " machine='%s'", def->os.machine);
    /*
     * HACK: For xen driver we previously used bogus 'linux' as the
     * os type for paravirt, whereas capabilities declare it to
     * be 'xen'. So we convert to the former for backcompat
     */
    if (def->virtType == VIR_DOMAIN_VIRT_XEN &&
        def->os.type == VIR_DOMAIN_OSTYPE_XEN)
        virBufferAsprintf(buf, ">%s</type>\n",
                          virDomainOSTypeToString(VIR_DOMAIN_OSTYPE_LINUX));
    else
        virBufferAsprintf(buf, ">%s</type>\n",
                          virDomainOSTypeToString(def->os.type));

    virBufferEscapeString(buf, "<init>%s</init>\n",
                          def->os.init);
    for (i = 0; def->os.initargv && def->os.initargv[i]; i++)
        virBufferEscapeString(buf, "<initarg>%s</initarg>\n",
                              def->os.initargv[i]);
    for (i = 0; def->os.initenv && def->os.initenv[i]; i++)
        virBufferAsprintf(buf, "<initenv name='%s'>%s</initenv>\n",
                          def->os.initenv[i]->name, def->os.initenv[i]->value);
    if (def->os.initdir)
        virBufferEscapeString(buf, "<initdir>%s</initdir>\n",
                              def->os.initdir);
    if (def->os.inituser)
        virBufferAsprintf(buf, "<inituser>%s</inituser>\n", def->os.inituser);
    if (def->os.initgroup)
        virBufferAsprintf(buf, "<initgroup>%s</initgroup>\n", def->os.initgroup);

    if (def->os.loader)
        virDomainLoaderDefFormat(buf, def->os.loader);
    virBufferEscapeString(buf, "<kernel>%s</kernel>\n",
                          def->os.kernel);
    virBufferEscapeString(buf, "<initrd>%s</initrd>\n",
                          def->os.initrd);
    virBufferEscapeString(buf, "<cmdline>%s</cmdline>\n",
                          def->os.cmdline);
    virBufferEscapeString(buf, "<dtb>%s</dtb>\n",
                          def->os.dtb);
    virBufferEscapeString(buf, "<root>%s</root>\n",
                          def->os.root);
    if (def->os.slic_table) {
        virBufferAddLit(buf, "<acpi>\n");
        virBufferAdjustIndent(buf, 2);
        virBufferEscapeString(buf, "<table type='slic'>%s</table>\n",
                              def->os.slic_table);
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</acpi>\n");
    }

    if (!def->os.bootloader) {
        for (n = 0; n < def->os.nBootDevs; n++) {
            const char *boottype =
                virDomainBootTypeToString(def->os.bootDevs[n]);
            if (!boottype) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("unexpected boot device type %d"),
                               def->os.bootDevs[n]);
                goto error;
            }
            virBufferAsprintf(buf, "<boot dev='%s'/>\n", boottype);
        }

        if (def->os.bootmenu) {
            virBufferAsprintf(buf, "<bootmenu enable='%s'",
                              virTristateBoolTypeToString(def->os.bootmenu));
            if (def->os.bm_timeout_set)
                virBufferAsprintf(buf, " timeout='%u'", def->os.bm_timeout);
            virBufferAddLit(buf, "/>\n");
        }

        if (def->os.bios.useserial || def->os.bios.rt_set) {
            virBufferAddLit(buf, "<bios");
            if (def->os.bios.useserial)
                virBufferAsprintf(buf, " useserial='%s'",
                                  virTristateBoolTypeToString(def->os.bios.useserial));
            if (def->os.bios.rt_set)
                virBufferAsprintf(buf, " rebootTimeout='%d'", def->os.bios.rt_delay);

            virBufferAddLit(buf, "/>\n");
        }
    }

    if (def->os.smbios_mode) {
        const char *mode;

        mode = virDomainSmbiosModeTypeToString(def->os.smbios_mode);
        if (mode == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("unexpected smbios mode %d"), def->os.smbios_mode);
            goto error;
        }
        virBufferAsprintf(buf, "<smbios mode='%s'/>\n", mode);
    }

    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</os>\n");


    if (def->idmap.uidmap) {
        virBufferAddLit(buf, "<idmap>\n");
        virBufferAdjustIndent(buf, 2);
        for (i = 0; i < def->idmap.nuidmap; i++) {
            virBufferAsprintf(buf,
                              "<uid start='%u' target='%u' count='%u'/>\n",
                              def->idmap.uidmap[i].start,
                              def->idmap.uidmap[i].target,
                              def->idmap.uidmap[i].count);
        }
        for (i = 0; i < def->idmap.ngidmap; i++) {
            virBufferAsprintf(buf,
                              "<gid start='%u' target='%u' count='%u'/>\n",
                              def->idmap.gidmap[i].start,
                              def->idmap.gidmap[i].target,
                              def->idmap.gidmap[i].count);
        }
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</idmap>\n");
    }

    if (virDomainDefFormatFeatures(buf, def) < 0)
        goto error;

    if (virCPUDefFormatBufFull(buf, def->cpu, def->numa) < 0)
        goto error;

    virBufferAsprintf(buf, "<clock offset='%s'",
                      virDomainClockOffsetTypeToString(def->clock.offset));
    switch (def->clock.offset) {
    case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME:
    case VIR_DOMAIN_CLOCK_OFFSET_UTC:
        if (def->clock.data.utc_reset)
            virBufferAddLit(buf, " adjustment='reset'");
        break;
    case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE:
        virBufferAsprintf(buf, " adjustment='%lld' basis='%s'",
                          def->clock.data.variable.adjustment,
                          virDomainClockBasisTypeToString(def->clock.data.variable.basis));
        if (flags & VIR_DOMAIN_DEF_FORMAT_CLOCK_ADJUST) {
            if (def->clock.data.variable.adjustment0)
                virBufferAsprintf(buf, " adjustment0='%lld'",
                                  def->clock.data.variable.adjustment0);
        }
        break;
    case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE:
        virBufferEscapeString(buf, " timezone='%s'", def->clock.data.timezone);
        break;
    }
    if (def->clock.ntimers == 0) {
        virBufferAddLit(buf, "/>\n");
    } else {
        virBufferAddLit(buf, ">\n");
        virBufferAdjustIndent(buf, 2);
        for (n = 0; n < def->clock.ntimers; n++) {
            if (virDomainTimerDefFormat(buf, def->clock.timers[n]) < 0)
                goto error;
        }
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</clock>\n");
    }

    if (virDomainEventActionDefFormat(buf, def->onPoweroff,
                                      "on_poweroff",
                                      virDomainLifecycleActionTypeToString) < 0)
        goto error;
    if (virDomainEventActionDefFormat(buf, def->onReboot,
                                      "on_reboot",
                                      virDomainLifecycleActionTypeToString) < 0)
        goto error;
    if (virDomainEventActionDefFormat(buf, def->onCrash,
                                      "on_crash",
                                      virDomainLifecycleActionTypeToString) < 0)
        goto error;
    if (def->onLockFailure != VIR_DOMAIN_LOCK_FAILURE_DEFAULT &&
        virDomainEventActionDefFormat(buf, def->onLockFailure,
                                      "on_lockfailure",
                                      virDomainLockFailureTypeToString) < 0)
        goto error;

    if (def->pm.s3 || def->pm.s4) {
        virBufferAddLit(buf, "<pm>\n");
        virBufferAdjustIndent(buf, 2);
        if (def->pm.s3) {
            virBufferAsprintf(buf, "<suspend-to-mem enabled='%s'/>\n",
                              virTristateBoolTypeToString(def->pm.s3));
        }
        if (def->pm.s4) {
            virBufferAsprintf(buf, "<suspend-to-disk enabled='%s'/>\n",
                              virTristateBoolTypeToString(def->pm.s4));
        }
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</pm>\n");
    }

    virDomainPerfDefFormat(buf, &def->perf);

    virBufferAddLit(buf, "<devices>\n");
    virBufferAdjustIndent(buf, 2);

    virBufferEscapeString(buf, "<emulator>%s</emulator>\n",
                          def->emulator);

    for (n = 0; n < def->ndisks; n++)
        if (virDomainDiskDefFormat(buf, def->disks[n], flags, xmlopt) < 0)
            goto error;

    for (n = 0; n < def->ncontrollers; n++)
        if (virDomainControllerDefFormat(buf, def->controllers[n], flags) < 0)
            goto error;

    for (n = 0; n < def->nleases; n++)
        if (virDomainLeaseDefFormat(buf, def->leases[n]) < 0)
            goto error;

    for (n = 0; n < def->nfss; n++)
        if (virDomainFSDefFormat(buf, def->fss[n], flags) < 0)
            goto error;

    for (n = 0; n < def->nnets; n++)
        if (virDomainNetDefFormat(buf, def->nets[n], xmlopt, flags) < 0)
            goto error;

    for (n = 0; n < def->nsmartcards; n++)
        if (virDomainSmartcardDefFormat(buf, def->smartcards[n], flags) < 0)
            goto error;

    for (n = 0; n < def->nserials; n++)
        if (virDomainChrDefFormat(buf, def->serials[n], flags) < 0)
            goto error;

    for (n = 0; n < def->nparallels; n++)
        if (virDomainChrDefFormat(buf, def->parallels[n], flags) < 0)
            goto error;

    for (n = 0; n < def->nconsoles; n++) {
        virDomainChrDef console;
        /* Back compat, ignore the console element for hvm guests
         * if it is type == serial
         */
        if (def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
            (def->consoles[n]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL ||
             def->consoles[n]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE) &&
            (n < def->nserials)) {
            memcpy(&console, def->serials[n], sizeof(console));
            console.deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
            console.targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
        } else {
            memcpy(&console, def->consoles[n], sizeof(console));
        }
        if (virDomainChrDefFormat(buf, &console, flags) < 0)
            goto error;
    }

    for (n = 0; n < def->nchannels; n++)
        if (virDomainChrDefFormat(buf, def->channels[n], flags) < 0)
            goto error;

    for (n = 0; n < def->ninputs; n++) {
        if (virDomainInputDefFormat(buf, def->inputs[n], flags) < 0)
            goto error;
    }

    if (def->tpm) {
        if (virDomainTPMDefFormat(buf, def->tpm, flags) < 0)
            goto error;
    }

    for (n = 0; n < def->ngraphics; n++) {
        if (virDomainGraphicsDefFormat(buf, def->graphics[n], flags) < 0)
            goto error;
    }

    for (n = 0; n < def->nsounds; n++) {
        if (virDomainSoundDefFormat(buf, def->sounds[n], flags) < 0)
            goto error;
    }

    for (n = 0; n < def->nvideos; n++) {
        if (virDomainVideoDefFormat(buf, def->videos[n], flags) < 0)
            goto error;
    }

    for (n = 0; n < def->nhostdevs; n++) {
        /* If parentnet != NONE, this is just a pointer to the
         * hostdev in a higher-level device (e.g. virDomainNetDef),
         * and will have already been formatted there.
         */
        if (!def->hostdevs[n]->parentnet &&
            virDomainHostdevDefFormat(buf, def->hostdevs[n], flags) < 0) {
            goto error;
        }
    }

    for (n = 0; n < def->nredirdevs; n++) {
        if (virDomainRedirdevDefFormat(buf, def->redirdevs[n], flags) < 0)
            goto error;
    }

    if (def->redirfilter)
        virDomainRedirFilterDefFormat(buf, def->redirfilter);

    for (n = 0; n < def->nhubs; n++) {
        if (virDomainHubDefFormat(buf, def->hubs[n], flags) < 0)
            goto error;
    }

    if (def->watchdog)
        virDomainWatchdogDefFormat(buf, def->watchdog, flags);

    if (def->memballoon)
        virDomainMemballoonDefFormat(buf, def->memballoon, flags);

    for (n = 0; n < def->nrngs; n++) {
        if (virDomainRNGDefFormat(buf, def->rngs[n], flags))
            goto error;
    }

    if (def->nvram)
        virDomainNVRAMDefFormat(buf, def->nvram, flags);

    for (n = 0; n < def->npanics; n++) {
        if (virDomainPanicDefFormat(buf, def->panics[n]) < 0)
            goto error;
    }

    for (n = 0; n < def->nshmems; n++) {
        if (virDomainShmemDefFormat(buf, def->shmems[n], flags) < 0)
            goto error;
    }

    for (n = 0; n < def->nmems; n++) {
        if (virDomainMemoryDefFormat(buf, def->mems[n], def, flags) < 0)
            goto error;
    }

    if (def->iommu &&
        virDomainIOMMUDefFormat(buf, def->iommu) < 0)
        goto error;

    if (def->vsock &&
        virDomainVsockDefFormat(buf, def->vsock) < 0)
        goto error;

    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</devices>\n");

    for (n = 0; n < def->nseclabels; n++)
        virSecurityLabelDefFormat(buf, def->seclabels[n], flags);

    if (def->namespaceData && def->ns.format) {
        if ((def->ns.format)(buf, def->namespaceData) < 0)
            goto error;
    }

    if (def->keywrap)
        virDomainKeyWrapDefFormat(buf, def->keywrap);

    virDomainSEVDefFormat(buf, def->sev);

    virBufferAdjustIndent(buf, -2);
    virBufferAsprintf(buf, "</%s>\n", rootname);

    return 0;

 error:
    virBufferFreeAndReset(buf);
    return -1;
}