Index: src/qemu_conf.c =================================================================== RCS file: /data/cvs/libvirt/src/qemu_conf.c,v retrieving revision 1.16 diff -u -p -r1.16 qemu_conf.c --- src/qemu_conf.c 12 Oct 2007 16:05:44 -0000 1.16 +++ src/qemu_conf.c 13 Oct 2007 02:46:23 -0000 @@ -499,11 +499,14 @@ int qemudExtractVersion(virConnectPtr co } -/* Parse the XML definition for a disk */ -static struct qemud_vm_disk_def *qemudParseDiskXML(virConnectPtr conn, - struct qemud_driver *driver ATTRIBUTE_UNUSED, - xmlNodePtr node) { - struct qemud_vm_disk_def *disk = calloc(1, sizeof(struct qemud_vm_disk_def)); +/* Parse the XML definition for a disk + * @param disk pre-allocated & zero'd disk record + * @param node XML nodeset to parse for disk definition + * @return 0 on success, -1 on failure + */ +static int qemudParseDiskXML(virConnectPtr conn, + struct qemud_vm_disk_def *disk, + xmlNodePtr node) { xmlNodePtr cur; xmlChar *device = NULL; xmlChar *source = NULL; @@ -511,11 +514,6 @@ static struct qemud_vm_disk_def *qemudPa xmlChar *type = NULL; int typ = 0; - if (!disk) { - qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "disk"); - return NULL; - } - type = xmlGetProp(node, BAD_CAST "type"); if (type != NULL) { if (xmlStrEqual(type, BAD_CAST "file")) @@ -612,7 +610,7 @@ static struct qemud_vm_disk_def *qemudPa xmlFree(target); xmlFree(source); - return disk; + return 0; error: if (type) @@ -623,8 +621,7 @@ static struct qemud_vm_disk_def *qemudPa xmlFree(source); if (device) xmlFree(device); - free(disk); - return NULL; + return -1; } static void qemudRandomMAC(struct qemud_vm_net_def *net) { @@ -637,11 +634,14 @@ static void qemudRandomMAC(struct qemud_ } -/* Parse the XML definition for a network interface */ -static struct qemud_vm_net_def *qemudParseInterfaceXML(virConnectPtr conn, - struct qemud_driver *driver ATTRIBUTE_UNUSED, - xmlNodePtr node) { - struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def)); +/* Parse the XML definition for a network interface + * @param net pre-allocated & zero'd net record + * @param node XML nodeset to parse for net definition + * @return 0 on success, -1 on failure + */ +static int qemudParseInterfaceXML(virConnectPtr conn, + struct qemud_vm_net_def *net, + xmlNodePtr node) { xmlNodePtr cur; xmlChar *macaddr = NULL; xmlChar *type = NULL; @@ -652,11 +652,6 @@ static struct qemud_vm_net_def *qemudPar xmlChar *address = NULL; xmlChar *port = NULL; - if (!net) { - qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "net"); - return NULL; - } - net->type = QEMUD_NET_USER; type = xmlGetProp(node, BAD_CAST "type"); @@ -869,7 +864,7 @@ static struct qemud_vm_net_def *qemudPar xmlFree(address); } - return net; + return 0; error: if (network) @@ -884,24 +879,17 @@ static struct qemud_vm_net_def *qemudPar xmlFree(script); if (bridge) xmlFree(bridge); - free(net); - return NULL; + return -1; } /* Parse the XML definition for a network interface */ -static struct qemud_vm_input_def *qemudParseInputXML(virConnectPtr conn, - struct qemud_driver *driver ATTRIBUTE_UNUSED, - xmlNodePtr node) { - struct qemud_vm_input_def *input = calloc(1, sizeof(struct qemud_vm_input_def)); +static int qemudParseInputXML(virConnectPtr conn, + struct qemud_vm_input_def *input, + xmlNodePtr node) { xmlChar *type = NULL; xmlChar *bus = NULL; - if (!input) { - qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "input"); - return NULL; - } - type = xmlGetProp(node, BAD_CAST "type"); bus = xmlGetProp(node, BAD_CAST "bus"); @@ -944,7 +932,7 @@ static struct qemud_vm_input_def *qemudP if (bus) xmlFree(bus); - return input; + return 0; error: if (type) @@ -952,8 +940,7 @@ static struct qemud_vm_input_def *qemudP if (bus) xmlFree(bus); - free(input); - return NULL; + return -1; } @@ -1318,8 +1305,13 @@ static struct qemud_vm_def *qemudParseXM (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { struct qemud_vm_disk_def *prev = NULL; for (i = 0; i < obj->nodesetval->nodeNr; i++) { - struct qemud_vm_disk_def *disk; - if (!(disk = qemudParseDiskXML(conn, driver, obj->nodesetval->nodeTab[i]))) { + struct qemud_vm_disk_def *disk = calloc(1, sizeof(struct qemud_vm_disk_def)); + if (!disk) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "disk"); + goto error; + } + if (qemudParseDiskXML(conn, disk, obj->nodesetval->nodeTab[i]) < 0) { + free(disk); goto error; } def->ndisks++; @@ -1341,8 +1333,13 @@ static struct qemud_vm_def *qemudParseXM (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { struct qemud_vm_net_def *prev = NULL; for (i = 0; i < obj->nodesetval->nodeNr; i++) { - struct qemud_vm_net_def *net; - if (!(net = qemudParseInterfaceXML(conn, driver, obj->nodesetval->nodeTab[i]))) { + struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def)); + if (!net) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "net"); + goto error; + } + if (qemudParseInterfaceXML(conn, net, obj->nodesetval->nodeTab[i]) < 0) { + free(net); goto error; } def->nnets++; @@ -1363,8 +1360,13 @@ static struct qemud_vm_def *qemudParseXM (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { struct qemud_vm_input_def *prev = NULL; for (i = 0; i < obj->nodesetval->nodeNr; i++) { - struct qemud_vm_input_def *input; - if (!(input = qemudParseInputXML(conn, driver, obj->nodesetval->nodeTab[i]))) { + struct qemud_vm_input_def *input = calloc(1, sizeof(struct qemud_vm_input_def)); + if (!input) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "input"); + goto error; + } + if (qemudParseInputXML(conn, input, obj->nodesetval->nodeTab[i]) < 0) { + free(input); goto error; } /* Mouse + PS/2 is implicit with graphics, so don't store it */ @@ -1929,6 +1931,51 @@ static int qemudSaveConfig(virConnectPtr return ret; } +struct qemud_vm_device_def * +qemudParseVMDeviceDef(virConnectPtr conn, + struct qemud_driver *driver ATTRIBUTE_UNUSED, + const char *xmlStr) +{ + xmlDocPtr xml; + xmlNodePtr node; + struct qemud_vm_device_def *dev = calloc(1, sizeof(struct qemud_vm_device_def)); + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "device.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, NULL); + return NULL; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, "missing root element"); + goto error; + } + if (xmlStrEqual(node->name, BAD_CAST "disk")) { + dev->type = QEMUD_DEVICE_DISK; + qemudParseDiskXML(conn, &(dev->data.disk), node); + } else if (xmlStrEqual(node->name, BAD_CAST "net")) { + dev->type = QEMUD_DEVICE_NET; + qemudParseInterfaceXML(conn, &(dev->data.net), node); + } else if (xmlStrEqual(node->name, BAD_CAST "input")) { + dev->type = QEMUD_DEVICE_DISK; + qemudParseInputXML(conn, &(dev->data.input), node); + } else { + qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, "unknown device type"); + goto error; + } + + xmlFreeDoc(xml); + + return dev; + + error: + if (xml) xmlFreeDoc(xml); + if (dev) free(dev); + return NULL; +} + struct qemud_vm_def * qemudParseVMDef(virConnectPtr conn, struct qemud_driver *driver, Index: src/qemu_conf.h =================================================================== RCS file: /data/cvs/libvirt/src/qemu_conf.h,v retrieving revision 1.10 diff -u -p -r1.10 qemu_conf.h --- src/qemu_conf.h 12 Oct 2007 16:05:44 -0000 1.10 +++ src/qemu_conf.h 13 Oct 2007 02:46:23 -0000 @@ -125,6 +125,22 @@ struct qemud_vm_input_def { struct qemud_vm_input_def *next; }; +/* Flags for the 'type' field in next struct */ +enum qemud_vm_device_type { + QEMUD_DEVICE_DISK, + QEMUD_DEVICE_NET, + QEMUD_DEVICE_INPUT, +}; + +struct qemud_vm_device_def { + int type; + union { + struct qemud_vm_disk_def disk; + struct qemud_vm_net_def net; + struct qemud_vm_input_def input; + } data; +}; + #define QEMUD_MAX_BOOT_DEVS 4 /* 3 possible boot devices */ @@ -354,6 +370,11 @@ struct qemud_vm * void qemudRemoveInactiveVM (struct qemud_driver *driver, struct qemud_vm *vm); +struct qemud_vm_device_def * + qemudParseVMDeviceDef (virConnectPtr conn, + struct qemud_driver *driver, + const char *xmlStr); + struct qemud_vm_def * qemudParseVMDef (virConnectPtr conn, struct qemud_driver *driver, Index: src/qemu_driver.c =================================================================== RCS file: /data/cvs/libvirt/src/qemu_driver.c,v retrieving revision 1.30 diff -u -p -r1.30 qemu_driver.c --- src/qemu_driver.c 12 Oct 2007 16:05:44 -0000 1.30 +++ src/qemu_driver.c 13 Oct 2007 02:46:25 -0000 @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -453,7 +454,7 @@ static int qemudOpenMonitor(virConnectPt char buf[1024]; int ret = -1; - if (!(monfd = open(monitor, O_RDWR))) { + if (!(monfd = open(monitor, O_NOCTTY |O_RDWR))) { qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Unable to open monitor path %s", monitor); return -1; @@ -1329,9 +1330,7 @@ static int qemudMonitorCommand(struct qe char *b; if (got == 0) { - if (buf) - free(buf); - return -1; + goto error; } if (got < 0) { if (errno == EINTR) @@ -1339,21 +1338,17 @@ static int qemudMonitorCommand(struct qe if (errno == EAGAIN) break; - if (buf) - free(buf); - return -1; + goto error; } if (!(b = realloc(buf, size+got+1))) { - free(buf); - return -1; + goto error; } buf = b; memmove(buf+size, data, got); buf[size+got] = '\0'; size += got; } - if (buf) - qemudDebug("Mon [%s]", buf); + /* Look for QEMU prompt to indicate completion */ if (buf && ((tmp = strstr(buf, "\n(qemu) ")) != NULL)) { tmp[0] = '\0'; @@ -1365,13 +1360,35 @@ static int qemudMonitorCommand(struct qe if (errno == EINTR) goto pollagain; - free(buf); - return -1; + goto error; } } + retry1: + if (write(vm->logfile, buf, strlen(buf)) < 0) { + /* Log, but ignore failures to write logfile for VM */ + if (errno == EINTR) + goto retry1; + qemudLog(QEMUD_WARN, "Unable to log VM console data: %s", + strerror(errno)); + } + *reply = buf; return 0; + + error: + if (buf) { + retry2: + if (write(vm->logfile, buf, strlen(buf)) < 0) { + /* Log, but ignore failures to write logfile for VM */ + if (errno == EINTR) + goto retry2; + qemudLog(QEMUD_WARN, "Unable to log VM console data: %s", + strerror(errno)); + } + free(buf); + } + return -1; } @@ -2313,6 +2330,93 @@ static int qemudDomainUndefine(virDomain return 0; } +static int qemudDomainChangeCDROM(virDomainPtr dom, + struct qemud_vm *vm, + struct qemud_vm_disk_def *olddisk, + struct qemud_vm_disk_def *newdisk) { + struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData; + char *cmd; + char *reply; + /* XXX QEMU only supports a single CDROM for now */ + /*cmd = malloc(strlen("change ") + strlen(olddisk->dst) + 1 + strlen(newdisk->src) + 2);*/ + cmd = malloc(strlen("change ") + strlen("cdrom") + 1 + strlen(newdisk->src) + 2); + if (!cmd) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_MEMORY, "monitor command"); + return -1; + } + strcpy(cmd, "change "); + /* XXX QEMU only supports a single CDROM for now */ + /*strcat(cmd, olddisk->dst);*/ + strcat(cmd, "cdrom"); + strcat(cmd, " "); + strcat(cmd, newdisk->src); + strcat(cmd, "\n"); + + fprintf(stderr, "Send '%s'\n", cmd); + if (qemudMonitorCommand(driver, vm, cmd, &reply) < 0) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "cannot change cdrom media"); + free(cmd); + return -1; + } + fprintf(stderr, "Get back %s\n", reply); + free(reply); + free(cmd); + strcpy(olddisk->dst, newdisk->dst); + olddisk->type = newdisk->type; + return 0; +} + +static int qemudDomainAttachDevice(virDomainPtr dom, + const char *xml) { + struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData; + struct qemud_vm *vm = qemudFindVMByUUID(driver, dom->uuid); + struct qemud_vm_device_def *dev; + struct qemud_vm_disk_def *disk; + + if (!vm) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid"); + return -1; + } + + if (!qemudIsActiveVM(vm)) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, "cannot attach device on inactive domain"); + return -1; + } + + dev = qemudParseVMDeviceDef(dom->conn, driver, xml); + if (dev == NULL) { + return -1; + } + + if (dev->type != QEMUD_DEVICE_DISK || dev->data.disk.device != QEMUD_DISK_CDROM) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, "only CDROM disk devices can be attached"); + free(dev); + return -1; + } + + disk = vm->def->disks; + while (disk) { + if (disk->device == QEMUD_DISK_CDROM && + STREQ(disk->dst, dev->data.disk.dst)) + break; + disk = disk->next; + } + + if (!disk) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, "CDROM not attached, cannot change media"); + free(dev); + return -1; + } + + if (qemudDomainChangeCDROM(dom, vm, disk, &dev->data.disk) < 0) { + free(dev); + return -1; + } + + free(dev); + return 0; +} + static int qemudDomainGetAutostart(virDomainPtr dom, int *autostart) { struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData; @@ -2705,7 +2809,7 @@ static virDriver qemuDriver = { qemudDomainStart, /* domainCreate */ qemudDomainDefine, /* domainDefineXML */ qemudDomainUndefine, /* domainUndefine */ - NULL, /* domainAttachDevice */ + qemudDomainAttachDevice, /* domainAttachDevice */ NULL, /* domainDetachDevice */ qemudDomainGetAutostart, /* domainGetAutostart */ qemudDomainSetAutostart, /* domainSetAutostart */