mirror of https://gitee.com/openkylin/libvirt.git
Added QEMU support
This commit is contained in:
parent
ed02290129
commit
23ad665cb0
17
ChangeLog
17
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
.deps
|
||||
.libs
|
||||
*.lo
|
||||
*.la
|
||||
libvirt_qemud
|
|
@ -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 =
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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:
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue