libvirt/src/qemu_internal.c
2007-03-15 18:23:00 +00:00

1277 lines
35 KiB
C

/*
* 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@redhat.com>
*/
#ifdef WITH_QEMU
#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"
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, NULL, 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");
}
}
/**
* 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[] = {
SBINDIR "/libvirt_qemud",
SBINDIR "/libvirt_qemud_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->qemud_fd = 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->qemud_fd, 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->qemud_fd, 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->qemud_fd, 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) {
char path[PATH_MAX];
int autostart = 0;
if (uri->server != NULL) {
return -1;
}
if (!strcmp(uri->path, "/system")) {
if (readonly) {
if (snprintf(path, sizeof(path), "%s/run/libvirt/qemud-sock-ro", LOCAL_STATE_DIR) >= (int)sizeof(path)) {
return -1;
}
} else {
if (snprintf(path, sizeof(path), "%s/run/libvirt/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/.libvirt/qemud-sock", pw->pw_dir) == sizeof(path)) {
return -1;
}
autostart = 1;
}
return qemuOpenClientUNIX(conn, path, autostart);
}
/*
* Open a connection to the QEMU manager
*/
static 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->qemud_fd = -1;
qemuOpenConnection(conn, uri, flags & VIR_DRV_OPEN_RO ? 1 : 0);
xmlFreeURI(uri);
if (conn->qemud_fd < 0) {
return -1;
}
return 0;
}
static int qemuClose (virConnectPtr conn) {
if (conn->qemud_fd != -1) {
close(conn->qemud_fd);
conn->qemud_fd = -1;
}
return 0;
}
static int qemuGetVersion(virConnectPtr conn,
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;
}
static int qemuNodeGetInfo(virConnectPtr conn,
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;
}
static char *
qemuGetCapabilities (virConnectPtr conn)
{
struct qemud_packet req, reply;
char *xml;
/* Punt the request across to the daemon, because the daemon
* has tables describing available architectures.
*/
req.header.type = QEMUD_PKT_GET_CAPABILITIES;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.getCapabilitiesReply.xml[QEMUD_MAX_XML_LEN-1] = '\0';
xml = strdup (reply.data.getCapabilitiesReply.xml);
if (!xml) {
qemuError (conn, NULL, VIR_ERR_NO_MEMORY, NULL);
return NULL;
}
return xml;
}
static 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;
}
static 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)
nDomains = maxids;
for (i = 0 ; i < nDomains ; i++) {
ids[i] = reply.data.listDomainsReply.domains[i];
}
return nDomains;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static int qemuShutdownDomain(virDomainPtr domain) {
return qemuDestroyDomain(domain);
}
static 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;
}
static 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;
}
static 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;
}
static 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);
}
static int qemuSaveDomain(virDomainPtr domain ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED) {
return -1;
}
static int qemuRestoreDomain(virConnectPtr conn ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED) {
return -1;
}
static 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;
}
static int qemuListDefinedDomains(virConnectPtr conn,
char **const 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)
nDomains = maxnames;
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;
}
static 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;
}
static 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;
}
static 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;
}
static int qemuDomainGetAutostart(virDomainPtr dom,
int *autostart) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DOMAIN_GET_AUTOSTART;
req.header.dataSize = sizeof(req.data.domainGetAutostartRequest);
memmove(req.data.domainGetAutostartRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) {
return -1;
}
*autostart = reply.data.domainGetAutostartReply.autostart;
return 0;
}
static int qemuDomainSetAutostart(virDomainPtr dom,
int autostart) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_DOMAIN_SET_AUTOSTART;
req.header.dataSize = sizeof(req.data.domainSetAutostartRequest);
req.data.domainSetAutostartRequest.autostart = (autostart != 0);
memmove(req.data.domainSetAutostartRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) {
return -1;
}
return 0;
}
static int qemuNetworkOpen(virConnectPtr conn,
const char *name,
int flags) {
xmlURIPtr uri = NULL;
int ret = -1;
if (conn->qemud_fd != -1)
return 0;
if (name)
uri = xmlParseURI(name);
if (uri && uri->scheme && !strcmp(uri->scheme, "qemu"))
ret = qemuOpen(conn, name, flags);
else if (geteuid() == 0)
ret = qemuOpen(conn, "qemu:///system", flags);
else
ret = qemuOpen(conn, "qemu:///session", flags);
if (uri)
xmlFreeURI(uri);
return ret;
}
static int qemuNumOfNetworks(virConnectPtr conn) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NUM_NETWORKS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
return reply.data.numNetworksReply.numNetworks;
}
static int qemuListNetworks(virConnectPtr conn,
char **const names,
int maxnames) {
struct qemud_packet req, reply;
int i, nNetworks;
req.header.type = QEMUD_PKT_LIST_NETWORKS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
nNetworks = reply.data.listNetworksReply.numNetworks;
if (nNetworks > maxnames)
return -1;
for (i = 0 ; i < nNetworks ; i++) {
reply.data.listNetworksReply.networks[i][QEMUD_MAX_NAME_LEN-1] = '\0';
names[i] = strdup(reply.data.listNetworksReply.networks[i]);
}
return nNetworks;
}
static int qemuNumOfDefinedNetworks(virConnectPtr conn) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NUM_DEFINED_NETWORKS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
return reply.data.numDefinedNetworksReply.numNetworks;
}
static int qemuListDefinedNetworks(virConnectPtr conn,
char **const names,
int maxnames) {
struct qemud_packet req, reply;
int i, nNetworks;
req.header.type = QEMUD_PKT_LIST_DEFINED_NETWORKS;
req.header.dataSize = 0;
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return -1;
}
nNetworks = reply.data.listDefinedNetworksReply.numNetworks;
if (nNetworks > maxnames)
return -1;
for (i = 0 ; i < nNetworks ; i++) {
reply.data.listDefinedNetworksReply.networks[i][QEMUD_MAX_NAME_LEN-1] = '\0';
names[i] = strdup(reply.data.listDefinedNetworksReply.networks[i]);
}
return nNetworks;
}
static virNetworkPtr qemuNetworkLookupByUUID(virConnectPtr conn,
const unsigned char *uuid) {
struct qemud_packet req, reply;
virNetworkPtr network;
req.header.type = QEMUD_PKT_NETWORK_LOOKUP_BY_UUID;
req.header.dataSize = sizeof(req.data.networkLookupByUUIDRequest);
memmove(req.data.networkLookupByUUIDRequest.uuid, uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.networkLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
if (!(network = virGetNetwork(conn,
reply.data.networkLookupByUUIDReply.name,
uuid)))
return NULL;
return network;
}
static virNetworkPtr qemuNetworkLookupByName(virConnectPtr conn,
const char *name) {
struct qemud_packet req, reply;
virNetworkPtr network;
if (strlen(name) > (QEMUD_MAX_NAME_LEN-1))
return NULL;
req.header.type = QEMUD_PKT_NETWORK_LOOKUP_BY_NAME;
req.header.dataSize = sizeof(req.data.networkLookupByNameRequest);
strcpy(req.data.networkLookupByNameRequest.name, name);
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
if (!(network = virGetNetwork(conn,
name,
reply.data.networkLookupByNameReply.uuid)))
return NULL;
return network;
}
static virNetworkPtr qemuNetworkCreateXML(virConnectPtr conn,
const char *xmlDesc) {
struct qemud_packet req, reply;
virNetworkPtr network;
int len = strlen(xmlDesc);
if (len > (QEMUD_MAX_XML_LEN-1)) {
return NULL;
}
req.header.type = QEMUD_PKT_NETWORK_CREATE;
req.header.dataSize = sizeof(req.data.networkCreateRequest);
strcpy(req.data.networkCreateRequest.xml, xmlDesc);
req.data.networkCreateRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0';
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.networkCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
if (!(network = virGetNetwork(conn,
reply.data.networkCreateReply.name,
reply.data.networkCreateReply.uuid)))
return NULL;
return network;
}
static virNetworkPtr qemuNetworkDefineXML(virConnectPtr conn,
const char *xml) {
struct qemud_packet req, reply;
virNetworkPtr network;
int len = strlen(xml);
if (len > (QEMUD_MAX_XML_LEN-1)) {
return NULL;
}
req.header.type = QEMUD_PKT_NETWORK_DEFINE;
req.header.dataSize = sizeof(req.data.networkDefineRequest);
strcpy(req.data.networkDefineRequest.xml, xml);
req.data.networkDefineRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0';
if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.networkDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
if (!(network = virGetNetwork(conn,
reply.data.networkDefineReply.name,
reply.data.networkDefineReply.uuid)))
return NULL;
return network;
}
static int qemuNetworkUndefine(virNetworkPtr network) {
struct qemud_packet req, reply;
int ret = 0;
req.header.type = QEMUD_PKT_NETWORK_UNDEFINE;
req.header.dataSize = sizeof(req.data.networkUndefineRequest);
memcpy(req.data.networkUndefineRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) {
ret = -1;
goto cleanup;
}
cleanup:
if (virFreeNetwork(network->conn, network) < 0)
ret = -1;
return ret;
}
static int qemuNetworkCreate(virNetworkPtr network) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NETWORK_START;
req.header.dataSize = sizeof(req.data.networkStartRequest);
memcpy(req.data.networkStartRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) {
return -1;
}
return 0;
}
static int qemuNetworkDestroy(virNetworkPtr network) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NETWORK_DESTROY;
req.header.dataSize = sizeof(req.data.networkDestroyRequest);
memcpy(req.data.networkDestroyRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) {
return -1;
}
return 0;
}
static char * qemuNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSED) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NETWORK_DUMP_XML;
req.header.dataSize = sizeof(req.data.networkDumpXMLRequest);
memmove(req.data.networkDumpXMLRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.networkDumpXMLReply.xml[QEMUD_MAX_XML_LEN-1] = '\0';
return strdup(reply.data.networkDumpXMLReply.xml);
}
static char * qemuNetworkGetBridgeName(virNetworkPtr network) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NETWORK_GET_BRIDGE_NAME;
req.header.dataSize = sizeof(req.data.networkGetBridgeNameRequest);
memmove(req.data.networkGetBridgeNameRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) {
return NULL;
}
reply.data.networkGetBridgeNameReply.ifname[QEMUD_MAX_IFNAME_LEN-1] = '\0';
return strdup(reply.data.networkGetBridgeNameReply.ifname);
}
static int qemuNetworkGetAutostart(virNetworkPtr network,
int *autostart) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NETWORK_GET_AUTOSTART;
req.header.dataSize = sizeof(req.data.networkGetAutostartRequest);
memmove(req.data.networkGetAutostartRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) {
return -1;
}
*autostart = reply.data.networkGetAutostartReply.autostart;
return 0;
}
static int qemuNetworkSetAutostart(virNetworkPtr network,
int autostart) {
struct qemud_packet req, reply;
req.header.type = QEMUD_PKT_NETWORK_SET_AUTOSTART;
req.header.dataSize = sizeof(req.data.networkSetAutostartRequest);
req.data.networkSetAutostartRequest.autostart = (autostart != 0);
memmove(req.data.networkSetAutostartRequest.uuid, network->uuid, QEMUD_UUID_RAW_LEN);
if (qemuProcessRequest(network->conn, NULL, &req, &reply) < 0) {
return -1;
}
return 0;
}
static virDriver qemuDriver = {
VIR_DRV_QEMU,
"QEMU",
LIBVIR_VERSION_NUMBER,
qemuOpen, /* open */
qemuClose, /* close */
NULL, /* type */
qemuGetVersion, /* version */
NULL, /* getMaxVcpus */
qemuNodeGetInfo, /* nodeGetInfo */
qemuGetCapabilities, /* getCapabilities */
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 */
NULL, /* domainGetMaxVcpus */
qemuDomainDumpXML, /* domainDumpXML */
qemuListDefinedDomains, /* listDomains */
qemuNumOfDefinedDomains, /* numOfDomains */
qemuDomainCreate, /* domainCreate */
qemuDomainDefineXML, /* domainDefineXML */
qemuUndefine, /* domainUndefine */
NULL, /* domainAttachDevice */
NULL, /* domainDetachDevice */
qemuDomainGetAutostart, /* domainGetAutostart */
qemuDomainSetAutostart, /* domainSetAutostart */
};
static virNetworkDriver qemuNetworkDriver = {
qemuNetworkOpen, /* open */
qemuClose, /* close */
qemuNumOfNetworks, /* numOfNetworks */
qemuListNetworks, /* listNetworks */
qemuNumOfDefinedNetworks, /* numOfDefinedNetworks */
qemuListDefinedNetworks, /* listDefinedNetworks */
qemuNetworkLookupByUUID, /* networkLookupByUUID */
qemuNetworkLookupByName, /* networkLookupByName */
qemuNetworkCreateXML , /* networkCreateXML */
qemuNetworkDefineXML , /* networkDefineXML */
qemuNetworkUndefine, /* networkUndefine */
qemuNetworkCreate, /* networkCreate */
qemuNetworkDestroy, /* networkDestroy */
qemuNetworkDumpXML, /* networkDumpXML */
qemuNetworkGetBridgeName, /* networkGetBridgeName */
qemuNetworkGetAutostart, /* networkGetAutostart */
qemuNetworkSetAutostart, /* networkSetAutostart */
};
/**
* qemuRegister:
*
* Registers QEmu/KVM in libvirt driver system
*/
void qemuRegister(void) {
virRegisterDriver(&qemuDriver);
virRegisterNetworkDriver(&qemuNetworkDriver);
}
#endif /* WITH_QEMU */
/*
* Local variables:
* indent-tabs-mode: nil
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/