+Wed Jun 28 19:23:25 CEST 2006 Daniel Veillard <veillard@redhat.com>
+
+ * configure.in Makefile.am proxy/Makefile.am proxy/libvirt_proxy.c
+ proxy/proxy.h proxy/proxy_client.c src/internal.h src/xen_internal.c
+ src/xend_internal.c: started working on a proxy to access xend
+ for unpriviledged users to avoid opening xend HTTP service to
+ serve those read-only operations.
+
Mon Jun 26 16:05:27 CEST 2006 Daniel Veillard <veillard@redhat.com>
* configure.in libvirt.spec.in docs/examples/* include/Makefile.am
## Process this file with automake to produce Makefile.in
-SUBDIRS = src include docs @PYTHON_SUBDIR@ tests
+SUBDIRS = src include docs @PYTHON_SUBDIR@ tests proxy
EXTRA_DIST = libvirt.spec.in libvirt.spec COPYING.LIB \
libvirt.pc.in libvirt.pc TODO AUTHORS ChangeLog \
libvirt.pc libvirt.spec \
include/libvirt/Makefile include/libvirt/libvirt.h \
python/Makefile python/tests/Makefile \
- tests/Makefile)
+ tests/Makefile proxy/Makefile)
VIR_FROM_SEXPR, /* Error in the S-Epression code */
VIR_FROM_XML, /* Error in the XML code */
VIR_FROM_DOM, /* Error when operating on a domain */
- VIR_FROM_RPC /* Error in the XML-RPC code */
+ VIR_FROM_RPC, /* Error in the XML-RPC code */
+ VIR_FROM_PROXY /* Error in the proxy code */
} virErrorDomain;
--- /dev/null
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include \
+ -I@top_srcdir@/proxy -I@top_srcdir@/src @LIBXML_CFLAGS@ \
+ -DBINDIR=\""$(libexecdir)"\" \
+ -DBUILDDIR=\""$(top_builddir)"\" \
+ -DXEN_RO
+
+
+bin_PROGRAMS = libvirt_proxy
+LIBS=
+
+libvirt_proxy_SOURCES = libvirt_proxy.c @top_srcdir@/src/xend_internal.c \
+ @top_srcdir@/src/xen_internal.c @top_srcdir@/src/virterror.c \
+ @top_srcdir@/src/sexpr.c
+
+libvirt_proxy_LDFLAGS =
+libvirt_proxy_DEPENDENCIES =
+libvirt_proxy_LDADD =
+
+noinst_PROGRAMS= test_proxy
+test_proxy_SOURCES = proxy_client.c
+test_proxy_LDFLAGS =
+test_proxy_DEPENDENCIES =
+test_proxy_LDADD =
--- /dev/null
+/*
+ * proxy_svr.c: root suid proxy server for Xen access to APIs with no
+ * side effects from unauthenticated clients.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Daniel Veillard <veillard@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "proxy.h"
+#include "internal.h"
+#include "xen_internal.h"
+
+static int fdServer = -1;
+static int debug = 0;
+static int done = 0;
+
+#define MAX_CLIENT 64
+
+static int nbClients = 0; /* client 0 is the unix listen socket */
+static struct pollfd pollInfos[MAX_CLIENT + 1];
+
+static virConnect conninfos;
+static virConnectPtr conn = &conninfos;
+
+/************************************************************************
+ * *
+ * Interfaces with the Xen hypervisor *
+ * *
+ ************************************************************************/
+
+/**
+ * proxyInitXen:
+ *
+ * Initialize the communication layer with Xen
+ *
+ * Returns 0 or -1 in case of error
+ */
+static int
+proxyInitXen(void) {
+ int ret;
+
+ ret = xenHypervisorOpen(conn, NULL, VIR_DRV_OPEN_QUIET);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to open Xen hypervisor\n");
+ return(-1);
+ }
+ ret = xenDaemonOpen_unix(conn, "/var/lib/xend/xend-socket");
+ if (ret < 0) {
+ fprintf(stderr, "Failed to connect to Xen daemon\n");
+ return(-1);
+ }
+ return(0);
+}
+
+/************************************************************************
+ * *
+ * Processing of the unix socket to listen for clients *
+ * *
+ ************************************************************************/
+
+/**
+ * proxyCloseUnixSocket:
+ *
+ * close the unix socket
+ *
+ * Returns 0 or -1 in case of error
+ */
+static int
+proxyCloseUnixSocket(void) {
+ int ret;
+
+ if (fdServer < 0)
+ return(0);
+
+ ret = close(fdServer);
+ if (debug > 0)
+ fprintf(stderr, "closing unix socket %d: %d\n", fdServer, ret);
+ fdServer = -1;
+ pollInfos[0].fd = -1;
+ return(ret);
+}
+
+/**
+ * proxyListenUnixSocket:
+ * @path: the fileame for the socket
+ *
+ * create a new abstract socket based on that path and listen on it
+ *
+ * Returns the associated file descriptor or -1 in case of failure
+ */
+static int
+proxyListenUnixSocket(const char *path) {
+ int fd;
+ struct sockaddr_un addr;
+
+ if (fdServer >= 0)
+ return(fdServer);
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to create unix socket");
+ 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) - 4) - 2);
+
+ /*
+ * now bind the socket to that address and listen on it
+ */
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "Failed to bind to socket %s\n", path);
+ close(fd);
+ return (-1);
+ }
+ if (listen(fd, 30 /* backlog */ ) < 0) {
+ fprintf(stderr, "Failed to listen to socket %s\n", path);
+ close(fd);
+ return (-1);
+ }
+
+ if (debug > 0)
+ fprintf(stderr, "opened and bound unix socket %d\n", fd);
+
+ fdServer = fd;
+ pollInfos[0].fd = fd;
+ pollInfos[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+ return (fd);
+}
+
+/**
+ * proxyAcceptClientSocket:
+ *
+ * Process a request to the unix socket
+ *
+ * Returns the filedescriptor of the new client or -1 in case of error
+ */
+static int
+proxyAcceptClientSocket(void) {
+ int client;
+ socklen_t client_addrlen;
+ struct sockaddr client_addr;
+
+retry:
+ client_addrlen = sizeof(client_addr);
+ client = accept(pollInfos[0].fd, &client_addr, &client_addrlen);
+ if (client < 0) {
+ if (errno == EINTR) {
+ if (debug > 0)
+ fprintf(stderr, "accept connection on socket %d interrupted\n",
+ pollInfos[0].fd);
+ goto retry;
+ }
+ fprintf(stderr, "Failed to accept incoming connection on socket %d\n",
+ pollInfos[0].fd);
+ done = 1;
+ return(-1);
+ }
+
+ if (nbClients >= MAX_CLIENT) {
+ fprintf(stderr, "Too many client registered\n");
+ close(client);
+ return(-1);
+ }
+ nbClients++;
+ pollInfos[nbClients].fd = client;
+ pollInfos[nbClients].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+ if (debug > 0)
+ fprintf(stderr, "accept connection on socket %d for client %d\n",
+ client, nbClients);
+ return(client);
+}
+
+/************************************************************************
+ * *
+ * Processing of client sockets *
+ * *
+ ************************************************************************/
+
+/**
+ * proxyCloseClientSocket:
+ * @nr: client number
+ *
+ * Close the socket from that client, and recompact the pollInfo array
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+static int
+proxyCloseClientSocket(int nr) {
+ int ret;
+
+ ret = close(pollInfos[nr].fd);
+ if (ret != 0)
+ fprintf(stderr, "Failed to close socket %d from client %d\n",
+ pollInfos[nr].fd, nr);
+ else if (debug > 0)
+ fprintf(stderr, "Closed socket %d from client %d\n",
+ pollInfos[nr].fd, nr);
+ if (nr < nbClients) {
+ memmove(&pollInfos[nr], &pollInfos[nr + 1],
+ (nbClients - nr) * sizeof(pollInfos[0]));
+ }
+ nbClients--;
+ return(ret);
+}
+
+/**
+ * proxyCloseClientSockets:
+ *
+ * Close all the sockets from the clients
+ */
+static void
+proxyCloseClientSockets(void) {
+ int i, ret;
+
+ for (i = 1;i <= nbClients;i++) {
+ ret = close(pollInfos[i].fd);
+ if (ret != 0)
+ fprintf(stderr, "Failed to close socket %d from client %d\n",
+ pollInfos[i].fd, i);
+ else if (debug > 0)
+ fprintf(stderr, "Closed socket %d from client %d\n",
+ pollInfos[i].fd, i);
+ }
+ nbClients = 0;
+}
+
+/**
+ * proxyWriteClientSocket:
+ * @nr: the client number
+ * @req: pointer to the packet
+ *
+ * Send back a packet to the client. If it seems write would be blocking
+ * then try to disconnect from it.
+ *
+ * Return 0 in case of success and -1 in case of error.
+ */
+static int
+proxyWriteClientSocket(int nr, virProxyPacketPtr req) {
+ int ret;
+
+ if ((nr <= 0) || (nr > nbClients) || (req == NULL) ||
+ (req->len < sizeof(virProxyPacket)) || (req->len > 4096) ||
+ (pollInfos[nr].fd < 0)) {
+ fprintf(stderr, "write to client %d in error", nr);
+ proxyCloseClientSocket(nr);
+ return(-1);
+ }
+
+retry:
+ ret = write(pollInfos[nr].fd, (char *) req, req->len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ if (debug > 0)
+ fprintf(stderr, "write socket %d to client %d interrupted\n",
+ pollInfos[nr].fd, nr);
+ goto retry;
+ }
+ fprintf(stderr, "write %d bytes to socket %d from client %d failed\n",
+ req->len, pollInfos[nr].fd, nr);
+ proxyCloseClientSocket(nr);
+ return(-1);
+ }
+ if (ret == 0) {
+ if (debug)
+ fprintf(stderr, "end of stream from client %d on socket %d\n",
+ nr, pollInfos[nr].fd);
+ proxyCloseClientSocket(nr);
+ return(-1);
+ }
+
+ if (ret != req->len) {
+ fprintf(stderr, "write %d of %d bytes to socket %d from client %d\n",
+ ret, req->len, pollInfos[nr].fd, nr);
+ proxyCloseClientSocket(nr);
+ return(-1);
+ }
+ if (debug)
+ fprintf(stderr, "wrote %d bytes to client %d on socket %d\n",
+ ret, nr, pollInfos[nr].fd);
+
+ return(0);
+}
+/**
+ * proxyReadClientSocket:
+ * @nr: the client number
+ *
+ * Process a read from a client socket
+ */
+static int
+proxyReadClientSocket(int nr) {
+ char buffer[4096];
+ virProxyPacketPtr req;
+ int ret;
+
+retry:
+ ret = read(pollInfos[nr].fd, buffer, sizeof(virProxyPacket));
+ if (ret < 0) {
+ if (errno == EINTR) {
+ if (debug > 0)
+ fprintf(stderr, "read socket %d from client %d interrupted\n",
+ pollInfos[nr].fd, nr);
+ goto retry;
+ }
+ fprintf(stderr, "Failed to read socket %d from client %d\n",
+ pollInfos[nr].fd, nr);
+ proxyCloseClientSocket(nr);
+ return(-1);
+ }
+ if (ret == 0) {
+ if (debug)
+ fprintf(stderr, "end of stream from client %d on socket %d\n",
+ nr, pollInfos[nr].fd);
+ proxyCloseClientSocket(nr);
+ return(-1);
+ }
+
+ if (debug)
+ fprintf(stderr, "read %d bytes from client %d on socket %d\n",
+ ret, nr, pollInfos[nr].fd);
+
+ req = (virProxyPacketPtr) &buffer[0];
+ if ((req->version != PROXY_PROTO_VERSION) ||
+ (req->len < sizeof(virProxyPacket)))
+ goto comm_error;
+
+ if (debug)
+ fprintf(stderr, "Gor command %d from client %d\n", req->command, nr);
+
+ switch (req->command) {
+ case VIR_PROXY_NONE:
+ if (req->len != sizeof(virProxyPacket))
+ goto comm_error;
+ break;
+ case VIR_PROXY_VERSION:
+ if (req->len != sizeof(virProxyPacket))
+ goto comm_error;
+ TODO;
+ req->data.larg = 3 * 1000000 + 2;
+ break;
+ case VIR_PROXY_NODE_INFO:
+ case VIR_PROXY_LIST:
+ case VIR_PROXY_NUM_DOMAIN:
+ case VIR_PROXY_LOOKUP_ID:
+ case VIR_PROXY_LOOKUP_UUID:
+ case VIR_PROXY_LOOKUP_NAME:
+ case VIR_PROXY_MAX_MEMORY:
+ case VIR_PROXY_DOMAIN_INFO:
+ break;
+ default:
+ goto comm_error;
+ }
+ ret = proxyWriteClientSocket(nr, req);
+ return(ret);
+
+comm_error:
+ fprintf(stderr,
+ "Communication error with client %d: malformed packet\n", nr);
+ proxyCloseClientSocket(nr);
+ return(-1);
+}
+
+/************************************************************************
+ * *
+ * Main loop processing *
+ * *
+ ************************************************************************/
+
+/**
+ * proxyProcessRequests:
+ *
+ * process requests and timers
+ */
+static void
+proxyProcessRequests(void) {
+ int exit_timeout = 30;
+ int ret, i;
+
+ while (!done) {
+ /*
+ * wait for requests, with a one second timeout
+ */
+ ret = poll(&pollInfos[0], nbClients + 1, 1000);
+ if (ret == 0) { /* timeout */
+ if (nbClients == 0) {
+ exit_timeout--;
+ if (exit_timeout == 0) {
+ done = 1;
+ if (debug > 0) {
+ fprintf(stderr, "Exitting after 30s without clients\n");
+ }
+ }
+ } else
+ exit_timeout = 30;
+ if (debug > 1)
+ fprintf(stderr, "poll timeout\n");
+ continue;
+ } else if (ret < 0) {
+ if (errno == EINTR) {
+ if (debug > 0)
+ fprintf(stderr, "poll syscall interrupted\n");
+ continue;
+ }
+ fprintf(stderr, "poll syscall failed\n");
+ break;
+ }
+ /*
+ * there have been I/O to process
+ */
+ exit_timeout = 30;
+ if (pollInfos[0].revents != 0) {
+ if (pollInfos[0].revents & POLLIN) {
+ proxyAcceptClientSocket();
+ } else {
+ fprintf(stderr, "Got an error %d on incoming socket %d\n",
+ pollInfos[0].revents, pollInfos[0].fd);
+ break;
+ }
+ }
+
+ /*
+ * process the clients in reverse order since on error or disconnect
+ * pollInfos is compacted to remove the given client.
+ */
+ for (i = nbClients;i > 0;i--) {
+ if (pollInfos[i].revents & POLLIN) {
+ proxyReadClientSocket(i);
+ } else if (pollInfos[i].revents != 0) {
+ fprintf(stderr, "Got an error %d on client %d socket %d\n",
+ pollInfos[i].revents, i, pollInfos[i].fd);
+ proxyCloseClientSocket(i);
+ }
+ }
+
+ }
+}
+
+/**
+ * proxyMainLoop:
+ *
+ * main loop for the proxy, continually try to keep the unix socket
+ * open, serve client requests, and process timing events.
+ */
+
+static void
+proxyMainLoop(void) {
+ while (! done) {
+ if (proxyListenUnixSocket(PROXY_SOCKET_PATH) < 0)
+ break;
+ proxyProcessRequests();
+ proxyCloseUnixSocket();
+ }
+ proxyCloseClientSockets();
+ proxyCloseUnixSocket();
+}
+
+/**
+ * usage:
+ *
+ * dump on stdout informations about the program
+ */
+static void
+usage(const char *progname) {
+ printf("Usage: %s [-v] [-v]\n", progname);
+ printf(" option -v increase the verbosity level for debugging\n");
+ printf("This is a proxy for xen services used by libvirt to offer\n");
+ printf("safe and fast status information on the Xen virtualization.\n");
+ printf("This need not be run manually it's started automatically.\n");
+}
+
+/**
+ * main:
+ *
+ * Check that we are running with root priviledges, initialize the
+ * connections to the daemon and or hypervisor, and then run the main loop
+ */
+int main(int argc, char **argv) {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-v")) {
+ debug++;
+ } else {
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "%s must be run as root or suid\n", argv[0]);
+ /* exit(1); */
+ }
+
+ proxyInitXen();
+ proxyMainLoop();
+ exit(0);
+}
--- /dev/null
+/*
+ * proxy.h: common definitions for proxy usage
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Daniel Veillard <veillard@redhat.com>
+ */
+
+
+#ifndef __LIBVIR_PROXY_H__
+#define __LIBVIR_PROXY_H__
+
+#include <libvirt/libvirt.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PROXY_SOCKET_PATH "/tmp/livirt_proxy_conn"
+#define PROXY_PROTO_VERSION 1
+
+/*
+ * the command allowed though the proxy
+ */
+typedef enum {
+ VIR_PROXY_NONE = 0,
+ VIR_PROXY_VERSION = 1,
+ VIR_PROXY_NODE_INFO = 2,
+ VIR_PROXY_LIST = 3,
+ VIR_PROXY_NUM_DOMAIN = 4,
+ VIR_PROXY_LOOKUP_ID = 5,
+ VIR_PROXY_LOOKUP_UUID = 6,
+ VIR_PROXY_LOOKUP_NAME = 7,
+ VIR_PROXY_MAX_MEMORY = 8,
+ VIR_PROXY_DOMAIN_INFO = 9
+} virProxyCommand;
+
+/*
+ * structure used by the client to make a request to the proxy
+ * and by the proxy when answering the client.
+ * the size may not be fixed, it's passed as len.
+ */
+struct _virProxyPacket {
+ unsigned short version; /* version of the proxy protocol */
+ unsigned short command; /* command number a virProxyCommand */
+ unsigned short serial; /* command serial number */
+ unsigned short len; /* the length of the request */
+ union {
+ char string[8]; /* string data */
+ int arg; /* or int argument */
+ long larg; /* or long argument */
+ } data;
+};
+typedef struct _virProxyPacket virProxyPacket;
+typedef virProxyPacket *virProxyPacketPtr;
+
+/*
+ * Functions callable from libvirt library
+ */
+int xenProxyInit(virConnectPtr conn);
+void xenProxyClose(virConnectPtr conn);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __LIBVIR_PROXY_H__ */
--- /dev/null
+/*
+ * proxy_client.c: client side of the communication with the libvirt proxy.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Daniel Veillard <veillard@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include "proxy.h"
+#include "internal.h"
+
+#define STANDALONE
+
+static int debug = 1;
+
+/************************************************************************
+ * *
+ * Error handling *
+ * *
+ ************************************************************************/
+
+/**
+ * virProxyError:
+ * @conn: the connection if available
+ * @error: the error noumber
+ * @info: extra information string
+ *
+ * Handle an error at the xend daemon interface
+ */
+static void
+virProxyError(virConnectPtr conn, virErrorNumber error, const char *info)
+{
+ const char *errmsg;
+
+ if (error == VIR_ERR_OK)
+ return;
+
+#if 0
+ errmsg = __virErrorMsg(error, info);
+ __virRaiseError(conn, NULL, VIR_FROM_XEND, error, VIR_ERR_ERROR,
+ errmsg, info, NULL, 0, 0, errmsg, info);
+#endif
+}
+
+/************************************************************************
+ * *
+ * Automatic startup of the proxy server if it is not running *
+ * *
+ ************************************************************************/
+/**
+ * virProxyFindServerPath:
+ *
+ * Tries to find the path to the gam_server binary.
+ *
+ * Returns path on success or NULL in case of error.
+ */
+static const char *
+virProxyFindServerPath(void)
+{
+ static const char *serverPaths[] = {
+#ifdef STANDALONE
+ "./libvirt_proxy",
+ BUILDDIR "/proxy/libvirt_proxy",
+#endif
+ BINDIR "/libvirt_proxy",
+ NULL
+ };
+ int i;
+ const char *debugProxy = getenv("LIBVIRT_DEBUG_PROXY");
+
+ if (debugProxy)
+ return(debugProxy);
+
+ for (i = 0; serverPaths[i]; i++) {
+ if (access(serverPaths[i], X_OK | R_OK) == 0) {
+ return serverPaths[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * virProxyForkServer:
+ *
+ * Forks and try to launch the proxy server processing the requests for
+ * libvirt when communicating with Xen.
+ *
+ * Returns 0 in case of success or -1 in case of detected error.
+ */
+static int
+virProxyForkServer(void)
+{
+ const char *proxyPath = virProxyFindServerPath();
+ int ret, pid, status;
+
+ if (!proxyPath) {
+ fprintf(stderr, "failed to find libvirt_proxy\n");
+ return(-1);
+ }
+
+ if (debug)
+ fprintf(stderr, "Asking to launch %s\n", proxyPath);
+
+ /* Become a daemon */
+ pid = fork();
+ if (pid == 0) {
+ long open_max;
+ long i;
+
+ /* don't hold open fd opened from the client of the library */
+ open_max = sysconf (_SC_OPEN_MAX);
+ for (i = 0; i < open_max; i++)
+ fcntl (i, F_SETFD, FD_CLOEXEC);
+
+ setsid();
+ if (fork() == 0) {
+ execl(proxyPath, proxyPath, NULL);
+ fprintf(stderr, "failed to exec %s\n", proxyPath);
+ }
+ /*
+ * calling exit() generate troubles for termination handlers
+ */
+ _exit(0);
+ }
+
+ /*
+ * 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);
+}
+
+/************************************************************************
+ * *
+ * Processing of client sockets *
+ * *
+ ************************************************************************/
+
+/**
+ * virProxyOpenClientSocket:
+ * @path: the fileame for the socket
+ *
+ * try to connect to the socket open by libvirt_proxy
+ *
+ * Returns the associated file descriptor or -1 in case of failure
+ */
+static int
+virProxyOpenClientSocket(const char *path) {
+ int fd;
+ struct sockaddr_un addr;
+ int trials = 0;
+
+retry:
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to create unix socket");
+ 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) - 4) - 2);
+
+ /*
+ * now bind the socket to that address and listen on it
+ */
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "Failed to connect to socket %s\n", path);
+ close(fd);
+ if (trials < 3) {
+ if (virProxyForkServer() < 0)
+ return(-1);
+ trials++;
+ usleep(5000 * trials * trials);
+ goto retry;
+ }
+ return (-1);
+ }
+
+ if (debug > 0)
+ fprintf(stderr, "connected to unix socket %s via %d\n", path, fd);
+
+ return (fd);
+}
+
+/**
+ * virProxyCloseClientSocket:
+ * @fd: the file descriptor for the socket
+ *
+ * Close the socket from that client
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+static int
+virProxyCloseClientSocket(int fd) {
+ int ret;
+
+ if (fd < 0)
+ return(-1);
+
+ ret = close(fd);
+ if (ret != 0)
+ fprintf(stderr, "Failed to close socket %d\n", fd);
+ else if (debug > 0)
+ fprintf(stderr, "Closed socket %d\n", fd);
+ return(ret);
+}
+
+/**
+ * virProxyReadClientSocket:
+ * @fd: the socket
+ * @buffer: the target memory area
+ * @len: the lenght in bytes
+ *
+ * Process a read from a client socket
+ *
+ * Returns the number of byte read or -1 in case of error.
+ */
+static int
+virProxyReadClientSocket(int fd, char *buffer, int len) {
+ int ret;
+
+ if ((fd < 0) || (buffer == NULL) || (len < 0))
+ return(-1);
+
+retry:
+ ret = read(fd, buffer, len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ if (debug > 0)
+ fprintf(stderr, "read socket %d interrupted\n", fd);
+ goto retry;
+ }
+ fprintf(stderr, "Failed to read socket %d\n", fd);
+ return(-1);
+ }
+
+ if (debug)
+ fprintf(stderr, "read %d bytes from socket %d\n",
+ ret, fd);
+ return(ret);
+}
+
+/**
+ * virProxyWriteClientSocket:
+ * @fd: the socket
+ * @data: the data
+ * @len: the lenght of data in bytes
+ *
+ * Process a read from a client socket
+ */
+static int
+virProxyWriteClientSocket(int fd, const char *data, int len) {
+ int ret;
+
+ if ((fd < 0) || (data == NULL) || (len < 0))
+ return(-1);
+
+retry:
+ ret = write(fd, data, len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ if (debug > 0)
+ fprintf(stderr, "write socket %d, %d bytes interrupted\n",
+ fd, len);
+ goto retry;
+ }
+ fprintf(stderr, "Failed to write to socket %d\n", fd);
+ return(-1);
+ }
+ if (debug)
+ fprintf(stderr, "wrote %d bytes to socket %d\n",
+ len, fd);
+
+ return(0);
+}
+
+/************************************************************************
+ * *
+ * Proxy commands processing *
+ * *
+ ************************************************************************/
+
+/**
+ * xenProxyClose:
+ * @conn: pointer to the hypervisor connection
+ *
+ * Shutdown the Xen proxy communication layer
+ */
+void
+xenProxyClose(virConnectPtr conn) {
+ if ((conn == NULL) || (conn->proxy < 0))
+ return;
+ virProxyCloseClientSocket(conn->proxy);
+ conn->proxy = -1;
+}
+
+static int
+xenProxyCommand(virConnectPtr conn, virProxyPacketPtr request,
+ virProxyPacketPtr *answer) {
+ static int serial = 0;
+ int ret;
+ virProxyPacketPtr res = NULL;
+ char packet[4096];
+
+ if ((conn == NULL) || (conn->proxy < 0))
+ return(-1);
+
+ /*
+ * normal communication serial numbers are in 0..4095
+ */
+ ++serial;
+ if (serial >= 4096)
+ serial = 0;
+ request->version = PROXY_PROTO_VERSION;
+ request->serial = serial;
+ ret = virProxyWriteClientSocket(conn->proxy, (const char *) request,
+ request->len);
+ if (ret < 0)
+ return(-1);
+retry:
+ if (answer == NULL) {
+ /* read in situ */
+ ret = virProxyReadClientSocket(conn->proxy, (char *) request,
+ sizeof(virProxyPacket));
+ if (ret < 0)
+ return(-1);
+ if (ret != sizeof(virProxyPacket)) {
+ fprintf(stderr,
+ "Communication error with proxy: got %d bytes of %d\n",
+ ret, sizeof(virProxyPacket));
+ xenProxyClose(conn);
+ return(-1);
+ }
+ res = request;
+ if (res->len != sizeof(virProxyPacket)) {
+ fprintf(stderr,
+ "Communication error with proxy: expected %d bytes got %d\n",
+ sizeof(virProxyPacket), res->len);
+ xenProxyClose(conn);
+ return(-1);
+ }
+ } else {
+ /* read in packet and duplicate if needed */
+ ret = virProxyReadClientSocket(conn->proxy, &packet[0],
+ sizeof(virProxyPacket));
+ if (ret < 0)
+ return(-1);
+ if (ret != sizeof(virProxyPacket)) {
+ fprintf(stderr,
+ "Communication error with proxy: got %d bytes of %d\n",
+ ret, sizeof(virProxyPacket));
+ xenProxyClose(conn);
+ return(-1);
+ }
+ res = (virProxyPacketPtr) &packet[0];
+ if ((res->len < sizeof(virProxyPacket)) ||
+ (res->len > sizeof(packet))) {
+ fprintf(stderr,
+ "Communication error with proxy: got %d bytes packet\n",
+ res->len);
+ xenProxyClose(conn);
+ return(-1);
+ }
+ if (res->len > sizeof(virProxyPacket)) {
+ ret = virProxyReadClientSocket(conn->proxy, &packet[ret],
+ res->len - ret);
+ if (ret != (int) (res->len - sizeof(virProxyPacket))) {
+ fprintf(stderr,
+ "Communication error with proxy: got %d bytes of %d\n",
+ ret, sizeof(virProxyPacket));
+ xenProxyClose(conn);
+ return(-1);
+ }
+ }
+ }
+ /*
+ * do more checks on the incoming packet.
+ */
+ if ((res == NULL) || (res->version != PROXY_PROTO_VERSION) ||
+ (res->len < sizeof(virProxyPacket))) {
+ fprintf(stderr,
+ "Communication error with proxy: malformed packet\n");
+ xenProxyClose(conn);
+ return(-1);
+ }
+ if (res->serial != serial) {
+ TODO /* Asynchronous communication */
+ fprintf(stderr, "gor asynchronous packet number %d\n", res->serial);
+ goto retry;
+ }
+ if (answer != NULL)
+ *answer = res;
+ return(0);
+}
+
+/**
+ * xenProxyInit:
+ * @conn: pointer to the hypervisor connection
+ *
+ * Try to initialize the Xen proxy communication layer
+ *
+ * Returns 0 in case of success, and -1 in case of failure
+ */
+int
+xenProxyInit(virConnectPtr conn) {
+ virProxyPacket req;
+ int ret;
+ int fd;
+
+
+ if (conn == NULL)
+ return(-1);
+
+ if (conn->proxy <= 0) {
+ fd = virProxyOpenClientSocket(PROXY_SOCKET_PATH);
+ if (fd < 0) {
+ return(-1);
+ }
+ conn->proxy = fd;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.command = VIR_PROXY_NONE;
+ req.len = sizeof(req);
+ ret = xenProxyCommand(conn, &req, NULL);
+ if ((ret < 0) || (req.command != VIR_PROXY_NONE)) {
+ xenProxyClose(conn);
+ return(-1);
+ }
+ return(0);
+}
+
+/************************************************************************
+ * *
+ * Driver entry points *
+ * *
+ ************************************************************************/
+
+/**
+ * xenProxyGetVersion:
+ * @conn: pointer to the Xen Daemon block
+ * @hvVer: return value for the version of the running hypervisor (OUT)
+ *
+ * Get the version level of the Hypervisor running.
+ *
+ * Returns -1 in case of error, 0 otherwise. if the version can't be
+ * extracted by lack of capacities returns 0 and @hvVer is 0, otherwise
+ * @hvVer value is major * 1,000,000 + minor * 1,000 + release
+ */
+static int
+xenProxyGetVersion(virConnectPtr conn, unsigned long *hvVer)
+{
+ virProxyPacket req;
+ int ret;
+
+ if (!VIR_IS_CONNECT(conn)) {
+ virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__);
+ return (-1);
+ }
+ if (hvVer == NULL) {
+ virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+ return (-1);
+ }
+ memset(&req, 0, sizeof(req));
+ req.command = VIR_PROXY_VERSION;
+ req.len = sizeof(req);
+ ret = xenProxyCommand(conn, &req, NULL);
+ if (ret < 0) {
+ xenProxyClose(conn);
+ return(-1);
+ }
+ *hvVer = req.data.larg;
+ return(0);
+}
+
+/**
+ * xenProxyNodeGetInfo:
+ * @conn: pointer to the Xen Daemon block
+ * @info: pointer to a virNodeInfo structure allocated by the user
+ *
+ * Extract hardware information about the node.
+ *
+ * Returns 0 in case of success and -1 in case of failure.
+ */
+static int
+xenProxyNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info) {
+}
+
+/**
+ * xenProxyListDomains:
+ * @conn: pointer to the hypervisor connection
+ * @ids: array to collect the list of IDs of active domains
+ * @maxids: size of @ids
+ *
+ * Collect the list of active domains, and store their ID in @maxids
+ * TODO: this is quite expensive at the moment since there isn't one
+ * xend RPC providing both name and id for all domains.
+ *
+ * Returns the number of domain found or -1 in case of error
+ */
+static int
+xenProxyListDomains(virConnectPtr conn, int *ids, int maxids)
+{
+}
+
+/**
+ * xenProxyNumOfDomains:
+ * @conn: pointer to the hypervisor connection
+ *
+ * Provides the number of active domains.
+ *
+ * Returns the number of domain found or -1 in case of error
+ */
+static int
+xenProxyNumOfDomains(virConnectPtr conn)
+{
+}
+
+/**
+ * xenProxyLookupByID:
+ * @conn: pointer to the hypervisor connection
+ * @id: the domain ID number
+ *
+ * Try to find a domain based on the hypervisor ID number
+ *
+ * Returns the domain name (to be freed) or NULL in case of failure
+ */
+static char *
+xenProxyLookupByID(virConnectPtr conn, int id) {
+}
+
+/**
+ * xenProxyLookupByUUID:
+ * @conn: pointer to the hypervisor connection
+ * @uuid: the raw UUID for the domain
+ *
+ * Try to lookup a domain on xend based on its UUID.
+ *
+ * Returns the domain id or -1 in case of error
+ */
+static int
+xenProxyLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
+{
+}
+
+/**
+ * xenProxyDomainLookupByName:
+ * @conn: A xend instance
+ * @name: The name of the domain
+ *
+ * This method looks up information about a domain based on its name
+ *
+ * Returns domain id or -1 in case of error
+ */
+static int
+xenProxyDomainLookupByName(virConnectPtr conn, const char *domname)
+{
+}
+
+
+/**
+ * xenProxyDomainGetMaxMemory:
+ * @domain: pointer to the domain block
+ *
+ * Ask the Xen Daemon for the maximum memory allowed for a domain
+ *
+ * Returns the memory size in kilobytes or 0 in case of error.
+ */
+unsigned long
+xenProxyDomainGetMaxMemory(virDomainPtr domain)
+{
+}
+
+/**
+ * xenProxyDomainGetInfo:
+ * @domain: a domain object
+ * @info: pointer to a virDomainInfo structure allocated by the user
+ *
+ * This method looks up information about a domain and update the
+ * information block provided.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+int
+xenProxyDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
+{
+}
+
+#ifdef STANDALONE
+int main(int argc, char **argv) {
+ int ret;
+ unsigned long ver;
+ virConnect conn;
+
+ memset(&conn, 0, sizeof(conn));
+ ret = xenProxyInit(&conn);
+ if (ret == 0) {
+ ret = xenProxyGetVersion(&conn, &ver);
+ if (ret != 0) {
+ fprintf(stderr, "Failed to get version from proxy\n");
+ } else {
+ printf("Proxy running with version %lu\n", ver);
+ }
+ xenProxyClose(&conn);
+ }
+ exit(0);
+}
+#endif
/* extra data needed by drivers */
int handle; /* internal handle used for hypercall */
- struct xs_handle *xshandle; /* handle to talk to the xenstore */
+ struct xs_handle *xshandle;/* handle to talk to the xenstore */
+ int proxy; /* file descriptor if using the proxy */
/* connection to xend */
int type; /* PF_UNIX or PF_INET */
static unsigned long xenHypervisorGetMaxMemory(virDomainPtr domain);
static int xenHypervisorInit(void);
+#ifndef XEN_RO
static virDriver xenHypervisorDriver = {
VIR_DRV_XEN_HYPERVISOR,
"Xen",
NULL, /* domainSave */
NULL /* domainRestore */
};
+#endif /* !XEN_RO */
/**
* virXenError:
}
+#ifndef XEN_RO
/**
* xenHypervisorRegister:
*
virRegisterDriver(&xenHypervisorDriver);
}
+#endif /* !XEN_RO */
/**
* xenHypervisorOpen:
const char *xmlDesc,
unsigned int flags);
+#ifndef XEN_RO
static virDriver xenDaemonDriver = {
VIR_DRV_XEN_DAEMON,
"XenDaemon",
{
virRegisterDriver(&xenDaemonDriver);
}
+#endif /* !XEN_RO */
/**
* xend_connection_type:
******
******
*****************************************************************/
+#ifndef XEN_RO
/**
* xend_parse_sexp_desc:
* @root: the root of the parsed S-Expression
free(ret);
return (NULL);
}
+#endif /* !XEN_RO */
/**
* sexpr_to_xend_domain_info:
return (0);
}
+#ifndef XEN_RO
/**
* sexpr_to_domain:
* @conn: an existing virtual connection block
virFreeDomain(conn, ret);
return(NULL);
}
-
+#endif /* !XEN_RO */
/*****************************************************************
******
******
******
*****************************************************************/
+#ifndef XEN_RO
/**
* xenDaemonOpen:
* @conn: an existing virtual connection block
return(ret);
}
+#endif /* !XEN_RO */
/**
* xenDaemonClose:
"target", buf, NULL);
}
+#ifndef XEN_RO
/**
* xenDaemonDomainDumpXML:
* @domain: a domain object
return (ret);
}
+#endif /* !XEN_RO */
/**
* xenDaemonDomainGetInfo:
return (ret);
}
+#ifndef XEN_RO
/**
* xenDaemonDomainLookupByName:
* @conn: A xend instance
sexpr_free(root);
return(ret);
}
+#endif
/**
* xenDaemonNodeGetInfo: