[Libvir] PATCH 2/2: Support QEMU (+KVM) in libvirt

Daniel P. Berrange berrange at redhat.com
Fri Jan 5 21:18:53 UTC 2007


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  -=| 
-------------- next part --------------
? 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 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"\" \
+           -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 at redhat.com>
+ */
+
+#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 at redhat.com>
+ */
+
+#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;


More information about the libvir-list mailing list