[Libvir] PATCH 1/2: Support QEMU (+KVM) in libvirt
Daniel Veillard
veillard at redhat.com
Tue Jan 9 12:00:57 UTC 2007
On Fri, Jan 05, 2007 at 09:16:54PM +0000, Daniel P. Berrange wrote:
> The attached patch provides the QEMU daemon for managing the QEMU instances
> and providing a network protocol for the libvirt driver to talk to over
> UNIX domain sockets or IPv4/6.
Okay, the size of the patch is big :-) I have tried to review it fully
but certainly didn't grasped all of it ! Still I found a few things...
comments in context inside.
Daniel
> 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 -ruN libvirt/qemud/config.c libvirt-qemu/qemud/config.c
> --- libvirt/qemud/config.c 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/config.c 2007-01-04 12:11:49.000000000 -0500
> @@ -0,0 +1,1234 @@
> +/*
> + * config.c: VM configuration management
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#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 <libxml/parser.h>
> +#include <libxml/tree.h>
> +#include <libxml/xpath.h>
> +#include <libxml/uri.h>
> +
> +#include <libvirt/virterror.h>
> +
> +#include "protocol.h"
> +#include "internal.h"
> +#include "config.h"
> +#include "driver.h"
> +
> +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:
> + return -1;
> +}
> +
need to go in lib/
> +struct qemu_arch_info {
> + const char *arch;
> + const char **machines;
> + const char *binary;
> +};
> +
> +/* The list of possible machine types for various architectures,
> + as supported by QEMU - taken from 'qemu -M ?' for each arch */
> +static const char *arch_info_x86_machines[] = {
> + "pc", "isapc"
> +};
> +static const char *arch_info_mips_machines[] = {
> + "mips"
> +};
> +static const char *arch_info_sparc_machines[] = {
> + "sun4m"
> +};
> +static const char *arch_info_ppc_machines[] = {
> + "g3bw", "mac99", "prep"
> +};
Hum, I wonder how we are gonna keep the sync :-)
> +/* The archicture tables for supported QEMU archs */
> +static struct qemu_arch_info archs[] = {
> + { "i686", arch_info_x86_machines, "qemu" },
> + { "x86_64", arch_info_x86_machines, "qemu-system-x86_64" },
> + { "mips", arch_info_mips_machines, "qemu-system-mips" },
> + { "mipsel", arch_info_mips_machines, "qemu-system-mipsel" },
> + { "sparc", arch_info_sparc_machines, "qemu-system-sparc" },
> + { "ppc", arch_info_ppc_machines, "qemu-system-ppc" },
> +};
> +
> +/* Return the default architecture if none is explicitly requested*/
> +static const char *qemudDefaultArch(void) {
> + return archs[0].arch;
> +}
> +
> +/* Return the default machine type for a given architecture */
> +static const char *qemudDefaultMachineForArch(const char *arch) {
> + int i;
> +
> + for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
> + if (!strcmp(archs[i].arch, arch)) {
> + return archs[i].machines[0];
> + }
> + }
> +
> + return NULL;
> +}
> +
> +/* Return the default binary name for a particular architecture */
> +static const char *qemudDefaultBinaryForArch(const char *arch) {
> + int i;
> +
> + for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
> + if (!strcmp(archs[i].arch, arch)) {
> + return archs[i].binary;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +/* Find the fully qualified path to the binary for an architecture */
> +static char *qemudLocateBinaryForArch(struct qemud_server *server,
> + int virtType, const char *arch) {
> + const char *name;
> + char *path;
> +
> + if (virtType == QEMUD_VIRT_KVM)
> + name = "qemu-kvm";
> + else
> + name = qemudDefaultBinaryForArch(arch);
> +
> + if (!name) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot determin binary for architecture %s", arch);
> + return NULL;
> + }
> +
> + /* XXX lame. should actually use $PATH ... */
> + path = malloc(strlen(name) + strlen("/usr/bin/") + 1);
> + if (!path) {
> + qemudReportError(server, VIR_ERR_NO_MEMORY, "path");
> + return NULL;
> + }
> + strcpy(path, "/usr/bin/");
> + strcat(path, name);
> + return path;
> +}
Shouldn't we walk $PATH to look up ?
> +/* Parse the XML definition for a disk */
> +static struct qemud_vm_disk_def *qemudParseDiskXML(struct qemud_server *server,
> + 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;
> +
> + if (!disk) {
> + qemudReportError(server, VIR_ERR_NO_MEMORY, "disk");
> + return NULL;
> + }
> +
> + 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 {
> + typ = QEMUD_DISK_FILE;
> + }
> + 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) {
> + qemudReportError(server, VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target);
> + goto error;
> + }
> + if (target == NULL) {
> + qemudReportError(server, VIR_ERR_NO_TARGET, source ? "%s" : NULL, source);
> + goto error;
> + }
> +
> + if (device &&
> + !strcmp((const char *)device, "floppy") &&
> + strcmp((const char *)target, "fda") &&
> + strcmp((const char *)target, "fdb")) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid floppy device name: %s", target);
> + goto error;
> + }
> +
> + if (device &&
> + !strcmp((const char *)device, "cdrom") &&
> + strcmp((const char *)target, "hdc")) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid cdrom device name: %s", target);
> + 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")) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid harddisk device name: %s", target);
> + 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 {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid device type: %s", device);
> + goto error;
> + }
> +
> + xmlFree(device);
> + xmlFree(target);
> + xmlFree(source);
> +
> + return disk;
> +
> + error:
> + if (type)
> + xmlFree(type);
> + if (target)
> + xmlFree(target);
> + if (source)
> + xmlFree(source);
> + if (device)
> + xmlFree(device);
> + free(disk);
> + return NULL;
> +}
> +
> +
> +/* Parse the XML definition for a network interface */
> +static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *server,
> + xmlNodePtr node) {
> + struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def));
> + xmlNodePtr cur;
> + xmlChar *macaddr = NULL;
> + xmlChar *type = NULL;
> +
> + if (!net) {
> + qemudReportError(server, VIR_ERR_NO_MEMORY, "net");
> + return NULL;
> + }
> +
> + net->type = QEMUD_NET_USER;
> +
> + type = xmlGetProp(node, BAD_CAST "type");
> + if (type != NULL) {
> + if (xmlStrEqual(type, BAD_CAST "user"))
> + net->type = QEMUD_NET_USER;
> + else if (xmlStrEqual(type, BAD_CAST "tap"))
> + net->type = QEMUD_NET_TAP;
> + else if (xmlStrEqual(type, BAD_CAST "server"))
> + net->type = QEMUD_NET_SERVER;
> + else if (xmlStrEqual(type, BAD_CAST "client"))
> + net->type = QEMUD_NET_CLIENT;
> + else if (xmlStrEqual(type, BAD_CAST "mcast"))
> + net->type = QEMUD_NET_MCAST;
> + /*
> + else if (xmlStrEqual(type, BAD_CAST "vde"))
> + typ = QEMUD_NET_VDE;
> + */
> + else
> + net->type = QEMUD_NET_USER;
> + xmlFree(type);
> + type = NULL;
> + }
> +
> + cur = node->children;
> + while (cur != NULL) {
> + if (cur->type == XML_ELEMENT_NODE) {
> + if ((macaddr == NULL) &&
> + (xmlStrEqual(cur->name, BAD_CAST "mac"))) {
> + macaddr = xmlGetProp(cur, BAD_CAST "address");
> + }
> + }
> + cur = cur->next;
> + }
> +
> + net->vlan = 0;
> +
> + if (macaddr) {
> + sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
> + (unsigned int*)&net->mac[0],
> + (unsigned int*)&net->mac[1],
> + (unsigned int*)&net->mac[2],
> + (unsigned int*)&net->mac[3],
> + (unsigned int*)&net->mac[4],
> + (unsigned int*)&net->mac[5]);
> + }
> +
> + xmlFree(macaddr);
> +
> + return net;
> +
> + /*
> + error:
> + if (macaddr)
> + xmlFree(macaddr);
> + free(net);
> + return NULL;
> + */
> +}
> +
> +
> +/*
> + * Parses a libvirt XML definition of a guest, and populates the
> + * the qemud_vm struct with matching data about the guests config
> + */
> +static int qemudParseXML(struct qemud_server *server,
> + xmlDocPtr xml,
> + struct qemud_vm *vm) {
> + xmlNodePtr root = NULL;
> + xmlChar *prop = NULL;
> + xmlXPathContextPtr ctxt = NULL;
> + xmlXPathObjectPtr obj = NULL;
> + char *conv = NULL;
> +
> + /* Prepare parser / xpath context */
> + root = xmlDocGetRootElement(xml);
> + if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element");
> + goto error;
> + }
> +
> + ctxt = xmlXPathNewContext(xml);
> + if (ctxt == NULL) {
> + qemudReportError(server, VIR_ERR_NO_MEMORY, "xmlXPathContext");
> + goto error;
> + }
> +
> +
> + /* Find out what type of QEMU virtualization to use */
> + if (!(prop = xmlGetProp(root, BAD_CAST "type"))) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing domain type attribute");
> + goto error;
> + }
> +
> + if (!strcmp((char *)prop, "qemu"))
> + vm->def.virtType = QEMUD_VIRT_QEMU;
> + else if (!strcmp((char *)prop, "kqemu"))
> + vm->def.virtType = QEMUD_VIRT_KQEMU;
> + else if (!strcmp((char *)prop, "kvm"))
> + vm->def.virtType = QEMUD_VIRT_KVM;
> + else {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "invalid domain type attribute");
> + goto error;
> + }
> + free(prop);
> + prop = NULL;
> +
> +
> + /* Extract domain name */
> + obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt);
> + if ((obj == NULL) || (obj->type != XPATH_STRING) ||
> + (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
> + qemudReportError(server, VIR_ERR_NO_NAME, NULL);
> + goto error;
> + }
> + if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "domain name length too long");
> + goto error;
> + }
> + strcpy(vm->def.name, (const char *)obj->stringval);
> + xmlXPathFreeObject(obj);
> +
> +
> + /* Extract domain uuid */
> + obj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])", ctxt);
> + if ((obj == NULL) || (obj->type != XPATH_STRING) ||
> + (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
> + /* XXX auto-generate a UUID */
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing uuid element");
> + goto error;
> + }
> + if (qemudParseUUID((const char *)obj->stringval, vm->def.uuid) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element");
> + goto error;
> + }
> + xmlXPathFreeObject(obj);
> +
> +
> + /* Extract domain memory */
> + 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 = 131072; /* 128 MB of ram */
> + } else {
> + conv = NULL;
> + vm->def.memory = strtoll((const char*)obj->stringval, &conv, 10);
> + if (conv == (const char*)obj->stringval) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
> + goto error;
> + }
> + }
> + if (obj)
> + xmlXPathFreeObject(obj);
> +
> +
> + /* Extract domain vcpu info */
> + 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) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed vcpu information");
> + goto error;
> + }
> + }
> + if (obj)
> + xmlXPathFreeObject(obj);
> +
> +
> + /* Extract OS type info */
> + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1])", ctxt);
> + if ((obj == NULL) || (obj->type != XPATH_STRING) ||
> + (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
> + qemudReportError(server, VIR_ERR_OS_TYPE, NULL);
> + goto error;
> + }
> + if (strcmp((const char *)obj->stringval, "hvm")) {
> + qemudReportError(server, VIR_ERR_OS_TYPE, "%s", obj->stringval);
> + goto error;
> + }
> + strcpy(vm->def.os.type, (const char *)obj->stringval);
> + xmlXPathFreeObject(obj);
> +
> +
> + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@arch)", ctxt);
> + if ((obj == NULL) || (obj->type != XPATH_STRING) ||
> + (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
> + const char *defaultArch = qemudDefaultArch();
> + if (strlen(defaultArch) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
> + goto error;
> + }
> + strcpy(vm->def.os.arch, defaultArch);
> + } else {
> + if (strlen((const char *)obj->stringval) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
> + goto error;
> + }
> + strcpy(vm->def.os.arch, (const char *)obj->stringval);
> + }
> + if (obj)
> + xmlXPathFreeObject(obj);
> +
> + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@machine)", ctxt);
> + if ((obj == NULL) || (obj->type != XPATH_STRING) ||
> + (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
> + const char *defaultMachine = qemudDefaultMachineForArch(vm->def.os.arch);
> + if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "machine type too long");
> + goto error;
> + }
> + strcpy(vm->def.os.machine, defaultMachine);
> + } else {
> + if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
> + goto error;
> + }
> + strcpy(vm->def.os.machine, (const char *)obj->stringval);
> + }
> + if (obj)
> + xmlXPathFreeObject(obj);
> +
> + obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt);
> + if ((obj == NULL) || (obj->type != XPATH_STRING) ||
> + (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
> + char *tmp = qemudLocateBinaryForArch(server, vm->def.virtType, vm->def.os.arch);
> + if (!tmp) {
> + goto error;
> + }
> + strcpy(vm->def.os.binary, tmp);
> + free(tmp);
> + } else {
> + if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "emulator path too long");
> + goto error;
> + }
> + strcpy(vm->def.os.binary, (const char *)obj->stringval);
> + }
> + if (obj)
> + xmlXPathFreeObject(obj);
> +
> + 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 = -1;
> + }
> + } else if (!strcmp((char *)prop, "sdl")) {
> + vm->def.graphicsType = QEMUD_GRAPHICS_SDL;
> + } else {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Unsupported graphics type %s", prop);
> + goto error;
> + }
> + }
> +
> + /* analysis of the disk 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(server, obj->nodesetval->nodeTab[i]))) {
> + goto error;
> + }
> + vm->def.ndisks++;
> + disk->next = vm->def.disks;
> + vm->def.disks = disk;
> + }
> + }
> + xmlXPathFreeObject(obj);
> +
> +
> + /* analysis of the network devices */
> + obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", 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_net_def *net;
> + if (!(net = qemudParseInterfaceXML(server, obj->nodesetval->nodeTab[i]))) {
> + goto error;
> + }
> + vm->def.nnets++;
> + net->next = vm->def.nets;
> + vm->def.nets = net;
> + }
> + }
> + xmlXPathFreeObject(obj);
> +
> + return 0;
> +
> + error:
> + /* XXX free all the stuff in the qemud_vm struct, or leave it upto
> + the caller ? */
> + if (prop)
> + free(prop);
> + if (obj)
> + xmlXPathFreeObject(obj);
> + return -1;
> +}
> +
Hum, there is a lot of duplication in the parsing, at least I should try to
provide libxml2 XPath lookup based functions instead of duplicating
xmlXPathEval all over the place with relatively complex cleanup etc...
> +/*
> + * Constructs a argv suitable for launching qemu with config defined
> + * for a given virtual machine.
> + */
> +int qemudBuildCommandLine(struct qemud_server *server,
> + struct qemud_vm *vm,
> + char ***argv,
> + int *argc) {
> + int n = -1;
> + char memory[50];
> + char vcpus[50];
> + struct qemud_vm_disk_def *disk = vm->def.disks;
> + struct qemud_vm_net_def *net = vm->def.nets;
> +
> + *argc = 1 + /* qemu */
> + 2 + /* machine type */
> + (vm->def.virtType == QEMUD_VIRT_QEMU ? 1 : 0) + /* Disable kqemu */
> + 2 * vm->def.ndisks + /* disks*/
> + (vm->def.nnets > 0 ? (4 * vm->def.nnets) : 2) + /* networks */
> + 2 + /* memory*/
> + 2 + /* cpus */
> + (vm->def.graphicsType == QEMUD_GRAPHICS_VNC ? 2 :
> + (vm->def.graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)); /* graphics */
> +
> + sprintf(memory, "%d", vm->def.memory/1024);
> + sprintf(vcpus, "%d", vm->def.vcpus);
> +
> + if (!(*argv = malloc(sizeof(char *) * (*argc +1))))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup(vm->def.os.binary)))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup("-M")))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup(vm->def.os.machine)))
> + goto no_memory;
> + if (vm->def.virtType == QEMUD_VIRT_QEMU) {
> + if (!((*argv)[++n] = strdup("-no-kqemu")))
> + goto no_memory;
> + }
> + if (!((*argv)[++n] = strdup("-m")))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup(memory)))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup("-smp")))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup(vcpus)))
> + goto no_memory;
> +
> + 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);
> +
> + if (!((*argv)[++n] = strdup(dev)))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup(file)))
> + goto no_memory;
> +
> + disk = disk->next;
> + }
> +
> + if (!net) {
> + if (!((*argv)[++n] = strdup("-net")))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup("none")))
> + goto no_memory;
> + } else {
> + while (net) {
> + char nic[3+1+7+1+17+1];
> + sprintf(nic, "nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
> + net->mac[0], net->mac[1],
> + net->mac[2], net->mac[3],
> + net->mac[4], net->mac[5]);
> +
> + if (!((*argv)[++n] = strdup("-net")))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup(nic)))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup("-net")))
> + goto no_memory;
> + /* XXX don't hardcode user */
> + if (!((*argv)[++n] = strdup("user")))
> + goto no_memory;
> +
> + net = net->next;
> + }
> + }
> +
> + if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) {
> + char port[10];
> + snprintf(port, 10, "%d", vm->def.vncActivePort - 5900);
> + if (!((*argv)[++n] = strdup("-vnc")))
> + goto no_memory;
> + if (!((*argv)[++n] = strdup(port)))
> + goto no_memory;
> + } else if (vm->def.graphicsType == QEMUD_GRAPHICS_NONE) {
> + if (!((*argv)[++n] = strdup("-nographic")))
> + goto no_memory;
> + } else {
> + /* SDL is the default. no args needed */
> + }
> +
> + (*argv)[++n] = NULL;
> +
> + return 0;
> +
> + no_memory:
> + if (argv) {
> + int i; > + for (i = 0 ; i < n ; i++)
> + free(argv[i]);
> + free(argv);
> + }
> + qemudReportError(server, VIR_ERR_NO_MEMORY, "argv");
> + return -1;
> +}
> +
> +/* Free all memory associated with a struct qemud_vm 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);
> +}
> +
> +/* Build up a fully qualfiied path for a config file to be
qualified :-)
Can we try to keep the fuction comments in line
/**
* funcname:
* @arg1: ...
*
* ....
*
* Returns ....
*/
even for internal ones, don't block commiting for that but I will probably
make a pass over this once commited, that will force me to get deeper
understanding of the code
> + * associated with a persistent guest */
> +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)
> + return -1;
> +
> + strcpy(buf, server->configDir);
> + strcat(buf, "/");
> + strcat(buf, name);
> + if (ext)
> + strcat(buf, ext);
> + return 0;
> +}
> +
> +
> +/* Save a guest's config data into a persistent file */
> +static int qemudSaveConfig(struct qemud_server *server,
> + struct qemud_vm *vm) {
> + char *xml;
> + int fd, ret = -1;
> + int towrite;
> + struct stat sb;
> +
> + if (!(xml = qemudGenerateXML(server, vm))) {
> + return -1;
> + }
> +
> + if (stat(server->configDir, &sb) < 0) {
> + if (errno == ENOENT) {
> + if (mkdir(server->configDir, 0700) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create config directory %s", server->configDir);
> + return -1;
> + }
> + } else {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot stat config directory %s", server->configDir);
> + return -1;
> + }
> + } else if (!S_ISDIR(sb.st_mode)) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "config directory %s is not a directory", server->configDir);
> + return -1;
> + }
> +
> + if ((fd = open(vm->configFile,
> + O_WRONLY | O_CREAT | O_TRUNC,
> + S_IRUSR | S_IWUSR )) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create config file %s", vm->configFile);
> + goto cleanup;
> + }
> +
> + towrite = strlen(xml);
> + if (write(fd, xml, towrite) != towrite) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot write config file %s", vm->configFile);
> + goto cleanup;
> + }
> +
> + if (close(fd) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot save config file %s", vm->configFile);
> + goto cleanup;
> + }
> +
> + ret = 0;
> +
> + cleanup:
Shouldn't it close fd if write() fails ?
> + free(xml);
> +
> + return ret;
> +}
> +
> +
> +/* Create a qemud_vm instance, populating it based on the data
> + * in a libvirt XML document describing the guest */
> +struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server,
> + const char *file,
> + const char *doc,
> + int save) {
> + struct qemud_vm *vm = NULL;
> + xmlDocPtr xml;
> +
> + if (!(xml = xmlReadDoc(BAD_CAST doc, file ? file : "domain.xml", NULL,
> + XML_PARSE_NOENT | XML_PARSE_NONET |
> + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
> + qemudReportError(server, VIR_ERR_XML_ERROR, NULL);
> + return NULL;
> + }
> +
> + if (!(vm = calloc(1, sizeof(struct qemud_vm)))) {
> + qemudReportError(server, VIR_ERR_NO_MEMORY, "vm");
> + return NULL;
> + }
> +
> + vm->stdout = -1;
> + vm->stderr = -1;
> + vm->monitor = -1;
> + vm->pid = -1;
> + vm->def.id = -1;
> +
> + if (qemudParseXML(server, xml, vm) < 0) {
> + xmlFreeDoc(xml);
> + qemudFreeVM(vm);
> + return NULL;
> + }
> + xmlFreeDoc(xml);
> +
> + if (qemudFindVMByUUID(server, vm->def.uuid)) {
> + qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.name);
> + qemudFreeVM(vm);
> + return NULL;
> + }
> +
> + if (qemudFindVMByName(server, vm->def.name)) {
> + qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.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) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot construct config file path");
> + qemudFreeVM(vm);
> + return NULL;
> + }
> +
> + if (qemudSaveConfig(server, vm) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot save config file for guest");
> + qemudFreeVM(vm);
> + return NULL;
> + }
> + } else {
> + vm->configFile[0] = '\0';
> + }
> + }
> +
> + return vm;
> +}
> +
> +
> +/* Load a guest from its persistent config file */
> +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"))) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open guest config file %s", file);
> + return;
> + }
> +
> + if (fstat(fileno(fh), &st) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot stat config file %s", file);
> + goto cleanup;
> + }
> +
> + if (st.st_size >= QEMUD_MAX_XML_LEN) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "guest config too large in file %s", file);
> + goto cleanup;
> + }
> +
> + if ((ret = fread(xml, st.st_size, 1, fh)) != 1) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot read config file %s", file);
> + 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 for all guest config files */
> +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;
> +}
> +
[...]
> +
> +/* Generate an XML document describing the guest's configuration */
> +char *qemudGenerateXML(struct qemud_server *server, struct qemud_vm *vm) {
> + struct qemudBuffer buf;
> + unsigned char *uuid;
> + struct qemud_vm_disk_def *disk;
> + struct qemud_vm_net_def *net;
> + const char *type = NULL;
> +
> + buf.len = QEMUD_MAX_XML_LEN;
> + buf.used = 0;
> + buf.data = malloc(buf.len);
> +
> + switch (vm->def.virtType) {
> + case QEMUD_VIRT_QEMU:
> + type = "qemu";
> + break;
> + case QEMUD_VIRT_KQEMU:
> + type = "kqemu";
> + break;
> + case QEMUD_VIRT_KVM:
> + type = "kvm";
> + break;
> + }
> + if (!type) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "unexpected domain type %d", vm->def.virtType);
> + goto cleanup;
> + }
> +
> + if (vm->def.id >= 0) {
> + if (qemudBufferPrintf(&buf, "<domain type='%s' id='%d'>\n", type, vm->def.id) < 0)
> + goto no_memory;
> + } else {
> + if (qemudBufferPrintf(&buf, "<domain type='%s'>\n", type) < 0)
> + goto no_memory;
> + }
> +
> + if (qemudBufferPrintf(&buf, " <name>%s</name>\n", vm->def.name) < 0)
> + goto no_memory;
> +
> + 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)
Hum, do we really want to use the format with '-' and not just the raw
hex dump by default, I'm still wondering what this brings...
> + goto no_memory;
> + if (qemudBufferPrintf(&buf, " <memory>%d</memory>\n", vm->def.memory) < 0)
> + goto no_memory;
> + if (qemudBufferPrintf(&buf, " <vcpu>%d</vcpu>\n", vm->def.vcpus) < 0)
> + goto no_memory;
> +
> + if (qemudBufferAdd(&buf, " <os>\n") < 0)
> + goto no_memory;
> +
> + if (vm->def.virtType == QEMUD_VIRT_QEMU) {
> + if (qemudBufferPrintf(&buf, " <type arch='%s' machine='%s'>%s</type>\n", vm->def.os.arch, vm->def.os.machine, vm->def.os.type) < 0)
> + goto no_memory;
> + } else {
> + if (qemudBufferPrintf(&buf, " <type>%s</type>\n", vm->def.os.type) < 0)
> + goto no_memory;
> + }
> +
> + if (vm->def.os.kernel[0])
> + if (qemudBufferPrintf(&buf, " <kernel>%s</kernel>\n", vm->def.os.kernel) < 0)
> + goto no_memory;
> + if (vm->def.os.initrd[0])
> + if (qemudBufferPrintf(&buf, " <initrd>%s</initrd>\n", vm->def.os.initrd) < 0)
> + goto no_memory;
> + if (vm->def.os.cmdline[0])
> + if (qemudBufferPrintf(&buf, " <cmdline>%s</cmdline>\n", vm->def.os.cmdline) < 0)
> + goto no_memory;
> +
> + if (qemudBufferAdd(&buf, " </os>\n") < 0)
> + goto no_memory;
> +
> + if (qemudBufferAdd(&buf, " <devices>\n") < 0)
> + goto no_memory;
> +
> + if (qemudBufferPrintf(&buf, " <emulator>%s</emulator>\n", vm->def.os.binary) < 0)
> + goto no_memory;
> +
> + 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 no_memory;
> +
> + if (qemudBufferPrintf(&buf, " <source %s='%s'/>\n", typeAttrs[disk->type], disk->src) < 0)
> + goto no_memory;
> +
> + if (qemudBufferPrintf(&buf, " <target dev='%s'/>\n", disk->dst) < 0)
> + goto no_memory;
> +
> + if (disk->readonly)
> + if (qemudBufferAdd(&buf, " <readonly/>\n") < 0)
> + goto no_memory;
> +
> + if (qemudBufferPrintf(&buf, " </disk>\n") < 0)
> + goto no_memory;
> +
> + disk = disk->next;
> + }
> +
> + net = vm->def.nets;
> + disk = vm->def.disks;
> + while (disk) {
> + const char *types[] = {
> + "user",
> + "tap",
> + "server",
> + "client",
> + "mcast",
> + "vde",
> + };
> + if (qemudBufferPrintf(&buf, " <interface type='%s'>\n",
> + types[net->type]) < 0)
> + goto no_memory;
> +
> + if (qemudBufferPrintf(&buf, " <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
> + net->mac[0], net->mac[1], net->mac[2],
> + net->mac[3], net->mac[4], net->mac[5]) < 0)
> + goto no_memory;
> +
> + if (qemudBufferPrintf(&buf, " </interface>\n") < 0)
> + goto no_memory;
> +
> + disk = disk->next;
> + }
> +
> + if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) {
> + if (vm->def.vncPort) {
> + qemudBufferPrintf(&buf, " <graphics type='vnc' port='%d'/>\n",
> + vm->def.id == -1 ? vm->def.vncPort : vm->def.vncActivePort);
> + } else {
> + qemudBufferPrintf(&buf, " <graphics type='vnc'/>\n");
> + }
> + }
> +
> + if (qemudBufferAdd(&buf, " </devices>\n") < 0)
> + goto no_memory;
> +
> +
> + if (qemudBufferAdd(&buf, "</domain>\n") < 0)
> + goto no_memory;
> +
> + return buf.data;
> +
> + no_memory:
> + qemudReportError(server, VIR_ERR_NO_MEMORY, "xml");
> + cleanup:
> + free(buf.data);
> + return NULL;
> +}
> +
> +
> +int qemudDeleteConfigXML(struct qemud_server *server, struct qemud_vm *vm) {
> + if (!vm->configFile[0]) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for guest %s", vm->def.name);
> + return -1;
> + }
> +
> + if (unlink(vm->configFile) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for guest %s", vm->def.name);
> + return -1;
> + }
> +
> + vm->configFile[0] = '\0';
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/config.h libvirt-qemu/qemud/config.h
> --- libvirt/qemud/config.h 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/config.h 2007-01-04 11:23:23.000000000 -0500
> @@ -0,2 +1,58 @@
> +/*
> + * config.h: VM configuration management
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#ifndef __QEMUD_CONFIG_H
> +#define __QEMUD_CONFIG_H
> +
> +#include "internal.h"
> +
> +int qemudBuildCommandLine(struct qemud_server *server,
> + 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_server *server,
> + struct qemud_vm *vm);
> +
> +int qemudDeleteConfigXML(struct qemud_server *server,
> + struct qemud_vm *vm);
> +
> +
> +#endif
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/dispatch.c libvirt-qemu/qemud/dispatch.c
> --- libvirt/qemud/dispatch.c 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/dispatch.c 2007-01-04 19:31:56.000000000 -0500
> @@ -0,0 +1,511 @@
> +/*
> + * dispatch.c: (De-)marshall wire messages to driver functions.
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#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"
> +
> +
> +static int qemudDispatchFailure(struct qemud_server *server ATTRIBUTE_UNUSED,
> + struct qemud_client *client ATTRIBUTE_UNUSED,
> + struct qemud_packet *out) {
> + out->header.type = QEMUD_PKT_FAILURE;
> + out->header.dataSize = sizeof(out->data.failureReply);
> + out->data.failureReply.code = server->errorCode;
> + strcpy(out->data.failureReply.message, server->errorMessage);
> + 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;
> +}
Hum, when qemudDomainCreate returns, we get a new vm, is that a new structure
which is gonna be ref-counted and cleaned up at some point ? I'm a bit lost
there, is that exactly the same scheme as for Xen domain instances ?
> +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 funcsRW[] = {
> + NULL, /* FAILURE code */
> + qemudDispatchGetProtocolVersion,
> + qemudDispatchGetVersion,
> + qemudDispatchListDomains,
> + qemudDispatchNumDomains,
> + qemudDispatchDomainCreate,
> + qemudDispatchDomainLookupByID,
> + qemudDispatchDomainLookupByUUID,
> + qemudDispatchDomainLookupByName,
> + qemudDispatchDomainSuspend,
> + qemudDispatchDomainResume,
> + qemudDispatchDomainDestroy,
> + qemudDispatchDomainGetInfo,
> + qemudDispatchDomainSave,
> + qemudDispatchDomainRestore,
> + qemudDispatchDumpXML,
> + qemudDispatchListDefinedDomains,
> + qemudDispatchNumDefinedDomains,
> + qemudDispatchDomainStart,
> + qemudDispatchDomainDefine,
> + qemudDispatchDomainUndefine
> +};
> +
> +clientFunc funcsRO[] = {
> + NULL, /* FAILURE code */
> + qemudDispatchGetProtocolVersion,
> + qemudDispatchGetVersion,
> + qemudDispatchListDomains,
> + qemudDispatchNumDomains,
> + NULL,
> + qemudDispatchDomainLookupByID,
> + qemudDispatchDomainLookupByUUID,
> + qemudDispatchDomainLookupByName,
> + NULL,
> + NULL,
> + NULL,
> + qemudDispatchDomainGetInfo,
> + NULL,
> + NULL,
> + qemudDispatchDumpXML,
> + qemudDispatchListDefinedDomains,
> + qemudDispatchNumDefinedDomains,
> + NULL,
> + NULL,
> + NULL,
> +};
> +
> +/*
> + * 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
> + *
> + * 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) {
> + clientFunc *funcs;
> + printf("> Dispatching request %d readonly ? %d\n", in->header.type, client->readonly);
> +
> + server->errorCode = 0;
> + server->errorMessage[0] = '\0';
> +
> + memset(out, 0, sizeof(struct qemud_packet));
> +
> + if (in->header.type >= (sizeof(funcsRW)/sizeof(clientFunc))) {
> + printf("Illegal request type\n");
> + return -1;
> + }
> +
> + if (in->header.type == QEMUD_PKT_FAILURE) {
> + printf("Illegal request type\n");
> + return -1;
> + }
> +
> + if (client->readonly)
> + funcs = funcsRO;
> + else
> + funcs = funcsRW;
> +
> + if (!funcs[in->header.type]) {
> + qemudReportError(server, VIR_ERR_OPERATION_DENIED, NULL);
> + qemudDispatchFailure(server, client, out);
> + } else {
> + if ((funcs[in->header.type])(server, client, in, out) < 0) {
> + printf("Dispatch failed\n");
> + return -1;
> + }
> + }
> +
> + printf("< Returning reply %d (%d bytes)\n",
> + out->header.type, out->header.dataSize);
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/dispatch.h libvirt-qemu/qemud/dispatch.h
> --- libvirt/qemud/dispatch.h 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/dispatch.h 2007-01-04 08:37:59.000000000 -0500
> @@ -0,0 +1,43 @@
> +/*
> + * dispatch.h: (De-)marshall wire messages to driver functions.
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +
> +#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
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/driver.c libvirt-qemu/qemud/driver.c
> --- libvirt/qemud/driver.c 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/driver.c 2007-01-04 12:21:02.000000000 -0500
> @@ -0,0 +1,359 @@
> +/*
> + * driver.c: core driver methods for managing qemu guests
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#include <sys/types.h>
> +#include <dirent.h>
> +#include <limits.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <stdarg.h>
> +
> +#include <libvirt/virterror.h>
> +
> +#include "internal.h"
> +#include "driver.h"
> +#include "config.h"
> +
> +void qemudReportError(struct qemud_server *server,
> + int code, const char *fmt, ...) {
> + va_list args;
> + server->errorCode = code;
> + if (fmt) {
> + va_start(args, fmt);
> + vsnprintf(server->errorMessage, QEMUD_MAX_ERROR_LEN-1, fmt, args);
> + va_end(args);
> + } else {
> + server->errorMessage[0] = '\0';
> + }
> +}
> +
> +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;
> + }
> +
> + 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;
> + }
> +
> + 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;
> + }
> +
> + 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) {
> + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
> + return -1;
> + }
> + if (vm->pid == -1) {
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
> + return -1;
> + }
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "suspend is not supported");
> + return -1;
> +}
> +
> +
> +int qemudDomainResume(struct qemud_server *server, int id) {
> + struct qemud_vm *vm = qemudFindVMByID(server, id);
> + if (!vm) {
> + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
> + return -1;
> + }
> + if (vm->pid == -1) {
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
> + return -1;
> + }
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "resume is not supported");
> + return -1;
> +}
> +
> +
> +int qemudDomainDestroy(struct qemud_server *server, int id) {
> + struct qemud_vm *vm = qemudFindVMByID(server, id);
> + if (!vm) {
> + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
> + return -1;
> + }
> + if (vm->pid == -1) {
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
> + 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) {
> + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid");
> + 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) {
> + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
> + return -1;
> + }
> + if (vm->pid == -1) {
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
> + return -1;
> + }
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "save is not supported");
> + return -1;
> +}
> +
> +
> +int qemudDomainRestore(struct qemud_server *server,
> + const char *path ATTRIBUTE_UNUSED) {
> + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "restore is not supported");
> + 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) {
> + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid");
> + return -1;
> + }
> +
> + vmxml = qemudGenerateXML(server, 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 (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) {
> + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid");
> + return -1;
> + }
> +
> + if (vm->pid != -1) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot delete active domain");
> + return -1;
> + }
> +
> + if (qemudDeleteConfigXML(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;
> + }
> +
> + qemudFreeVM(vm);
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/driver.h libvirt-qemu/qemud/driver.h
> --- libvirt/qemud/driver.h 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/driver.h 2007-01-04 08:38:07.000000000 -0500
> @@ -0,0 +1,89 @@
> +/*
> + * driver.h: core driver methods for managing qemu guests
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +
> +#ifndef QEMUD_DRIVER_H
> +#define QEMUD_DRIVER_H
> +
> +#include "internal.h"
> +
> +void qemudReportError(struct qemud_server *server,
> + int code, const char *fmt, ...);
> +
> +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
> +
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/internal.h libvirt-qemu/qemud/internal.h
> --- libvirt/qemud/internal.h 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/internal.h 2007-01-04 20:29:22.000000000 -0500
> @@ -0,0 +1,239 @@
> +/*
> + * internal.h: daemon data structure definitions
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +
> +#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
> +
> +
> +#define UUID_LEN 16
> +
> +/* Different types of QEMU acceleration possible */
> +enum qemud_vm_virt_type {
> + QEMUD_VIRT_QEMU,
> + QEMUD_VIRT_KQEMU,
> + QEMUD_VIRT_KVM,
> +};
> +
> +/* Stores the per-client connection state */
> +struct qemud_client {
> + int fd;
> + int readonly;
> + struct qemud_packet incoming;
> + unsigned int incomingReceived;
> + struct qemud_packet outgoing;
> + unsigned int outgoingSent;
> + int tx;
> + struct qemud_client *next;
> +};
> +
> +
> +/* Two types of disk backends */
> +enum qemud_vm_disk_type {
> + QEMUD_DISK_BLOCK,
> + QEMUD_DISK_FILE
> +};
> +
> +/* Three types of disk frontend */
> +enum qemud_vm_disk_device {
> + QEMUD_DISK_DISK,
> + QEMUD_DISK_CDROM,
> + QEMUD_DISK_FLOPPY,
> +};
> +
> +/* Stores the virtual disk configuration */
> +struct qemud_vm_disk_def {
> + int type;
> + int device;
> + char src[PATH_MAX];
> + char dst[NAME_MAX];
> + int readonly;
> +
> + struct qemud_vm_disk_def *next;
> +};
> +
> +#define QEMUD_MAC_ADDRESS_LEN 6
> +#define QEMUD_OS_TYPE_MAX_LEN 10
> +#define QEMUD_OS_ARCH_MAX_LEN 10
> +#define QEMUD_OS_MACHINE_MAX_LEN 10
> +
> +/* 5 different types of networking config */
> +enum qemud_vm_net_type {
> + QEMUD_NET_USER,
> + QEMUD_NET_TAP,
> + QEMUD_NET_SERVER,
> + QEMUD_NET_CLIENT,
> + QEMUD_NET_MCAST,
> + /* QEMUD_NET_VDE*/
> +};
> +
> +/* Stores the virtual network interface configuration */
> +struct qemud_vm_net_def {
> + int type;
> + int vlan;
> + unsigned char mac[QEMUD_MAC_ADDRESS_LEN];
> + union {
> + struct {
> + char ifname[NAME_MAX];
> + char script[PATH_MAX];
> + } tap;
> + struct {
> + struct sockaddr_in listen;
> + int port;
> + } server;
> + struct {
> + struct sockaddr_in connect;
> + int port;
> + } client;
> + struct {
> + struct sockaddr_in group;
> + int port;
> + } mcast;
> + struct {
> + char vlan[PATH_MAX];
> + } vde;
> + } dst;
> +
> + struct qemud_vm_net_def *next;
> +};
> +
> +/* 3 possible boot devices */
> +enum qemud_vm_boot_order {
> + QEMUD_BOOT_FLOPPY,
> + QEMUD_BOOT_CDROM,
> + QEMUD_BOOT_DISK
> +};
> +
> +/* 3 possible graphics console modes */
> +enum qemud_vm_grapics_type {
> + QEMUD_GRAPHICS_NONE,
> + QEMUD_GRAPHICS_SDL,
> + QEMUD_GRAPHICS_VNC,
> +};
> +
> +/* Operating system configuration data & machine / arch */
> +struct qemud_vm_os_def {
> + char type[QEMUD_OS_TYPE_MAX_LEN];
> + char arch[QEMUD_OS_ARCH_MAX_LEN];
> + char machine[QEMUD_OS_MACHINE_MAX_LEN];
> + int bootOrder[3];
> + char kernel[PATH_MAX];
> + char initrd[PATH_MAX];
> + char cmdline[PATH_MAX];
> + char binary[PATH_MAX];
> +};
> +
> +/* Guest VM main configuration */
> +struct qemud_vm_def {
> + int id;
> + int virtType;
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + char name[QEMUD_MAX_NAME_LEN];
> +
> + int memory;
> + int vcpus;
> +
> + struct qemud_vm_os_def os;
> +
> + int graphicsType;
> + int vncPort;
> + int vncActivePort;
> +
> + int ndisks;
> + struct qemud_vm_disk_def *disks;
> +
> + int nnets;
> + struct qemud_vm_net_def *nets;
> +};
> +
> +/* Guest VM runtime state */
> +struct qemud_vm {
> + int stdout;
> + int stderr;
> + int monitor;
> + int pid;
> +
> + char configFile[PATH_MAX];
> +
> + struct qemud_vm_def def;
> +
> + struct qemud_vm *next;
> +};
> +
> +struct qemud_socket {
> + int fd;
> + int readonly;
> + struct qemud_socket *next;
> +};
> +
> +/* Main server state */
> +struct qemud_server {
> + int nsockets;
> + struct qemud_socket *sockets;
> + int qemuVersion;
> + int nclients;
> + struct qemud_client *clients;
> + int nvmfds;
> + int nactivevms;
> + struct qemud_vm *activevms;
> + int ninactivevms;
> + struct qemud_vm *inactivevms;
> + int nextvmid;
> + char configDir[PATH_MAX];
> + char errorMessage[QEMUD_MAX_ERROR_LEN];
> + int errorCode;
> +};
> +
> +int qemudStartVMDaemon(struct qemud_server *server,
> + struct qemud_vm *vm);
> +
> +int qemudShutdownVMDaemon(struct qemud_server *server,
> + struct qemud_vm *vm);
> +
> +
> +#endif
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/Makefile libvirt-qemu/qemud/Makefile
> diff -ruN libvirt/qemud/Makefile.am libvirt-qemu/qemud/Makefile.am
> --- libvirt/qemud/Makefile.am 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/Makefile.am 2007-01-04 07:34:31.000000000 -0500
> @@ -0,0 +1,17 @@
> +## Process this file with automake to produce Makefile.in
> +
> +INCLUDES = @LIBXML_CFLAGS@
> +
> +libexec_PROGRAMS = libvirt_qemud
> +
> +libvirt_qemud_SOURCES = qemud.c internal.h protocol.h \
> + driver.c driver.h \
> + dispatch.c dispatch.h \
> + config.c config.h
> +libvirt_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
> +libvirt_qemud_LDFLAGS = $(LIBXML_LIBS)
> +libvirt_qemud_DEPENDENCIES =
> +libvirt_qemud_LDADD =
> +
> diff -ruN libvirt/qemud/protocol.h libvirt-qemu/qemud/protocol.h
> --- libvirt/qemud/protocol.h 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/protocol.h 2007-01-04 20:32:08.000000000 -0500
> @@ -0,0 +1,206 @@
> +/*
> + * protocol.h: wire protocol message format & data structures
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +
> +#ifndef QEMUD_PROTOCOL_H__
> +#define QEMUD_PROTOCOL_H__
> +
> +/* List of different packet types which can be sent */
> +enum {
> + QEMUD_PKT_FAILURE,
> + QEMUD_PKT_GET_PROTOCOL_VERSION,
I always feel safer to initialize at least the first values in an enum.
for example QEMUD_PKT_FAILURE = 0, not strictly necessary but for client/server
stuff values I feel just better ... somehow I don't trust the way compilers
allocate enums .
> + 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
> +} qemud_packet_type;
> +
> +
> +#define QEMUD_DEFAULT_PORT_STR "8123"
> +
> +#define QEMUD_PROTOCOL_VERSION 1
> +#define QEMUD_UUID_RAW_LEN 16
> +#define QEMUD_MAX_NAME_LEN 50
> +#define QEMUD_MAX_XML_LEN 4096
> +#define QEMUD_MAX_NUM_DOMAINS 100
> +#define QEMUD_MAX_ERROR_LEN 1024
> +
> +/* Possible guest VM states */
> +enum {
> + QEMUD_STATE_RUNNING = 1,
> + QEMUD_STATE_PAUSED,
> + QEMUD_STATE_STOPPED,
> +} qemud_domain_runstate;
> +
> +/* Each packets has at least a fixed size header */
> +struct qemud_packet_header {
> + unsigned int type;
> + /* Stores the size of the data struct matching
> + the type arg.
> + Must be <= sizeof(union qemudPacketData) */
> + unsigned int dataSize;
> +};
> +
> +/* Most packets also have some message specific data */
> +union qemud_packet_data {
> + struct {
> + int code;
> + char message[QEMUD_MAX_ERROR_LEN];
> + } failureReply;
> + struct {
> + int version;
> + } getProtocolVersionReply;
> + struct {
> + int version;
> + } getVersionReply;
> + struct {
> + int numDomains;
> + int domains[QEMUD_MAX_NUM_DOMAINS];
> + } listDomainsReply;
> + struct {
> + int numDomains;
> + } numDomainsReply;
> + struct {
> + char xml[QEMUD_MAX_XML_LEN];
> + } domainCreateRequest;
> + struct {
> + int id;
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + char name[QEMUD_MAX_NAME_LEN];
> + } domainCreateReply;
> + struct {
> + int id;
> + } domainLookupByIDRequest;
> + struct {
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + char name[QEMUD_MAX_NAME_LEN];
> + } domainLookupByIDReply;
> + struct {
> + char name[QEMUD_MAX_NAME_LEN];
> + } domainLookupByNameRequest;
> + struct {
> + int id;
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + } domainLookupByNameReply;
> + struct {
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + } domainLookupByUUIDRequest;
> + struct {
> + int id;
> + char name[QEMUD_MAX_NAME_LEN];
> + } domainLookupByUUIDReply;
> + struct {
> + int id;
> + } domainSuspendRequest;
> + struct {
> + int id;
> + } domainResumeRequest;
> + struct {
> + } domainResumeReply;
> + struct {
> + int id;
> + } domainDestroyRequest;
> + struct {
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + } domainGetInfoRequest;
> + struct {
> + int runstate;
> + unsigned long long cpuTime;
> + unsigned long memory;
> + unsigned int nrVirtCpu;
> + } domainGetInfoReply;
> + struct {
> + int id;
> + char file[PATH_MAX];
> + } domainSaveRequest;
> + struct {
> + char file[PATH_MAX];
> + } domainRestoreRequest;
> + struct {
> + int id;
> + } domainRestoreReply;
> + struct {
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + } domainDumpXMLRequest;
> + struct {
> + char xml[QEMUD_MAX_XML_LEN];
> + } domainDumpXMLReply;
> + struct {
> + int numDomains;
> + char domains[QEMUD_MAX_NUM_DOMAINS][QEMUD_MAX_NAME_LEN];
> + } listDefinedDomainsReply;
> + struct {
> + int numDomains;
> + } numDefinedDomainsReply;
> + struct {
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + } domainStartRequest;
> + struct {
> + int id;
> + } domainStartReply;
> + struct {
> + char xml[QEMUD_MAX_XML_LEN];
> + } domainDefineRequest;
> + struct {
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + char name[QEMUD_MAX_NAME_LEN];
> + } domainDefineReply;
> + struct {
> + unsigned char uuid[QEMUD_UUID_RAW_LEN];
> + } domainUndefineRequest;
> +};
As suggested better to stick with predefined types with size here.
> +/* Each packet has header & data */
> +struct qemud_packet {
> + struct qemud_packet_header header;
> + union qemud_packet_data data;
> +};
> +
> +
> +#endif
> +
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> diff -ruN libvirt/qemud/qemud.c libvirt-qemu/qemud/qemud.c
> --- libvirt/qemud/qemud.c 1969-12-31 19:00:00.000000000 -0500
> +++ libvirt-qemu/qemud/qemud.c 2007-01-04 20:55:25.000000000 -0500
> @@ -0,0 +1,946 @@
> +/*
> + * qemud.c: daemon start of day, guest process & i/o management
> + *
> + * Copyright (C) 2006, 2007 Red Hat, Inc.
> + * Copyright (C) 2006 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#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 <netinet/in.h>
> +#include <netdb.h>
> +#include <stdlib.h>
> +#include <pwd.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <getopt.h>
> +
> +#include <libvirt/virterror.h>
> +
> +#include "internal.h"
> +#include "dispatch.h"
> +#include "driver.h"
> +#include "config.h"
> +
> +static void reapchild(int sig ATTRIBUTE_UNUSED) {
> + /* We explicitly waitpid the child later */
> +}
> +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;
> +}
> +
> +
> +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;
> +}
> +
> +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;
> + }
> + }
> +}
> +
> +static int qemudListenUnix(struct qemud_server *server,
> + const char *path, int readonly) {
> + struct qemud_socket *sock = calloc(1, sizeof(struct qemud_socket));
> + struct sockaddr_un addr;
> + mode_t oldmask;
> +
> + if (!sock)
> + return -1;
> +
> + sock->readonly = readonly;
> + sock->next = server->sockets;
> + server->sockets = sock;
> + server->nsockets++;
> +
> + if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
> + return -1;
> +
> + if (qemudSetCloseExec(sock->fd) < 0)
> + return -1;
> + if (qemudSetNonBlock(sock->fd) < 0)
> + return -1;
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.sun_family = AF_UNIX;
> + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
> + /* Use '@' to indicate abstract socket namespace */
> + if (path[0] == '@') {
> + addr.sun_path[0] = '\0';
> + } else {
> + unlink(addr.sun_path);
> + }
> +
> + if (readonly)
> + oldmask = umask(~(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH));
> + else
> + oldmask = umask(~(S_IRUSR | S_IWUSR));
> + if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
sock is not freed here, it's still linked in the server sockets list,
rather bizarre, no ?
> + return -1;
> + umask(oldmask);
> +
> + if (listen(sock->fd, 30) < 0)
Same here, I don't understand the error handling :-)
> + return -1;
> +
> + return 0;
> +}
> +
> +static int qemudListenAddr(struct qemud_server *server,
> + const char *addr, int readonly) {
> + struct addrinfo *ai;
> + struct addrinfo hints;
> + struct addrinfo *tmp;
> + char *node, *offset;
> + const char *service;
> +
> + node = strdup(addr);
> + if (!node)
> + return -1;
> +
> + if (!(offset = strstr(node, ","))) {
> + service = QEMUD_DEFAULT_PORT_STR;
> + } else {
> + offset[0] = '\0';
> + service = offset + 1;
> + }
> +
> + memset (&hints, '\0', sizeof (hints));
> + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG | AI_NUMERICSERV;
> + hints.ai_socktype = SOCK_STREAM;
> + if (getaddrinfo (node, service, &hints, &ai) < 0) {
> + free(node);
> + return -1;
> + }
> + free(node);
> +
> + tmp = ai;
> + while (tmp != NULL) {
> + int opt = 1;
> + struct qemud_socket *sock = calloc(1, sizeof(struct qemud_socket));
> + if (!sock)
> + goto error;
> +
> + sock->readonly = readonly;
> + sock->next = server->sockets;
> + server->sockets = sock;
> + server->nsockets++;
> +
> + if ((sock->fd = socket (tmp->ai_family, tmp->ai_socktype,
> + tmp->ai_protocol)) < 0)
> + goto error;
> +
> + if (qemudSetCloseExec(sock->fd) < 0)
> + goto error;
> +
> + if (qemudSetNonBlock(sock->fd) < 0)
> + goto error;
> +
> + if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR,
> + &opt, sizeof (opt)) < 0)
> + goto error;
> +
> + if (bind (sock->fd, tmp->ai_addr, tmp->ai_addrlen) < 0)
> + goto error;
> +
> + if (listen (sock->fd, SOMAXCONN) < 0)
> + goto error;
> +
> + tmp = tmp->ai_next;
> + }
> +
> + freeaddrinfo (ai);
> + return 0;
> +
> + error:
> + freeaddrinfo (ai);
> + return -1;
> +}
> +
> +static int qemudListen(struct qemud_server *server,
> + const char *listenAddr) {
> + int readonly = 0;
> +
> + if (!strncmp(listenAddr, "ro:", 3)) {
> + readonly = 1;
> + listenAddr += 3;
> + }
wouldn't "ro-" (or ro+ or ro.) be cleaner, allowing to stick it in front
of any URI and still keeping it URI-valid ?
> + if (listenAddr[0] == '/' ||
> + (listenAddr[0] == '@' && listenAddr[1] == '/')) {
> + if (qemudListenUnix(server, listenAddr, readonly) < 0)
> + return -1;
> + } else {
> + if (qemudListenAddr(server, listenAddr, readonly) < 0)
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static struct qemud_server *qemudInitialize(char **listenAddrs,
> + int naddrs) {
> + struct qemud_server *server;
> + int i;
> + char path[PATH_MAX];
> + struct passwd *pw;
> + int uid, ret;
> +
> + if (!(server = calloc(1, sizeof(struct qemud_server))))
> + return NULL;
> +
> + /* 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;
> +
> + 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;
> + }
Hum, at some point the directory modes and ownership should be tested and
possibly created, and I guess the sooner the better, why not there ? But I
may have missed the check (or not gone there yet :-)
> + for (i = 0 ; i < naddrs ; i++) {
> + if (qemudListen(server, listenAddrs[i]) < 0)
> + goto cleanup;
> + }
> +
> + if (qemudScanConfigs(server) < 0) {
> + goto cleanup;
> + }
> +
> + return server;
> +
> + cleanup:
> + if (server) {
> + struct qemud_socket *sock = server->sockets;
> + while (sock) {
> + close(sock->fd);
> + sock = sock->next;
> + }
> +
> + free(server);
> + }
> + return NULL;
> +}
> +
> +
> +static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) {
> + int fd;
> + struct sockaddr_storage addr;
> + unsigned int addrlen = sizeof(addr);
> + struct qemud_client *client;
> +
> + if ((fd = accept(sock->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));
calloc return check missing, close of fd in that case
> + client->fd = fd;
> + client->next = server->clients;
> + client->readonly = sock->readonly;
> + server->clients = client;
> + server->nclients++;
> +
> + return 0;
> +}
> +
> +
> +int qemudStartVMDaemon(struct qemud_server *server,
> + struct qemud_vm *vm) {
> + char **argv = NULL;
> + int argc = 0;
> + int pid;
> + int i, ret = -1;
> + int stdinfd = -1;
> + int pipeout[2] = {-1,-1};
> + int pipeerr[2] = {-1,-1};
> +
> + if (vm->def.vncPort < 0)
> + vm->def.vncActivePort = 5900 + server->nextvmid;
> + else
> + vm->def.vncActivePort = vm->def.vncPort;
> +
> + if (qemudBuildCommandLine(server, vm, &argv, &argc) < 0)
> + return -1;
> +
> + if (1) { /* XXX debug stuff */
> + printf("Spawn QEMU '");
> + for (i = 0 ; i < argc; i++) {
> + printf("%s", argv[i]);
> + if (i == (argc-1))
> + printf("'\n");
> + else
> + printf(" ");
> + }
> + }
> +
> + if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open %s", _PATH_DEVNULL);
> + goto cleanup;
> + }
> +
> + if (pipe(pipeout) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe");
> + goto cleanup;
> + }
> +
> + if (pipe(pipeerr) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe");
> + goto cleanup;
> + }
> +
> + if ((pid = fork()) < 0) {
> + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot fork child process");
> + goto cleanup;
> + }
> +
> + if (pid) { /* parent */
> + close(stdinfd);
> + 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 { /* child */
> + int null;
> + if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0)
> + _exit(1);
> +
> + if (close(pipeout[0]) < 0)
> + _exit(1);
> + if (close(pipeerr[0]) < 0)
> + _exit(1);
> +
> + if (dup2(stdinfd, STDIN_FILENO) < 0)
> + _exit(1);
> + 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);
> +
> + execvp(argv[0], argv);
> +
> + _exit(1);
> + }
> +
> + ret = 0;
> +
> + cleanup:
> +
> + for (i = 0 ; i < argc ; i++) {
> + free(argv[i]);
> + }
> + free(argv);
> +
> + return ret;
> +}
> +
> +
> +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);
> +}
> +
> +
> +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;
> +}
> +
> +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:
> + if (got >= sizeof(struct qemud_packet_header)) {
> + want = sizeof(struct qemud_packet_header) + client->incoming.header.dataSize - got;
> + } else {
> + want = sizeof(struct qemud_packet_header) - got;
> + }
> +
> + 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 */
> + if (client->incoming.header.dataSize > sizeof(union qemud_packet_data)) {
> + printf("Bogus data size %u\n", client->incoming.header.dataSize);
> + qemudDispatchClientFailure(server, client);
> + return;
> + }
> + if (client->incoming.header.dataSize) {
> + printf("- Restarting recv to process body (%d bytes)\n", client->incoming.header.dataSize);
> + goto restart;
> + }
> + }
> +
> + /* If we've finished body, dispatch the request */
> + if (ret == want) {
> + if (qemudDispatchClientRequest(server, client) < 0)
> + qemudDispatchClientFailure(server, 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;
> + if ((ret = write(client->fd, data+sent, todo)) < 0) {
> + if (errno != EAGAIN) {
> + qemudDispatchClientFailure(server, client);
> + return;
> + }
> + return;
> + }
> + client->outgoingSent += ret;
> +
> + if (todo == ret) {
> + client->tx = 0;
> + }
> +}
> +
> +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;
> + }
> + write(STDOUT_FILENO, "[", 1);
> + write(STDOUT_FILENO, buf, ret);
> + write(STDOUT_FILENO, "]", 1);
> + }
> +}
> +
> +int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) {
> + struct qemud_vm *prev = NULL, *curr = server->activevms;
> +
> + /* Already cleaned-up */
> + if (vm->pid < 0)
> + return 0;
> +
> + kill(vm->pid, SIGTERM);
> +
> + /* Move it to inactive vm list */
> + while (curr) {
> + if (curr == vm) {
> + if (prev) {
> + prev->next = curr->next;
> + } else {
> + server->activevms = curr->next;
> + }
> + server->nactivevms--;
> +
> + curr->next = server->inactivevms;
> + server->inactivevms = curr;
> + server->ninactivevms++;
> + break;
> + }
> + prev = curr;
> + curr = curr->next;
> + }
> +
> + 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;
> +
> + if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) {
> + kill(vm->pid, SIGKILL);
> + if (waitpid(vm->pid, NULL, 0) != vm->pid) {
> + printf("Got unexpected pid, damn\n");
> + }
> + }
> +
> + vm->pid = -1;
> + vm->def.id = -1;
> +
> + return 0;
> +}
> +
> +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;
> +}
> +static int qemudDispatchVMMonitor(struct qemud_server *server ATTRIBUTE_UNUSED,
> + struct qemud_vm *vm ATTRIBUTE_UNUSED) {
> + return -1;
> +}
> +static int qemudDispatchVMFailure(struct qemud_server *server, struct qemud_vm *vm,
> + int fd ATTRIBUTE_UNUSED) {
> + if (qemudShutdownVMDaemon(server, vm) < 0)
> + return -1;
> + return 0;
> +}
> +
> +
> +static int qemudDispatchPoll(struct qemud_server *server, struct pollfd *fds) {
> + struct qemud_socket *sock = server->sockets;
> + struct qemud_client *client = server->clients;
> + struct qemud_vm *vm = server->activevms;
> + struct qemud_vm *tmp;
> + int ret = 0;
> + int fd = 0;
> +
> + while (sock) {
> + struct qemud_socket *next = sock->next;
> + if (fds[fd].revents)
> + if (qemudDispatchServer(server, sock) < 0)
> + return -1;
> + fd++;
> + sock = next;
> + }
> +
> + 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;
> + }
> + 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;
> + }
> +
> + /* Cleanup any VMs which shutdown & dont have an associated
> + config file */
> + vm = server->inactivevms;
> + tmp = NULL;
> + while (vm) {
> + if (!vm->configFile[0]) {
> + struct qemud_vm *next = vm->next;
> + if (tmp) {
> + tmp->next = next;
> + } else {
> + server->inactivevms = next;
> + }
> + qemudFreeVM(vm);
> + vm = next;
> + } else {
> + tmp = vm;
> + vm = vm->next;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static void qemudPreparePoll(struct qemud_server *server, struct pollfd *fds) {
> + int fd = 0;
> +
> + for (struct qemud_socket *sock = server->sockets ; sock ; sock = sock->next) {
> + fds[fd].fd = sock->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++;
> + }
> + 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++;
> + }
> + }
> +}
> +
> +
> +
> +static int qemudOneLoop(struct qemud_server *server, int timeout) {
> + int nfds = server->nsockets + server->nclients + server->nvmfds;
> + struct pollfd fds[nfds];
> + int thistimeout = -1;
> + int ret;
> +
> + /* If we have no clients or vms, then timeout after
> + 30 seconds, letting daemon exit */
> + if (timeout > 0 &&
> + !server->nclients &&
> + !server->nactivevms)
> + thistimeout = timeout;
> +
> + qemudPreparePoll(server, fds);
> +
> + retry:
> +
> + if ((ret = poll(fds, nfds, thistimeout * 1000)) < 0) {
> + if (errno == EINTR) {
> + goto retry;
> + }
> + return -1;
> + }
> +
> + /* Must have timed out */
> + if (ret == 0)
> + return -1;
> +
> + if (qemudDispatchPoll(server, fds) < 0)
> + return -1;
> +
> + return 0;
> +}
> +
> +static int qemudRunLoop(struct qemud_server *server, int timeout) {
> + int ret;
> +
> + while ((ret = qemudOneLoop(server, timeout)) == 0)
> + ;
> +
> + return ret == -1 ? -1 : 0;
> +}
> +
> +static void qemudCleanup(struct qemud_server *server) {
> + struct qemud_socket *sock = server->sockets;
> + while (sock) {
> + close(sock->fd);
> + sock = sock->next;
> + }
> + free(server);
> +}
> +
> +#define MAX_LISTEN 5
> +int main(int argc, char **argv) {
> + int daemon = 0;
> + int verbose = 0;
> + int timeout = -1;
> + char *listenAddrs[MAX_LISTEN];
> + int naddrs = 0;
> +
> + struct option opts[] = {
> + { "verbose", no_argument, &verbose, 1},
> + { "daemon", no_argument, &daemon, 1},
> + { "timeout", required_argument, 0, 't'},
> + { "listen", required_argument, 0, 'l' },
> + {0, 0, 0, 0}
> + };
> +
> + while (1) {
> + int optidx = 0;
> + int c;
> + char *tmp;
> +
> + c = getopt_long(argc, argv, "vdt:u:U:l:L:", opts, &optidx);
> +
> + if (c == -1)
> + break;
> +
> + switch (c) {
> + case 0:
> + /* Got one of the flags */
> + break;
> + case 't':
> + timeout = strtol(optarg, &tmp, 10);
> + if (!tmp)
> + timeout = -1;
> + if (timeout <= 0)
> + timeout = -1;
> + break;
> + case 'l':
> + if (naddrs >= MAX_LISTEN)
> + abort();
> + if (!(listenAddrs[naddrs] = strdup(optarg)))
> + abort();
> + naddrs++;
> + break;
> + case '?':
> + /* getopt_long already printed error message */
> + break;
> + default:
> + abort();
> + }
> + }
> +
> + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
> + return 3;
> + if (signal(SIGCHLD, reapchild) == SIG_ERR)
> + return 3;
> +
> + if (daemon) {
> + int pid = qemudGoDaemon();
> + if (pid < 0)
> + return 1;
> + if (pid > 0)
> + return 0;
> + }
> +
> + struct qemud_server *server = qemudInitialize(listenAddrs, naddrs);
> +
> + if (!server)
> + return 2;
> +
> + qemudRunLoop(server, timeout);
> +
> + qemudCleanup(server);
> +
> + return 0;
> +}
> +
> +/*
> + * Local variables:
> + * indent-tabs-mode: nil
> + * c-indent-level: 4
> + * c-basic-offset: 4
> + * tab-width: 4
> + * End:
> + */
> --
> Libvir-list mailing list
> Libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list
--
Red Hat Virtualization group http://redhat.com/virtualization/
Daniel Veillard | virtualization library http://libvirt.org/
veillard at redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/
http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
More information about the libvir-list
mailing list