]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
* configure.in Makefile.am proxy/Makefile.am proxy/libvirt_proxy.c
authorDaniel Veillard <veillard@redhat.com>
Wed, 28 Jun 2006 18:19:13 +0000 (18:19 +0000)
committerDaniel Veillard <veillard@redhat.com>
Wed, 28 Jun 2006 18:19:13 +0000 (18:19 +0000)
  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
Makefile.am
configure.in
include/libvirt/virterror.h
proxy/Makefile.am [new file with mode: 0644]
proxy/libvirt_proxy.c [new file with mode: 0644]
proxy/proxy.h [new file with mode: 0644]
proxy/proxy_client.c [new file with mode: 0644]
src/internal.h
src/xen_internal.c
src/xend_internal.c

index 9daaeb1f8a5ef227ff6cfcadac5a6bfa98ab0f33..b7d0ecb9ef841ef6db399b31a6c4021446e844ad 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+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
index 8c02b0a6ff66f2d40a9079a83a8b2c91578b0441..c1e1265008ca0d1f79870555b1f2cf682879033f 100644 (file)
@@ -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 \
index 97957388b9a3ea6f58f5f91050e30e0440741bf3..57ae0d949aaa90f0dad0aa6ae88f23bbcd9a9aa6 100644 (file)
@@ -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)
index eca500b32ddd222064bf36f958612e0889038b99..8aeddb198462f9db47b224b324cd0d2a8760a925 100644 (file)
@@ -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 (file)
index 0000000..1043412
--- /dev/null
@@ -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 (file)
index 0000000..c1eaa9d
--- /dev/null
@@ -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 <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);
+}
diff --git a/proxy/proxy.h b/proxy/proxy.h
new file mode 100644 (file)
index 0000000..1a5f205
--- /dev/null
@@ -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 <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__ */
diff --git a/proxy/proxy_client.c b/proxy/proxy_client.c
new file mode 100644 (file)
index 0000000..78bb33d
--- /dev/null
@@ -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 <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
index 811136cec6d6ceb25dda45bf07ce4b1d87723c3b..d15e373016d797326c8c1b37150898d48e6d45b0 100644 (file)
@@ -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 */
index e50786d6f2227b25139951122b6bc5fcb9c024d3..ccf7f94f84977ebc6335e7c98a7df4372747b029 100644 (file)
@@ -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:
index 0a97a85ff1d214733928134a1b63865e01f5e563..69a61ec17d06d1482a6b8f31f035e0f26bc66be9 100644 (file)
@@ -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: