On Mon, Aug 28, 2006 at 04:55:09AM -0400, Daniel Veillard wrote: > I scanned quickly the protocol part but I need to get my head around it :-) > That's a lot of code ! One think I would like is to try to get all commenting > at the same level, i.e. having every function carrying a comment header, > That's something I will help with, maybe as a side effect of reviewing the > code. Tell me how we could do that ? Maybe I could start with the simplest > code from qemud. I'm attaching an updated patch which has many many more comments in it, and also has much better error reporting - nearly all errors are propagated back to libvirt with formal codes & messages. Other changes previously suggested (re-factoring shared code, etc) are yet to be done. Dan -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
diff --exclude docs --exclude CVS -ruN libvirt-pristine/configure.in libvirt-qemu/configure.in --- libvirt-pristine/configure.in 2006-08-29 18:27:07.000000000 -0400 +++ libvirt-qemu/configure.in 2006-08-30 08:42:25.000000000 -0400 @@ -256,5 +256,6 @@ libvirt.pc libvirt.spec \ include/libvirt/Makefile include/libvirt/libvirt.h \ python/Makefile python/tests/Makefile \ + qemud/Makefile \ tests/Makefile proxy/Makefile \ tests/virshdata/Makefile tests/confdata/Makefile) diff --exclude docs --exclude CVS -ruN libvirt-pristine/Makefile.am libvirt-qemu/Makefile.am --- libvirt-pristine/Makefile.am 2006-06-28 14:19:13.000000000 -0400 +++ libvirt-qemu/Makefile.am 2006-08-27 17:05:39.000000000 -0400 @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = src include docs @PYTHON_SUBDIR@ tests proxy +SUBDIRS = src include docs @PYTHON_SUBDIR@ tests proxy qemud EXTRA_DIST = libvirt.spec.in libvirt.spec COPYING.LIB \ libvirt.pc.in libvirt.pc TODO AUTHORS ChangeLog \ diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/config.c libvirt-qemu/qemud/config.c --- libvirt-pristine/qemud/config.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/config.c 2006-08-30 22:19:20.000000000 -0400 @@ -0,0 +1,840 @@ + +#include <dirent.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <errno.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/uri.h> + +#include "protocol.h" +#include "internal.h" +#include "config.h" +#include "driver.h" + +/* XXX re-factor to shared lib */ +static int qemudParseUUID(const char *uuid, + unsigned char *rawuuid) { + const char *cur; + int i; + + /* + * do a liberal scan allowing '-' and ' ' anywhere between character + * pairs as long as there is 32 of them in the end. + */ + cur = uuid; + for (i = 0;i < 16;) { + rawuuid[i] = 0; + if (*cur == 0) + goto error; + if ((*cur == '-') || (*cur == ' ')) { + cur++; + continue; + } + if ((*cur >= '0') && (*cur <= '9')) + rawuuid[i] = *cur - '0'; + else if ((*cur >= 'a') && (*cur <= 'f')) + rawuuid[i] = *cur - 'a' + 10; + else if ((*cur >= 'A') && (*cur <= 'F')) + rawuuid[i] = *cur - 'A' + 10; + else + goto error; + rawuuid[i] *= 16; + cur++; + if (*cur == 0) + goto error; + if ((*cur >= '0') && (*cur <= '9')) + rawuuid[i] += *cur - '0'; + else if ((*cur >= 'a') && (*cur <= 'f')) + rawuuid[i] += *cur - 'a' + 10; + else if ((*cur >= 'A') && (*cur <= 'F')) + rawuuid[i] += *cur - 'A' + 10; + else + goto error; + i++; + cur++; + } + + return 0; + +error: + qemudSetError(QEMUD_ERR_MALFORMED_UUID, "uuid is not well-formed"); + return -1; +} + + +/* Take an XML domain node pointing to a disk device, + * grok it attributes / child & a create/populate + * a qemud_vm_disk_def object with the info. + * Return NULL on failure */ +static struct qemud_vm_disk_def *qemudParseDiskXML(xmlNodePtr node) { + struct qemud_vm_disk_def *disk = calloc(1, sizeof(struct qemud_vm_disk_def)); + xmlNodePtr cur; + xmlChar *device = NULL; + xmlChar *source = NULL; + xmlChar *target = NULL; + xmlChar *type = NULL; + int typ = 0; + + type = xmlGetProp(node, BAD_CAST "type"); + if (type != NULL) { + if (xmlStrEqual(type, BAD_CAST "file")) + typ = QEMUD_DISK_FILE; + else if (xmlStrEqual(type, BAD_CAST "block")) + typ = QEMUD_DISK_BLOCK; + else { + qemudSetError(QEMUD_ERR_ILLEGAL_DISK_CONFIG, "unknown disk type"); + goto error; + } + xmlFree(type); + type = NULL; + } + + device = xmlGetProp(node, BAD_CAST "device"); + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((source == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "source"))) { + + if (typ == QEMUD_DISK_FILE) + 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")) { + disk->readonly = 1; + } + } + cur = cur->next; + } + + if (source == NULL) { + qemudSetError(QEMUD_ERR_ILLEGAL_DISK_CONFIG, "missing disk source"); + goto error; + } + if (target == NULL) { + qemudSetError(QEMUD_ERR_ILLEGAL_DISK_CONFIG, "missing disk target"); + goto error; + } + + if (device && + !strcmp((const char *)device, "floppy") && + strcmp((const char *)target, "fda") && + strcmp((const char *)target, "fdb")) { + qemudSetError(QEMUD_ERR_ILLEGAL_DISK_CONFIG, "illegal floppy device name"); + goto error; + } + + if (device && + !strcmp((const char *)device, "cdrom") && + strcmp((const char *)target, "hdc")) { + qemudSetError(QEMUD_ERR_ILLEGAL_DISK_CONFIG, "illegal cdrom device name"); + goto error; + } + + if (device && + !strcmp((const char *)device, "cdrom")) + disk->readonly = 1; + + if ((!device || !strcmp((const char *)device, "disk")) && + strcmp((const char *)target, "hda") && + strcmp((const char *)target, "hdb") && + strcmp((const char *)target, "hdc") && + strcmp((const char *)target, "hdd")) { + qemudSetError(QEMUD_ERR_ILLEGAL_DISK_CONFIG, "illegal harddrive device name"); + goto error; + } + + strncpy(disk->src, (const char *)source, NAME_MAX-1); + disk->src[NAME_MAX-1] = '\0'; + + strncpy(disk->dst, (const char *)target, NAME_MAX-1); + disk->dst[NAME_MAX-1] = '\0'; + disk->type = typ; + + if (!device) + disk->device = QEMUD_DISK_DISK; + else if (!strcmp((const char *)device, "disk")) + disk->device = QEMUD_DISK_DISK; + else if (!strcmp((const char *)device, "cdrom")) + disk->device = QEMUD_DISK_CDROM; + else if (!strcmp((const char *)device, "floppy")) + disk->device = QEMUD_DISK_FLOPPY; + else { + qemudSetError(QEMUD_ERR_ILLEGAL_DISK_CONFIG, "unknown device type"); + goto error; + } + + xmlFree(target); + xmlFree(source); + + return disk; + + error: + if (type) + xmlFree(type); + if (target) + xmlFree(target); + if (source) + xmlFree(source); + free(disk); + return NULL; +} + + +/* Take an XML doc and grok its nodes/atttributes to populate + * the hardware definition in the qemud_vm instance + * returns -1 on failure, 0 on success + */ +static int qemudParseXML(xmlDocPtr xml, + struct qemud_vm *vm) { + xmlNodePtr root = NULL; + xmlChar *prop = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + char *conv = NULL; + + root = xmlDocGetRootElement(xml); + if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) { + qemudSetError(QEMUD_ERR_XML_NO_ROOT, "no root element named 'domain'"); + goto error; + } + + if (!(prop = xmlGetProp(root, BAD_CAST "type")) || + strcmp((char *)prop, "qemu")) { + qemudSetError(QEMUD_ERR_XML_WRONG_TYPE, "wrong domain type"); + goto error; + } + free(prop); + prop = NULL; + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + qemudSetError(QEMUD_ERR_GENERIC, "cannot create xpath context"); + 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)) { + qemudSetError(QEMUD_ERR_XML_MISSING_NAME, NULL); + goto error; + } + if (strlen((const char *)obj->stringval) >= QEMUD_MAX_NAME_LEN) { + qemudSetError(QEMUD_ERR_NAME_OVERFLOW, NULL); + goto error; + } + strcpy(vm->def.name, (const char *)obj->stringval); + xmlXPathFreeObject(obj); + + obj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + qemudSetError(QEMUD_ERR_XML_MISSING_UUID, NULL); + goto error; + } + if (qemudParseUUID((const char *)obj->stringval, vm->def.uuid) < 0) { + goto error; + } + xmlXPathFreeObject(obj); + + + obj = xmlXPathEval(BAD_CAST "string(/domain/memory[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + vm->def.memory = 64 * 1024; + } else { + conv = NULL; + vm->def.memory = strtoll((const char*)obj->stringval, &conv, 10); + if (conv == (const char*)obj->stringval) { + qemudSetError(QEMUD_ERR_GENERIC, "malformed number for domain memory"); + goto error; + } + } + if (obj) { + xmlXPathFreeObject(obj); + obj = NULL; + } + + obj = xmlXPathEval(BAD_CAST "string(/domain/vcpu[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + vm->def.vcpus = 1; + } else { + conv = NULL; + vm->def.vcpus = strtoll((const char*)obj->stringval, &conv, 10); + if (conv == (const char*)obj->stringval) { + qemudSetError(QEMUD_ERR_GENERIC, "malformed number for domain cpus"); + goto error; + } + } + if (obj) { + xmlXPathFreeObject(obj); + obj = NULL; + } + + obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) { + vm->def.graphicsType = QEMUD_GRAPHICS_NONE; + } else { + prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type"); + if (!strcmp((char *)prop, "vnc")) { + vm->def.graphicsType = QEMUD_GRAPHICS_VNC; + prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "port"); + if (prop) { + conv = NULL; + vm->def.vncPort = strtoll((const char*)prop, &conv, 10); + } else { + vm->def.vncPort = 0; + } + } + } + + /* analyze of the devices */ + obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { + for (int i = 0; i < obj->nodesetval->nodeNr; i++) { + struct qemud_vm_disk_def *disk; + if (!(disk = qemudParseDiskXML(obj->nodesetval->nodeTab[i]))) { + goto error; + } + vm->def.ndisks++; + disk->next = vm->def.disks; + vm->def.disks = disk; + } + } + xmlXPathFreeObject(obj); + + return 0; + + error: + if (prop) + free(prop); + if (obj) + xmlXPathFreeObject(obj); + return -1; +} + + +/* Construct a command line for launching qemu according + * to the hardware definition of the passed in qemud_vm + * instance. + * argv: populated with a char ** array containing the args + * which is NULL terminated. Callers job to free the argv, + * and all non-NULL elements within + * argc: popilated with number of elements in argv - including + * the trailing NULL. + * Returns -1 on failure, 0 on success + */ +int qemudBuildCommandLine(struct qemud_vm *vm, + char ***argv, + int *argc) { + int n = 0; + char memory[50]; + char vcpus[50]; + struct qemud_vm_disk_def *disk = vm->def.disks; + + *argc = 1 + /* qemu */ + 2 * vm->def.ndisks + /* disks*/ + 2 + /* memory*/ + 2 + /* cpus */ + (vm->def.graphicsType == QEMUD_GRAPHICS_VNC ? 2 : 0) + /* graphics */ + 1; /* NULL */ + + sprintf(memory, "%d", vm->def.memory/1024); + sprintf(vcpus, "%d", vm->def.vcpus); + + if (!(*argv = malloc(sizeof(char *) * *argc))) { + qemudSetError(QEMUD_ERR_NO_MEMORY, "cannot allocate command line"); + return -1; + } + (*argv)[n++] = strdup("qemu"); + (*argv)[n++] = strdup("-m"); + (*argv)[n++] = strdup(memory); + (*argv)[n++] = strdup("-smp"); + (*argv)[n++] = strdup(vcpus); + + while (disk) { + char dev[NAME_MAX]; + char file[PATH_MAX]; + if (!strcmp(disk->dst, "hdc") && + disk->device == QEMUD_DISK_CDROM) + snprintf(dev, NAME_MAX, "-%s", "cdrom"); + else + snprintf(dev, NAME_MAX, "-%s", disk->dst); + snprintf(file, PATH_MAX, "%s", disk->src); + + (*argv)[n++] = strdup(dev); + (*argv)[n++] = strdup(file); + + disk = disk->next; + } + + if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) { + char port[10]; + snprintf(port, 10, "%d", vm->def.vncPort - 5900); + (*argv)[n++] = strdup("-vnc"); + (*argv)[n++] = strdup(port); + } + + (*argv)[n++] = NULL; + + return 0; +} + + +/* Recursively free all memory associated with the + * passed in qemud_vm object. The object must already + * have been removed from the active/inactive vms + * list in the qemud_server object. + */ +void qemudFreeVM(struct qemud_vm *vm) { + struct qemud_vm_disk_def *disk = vm->def.disks; + struct qemud_vm_net_def *net = vm->def.nets; + + while (disk) { + struct qemud_vm_disk_def *prev = disk; + disk = disk->next; + free(prev); + } + while (net) { + struct qemud_vm_net_def *prev = net; + net = net->next; + free(prev); + } + + free(vm); +} + +/* Constructs a path to a domain's config file + * name: filename of the domain config + * ext: optional extension, if 'name' doesn't already include it + * buf: buffer to populate with config file path + * buflen: max length of buf arg, including NULL + * Returns 0 on success, and buf is filled with fully + * qualified config path. Returns -1 on failure + */ +static +int qemudMakeConfigPath(struct qemud_server *server, + const char *name, + const char *ext, + char *buf, + unsigned int buflen) { + if ((strlen(server->configDir) + 1 + strlen(name) + (ext ? strlen(ext) : 0) + 1) > buflen) { + qemudSetError(QEMUD_ERR_PATH_OVERFLOW, "config file path too long"); + return -1; + } + + strcpy(buf, server->configDir); + strcat(buf, "/"); + strcat(buf, name); + if (ext) + strcat(buf, ext); + return 0; +} + + +/* Save the hardware definition of a VM to its + * persistent config file backing store. + * Returns 0 on success, -1 on failure + */ +static int qemudSaveConfig(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_vm *vm) { + char *xml; + int fd, ret = -1; + int towrite; + struct stat sb; + + if (!(xml = qemudGenerateXML(vm))) { + return -1; + } + +#if DEBUG + printf("Save VM %s\n", vm->configFile); +#endif + + if (stat(server->configDir, &sb) < 0) { + if (errno == ENOENT) { + if (mkdir(server->configDir, 0700) < 0) { + qemudSetError(QEMUD_ERR_GENERIC, "cannot create config directory"); + return -1; + } + } else { + qemudSetError(QEMUD_ERR_GENERIC, "cannot state config directory"); + return -1; + } + } else if (!S_ISDIR(sb.st_mode)) { + qemudSetError(QEMUD_ERR_GENERIC, "config directory is not a directory"); + return -1; + } + + if ((fd = open(vm->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + qemudSetError(QEMUD_ERR_GENERIC, "cannot create config file"); + goto cleanup; + } + + towrite = strlen(xml); + if (write(fd, xml, towrite) != towrite) { + qemudSetError(QEMUD_ERR_GENERIC, "cannot create config file"); + goto cleanup; + } + + if (close(fd) < 0) { + qemudSetError(QEMUD_ERR_GENERIC, "cannot save config file"); + goto cleanup; + } + + ret = 0; + + cleanup: + + free(xml); + + return ret; +} + + +/* Load & parse a config file, constructing a qemud_vm + * object to reppresent it in memory. The returned qemud_vm + * object is not hooked into the qemud_server's active or + * inactive VM list + * file: path to the config file, or NULL + * doc: the raw XML document data + * save: whether to save the config after parsing the XML + */ +struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server, + const char *file, + const char *doc, + int save) { + struct qemud_vm *vm = NULL; + xmlDocPtr xml; + +#if DEBUG + printf("Load VM %s\n", file); +#endif + if (!(xml = xmlReadDoc(BAD_CAST doc, file ? file : "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + qemudSetError(QEMUD_ERR_CONFIG_MALFORMED, "cannot parse XML file"); + return NULL; + } + + if (!(vm = calloc(1, sizeof(struct qemud_vm)))) { + qemudSetError(QEMUD_ERR_NO_MEMORY, "cannot allocated machine"); + return NULL; + } + + vm->stdout = -1; + vm->stderr = -1; + vm->monitor = -1; + vm->pid = -1; + vm->def.id = -1; + + if (qemudParseXML(xml, vm) < 0) { + xmlFreeDoc(xml); + qemudFreeVM(vm); + return NULL; + } + xmlFreeDoc(xml); + + if (qemudFindVMByUUID(server, vm->def.uuid)) { + qemudSetError(QEMUD_ERR_DUPLICATE_DOMAIN, "domain with duplicate uuid"); + qemudFreeVM(vm); + return NULL; + } + + if (qemudFindVMByName(server, vm->def.name)) { + qemudSetError(QEMUD_ERR_DUPLICATE_DOMAIN, "domain with duplicate name"); + qemudFreeVM(vm); + return NULL; + } + + if (file) { + strncpy(vm->configFile, file, PATH_MAX); + vm->configFile[PATH_MAX-1] = '\0'; + } else { + if (save) { + if (qemudMakeConfigPath(server, vm->def.name, ".xml", vm->configFile, PATH_MAX) < 0) { + qemudFreeVM(vm); + return NULL; + } + + if (qemudSaveConfig(server, vm) < 0) { + qemudFreeVM(vm); + return NULL; + } + } else { + vm->configFile[0] = '\0'; + } + } + + return vm; +} + + +/* Load a VM froma config file & hook it into the inactive + * VMs linked listed in the server. + * file: path to the config file to load + */ +static void qemudLoadConfig(struct qemud_server *server, + const char *file) { + FILE *fh; + struct stat st; + struct qemud_vm *vm; + char xml[QEMUD_MAX_XML_LEN]; + int ret; + + if (!(fh = fopen(file, "r"))) { + return; + } + + if (fstat(fileno(fh), &st) < 0) + goto cleanup; + + if (st.st_size >= QEMUD_MAX_XML_LEN) + goto cleanup; + + if ((ret = fread(xml, st.st_size, 1, fh)) != 1) { +#if DEBUG + printf("Couldn't read %d\n", ret); +#endif + goto cleanup; + } + xml[st.st_size] = '\0'; + + if ((vm = qemudLoadConfigXML(server, file, xml, 1))) { + vm->next = server->inactivevms; + server->inactivevms = vm; + server->ninactivevms++; + } + + cleanup: + fclose(fh); +} + + +/* Scan the directory containing config files for domains + * and load them into memory, hooking them into the + * inactive domains linked list + */ +int qemudScanConfigs(struct qemud_server *server) { + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(server->configDir))) { + if (errno == ENOENT) + return 0; + return -1; + } + + while ((entry = readdir(dir))) { + char file[PATH_MAX]; + if (entry->d_name[0] == '.') + continue; + + if (qemudMakeConfigPath(server, entry->d_name, NULL, file, PATH_MAX) < 0) + continue; + + qemudLoadConfig(server, file); + } + + closedir(dir); + + return 0; +} + + +/* XXX - following is copied from src/xml.c - refactor */ +struct qemudBuffer { + char *data; + int len; + int used; +}; + +static +int qemudBufferAdd(struct qemudBuffer *buf, const char *str) { + int need = strlen(str); + + if ((need+1) > (buf->len-buf->used)) { + return -1; + } + + memcpy(buf->data + buf->used, str, need+1); + buf->used += need; + + return 0; +} + + +static +int qemudBufferPrintf(struct qemudBuffer *buf, + const char *format, ...) { + int size, count; + va_list locarg, argptr; + + if ((format == NULL) || (buf == NULL)) { + return -1; + } + size = buf->len - buf->used - 1; + va_start(argptr, format); + va_copy(locarg, argptr); + + if ((count = vsnprintf(&buf->data[buf->used], + size, + format, + locarg)) >= size) { + qemudSetError(QEMUD_ERR_NO_MEMORY, "cannot increase buffer size"); + return -1; + } + va_end(locarg); + buf->used += count; + + buf->data[buf->used] = '\0'; + return 0; +} + + +/* Generate an XML document representing the hardware + * definition for the virtual machine. + * Returns the XML doc on success, NULL on failure. It + * is the callers responsibility to free the doc if + * it is non-NULL + */ +char *qemudGenerateXML(struct qemud_vm *vm) { + struct qemudBuffer buf; + unsigned char *uuid; + struct qemud_vm_disk_def *disk; + struct qemud_vm_net_def *net; + + buf.len = QEMUD_MAX_XML_LEN; + buf.used = 0; + buf.data = malloc(buf.len); + + if (vm->def.id >= 0) { + if (qemudBufferPrintf(&buf, "<domain type='qemu' id='%d'>\n", vm->def.id) < 0) + goto cleanup; + } else { + if (qemudBufferAdd(&buf, "<domain type='qemu'>\n") < 0) + goto cleanup; + } + + if (qemudBufferPrintf(&buf, " <name>%s</name>\n", vm->def.name) < 0) + goto cleanup; + + uuid = vm->def.uuid; + if (qemudBufferPrintf(&buf, " <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]) < 0) + goto cleanup; + if (qemudBufferPrintf(&buf, " <memory>%d</memory>\n", vm->def.memory) < 0) + goto cleanup; + if (qemudBufferPrintf(&buf, " <vcpu>%d</vcpu>\n", vm->def.vcpus) < 0) + goto cleanup; + + if (qemudBufferAdd(&buf, " <devices>\n") < 0) + goto cleanup; + + disk = vm->def.disks; + while (disk) { + const char *types[] = { + "block", + "file", + }; + const char *typeAttrs[] = { + "dev", + "file", + }; + const char *devices[] = { + "disk", + "cdrom", + "floppy", + }; + if (qemudBufferPrintf(&buf, " <disk type='%s' device='%s'>\n", + types[disk->type], devices[disk->device]) < 0) + goto cleanup; + + if (qemudBufferPrintf(&buf, " <source %s='%s'/>\n", typeAttrs[disk->type], disk->src) < 0) + goto cleanup; + + if (qemudBufferPrintf(&buf, " <target dev='%s'/>\n", disk->dst) < 0) + goto cleanup; + + if (disk->readonly) + if (qemudBufferAdd(&buf, " <readonly/>\n") < 0) + goto cleanup; + + if (qemudBufferPrintf(&buf, " </disk>\n") < 0) + goto cleanup; + + disk = disk->next; + } + + net = vm->def.nets; + + if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) { + if (vm->def.vncPort) { + qemudBufferPrintf(&buf, " <graphics type='vnc' port='%d'/>\n", vm->def.vncPort); + } else { + qemudBufferPrintf(&buf, " <graphics type='vnc'/>\n"); + } + } + + if (qemudBufferAdd(&buf, " </devices>\n") < 0) + goto cleanup; + + + if (qemudBufferAdd(&buf, "</domain>\n") < 0) + goto cleanup; + + return buf.data; + + cleanup: + printf("Fail\n"); + free(buf.data); + return NULL; +} + + +/* Remove a config file from disk */ +int qemudDeleteConfigXML(struct qemud_vm *vm) { + if (!vm->configFile[0]) { + qemudSetError(QEMUD_ERR_GENERIC, "domain has no config file"); + return -1; + } + +#if DEBUG + printf("Delete VM %s\n", vm->configFile); +#endif + + if (unlink(vm->configFile) < 0) { + if (errno == ENOENT) + qemudSetError(QEMUD_ERR_NO_DOMAIN, "no such domain"); + else if (errno == EPERM || errno == EACCES) + qemudSetError(QEMUD_ERR_READONLY, "unlink request denied"); + else + qemudSetError(QEMUD_ERR_GENERIC, "unlink failed"); + + return -1; + } + + vm->configFile[0] = '\0'; + + return 0; +} diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/config.h libvirt-qemu/qemud/config.h --- libvirt-pristine/qemud/config.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/config.h 2006-08-27 18:10:25.000000000 -0400 @@ -0,0 +1,22 @@ + +#ifndef __QEMUD_CONFIG_H +#define __QEMUD_CONFIG_H + +#include "internal.h" + +int qemudBuildCommandLine(struct qemud_vm *vm, + char ***argv, + int *argc); + +void qemudFreeVM(struct qemud_vm *vm); +struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server, + const char *file, + const char *doc, + int persist); +int qemudScanConfigs(struct qemud_server *server); +char *qemudGenerateXML(struct qemud_vm *vm); + +int qemudDeleteConfigXML(struct qemud_vm *vm); + + +#endif diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/dispatch.c libvirt-qemu/qemud/dispatch.c --- libvirt-pristine/qemud/dispatch.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/dispatch.c 2006-08-30 22:17:45.000000000 -0400 @@ -0,0 +1,474 @@ + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <libvirt/virterror.h> + +#include "internal.h" +#include "driver.h" +#include "dispatch.h" + +/* This file is just a massive set of functions to (de-)serialize + * wire messages to/from the functions in drivers.h + * + * Contract of wire protocol says that given an incoming packet + * with XXXRequest body, the return packet must either be the + * corresponding XXXXReply body, or a failureReply body. + * + * Anything odd / unexpected happens on the wire, we terminate + * with extreme prejudice. + * + * We're also paranoid about strings thoughout - we don't trust + * the client to have null-terminated them, so explicitly add + * a NULL in the last character position before dealing with + * any strings + */ + +/* Fill out the reply message with error details */ +static int qemudDispatchFailure(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + struct qemud_packet *out) { + const char *msg; + out->header.type = QEMUD_PKT_FAILURE; + out->header.dataSize = sizeof(out->data.failureReply); + out->data.failureReply.code = qemudGetLastError(&msg); + if (msg) { + strncpy(out->data.failureReply.message, msg, QEMUD_MAX_ERROR_LEN-1); + out->data.failureReply.message[QEMUD_MAX_ERROR_LEN-1] = '\0'; + } else { + out->data.failureReply.message[0] = '\0'; + } + qemudClearLastError(); + return 0; +} + +static int qemudDispatchGetProtocolVersion(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + out->header.type = QEMUD_PKT_GET_PROTOCOL_VERSION; + out->header.dataSize = sizeof(out->data.getProtocolVersionReply); + out->data.getProtocolVersionReply.version = QEMUD_PROTOCOL_VERSION; + return 0; +} + +static int qemudDispatchGetVersion(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int version = qemudGetVersion(server); + if (version < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_GET_VERSION; + out->header.dataSize = sizeof(out->data.getVersionReply); + out->data.getVersionReply.version = version; + } + return 0; +} +static int qemudDispatchListDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int ndomains = qemudListDomains(server, + out->data.listDomainsReply.domains, + QEMUD_MAX_NUM_DOMAINS); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_LIST_DOMAINS; + out->header.dataSize = sizeof(out->data.listDomainsReply); + out->data.listDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchNumDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int ndomains = qemudNumDomains(server); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NUM_DOMAINS; + out->header.dataSize = sizeof(out->data.numDomainsReply); + out->data.numDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchDomainCreate(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainCreateRequest)) + return -1; + + in->data.domainCreateRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0'; + + struct qemud_vm *vm = qemudDomainCreate(server, in->data.domainCreateRequest.xml); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_CREATE; + out->header.dataSize = sizeof(out->data.domainCreateReply); + out->data.domainCreateReply.id = vm->def.id; + memcpy(out->data.domainCreateReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.domainCreateReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} +static int qemudDispatchDomainLookupByID(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainLookupByIDRequest)) + return -1; + + struct qemud_vm *vm = qemudFindVMByID(server, in->data.domainLookupByIDRequest.id); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_ID; + out->header.dataSize = sizeof(out->data.domainLookupByIDReply); + memcpy(out->data.domainLookupByIDReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.domainLookupByIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainLookupByIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} +static int qemudDispatchDomainLookupByUUID(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainLookupByUUIDRequest)) + return -1; + + struct qemud_vm *vm = qemudFindVMByUUID(server, in->data.domainLookupByUUIDRequest.uuid); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID; + out->header.dataSize = sizeof(out->data.domainLookupByUUIDReply); + out->data.domainLookupByUUIDReply.id = vm->def.id; + strncpy(out->data.domainLookupByUUIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} +static int qemudDispatchDomainLookupByName(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainLookupByNameRequest)) + return -1; + + /* Paranoia NULL termination */ + in->data.domainLookupByNameRequest.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + struct qemud_vm *vm = qemudFindVMByName(server, in->data.domainLookupByNameRequest.name); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME; + out->header.dataSize = sizeof(out->data.domainLookupByNameReply); + out->data.domainLookupByNameReply.id = vm->def.id; + memcpy(out->data.domainLookupByNameReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + } + return 0; +} +static int qemudDispatchDomainSuspend(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainSuspendRequest)) + return -1; + + int ret = qemudDomainSuspend(server, in->data.domainSuspendRequest.id); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_SUSPEND; + out->header.dataSize = 0; + } + return 0; +} +static int qemudDispatchDomainResume(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainResumeRequest)) + return -1; + + int ret = qemudDomainResume(server, in->data.domainResumeRequest.id); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_RESUME; + out->header.dataSize =0; + } + return 0; +} +static int qemudDispatchDomainDestroy(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainDestroyRequest)) + return -1; + + int ret = qemudDomainDestroy(server, in->data.domainDestroyRequest.id); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_DESTROY; + out->header.dataSize = 0; + } + return 0; +} +static int qemudDispatchDomainGetInfo(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainGetInfoRequest)) + return -1; + + int ret = qemudDomainGetInfo(server, in->data.domainGetInfoRequest.uuid, + &out->data.domainGetInfoReply.runstate, + &out->data.domainGetInfoReply.cpuTime, + &out->data.domainGetInfoReply.memory, + &out->data.domainGetInfoReply.nrVirtCpu); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_GET_INFO; + out->header.dataSize = sizeof(out->data.domainGetInfoReply); + } + return 0; +} +static int qemudDispatchDomainSave(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainSaveRequest)) + return -1; + + /* Paranoia NULL termination */ + in->data.domainSaveRequest.file[PATH_MAX-1] ='\0'; + + int ret = qemudDomainSave(server, + in->data.domainSaveRequest.id, + in->data.domainSaveRequest.file); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_SAVE; + out->header.dataSize = 0; + } + return 0; +} +static int qemudDispatchDomainRestore(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainRestoreRequest)) + return -1; + + /* Paranoia null termination */ + in->data.domainRestoreRequest.file[PATH_MAX-1] ='\0'; + + int id = qemudDomainRestore(server, in->data.domainRestoreRequest.file); + if (id < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_RESTORE; + out->header.dataSize = sizeof(out->data.domainRestoreReply); + out->data.domainRestoreReply.id = id; + } + return 0; +} +static int qemudDispatchDumpXML(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainDumpXMLRequest)) + return -1; + + int ret = qemudDomainDumpXML(server, in->data.domainDumpXMLRequest.uuid, + out->data.domainDumpXMLReply.xml, QEMUD_MAX_XML_LEN); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DUMP_XML; + out->header.dataSize = sizeof(out->data.domainDumpXMLReply); + } + return 0; +} +static int qemudDispatchListDefinedDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + char **names; + int i; + if (in->header.dataSize != 0) + return -1; + + if (!(names = malloc(sizeof(char *)*QEMUD_MAX_NUM_DOMAINS))) + return -1; + + for (i = 0 ; i < QEMUD_MAX_NUM_DOMAINS ; i++) { + names[i] = out->data.listDefinedDomainsReply.domains[i]; + } + + int ndomains = qemudListDefinedDomains(server, + names, + QEMUD_MAX_NUM_DOMAINS); + free(names); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_LIST_DEFINED_DOMAINS; + out->header.dataSize = sizeof(out->data.listDefinedDomainsReply); + out->data.listDefinedDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchNumDefinedDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int ndomains = qemudNumDefinedDomains(server); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS; + out->header.dataSize = sizeof(out->data.numDefinedDomainsReply); + out->data.numDefinedDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchDomainStart(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainStartRequest)) + return -1; + + struct qemud_vm *vm = qemudFindVMByUUID(server, in->data.domainStartRequest.uuid); + if (!vm || qemudDomainStart(server, vm) < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_START; + out->header.dataSize = sizeof(out->data.domainStartReply); + out->data.domainStartReply.id = vm->def.id; + } + return 0; +} +static int qemudDispatchDomainDefine(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainDefineRequest)) + return -1; + + in->data.domainDefineRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0'; + + struct qemud_vm *vm = qemudDomainDefine(server, in->data.domainDefineRequest.xml); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_DEFINE; + out->header.dataSize = sizeof(out->data.domainDefineReply); + memcpy(out->data.domainDefineReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.domainDefineReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} +static int qemudDispatchDomainUndefine(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainUndefineRequest)) + return -1; + + int ret = qemudDomainUndefine(server, in->data.domainUndefineRequest.uuid); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_UNDEFINE; + out->header.dataSize = 0; + } + return 0; +} + + +typedef int (*clientFunc)(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out); + + +/* One per message type recorded in qemud_packet_type enum */ +clientFunc funcs[] = { + NULL, /* FAILURE code */ + qemudDispatchGetProtocolVersion, + qemudDispatchGetVersion, + qemudDispatchListDomains, + qemudDispatchNumDomains, + qemudDispatchDomainCreate, + qemudDispatchDomainLookupByID, + qemudDispatchDomainLookupByUUID, + qemudDispatchDomainLookupByName, + qemudDispatchDomainSuspend, + qemudDispatchDomainResume, + qemudDispatchDomainDestroy, + qemudDispatchDomainGetInfo, + qemudDispatchDomainSave, + qemudDispatchDomainRestore, + qemudDispatchDumpXML, + qemudDispatchListDefinedDomains, + qemudDispatchNumDefinedDomains, + qemudDispatchDomainStart, + qemudDispatchDomainDefine, + qemudDispatchDomainUndefine +}; + +/* + * Returns -1 if message processing failed - eg, illegal header sizes, + * a memory error dealing with stuff, or any other bad stuff which + * should trigger immediate client disconnect, with extreme prejudice + * + * Return 0 if message processing succeeded. NB, this does not mean + * the operation itself succeeded - success/failure of the operation + * is recorded by the return message type - either it matches the + * incoming type, or is QEMUD_PKT_FAILURE + */ +int qemudDispatch(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { +#if DEBUG + printf("> Dispatching request %d\n", in->header.type); +#endif + + memset(out, 0, sizeof(struct qemud_packet)); + + if (in->header.type >= (sizeof(funcs)/sizeof(clientFunc))) { +#if DEBUG + printf("Illegal request type\n"); +#endif + return -1; + } + + if (in->header.type == QEMUD_PKT_FAILURE) { +#if DEBUG + printf("Illegal request type\n"); +#endif + return -1; + } + + if ((funcs[in->header.type])(server, client, in, out) < 0) { +#if DEBUG + printf("Dispatch failed\n"); +#endif + return -1; + } + +#if DEBUG + printf("< Returning reply %d (%d bytes)\n", + out->header.type, out->header.dataSize); +#endif + + return 0; +} diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/dispatch.h libvirt-qemu/qemud/dispatch.h --- libvirt-pristine/qemud/dispatch.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/dispatch.h 2006-08-27 17:05:39.000000000 -0400 @@ -0,0 +1,11 @@ + +#ifndef QEMUD_DISPATCH_H +#define QEMUD_DISPATCH_H + +#include "internal.h" + + +int qemudDispatch(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out); + +#endif diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/driver.c libvirt-qemu/qemud/driver.c --- libvirt-pristine/qemud/driver.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/driver.c 2006-08-30 21:28:47.000000000 -0400 @@ -0,0 +1,284 @@ + +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> +#include <string.h> +#include <stdio.h> + +#include "internal.h" +#include "driver.h" +#include "config.h" + +struct qemud_vm *qemudFindVMByID(const struct qemud_server *server, int id) { + struct qemud_vm *vm = server->activevms; + + while (vm) { + if (vm->def.id == id) + return vm; + vm = vm->next; + } + + qemudSetError(QEMUD_ERR_NO_DOMAIN, "no domain matching id"); + return NULL; +} + +struct qemud_vm *qemudFindVMByUUID(const struct qemud_server *server, + const unsigned char *uuid) { + struct qemud_vm *vm = server->activevms; + + while (vm) { + if (!memcmp(vm->def.uuid, uuid, QEMUD_UUID_RAW_LEN)) + return vm; + vm = vm->next; + } + + vm = server->inactivevms; + while (vm) { + if (!memcmp(vm->def.uuid, uuid, QEMUD_UUID_RAW_LEN)) + return vm; + vm = vm->next; + } + + qemudSetError(QEMUD_ERR_NO_DOMAIN, "no domain matching uuid"); + return NULL; +} + +struct qemud_vm *qemudFindVMByName(const struct qemud_server *server, + const char *name) { + struct qemud_vm *vm = server->activevms; + + while (vm) { + if (!strcmp(vm->def.name, name)) + return vm; + vm = vm->next; + } + + vm = server->inactivevms; + while (vm) { + if (!strcmp(vm->def.name, name)) + return vm; + vm = vm->next; + } + + qemudSetError(QEMUD_ERR_NO_DOMAIN, "no domain matching name"); + return NULL; +} + +int qemudGetVersion(struct qemud_server *server) { + return server->qemuVersion; +} + +int qemudListDomains(struct qemud_server *server, int *ids, int nids) { + struct qemud_vm *vm = server->activevms; + int got = 0; + while (vm && got < nids) { + ids[got] = vm->def.id; + vm = vm->next; + got++; + } + return got; +} +int qemudNumDomains(struct qemud_server *server) { + return server->nactivevms; +} +struct qemud_vm *qemudDomainCreate(struct qemud_server *server, const char *xml) { + struct qemud_vm *vm; + + if (!(vm = qemudLoadConfigXML(server, NULL, xml, 0))) { + return NULL; + } + + if (qemudStartVMDaemon(server, vm) < 0) { + qemudFreeVM(vm); + return NULL; + } + + vm->next = server->activevms; + server->activevms = vm; + server->nactivevms++; + server->nvmfds += 2; + + return vm; +} + +int qemudDomainSuspend(struct qemud_server *server, int id) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) + return -1; + if (vm->pid == -1) { + qemudSetError(QEMUD_ERR_DOMAIN_INACTIVE, "domain is not active"); + return -1; + } + return -1; +} + +int qemudDomainResume(struct qemud_server *server, int id) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) + return -1; + if (vm->pid == -1) { + qemudSetError(QEMUD_ERR_DOMAIN_INACTIVE, "domain is not active"); + return -1; + } + return -1; +} + +int qemudDomainDestroy(struct qemud_server *server, int id) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) + return -1; + if (vm->pid == -1) { + qemudSetError(QEMUD_ERR_DOMAIN_INACTIVE, "domain is not active"); + return -1; + } + + if (qemudShutdownVMDaemon(server, vm) < 0) + return -1; + return 0; +} + +int qemudDomainGetInfo(struct qemud_server *server, const unsigned char *uuid, + int *runstate, + unsigned long long *cputime, + unsigned long *memory, + unsigned int *nrVirtCpu) { + struct qemud_vm *vm = qemudFindVMByUUID(server, uuid); + if (!vm) + return -1; + + if (vm->pid == -1) { + *runstate = QEMUD_STATE_STOPPED; + } else { + /* XXX in future need to add PAUSED */ + *runstate = QEMUD_STATE_RUNNING; + } + + *cputime = 0; + *memory = vm->def.memory; + *nrVirtCpu = vm->def.vcpus; + return 0; +} +int qemudDomainSave(struct qemud_server *server, int id, + const char *path ATTRIBUTE_UNUSED) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) + return -1; + if (vm->pid == -1) { + qemudSetError(QEMUD_ERR_DOMAIN_INACTIVE, "domain is not active"); + return -1; + } + return -1; +} +int qemudDomainRestore(struct qemud_server *server ATTRIBUTE_UNUSED, + const char *path ATTRIBUTE_UNUSED) { + + return -1; +} +int qemudDomainDumpXML(struct qemud_server *server, const unsigned char *uuid, char *xml, int xmllen) { + struct qemud_vm *vm = qemudFindVMByUUID(server, uuid); + char *vmxml; + if (!vm) + return -1; + + vmxml = qemudGenerateXML(vm); + if (!vmxml) + return -1; + + strncpy(xml, vmxml, xmllen); + xml[xmllen-1] = '\0'; + + return 0; +} +int qemudListDefinedDomains(struct qemud_server *server, char *const*names, int nnames) { + struct qemud_vm *vm = server->inactivevms; + int got = 0; + while (vm && got < nnames) { + strncpy(names[got], vm->def.name, QEMUD_MAX_NAME_LEN-1); + names[got][QEMUD_MAX_NAME_LEN-1] = '\0'; + vm = vm->next; + got++; + } + return got; +} +int qemudNumDefinedDomains(struct qemud_server *server) { + return server->ninactivevms; +} +int qemudDomainStart(struct qemud_server *server, struct qemud_vm *vm) { + struct qemud_vm *prev = NULL, *curr = server->inactivevms; + + if (vm->pid != -1) { + qemudSetError(QEMUD_ERR_DOMAIN_INACTIVE, "domain is still active"); + return -1; + } + + if (qemudStartVMDaemon(server, vm) < 0) { + return -1; + } + + while (curr) { + if (curr == vm) { + if (prev) + prev->next = curr->next; + else + server->inactivevms = curr->next; + server->ninactivevms--; + break; + } + prev = curr; + curr = curr->next; + } + + vm->next = server->activevms; + server->activevms = vm; + server->nactivevms++; + server->nvmfds += 2; + + return 0; +} + +struct qemud_vm *qemudDomainDefine(struct qemud_server *server, const char *xml) { + struct qemud_vm *vm; + + if (!(vm = qemudLoadConfigXML(server, NULL, xml, 1))) { + return NULL; + } + + vm->next = server->inactivevms; + server->inactivevms = vm; + server->ninactivevms++; + + return vm; +} + +int qemudDomainUndefine(struct qemud_server *server, const unsigned char *uuid) { + struct qemud_vm *vm = qemudFindVMByUUID(server, uuid); + struct qemud_vm *prev = NULL, *curr = server->inactivevms; + + if (!vm) + return -1; + + if (vm->pid != -1) + return -1; + + if (qemudDeleteConfigXML(vm) < 0) + return -1; + + while (curr) { + if (curr == vm) { + if (prev) { + prev->next = curr->next; + } else { + server->inactivevms = curr->next; + } + server->ninactivevms--; + break; + } + + prev = curr; + curr = curr->next; + } + + qemudFreeVM(vm); + + return 0; +} diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/driver.h libvirt-qemu/qemud/driver.h --- libvirt-pristine/qemud/driver.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/driver.h 2006-08-27 17:05:39.000000000 -0400 @@ -0,0 +1,53 @@ + +#ifndef QEMUD_DRIVER_H +#define QEMUD_DRIVER_H + +#include "internal.h" + +struct qemud_vm *qemudFindVMByID(const struct qemud_server *server, + int id); +struct qemud_vm *qemudFindVMByUUID(const struct qemud_server *server, + const unsigned char *uuid); +struct qemud_vm *qemudFindVMByName(const struct qemud_server *server, + const char *name); + +int qemudGetVersion(struct qemud_server *server); +int qemudListDomains(struct qemud_server *server, + int *ids, + int nids); +int qemudNumDomains(struct qemud_server *server); +struct qemud_vm *qemudDomainCreate(struct qemud_server *server, + const char *xml); +int qemudDomainSuspend(struct qemud_server *server, + int id); +int qemudDomainResume(struct qemud_server *server, + int id); +int qemudDomainDestroy(struct qemud_server *server, + int id); +int qemudDomainGetInfo(struct qemud_server *server, + const unsigned char *uuid, + int *runstate, + unsigned long long *cputime, + unsigned long *memory, + unsigned int *nrVirtCpu); +int qemudDomainSave(struct qemud_server *server, + int id, + const char *path); +int qemudDomainRestore(struct qemud_server *server, + const char *path); +int qemudDomainDumpXML(struct qemud_server *server, + const unsigned char *uuid, + char *xml, + int xmllen); +int qemudListDefinedDomains(struct qemud_server *server, + char *const*names, + int nnames); +int qemudNumDefinedDomains(struct qemud_server *server); +int qemudDomainStart(struct qemud_server *server, + struct qemud_vm *vm); +struct qemud_vm *qemudDomainDefine(struct qemud_server *server, + const char *xml); +int qemudDomainUndefine(struct qemud_server *server, + const unsigned char *uuid); + +#endif diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/error.h libvirt-qemu/qemud/error.h --- libvirt-pristine/qemud/error.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/error.h 2006-08-30 19:15:53.000000000 -0400 @@ -0,0 +1 @@ + diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/internal.h libvirt-qemu/qemud/internal.h --- libvirt-pristine/qemud/internal.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/internal.h 2006-08-30 20:00:37.000000000 -0400 @@ -0,0 +1,202 @@ + +#ifndef QEMUD_INTERNAL_H__ +#define QEMUD_INTERNAL_H__ + +#include <sys/socket.h> +#include <netinet/in.h> + +#include "protocol.h" + +#ifdef __GNUC__ +#ifdef HAVE_ANSIDECL_H +#include <ansidecl.h> +#endif +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#endif +#else +#define ATTRIBUTE_UNUSED +#endif + +#ifndef DEBUG +#define DEBUG 0 +#endif + +#define UUID_LEN 16 + +struct qemud_client { + /* The socket to the client */ + int fd; + /* 1 if this client should be restricted to readonly ops only */ + int readonly; + /* If this is 0 we are processing next incoming packet, + incomingReceived gives how much we've got so far. Expected + amount is header size + data size listed in header; + If this is 1 we are sending back a reply - and not interested + in any further incoming data until we've finished sending. + outgoingSent is how much we've dispatched so far. + Again total send size is header + data size listed in header */ + int tx; + struct qemud_packet incoming; + unsigned int incomingReceived; + struct qemud_packet outgoing; + unsigned int outgoingSent; + /* Next client connection, or NULL */ + struct qemud_client *next; +}; + +/* Two difference types of disk backend storage supported */ +enum qmeud_vm_disk_type { + QEMUD_DISK_BLOCK, + QEMUD_DISK_FILE +}; + +/* Three different types of emulated device */ +enum qemud_vm_disk_device { + QEMUD_DISK_DISK, + QEMUD_DISK_CDROM, + QEMUD_DISK_FLOPPY, +}; + +struct qemud_vm_disk_def { + int type; /* One of qemud_vm_disk_type */ + int device; /* One of qemud_vm_disk_device */ + char src[PATH_MAX]; /* Path to backend storage in host */ + char dst[NAME_MAX]; /* Name of device within guest */ + int readonly; /* 1 if the dev is to be readonly */ + + /* Next disk device, or NULL */ + struct qemud_vm_disk_def *next; +}; + +#define QEMUD_MAC_ADDRESS_LEN 6 + +/* QEMU has a whole tonne of different network setups */ +enum qemud_vm_net_type { + QEMUD_NET_USER, + QEMUD_NET_TAP, + QEMUD_NET_SERVER, + QEMUD_NET_CLIENT, + QEMUD_NET_MCAST, + QEMUD_NET_VDE +}; + +struct qemud_vm_net_def { + int type; /* One of qemud_vm_net_type */ + int vlan; /* VLAN number */ + unsigned char mac[QEMUD_MAC_ADDRESS_LEN]; /* Mac address */ + union { + struct { /* If type == QEMUD_NET_TAP */ + char ifname[NAME_MAX]; + char script[PATH_MAX]; + } tap; + struct { /* If type == QEMUD_NET_SERVER */ + struct sockaddr_in listen; + int port; + } server; + struct { /* If type == QEMUD_NET_CLIENT */ + struct sockaddr_in connect; + int port; + } client; + struct { /* If type == QEMUD_NET_MCAST */ + struct sockaddr_in group; + int port; + } mcast; + struct { /* If type == QEMUD_NET_VDE */ + char vlan[PATH_MAX]; + } vde; + } dst; + + /* Next network device, or NULL */ + struct qemud_vm_net_def *next; +}; + +enum qemud_vm_boot_order { + QEMUD_BOOT_FLOPPY, + QEMUD_BOOT_CDROM, + QEMUD_BOOT_DISK +}; + +enum qemud_vm_graphics_type { + QEMUD_GRAPHICS_NONE, + QEMUD_GRAPHICS_VNC +}; + +struct qemud_vm_def { + int id; /* Unique identifier for lifetime of qemud process */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; /* Globally unique ID */ + char name[QEMUD_MAX_NAME_LEN]; /* A unique name for VM on this host */ + + int memory; /* Memory in KB */ + int vcpus; /* Number of virtual CPUs - no tech limit; + effective limit according to guest OS scalability */ + + int bootOrder[3]; /* One of qemud_vm_boot_order */ + + int graphicsType; /* One of qemud_vm_graphics_types */ + int vncPort; /* TCP port number if graphicsType == QEMUD_GRAPHICS_VNC */ + + int ndisks; /* Number of disks configured */ + struct qemud_vm_disk_def *disks; /* Pointer to first disk in linked list */ + + int nnets; /* Number of net devs configured */ + struct qemud_vm_net_def *nets; /* Pointer to first net dev in linked list */ +}; + +struct qemud_vm { + int stdout; /* Read end of pipe attached to qemu's stdout; -1 if inactive */ + int stderr; /* Read end of pipe attached to qemu's stderr; -1 if inactive */ + int monitor; /* Pipe to monitor TTy; -1 if not active */ + int pid; /* PID of qemu child processes; -1 if inactive */ + + char configFile[PATH_MAX]; /* Path to persistent config file. '' if transient */ + + struct qemud_vm_def def; /* Hardware definition */ + + struct qemud_vm *next; /* Next VM, or NULL */ +}; + +struct qemud_server { + int fd; /* Handle for server socket to libvirt */ + int qemuVersion; /* QEMU version number encoded (major *1000*1000)+(minor*1000)+micro */ + int nclients; /* Total number of clients */ + struct qemud_client *clients; /* First client in linked list */ + int nvmfds; /* Number of active file handles associated with VMs */ + int nactivevms; /* Number of active VMs */ + struct qemud_vm *activevms; /* First active vm in linked list */ + int ninactivevms; /* Number of inactive VMs */ + struct qemud_vm *inactivevms; /* First inactive VM in linked list */ + int nextvmid; /* Next unique VM id to be allocated */ + char configDir[PATH_MAX]; /* Directory storing VM config files */ +}; + +/* Start an inactive VM + * Returns 0 on success; -1 on failure + */ +int qemudStartVMDaemon(struct qemud_server *server, + struct qemud_vm *vm); + +/* Shutdown an active VM + * Returns 0 on success; -1 on failure + */ +int qemudShutdownVMDaemon(struct qemud_server *server, + struct qemud_vm *vm); + +/* Set an error message, + * code: one of qemud_error_code + * msg: string description of problem + */ +void qemudSetError(int code, + const char *msg); + +/* Get last error code + * msg: pointer to location to receive error msg (read only) + * Returns error code, or 0 if no current erro + */ +int qemudGetLastError(const char **msg); + +/* Clears the current error code + */ +void qemudClearLastError(void); + +#endif diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/Makefile.am libvirt-qemu/qemud/Makefile.am --- libvirt-pristine/qemud/Makefile.am 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/Makefile.am 2006-08-27 17:54:39.000000000 -0400 @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = @LIBXML_CFLAGS@ + +libexec_PROGRAMS = qemud + +qemud_SOURCES = qemud.c internal.h protocol.h \ + driver.c driver.h \ + dispatch.c dispatch.h \ + config.c config.h +qemud_CFLAGS = -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L -std=c99 \ + -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \ + -Werror -Wall -Wextra +qemud_LDFLAGS = $(LIBXML_LIBS) +qemud_DEPENDENCIES = +qemud_LDADD = + diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/protocol.h libvirt-qemu/qemud/protocol.h --- libvirt-pristine/qemud/protocol.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/protocol.h 2006-08-30 22:57:44.000000000 -0400 @@ -0,0 +1,203 @@ + +#ifndef QEMUD_PROTOCOL_H__ +#define QEMUD_PROTOCOL_H__ + +/* The set of message types allowed on wire */ +enum qemud_packet_type { + QEMUD_PKT_FAILURE, + QEMUD_PKT_GET_PROTOCOL_VERSION, + QEMUD_PKT_GET_VERSION, + QEMUD_PKT_LIST_DOMAINS, + QEMUD_PKT_NUM_DOMAINS, + QEMUD_PKT_DOMAIN_CREATE, + QEMUD_PKT_DOMAIN_LOOKUP_BY_ID, + QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID, + QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME, + QEMUD_PKT_DOMAIN_SUSPEND, + QEMUD_PKT_DOMAIN_RESUME, + QEMUD_PKT_DOMAIN_DESTROY, + QEMUD_PKT_DOMAIN_GET_INFO, + QEMUD_PKT_DOMAIN_SAVE, + QEMUD_PKT_DOMAIN_RESTORE, + QEMUD_PKT_DUMP_XML, + QEMUD_PKT_LIST_DEFINED_DOMAINS, + QEMUD_PKT_NUM_DEFINED_DOMAINS, + QEMUD_PKT_DOMAIN_START, + QEMUD_PKT_DOMAIN_DEFINE, + QEMUD_PKT_DOMAIN_UNDEFINE, + QEMUD_PKT_MAX +}; + +/* Error conditions which can be returned */ +enum qemud_error { + QEMUD_ERR_GENERIC = 1, + QEMUD_ERR_NO_MEMORY, + QEMUD_ERR_NO_DOMAIN, + QEMUD_ERR_READONLY, + QEMUD_ERR_DOMAIN_INACTIVE, + QEMUD_ERR_DOMAIN_ACTIVE, + QEMUD_ERR_CONFIG_MALFORMED, + QEMUD_ERR_DUPLICATE_DOMAIN, + QEMUD_ERR_PATH_OVERFLOW, + QEMUD_ERR_MALFORMED_UUID, + QEMUD_ERR_ILLEGAL_DISK_CONFIG, + QEMUD_ERR_XML_NO_ROOT, + QEMUD_ERR_XML_WRONG_TYPE, + QEMUD_ERR_XML_MISSING_NAME, + QEMUD_ERR_XML_MISSING_UUID, + QEMUD_ERR_NAME_OVERFLOW, +}; + +/* If we break protocol compatability, this is + * going to be increased */ +#define QEMUD_PROTOCOL_VERSION 1 + +/* Max lengths for various arrays - this length + * includes the trailing NULL, if appropriate + */ +#define QEMUD_UUID_RAW_LEN 16 +#define QEMUD_MAX_NAME_LEN 50 +#define QEMUD_MAX_XML_LEN 4096 +#define QEMUD_MAX_ERROR_LEN 1024 +#define QEMUD_MAX_NUM_DOMAINS 100 + +/* Domain runtime state - may expand over time */ +enum qemud_domain_runstate { + QEMUD_STATE_RUNNING = 1, + QEMUD_STATE_PAUSED, + QEMUD_STATE_STOPPED, +}; + + +struct qemud_packet_header { + /* One of the qemud_packet_type constants */ + unsigned int type; + /* Stores the size of the data struct matching + the type arg. The exact size is dependnant + on the memory the union qemudPacketData which + matches the type field above. + Must be <= sizeof(union qemudPacketData) */ + unsigned int dataSize; +}; + +/* Most qemud_packet_type constants have both a + * XXXRequest & XXXReply field - but some have + * either no params, or no reply. That said, there + * is always possibility of a failureReply for any + * of the request types */ +union qemud_packet_data { + struct { /* QEMUD_PKT_FAILURE */ + int code; + char message[QEMUD_MAX_ERROR_LEN]; + } failureReply; + struct { /* QEMUD_PKT_GET_PROTOCOL_VERSION */ + int version; + } getProtocolVersionReply; + struct { /* QEMUD_PKT_GET_VERSION */ + int version; + } getVersionReply; + struct { /* QEMUD_PKT_LIST_DOMAINS */ + int numDomains; + int domains[QEMUD_MAX_NUM_DOMAINS]; + } listDomainsReply; + struct { /* QEMUD_PKT_NUM_DOMAINS */ + int numDomains; + } numDomainsReply; + struct { /* QEMUD_PKT_DOMAIN_CREATE */ + char xml[QEMUD_MAX_XML_LEN]; + } domainCreateRequest; + struct { /* QEMUD_PKT_DOMAIN_CREATE */ + int id; + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } domainCreateReply; + struct { /* QEMUD_PKT_DOMAIN_LOOKUP_BY_ID */ + int id; + } domainLookupByIDRequest; + struct { /* QEMUD_PKT_DOMAIN_LOOKUP_BY_ID */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } domainLookupByIDReply; + struct { /* QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME */ + char name[QEMUD_MAX_NAME_LEN]; + } domainLookupByNameRequest; + struct { /* QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME */ + int id; + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainLookupByNameReply; + struct { /* QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainLookupByUUIDRequest; + struct { /* QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID */ + int id; + char name[QEMUD_MAX_NAME_LEN]; + } domainLookupByUUIDReply; + struct { /* QEMUD_PKT_DOMAIN_SUSPEND */ + int id; + } domainSuspendRequest; + struct { /* QEMUD_PKT_DOMAIN_RESUME */ + int id; + } domainResumeRequest; + struct { /* QEMUD_PKT_DOMAIN_DESTROY */ + int id; + } domainDestroyRequest; + struct { /* QEMUD_PKT_DOMAIN_GET_INFO */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainGetInfoRequest; + struct { /* QEMUD_PKT_DOMAIN_GET_INFO */ + int runstate; + unsigned long long cpuTime; + unsigned long memory; + unsigned int nrVirtCpu; + } domainGetInfoReply; + struct { /* QEMUD_PKT_DOMAIN_SAVE */ + int id; + char file[PATH_MAX]; + } domainSaveRequest; + struct { /* QEMUD_PKT_DOMAIN_RESTORE */ + char file[PATH_MAX]; + } domainRestoreRequest; + struct { /* QEMUD_PKT_DOMAIN_RESTORE */ + int id; + } domainRestoreReply; + struct { /* QEMUD_PKT_DUMP_XML */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainDumpXMLRequest; + struct { /* QEMUD_PKT_DUMP_XML */ + char xml[QEMUD_MAX_XML_LEN]; + } domainDumpXMLReply; + struct { /* QEMUD_PKT_LIST_DEFINED_DOMAINS */ + int numDomains; + char domains[QEMUD_MAX_NUM_DOMAINS][QEMUD_MAX_NAME_LEN]; + } listDefinedDomainsReply; + struct { /* QEMUD_PKT_NUM_DEFINED_DOMAINS */ + int numDomains; + } numDefinedDomainsReply; + struct { /* QEMUD_PKT_DOMAIN_START */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainStartRequest; + struct { /* QEMUD_PKT_DOMAIN_START */ + int id; + } domainStartReply; + struct { /* QEMUD_PKT_DOMAIN_DEFINE */ + char xml[QEMUD_MAX_XML_LEN]; + } domainDefineRequest; + struct { /* QEMUD_PKT_DOMAIN_DEFINE */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } domainDefineReply; + struct { /* QEMUD_PKT_DOMAIN_UNDEFINE */ + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainUndefineRequest; +}; + +/* A single packet on the wire contains header & data. + * header is always fixed size. data is the size of the + * particular union member matching header->type */ +struct qemud_packet { + struct qemud_packet_header header; + union qemud_packet_data data; +}; + + +#endif diff --exclude docs --exclude CVS -ruN libvirt-pristine/qemud/qemud.c libvirt-qemu/qemud/qemud.c --- libvirt-pristine/qemud/qemud.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/qemud.c 2006-08-30 22:06:42.000000000 -0400 @@ -0,0 +1,795 @@ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <paths.h> +#include <limits.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <stdlib.h> +#include <pwd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "internal.h" +#include "dispatch.h" +#include "config.h" + +static void reapchild(int sig ATTRIBUTE_UNUSED) { + /* We explicitly waitpid the child later */ +} + + +static int errorCode = 0; +static char *errorMsg = NULL; + +void qemudSetError(int code, + const char *msg) { + qemudClearLastError(); + errorCode = code; + errorMsg = msg ? strdup(msg) : NULL; +} + +int qemudGetLastError(const char **msg) { + if (msg) + *msg = errorMsg; + return errorCode; +} + +void qemudClearLastError(void) { + if (errorMsg) { + free(errorMsg); + errorMsg = NULL; + } + errorCode = 0; +} + + +/* Enables the CLOSE-ON-EXEC flag for the passed in + file handle */ +static int qemudSetCloseExec(int fd) { + int flags; + if ((flags = fcntl(fd, F_GETFD)) < 0) { + return -1; + } + flags |= FD_CLOEXEC; + if ((fcntl(fd, F_SETFD, flags)) < 0) { + return -1; + } + return 0; +} + + +/* Enables non blocking IO operation for the passed + in file handle */ +static int qemudSetNonBlock(int fd) { + int flags; + if ((flags = fcntl(fd, F_GETFL)) < 0) { + return -1; + } + flags |= O_NONBLOCK; + if ((fcntl(fd, F_SETFL, flags)) < 0) { + return -1; + } + return 0; +} + + +/* Does the traiditional double-fork() routine + to go daemon & dis-inherit terminal & IO + streams */ +static int qemudGoDaemon(void) { + int pid = fork(); + switch (pid) { + case 0: + { + int stdinfd = -1; + int stdoutfd = -1; + if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) + goto cleanup; + if ((stdoutfd = open(_PATH_DEVNULL, O_WRONLY)) < 0) + goto cleanup; + if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO) + goto cleanup; + if (close(stdinfd) < 0) + goto cleanup; + stdinfd = -1; + if (close(stdoutfd) < 0) + goto cleanup; + stdoutfd = -1; + + int open_max = sysconf (_SC_OPEN_MAX); + for (int i = 0; i < open_max; i++) + if (i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO) + close(i); + + if (setsid() < 0) + goto cleanup; + + int nextpid = fork(); + switch (nextpid) { + case 0: + return 0; + case -1: + return -1; + default: + return nextpid; + } + + cleanup: + if (stdoutfd != -1) + close(stdoutfd); + if (stdinfd != -1) + close(stdinfd); + return -1; + + } + + case -1: + return -1; + + default: + { + int got, status = 0; + /* We wait to make sure the next child forked + successfully */ + if ((got = waitpid(pid, &status, 0)) < 0 || + got != pid || + status != 0) { + return -1; + } + + return pid; + } + } +} + + +/* General startup / initialization bits & pieces */ +static struct qemud_server *qemudInitialize(void) { + struct qemud_server *server; + struct passwd *pw; + int uid, ret; + char path[PATH_MAX]; + struct sockaddr_un addr; + + if (!(server = calloc(1, sizeof(struct qemud_server)))) + return NULL; + + server->fd = -1; + /* XXX extract actual lversion */ + server->qemuVersion = (0*1000000)+(8*1000)+(0); + /* We don't have a dom-0, so start from 1 */ + server->nextvmid = 1; + + /* First off, figure out out socket path based on user + home directory */ + if ((uid = geteuid()) < 0) { + goto cleanup; + } + if (!(pw = getpwuid(uid))) { + goto cleanup; + } + + if ((ret = snprintf(path, PATH_MAX, "%s/.qemud", pw->pw_dir)) > PATH_MAX) { + goto cleanup; + } + + if ((ret = snprintf(server->configDir, PATH_MAX, "%s/.qemud.d", pw->pw_dir)) > PATH_MAX) { + goto cleanup; + } + + + /* Open & bind to the socket, in abstract namespace */ + if ((server->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + goto cleanup; + } + + if (qemudSetCloseExec(server->fd) < 0) + goto cleanup; + if (qemudSetNonBlock(server->fd) < 0) + goto cleanup; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + addr.sun_path[0] = '\0'; /* Abstract namespace */ + strncpy(&addr.sun_path[1], path, (sizeof(addr)-4)-2); + + if (bind(server->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + goto cleanup; + } + /* Only matching userid to connect to us for now */ + if (fchmod(server->fd, S_IRUSR | S_IWUSR) < 0) + goto cleanup; + + if (listen(server->fd, 30) < 0) { + goto cleanup; + } + + /* Load all pre-existing virtual machine configs */ + if (qemudScanConfigs(server) < 0) { + goto cleanup; + } + + return server; + + cleanup: + if (server->fd != -1) + close(server->fd); + if (server) + free(server); + return NULL; +} + + +/* Handles new incoming connection attempts. + * Accepts the connection & sets up a client + * state structure + */ +static int qemudDispatchServer(struct qemud_server *server) { + int fd; + struct sockaddr_un addr; + unsigned int addrlen = sizeof(addr); + struct qemud_client *client; + + if ((fd = accept(server->fd, (struct sockaddr *)&addr, &addrlen)) < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + + if (qemudSetCloseExec(fd) < 0) { + close(fd); + return -1; + } + + if (qemudSetNonBlock(fd) < 0) { + close(fd); + return -1; + } + + client = calloc(1, sizeof(struct qemud_client)); + client->fd = fd; + client->next = server->clients; + server->clients = client; + server->nclients++; + + return 0; +} + + +/* Starts an inactive virtual machine */ +int qemudStartVMDaemon(struct qemud_server *server, + struct qemud_vm *vm) { + char **argv = NULL; + int argc = 0; + int pid; + int i, ret = -1; + int pipeout[2] = {-1,-1}; + int pipeerr[2] = {-1,-1}; + + if (qemudBuildCommandLine(vm, &argv, &argc) < 0) + return -1; + + printf("Spawn QEMU '"); + for (i = 0 ; i < argc; i++) { + printf("%s", argv[i]); + if (i == (argc-1)) + printf("'\n"); + else + printf(" "); + } + + + if (pipe(pipeout) < 0) + goto cleanup; + if (qemudSetCloseExec(pipeout[0])) + goto cleanup; + + if (pipe(pipeerr) < 0) + goto cleanup; + if (qemudSetCloseExec(pipeerr[0])) + goto cleanup; + + if ((pid = fork()) < 0) + goto cleanup; + + if (pid) { + close(pipeout[1]); + close(pipeerr[1]); + qemudSetNonBlock(pipeout[0]); + qemudSetNonBlock(pipeerr[0]); + vm->def.id = server->nextvmid++; + vm->pid = pid; + vm->stdout = pipeout[0]; + vm->stderr = pipeerr[0]; + } else { + int null; + /* Connect stdin to /dev/null */ + if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0) + _exit(1); + if (dup2(null, STDIN_FILENO) < 0) + _exit(1); + + /* Connect stdout/err to pipe */ + if (dup2(pipeout[1], STDOUT_FILENO) < 0) + _exit(1); + if (dup2(pipeerr[1], STDERR_FILENO) < 0) + _exit(1); + + int open_max = sysconf (_SC_OPEN_MAX); + for (i = 0; i < open_max; i++) + if (i != STDOUT_FILENO && + i != STDERR_FILENO && + i != STDIN_FILENO) + close(i); + + /* Exec qemu process */ + execvp(argv[0], argv); + + /* Should never get here, but just in case */ + _exit(1); + } + + ret = 0; + + cleanup: + + for (i = 0 ; i < argc ; i++) { + free(argv[i]); + } + free(argv); + + return ret; +} + + +/* If something went wrong with client, then drop its + connection with extreme prejudice */ +static void qemudDispatchClientFailure(struct qemud_server *server, struct qemud_client *client) { + struct qemud_client *tmp = server->clients; + struct qemud_client *prev = NULL; + while (tmp) { + if (tmp == client) { + if (prev == NULL) + server->clients = client->next; + else + prev->next = client->next; + server->nclients--; + break; + } + prev = tmp; + tmp = tmp->next; + } + close(client->fd); + free(client); +} + + +/* + * Disptach an incoming message from a client, and + * switch into transmit mode for sending the reply + */ +static int qemudDispatchClientRequest(struct qemud_server *server, struct qemud_client *client) { + if (qemudDispatch(server, + client, + &client->incoming, + &client->outgoing) < 0) { + return -1; + } + + client->outgoingSent = 0; + client->tx = 1; + client->incomingReceived = 0; + + return 0; +} + +/* Handle incoming client data, and possibly + * dispatch an incoming mesage if its complete + */ +static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) { + char *data = (char *)&client->incoming; + unsigned int got = client->incomingReceived; + int want; + int ret; + + restart: + /* Either calculate how much of packet body is outstanding */ + if (got >= sizeof(struct qemud_packet_header)) { + want = sizeof(struct qemud_packet_header) + client->incoming.header.dataSize - got; + } else { /* Or how much of header is outstanding */ + want = sizeof(struct qemud_packet_header) - got; + } + + /* Read as much as possible - we're non-blocking remember */ + if ((ret = read(client->fd, data+got, want)) <= 0) { + if (errno != EAGAIN) { + qemudDispatchClientFailure(server, client); + return; + } + return; + } + got += ret; + client->incomingReceived += ret; + + /* If we've finished header, move onto body */ + if (client->incomingReceived == sizeof(struct qemud_packet_header)) { + /* Client lied about dataSize, kill with prejudice */ + if (client->incoming.header.dataSize > sizeof(union qemud_packet_data)) { +#if DEBUG + printf("Bogus data size %u\n", client->incoming.header.dataSize); +#endif + qemudDispatchClientFailure(server, client); + return; + } + /* We reached omplete header size, go back & do body now */ + if (client->incoming.header.dataSize) { +#if DEBUG + printf("- Restarting recv to process body (%d bytes)\n", client->incoming.header.dataSize); +#endif + goto restart; + } + } + + /* If we've finished body too, then dispatch the request */ + if (ret == want) { + if (qemudDispatchClientRequest(server, client) < 0) + qemudDispatchClientFailure(server, client); + } +} + +/* Handles transmission of a reply to the client */ +static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) { + char *data = (char *)&client->outgoing; + int sent = client->outgoingSent; + int todo = sizeof(struct qemud_packet_header) + client->outgoing.header.dataSize - sent; + int ret; + + /* Write as much as possible - we're non-blocking so maybe not all */ + if ((ret = write(client->fd, data+sent, todo)) < 0) { + if (errno != EAGAIN) { + qemudDispatchClientFailure(server, client); + return; + } + return; + } + client->outgoingSent += ret; + + /* If we've sent entire packet, then switch back to receive mode */ + if (todo == ret) { + client->tx = 0; + } +} + + +/* Deals with incoming log data from qemu process on its stdout/err */ +static int qemudVMData(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_vm *vm, int fd) { + char buf[4096]; + if (vm->pid < 0) + return 0; + + for (;;) { + int ret = read(fd, buf, 4096); + if (ret < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + if (ret == 0) { + return 0; + } + /* Save it to a logfile */ + write(STDOUT_FILENO, "[", 1); + write(STDOUT_FILENO, buf, ret); + write(STDOUT_FILENO, "]", 1); + } +} + +/* Forceably kill of qemu, reading any outstanding log data */ +int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) { + struct qemud_vm *prev = NULL, *curr = server->activevms; + + /* Already cleanup */ + if (vm->pid < 0) + return 0; + +#if DEBUG + printf("Do VM cleanup\n"); +#endif + kill(vm->pid, SIGTERM); + + /* Move VM from active -> inactive list, + possibly discarding VM if it had no + config file backing store */ + while (curr) { + if (curr == vm) { + if (prev) { + prev->next = curr->next; + } else { + server->activevms = curr->next; + } + server->nactivevms--; + + if (vm->configFile[0]) { + curr->next = server->inactivevms; + server->inactivevms = curr; + server->ninactivevms++; + } + break; + } + prev = curr; + curr = curr->next; + } + + /* Suck in any outstanding stdout/err data */ + qemudVMData(server, vm, curr->stdout); + qemudVMData(server, vm, curr->stderr); + close(curr->stdout); + close(curr->stderr); + curr->stdout = -1; + curr->stderr = -1; + server->nvmfds -= 2; + + /* It ought to be dea by now */ + if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) { + /* But we may need to be really forceful */ + kill(vm->pid, SIGKILL); + if (waitpid(vm->pid, NULL, 0) != vm->pid) { + return -1; + } + } + + vm->pid = -1; + vm->def.id = -1; + + /* If no backing store, then just throw away this vm */ + if (!vm->configFile[0]) { + qemudFreeVM(vm); + } + + return 0; +} + +/* Handle incoming log data from QEMU stdout/err */ +static int qemudDispatchVMLog(struct qemud_server *server, struct qemud_vm *vm, int fd) { + if (qemudVMData(server, vm, fd) < 0) + if (qemudShutdownVMDaemon(server, vm) < 0) + return -1; + return 0; +} +/* Process monitor IO - TODO */ +static int qemudDispatchVMMonitor(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_vm *vm ATTRIBUTE_UNUSED) { + return -1; +} + +/* Handle error condition on IO streams */ +static int qemudDispatchVMFailure(struct qemud_server *server, struct qemud_vm *vm, + int fd ATTRIBUTE_UNUSED) { + if (qemudShutdownVMDaemon(server, vm) < 0) + return -1; + return 0; +} + + +/* Looks at poll revents and invokes appropriate dispatchers + * depending on which condiitons were set */ +static int qemudDispatchPoll(struct qemud_server *server, struct pollfd *fds) { + struct qemud_client *client = server->clients; + struct qemud_vm *vm = server->activevms; + int ret = 0; + int fd = 0; + /* Incoming client connection */ + if (fds[fd].revents) + if (qemudDispatchServer(server) < 0) + return -1; + fd++; + + /* Client data read/write */ + while (client) { + struct qemud_client *next = client->next; + if (fds[fd].revents) { + if (fds[fd].revents == POLLOUT) + qemudDispatchClientWrite(server, client); + else if (fds[fd].revents == POLLIN) + qemudDispatchClientRead(server, client); + else + qemudDispatchClientFailure(server, client); + } + fd++; + client = next; + } + + /* VM data read/write */ + while (vm) { + struct qemud_vm *next = vm->next; + int failed = 0, + stdoutfd = vm->stdout, + stderrfd = vm->stderr, + monitorfd = vm->monitor; + if (stdoutfd != -1) { + if (fds[fd].revents) { + if (fds[fd].revents == POLLIN) { + if (qemudDispatchVMLog(server, vm, fds[fd].fd) < 0) + failed = 1; + } else { + if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0) + failed = 1; + } + } + fd++; + } + if (stderrfd != -1) { + if (!failed) { + if (fds[fd].revents) { + if (fds[fd].revents == POLLIN) { + if (qemudDispatchVMLog(server, vm, fds[fd].fd) < 0) + failed = 1; + } else { + if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0) + failed = 1; + } + } + } + fd++; + } + if (monitorfd != -1) { + if (!failed) { + if (fds[fd].revents) { + if (fds[fd].revents == POLLIN) { + if (qemudDispatchVMMonitor(server, vm) < 0) + failed = 1; + } else { + if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0) + failed = 1; + } + } + } + fd++; + } + vm = next; + if (failed) + ret = -1; + } + return ret; +} + + +/* Fill up the poll data with any FD's which we + * want to monitor + */ +static void qemudPreparePoll(struct qemud_server *server, struct pollfd *fds) { + int fd = 0; + + fds[fd].fd = server->fd; + fds[fd].events = POLLIN; + fd++; + + for (struct qemud_client *client = server->clients ; client ; client = client->next) { + fds[fd].fd = client->fd; + /* Refuse to read more from client if tx is pending to + rate limit */ + if (client->tx) + fds[fd].events = POLLOUT | POLLERR | POLLHUP; + else + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + /* See if any active VMs have handles which need monitoring */ + for (struct qemud_vm *vm = server->activevms ; vm ; vm = vm->next) { + if (vm->stdout != -1) { + fds[fd].fd = vm->stdout; + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + if (vm->stderr != -1) { + fds[fd].fd = vm->stderr; + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + if (vm->monitor != -1) { + fds[fd].fd = vm->monitor; + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + } +} + + +/* One iteration of the poll event loop. Returns -1 + * if an error occurred, of the loop needs to be + * shutdown through lack ofactivity + */ +static int qemudOneLoop(struct qemud_server *server) { + int nfds = 1 + server->nclients + server->nvmfds; + struct pollfd fds[nfds]; + int timeout = -1; + int ret; + + /* If nothing happens for 30 secs, quit */ + if (!server->nclients && + !server->nactivevms) + timeout = 30; + + /* Determine which handles need monitoring */ + qemudPreparePoll(server, fds); + + retry: + + if ((ret = poll(fds, nfds, timeout * 1000)) < 0) { + if (errno == EINTR) { + goto retry; + } + return -1; + } + + /* We timeed out, so shutdown */ + if (ret == 0) + return -1; + + /* Dispatch events we were notified on */ + if (qemudDispatchPoll(server, fds) < 0) + return -1; + + + return 0; +} + + +/* Run the event loop for ever */ +static int qemudRunLoop(struct qemud_server *server) { + int ret; + + while ((ret = qemudOneLoop(server)) == 0) + ; + + return ret == -1 ? -1 : 0; +} + +/* Pre-quite cleanup */ +static void qemudCleanup(struct qemud_server *server) { + close(server->fd); + free(server); +} + + +int main(void) { + int godaemon = 0; + + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return 3; + if (signal(SIGCHLD, reapchild) == SIG_ERR) + return 3; + + /* XXX switch on command line flag */ + if (godaemon) { + int pid = qemudGoDaemon(); + if (pid < 0) + return 1; + if (pid > 0) + return 0; + } + + struct qemud_server *server = qemudInitialize(); + + if (!server) + return 2; + + qemudRunLoop(server); + + qemudCleanup(server); + + return 0; +} diff --exclude docs --exclude CVS -ruN libvirt-pristine/src/driver.h libvirt-qemu/src/driver.h --- libvirt-pristine/src/driver.h 2006-08-30 10:21:03.000000000 -0400 +++ libvirt-qemu/src/driver.h 2006-08-30 18:57:32.000000000 -0400 @@ -21,7 +21,8 @@ VIR_DRV_XEN_STORE = 2, VIR_DRV_XEN_DAEMON = 3, VIR_DRV_TEST = 4, - VIR_DRV_XEN_PROXY = 5 + VIR_DRV_XEN_PROXY = 5, + VIR_DRV_QEMU = 6 } virDrvNo; diff --exclude docs --exclude CVS -ruN libvirt-pristine/src/internal.h libvirt-qemu/src/internal.h --- libvirt-pristine/src/internal.h 2006-06-28 14:19:13.000000000 -0400 +++ libvirt-qemu/src/internal.h 2006-08-27 17:05:39.000000000 -0400 @@ -79,7 +79,7 @@ #define VIR_IS_DOMAIN(obj) ((obj) && (obj)->magic==VIR_DOMAIN_MAGIC) #define VIR_IS_CONNECTED_DOMAIN(obj) (VIR_IS_DOMAIN(obj) && VIR_IS_CONNECT((obj)->conn)) -#define MAX_DRIVERS 5 +#define MAX_DRIVERS 10 /* * Flags for Xen connections diff --exclude docs --exclude CVS -ruN libvirt-pristine/src/libvirt.c libvirt-qemu/src/libvirt.c --- libvirt-pristine/src/libvirt.c 2006-08-30 10:21:03.000000000 -0400 +++ libvirt-qemu/src/libvirt.c 2006-08-30 18:59:30.000000000 -0400 @@ -31,6 +31,7 @@ #include "proxy_internal.h" #include "xml.h" #include "test.h" +#include "qemu_internal.h" /* * TODO: @@ -74,6 +75,7 @@ xenDaemonRegister(); xenStoreRegister(); testRegister(); + qemuRegister(); return(0); } @@ -432,6 +434,7 @@ return(0); } } + return (-1); } diff --exclude docs --exclude CVS -ruN libvirt-pristine/src/Makefile.am libvirt-qemu/src/Makefile.am --- libvirt-pristine/src/Makefile.am 2006-08-29 18:27:07.000000000 -0400 +++ libvirt-qemu/src/Makefile.am 2006-08-30 22:17:04.000000000 -0400 @@ -1,6 +1,8 @@ ## Process this file with automake to produce Makefile.in INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ \ + -I$(top_srcdir)/qemud \ + -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L \ -DBINDIR=\""$(libexecdir)"\" DEPS = libvirt.la LDADDS = @STATIC_BINARIES@ libvirt.la @@ -25,6 +27,7 @@ sexpr.c sexpr.h \ virterror.c \ driver.h \ + qemu_internal.c qemu_internal.h \ proxy_internal.c proxy_internal.h \ conf.c conf.h diff --exclude docs --exclude CVS -ruN libvirt-pristine/src/qemu_internal.c libvirt-qemu/src/qemu_internal.c --- libvirt-pristine/src/qemu_internal.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/src/qemu_internal.c 2006-08-30 23:03:21.000000000 -0400 @@ -0,0 +1,837 @@ +/* + * test.c: A "mock" hypervisor for use by application unit tests + * + * Copyright (C) 2006 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Berrange <berrange@xxxxxxxxxx> + */ + +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#include <pwd.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/uri.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <limits.h> +#include <paths.h> + +#include "internal.h" +#include "qemu_internal.h" +#include "xml.h" +#include "protocol.h" + +static virDriver qemuDriver = { + VIR_DRV_QEMU, + "QEMU", + LIBVIR_VERSION_NUMBER, + NULL, /* init */ + qemuOpen, /* open */ + qemuClose, /* close */ + NULL, /* type */ + qemuGetVersion, /* version */ + qemuNodeGetInfo, /* nodeGetInfo */ + qemuListDomains, /* listDomains */ + qemuNumOfDomains, /* numOfDomains */ + qemuDomainCreateLinux, /* domainCreateLinux */ + qemuLookupDomainByID, /* domainLookupByID */ + qemuLookupDomainByUUID, /* domainLookupByUUID */ + qemuLookupDomainByName, /* domainLookupByName */ + qemuPauseDomain, /* domainSuspend */ + qemuResumeDomain, /* domainResume */ + qemuShutdownDomain, /* domainShutdown */ + NULL, /* domainReboot */ + qemuDestroyDomain, /* domainDestroy */ + NULL, /* domainFree */ + NULL, /* domainGetName */ + NULL, /* domainGetID */ + NULL, /* domainGetUUID */ + NULL, /* domainGetOSType */ + NULL, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + qemuGetDomainInfo, /* domainGetInfo */ + qemuSaveDomain, /* domainSave */ + qemuRestoreDomain, /* domainRestore */ + NULL, /* domainSetVcpus */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + qemuDomainDumpXML, /* domainDumpXML */ + qemuListDefinedDomains, /* listDomains */ + qemuNumOfDefinedDomains, /* numOfDomains */ + qemuDomainCreate, /* domainCreate */ + qemuDomainDefineXML, /* domainDefineXML */ + qemuUndefine, /* domainUndefine */ +}; + + +static void +qemuError(virConnectPtr con, + virDomainPtr dom, + virErrorNumber error, + const char *info) +{ + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = __virErrorMsg(error, info); + __virRaiseError(con, dom, VIR_FROM_XEN, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info, 0); +} + +/* If the returned packet type does not match the outgoing packet + type then something went wrong. Usually this is a QEMU_PKT_FAILURE + which indicates the operation requested failed. This deserializes + the error message & dispatches it via libvirt error handling. + Occasionally return type type is totally bogus, which triggers + an internal error */ +static void qemuPacketError(virConnectPtr con, + virDomainPtr dom, + struct qemud_packet *pkt) { + if (!pkt) { + qemuError(con, dom, VIR_ERR_INTERNAL_ERROR, "Malformed data packet"); + return; + } + if (pkt->header.type == QEMUD_PKT_FAILURE) { + int code; + /* Paranoia in case remote side didn't terminate it */ + if (pkt->data.failureReply.message[0]) + pkt->data.failureReply.message[QEMUD_MAX_ERROR_LEN-1] = '\0'; + + switch (pkt->data.failureReply.code) { + case QEMUD_ERR_GENERIC: + code = VIR_ERR_INTERNAL_ERROR; + break; + + case QEMUD_ERR_NO_MEMORY: + code = VIR_ERR_NO_MEMORY; + break; + + case QEMUD_ERR_NO_DOMAIN: + code = VIR_ERR_INVALID_ARG; + break; + + case QEMUD_ERR_READONLY: + code = VIR_ERR_OPERATION_DENIED; + break; + + case QEMUD_ERR_DOMAIN_INACTIVE: + code = VIR_ERR_INVALID_ARG; + break; + + case QEMUD_ERR_DOMAIN_ACTIVE: + code = VIR_ERR_INVALID_ARG; + break; + + case QEMUD_ERR_CONFIG_MALFORMED: + code = VIR_ERR_XML_ERROR; + break; + + case QEMUD_ERR_DUPLICATE_DOMAIN: + code = VIR_ERR_DOM_EXIST; + break; + + case QEMUD_ERR_PATH_OVERFLOW: + code = VIR_ERR_INTERNAL_ERROR; + break; + + case QEMUD_ERR_MALFORMED_UUID: + code = VIR_ERR_XML_ERROR; + break; + + case QEMUD_ERR_ILLEGAL_DISK_CONFIG: + code = VIR_ERR_XML_ERROR; + break; + + case QEMUD_ERR_XML_NO_ROOT: + code = VIR_ERR_XML_ERROR; + break; + + case QEMUD_ERR_XML_WRONG_TYPE: + code = VIR_ERR_XML_ERROR; + break; + + case QEMUD_ERR_XML_MISSING_NAME: + code = VIR_ERR_NO_NAME; + break; + + case QEMUD_ERR_XML_MISSING_UUID: + code = VIR_ERR_XML_ERROR; + break; + + case QEMUD_ERR_NAME_OVERFLOW: + code = VIR_ERR_INTERNAL_ERROR; + break; + + default: + code = VIR_ERR_INTERNAL_ERROR; + break; + } + + qemuError(con, dom, code, + pkt->data.failureReply.message[0] ? + pkt->data.failureReply.message : NULL); + } else { + qemuError(con, dom, VIR_ERR_INTERNAL_ERROR, "Incorrect reply type"); + } +} + +void qemuRegister(void) { + virRegisterDriver(&qemuDriver); +} + +/** + * qemuFindServerPath: + * + * Tries to find the path to the qemud binary. + * + * Returns path on success or NULL in case of error. + */ +static const char * +qemuFindServerPath(void) +{ + static const char *serverPaths[] = { + BINDIR "/qemud", + BINDIR "/qemud_dbg", + NULL + }; + int i; + const char *debugQemuD = getenv("QEMU_DEBUG_SERVER"); + + if (debugQemuD) + return(debugQemuD); + + for (i = 0; serverPaths[i]; i++) { + if (access(serverPaths[i], X_OK | R_OK) == 0) { + return serverPaths[i]; + } + } + return NULL; +} + + +/** + * qemuForkServer: + * + * Forks and try to launch the qemu server + * + * Returns 0 in case of success or -1 in case of detected error. + */ +static int +qemuForkServer(void) +{ + const char *proxyPath = qemuFindServerPath(); + int ret, pid, status; + + if (!proxyPath) { + fprintf(stderr, "failed to find qemud\n"); + return(-1); + } + + /* Become a daemon */ + pid = fork(); + if (pid == 0) { + int stdinfd = -1; + int stdoutfd = -1; + int i; + /* Dup stdin & oout onto /dev/null - we can't just close + them since then a socket connection might end up with + fd 0/1/2 and if something printf()'s then bad stuff + happens */ + if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) + goto cleanup; + if ((stdoutfd = open(_PATH_DEVNULL, O_WRONLY)) < 0) + goto cleanup; + if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO) + goto cleanup; + if (close(stdinfd) < 0) + goto cleanup; + stdinfd = -1; + if (close(stdoutfd) < 0) + goto cleanup; + stdoutfd = -1; + + int open_max = sysconf (_SC_OPEN_MAX); + for (i = 0; i < open_max; i++) + if (i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO) + close(i); + + setsid(); + if (fork() == 0) { + execl(proxyPath, proxyPath, NULL); + fprintf(stderr, "failed to exec %s\n", proxyPath); + } + /* + * calling exit() generate troubles for termination handlers + */ + _exit(0); + + cleanup: + if (stdoutfd != -1) + close(stdoutfd); + if (stdinfd != -1) + close(stdinfd); + _exit(-1); + } + + /* + * do a waitpid on the intermediate process to avoid zombies. + */ +retry_wait: + ret = waitpid(pid, &status, 0); + if (ret < 0) { + if (errno == EINTR) + goto retry_wait; + } + + return (0); +} + +/** + * qemuOpenClientSocket: + * @path: the fileame for the socket + * + * try to connect to the socket open by qemud + * + * Returns the associated file descriptor or -1 in case of failure + */ +static int +qemuOpenClientSocket(const char *path) { + int fd; + struct sockaddr_un addr; + int trials = 0; + +retry: + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + return(-1); + } + + /* + * Abstract socket do not hit the filesystem, way more secure and + * garanteed to be atomic + */ + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + addr.sun_path[0] = '\0'; + strncpy(&addr.sun_path[1], path, (sizeof(addr) - 4) - 2); + + /* + * now bind the socket to that address and listen on it + */ + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + if (trials < 3) { + if (qemuForkServer() < 0) + return(-1); + trials++; + /* Give the server a little time to startup */ + usleep(5000 * trials * trials); + goto retry; + } + return (-1); + } + + return (fd); +} + +static int qemuProcessRequest(virConnectPtr conn, + virDomainPtr dom, + struct qemud_packet *req, + struct qemud_packet *reply) { + char *out = (char *)req; + int outDone = 0; + int outLeft = sizeof(struct qemud_packet_header) + req->header.dataSize; + char *in = (char *)reply; + int inGot = 0; + int inLeft = sizeof(struct qemud_packet_header); + + /* printf("Send request %d\n", req->header.type);*/ + + /* Block sending entire outgoing packet */ + while (outLeft) { + int got = write(conn->handle, out+outDone, outLeft); + if (got < 0) { + return -1; + } + outDone += got; + outLeft -= got; + } + + /* Block waiting for header to come back */ + while (inLeft) { + int done = read(conn->handle, in+inGot, inLeft); + if (done <= 0) { + return -1; + } + inGot += done; + inLeft -= done; + } + + /* Validate header isn't bogus (bigger than + maximum defined packet size) */ + if (reply->header.dataSize > sizeof(union qemud_packet_data)) { + /* + printf("Got type %ds body %d (max %ld)\n", reply->header.type, reply->header.dataSize, sizeof(union qemud_packet_data)); + printf("%ld == %ld + %ld\n", sizeof(struct qemud_packet), sizeof(struct qemud_packet_header), sizeof(union qemud_packet_data)); + */ + qemuPacketError(conn, dom, NULL); + return -1; + } + + /* Now block reading in body */ + inLeft = reply->header.dataSize; + while (inLeft) { + int done = read(conn->handle, in+inGot, inLeft); + if (done <= 0) { + return -1; + } + inGot += done; + inLeft -= done; + } + + /* Ensure return type matches outgoing type, otherwise report + the error */ + if (reply->header.type != req->header.type) { + qemuPacketError(conn, dom, reply); + return -1; + } + + return 0; +} + +static int qemuOpenConnection(const char *uri) { + struct passwd *pw; + int uid; + char path[PATH_MAX]; + + if (!strcmp(uri, "/system")) { + uid = 0; + } else if (!strcmp(uri, "/session")) { + if ((uid = geteuid()) < 0) { + return -1; + } + } else { + return -1; + } + + if (!(pw = getpwuid(uid))) + return -1; + + if (snprintf(path, PATH_MAX, "%s/.qemud", pw->pw_dir) == PATH_MAX) { + return -1; + } + + return qemuOpenClientSocket(path); +} + +int qemuOpen(virConnectPtr conn, + const char *name, + int flags){ + xmlURIPtr uri; + int fd; + + if (!name) { + return -1; + } + + uri = xmlParseURI(name); + if (uri == NULL) { + if (!(flags & VIR_DRV_OPEN_QUIET)) + qemuError(conn, NULL, VIR_ERR_NO_SUPPORT, name); + return(-1); + } + + /* Only handle schemes starting qemu:/// */ + if (!uri->scheme || + strcmp(uri->scheme, "qemu") || + !uri->path) { + xmlFreeURI(uri); + return -1; + } + + fd = qemuOpenConnection(uri->path); + xmlFreeURI(uri); + + if (fd < 0) { + return -1; + } + + conn->handle = fd; + + return 0; +} + +int qemuClose (virConnectPtr conn) { + if (conn->handle != -1) { + close(conn->handle); + conn->handle = -1; + } + return 0; +} + +int qemuGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned long *hvVer){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_GET_VERSION; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + *hvVer = reply.data.getVersionReply.version; + return 0; +} + +int qemuNodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, + virNodeInfoPtr info){ + /* XXX implement me - how do we do it portably ? */ + info->cores = 1; + info->threads = 1; + info->sockets = 1; + info->nodes = 1; + strcpy(info->model, "i686"); + info->mhz = 6000; + info->cpus = 1; + info->memory = 1024*1024; + return 0; +} + +int qemuNumOfDomains(virConnectPtr conn){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NUM_DOMAINS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return reply.data.numDomainsReply.numDomains; +} + +int qemuListDomains(virConnectPtr conn, + int *ids, + int maxids){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_LIST_DOMAINS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + if (reply.data.listDomainsReply.numDomains > maxids) + return -1; + + memmove(ids, + reply.data.listDomainsReply.domains, + sizeof(int)*reply.data.listDomainsReply.numDomains); + + return reply.data.listDomainsReply.numDomains; +} + +virDomainPtr +qemuDomainCreateLinux(virConnectPtr conn, const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED){ + struct qemud_packet req, reply; + virDomainPtr dom; + + if (strlen(xmlDesc) > (QEMUD_MAX_XML_LEN-1)) { + return NULL; + } + + req.header.type = QEMUD_PKT_DOMAIN_CREATE; + req.header.dataSize = sizeof(req.data.domainCreateRequest); + strcpy(req.data.domainCreateRequest.xml, xmlDesc); + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + /* Paranoia, just in case */ + reply.data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + + if (!(dom = virGetDomain(conn, + reply.data.domainCreateReply.name, + reply.data.domainCreateReply.uuid))) + return NULL; + + dom->handle = reply.data.domainCreateReply.id; + return dom; +} + +virDomainPtr qemuLookupDomainByID(virConnectPtr conn, + int id){ + struct qemud_packet req, reply; + virDomainPtr dom; + + req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_ID; + req.header.dataSize = sizeof(req.data.domainLookupByIDRequest); + req.data.domainLookupByIDRequest.id = id; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + /* Paranoia, just in case */ + reply.data.domainLookupByIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + + if (!(dom = virGetDomain(conn, + reply.data.domainLookupByIDReply.name, + reply.data.domainLookupByIDReply.uuid))) + return NULL; + + dom->handle = id; + return dom; +} + +virDomainPtr qemuLookupDomainByUUID(virConnectPtr conn, + const unsigned char *uuid){ + struct qemud_packet req, reply; + virDomainPtr dom; + + req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID; + req.header.dataSize = sizeof(req.data.domainLookupByUUIDRequest); + memmove(req.data.domainLookupByUUIDRequest.uuid, uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + /* Paranoia, just in case */ + reply.data.domainLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + + if (!(dom = virGetDomain(conn, + reply.data.domainLookupByUUIDReply.name, + uuid))) + return NULL; + + dom->handle = reply.data.domainLookupByUUIDReply.id; + return dom; +} +virDomainPtr qemuLookupDomainByName(virConnectPtr conn, + const char *name){ + struct qemud_packet req, reply; + virDomainPtr dom; + + if (strlen(name) > (QEMUD_MAX_NAME_LEN-1)) + return NULL; + + req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME; + req.header.dataSize = sizeof(req.data.domainLookupByNameRequest); + strcpy(req.data.domainLookupByNameRequest.name, name); + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + if (!(dom = virGetDomain(conn, + name, + reply.data.domainLookupByNameReply.uuid))) + return NULL; + + dom->handle = reply.data.domainLookupByNameReply.id; + return dom; +} +int qemuShutdownDomain(virDomainPtr domain){ + return qemuDestroyDomain(domain); +} +int qemuDestroyDomain(virDomainPtr domain){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_DOMAIN_DESTROY; + req.header.dataSize = sizeof(req.data.domainDestroyRequest); + req.data.domainDestroyRequest.id = domain->handle; + + if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) { + return -1; + } + + return 0; +} +int qemuResumeDomain(virDomainPtr domain ATTRIBUTE_UNUSED){ + return -1; +} +int qemuPauseDomain(virDomainPtr domain ATTRIBUTE_UNUSED){ + return -1; +} +int qemuGetDomainInfo(virDomainPtr domain, + virDomainInfoPtr info){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_DOMAIN_GET_INFO; + req.header.dataSize = sizeof(req.data.domainGetInfoRequest); + memmove(req.data.domainGetInfoRequest.uuid, domain->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) { + return -1; + } + + memset(info, 0, sizeof(virDomainInfo)); + switch (reply.data.domainGetInfoReply.runstate) { + case QEMUD_STATE_RUNNING: + info->state = VIR_DOMAIN_RUNNING; + break; + + case QEMUD_STATE_PAUSED: + info->state = VIR_DOMAIN_PAUSED; + break; + + case QEMUD_STATE_STOPPED: + info->state = VIR_DOMAIN_SHUTOFF; + break; + + default: + return -1; + } + info->maxMem = reply.data.domainGetInfoReply.memory; + info->memory = reply.data.domainGetInfoReply.memory; + info->nrVirtCpu = reply.data.domainGetInfoReply.nrVirtCpu; + info->cpuTime = reply.data.domainGetInfoReply.cpuTime; + + return 0; +} +char * qemuDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_DUMP_XML; + req.header.dataSize = sizeof(req.data.domainDumpXMLRequest); + memmove(req.data.domainDumpXMLRequest.uuid, domain->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) { + return NULL; + } + + /* Paranoia, just in case */ + reply.data.domainDumpXMLReply.xml[QEMUD_MAX_XML_LEN-1] = '\0'; + + return strdup(reply.data.domainDumpXMLReply.xml); +} + +int qemuSaveDomain(virDomainPtr domain ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED){ + return -1; +} +int qemuRestoreDomain(virConnectPtr conn ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED){ + return -1; +} +int qemuNumOfDefinedDomains(virConnectPtr conn){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return reply.data.numDefinedDomainsReply.numDomains; +} + +int qemuListDefinedDomains(virConnectPtr conn, + const char **names, + int maxnames){ + struct qemud_packet req, reply; + int i; + + req.header.type = QEMUD_PKT_LIST_DEFINED_DOMAINS; + req.header.dataSize = 0; + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + if (reply.data.listDefinedDomainsReply.numDomains > maxnames) + return -1; + + for (i = 0 ; i < reply.data.listDefinedDomainsReply.numDomains ; i++) { + reply.data.listDefinedDomainsReply.domains[i][QEMUD_MAX_NAME_LEN-1] = '\0'; + names[i] = strdup(reply.data.listDefinedDomainsReply.domains[i]); + } + + return reply.data.listDefinedDomainsReply.numDomains; +} +int qemuDomainCreate(virDomainPtr dom) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_DOMAIN_START; + req.header.dataSize = sizeof(req.data.domainStartRequest); + memcpy(req.data.domainStartRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) { + return -1; + } + + dom->handle = reply.data.domainStartReply.id; + + return 0; +} + +virDomainPtr qemuDomainDefineXML(virConnectPtr conn, const char *xml) { + struct qemud_packet req, reply; + virDomainPtr dom; + + if (strlen(xml) > (QEMUD_MAX_XML_LEN-1)) { + return NULL; + } + + req.header.type = QEMUD_PKT_DOMAIN_DEFINE; + req.header.dataSize = sizeof(req.data.domainDefineRequest); + strcpy(req.data.domainDefineRequest.xml, xml); + + if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + /* Paranoia, just in case */ + reply.data.domainDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + + if (!(dom = virGetDomain(conn, + reply.data.domainDefineReply.name, + reply.data.domainDefineReply.uuid))) + return NULL; + + dom->handle = -1; + return dom; +} + +int qemuUndefine(virDomainPtr dom) { + struct qemud_packet req, reply; + int ret = 0; + + req.header.type = QEMUD_PKT_DOMAIN_UNDEFINE; + req.header.dataSize = sizeof(req.data.domainUndefineRequest); + memcpy(req.data.domainUndefineRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN); + + if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) { + ret = -1; + goto cleanup; + } + + cleanup: + if (virFreeDomain(dom->conn, dom) < 0) + ret = -1; + + return ret; +} diff --exclude docs --exclude CVS -ruN libvirt-pristine/src/qemu_internal.h libvirt-qemu/src/qemu_internal.h --- libvirt-pristine/src/qemu_internal.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/src/qemu_internal.h 2006-08-27 17:05:39.000000000 -0400 @@ -0,0 +1,62 @@ +/* + * qemu_internal.h: A backend for managing QEMU machines + * + * Copyright (C) 2006 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __VIR_QEMU_INTERNAL_H__ +#define __VIR_QEMU_INTERNAL_H__ + +#include <libvirt/virterror.h> + +#ifdef __cplusplus +extern "C" { +#endif + + void qemuRegister(void); + int qemuOpen(virConnectPtr conn, + const char *name, + int flags); + int qemuClose (virConnectPtr conn); + int qemuGetVersion(virConnectPtr conn, + unsigned long *hvVer); + int qemuNodeGetInfo(virConnectPtr conn, + virNodeInfoPtr info); + int qemuNumOfDomains(virConnectPtr conn); + int qemuListDomains(virConnectPtr conn, + int *ids, + int maxids); + int qemuNumOfDefinedDomains(virConnectPtr conn); + int qemuListDefinedDomains(virConnectPtr conn, + const char **names, + int maxnames); + virDomainPtr + qemuDomainCreateLinux(virConnectPtr conn, const char *xmlDesc, + unsigned int flags); + virDomainPtr qemuLookupDomainByID(virConnectPtr conn, + int id); + virDomainPtr qemuLookupDomainByUUID(virConnectPtr conn, + const unsigned char *uuid); + virDomainPtr qemuLookupDomainByName(virConnectPtr conn, + const char *name); + int qemuShutdownDomain(virDomainPtr domain); + int qemuDestroyDomain(virDomainPtr domain); + int qemuResumeDomain(virDomainPtr domain); + int qemuPauseDomain(virDomainPtr domain); + int qemuGetDomainInfo(virDomainPtr domain, + virDomainInfoPtr info); + char * qemuDomainDumpXML(virDomainPtr domain, int flags); + int qemuSaveDomain(virDomainPtr domain, const char *file); + int qemuRestoreDomain(virConnectPtr conn, const char *file); + int qemuDomainCreate(virDomainPtr conn); + virDomainPtr qemuDomainDefineXML(virConnectPtr conn, const char *xml); + int qemuUndefine(virDomainPtr dom); + +#ifdef __cplusplus +} +#endif +#endif /* __VIR_QEMU_INTERNAL_H__ */ diff --exclude docs --exclude CVS -ruN libvirt-pristine/src/virsh.c libvirt-qemu/src/virsh.c --- libvirt-pristine/src/virsh.c 2006-08-30 10:32:32.000000000 -0400 +++ libvirt-qemu/src/virsh.c 2006-08-30 19:00:13.000000000 -0400 @@ -2284,7 +2284,8 @@ /* basic connection to hypervisor, for Xen connections unless we're root open a read only connections. Allow 'test' HV to be RW all the time though */ - if (ctl->uid == 0 || (ctl->name && !strncmp(ctl->name, "test", 4))) + if (ctl->uid == 0 || (ctl->name && (!strncmp(ctl->name, "test", 4) || + !strncmp(ctl->name, "qemu", 4)))) ctl->conn = virConnectOpen(ctl->name); else ctl->conn = virConnectOpenReadOnly(ctl->name);