diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/include/libvirt/virterror.h libvirt-qemu/include/libvirt/virterror.h --- libvirt/include/libvirt/virterror.h 2006-11-08 11:40:42.000000000 -0500 +++ libvirt-qemu/include/libvirt/virterror.h 2007-01-04 17:53:35.000000000 -0500 @@ -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; diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/driver.h libvirt-qemu/src/driver.h --- libvirt/src/driver.h 2007-01-22 15:50:58.000000000 -0500 +++ libvirt-qemu/src/driver.h 2007-01-22 12:00:57.000000000 -0500 @@ -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; diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/internal.h libvirt-qemu/src/internal.h --- libvirt/src/internal.h 2007-01-22 15:50:58.000000000 -0500 +++ libvirt-qemu/src/internal.h 2007-01-22 12:00:58.000000000 -0500 @@ -18,6 +18,8 @@ #include "driver.h" #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -91,6 +93,8 @@ extern "C" { */ #define VIR_CONNECT_RO 1 +extern gnutls_certificate_credentials_t xcred; + /** * _virConnect: * @@ -117,6 +121,9 @@ struct _virConnect { struct sockaddr_un addr_un; /* the unix address */ struct sockaddr_in addr_in; /* the inet address */ + int secure; + gnutls_session_t session; + /* error stuff */ virError err; /* the last error */ virErrorFunc handler; /* associated handlet */ diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/libvirt.c libvirt-qemu/src/libvirt.c --- libvirt/src/libvirt.c 2007-01-22 15:50:59.000000000 -0500 +++ libvirt-qemu/src/libvirt.c 2007-01-22 12:00:58.000000000 -0500 @@ -32,6 +32,7 @@ #include "proxy_internal.h" #include "xml.h" #include "test.h" +#include "qemud_internal.h" /* * TODO: @@ -79,6 +80,8 @@ virInitialize(void) xenStoreRegister(); xenXMRegister(); testRegister(); + qemudRegister(); + return(0); } @@ -441,6 +444,7 @@ virConnectGetVersion(virConnectPtr conn, return(0); } } + return (-1); } diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/Makefile.am libvirt-qemu/src/Makefile.am --- libvirt/src/Makefile.am 2006-11-16 14:01:42.000000000 -0500 +++ libvirt-qemu/src/Makefile.am 2007-01-08 15:16:29.000000000 -0500 @@ -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 @@ -11,9 +12,10 @@ EXTRA_DIST = libvirt_sym.version lib_LTLIBRARIES = libvirt.la libvirt_la_LIBADD = @LIBXML_LIBS@ - +libvirt_la_CFLAGS = $(shell pkg-config --cflags gnutls) libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \ - -version-info @LIBVIRT_VERSION_INFO@ + -version-info @LIBVIRT_VERSION_INFO@ \ + $(shell pkg-config --libs gnutls) libvirt_la_SOURCES = \ libvirt.c internal.h \ @@ -28,7 +30,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 diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/qemud_internal.c libvirt-qemu/src/qemud_internal.c --- libvirt/src/qemud_internal.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/src/qemud_internal.c 2007-01-22 15:06:06.000000000 -0500 @@ -0,0 +1,1134 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "internal.h" +#include "qemud_internal.h" +#include "xml.h" +#include "protocol.h" + +#define CA_FILE "ca-cert.pem" +#define CRL_FILE "ca-crl.pem" +#define KEY_FILE "key.pem" +#define CERT_FILE "cert.pem" + +static gnutls_certificate_credentials_t x509_cred; +static int tlsinitialized = 0; + +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, /* 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 (qemud_wire_32(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, + qemud_wire_32(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); +} + + +static int qemudTLSInitialize(void) { + int ret; + struct stat st; + if (tlsinitialized) + return 0; + + if (gnutls_global_init () < 0) + return -1; + + if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { + printf("Cannot allocate credentials %s\n", gnutls_strerror(ret)); + return -1; + } + if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, CA_FILE, GNUTLS_X509_FMT_PEM)) < 0) { + printf("Cannot load CA certificate %s\n", gnutls_strerror(ret)); + return -1; + } + + if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, CERT_FILE, KEY_FILE, + GNUTLS_X509_FMT_PEM)) < 0) { + printf("Cannot load certificate & key %s\n", gnutls_strerror(ret)); + return -1; + } + + if (stat(CRL_FILE, &st) < 0) { + if (errno != ENOENT) { + return -1; + } + } else { + if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, CRL_FILE, GNUTLS_X509_FMT_PEM)) < 0) { + printf("Cannot load CRL %s\n", gnutls_strerror(ret)); + return -1; + } + } + tlsinitialized = 1; + return 0; +} + +/** + * 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(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; + 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); + } + + conn->handle = fd; + conn->secure = 0; + + return (0); +} + +static int qemudValidateCertificate(virConnectPtr conn) { + int ret; + unsigned int status; + const gnutls_datum_t *certs; + unsigned int nCerts, i; + time_t now; + + printf("Validating\n"); + if ((ret = gnutls_certificate_verify_peers2 (conn->session, &status)) < 0) { + printf("Verify failed %s\n", gnutls_strerror(ret)); + return -1; + } + + if ((now = time(NULL)) == ((time_t)-1)) { + return -1; + } + + if (status != 0) { + if (status & GNUTLS_CERT_INVALID) + printf ("The certificate is not trusted.\n"); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + printf ("The certificate hasn't got a known issuer.\n"); + + if (status & GNUTLS_CERT_REVOKED) + printf ("The certificate has been revoked.\n"); + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + printf ("The certificate uses an insecure algorithm\n"); + + return -1; + } else { + printf("Certificate is valid!\n"); + } + + if (gnutls_certificate_type_get(conn->session) != GNUTLS_CRT_X509) + return -1; + + if (!(certs = gnutls_certificate_get_peers(conn->session, &nCerts))) + return -1; + + for (i = 0 ; i < nCerts ; i++) { + gnutls_x509_crt_t cert; + printf ("Checking chain %d\n", i); + if (gnutls_x509_crt_init (&cert) < 0) + return -1; + + if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time (cert) < now) { + printf("The certificate has expired\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_activation_time (cert) > now) { + printf("The certificate is not yet activated\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (i == 0) { + const char *hostname = "foo"; + if (!gnutls_x509_crt_check_hostname (cert, hostname)) { + printf ("The certificate's owner does not match hostname '%s'\n", + hostname); + gnutls_x509_crt_deinit (cert); + return -1; + } + } + } + + return 0; +} + +/** + * 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(virConnectPtr conn, 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); + + conn->handle = fd; + conn->secure = 0; + + 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 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) + qemud_wire_32(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; + if (conn->secure) + got = gnutls_write(conn->session, out+outDone, outLeft); + else + 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; + if (conn->secure) + done = gnutls_read(conn->session, in+inGot, inLeft); + else + 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 (qemud_wire_32(reply->header.dataSize) > sizeof(union qemud_packet_data)) { + printf("Got type %ds body %d (max %ld)\n", + qemud_wire_32(reply->header.type), + qemud_wire_32(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 = qemud_wire_32(reply->header.dataSize); + while (inLeft) { + int done; + if (conn->secure) + done = gnutls_read(conn->session, in+inGot, inLeft); + else + done = read(conn->handle, in+inGot, inLeft); + if (done <= 0) { + return -1; + } + inGot += done; + inLeft -= done; + } + + if (qemud_wire_32(reply->header.type) != qemud_wire_32(req->header.type)) { + qemudPacketError(conn, dom, reply); + return -1; + } + + return 0; +} + +static int qemudClientInitialize(virConnectPtr conn) { + static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; + static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_SSL3, 0 }; + struct qemud_packet req, reply; + int tls = 0; + + printf("Initializing\n"); + req.header.type = qemud_wire_32(QEMUD_PKT_PROTOCOL_HELLO); + req.header.dataSize = qemud_wire_32(sizeof(req.data.protocolHelloRequest)); + req.data.protocolHelloRequest.version = qemud_wire_32((QEMUD_PROTOCOL_VERSION_MAJOR << 16) + + QEMUD_PROTOCOL_VERSION_MINOR); + req.data.protocolHelloRequest.modes = qemud_wire_32(QEMUD_MODE_CLEAR | + QEMUD_MODE_TLS); + + /* Do version info initalize */ + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + printf("Server accepts major %d minor %d\n", + ((qemud_wire_32(reply.data.protocolHelloReply.version) >> 16) && 0xff), + (qemud_wire_32(reply.data.protocolHelloReply.version) && 0xff)); + + if (qemud_wire_32(reply.data.protocolHelloReply.mode) == QEMUD_MODE_TLS) { + printf("Server requested TLS mode\n"); + tls = 1; + } + + if (tls) { + printf("TLS accepted, starting handshake\n"); + if (gnutls_init(&conn->session, GNUTLS_CLIENT) < 0) + return -1; + //gnutls_set_default_priority(conn->session); + if (gnutls_set_default_export_priority(conn->session) < 0) { + gnutls_deinit(conn->session); + return -1; + } + if (gnutls_certificate_type_set_priority(conn->session, cert_type_priority) < 0) { + gnutls_deinit(conn->session); + return -1; + } + if (gnutls_protocol_set_priority(conn->session, protocol_priority) < 0) { + gnutls_deinit(conn->session); + return -1; + } + if (gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { + gnutls_deinit(conn->session); + return -1; + } + + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) (long)conn->handle); + + if (gnutls_handshake(conn->session) < 0) { + printf("Handshake failed\n"); + gnutls_deinit(conn->session); + return -1; + } + printf("Handshake done\n"); + conn->secure = 1; + + if (qemudValidateCertificate(conn) < 0) { + return -1; + } + } + + return 0; +} + + +/* + * Open a connection to the libvirt QEMU daemon + */ +static int qemudOpenConnection(virConnectPtr conn, xmlURIPtr uri, int readonly) { + + if (uri->server == NULL) { + if (!strcmp(uri->path, "/system")) { + if (readonly) + return qemudOpenClientUNIX(conn, LOCALSTATEDIR "/run/qemud-ro", 0); + else + return qemudOpenClientUNIX(conn, 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(conn, path, 1); + } else { + return -1; + } + } else { + return qemudOpenClientAddr(conn, uri->server, uri->port); + } +} + + +/* + * Open a connection to the QEMU manager + */ +int qemudOpen(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)) + qemudError(conn, NULL, VIR_ERR_NO_SUPPORT, name); + return(-1); + } + + if (!uri->scheme || + strcmp(uri->scheme, "qemud") || + !uri->path) { + xmlFreeURI(uri); + return -1; + } + + if (qemudTLSInitialize() < 0) { + return -1; + } + conn->handle = -1; + qemudOpenConnection(conn, uri, flags & VIR_DRV_OPEN_RO ? 1 : 0); + xmlFreeURI(uri); + + if (conn->handle < 0) { + return -1; + } + + if (qemudClientInitialize(conn) < 0) { + qemudClose(conn); + return -1; + } + + return 0; +} + + +int qemudClose (virConnectPtr conn) { + if (conn->handle != -1) { + if (conn->secure) { + gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); + gnutls_deinit(conn->session); + } + 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_wire_32(QEMUD_PKT_GET_VERSION); + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + *hvVer = qemud_wire_32(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_wire_32(QEMUD_PKT_NUM_DOMAINS); + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return qemud_wire_32(reply.data.numDomainsReply.numDomains); +} + + +int qemudListDomains(virConnectPtr conn, + int *ids, + int maxids){ + struct qemud_packet req, reply; + int i, nDomains; + + req.header.type = qemud_wire_32(QEMUD_PKT_LIST_DOMAINS); + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + nDomains = qemud_wire_32(reply.data.listDomainsReply.numDomains); + if (nDomains > maxids) + return -1; + + for (i = 0 ; i < nDomains ; i++) { + ids[i] = qemud_wire_32(reply.data.listDomainsReply.domains[i]); + } + + return nDomains; +} + + +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_wire_32(QEMUD_PKT_DOMAIN_CREATE); + req.header.dataSize = qemud_wire_32(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->id = qemud_wire_32(reply.data.domainCreateReply.id); + return dom; +} + + +virDomainPtr qemudLookupDomainByID(virConnectPtr conn, + int id){ + struct qemud_packet req, reply; + virDomainPtr dom; + + req.header.type = qemud_wire_32(QEMUD_PKT_DOMAIN_LOOKUP_BY_ID); + req.header.dataSize = qemud_wire_32(sizeof(req.data.domainLookupByIDRequest)); + req.data.domainLookupByIDRequest.id = qemud_wire_32(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->id = id; + return dom; +} + + +virDomainPtr qemudLookupDomainByUUID(virConnectPtr conn, + const unsigned char *uuid){ + struct qemud_packet req, reply; + virDomainPtr dom; + + req.header.type = qemud_wire_32(QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID); + req.header.dataSize = qemud_wire_32(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->id = qemud_wire_32(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_wire_32(QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME); + req.header.dataSize = qemud_wire_32(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->id = qemud_wire_32(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_wire_32(QEMUD_PKT_DOMAIN_DESTROY); + req.header.dataSize = qemud_wire_32(sizeof(req.data.domainDestroyRequest)); + req.data.domainDestroyRequest.id = qemud_wire_32(domain->id); + + 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_wire_32(QEMUD_PKT_DOMAIN_GET_INFO); + req.header.dataSize = qemud_wire_32(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 (qemud_wire_32(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 = qemud_wire_32(reply.data.domainGetInfoReply.maxmem); + info->memory = qemud_wire_32(reply.data.domainGetInfoReply.memory); + info->nrVirtCpu = qemud_wire_32(reply.data.domainGetInfoReply.nrVirtCpu); + info->cpuTime = qemud_wire_64(reply.data.domainGetInfoReply.cpuTime); + + return 0; +} + +char * qemudDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED){ + struct qemud_packet req, reply; + + req.header.type = qemud_wire_32(QEMUD_PKT_DUMP_XML); + req.header.dataSize = qemud_wire_32(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_wire_32(QEMUD_PKT_NUM_DEFINED_DOMAINS); + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + return qemud_wire_32(reply.data.numDefinedDomainsReply.numDomains); +} + +int qemudListDefinedDomains(virConnectPtr conn, + const char **names, + int maxnames){ + struct qemud_packet req, reply; + int i, nDomains; + + req.header.type = qemud_wire_32(QEMUD_PKT_LIST_DEFINED_DOMAINS); + req.header.dataSize = 0; + + if (qemudProcessRequest(conn, NULL, &req, &reply) < 0) { + return -1; + } + + nDomains = qemud_wire_32(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 qemudDomainCreate(virDomainPtr dom) { + struct qemud_packet req, reply; + + req.header.type = qemud_wire_32(QEMUD_PKT_DOMAIN_START); + req.header.dataSize = qemud_wire_32(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->id = qemud_wire_32(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_wire_32(QEMUD_PKT_DOMAIN_DEFINE); + req.header.dataSize = qemud_wire_32(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->id = -1; + return dom; +} + +int qemudUndefine(virDomainPtr dom) { + struct qemud_packet req, reply; + int ret = 0; + + req.header.type = qemud_wire_32(QEMUD_PKT_DOMAIN_UNDEFINE); + req.header.dataSize = qemud_wire_32(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: + */ diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/qemud_internal.h libvirt-qemu/src/qemud_internal.h --- libvirt/src/qemud_internal.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/src/qemud_internal.h 2007-01-04 12:45:01.000000000 -0500 @@ -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 + */ + +#ifndef __VIR_QEMUD_INTERNAL_H__ +#define __VIR_QEMUD_INTERNAL_H__ + +#include + +#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: + */ diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/virsh.c libvirt-qemu/src/virsh.c --- libvirt/src/virsh.c 2007-01-22 15:50:59.000000000 -0500 +++ libvirt-qemu/src/virsh.c 2007-01-22 15:45:02.000000000 -0500 @@ -10,7 +10,7 @@ * Daniel P. Berrange * * - * $Id: virsh.c,v 1.42 2007/01/22 20:43:02 berrange Exp $ + * $Id: virsh.c,v 1.41 2006/11/22 17:48:29 veillard Exp $ */ #define _GNU_SOURCE /* isblank() */ @@ -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); diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/src/virterror.c libvirt-qemu/src/virterror.c --- libvirt/src/virterror.c 2006-11-08 11:40:42.000000000 -0500 +++ libvirt-qemu/src/virterror.c 2007-01-04 17:54:07.000000000 -0500 @@ -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; diff --exclude .deps --exclude CVS --exclude Makefile.in -ruNp libvirt/tests/Makefile.am libvirt-qemu/tests/Makefile.am --- libvirt/tests/Makefile.am 2007-01-19 15:24:28.000000000 -0500 +++ libvirt-qemu/tests/Makefile.am 2007-01-22 13:30:52.000000000 -0500 @@ -15,7 +15,7 @@ INCLUDES = \ LDADDS = \ @STATIC_BINARIES@ \ @LIBXML_LIBS@ \ - $(LIBVIRT) + $(LIBVIRT) -lgnutls EXTRA_DIST = xmlrpcserver.py test_conf.sh