[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