Added QEMU support

This commit is contained in:
Daniel P. Berrange 2007-02-14 01:40:09 +00:00
parent ed02290129
commit 23ad665cb0
24 changed files with 6655 additions and 7 deletions

View File

@ -1,3 +1,20 @@
Tue Feb 13 19:29:35 EST 2007 Daniel Berrange <berrange@redhat.com>
* src/qemu_internal.h, src/qemu_internal.c, src/Makefile.am,
src/driver.h, src/libvirt.c: Added a new driver to talk to
the QEMU daemon
* src/virterror.c, include/libvirt/virterror.c: Added new
error domain for QEMU.
* qemud/*: Added a daemon service for managing QEMU machines
via the libvirt qemu_internal driver
* src/virsh.c: use a read-write connection by default for QEMU
urls.
* configure.in, Makefile.am: Added qemud subdirectory.
Thu Feb 8 12:59:14 EST 2007 Daniel Berrange <berrange@redhat.com>
* src/xml.c, src/xend_internal.c, src/xend_internal.h: Remove

View File

@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
SUBDIRS = src include docs @PYTHON_SUBDIR@ tests proxy po
SUBDIRS = src qemud proxy include docs @PYTHON_SUBDIR@ tests po
ACLOCAL_AMFLAGS = -I m4

View File

@ -288,6 +288,7 @@ AC_OUTPUT(Makefile src/Makefile include/Makefile docs/Makefile \
po/Makefile.in \
include/libvirt/Makefile include/libvirt/libvirt.h \
python/Makefile python/tests/Makefile \
qemud/Makefile \
tests/Makefile proxy/Makefile \
tests/xml2sexprdata/Makefile \
tests/sexpr2xmldata/Makefile \

View File

@ -46,7 +46,8 @@ typedef enum {
VIR_FROM_DOM, /* Error when operating on a domain */
VIR_FROM_RPC, /* Error in the XML-RPC code */
VIR_FROM_PROXY, /* Error in the proxy code */
VIR_FROM_CONF /* Error in the configuration file handling */
VIR_FROM_CONF, /* Error in the configuration file handling */
VIR_FROM_QEMU, /* Error at the QEMU daemon */
} virErrorDomain;

7
qemud/.cvsignore Normal file
View File

@ -0,0 +1,7 @@
Makefile
Makefile.in
.deps
.libs
*.lo
*.la
libvirt_qemud

19
qemud/Makefile.am Normal file
View File

@ -0,0 +1,19 @@
## 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
#-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L
libvirt_qemud_CFLAGS = \
-I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \
-Werror -Wall -Wextra -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \
-DSYSCONF_DIR="\"$(sysconfdir)\""
libvirt_qemud_LDFLAGS = $(LIBXML_LIBS)
libvirt_qemud_DEPENDENCIES =
libvirt_qemud_LDADD =

1429
qemud/conf.c Normal file

File diff suppressed because it is too large Load Diff

56
qemud/conf.h Normal file
View File

@ -0,0 +1,56 @@
/*
* 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@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:
*/

1429
qemud/config.c Normal file

File diff suppressed because it is too large Load Diff

56
qemud/config.h Normal file
View File

@ -0,0 +1,56 @@
/*
* 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@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:
*/

580
qemud/dispatch.c Normal file
View File

@ -0,0 +1,580 @@
/*
* 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@redhat.com>
*/
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/utsname.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 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 qemudDispatchGetNodeInfo(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
struct utsname info;
if (in->header.dataSize != 0)
return -1;
if (uname(&info) < 0) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
return 0;
}
if (qemudGetCPUInfo(&out->data.getNodeInfoReply.cpus,
&out->data.getNodeInfoReply.mhz,
&out->data.getNodeInfoReply.nodes,
&out->data.getNodeInfoReply.sockets,
&out->data.getNodeInfoReply.cores,
&out->data.getNodeInfoReply.threads) < 0) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
return 0;
}
if (qemudGetMemInfo(&out->data.getNodeInfoReply.memory) < 0) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
return 0;
}
out->header.type = QEMUD_PKT_GET_NODEINFO;
out->header.dataSize = sizeof(out->data.getNodeInfoReply);
strncpy(out->data.getNodeInfoReply.model, info.machine, sizeof(out->data.getNodeInfoReply.model));
out->data.getNodeInfoReply.model[sizeof(out->data.getNodeInfoReply.model)-1] = '\0';
return 0;
}
static int qemudDispatchListDomains(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
int i, ndomains, domains[QEMUD_MAX_NUM_DOMAINS];
if (in->header.dataSize != 0)
return -1;
ndomains = qemudListDomains(server,
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);
for (i = 0 ; i < ndomains ; i++) {
out->data.listDomainsReply.domains[i] = domains[i];
}
out->data.listDomainsReply.numDomains = ndomains;
}
return 0;
}
static int qemudDispatchNumDomains(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != 0)
return -1;
int ndomains = qemudNumDomains(server);
if (ndomains < 0) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_NUM_DOMAINS;
out->header.dataSize = sizeof(out->data.numDomainsReply);
out->data.numDomainsReply.numDomains = ndomains;
}
return 0;
}
static int qemudDispatchDomainCreate(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != sizeof(in->data.domainCreateRequest))
return -1;
in->data.domainCreateRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0';
struct qemud_vm *vm = qemudDomainCreate(server, in->data.domainCreateRequest.xml);
if (!vm) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_DOMAIN_CREATE;
out->header.dataSize = sizeof(out->data.domainCreateReply);
out->data.domainCreateReply.id = vm->def.id;
memcpy(out->data.domainCreateReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN);
strncpy(out->data.domainCreateReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1);
out->data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
}
return 0;
}
static int qemudDispatchDomainLookupByID(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != sizeof(in->data.domainLookupByIDRequest))
return -1;
struct qemud_vm *vm = qemudFindVMByID(server, in->data.domainLookupByIDRequest.id);
if (!vm) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_ID;
out->header.dataSize = sizeof(out->data.domainLookupByIDReply);
memcpy(out->data.domainLookupByIDReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN);
strncpy(out->data.domainLookupByIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1);
out->data.domainLookupByIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
}
return 0;
}
static int qemudDispatchDomainLookupByUUID(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != sizeof(in->data.domainLookupByUUIDRequest))
return -1;
struct qemud_vm *vm = qemudFindVMByUUID(server, in->data.domainLookupByUUIDRequest.uuid);
if (!vm) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID;
out->header.dataSize = sizeof(out->data.domainLookupByUUIDReply);
out->data.domainLookupByUUIDReply.id = vm->def.id;
strncpy(out->data.domainLookupByUUIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1);
out->data.domainLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
}
return 0;
}
static int qemudDispatchDomainLookupByName(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != sizeof(in->data.domainLookupByNameRequest))
return -1;
/* Paranoia NULL termination */
in->data.domainLookupByNameRequest.name[QEMUD_MAX_NAME_LEN-1] = '\0';
struct qemud_vm *vm = qemudFindVMByName(server, in->data.domainLookupByNameRequest.name);
if (!vm) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME;
out->header.dataSize = sizeof(out->data.domainLookupByNameReply);
out->data.domainLookupByNameReply.id = vm->def.id;
memcpy(out->data.domainLookupByNameReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN);
}
return 0;
}
static int qemudDispatchDomainSuspend(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != sizeof(in->data.domainSuspendRequest))
return -1;
int ret = qemudDomainSuspend(server, in->data.domainSuspendRequest.id);
if (ret < 0) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_DOMAIN_SUSPEND;
out->header.dataSize = 0;
}
return 0;
}
static int qemudDispatchDomainResume(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != sizeof(in->data.domainResumeRequest))
return -1;
int ret = qemudDomainResume(server, in->data.domainResumeRequest.id);
if (ret < 0) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_DOMAIN_RESUME;
out->header.dataSize = 0;
}
return 0;
}
static int qemudDispatchDomainDestroy(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
if (in->header.dataSize != sizeof(in->data.domainDestroyRequest))
return -1;
int ret = qemudDomainDestroy(server, in->data.domainDestroyRequest.id);
if (ret < 0) {
if (qemudDispatchFailure(server, client, out) < 0)
return -1;
} else {
out->header.type = QEMUD_PKT_DOMAIN_DESTROY;
out->header.dataSize = 0;
}
return 0;
}
static int qemudDispatchDomainGetInfo(struct qemud_server *server, struct qemud_client *client,
struct qemud_packet *in, struct qemud_packet *out) {
int runstate;
unsigned long long cpuTime;
unsigned long memory;
unsigned long maxmem;
unsigned int nrVirtCpu;
if (in->header.dataSize != sizeof(in->data.domainGetInfoRequest))
return -1;
int ret = qemudDomainGetInfo(server, in->data.domainGetInfoRequest.uuid,
&runstate,
&cpuTime,
&maxmem,
&memory,
&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);
out->data.domainGetInfoReply.runstate = runstate;
out->data.domainGetInfoReply.cpuTime = cpuTime;
out->data.domainGetInfoReply.maxmem = maxmem;
out->data.domainGetInfoReply.memory = memory;
out->data.domainGetInfoReply.nrVirtCpu = nrVirtCpu;
}
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) {
int id;
if (in->header.dataSize != sizeof(in->data.domainRestoreRequest))
return -1;
/* Paranoia null termination */
in->data.domainRestoreRequest.file[PATH_MAX-1] ='\0';
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) {
int ret;
if (in->header.dataSize != sizeof(in->data.domainDumpXMLRequest))
return -1;
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, ndomains;
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];
}
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 funcsTransmitRW[QEMUD_PKT_MAX] = {
NULL, /* FAILURE code */
qemudDispatchGetVersion,
qemudDispatchGetNodeInfo,
qemudDispatchListDomains,
qemudDispatchNumDomains,
qemudDispatchDomainCreate,
qemudDispatchDomainLookupByID,
qemudDispatchDomainLookupByUUID,
qemudDispatchDomainLookupByName,
qemudDispatchDomainSuspend,
qemudDispatchDomainResume,
qemudDispatchDomainDestroy,
qemudDispatchDomainGetInfo,
qemudDispatchDomainSave,
qemudDispatchDomainRestore,
qemudDispatchDumpXML,
qemudDispatchListDefinedDomains,
qemudDispatchNumDefinedDomains,
qemudDispatchDomainStart,
qemudDispatchDomainDefine,
qemudDispatchDomainUndefine
};
clientFunc funcsTransmitRO[QEMUD_PKT_MAX] = {
NULL, /* FAILURE code */
qemudDispatchGetVersion,
qemudDispatchGetNodeInfo,
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;
unsigned int type = in->header.type;
QEMUD_DEBUG("> Dispatching request %d readonly ? %d\n", type, client->readonly);
server->errorCode = 0;
server->errorMessage[0] = '\0';
memset(out, 0, sizeof(struct qemud_packet));
if (type >= QEMUD_PKT_MAX) {
QEMUD_DEBUG("Illegal request type\n");
return -1;
}
if (type == QEMUD_PKT_FAILURE) {
QEMUD_DEBUG("Illegal request type\n");
return -1;
}
if (client->readonly)
funcs = funcsTransmitRO;
else
funcs = funcsTransmitRW;
if (!funcs[type]) {
QEMUD_DEBUG("Illegal operation requested\n");
qemudReportError(server, VIR_ERR_OPERATION_DENIED, NULL);
qemudDispatchFailure(server, client, out);
} else {
if ((funcs[type])(server, client, in, out) < 0) {
QEMUD_DEBUG("Dispatch failed\n");
return -1;
}
}
QEMUD_DEBUG("< 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:
*/

43
qemud/dispatch.h Normal file
View File

@ -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@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:
*/

554
qemud/driver.c Normal file
View File

@ -0,0 +1,554 @@
/*
* 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@redhat.com>
*/
#include <sys/types.h>
#include <sys/poll.h>
#include <dirent.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <strings.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.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';
}
}
int qemudMonitorCommand(struct qemud_server *server ATTRIBUTE_UNUSED,
struct qemud_vm *vm,
const char *cmd,
char **reply) {
int size = 0;
char *buf = NULL;
if (write(vm->monitor, cmd, strlen(cmd)) < 0) {
return -1;
}
*reply = NULL;
for (;;) {
struct pollfd fd = { vm->monitor, POLLIN | POLLERR | POLLHUP, 0 };
char *tmp;
/* Read all the data QEMU has sent thus far */
for (;;) {
char data[1024];
int got = read(vm->monitor, data, sizeof(data));
if (got < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
break;
free(buf);
return -1;
}
if (!(buf = realloc(buf, size+got+1)))
return -1;
memmove(buf+size, data, got);
buf[size+got] = '\0';
size += got;
}
if (buf)
QEMUD_DEBUG("Mon [%s]\n", buf);
/* Look for QEMU prompt to indicate completion */
if (buf && ((tmp = strstr(buf, "\n(qemu)")) != NULL)) {
tmp[0] = '\0';
break;
}
pollagain:
/* Need to wait for more data */
if (poll(&fd, 1, -1) < 0) {
if (errno == EINTR)
goto pollagain;
free(buf);
return -1;
}
}
*reply = buf;
return 0;
}
int qemudGetMemInfo(unsigned int *memory) {
FILE *meminfo = fopen("/proc/meminfo", "r");
char line[1024];
*memory = 0;
if (!meminfo) {
return -1;
}
/* XXX NUMA and hyperthreads ? */
while (fgets(line, sizeof(line), meminfo) != NULL) {
if (!strncmp(line, "MemTotal:", 9)) {
*memory = (unsigned int)strtol(line + 10, NULL, 10);
}
}
fclose(meminfo);
return 0;
}
int qemudGetCPUInfo(unsigned int *cpus, unsigned int *mhz,
unsigned int *nodes, unsigned int *sockets,
unsigned int *cores, unsigned int *threads) {
FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
char line[1024];
*cpus = 0;
*mhz = 0;
*nodes = *sockets = *cores = *threads = 1;
if (!cpuinfo) {
return -1;
}
/* XXX NUMA and hyperthreads ? */
while (fgets(line, sizeof(line), cpuinfo) != NULL) {
if (!strncmp(line, "processor\t", 10)) { /* aka a single logical CPU */
(*cpus)++;
} else if (!strncmp(line, "cpu MHz\t", 8)) {
char *offset = index(line, ':');
if (!offset)
continue;
offset++;
if (!*offset)
continue;
*mhz = (unsigned int)strtol(offset, NULL, 10);
} else if (!strncmp(line, "physical id\t", 12)) { /* aka socket */
unsigned int id;
char *offset = index(line, ':');
if (!offset)
continue;
offset++;
if (!*offset)
continue;
id = (unsigned int)strtol(offset, NULL, 10);
if ((id+1) > *sockets)
*sockets = (id + 1);
} else if (!strncmp(line, "cpu cores\t", 9)) { /* aka cores */
unsigned int id;
char *offset = index(line, ':');
if (!offset)
continue;
offset++;
if (!*offset)
continue;
id = (unsigned int)strtol(offset, NULL, 10);
if (id > *cores)
*cores = id;
}
}
fclose(cpuinfo);
return 0;
}
static int qemudGetProcessInfo(unsigned long long *cpuTime, int pid) {
char proc[PATH_MAX];
FILE *pidinfo;
unsigned long usertime, systime;
if (snprintf(proc, sizeof(proc), "/proc/%d/stat", pid) >= (int)sizeof(proc)) {
return -1;
}
if (!(pidinfo = fopen(proc, "r"))) {
/*printf("cannnot read pid info");*/
/* VM probably shut down, so fake 0 */
*cpuTime = 0;
return 0;
}
if (fscanf(pidinfo, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu", &usertime, &systime) != 2) {
QEMUD_DEBUG("not enough arg\n");
return -1;
}
/* We got jiffies
* We want nanoseconds
* _SC_CLK_TCK is jiffies per second
* So calulate thus....
*/
*cpuTime = 1000 * 1000 * 1000 * (usertime + systime) / sysconf(_SC_CLK_TCK);
QEMUD_DEBUG("Got %lu %lu %lld\n", usertime, systime, *cpuTime);
fclose(pidinfo);
return 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) {
char *info;
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 (qemudMonitorCommand(server, vm, "stop\n", &info) < 0) {
qemudReportError(server, VIR_ERR_OPERATION_FAILED, "suspend operation failed");
return -1;
}
printf("Reply %s\n", info);
free(info);
return 0;
}
int qemudDomainResume(struct qemud_server *server, int id) {
char *info;
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 (qemudMonitorCommand(server, vm, "cont\n", &info) < 0) {
qemudReportError(server, VIR_ERR_OPERATION_FAILED, "resume operation failed");
return -1;
}
printf("Reply %s\n", info);
free(info);
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 *maxmem,
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;
}
if (vm->pid == -1) {
*cputime = 0;
} else {
if (qemudGetProcessInfo(cputime, vm->pid) < 0) {
qemudReportError(server, VIR_ERR_OPERATION_FAILED, "cannot read cputime for domain");
return -1;
}
}
*maxmem = vm->def.maxmem;
*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:
*/

99
qemud/driver.h Normal file
View File

@ -0,0 +1,99 @@
/*
* 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@redhat.com>
*/
#ifndef QEMUD_DRIVER_H
#define QEMUD_DRIVER_H
#include "internal.h"
void qemudReportError(struct qemud_server *server,
int code, const char *fmt, ...);
int qemudGetCPUInfo(unsigned int *cpus, unsigned int *mhz,
unsigned int *nodes, unsigned int *sockets,
unsigned int *cores, unsigned int *threads);
int qemudGetMemInfo(unsigned int *memory);
int qemudMonitorCommand(struct qemud_server *server,
struct qemud_vm *vm,
const char *cmd,
char **reply);
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 *maxmem,
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:
*/

254
qemud/internal.h Normal file
View File

@ -0,0 +1,254 @@
/*
* 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@redhat.com>
*/
#ifndef QEMUD_INTERNAL_H__
#define QEMUD_INTERNAL_H__
#include <sys/socket.h>
#include <netinet/in.h>
#include <gnutls/gnutls.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
#ifdef DEBUG
#define QEMUD_DEBUG(args...) fprintf(stderr, args)
#else
#define QEMUD_DEBUG(args...) do {} while(0)
#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,
};
/* 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;
};
#define QEMUD_MAX_BOOT_DEVS 4
/* 3 possible boot devices */
enum qemud_vm_boot_order {
QEMUD_BOOT_FLOPPY,
QEMUD_BOOT_CDROM,
QEMUD_BOOT_DISK,
QEMUD_BOOT_NET,
};
/* 3 possible graphics console modes */
enum qemud_vm_grapics_type {
QEMUD_GRAPHICS_NONE,
QEMUD_GRAPHICS_SDL,
QEMUD_GRAPHICS_VNC,
};
enum qemud_vm_features {
QEMUD_FEATURE_ACPI = 1,
};
/* 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 nBootDevs;
int bootDevs[QEMUD_MAX_BOOT_DEVS];
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 maxmem;
int vcpus;
struct qemud_vm_os_def os;
int features;
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;
};
/* 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;
};
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:
*/

222
qemud/protocol.h Normal file
View File

@ -0,0 +1,222 @@
/*
* 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@redhat.com>
*/
#ifndef QEMUD_PROTOCOL_H__
#define QEMUD_PROTOCOL_H__
#include <stdint.h>
/* List of different packet types which can be sent */
enum {
QEMUD_PKT_FAILURE = 0,
QEMUD_PKT_GET_VERSION,
QEMUD_PKT_GET_NODEINFO,
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_PROTOCOL_VERSION_MAJOR 1
#define QEMUD_PROTOCOL_VERSION_MINOR 0
#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.
*
* All data required to be network byte order
* to 32-bit boundaries */
struct qemud_packet_header {
uint32_t type;
/* Stores the size of the data struct matching
the type arg.
Must be <= sizeof(union qemudPacketData) */
uint32_t dataSize;
};
/* Most packets also have some message specific data
* All data required to be network byte order, padded
* to 32-bit boundaries */
union qemud_packet_data {
struct {
int32_t code;
char message[QEMUD_MAX_ERROR_LEN];
} failureReply;
struct {
int32_t version;
} getVersionReply;
struct {
char model[32];
uint32_t memory;
uint32_t cpus;
uint32_t mhz;
uint32_t nodes;
uint32_t sockets;
uint32_t cores;
uint32_t threads;
} getNodeInfoReply;
struct {
int32_t numDomains;
int32_t domains[QEMUD_MAX_NUM_DOMAINS];
} listDomainsReply;
struct {
int32_t numDomains;
} numDomainsReply;
struct {
char xml[QEMUD_MAX_XML_LEN];
} domainCreateRequest;
struct {
int32_t id;
unsigned char uuid[QEMUD_UUID_RAW_LEN];
char name[QEMUD_MAX_NAME_LEN];
} domainCreateReply;
struct {
int32_t 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 {
int32_t id;
unsigned char uuid[QEMUD_UUID_RAW_LEN];
} domainLookupByNameReply;
struct {
unsigned char uuid[QEMUD_UUID_RAW_LEN];
} domainLookupByUUIDRequest;
struct {
int32_t id;
char name[QEMUD_MAX_NAME_LEN];
} domainLookupByUUIDReply;
struct {
int32_t id;
} domainSuspendRequest;
struct {
int32_t id;
} domainResumeRequest;
struct {
} domainResumeReply;
struct {
int32_t id;
} domainDestroyRequest;
struct {
unsigned char uuid[QEMUD_UUID_RAW_LEN];
} domainGetInfoRequest;
struct {
uint64_t cpuTime;
int32_t runstate;
uint32_t memory;
uint32_t maxmem;
uint32_t nrVirtCpu;
} domainGetInfoReply;
struct {
int32_t id;
char file[PATH_MAX];
} domainSaveRequest;
struct {
char file[PATH_MAX];
} domainRestoreRequest;
struct {
int32_t id;
} domainRestoreReply;
struct {
unsigned char uuid[QEMUD_UUID_RAW_LEN];
} domainDumpXMLRequest;
struct {
char xml[QEMUD_MAX_XML_LEN];
} domainDumpXMLReply;
struct {
int32_t numDomains;
char domains[QEMUD_MAX_NUM_DOMAINS][QEMUD_MAX_NAME_LEN];
} listDefinedDomainsReply;
struct {
int32_t numDomains;
} numDefinedDomainsReply;
struct {
unsigned char uuid[QEMUD_UUID_RAW_LEN];
} domainStartRequest;
struct {
int32_t 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;
};
/* 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:
*/

958
qemud/qemud.c Normal file
View File

@ -0,0 +1,958 @@
/*
* 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@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 <gnutls/x509.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;
int i, open_max, nextpid;
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;
open_max = sysconf (_SC_OPEN_MAX);
for (i = 0; i < open_max; i++)
if (i != STDIN_FILENO &&
i != STDOUT_FILENO &&
i != STDERR_FILENO)
close(i);
if (setsid() < 0)
goto cleanup;
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);
if (addr.sun_path[0] == '@')
addr.sun_path[0] = '\0';
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)
return -1;
umask(oldmask);
if (listen(sock->fd, 30) < 0)
return -1;
return 0;
}
static int qemudListen(struct qemud_server *server, int sys) {
char sockname[PATH_MAX];
if (sys) {
if (snprintf(sockname, sizeof(sockname), "%s/run/qemud/sock", LOCAL_STATE_DIR) >= (int)sizeof(sockname)) {
return -1;
}
unlink(sockname);
if (qemudListenUnix(server, sockname, 0) < 0)
return -1;
if (snprintf(sockname, sizeof(sockname), "%s/run/qemud/sock-ro", LOCAL_STATE_DIR) >= (int)sizeof(sockname)) {
return -1;
}
unlink(sockname);
if (qemudListenUnix(server, sockname, 1) < 0)
return -1;
} else {
struct passwd *pw;
int uid;
if ((uid = geteuid()) < 0) {
return -1;
}
if (!(pw = getpwuid(uid)))
return -1;
if (snprintf(sockname, sizeof(sockname), "@%s/.qemud/sock", pw->pw_dir) >= (int)sizeof(sockname)) {
return -1;
}
if (qemudListenUnix(server, sockname, 0) < 0)
return -1;
}
return 0;
}
static struct qemud_server *qemudInitialize(int sys) {
struct qemud_server *server;
if (!(server = calloc(1, sizeof(struct qemud_server))))
return NULL;
/* XXX extract actual version */
server->qemuVersion = (0*1000000)+(8*1000)+(0);
/* We don't have a dom-0, so start from 1 */
server->nextvmid = 1;
if (sys) {
if (snprintf(server->configDir, sizeof(server->configDir), "%s/qemud", SYSCONF_DIR) >= (int)sizeof(server->configDir)) {
goto cleanup;
}
} else {
struct passwd *pw;
int uid;
if ((uid = geteuid()) < 0) {
goto cleanup;
}
if (!(pw = getpwuid(uid))) {
goto cleanup;
}
if (snprintf(server->configDir, sizeof(server->configDir), "%s/.qemud", pw->pw_dir) >= (int)sizeof(server->configDir)) {
goto cleanup;
}
}
if (qemudListen(server, sys) < 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));
client->fd = fd;
client->readonly = sock->readonly;
client->next = server->clients;
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 */
QEMUD_DEBUG("Spawn QEMU '");
for (i = 0 ; i < argc; i++) {
QEMUD_DEBUG("%s", argv[i]);
if (i == (argc-1)) {
QEMUD_DEBUG("'\n");
} else {
QEMUD_DEBUG(" ");
}
}
}
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 int qemudClientRead(struct qemud_server *server,
struct qemud_client *client,
char *buf, size_t want) {
int ret;
if ((ret = read(client->fd, buf, want)) <= 0) {
QEMUD_DEBUG("Plain read error %d\n", ret);
if (!ret || errno != EAGAIN)
qemudDispatchClientFailure(server, client);
return -1;
}
QEMUD_DEBUG("Plain data read %d\n", ret);
return ret;
}
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 = qemudClientRead(server, client, data+got, want)) < 0) {
return;
}
got += ret;
client->incomingReceived += ret;
/* If we've finished header, move onto body */
if (client->incomingReceived == sizeof(struct qemud_packet_header)) {
QEMUD_DEBUG("Type %d, data %d\n",
client->incoming.header.type,
client->incoming.header.dataSize);
/* Client lied about dataSize */
if (client->incoming.header.dataSize > sizeof(union qemud_packet_data)) {
QEMUD_DEBUG("Bogus data size %u\n", client->incoming.header.dataSize);
qemudDispatchClientFailure(server, client);
return;
}
if (client->incoming.header.dataSize) {
QEMUD_DEBUG("- 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);
QEMUD_DEBUG("Dispatch\n");
}
}
static int qemudClientWrite(struct qemud_server *server,
struct qemud_client *client,
char *buf, size_t want) {
int ret;
if ((ret = write(client->fd, buf, want)) < 0) {
QEMUD_DEBUG("Plain write error %d\n", ret);
if (errno != EAGAIN)
qemudDispatchClientFailure(server, client);
return -1;
}
QEMUD_DEBUG("Plain data write %d\n", ret);
return ret;
}
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 = qemudClientWrite(server, client, data+sent, todo)) < 0) {
return;
}
client->outgoingSent += ret;
QEMUD_DEBUG("Done %d %d\n", todo, 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, sizeof(buf)-1);
if (ret < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}
if (ret == 0) {
return 0;
}
buf[ret] = '\0';
/*
* XXX this is bad - we should wait for tty and open the
* monitor when actually starting the guest, so we can
* reliably trap startup failures
*/
if (vm->monitor == -1) {
char monitor[20];
/* Fairly lame assuming we receive the data all in one chunk.
This isn't guarenteed, but in practice it seems good enough.
This will probably bite me in the future.... */
if (sscanf(buf, "char device redirected to %19s", monitor) == 1) {
int monfd;
if (!(monfd = open(monitor, O_RDWR))) {
perror("cannot open monitor");
return -1;
}
if (qemudSetCloseExec(monfd) < 0) {
close(monfd);
return -1;
}
if (qemudSetNonBlock(monfd) < 0) {
close(monfd);
return -1;
}
/* Consume & discard the initial greeting */
/* XXX this is broken, we need to block until
we see the initial prompt to ensure startup
has completed */
for(;;) {
char line[1024];
if (read(monfd, line, sizeof(line)) < 0) {
if (errno == EAGAIN) {
break;
}
close(monfd);
return -1;
}
QEMUD_DEBUG("[%s]\n", line);
}
vm->monitor = monfd;
}
}
QEMUD_DEBUG("[%s]\n", buf);
}
}
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;
}
if (!curr) {
QEMUD_DEBUG("Could not find VM to shutdown\n");
return 0;
}
qemudVMData(server, vm, curr->stdout);
qemudVMData(server, vm, curr->stderr);
close(curr->stdout);
close(curr->stderr);
if (curr->monitor != -1)
close(curr->monitor);
curr->stdout = -1;
curr->stderr = -1;
curr->monitor = -1;
server->nvmfds -= 2;
if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) {
kill(vm->pid, SIGKILL);
if (waitpid(vm->pid, NULL, 0) != vm->pid) {
QEMUD_DEBUG("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 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) {
QEMUD_DEBUG("Poll data normal\n");
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;
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++;
}
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;
struct qemud_socket *sock;
struct qemud_client *client;
struct qemud_vm *vm;
for (sock = server->sockets ; sock ; sock = sock->next) {
fds[fd].fd = sock->fd;
fds[fd].events = POLLIN;
fd++;
}
for (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 (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++;
}
}
}
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 godaemon = 0;
int verbose = 0;
int sys = 0;
int timeout = -1;
struct qemud_server *server;
struct option opts[] = {
{ "verbose", no_argument, &verbose, 1},
{ "daemon", no_argument, &godaemon, 1},
{ "system", no_argument, &sys, 1},
{ "timeout", required_argument, 0, 't'},
{0, 0, 0, 0}
};
while (1) {
int optidx = 0;
int c;
char *tmp;
c = getopt_long(argc, argv, "vsdt:", opts, &optidx);
if (c == -1) {
break;
}
switch (c) {
case 0:
/* Got one of the flags */
break;
case 'v':
verbose = 1;
break;
case 'd':
godaemon = 1;
break;
case 's':
sys = 1;
break;
case 't':
timeout = strtol(optarg, &tmp, 10);
if (!tmp)
timeout = -1;
if (timeout <= 0)
timeout = -1;
break;
case '?':
return 2;
break;
default:
abort();
}
}
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
return 3;
if (signal(SIGCHLD, reapchild) == SIG_ERR)
return 3;
if (godaemon) {
int pid = qemudGoDaemon();
if (pid < 0)
return 1;
if (pid > 0)
return 0;
}
if (!(server = qemudInitialize(sys)))
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:
*/

View File

@ -1,7 +1,8 @@
## Process this file with automake to produce Makefile.in
INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ \
INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ -I@top_srcdir@/qemud \
-DBINDIR=\""$(libexecdir)"\" -DLOCALEBASEDIR=\""$(datadir)/locale"\" \
-DLOCAL_STATE_DIR=\""$(localstatedir)"\" \
-DGETTEXT_PACKAGE=\"$(PACKAGE)\"
DEPS = libvirt.la
LDADDS = @STATIC_BINARIES@ libvirt.la
@ -11,7 +12,6 @@ EXTRA_DIST = libvirt_sym.version
lib_LTLIBRARIES = libvirt.la
libvirt_la_LIBADD = @LIBXML_LIBS@
libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \
-version-info @LIBVIRT_VERSION_INFO@
@ -28,7 +28,8 @@ libvirt_la_SOURCES = \
driver.h \
proxy_internal.c proxy_internal.h \
conf.c conf.h \
xm_internal.c xm_internal.h
xm_internal.c xm_internal.h \
qemu_internal.c qemu_internal.h
bin_PROGRAMS = virsh

View File

@ -22,7 +22,8 @@ typedef enum {
VIR_DRV_XEN_DAEMON = 3,
VIR_DRV_TEST = 4,
VIR_DRV_XEN_PROXY = 5,
VIR_DRV_XEN_XM = 6
VIR_DRV_XEN_XM = 6,
VIR_DRV_QEMU = 7
} virDrvNo;

View File

@ -32,6 +32,7 @@
#include "proxy_internal.h"
#include "xml.h"
#include "test.h"
#include "qemu_internal.h"
/*
* TODO:
@ -79,6 +80,8 @@ virInitialize(void)
xenStoreRegister();
xenXMRegister();
testRegister();
qemuRegister();
return(0);
}
@ -441,6 +444,7 @@ virConnectGetVersion(virConnectPtr conn, unsigned long *hvVer)
return(0);
}
}
return (-1);
}

864
src/qemu_internal.c Normal file
View File

@ -0,0 +1,864 @@
/*
* qemu_internal.c: A backend for managing QEMU machines
*
* 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@redhat.com>
*/
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/uri.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
#include <limits.h>
#include <paths.h>
#include "internal.h"
#include "qemu_internal.h"
#include "xml.h"
#include "protocol.h"
static void
qemuError(virConnectPtr con,
virDomainPtr dom,
virErrorNumber error,
const char *info)
{
const char *errmsg;
if (error == VIR_ERR_OK)
return;
errmsg = __virErrorMsg(error, info);
__virRaiseError(con, dom, VIR_FROM_QEMU, error, VIR_ERR_ERROR,
errmsg, info, NULL, 0, 0, errmsg, info, 0);
}
static void qemuPacketError(virConnectPtr con,
virDomainPtr dom,
struct qemud_packet *pkt) {
if (!pkt) {
qemuError(con, dom, VIR_ERR_INTERNAL_ERROR, "Malformed data packet");
return;
}
if (pkt->header.type == QEMUD_PKT_FAILURE) {
/* Paranoia in case remote side didn't terminate it */
if (pkt->data.failureReply.message[0])
pkt->data.failureReply.message[QEMUD_MAX_ERROR_LEN-1] = '\0';
qemuError(con,
dom,
pkt->data.failureReply.code,
pkt->data.failureReply.message[0] ?
pkt->data.failureReply.message : NULL);
} else {
qemuError(con, dom, VIR_ERR_INTERNAL_ERROR, "Incorrect reply type");
}
}
/**
* qemuFindServerPath:
*
* Tries to find the path to the qemu binary.
*
* Returns path on success or NULL in case of error.
*/
static const char *
qemuFindServerPath(void)
{
static const char *serverPaths[] = {
BINDIR "/libvirt_qemu",
BINDIR "/libvirt_qemu_dbg",
NULL
};
int i;
const char *debugQemu = getenv("LIBVIRT_QEMU_SERVER");
if (debugQemu)
return(debugQemu);
for (i = 0; serverPaths[i]; i++) {
if (access(serverPaths[i], X_OK | R_OK) == 0) {
return serverPaths[i];
}
}
return NULL;
}
/**
* qemuForkServer:
*
* Forks and try to launch the qemu server
*
* Returns 0 in case of success or -1 in case of detected error.
*/
static int
qemuForkServer(void)
{
const char *proxyPath = qemuFindServerPath();
int ret, pid, status;
if (!proxyPath) {
fprintf(stderr, "failed to find qemu\n");
return(-1);
}
/* Become a daemon */
pid = fork();
if (pid == 0) {
int stdinfd = -1;
int stdoutfd = -1;
int i, open_max;
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;
open_max = sysconf (_SC_OPEN_MAX);
for (i = 0; i < open_max; i++)
if (i != STDIN_FILENO &&
i != STDOUT_FILENO &&
i != STDERR_FILENO)
close(i);
setsid();
if (fork() == 0) {
/* Run daemon in auto-shutdown mode, so it goes away when
no longer needed by an active guest, or client */
execl(proxyPath, proxyPath, "--timeout", "30", NULL);
fprintf(stderr, "failed to exec %s\n", proxyPath);
}
/*
* calling exit() generate troubles for termination handlers
*/
_exit(0);
cleanup:
if (stdoutfd != -1)
close(stdoutfd);
if (stdinfd != -1)
close(stdinfd);
_exit(-1);
}
/*
* do a waitpid on the intermediate process to avoid zombies.
*/
retry_wait:
ret = waitpid(pid, &status, 0);
if (ret < 0) {
if (errno == EINTR)
goto retry_wait;
}
return (0);
}
/**
* qemuOpenClientUNIX:
* @path: the fileame for the socket
*
* try to connect to the socket open by qemu
*
* Returns the associated file descriptor or -1 in case of failure
*/
static int
qemuOpenClientUNIX(virConnectPtr conn, const char *path, int autostart) {
int fd;
struct sockaddr_un addr;
int trials = 0;
retry:
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
return(-1);
}
/*
* Abstract socket do not hit the filesystem, way more secure and
* garanteed to be atomic
*/
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
if (addr.sun_path[0] == '@')
addr.sun_path[0] = '\0';
/*
* now bind the socket to that address and listen on it
*/
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(fd);
if (autostart && trials < 3) {
if (qemuForkServer() < 0)
return(-1);
trials++;
usleep(5000 * trials * trials);
goto retry;
}
return (-1);
}
conn->handle = fd;
return (0);
}
/* Takes a single request packet, does a blocking send on it.
* then blocks until the complete reply has come back, or
* connection closes.
*/
static int qemuProcessRequest(virConnectPtr conn,
virDomainPtr dom,
struct qemud_packet *req,
struct qemud_packet *reply) {
char *out = (char *)req;
int outDone = 0;
int outLeft = sizeof(struct qemud_packet_header) + req->header.dataSize;
char *in = (char *)reply;
int inGot = 0;
int inLeft = sizeof(struct qemud_packet_header);
/* printf("Send request %d\n", req->header.type); */
/* Block sending entire outgoing packet */
while (outLeft) {
int got = write(conn->handle, out+outDone, outLeft);
if (got < 0) {
return -1;
}
outDone += got;
outLeft -= got;
}
/* Block waiting for header to come back */
while (inLeft) {
int done = read(conn->handle, in+inGot, inLeft);
if (done <= 0) {
return -1;
}
inGot += done;
inLeft -= done;
}
/* Validate header isn't bogus (bigger than
maximum defined packet size) */
if (reply->header.dataSize > sizeof(union qemud_packet_data)) {
/*
printf("Got type %ds body %d (max %ld)\n",
reply->header.type,
reply->header.dataSize,
sizeof(union qemud_packet_data));
printf("%ld == %ld + %ld\n",
sizeof(struct qemud_packet),
sizeof(struct qemud_packet_header),
sizeof(union qemud_packet_data));
*/
qemuPacketError(conn, dom, NULL);
return -1;
}
/* Now block reading in body */
inLeft = reply->header.dataSize;
while (inLeft) {
int done = read(conn->handle, in+inGot, inLeft);
if (done <= 0) {
return -1;
}
inGot += done;
inLeft -= done;
}
if (reply->header.type != req->header.type) {
qemuPacketError(conn, dom, reply);
return -1;
}
return 0;
}
/*
* Open a connection to the libvirt QEMU daemon
*/
static int qemuOpenConnection(virConnectPtr conn, xmlURIPtr uri, int readonly ATTRIBUTE_UNUSED) {
char path[PATH_MAX];
if (uri->server != NULL) {
return -1;
}
if (!strcmp(uri->path, "/system")) {
if (readonly) {
if (snprintf(path, sizeof(path), "%s/run/qemud/sock-ro", LOCAL_STATE_DIR) >= (int)sizeof(path)) {
return -1;
}
} else {
if (snprintf(path, sizeof(path), "%s/run/qemud/sock", LOCAL_STATE_DIR) >= (int)sizeof(path)) {
return -1;
}
}
} else if (!strcmp(uri->path, "/session")) {
struct passwd *pw;
int uid;
if ((uid = geteuid()) < 0) {
return -1;
}
if (!(pw = getpwuid(uid)))
return -1;
if (snprintf(path, sizeof(path), "@%s/.qemud/sock", pw->pw_dir) == sizeof(path)) {
return -1;
}
}
return qemuOpenClientUNIX(conn, path, 1);
}
/*
* Open a connection to the QEMU manager
*/
static int qemuOpen(virConnectPtr conn,
const char *name,
int flags){
xmlURIPtr uri;
if (!name) {
return -1;
}
uri = xmlParseURI(name);
if (uri == NULL) {
if (!(flags & VIR_DRV_OPEN_QUIET))
qemuError(conn, NULL, VIR_ERR_NO_SUPPORT, name);
return(-1);
}
if (!uri->scheme ||
strcmp(uri->scheme, "qemu") ||
!uri->path) {
xmlFreeURI(uri);
return -1;
}
conn->handle = -1;
qemuOpenConnection(conn, uri, flags & VIR_DRV_OPEN_RO ? 1 : 0);
xmlFreeURI(uri);
if (conn->handle < 0) {
return -1;
}
return 0;
}
static int qemuClose (virConnectPtr conn) {
if (conn->handle != -1) {
close(conn->handle);
conn->handle = -1;
}
return 0;
}
static int qemuGetVersion(virConnectPtr conn,
unsigned long *hvVer) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_GET_VERSION;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
*hvVer = reply.data.getVersionReply.version;
return 0;
}
static int qemuNodeGetInfo(virConnectPtr conn,
virNodeInfoPtr info) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_GET_NODEINFO;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
info->cores = reply.data.getNodeInfoReply.cores;
info->threads = reply.data.getNodeInfoReply.threads;
info->sockets = reply.data.getNodeInfoReply.sockets;
info->nodes = reply.data.getNodeInfoReply.nodes;
strncpy(info->model, reply.data.getNodeInfoReply.model, sizeof(info->model));
info->mhz = reply.data.getNodeInfoReply.mhz;
info->cpus = reply.data.getNodeInfoReply.cpus;
info->memory = reply.data.getNodeInfoReply.memory;
return 0;
}
static int qemuNumOfDomains(virConnectPtr conn) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NUM_DOMAINS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
return reply.data.numDomainsReply.numDomains;
}
static int qemuListDomains(virConnectPtr conn,
int *ids,
int maxids) {
struct qemud_packet req, reply;
int i, nDomains;
req.header.type = QEMUD_PKT_LIST_DOMAINS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
nDomains = reply.data.listDomainsReply.numDomains;
if (nDomains > maxids)
nDomains = maxids;
for (i = 0 ; i < nDomains ; i++) {
ids[i] = reply.data.listDomainsReply.domains[i];
}
return nDomains;
}
static virDomainPtr
qemuDomainCreateLinux(virConnectPtr conn, const char *xmlDesc,
unsigned int flags ATTRIBUTE_UNUSED) {
struct qemud_packet req, reply;
virDomainPtr dom;
int len = strlen(xmlDesc);
if (len > (QEMUD_MAX_XML_LEN-1)) {
return NULL;
}
req.header.type = QEMUD_PKT_DOMAIN_CREATE;
req.header.dataSize = sizeof(req.data.domainCreateRequest);
strcpy(req.data.domainCreateRequest.xml, xmlDesc);
req.data.domainCreateRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0';
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
if (!(dom = virGetDomain(conn,
reply.data.domainCreateReply.name,
reply.data.domainCreateReply.uuid)))
return NULL;
dom->id = reply.data.domainCreateReply.id;
return dom;
}
static virDomainPtr qemuLookupDomainByID(virConnectPtr conn,
int id) {
struct qemud_packet req, reply;
virDomainPtr dom;
req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_ID;
req.header.dataSize = sizeof(req.data.domainLookupByIDRequest);
req.data.domainLookupByIDRequest.id = id;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.domainLookupByIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
if (!(dom = virGetDomain(conn,
reply.data.domainLookupByIDReply.name,
reply.data.domainLookupByIDReply.uuid)))
return NULL;
dom->id = id;
return dom;
}
static virDomainPtr qemuLookupDomainByUUID(virConnectPtr conn,
const unsigned char *uuid) {
struct qemud_packet req, reply;
virDomainPtr dom;
req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID;
req.header.dataSize = sizeof(req.data.domainLookupByUUIDRequest);
memmove(req.data.domainLookupByUUIDRequest.uuid, uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.domainLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
if (!(dom = virGetDomain(conn,
reply.data.domainLookupByUUIDReply.name,
uuid)))
return NULL;
dom->id = reply.data.domainLookupByUUIDReply.id;
return dom;
}
static virDomainPtr qemuLookupDomainByName(virConnectPtr conn,
const char *name) {
struct qemud_packet req, reply;
virDomainPtr dom;
if (strlen(name) > (QEMUD_MAX_NAME_LEN-1))
return NULL;
req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME;
req.header.dataSize = sizeof(req.data.domainLookupByNameRequest);
strcpy(req.data.domainLookupByNameRequest.name, name);
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
if (!(dom = virGetDomain(conn,
name,
reply.data.domainLookupByNameReply.uuid)))
return NULL;
dom->id = reply.data.domainLookupByNameReply.id;
return dom;
}
static int qemuDestroyDomain(virDomainPtr domain) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DOMAIN_DESTROY;
req.header.dataSize = sizeof(req.data.domainDestroyRequest);
req.data.domainDestroyRequest.id = domain->id;
if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
return -1;
}
return 0;
}
static int qemuShutdownDomain(virDomainPtr domain) {
return qemuDestroyDomain(domain);
}
static int qemuResumeDomain(virDomainPtr domain ATTRIBUTE_UNUSED) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DOMAIN_RESUME;
req.header.dataSize = sizeof(req.data.domainResumeRequest);
req.data.domainResumeRequest.id = domain->id;
if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
return -1;
}
return 0;
}
static int qemuPauseDomain(virDomainPtr domain ATTRIBUTE_UNUSED) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DOMAIN_SUSPEND;
req.header.dataSize = sizeof(req.data.domainSuspendRequest);
req.data.domainSuspendRequest.id = domain->id;
if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
return -1;
}
return 0;
}
static int qemuGetDomainInfo(virDomainPtr domain,
virDomainInfoPtr info) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DOMAIN_GET_INFO;
req.header.dataSize = sizeof(req.data.domainGetInfoRequest);
memmove(req.data.domainGetInfoRequest.uuid, domain->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
return -1;
}
memset(info, 0, sizeof(virDomainInfo));
switch (reply.data.domainGetInfoReply.runstate) {
case QEMUD_STATE_RUNNING:
info->state = VIR_DOMAIN_RUNNING;
break;
case QEMUD_STATE_PAUSED:
info->state = VIR_DOMAIN_PAUSED;
break;
case QEMUD_STATE_STOPPED:
info->state = VIR_DOMAIN_SHUTOFF;
break;
default:
return -1;
}
info->maxMem = reply.data.domainGetInfoReply.maxmem;
info->memory = reply.data.domainGetInfoReply.memory;
info->nrVirtCpu = reply.data.domainGetInfoReply.nrVirtCpu;
info->cpuTime = reply.data.domainGetInfoReply.cpuTime;
return 0;
}
static char *qemuDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DUMP_XML;
req.header.dataSize = sizeof(req.data.domainDumpXMLRequest);
memmove(req.data.domainDumpXMLRequest.uuid, domain->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.domainDumpXMLReply.xml[QEMUD_MAX_XML_LEN-1] = '\0';
return strdup(reply.data.domainDumpXMLReply.xml);
}
static int qemuSaveDomain(virDomainPtr domain ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED) {
return -1;
}
static int qemuRestoreDomain(virConnectPtr conn ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED) {
return -1;
}
static int qemuNumOfDefinedDomains(virConnectPtr conn) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
return reply.data.numDefinedDomainsReply.numDomains;
}
static int qemuListDefinedDomains(virConnectPtr conn,
const char **names,
int maxnames){
struct qemud_packet req, reply;
int i, nDomains;
req.header.type = QEMUD_PKT_LIST_DEFINED_DOMAINS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
nDomains = reply.data.listDefinedDomainsReply.numDomains;
if (nDomains > maxnames)
nDomains = maxnames;
for (i = 0 ; i < nDomains ; i++) {
reply.data.listDefinedDomainsReply.domains[i][QEMUD_MAX_NAME_LEN-1] = '\0';
names[i] = strdup(reply.data.listDefinedDomainsReply.domains[i]);
}
return nDomains;
}
static int qemuDomainCreate(virDomainPtr dom) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DOMAIN_START;
req.header.dataSize = sizeof(req.data.domainStartRequest);
memcpy(req.data.domainStartRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) {
return -1;
}
dom->id = reply.data.domainStartReply.id;
return 0;
}
static virDomainPtr qemuDomainDefineXML(virConnectPtr conn, const char *xml) {
struct qemud_packet req, reply;
virDomainPtr dom;
int len = strlen(xml);
if (len > (QEMUD_MAX_XML_LEN-1)) {
return NULL;
}
req.header.type = QEMUD_PKT_DOMAIN_DEFINE;
req.header.dataSize = sizeof(req.data.domainDefineRequest);
strcpy(req.data.domainDefineRequest.xml, xml);
req.data.domainDefineRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0';
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.domainDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
if (!(dom = virGetDomain(conn,
reply.data.domainDefineReply.name,
reply.data.domainDefineReply.uuid)))
return NULL;
dom->id = -1;
return dom;
}
static int qemuUndefine(virDomainPtr dom) {
struct qemud_packet req, reply;
int ret = 0;
req.header.type = QEMUD_PKT_DOMAIN_UNDEFINE;
req.header.dataSize = sizeof(req.data.domainUndefineRequest);
memcpy(req.data.domainUndefineRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) {
ret = -1;
goto cleanup;
}
cleanup:
if (virFreeDomain(dom->conn, dom) < 0)
ret = -1;
return ret;
}
static virDriver qemuDriver = {
VIR_DRV_QEMU,
"QEMU",
LIBVIR_VERSION_NUMBER,
NULL, /* init */
qemuOpen, /* open */
qemuClose, /* close */
NULL, /* type */
qemuGetVersion, /* version */
qemuNodeGetInfo, /* nodeGetInfo */
qemuListDomains, /* listDomains */
qemuNumOfDomains, /* numOfDomains */
qemuDomainCreateLinux, /* domainCreateLinux */
qemuLookupDomainByID, /* domainLookupByID */
qemuLookupDomainByUUID, /* domainLookupByUUID */
qemuLookupDomainByName, /* domainLookupByName */
qemuPauseDomain, /* domainSuspend */
qemuResumeDomain, /* domainResume */
qemuShutdownDomain, /* domainShutdown */
NULL, /* domainReboot */
qemuDestroyDomain, /* domainDestroy */
NULL, /* domainGetOSType */
NULL, /* domainGetMaxMemory */
NULL, /* domainSetMaxMemory */
NULL, /* domainSetMemory */
qemuGetDomainInfo, /* domainGetInfo */
qemuSaveDomain, /* domainSave */
qemuRestoreDomain, /* domainRestore */
NULL, /* domainCoreDump */
NULL, /* domainSetVcpus */
NULL, /* domainPinVcpu */
NULL, /* domainGetVcpus */
qemuDomainDumpXML, /* domainDumpXML */
qemuListDefinedDomains, /* listDomains */
qemuNumOfDefinedDomains, /* numOfDomains */
qemuDomainCreate, /* domainCreate */
qemuDomainDefineXML, /* domainDefineXML */
qemuUndefine, /* domainUndefine */
NULL, /* domainAttachDevice */
NULL, /* domainDetachDevice */
};
void qemuRegister(void) {
virRegisterDriver(&qemuDriver);
}
/*
* Local variables:
* indent-tabs-mode: nil
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/

48
src/qemu_internal.h Normal file
View File

@ -0,0 +1,48 @@
/*
* qemu_internal.h: A backend for managing QEMU machines
*
* 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@redhat.com>
*/
#ifndef __VIR_QEMU_INTERNAL_H__
#define __VIR_QEMU_INTERNAL_H__
#include <libvirt/virterror.h>
#ifdef __cplusplus
extern "C" {
#endif
void qemuRegister(void);
#ifdef __cplusplus
}
#endif
#endif /* __VIR_QEMU_INTERNAL_H__ */
/*
* Local variables:
* indent-tabs-mode: nil
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/

View File

@ -2529,7 +2529,9 @@ vshInit(vshControl * ctl)
/* basic connection to hypervisor, for Xen connections unless
we're root open a read only connections. Allow 'test' HV
to be RW all the time though */
if (ctl->uid == 0 || (ctl->name && !strncmp(ctl->name, "test", 4)))
if (ctl->uid == 0 || (ctl->name &&
(!strncmp(ctl->name, "test", 4) ||
!strncmp(ctl->name, "qemu", 4))))
ctl->conn = virConnectOpen(ctl->name);
else
ctl->conn = virConnectOpenReadOnly(ctl->name);

View File

@ -268,6 +268,9 @@ virDefaultErrorFunc(virErrorPtr err)
case VIR_FROM_RPC:
dom = "XML-RPC ";
break;
case VIR_FROM_QEMU:
dom = "QEMU ";
break;
}
if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
domain = err->dom->name;