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 -=|
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@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 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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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;