Index: src/conf.c =================================================================== RCS file: /data/cvs/libvirt/src/conf.c,v retrieving revision 1.16 diff -u -p -r1.16 conf.c --- src/conf.c 7 Jan 2008 15:21:33 -0000 1.16 +++ src/conf.c 8 Jan 2008 01:10:52 -0000 @@ -57,27 +57,6 @@ struct _virConfParserCtxt { (((c) >= 'A') && ((c) <= 'Z'))) #define IS_DIGIT(c) (((c) >= '0') && ((c) <= '9')) -/************************************************************************ - * * - * Structures used by configuration data * - * * - ************************************************************************/ - -typedef struct _virConfEntry virConfEntry; -typedef virConfEntry *virConfEntryPtr; - -struct _virConfEntry { - virConfEntryPtr next; - char* name; - char* comment; - virConfValuePtr value; -}; - -struct _virConf { - const char* filename; - virConfEntryPtr entries; -}; - /** * virConfError: * @conf: the configuration if available Index: src/conf.h =================================================================== RCS file: /data/cvs/libvirt/src/conf.h,v retrieving revision 1.4 diff -u -p -r1.4 conf.h --- src/conf.h 30 Nov 2007 15:43:42 -0000 1.4 +++ src/conf.h 8 Jan 2008 01:10:52 -0000 @@ -59,12 +59,31 @@ struct _virConfValue { }; /** + * virConfEntryPtr: + * used by configuration data + */ +typedef struct _virConfEntry virConfEntry; +typedef virConfEntry *virConfEntryPtr; + +struct _virConfEntry { + virConfEntryPtr next; + char* name; + char* comment; + virConfValuePtr value; +}; + +/** * virConfPtr: * a pointer to a parsed configuration file */ typedef struct _virConf virConf; typedef virConf *virConfPtr; +struct _virConf { + const char* filename; + virConfEntryPtr entries; +}; + virConfPtr __virConfNew (void); virConfPtr __virConfReadFile (const char *filename); virConfPtr __virConfReadMem (const char *memory, Index: src/xm_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/xm_internal.c,v retrieving revision 1.53 diff -u -p -r1.53 xm_internal.c --- src/xm_internal.c 14 Dec 2007 15:51:42 -0000 1.53 +++ src/xm_internal.c 8 Jan 2008 01:10:53 -0000 @@ -72,6 +72,12 @@ static virHashTablePtr nameConfigMap = N static int nconnections = 0; static time_t lastRefresh = 0; +char * xenXMAutoAssignMac(void); +static int xenXMAttachDisk(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm, + xmlNodePtr node, xenXMConfCachePtr entry); +static int xenXMAttachInterface(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm, + xmlNodePtr node, xenXMConfCachePtr entry); + #define XM_REFRESH_INTERVAL 10 #define XM_CONFIG_DIR "/etc/xen" @@ -79,6 +85,7 @@ static time_t lastRefresh = 0; #define XEND_CONFIG_FILE "xend-config.sxp" #define XEND_PCI_CONFIG_PREFIX "xend-pci-" #define QEMU_IF_SCRIPT "qemu-ifup" +#define XM_XML_ERROR "Invalid xml" struct xenUnifiedDriver xenXMDriver = { xenXMOpen, /* open */ @@ -113,8 +120,8 @@ struct xenUnifiedDriver xenXMDriver = { xenXMDomainCreate, /* domainCreate */ xenXMDomainDefineXML, /* domainDefineXML */ xenXMDomainUndefine, /* domainUndefine */ - NULL, /* domainAttachDevice */ - NULL, /* domainDetachDevice */ + xenXMDomainAttachDevice, /* domainAttachDevice */ + xenXMDomainDetachDevice, /* domainDetachDevice */ NULL, /* domainGetAutostart */ NULL, /* domainSetAutostart */ NULL, /* domainGetSchedulerType */ @@ -2521,6 +2528,672 @@ int xenXMNumOfDefinedDomains(virConnectP return virHashSize(nameConfigMap); } +/** + * xenXMDomainAttachDevice: + * @domain: pointer to domain object + * @xml: pointer to XML description of device + * + * Create a virtual device attachment to backend. + * XML description is translated into config file. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xenXMDomainAttachDevice(virDomainPtr domain, const char *xml) { + const char *filename = NULL; + xenXMConfCachePtr entry = NULL; + xmlDocPtr doc = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + char *domxml = NULL; + int ret = -1, hvm = 0; + + if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + goto cleanup; + } + if (domain->conn->flags & VIR_CONNECT_RO) + goto cleanup; + if (domain->id != -1) + goto cleanup; + if (!(filename = virHashLookup(nameConfigMap, domain->name))) + goto cleanup; + if (!(entry = virHashLookup(configCache, filename))) + goto cleanup; + if (!(entry->conf) || !(entry->conf->entries)) + goto cleanup; + + if (!(domxml = xenXMDomainDumpXML(domain, 0))) + goto cleanup; + + doc = xmlReadDoc((const xmlChar *) domxml, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING); + if (!doc) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, "cannot read XML domain definition"); + goto cleanup; + } + if (!(ctxt = xmlXPathNewContext(doc))) { + xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR, "cannot create XPath context"); + goto cleanup; + } + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type)", ctxt); + if (obj && (obj->type == XPATH_STRING) && + (obj->stringval) && !strcmp((char*)obj->stringval, "hvm")) + hvm = 1; + + if (ctxt) + xmlXPathFreeContext(ctxt); + if (doc) + xmlFreeDoc(doc); + doc = xmlReadDoc((const xmlChar *) xml, "device.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING); + if (!doc) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, "cannot read XML domain definition"); + goto cleanup; + } + if (!(ctxt = xmlXPathNewContext(doc))) { + xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR, "cannot create XPath context"); + goto cleanup; + } + + if ((node = virXPathNode("/disk", ctxt))) { + if (xenXMAttachDisk(domain, ctxt, hvm, node, entry)) + goto cleanup; + } else if ((node = virXPathNode("/interface", ctxt))) { + if (xenXMAttachInterface(domain, ctxt, hvm, node, entry)) + goto cleanup; + } else { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, "unknown device"); + goto cleanup; + } + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (virConfWriteFile(entry->filename, entry->conf) < 0) + goto cleanup; + + ret = 0; + + cleanup: + if (domxml) + free(domxml); + if (obj) + xmlXPathFreeObject(obj); + if (ctxt) + xmlXPathFreeContext(ctxt); + if (doc) + xmlFreeDoc(doc); + + return ret; +} + +static int +xenXMAttachDisk(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm, + xmlNodePtr node, xenXMConfCachePtr entry) { + virConfValuePtr list_item = NULL, list_val = NULL, prev = NULL; + virConfEntryPtr cur = NULL; + xenUnifiedPrivatePtr priv = NULL; + xmlChar *type = NULL, *source = NULL, *target = NULL; + int ret = -1; + char *dev; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + xenXMParseXMLDisk(node, hvm, ((xenUnifiedPrivatePtr) domain->conn->privateData)->xendConfigVersion, &dev); + if (!dev) + goto cleanup; + + if (!(type = xmlGetProp(node, BAD_CAST "type"))) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR); + goto cleanup; + } + if (!(node = virXPathNode("/disk/source", ctxt))) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR); + goto cleanup; + } + if (!strcmp((const char *) type, "block")) + source = xmlGetProp(node, BAD_CAST "dev"); + else if (!strcmp((const char *) type, "file")) + source = xmlGetProp(node, BAD_CAST "file"); + else { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR); + goto cleanup; + } + if (!(node = virXPathNode("/disk/target", ctxt))) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR); + goto cleanup; + } + target = xmlGetProp(node, BAD_CAST "dev"); + + list_item = virConfGetValue(entry->conf, "disk"); + if (list_item && list_item->type == VIR_CONF_LIST) { + prev = list_item; + list_val = list_item->list; + while (list_val) { + if ((list_val->type != VIR_CONF_STRING) || (!list_val->str)) + goto skip; + char domdev[NAME_MAX]; + char *head; + char *offset; + char *tmp; + + head = list_val->str; + + /* Extract the source */ + if (!(offset = strchr(head, ',')) || offset[0] == '\0') + goto skip; + if ((offset - head) >= (PATH_MAX-1)) + goto skip; + head = offset + 1; + + /* Extract the dest */ + if (!(offset = strchr(head, ',')) || offset[0] == '\0') + goto skip; + if ((offset - head) >= (PATH_MAX-1)) + goto skip; + strncpy(domdev, head, (offset - head)); + domdev[(offset-head)] = '\0'; + head = offset + 1; + + /* Remove legacy ioemu: junk */ + if (!strncmp(domdev, "ioemu:", 6)) { + memmove(domdev, domdev+6, strlen(domdev)-5); + } + + /* Check for a :cdrom/:disk postfix */ + if ((tmp = strchr(domdev, ':'))) + tmp[0] = '\0'; + + if (!(strcmp(domdev, (const char *) target))) + break; + skip: + prev = list_val; + list_val = list_val->next; + } + } else if (!list_item) { + virConfEntryPtr prev_entry; + + cur = entry->conf->entries; + while (cur->next) + cur = cur->next; + prev_entry = cur; + if (!(cur = calloc(1, sizeof(virConfEntry)))) + goto cleanup; + if(!(cur->name = calloc(1, strlen("disk") + 1))) + goto error; + strcpy(cur->name, "disk"); + cur->next = NULL; + cur->comment = NULL; + prev_entry->next = cur; + + if (!(list_item = calloc(1, sizeof(virConfValue)))) { + free(cur->name); + goto error; + } + list_item->type = VIR_CONF_LIST; + list_item->list = NULL; + list_item->str = NULL; + list_item->next = NULL; + + cur->value = list_item; + list_val = NULL; + prev = cur->value; + } else + goto cleanup; + + if (!list_val) { + /* insert */ + if (!(list_val = malloc(sizeof(virConfValue)))) + goto cleanup; + list_val->type = VIR_CONF_STRING; + list_val->next = NULL; + list_val->str = dev; + if (prev->type == VIR_CONF_LIST) + prev->list = list_val; + else + prev->next = list_val; + } else { + /* configure */ + if (list_val->str) + free(list_val->str); + list_val->str = dev; + } + + ret = 0; + goto cleanup; + + error: + if (cur) + free(cur); + cleanup: + if (type) + free(type); + if (source) + free(source); + if (target) + free(target); + + return (ret); +} + +static int +xenXMAttachInterface(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm, + xmlNodePtr node, xenXMConfCachePtr entry) { + virConfValuePtr list_item = NULL, list_val = NULL, prev = NULL; + virConfEntryPtr cur = NULL; + xmlChar *type = NULL, *source = NULL, *mac = NULL; + int ret = -1, autoassign = 0; + char *dev; + + xmlNodePtr node_cur = NULL, node_tmp = NULL; + xmlAttrPtr attr_node = NULL; + xmlNodePtr text_node = NULL; + + if(!(type = xmlGetProp(node, BAD_CAST "type"))) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR); + goto cleanup; + } + + if (!(node = virXPathNode("/interface/source", ctxt))) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR); + goto cleanup; + } + source = xmlGetProp(node, BAD_CAST type); + + if (!(node = virXPathNode("/interface/mac", ctxt))) { + if (!(mac = (xmlChar *)xenXMAutoAssignMac())) + goto cleanup; + autoassign = 1; + } else + mac = xmlGetProp(node, BAD_CAST "address"); + + list_item = virConfGetValue(entry->conf, "vif"); + if (list_item && list_item->type == VIR_CONF_LIST) { + prev = list_item; + list_val = list_item->list; + while (list_val) { + if ((list_val->type != VIR_CONF_STRING) || (!list_val->str)) + goto skip; + char dommac[18]; + char *key; + + dommac[0] = '\0'; + + key = list_val->str; + while (key) { + char *data; + char *nextkey = strchr(key, ','); + + if (!(data = strchr(key, '=')) || (data[0] == '\0')) + goto skip; + data++; + + if (!strncmp(key, "mac=", 4)) { + int len = nextkey ? (nextkey - data) : 17; + if (len > 17) + len = 17; + strncpy(dommac, data, len); + dommac[len] = '\0'; + } + + while (nextkey && (nextkey[0] == ',' || + nextkey[0] == ' ' || + nextkey[0] == '\t')) + nextkey++; + key = nextkey; + } + + if (!(strcmp(dommac, (const char *) mac))) { + if (autoassign) { + if (mac) + free(mac); + mac = NULL; + if (!(mac = (xmlChar *)xenXMAutoAssignMac())) + goto cleanup; + /* initialize the list */ + list_item = virConfGetValue(entry->conf, "vif"); + prev = list_item; + list_val = list_item->list; + continue; + } else + break; + } + skip: + prev = list_val; + list_val = list_val->next; + } + } else if (!list_item) { + virConfEntryPtr prev_entry; + + cur = entry->conf->entries; + while (cur->next) + cur = cur->next; + prev_entry = cur; + if (!(cur = calloc(1, sizeof(virConfEntry)))) + goto cleanup; + if(!(cur->name = calloc(1, strlen("vif") + 1))) { + free(cur); + goto cleanup; + } + strcpy(cur->name, "vif"); + cur->next = NULL; + cur->comment = NULL; + prev_entry->next = cur; + + if (!(list_item = calloc(1, sizeof(virConfValue)))) { + free(cur->name); + free(cur); + goto cleanup; + } + list_item->type = VIR_CONF_LIST; + list_item->list = NULL; + list_item->str = NULL; + list_item->next = NULL; + + cur->value = list_item; + list_val = NULL; + prev = cur->value; + } else + goto cleanup; + + if ((node = virXPathNode("/interface", ctxt))) { + if (autoassign) { + node_cur = node->children; + + while (node_cur->next) + node_cur = node_cur->next; + + if (!(node_tmp = calloc(1, sizeof(xmlNode)))) + goto node_cleanup; + node_tmp->type = XML_ELEMENT_NODE; + if (!(node_tmp->name = malloc(4))) + goto node_cleanup; + strcpy((char *)node_tmp->name, "mac"); + node_tmp->children = NULL; + + if (!(attr_node = calloc(1, sizeof(xmlAttr)))) + goto node_cleanup; + attr_node->type = XML_ATTRIBUTE_NODE; + attr_node->ns = NULL; + if (!(attr_node->name = malloc(8))) + goto node_cleanup; + strcpy((char *) attr_node->name, "address"); + node_tmp->properties = attr_node; + + if (!(text_node = calloc(1, sizeof(xmlNode)))) + goto node_cleanup; + text_node->type = XML_TEXT_NODE; + text_node->_private = NULL; + if (!(text_node->name = malloc(8))) + goto node_cleanup; + strcpy((char *) text_node->name, "text"); + text_node->children = NULL; + text_node->parent = (xmlNodePtr)attr_node; + text_node->content = mac; + mac = NULL; + attr_node->children = text_node; + attr_node->last = text_node; + attr_node->parent = node_tmp; + + node_cur->next = node_tmp; + } + if (!(dev = xenXMParseXMLVif(domain->conn, node, hvm))) + goto cleanup; + } else + goto cleanup; + + if (!list_val) { + /* insert */ + if (!(list_val = malloc(sizeof(virConfValue)))) + goto cleanup; + list_val->type = VIR_CONF_STRING; + list_val->next = NULL; + list_val->str = dev; + if (prev->type == VIR_CONF_LIST) + prev->list = list_val; + else + prev->next = list_val; + } else { + /* configure */ + if (list_val->str) + free(list_val->str); + list_val->str = dev; + } + + ret = 0; + goto cleanup; + + node_cleanup: + if (node_tmp) + xmlFree(node_tmp); + if (attr_node) + xmlFree(attr_node); + if (text_node) + xmlFree(text_node); + cleanup: + if (type) + free(type); + if (source) + free(source); + if (mac) + free(mac); + + return (ret); +} + +/** + * xenXMAutoAssignMac: + * @mac: pointer to Mac String + * + * a mac is assigned automatically. + * + * Returns 0 in case of success, -1 in case of failure. + */ +char * +xenXMAutoAssignMac() { + char *buf; + + if (!(buf = malloc(18))) + return 0; + srand((unsigned)time(NULL)); + sprintf(buf, "00:16:3e:%02x:%02x:%02x" + ,1 + (int)(256*(rand()/(RAND_MAX+1.0))) + ,1 + (int)(256*(rand()/(RAND_MAX+1.0))) + ,1 + (int)(256*(rand()/(RAND_MAX+1.0)))); + return buf; +} + +/** + * xenXMDomainDetachDevice: + * @domain: pointer to domain object + * @xml: pointer to XML description of device + * + * Destroy a virtual device attachment to backend. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xenXMDomainDetachDevice(virDomainPtr domain, const char *xml) { + const char *filename = NULL; + char device[8], *domdevice = NULL; + xenXMConfCachePtr entry = NULL; + virConfEntryPtr cur = NULL; + virConfValuePtr prev = NULL, list_ptr = NULL, list_val = NULL; + xmlDocPtr doc = NULL; + xmlNodePtr node = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlChar *key = NULL; + int ret = -1; + + if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + goto cleanup; + } + if (domain->conn->flags & VIR_CONNECT_RO) + goto cleanup; + if (domain->id != -1) + goto cleanup; + if (!(filename = virHashLookup(nameConfigMap, domain->name))) + goto cleanup; + + doc = xmlReadDoc((const xmlChar *) xml, "device.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING); + if (!doc) { + xenXMError(domain->conn, VIR_ERR_XML_ERROR, "cannot read XML domain definition"); + goto cleanup; + } + if (!(ctxt = xmlXPathNewContext(doc))) { + xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR, "cannot create XPath context"); + goto cleanup; + } + + if ((node = virXPathNode("/disk", ctxt))) { + strcpy(device, "disk"); + if (!(node = virXPathNode("/disk/target", ctxt))) + goto cleanup; + key = xmlGetProp(node, BAD_CAST "dev"); + } else if ((node = virXPathNode("/interface", ctxt))) { + strcpy(device, "vif"); + if (!(node = virXPathNode("/interface/mac", ctxt))) + goto cleanup; + key = xmlGetProp(node, BAD_CAST "address"); + } else + goto cleanup; + if (!key || (strlen((char *)key) == 0)) + goto cleanup; + + if (!(entry = virHashLookup(configCache, filename))) + goto cleanup; + if (!entry->conf || !entry->conf->entries) + goto cleanup; + cur = entry->conf->entries; + while (strcmp(cur->name, device)) { + cur = cur->next; + if (!cur) + goto cleanup; + } + + list_ptr = cur->value; + if (list_ptr && list_ptr->type == VIR_CONF_LIST) { + list_val = list_ptr->list; + while (list_val) { + if (!(strcmp(device, "disk"))) { + char domdev[NAME_MAX]; + char *head; + char *offset; + char *tmp; + + if ((list_val->type != VIR_CONF_STRING) || (!list_val->str)) + goto skip; + head = list_val->str; + + /* Extract the source */ + if (!(offset = strchr(head, ',')) || offset[0] == '\0') + goto skip; + if ((offset - head) >= (PATH_MAX-1)) + goto skip; + head = offset + 1; + + /* Extract the dest */ + if (!(offset = strchr(head, ',')) || offset[0] == '\0') + goto skip; + if ((offset - head) >= (PATH_MAX-1)) + goto skip; + strncpy(domdev, head, (offset - head)); + domdev[(offset-head)] = '\0'; + head = offset + 1; + + /* Remove legacy ioemu: junk */ + if (!strncmp(domdev, "ioemu:", 6)) { + memmove(domdev, domdev+6, strlen(domdev)-5); + } + + /* Check for a :cdrom/:disk postfix */ + if ((tmp = strchr(domdev, ':'))) + tmp[0] = '\0'; + + if (!(strcmp(domdev, (const char *) key))) + break; + } else { + char dommac[18]; + char *mac; + + dommac[0] = '\0'; + + if ((list_val->type != VIR_CONF_STRING) || (!list_val->str)) + goto skip; + + mac = list_val->str; + while (mac) { + char *data; + char *nextmac = strchr(mac, ','); + + if (!(data = strchr(mac, '=')) || (data[0] == '\0')) + goto skip; + data++; + + if (!strncmp(mac, "mac=", 4)) { + int len = nextmac ? (nextmac - data) : 17; + if (len > 17) + len = 17; + strncpy(dommac, data, len); + dommac[len] = '\0'; + } + + while (nextmac && (nextmac[0] == ',' || + nextmac[0] == ' ' || + nextmac[0] == '\t')) + nextmac++; + mac = nextmac; + } + + if (!(strcmp(dommac, (const char *) key))) + break; + } + skip: + prev = list_val; + list_val = list_val->next; + } + } + + if (!list_val) + goto cleanup; + else { + if (!prev) + cur->value->list = list_val->next; + else + prev->next = list_val->next; + } + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (virConfWriteFile(entry->filename, entry->conf) < 0) + goto cleanup; + + ret = 0; + + cleanup: + if (ctxt) + xmlXPathFreeContext(ctxt); + if (doc) + xmlFreeDoc(doc); + if (domdevice) + free(domdevice); + if (key) + free(key); + if (list_val) + free(list_val); + + return (ret); +} + #endif /* WITH_XEN */ /* * Local variables: Index: src/xm_internal.h =================================================================== RCS file: /data/cvs/libvirt/src/xm_internal.h,v retrieving revision 1.8 diff -u -p -r1.8 xm_internal.h --- src/xm_internal.h 5 Dec 2007 18:28:05 -0000 1.8 +++ src/xm_internal.h 8 Jan 2008 01:10:53 -0000 @@ -61,6 +61,9 @@ int xenXMDomainUndefine(virDomainPtr dom virConfPtr xenXMParseXMLToConfig(virConnectPtr conn, const char *xml); char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf); +static int xenXMDomainAttachDevice(virDomainPtr domain, const char *xml); +static int xenXMDomainDetachDevice(virDomainPtr domain, const char *xml); + #ifdef __cplusplus } #endif