/*******************************************************************************
 * Copyright (C) 2006 ???.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
 *
 *******************************************************************************/

/*
 * TODO:
 * 1. Flush out errors returned by the various functions.
 * 2. Resolve how to handle xm_set_domain(), xm_delete(), and xm_create().
 *    No equivalents in libvirt at this time.  Ongoing discussions on if/how
 *    they will be implemented in libvirt.
 * 3. Memory issues??  Particularly when creating/populating xm_config
 *    structure from xml.
 *
 */

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <libxml/xpath.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libvirt/libvirt.h>

#include "xm.h"


struct xm
{
   virConnectPtr libvirtHandle;
};


/**
 * NOTICE:
 * Using XML buffer and supporting routines from libvirt.
 */
typedef struct _xml_buffer
{
   char *content;          /* The buffer content UTF8 */
   unsigned int use;       /* The buffer size used */
   unsigned int size;      /* The buffer size */
} xml_buffer;


static int
xml_buffer_grow(xml_buffer *buf, unsigned int len)
{
   int size;
   char *newbuf;

   if (buf == NULL)
      return (-1);
   if (len + buf->use < buf->size)
      return (0);

   size = buf->use + len + 1000;

   newbuf = (char *) realloc(buf->content, size);
   if (newbuf == NULL)
      return (-1);

   buf->content = newbuf;
   buf->size = size;
   return (buf->size - buf->use);
}


static int
xml_buffer_add(xml_buffer *buf, const char *str, int len)
{
   unsigned int needSize;

   if ((str == NULL) || (buf == NULL))
      return -1;

   if (len == 0)
      return 0;

   if (len < 0)
      len = strlen(str);

   needSize = buf->use + len + 2;
   if (needSize > buf->size)
   {
      if (!xml_buffer_grow(buf, needSize))
         return (-1);
   }

   memmove(&buf->content[buf->use], str, len);
   buf->use += len;
   buf->content[buf->use] = 0;
   return (0);
}


static int
xml_buffer_VSprintf(xml_buffer *buf, const char *format, ...)
{
   int size, count;
   va_list locarg, argptr;

   if ((format == NULL) || (buf == NULL))
      return (-1);

   size = buf->size - buf->use - 1;
   va_start(argptr, format);
   va_copy(locarg, argptr);
   while (((count = vsnprintf(&buf->content[buf->use], size, format,
                              locarg)) < 0) || (count >= size - 1))
   {
      buf->content[buf->use] = 0;
      va_end(locarg);
      if (xml_buffer_grow(buf, 1000) < 0)
         return (-1);

      size = buf->size - buf->use - 1;
      va_copy(locarg, argptr);
   }
   va_end(locarg);
   buf->use += count;
   buf->content[buf->use] = 0;
   return (0);
}


static int
vifxml2config(xmlNodePtr node, struct xm_config *config)
{
    xmlNodePtr cur;
    xmlChar *type = NULL;
    xmlChar *source = NULL;
    xmlChar *mac = NULL;
    xmlChar *script = NULL;
    int typ = 0;
    char params[1024];
    struct xm_config_vif *vif;
    size_t param_len;

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL)
    {
        if (xmlStrEqual(type, BAD_CAST "bridge"))
            typ = 0;
        else if (xmlStrEqual(type, BAD_CAST "ethernet"))
            typ = 1;
        xmlFree(type);
    }
    cur = node->children;
    while (cur != NULL)
    {
        if (cur->type == XML_ELEMENT_NODE)
        {
            if ((source == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "source")))
            {
                if (typ == 0)
                    source = xmlGetProp(cur, BAD_CAST "bridge");
                else
                    source = xmlGetProp(cur, BAD_CAST "dev");
            }
            else if ((mac == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "mac")))
            {
                mac = xmlGetProp(cur, BAD_CAST "address");
            }
            else if ((script == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "script")))
            {
                script = xmlGetProp(cur, BAD_CAST "path");
            }
        }
        cur = cur->next;
    }

    params[0] = '\0';
    if (source)
    {
       strcat(params, "bridge=");
       strcat(params, source);
       strcat(params, ",");
    }
    if (mac)
    {
       strcat(params, "mac=");
       strcat(params, mac);
       strcat(params, ",");
    }
    if (script)
    {
       strcat(params, "script=");
       strcat(params, script);
       strcat(params, ",");
    }

    if ((param_len = strlen(params)) > 0)
    {
       if (params[param_len - 1] == ',')
          params[param_len - 1] = '\0';
       
       vif = realloc(config->vif, (config->n_vif + 1) * sizeof(struct xm_config_vif));
       if (vif)
       {
          config->vif = vif;
          config->vif[config->n_vif].params = strdup(params);
          config->n_vif++;
       }
    }
    
    return 0;
}


static int
vbdxml2config(xmlNodePtr node, struct xm_config *config)
{
    xmlNodePtr cur;
    xmlChar *type = NULL;
    xmlChar *source = NULL;
    xmlChar *target = NULL;
    int ro = 0;
    int typ = 0;
    struct xm_config_vbd *vbd;

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL)
    {
        if (xmlStrEqual(type, BAD_CAST "file"))
            typ = 0;
        else if (xmlStrEqual(type, BAD_CAST "block"))
            typ = 1;
        xmlFree(type);
    }

    cur = node->children;
    while (cur != NULL)
    {
        if (cur->type == XML_ELEMENT_NODE)
        {
            if ((source == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "source")))
            {
               if (typ == 0)
                    source = xmlGetProp(cur, BAD_CAST "file");
                else
                    source = xmlGetProp(cur, BAD_CAST "dev");
            }
            else if ((target == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "target")))
            {
                target = xmlGetProp(cur, BAD_CAST "dev");
            }
            else if (xmlStrEqual(cur->name, BAD_CAST "readonly"))
            {
                ro = 1;
            }
        }
        cur = cur->next;
    }

    if (source && target)
    {
       vbd = realloc(config->vbd, (config->n_vbd + 1) * sizeof(struct xm_config_vbd));
       if (vbd)
       {
          char uname[1024];
          
          uname[0] = '\0';
          config->vbd = vbd;
          snprintf(uname, 1024, "%s:%s", type == 0 ? "file" : "phy", source);
          config->vbd[config->n_vbd].uname = strdup(uname);
          config->vbd[config->n_vbd].dev = strdup(target);
          config->vbd[config->n_vbd].mode = ro ? strdup("r") : strdup("w");
          config->n_vbd++;
       }
    }
}


static int
osxml2config(xmlNodePtr node, struct xm_config *config)
{
   xmlNodePtr cur, txt;

   cur = node->children;
   while (cur != NULL)
   {
      if (cur->type == XML_ELEMENT_NODE)
      {
         if ((config->kernel == NULL) &&
                  (xmlStrEqual(cur->name, BAD_CAST "kernel")))
         {
            txt = cur->children;
            if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
               config->kernel = strdup((const char *)txt->content);
         }
         else if ((config->root == NULL) &&
                  (xmlStrEqual(cur->name, BAD_CAST "root")))
         {
            txt = cur->children;
            if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
               config->root = strdup((const char *)txt->content);
         }
         else if ((config->ramdisk == NULL) &&
                  (xmlStrEqual(cur->name, BAD_CAST "initrd")))
         {
            txt = cur->children;
            if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
               config->ramdisk = strdup((const char *)txt->content);
         }
         else if ((config->cmdline == NULL) &&
                  (xmlStrEqual(cur->name, BAD_CAST "cmdline")))
         {
            txt = cur->children;
            if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
               config->cmdline = strdup((const char *)txt->content);
         }
      }
      cur = cur->next;
   }
    
   return (0);
}


static struct xm_config*
xml2config(virDomainPtr domainPtr, const char *xmldesc, virDomainInfoPtr dynamicInfo)
{
   struct xm_config *config;
   xmlDocPtr xml;
   struct xm_dynamic_info *dynInfo;
   xmlNodePtr node;
   xmlChar *prop;
   xmlXPathObjectPtr obj = NULL;
   xmlXPathContextPtr ctxt = NULL;
   int i;
   
   config = calloc(1, sizeof(struct xm_config));
   if (config == NULL)
      return NULL;
   
   xml = xmlReadDoc((const xmlChar *)xmldesc, "domain.xml", NULL,
                    XML_PARSE_NOENT | XML_PARSE_NONET |
                    XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
   if (xml == NULL)
   {
      free(config);
      return NULL;
   }
   node = xmlDocGetRootElement(xml);
   if ((node == NULL) || (!xmlStrEqual(node->name, BAD_CAST "domain")))
      goto error;
   
   prop = xmlGetProp(node, BAD_CAST "type");
   if (prop != NULL) {
      if (!xmlStrEqual(prop, BAD_CAST "xen")) {
         xmlFree(prop);
         goto error;
      }
      xmlFree(prop);
   }
   
   ctxt = xmlXPathNewContext(xml);
   if (ctxt == NULL)
   {
      goto error;
   }

   obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt);
   if ((obj == NULL) || (obj->type != XPATH_STRING) ||
       (obj->stringval == NULL) || (obj->stringval[0] == 0))
   {
      goto error;
   }
   config->name = strdup((const char *)obj->stringval);
   xmlXPathFreeObject(obj);

   obj = xmlXPathEval(BAD_CAST "number(/domain/memory[1])", ctxt);
   if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
       (obj->floatval < 64000))
      config->memory = (memory_t)64000000;
   else
      config->memory = (memory_t)(obj->floatval * 1024);
   xmlXPathFreeObject(obj);

   obj = xmlXPathEval(BAD_CAST "number(/domain/vcpu[1])", ctxt);
   if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
       (obj->floatval <= 0))
      config->vcpus = 1;
   else
      config->vcpus = (int) obj->floatval;
   xmlXPathFreeObject(obj);

   obj = xmlXPathEval(BAD_CAST "/domain/os[1]", ctxt);
   if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
       (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr != 1))
   {
      goto error;
   }
   osxml2config(obj->nodesetval->nodeTab[0], config);
   xmlXPathFreeObject(obj);

   obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
   if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
       (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr < 1))
   {
      goto error;
   }
   for (i = 0; i < obj->nodesetval->nodeNr; i++)
   {
      vbdxml2config(obj->nodesetval->nodeTab[i], config);
   }
   xmlXPathFreeObject(obj);

   obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
   if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
       (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0))
   {
      for (i = 0; i < obj->nodesetval->nodeNr; i++)
      {
         vifxml2config(obj->nodesetval->nodeTab[i], config);
      }
   }
   xmlXPathFreeObject(obj);

   //
   // Done with xml.
   //
   xmlXPathFreeContext(ctxt);
   xmlFreeDoc(xml);

   //
   // Now dynamic info, if any.
   //
   if (dynamicInfo == NULL)
      return config;

   dynInfo = calloc(1, sizeof(struct xm_dynamic_info));
   if (dynInfo == NULL)
      return config;

   config->info = dynInfo;

   dynInfo->id = virDomainGetID(domainPtr);
   // FIXME
   //dynInfo->ssidref = ?
   dynInfo->memory.current = dynamicInfo->memory;
   dynInfo->memory.maximum = dynamicInfo->maxMem;
   // FIXME
   //dynInfo->cpu = ?
   dynInfo->vcpus = dynamicInfo->nrVirtCpu;
   switch (dynamicInfo->state)
   {
      case VIR_DOMAIN_RUNNING:
         dynInfo->state.running = true;
         break;
      case VIR_DOMAIN_BLOCKED:
         dynInfo->state.blocked = true;
         break;
      case VIR_DOMAIN_PAUSED:
         dynInfo->state.paused = true;
         break;
      case VIR_DOMAIN_SHUTDOWN:
         dynInfo->state.dying = true;
         break;
      case VIR_DOMAIN_SHUTOFF:
         dynInfo->state.shutdown = true;
         break;
      case VIR_DOMAIN_CRASHED:
         dynInfo->state.crashed = true;
         break;
   }
   dynInfo->up_time = (double)dynamicInfo->cpuTime;

   return config;

 error:
   /*
   if (config->name) free(config->name);
   if (config->kernel) free(config->kernel);
   if (config->ramdisk) free(config->ramdisk);
   if (config->root) free(config->root);
   if (config->cmdline) free(config->cmdline);
   if (config->vif) free(config->vif);
   if (config->vbd) free(config->vbd);
   if (config->info) free(config->info);
   */
   free(config);
   
   if (obj)
      xmlXPathFreeObject(obj);

   if (ctxt)
      xmlXPathFreeContext(ctxt);

   xmlFreeDoc(xml);

   return NULL;
}


static char*
read_xml_config(const struct xm *xm, const char *name)
{
   virDomainPtr domainPtr;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return NULL;
   
   return virDomainGetXMLDesc(domainPtr, 0);
}


static int write_xml_config(struct xm *xm, const char *xml)
{
   //
   // Need a libvirt entry point to write config to xenstore
   //
   assert(0);
   return 0;
}


static int
vif2xml(xml_buffer *buf, struct xm_config *config)
{
   int i;
   char *tempStr;
   const char *token;
   const char delim[] = ",=";

   //
   // Write out all of the network devices.
   //
   for (i = 0; i < config->n_vif; i++)
   {
      if (config->vif[i].params)
      {
         //
         // Start an element named "interface" as child of "devices".
         //
         xml_buffer_VSprintf(buf, "    <interface type='bridge'>\n");

         tempStr = strdup(config->vif[i].params);
         if (tempStr == NULL)
            continue;

         token = strtok(tempStr, delim);
         while (token)
         {
            if (strcmp(token, "mac") == 0)
            {
               token = strtok(NULL, delim);
               xml_buffer_VSprintf(buf, "      <mac address='%s'/>\n", token);
            }
            else if (strcmp(token, "bridge") == 0)
            {
               token = strtok(NULL, delim);
               xml_buffer_VSprintf(buf, "      <source bridge='%s'/>\n", token);
            }
            else if (strcmp(token, "ip") == 0)
            {
               token = strtok(NULL, delim);
               xml_buffer_VSprintf(buf, "      <ip address='%s'/>\n", token);
            }
            else if (strcmp(token, "script") == 0)
            {
               token = strtok(NULL, delim);
               xml_buffer_VSprintf(buf, "      <script path='%s'/>\n", token);
            }
            else token = strtok(NULL, delim);
         }
         xml_buffer_add(buf, "    </interface>\n", 17);
         free(tempStr);
      }
   }
   
   return 0;
}


static int
vbd2xml(xml_buffer *buf, struct xm_config *config)
{
   int i;
   char *tempStr;
   
   for (i = 0; i < config->n_vbd; i++)
   {
      //
      // What is the type?
      //
      tempStr = strchr(config->vbd[i].uname, ':');
      if (tempStr == NULL)
         continue;
      
      tempStr++;
      if (strncasecmp(config->vbd[i].uname, "file", 4) == 0)
      {
         xml_buffer_add(buf, "    <disk type='file'>\n", 23);
         xml_buffer_VSprintf(buf, "      <source file='%s'/>\n", tempStr);
      }
      else
      {
         xml_buffer_add(buf, "    <disk type='block'>\n", 24);
         xml_buffer_VSprintf(buf, "      <source dev='%s'/>\n", tempStr);
      }
      
      xml_buffer_VSprintf(buf, "      <target dev='%s'/>\n", config->vbd[i].dev);
      

      //
      // Write "readonly" element if device is marked readonly
      //
      if (!strcasecmp(config->vbd[i].mode, "r"))
         xml_buffer_add(buf, "      <readonly/>\n", 18);
      //
      // Terminate "disk" element
      //
      xml_buffer_add(buf, "    </disk>\n", 12);
   }

   return 0;
}


static char*
config2xml(struct xm_config *config)
{
   xml_buffer buf;

   if (config == NULL || config->name == NULL || config->kernel == NULL)
      return NULL;
   
   buf.content = calloc(1, 1000);
   if (buf.content == NULL)
      return NULL;
   
   buf.size = 1000;
   buf.use = 0;
   
   xml_buffer_add(&buf, "<domain type='xen'>\n", 20);
   
   xml_buffer_VSprintf(&buf, "  <name>%s</name>\n", config->name);
   xml_buffer_add(&buf, "  <os>\n", 7);
   xml_buffer_add(&buf, "    <type>linux<type>\n", 22);  // FIXME
   xml_buffer_VSprintf(&buf, "    <kernel>%s</kernel>\n", config->kernel);

   if (config->ramdisk)
      xml_buffer_VSprintf(&buf, "    <initrd>%s</initrd>\n", config->ramdisk);
   if (config->root)
      xml_buffer_VSprintf(&buf, "    <root>%s</root>\n", config->root);
   if (config->cmdline)
      xml_buffer_VSprintf(&buf, "    <cmdline>%s</cmdline>\n", config->cmdline);

   xml_buffer_add(&buf, "  </os>\n", 8);

   xml_buffer_VSprintf(&buf, "  <memory>%</memory>\n", (int)(config->memory >> 10));
   xml_buffer_VSprintf(&buf, "  <vcpu>%d</vcpu>\n", config->vcpus);
   xml_buffer_VSprintf(&buf, "  <name>%s</name>\n", config->name);

   xml_buffer_add(&buf, "  <devices>\n", 12);
   
   vbd2xml(&buf, config);
   vif2xml(&buf, config);
   
   xml_buffer_add(&buf, "  </devices>\n", 13);
   xml_buffer_add(&buf, "</domain>\n", 10);
   
   buf.content[buf.use] = 0;
   return buf.content;
   
 Error:
   free(buf.content);
   return NULL;
}


struct xm *xm_init(void)
{
   virConnectPtr libvirtPtr;
   struct xm *ret;
   
   if ((libvirtPtr = virConnectOpen(NULL)) == NULL)
      return NULL;

   ret = (struct xm *)malloc(sizeof(struct xm));
   if (ret == NULL)
   {
      virConnectClose(libvirtPtr);
      return NULL;
   }
   ret->libvirtHandle = libvirtPtr;

   //
   // Initialize libxml and check for ABI mismatches between version used
   // when building and runtime version.
   //
   LIBXML_TEST_VERSION;
   
   return ret;
}


void xm_close(struct xm *xm)
{
   if (xm == NULL)
      return;

   if (xm->libvirtHandle == NULL)
   {
      free(xm);
      return;
   }
   
   virConnectClose(xm->libvirtHandle);
   free(xm);

   //
   // WARNING!
   // Any cleanup for libxml?
   //
}


/* attach to console of DomId */
int xm_console(struct xm *xm, const char *name)
{
   //
   // WARNING!
   // Currently no function in libvirt for attaching to consoles.
   // Do we need this?
   // Return failure for now.
   //

   return -1;
}


/* sets a domain's configuration */
int xm_set_domain(struct xm *xm, struct xm_config *config)
{
   char *xml;
   int ccode = -1;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;

   xml = config2xml(config);
   if (xml == NULL)
      return ccode;
   
   ccode = write_xml_config(xm, xml);
   free(xml);
   return ccode;
}


/* create a previously configured domain */
int xm_create(struct xm *xm, const char *name)
{
   virDomainPtr domainPtr;
   char *xml;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return -1;

   //
   // How to handle this?  If the config has been set in xenstore
   // we should just be able to launch the domain.  For now I guess
   // we can just read the config from libvirt and send it on to
   // create.
   //
   xml = read_xml_config(xm, name);
   if (xml == NULL)
      return -1;
   
   domainPtr = virDomainCreateLinux(xm->libvirtHandle, xml, VIR_DOMAIN_NONE);
   free(xml);
   if (domainPtr == NULL)
      return -1;
   
   //
   // What to do with the domain pointer returned by libvirt??
   // For now we'll free it and recreate when needed :-/
   //
   virDomainFree(domainPtr);
   return 0;
}

   
/* terminate a domain immediately */
int xm_destroy(struct xm *xm, const char *name)
{
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;

   ccode = virDomainDestroy(domainPtr);
   virDomainFree(domainPtr);
   return ccode;
}

      
/* remove the configuration information for the domain */
int xm_delete(struct xm *xm, const char *name)
{
   if (xm == NULL || xm->libvirtHandle == NULL)
      return -1;

   //
   // WARNING!
   // What is the equivalent of this in libvirt?
   //
   assert(0);
}


/* list information about a domain -- returns NULL on error.
   result should be free()'d by caller. */
struct xm_config *xm_get_domain(struct xm *xm, const char *name)
{
   virDomainPtr domainPtr;
   virDomainInfo domainInfo;
   virDomainInfoPtr domainInfoPtr = NULL;
   char *xml;
   struct xm_config *ret;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return NULL;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return NULL;
   
   if ((xml = virDomainGetXMLDesc(domainPtr, 0)) == NULL)
   {
      virDomainFree(domainPtr);
      return NULL;
   }

   memset(&domainInfo, 0, sizeof(virDomainInfo));
   if (!virDomainGetInfo(domainPtr, &domainInfo))
      domainInfoPtr = &domainInfo;

   ret = xml2config(domainPtr, xml, domainInfoPtr);
   virDomainFree(domainPtr);
   return ret;
}
 

/* enumerate domains -- returns an array of domain names of num size. result
   should be free()'d by caller. */
char **xm_enum_domains(struct xm *xm, unsigned int *num)
{
   int domIds[32];
   int numDomains;
   int i;
   virDomainPtr domainPtr;
   const char *tempName;
   int memSize = 0;
   int numDomainNames = 0;
   char **names;
   char *offsetPtr;
   
   //
   // Hmm... Kind of ugly and not very efficient.  Currently returning
   // an array of pointers and what they point to all in one block of
   // memory.  Necessary since we are not keeping around the domain
   // objects.
   //
   if (xm == NULL || xm->libvirtHandle == NULL)
   {
      *num = 0;
      return NULL;
   }
   
   numDomains = virConnectListDomains(xm->libvirtHandle, domIds, 32);
   if (numDomains <= 0)
   {
      *num = 0;
      return NULL;
   }
   
   //
   // Calculate the amount of memory required to hold all domain names
   // and their pointers.
   //
   for (i = 0; i < numDomains; i++)
   {
      domainPtr = virDomainLookupByID(xm->libvirtHandle, domIds[i]);
      if (domainPtr == NULL)
         continue;
      
      tempName = virDomainGetName(domainPtr);
      if (tempName == NULL)
      {
         virDomainFree(domainPtr);
         continue;
      }
      
      memSize += strlen(tempName) + 1;
      numDomainNames++;
      virDomainFree(domainPtr);
   }

   //
   // Did we actually find some names?
   //
   if (numDomainNames == 0)
   {
      *num = 0;
      return NULL;
   }

   memSize += (numDomainNames + 1) * sizeof(char *);
   names = calloc(1, memSize);
   if (names == NULL)
   {
      *num = 0;
      return NULL;
   }

   //
   // Copy domain names into buffer and pointers to them.
   //
   offsetPtr = (char*)names + ((numDomainNames + 1) * sizeof(char *));
   for (i = 0; i < numDomains; i++)
   {
      domainPtr = virDomainLookupByID(xm->libvirtHandle, domIds[i]);
      if (domainPtr == NULL)
         continue;
      
      tempName = virDomainGetName(domainPtr);
      if (tempName == NULL)
      {
         virDomainFree(domainPtr);
         continue;
      }
   
      strcpy(offsetPtr, tempName);
      names[i] = offsetPtr;

      offsetPtr += strlen(tempName) + 1;
      virDomainFree(domainPtr);
   }
   names[i] = NULL;
   
   *num = numDomainNames;
   return (char **)names;
}


/* set the maximum memory reservation for a domain */
int xm_mem_max(struct xm *xm, const char *name, memory_t memory)
{
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;
   
   ccode = virDomainSetMaxMemory(domainPtr, (unsigned long)memory);
   virDomainFree(domainPtr);
   return ccode;
}


/* adjust the current memory usage for a domain */
int xm_mem_set(struct xm *xm, const char *name, memory_t memory)
{
   //
   // WARNING!
   // Currently no function in libvirt to handle setting current memory.
   // Return failure for now.
   //
#if 0   
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;
   
   ccode = virDomainSetCurrentMemory(domainPtr, (unsigned long)memory);
   virDomainFree(domainPtr);
   return ccode;
#endif
   return -1;
}


/* migrate a domain to another machine */
int xm_migrate(struct xm *xm, const char *name, const char *host)
{
   //
   // WARNING!
   // Currently no function in libvirt for migration.
   // Return failure for now.
   //
#if 0
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;

   ccode = virDomainMigrate(domainPtr, host);
   virDomainFree(domainPtr);
   return ccode;
#endif
   return -1;
}


/* pause execution of a domain */
int xm_pause(struct xm *xm, const char *name)
{
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;
   
   ccode = virDomainSuspend(domainPtr);
   virDomainFree(domainPtr);
   return ccode;
}


/* reboot a domain */
int xm_reboot(struct xm *xm, const char *name)
{
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;
   
   ccode = virDomainReboot(domainPtr, 0);
   virDomainFree(domainPtr);
   return ccode;
}


/* create a domain from a saved state file */
int xm_restore(struct xm *xm, const char *filename)
{
   if (xm == NULL || xm->libvirtHandle == NULL)
      return -1;
   
   return virDomainRestore(xm->libvirtHandle, filename);
}


/* save domain state (and config) to file */
int xm_save(struct xm *xm, const char *name, const char *filename)
{
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;

   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;
   
   ccode = virDomainSave(domainPtr, filename);
   virDomainFree(domainPtr);
   return ccode;
}


/* shutdown a domain */
int xm_shutdown(struct xm *xm, const char *name)
{
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;
   
   ccode = virDomainShutdown(domainPtr);
   virDomainFree(domainPtr);
   return ccode;
}


/* unpause a paused domain */
int xm_unpause(struct xm *xm, const char *name)
{
   int ccode = -1;
   virDomainPtr domainPtr;
   
   if (xm == NULL || xm->libvirtHandle == NULL)
      return ccode;
   
   domainPtr = virDomainLookupByName(xm->libvirtHandle, name);
   if (domainPtr == NULL)
      return ccode;
   
   ccode = virDomainResume(domainPtr);
   virDomainFree(domainPtr);
   return ccode;
}
