From 27b7a8be52cb0fd4fd4489607ccba13b8fe03003 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Wed, 28 Jun 2006 18:19:13 +0000 Subject: [PATCH] * 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. Daniel --- ChangeLog | 8 + Makefile.am | 2 +- configure.in | 2 +- include/libvirt/virterror.h | 3 +- proxy/Makefile.am | 25 ++ proxy/libvirt_proxy.c | 514 +++++++++++++++++++++++++++++ proxy/proxy.h | 68 ++++ proxy/proxy_client.c | 632 ++++++++++++++++++++++++++++++++++++ src/internal.h | 3 +- src/xen_internal.c | 4 + src/xend_internal.c | 13 +- 11 files changed, 1269 insertions(+), 5 deletions(-) create mode 100644 proxy/Makefile.am create mode 100644 proxy/libvirt_proxy.c create mode 100644 proxy/proxy.h create mode 100644 proxy/proxy_client.c diff --git a/ChangeLog b/ChangeLog index 9daaeb1f8a..b7d0ecb9ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Wed Jun 28 19:23:25 CEST 2006 Daniel Veillard + + * 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 * configure.in libvirt.spec.in docs/examples/* include/Makefile.am diff --git a/Makefile.am b/Makefile.am index 8c02b0a6ff..c1e1265008 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## 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 \ diff --git a/configure.in b/configure.in index 97957388b9..57ae0d949a 100644 --- a/configure.in +++ b/configure.in @@ -256,4 +256,4 @@ AC_OUTPUT(Makefile src/Makefile include/Makefile docs/Makefile \ libvirt.pc libvirt.spec \ include/libvirt/Makefile include/libvirt/libvirt.h \ python/Makefile python/tests/Makefile \ - tests/Makefile) + tests/Makefile proxy/Makefile) diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index eca500b32d..8aeddb1984 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -44,7 +44,8 @@ typedef enum { 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; diff --git a/proxy/Makefile.am b/proxy/Makefile.am new file mode 100644 index 0000000000..1043412c40 --- /dev/null +++ b/proxy/Makefile.am @@ -0,0 +1,25 @@ +## 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 = diff --git a/proxy/libvirt_proxy.c b/proxy/libvirt_proxy.c new file mode 100644 index 0000000000..c1eaa9dfe9 --- /dev/null +++ b/proxy/libvirt_proxy.c @@ -0,0 +1,514 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/proxy/proxy.h b/proxy/proxy.h new file mode 100644 index 0000000000..1a5f2055f6 --- /dev/null +++ b/proxy/proxy.h @@ -0,0 +1,68 @@ +/* + * proxy.h: common definitions for proxy usage + * + * Copyright (C) 2006 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Veillard + */ + + +#ifndef __LIBVIR_PROXY_H__ +#define __LIBVIR_PROXY_H__ + +#include + +#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__ */ diff --git a/proxy/proxy_client.c b/proxy/proxy_client.c new file mode 100644 index 0000000000..78bb33df1a --- /dev/null +++ b/proxy/proxy_client.c @@ -0,0 +1,632 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 diff --git a/src/internal.h b/src/internal.h index 811136cec6..d15e373016 100644 --- a/src/internal.h +++ b/src/internal.h @@ -101,7 +101,8 @@ struct _virConnect { /* 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 */ diff --git a/src/xen_internal.c b/src/xen_internal.c index e50786d6f2..ccf7f94f84 100644 --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -53,6 +53,7 @@ static const char * xenHypervisorGetType(virConnectPtr conn); static unsigned long xenHypervisorGetMaxMemory(virDomainPtr domain); static int xenHypervisorInit(void); +#ifndef XEN_RO static virDriver xenHypervisorDriver = { VIR_DRV_XEN_HYPERVISOR, "Xen", @@ -88,6 +89,7 @@ static virDriver xenHypervisorDriver = { NULL, /* domainSave */ NULL /* domainRestore */ }; +#endif /* !XEN_RO */ /** * virXenError: @@ -175,6 +177,7 @@ done: } +#ifndef XEN_RO /** * xenHypervisorRegister: * @@ -187,6 +190,7 @@ void xenHypervisorRegister(void) virRegisterDriver(&xenHypervisorDriver); } +#endif /* !XEN_RO */ /** * xenHypervisorOpen: diff --git a/src/xend_internal.c b/src/xend_internal.c index 0a97a85ff1..69a61ec17d 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -48,6 +48,7 @@ static virDomainPtr xenDaemonCreateLinux(virConnectPtr conn, const char *xmlDesc, unsigned int flags); +#ifndef XEN_RO static virDriver xenDaemonDriver = { VIR_DRV_XEN_DAEMON, "XenDaemon", @@ -93,6 +94,7 @@ void xenDaemonRegister(void) { virRegisterDriver(&xenDaemonDriver); } +#endif /* !XEN_RO */ /** * xend_connection_type: @@ -1322,6 +1324,7 @@ xend_log(virConnectPtr xend, char *buffer, size_t n_buffer) ****** ****** *****************************************************************/ +#ifndef XEN_RO /** * xend_parse_sexp_desc: * @root: the root of the parsed S-Expression @@ -1509,6 +1512,7 @@ xend_parse_sexp_desc(struct sexpr *root) free(ret); return (NULL); } +#endif /* !XEN_RO */ /** * sexpr_to_xend_domain_info: @@ -1591,6 +1595,7 @@ sexpr_to_xend_node_info(struct sexpr *root, virNodeInfoPtr info) return (0); } +#ifndef XEN_RO /** * sexpr_to_domain: * @conn: an existing virtual connection block @@ -1636,7 +1641,7 @@ error: virFreeDomain(conn, ret); return(NULL); } - +#endif /* !XEN_RO */ /***************************************************************** ****** @@ -1649,6 +1654,7 @@ error: ****** ****** *****************************************************************/ +#ifndef XEN_RO /** * xenDaemonOpen: * @conn: an existing virtual connection block @@ -1699,6 +1705,7 @@ xenDaemonOpen(virConnectPtr conn, const char *name, int flags) return(ret); } +#endif /* !XEN_RO */ /** * xenDaemonClose: @@ -1958,6 +1965,7 @@ xenDaemonDomainSetMemory(virDomainPtr domain, unsigned long memory) "target", buf, NULL); } +#ifndef XEN_RO /** * xenDaemonDomainDumpXML: * @domain: a domain object @@ -1988,6 +1996,7 @@ xenDaemonDomainDumpXML(virDomainPtr domain) return (ret); } +#endif /* !XEN_RO */ /** * xenDaemonDomainGetInfo: @@ -2022,6 +2031,7 @@ xenDaemonDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) return (ret); } +#ifndef XEN_RO /** * xenDaemonDomainLookupByName: * @conn: A xend instance @@ -2053,6 +2063,7 @@ error: sexpr_free(root); return(ret); } +#endif /** * xenDaemonNodeGetInfo: