[Libvir] PATCH 1/2 QEMU driver - internal driver
Daniel P. Berrange
berrange at redhat.com
Tue Feb 13 19:03:42 UTC 2007
The attached patch implements the library driver for QEMU.
The driver is pretty much identical in style to the xen proxy driver. There
are two supported URLs:
qemu:///session - a per-user (private) daemon.Can be run by unprivileged
users. Config files kept in $HOME/.qemu and the socket
is in the abstract namespace at $HOME/.qemu/sock
The daemon for session instance is spawned on demand
qemu:///system - a per-machine (public) daemon. Must be launched ahead
of time by root. Config files kept in /etc/qemu and
socket on the FS at /var/run/qemu/sock & sock-ro
The read-write socket is restricted to root only, while
the read-only socket is public.
This patch also:
- makes virsh use a read-write connection by default
- adds extra error info to virterror & friends
Regards,
Dan.
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules: http://search.cpan.org/~danberr/ -=|
|=- Projects: http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
-------------- next part --------------
Index: include/libvirt/virterror.h
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/virterror.h,v
retrieving revision 1.17
diff -u -p -r1.17 virterror.h
--- include/libvirt/virterror.h 8 Nov 2006 16:55:20 -0000 1.17
+++ include/libvirt/virterror.h 13 Feb 2007 19:44:53 -0000
@@ -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;
Index: src/Makefile.am
===================================================================
RCS file: /data/cvs/libvirt/src/Makefile.am,v
retrieving revision 1.31
diff -u -p -r1.31 Makefile.am
--- src/Makefile.am 26 Jan 2007 11:54:29 -0000 1.31
+++ src/Makefile.am 13 Feb 2007 19:44:57 -0000
@@ -1,7 +1,8 @@
## Process this file with automake to produce Makefile.in
-INCLUDES = -I$(top_builddir)/include -I at top_srcdir@/include @LIBXML_CFLAGS@ \
+INCLUDES = -I$(top_builddir)/include -I at top_srcdir@/include @LIBXML_CFLAGS@ -I at 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
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.16
diff -u -p -r1.16 driver.h
--- src/driver.h 22 Jan 2007 16:21:27 -0000 1.16
+++ src/driver.h 13 Feb 2007 19:44:57 -0000
@@ -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;
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.53
diff -u -p -r1.53 libvirt.c
--- src/libvirt.c 23 Jan 2007 14:39:45 -0000 1.53
+++ src/libvirt.c 13 Feb 2007 19:44:58 -0000
@@ -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,
return(0);
}
}
+
return (-1);
}
Index: src/qemu_internal.c
===================================================================
RCS file: src/qemu_internal.c
diff -N src/qemu_internal.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/qemu_internal.c 13 Feb 2007 19:45:00 -0000
@@ -0,0 +1,897 @@
+/*
+ * 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 at 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"
+
+int qemuOpen(virConnectPtr conn,
+ const char *name,
+ int flags);
+int qemuClose (virConnectPtr conn);
+int qemuGetVersion(virConnectPtr conn,
+ unsigned long *hvVer);
+int qemuNodeGetInfo(virConnectPtr conn,
+ virNodeInfoPtr info);
+int qemuNumOfDomains(virConnectPtr conn);
+int qemuListDomains(virConnectPtr conn,
+ int *ids,
+ int maxids);
+int qemuNumOfDefinedDomains(virConnectPtr conn);
+int qemuListDefinedDomains(virConnectPtr conn,
+ const char **names,
+ int maxnames);
+virDomainPtr
+qemuDomainCreateLinux(virConnectPtr conn, const char *xmlDesc,
+ unsigned int flags);
+virDomainPtr qemuLookupDomainByID(virConnectPtr conn,
+ int id);
+virDomainPtr qemuLookupDomainByUUID(virConnectPtr conn,
+ const unsigned char *uuid);
+virDomainPtr qemuLookupDomainByName(virConnectPtr conn,
+ const char *name);
+int qemuShutdownDomain(virDomainPtr domain);
+int qemuDestroyDomain(virDomainPtr domain);
+int qemuResumeDomain(virDomainPtr domain);
+int qemuPauseDomain(virDomainPtr domain);
+int qemuGetDomainInfo(virDomainPtr domain,
+ virDomainInfoPtr info);
+char * qemuDomainDumpXML(virDomainPtr domain, int flags);
+int qemuSaveDomain(virDomainPtr domain, const char *file);
+int qemuRestoreDomain(virConnectPtr conn, const char *file);
+int qemuDomainCreate(virDomainPtr conn);
+virDomainPtr qemuDomainDefineXML(virConnectPtr conn, const char *xml);
+int qemuUndefine(virDomainPtr dom);
+
+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 */
+};
+
+
+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");
+ }
+}
+
+void qemuRegister(void) {
+ virRegisterDriver(&qemuDriver);
+}
+
+/**
+ * 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
+ */
+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;
+}
+
+
+int qemuClose (virConnectPtr conn) {
+ if (conn->handle != -1) {
+ close(conn->handle);
+ conn->handle = -1;
+ }
+ return 0;
+}
+
+
+int qemuGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED,
+ unsigned long *hvVer) {
+ struct qemud_packet req, reply;
+
+ req.header.type = QEMUD_PKT_GET_VERSION;
+ req.header.dataSize = 0;
+
+ if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+ return -1;
+ }
+
+ *hvVer = reply.data.getVersionReply.version;
+ return 0;
+}
+
+
+int qemuNodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virNodeInfoPtr info){
+ 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;
+}
+
+
+int qemuNumOfDomains(virConnectPtr conn){
+ struct qemud_packet req, reply;
+
+ req.header.type = QEMUD_PKT_NUM_DOMAINS;
+ req.header.dataSize = 0;
+
+ if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+ return -1;
+ }
+
+ return reply.data.numDomainsReply.numDomains;
+}
+
+
+int qemuListDomains(virConnectPtr conn,
+ int *ids,
+ int maxids){
+ struct qemud_packet req, reply;
+ 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)
+ return -1;
+
+ for (i = 0 ; i < nDomains ; i++) {
+ ids[i] = reply.data.listDomainsReply.domains[i];
+ }
+
+ return nDomains;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+int qemuShutdownDomain(virDomainPtr domain){
+ return qemuDestroyDomain(domain);
+}
+
+int qemuDestroyDomain(virDomainPtr domain){
+ struct qemud_packet req, reply;
+
+ req.header.type = QEMUD_PKT_DOMAIN_DESTROY;
+ req.header.dataSize = sizeof(req.data.domainDestroyRequest);
+ req.data.domainDestroyRequest.id = domain->id;
+
+ if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+}
+
+int qemuSaveDomain(virDomainPtr domain ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED){
+ return -1;
+}
+int qemuRestoreDomain(virConnectPtr conn ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED){
+ return -1;
+}
+int qemuNumOfDefinedDomains(virConnectPtr conn){
+ struct qemud_packet req, reply;
+
+ req.header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS;
+ req.header.dataSize = 0;
+
+ if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+ return -1;
+ }
+
+ return reply.data.numDefinedDomainsReply.numDomains;
+}
+
+int qemuListDefinedDomains(virConnectPtr conn,
+ const char **names,
+ int maxnames){
+ struct qemud_packet req, reply;
+ int i, 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)
+ return -1;
+
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
Index: src/qemu_internal.h
===================================================================
RCS file: src/qemu_internal.h
diff -N src/qemu_internal.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/qemu_internal.h 13 Feb 2007 19:45:00 -0000
@@ -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 at 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:
+ */
Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.48
diff -u -p -r1.48 virsh.c
--- src/virsh.c 7 Feb 2007 13:50:18 -0000 1.48
+++ src/virsh.c 13 Feb 2007 19:45:01 -0000
@@ -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);
Index: src/virterror.c
===================================================================
RCS file: /data/cvs/libvirt/src/virterror.c,v
retrieving revision 1.19
diff -u -p -r1.19 virterror.c
--- src/virterror.c 8 Nov 2006 16:55:20 -0000 1.19
+++ src/virterror.c 13 Feb 2007 19:45:02 -0000
@@ -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;
More information about the libvir-list
mailing list