The attached patch provides a new driver backend for libvirt which allows management of QEMU instances by talking to the libvirt QEMU daemon. It basically just marshalls libvirt API calls onto the wire & de-marshalls the reply. All the interesting functionality stuff is in the daemon. 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 -=|
? qemud Index: Makefile.am =================================================================== RCS file: /data/cvs/libvirt/Makefile.am,v retrieving revision 1.12 diff -u -p -r1.12 Makefile.am --- Makefile.am 21 Sep 2006 15:24:37 -0000 1.12 +++ Makefile.am 5 Jan 2007 21:09:06 -0000 @@ -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 Index: configure.in =================================================================== RCS file: /data/cvs/libvirt/configure.in,v retrieving revision 1.50 diff -u -p -r1.50 configure.in --- configure.in 20 Dec 2006 14:54:25 -0000 1.50 +++ configure.in 5 Jan 2007 21:09:06 -0000 @@ -266,6 +266,7 @@ AC_OUTPUT(Makefile src/Makefile include/ 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 \ 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 5 Jan 2007 21:09:06 -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_QEMUD, /* Error at the QEMU daemon */ } virErrorDomain; Index: src/Makefile.am =================================================================== RCS file: /data/cvs/libvirt/src/Makefile.am,v retrieving revision 1.30 diff -u -p -r1.30 Makefile.am --- src/Makefile.am 16 Nov 2006 19:06:13 -0000 1.30 +++ src/Makefile.am 5 Jan 2007 21:09:07 -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"\" \ + -DLOCALSTATEDIR=\""$(localstatedir)"\" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" DEPS = libvirt.la LDADDS = @STATIC_BINARIES@ libvirt.la @@ -28,7 +29,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 \ + qemud_internal.c qemud_internal.h bin_PROGRAMS = virsh Index: src/driver.h =================================================================== RCS file: /data/cvs/libvirt/src/driver.h,v retrieving revision 1.15 diff -u -p -r1.15 driver.h --- src/driver.h 22 Nov 2006 17:48:29 -0000 1.15 +++ src/driver.h 5 Jan 2007 21:09:07 -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_QEMUD = 7 } virDrvNo; Index: src/libvirt.c =================================================================== RCS file: /data/cvs/libvirt/src/libvirt.c,v retrieving revision 1.51 diff -u -p -r1.51 libvirt.c --- src/libvirt.c 22 Nov 2006 17:48:29 -0000 1.51 +++ src/libvirt.c 5 Jan 2007 21:09:07 -0000 @@ -32,6 +32,7 @@ #include "proxy_internal.h" #include "xml.h" #include "test.h" +#include "qemud_internal.h" /* * TODO: @@ -79,6 +80,7 @@ virInitialize(void) xenStoreRegister(); xenXMRegister(); testRegister(); + qemudRegister(); return(0); } @@ -441,6 +443,7 @@ virConnectGetVersion(virConnectPtr conn, return(0); } } + return (-1); } Index: src/qemud_internal.c =================================================================== RCS file: src/qemud_internal.c diff -N src/qemud_internal.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/qemud_internal.c 5 Jan 2007 21:09:07 -0000 @@ -0,0 +1,896 @@ +/* + * qemud_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 <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 "qemud_internal.h" +#include "xml.h" +#include "protocol.h" + +int qemudOpen(virConnectPtr conn, + const char *name, + int flags); +int qemudClose (virConnectPtr conn); +int qemudGetVersion(virConnectPtr conn, + unsigned long *hvVer); +int qemudNodeGetInfo(virConnectPtr conn, + virNodeInfoPtr info); +int qemudNumOfDomains(virConnectPtr conn); +int qemudListDomains(virConnectPtr conn, + int *ids, + int maxids); +int qemudNumOfDefinedDomains(virConnectPtr conn); +int qemudListDefinedDomains(virConnectPtr conn, + const char **names, + int maxnames); +virDomainPtr +qemudDomainCreateLinux(virConnectPtr conn, const char *xmlDesc, + unsigned int flags); +virDomainPtr qemudLookupDomainByID(virConnectPtr conn, + int id); +virDomainPtr qemudLookupDomainByUUID(virConnectPtr conn, + const unsigned char *uuid); +virDomainPtr qemudLookupDomainByName(virConnectPtr conn, + const char *name); +int qemudShutdownDomain(virDomainPtr domain); +int qemudDestroyDomain(virDomainPtr domain); +int qemudResumeDomain(virDomainPtr domain); +int qemudPauseDomain(virDomainPtr domain); +int qemudGetDomainInfo(virDomainPtr domain, + virDomainInfoPtr info); +char * qemudDomainDumpXML(virDomainPtr domain, int flags); +int qemudSaveDomain(virDomainPtr domain, const char *file); +int qemudRestoreDomain(virConnectPtr conn, const char *file); +int qemudDomainCreate(virDomainPtr conn); +virDomainPtr qemudDomainDefineXML(virConnectPtr conn, const char *xml); +int qemudUndefine(virDomainPtr dom); + +static virDriver qemudDriver = { + VIR_DRV_QEMUD, + "QEMUD", + LIBVIR_VERSION_NUMBER, + NULL, /* init */ + qemudOpen, /* open */ + qemudClose, /* close */ + NULL, /* type */ + qemudGetVersion, /* version */ + qemudNodeGetInfo, /* nodeGetInfo */ + qemudListDomains, /* listDomains */ + qemudNumOfDomains, /* numOfDomains */ + qemudDomainCreateLinux, /* domainCreateLinux */ + qemudLookupDomainByID, /* domainLookupByID */ + qemudLookupDomainByUUID, /* domainLookupByUUID */ + qemudLookupDomainByName, /* domainLookupByName */ + qemudPauseDomain, /* domainSuspend */ + qemudResumeDomain, /* domainResume */ + qemudShutdownDomain, /* domainShutdown */ + NULL, /* domainReboot */ + qemudDestroyDomain, /* domainDestroy */ + NULL, /* domainFree */ + NULL, /* domainGetName */ + NULL, /* domainGetID */ + NULL, /* domainGetUUID */ + NULL, /* domainGetOSType */ + NULL, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + qemudGetDomainInfo, /* domainGetInfo */ + qemudSaveDomain, /* domainSave */ + qemudRestoreDomain, /* domainRestore */ + NULL, /* domainCoreDump */ + NULL, /* domainSetVcpus */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + qemudDomainDumpXML, /* domainDumpXML */ + qemudListDefinedDomains, /* listDomains */ + qemudNumOfDefinedDomains, /* numOfDomains */ + qemudDomainCreate, /* domainCreate */ + qemudDomainDefineXML, /* domainDefineXML */ + qemudUndefine, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ +}; + + +static void +qemudError(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_QEMUD, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info, 0); +} + +static void qemudPacketError(virConnectPtr con, + virDomainPtr dom, + struct qemud_packet *pkt) { + if (!pkt) { + qemudError(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'; + + qemudError(con, dom, pkt->data.failureReply.code, + pkt->data.failureReply.message[0] ? + pkt->data.failureReply.message : NULL); + } else { + qemudError(con, dom, VIR_ERR_INTERNAL_ERROR, "Incorrect reply type"); + } +} + +void qemudRegister(void) { + virRegisterDriver(&qemudDriver); +} + +/** + * qemudFindServerPath: + * + * Tries to find the path to the qemud binary. + * + * Returns path on success or NULL in case of error. + */ +static const char * +qemudFindServerPath(void) +{ + static const char *serverPaths[] = { + BINDIR "/libvirt_qemud", + BINDIR "/libvirt_qemud_dbg", + NULL + }; + int i; + const char *debugQemuD = getenv("LIBVIRT_QEMUD_SERVER"); + + if (debugQemuD) + return(debugQemuD); + + for (i = 0; serverPaths[i]; i++) { + if (access(serverPaths[i], X_OK | R_OK) == 0) { + return serverPaths[i]; + } + } + return NULL; +} + + +/** + * qemudForkServer: + * + * Forks and try to launch the qemud server + * + * Returns 0 in case of success or -1 in case of detected error. + */ +static int +qemudForkServer(const char *path) +{ + const char *proxyPath = qemudFindServerPath(); + int ret, pid, status; + + if (!proxyPath) { + fprintf(stderr, "failed to find qemud\n"); + return(-1); + } + + /* Become a daemon */ + pid = fork(); + if (pid == 0) { + int stdinfd = -1; + int stdoutfd = -1; + int i; + if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) + goto cleanup; + if ((stdoutfd = open(_PATH_DEVNULL, O_WRONLY)) < 0) + goto cleanup; + if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO) + goto cleanup; + if (close(stdinfd) < 0) + goto cleanup; + stdinfd = -1; + if (close(stdoutfd) < 0) + goto cleanup; + stdoutfd = -1; + + int open_max = sysconf (_SC_OPEN_MAX); + for (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, "--listen", path, "--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); +} + +/** + * qemudOpenClientUNIX: + * @path: the fileame for the socket + * + * try to connect to the socket open by qemud + * + * Returns the associated file descriptor or -1 in case of failure + */ +static int +qemudOpenClientUNIX(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; + addr.sun_path[0] = '\0'; + strncpy(&addr.sun_path[1], path, sizeof(addr.sun_path) - 2); + + /* + * 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) { + addr.sun_path[0] = '@'; /* We can't pass actual nulls around */ + if (qemudForkServer(addr.sun_path) < 0) + return(-1); + trials++; + usleep(5000 * trials * trials); + goto retry; + } + return (-1); + } + + return (fd); +} + +/** + * qemudOpenClientAddr: + * @addr: the address of the host + * + * try to connect to the socket open by qemud + * + * Returns the associated file descriptor or -1 in case of failure + */ +static int +qemudOpenClientAddr(const char *addr, int port) { + int fd = -1; + struct addrinfo *ai; + struct addrinfo hints; + struct addrinfo *tmp; + char service[30]; + + if (port == 0) + strcpy(service, QEMUD_DEFAULT_PORT_STR); + else + snprintf(service, sizeof(service)-1, "%d", port); + + memset (&hints, '\0', sizeof (hints)); + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo (addr, service, &hints, &ai) < 0) + return -1; + + tmp = ai; + while (tmp) { + if ((fd = socket(tmp->ai_family, tmp->ai_socktype, + tmp->ai_protocol)) < 0) + break; + + if (connect(fd, tmp->ai_addr, tmp->ai_addrlen) == 0) + break; + + close(fd); + fd = -1; + tmp = tmp->ai_next; + } + + freeaddrinfo(ai); + + return fd; +} + + + +/* 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 qemudProcessRequest(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)); + qemudPacketError(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) { + qemudPacketError(conn, dom, reply); + return -1; + } + + return 0; +} + + +/* + * Open a connection to the libvirt QEMU daemon + */ +static int qemudOpenConnection(xmlURIPtr uri, int readonly) { + + if (uri->server == NULL) { + if (!strcmp(uri->path, "/system")) { + if (readonly) + return qemudOpenClientUNIX(LOCALSTATEDIR "/run/qemud-ro", 0); + else + return qemudOpenClientUNIX(LOCALSTATEDIR "/run/qemud", 0); + } else if (!strcmp(uri->path, "/session")) { + char path[PATH_MAX]; + struct passwd *pw; + int uid; + + if ((uid = geteuid()) < 0) { + return -1; + } + + if (!(pw = getpwuid(uid))) + return -1; + + if (snprintf(path, PATH_MAX, "%s/.qemud.d/sock", pw->pw_dir) == PATH_MAX) { + return -1; + } + return qemudOpenClientUNIX(path, 1); + } else { + return -1; + } + } else { + return qemudOpenClientAddr(uri->server, uri->port); + } +} + + +/* + * Open a connection to the QEMU manager + */ +int qemudOpen(virConnectPtr conn, + const char *name, + int flags){ + xmlURIPtr uri; + int fd; + + if (!name) { + return -1; + } + + uri = xmlParseURI(name); + if (uri == NULL) { + if (!(flags & VIR_DRV_OPEN_QUIET)) + qemudError(conn, NULL, VIR_ERR_NO_SUPPORT, name); + return(-1); + } + + if (!uri->scheme || + strcmp(uri->scheme, "qemud") || + !uri->path) { + xmlFreeURI(uri); + return -1; + } + + fd = qemudOpenConnection(uri, flags & VIR_DRV_OPEN_RO ? 1 : 0); + xmlFreeURI(uri); + + if (fd < 0) { + return -1; + } + + conn->handle = fd; + + return 0; +} + + +int qemudClose (virConnectPtr conn) { + if (conn->handle != -1) { + close(conn->handle); + conn->handle = -1; + } + return 0; +} + + +int qemudGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned long *hvVer) { + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_GET_VERSION; + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + *hvVer = reply.data.getVersionReply.version; + return 0; +} + + +int qemudNodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, + virNodeInfoPtr info){ + info->cores = 1; + info->threads = 1; + info->sockets = 1; + info->nodes = 1; + strcpy(info->model, "i686"); + info->mhz = 6000; + info->cpus = 1; + info->memory = 1024*1024; + return 0; +} + + +int qemudNumOfDomains(virConnectPtr conn){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NUM_DOMAINS; + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return reply.data.numDomainsReply.numDomains; +} + + +int qemudListDomains(virConnectPtr conn, + int *ids, + int maxids){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_LIST_DOMAINS; + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + if (reply.data.listDomainsReply.numDomains > maxids) + return -1; + + memmove(ids, + reply.data.listDomainsReply.domains, + sizeof(int)*reply.data.listDomainsReply.numDomains); + + return reply.data.listDomainsReply.numDomains; +} + + +virDomainPtr +qemudDomainCreateLinux(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 (qemudProcessRequest(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->handle = reply.data.domainCreateReply.id; + return dom; +} + + +virDomainPtr qemudLookupDomainByID(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 (qemudProcessRequest(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->handle = id; + return dom; +} + + +virDomainPtr qemudLookupDomainByUUID(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 (qemudProcessRequest(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->handle = reply.data.domainLookupByUUIDReply.id; + return dom; +} + + +virDomainPtr qemudLookupDomainByName(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 (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return NULL; + } + + if (!(dom = virGetDomain(conn, + name, + reply.data.domainLookupByNameReply.uuid))) + return NULL; + + dom->handle = reply.data.domainLookupByNameReply.id; + return dom; +} +int qemudShutdownDomain(virDomainPtr domain){ + return qemudDestroyDomain(domain); +} +int qemudDestroyDomain(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->handle; + + if (qemudProcessRequest(domain->conn, NULL, &req, &reply) < 0) { + return -1; + } + + return 0; +} +int qemudResumeDomain(virDomainPtr domain ATTRIBUTE_UNUSED){ + return -1; +} +int qemudPauseDomain(virDomainPtr domain ATTRIBUTE_UNUSED){ + return -1; +} +int qemudGetDomainInfo(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 (qemudProcessRequest(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.memory; + info->memory = reply.data.domainGetInfoReply.memory; + info->nrVirtCpu = reply.data.domainGetInfoReply.nrVirtCpu; + info->cpuTime = reply.data.domainGetInfoReply.cpuTime; + + return 0; +} +char * qemudDomainDumpXML(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 (qemudProcessRequest(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 qemudSaveDomain(virDomainPtr domain ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED){ + return -1; +} +int qemudRestoreDomain(virConnectPtr conn ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED){ + return -1; +} +int qemudNumOfDefinedDomains(virConnectPtr conn){ + struct qemud_packet req, reply; + + req.header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS; + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return reply.data.numDefinedDomainsReply.numDomains; +} + +int qemudListDefinedDomains(virConnectPtr conn, + const char **names, + int maxnames){ + struct qemud_packet req, reply; + int i; + + req.header.type = QEMUD_PKT_LIST_DEFINED_DOMAINS; + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + if (reply.data.listDefinedDomainsReply.numDomains > maxnames) + return -1; + + for (i = 0 ; i < reply.data.listDefinedDomainsReply.numDomains ; i++) { + reply.data.listDefinedDomainsReply.domains[i][QEMUD_MAX_NAME_LEN-1] = '\0'; + names[i] = strdup(reply.data.listDefinedDomainsReply.domains[i]); + } + + return reply.data.listDefinedDomainsReply.numDomains; +} +int qemudDomainCreate(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 (qemudProcessRequest(dom->conn, NULL, &req, &reply) < 0) { + return -1; + } + + dom->handle = reply.data.domainStartReply.id; + + return 0; +} + +virDomainPtr qemudDomainDefineXML(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 (qemudProcessRequest(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->handle = -1; + return dom; +} + +int qemudUndefine(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 (qemudProcessRequest(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/qemud_internal.h =================================================================== RCS file: src/qemud_internal.h diff -N src/qemud_internal.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/qemud_internal.h 5 Jan 2007 21:09:07 -0000 @@ -0,0 +1,48 @@ +/* + * qemud_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_QEMUD_INTERNAL_H__ +#define __VIR_QEMUD_INTERNAL_H__ + +#include <libvirt/virterror.h> + +#ifdef __cplusplus +extern "C" { +#endif + + void qemudRegister(void); + +#ifdef __cplusplus +} +#endif +#endif /* __VIR_QEMUD_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.41 diff -u -p -r1.41 virsh.c --- src/virsh.c 22 Nov 2006 17:48:29 -0000 1.41 +++ src/virsh.c 5 Jan 2007 21:09:08 -0000 @@ -292,7 +292,7 @@ cmdConnect(vshControl * ctl, vshCmd * cm if (ctl->name) free(ctl->name); - ctl->name = vshCommandOptString(cmd, "name", NULL); + ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "name", NULL)); if (!ro) ctl->conn = virConnectOpen(ctl->name); @@ -2372,7 +2372,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 5 Jan 2007 21:09:08 -0000 @@ -268,6 +268,9 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_RPC: dom = "XML-RPC "; break; + case VIR_FROM_QEMUD: + dom = "QEMUD "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name;