]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Mon Jun 11 12:41:00 BST 2007 Richard W.M. Jones <rjones@redhat.com>
authorRichard W.M. Jones <rjones@redhat.com>
Mon, 11 Jun 2007 11:43:41 +0000 (11:43 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Mon, 11 Jun 2007 11:43:41 +0000 (11:43 +0000)
* src/remote_internal.c, src/remote_internal.h,
  src/driver.h, src/libvirt.c, src/Makefile.am: Client side
  of the remote patch - adds a remote driver.

ChangeLog
src/Makefile.am
src/driver.h
src/libvirt.c
src/remote_internal.c [new file with mode: 0644]
src/remote_internal.h [new file with mode: 0644]

index 4f2f456882000342bf4bc7c5a3a90bb2248cad66..d28d9652227b7d5f41fd82887602611bb8a99013 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,13 @@
+Mon Jun 11 12:41:00 BST 2007 Richard W.M. Jones <rjones@redhat.com>
+
+       * src/remote_internal.c, src/remote_internal.h,
+         src/driver.h, src/libvirt.c, src/Makefile.am: Client side
+         of the remote patch - adds a remote driver.
+
 Mon Jun 11 12:34:00 BST 2007 Richard W.M. Jones <rjones@redhat.com>
 
-       * qemud/remote_protocol.x qemud/rpcgen_fix.pl
-         qemud/remote_protocol.c qemud/remote_protocol.h: Client/server
+       * qemud/remote_protocol.x, qemud/rpcgen_fix.pl,
+         qemud/remote_protocol.c, qemud/remote_protocol.h: Client/server
          protocol used by remote.
 
 Fri Jun  8 13:06:01 CEST 2007 Daniel Veillard <veillard@redhat.com>
index bcf42f3610fba87299d65b7c347ca146616961e4..f7170f207ab3218e3048dab18c652bec2a4a221f 100644 (file)
@@ -1,9 +1,17 @@
 ## Process this file with automake to produce Makefile.in
 
-INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ -I@top_srcdir@/qemud \
-          -DBINDIR=\""$(libexecdir)"\" -DSBINDIR=\""$(sbindir)"\" -DLOCALEBASEDIR=\""$(datadir)/locale"\" \
+INCLUDES = -I$(top_builddir)/include \
+          -I@top_srcdir@/include \
+          -I@top_srcdir@/qemud \
+          @LIBXML_CFLAGS@ \
+          -DBINDIR=\""$(libexecdir)"\" \
+          -DSBINDIR=\""$(sbindir)"\" \
+          -DSYSCONF_DIR="\"$(sysconfdir)\"" \
+          -DLOCALEBASEDIR=\""$(datadir)/locale"\" \
            -DLOCAL_STATE_DIR=\""$(localstatedir)"\" \
-           -DGETTEXT_PACKAGE=\"$(PACKAGE)\" $(WARN_CFLAGS) $(LIBVIRT_FEATURES)
+           -DGETTEXT_PACKAGE=\"$(PACKAGE)\" \
+          $(WARN_CFLAGS) \
+          $(LIBVIRT_FEATURES)
 DEPS = libvirt.la
 LDADDS = @STATIC_BINARIES@ libvirt.la
 VIRSH_LIBS = @VIRSH_LIBS@
@@ -31,7 +39,10 @@ libvirt_la_SOURCES =                                         \
                proxy_internal.c proxy_internal.h               \
                conf.c conf.h                                   \
                xm_internal.c xm_internal.h                     \
-               qemu_internal.c qemu_internal.h
+               ../qemud/protocol.h ../qemud/protocol.c         \
+               qemu_internal.c qemu_internal.h                 \
+               ../qemud/remote_protocol.c ../qemud/remote_protocol.h \
+               remote_internal.c remote_internal.h
 
 bin_PROGRAMS = virsh
 
index 6ed1080ff43bb937cf79929b87d967fc1737ed2c..e3ed93b32b6ac0eca2b5711dd6d6708ee05305cc 100644 (file)
@@ -20,6 +20,7 @@ typedef enum {
     VIR_DRV_XEN_UNIFIED = 1,
     VIR_DRV_TEST = 2,
     VIR_DRV_QEMU = 3,
+    VIR_DRV_REMOTE = 4,
 } virDrvNo;
 
 
index 5a79bc1f7c69418db174f5569104ecfb577a6ebe..c81fe5c0330190cb190dd3d0f5f708b32009b1c6 100644 (file)
@@ -28,6 +28,7 @@
 #include "test.h"
 #include "xen_unified.h"
 #include "qemu_internal.h"
+#include "remote_internal.h"
 
 /*
  * TODO:
@@ -64,6 +65,9 @@ virInitialize(void)
      * Note that the order is important: the first ones have a higher
      * priority when calling virConnectOpen.
      */
+#ifdef WITH_REMOTE
+    if (remoteRegister () == -1) return -1;
+#endif
 #ifdef WITH_TEST
     if (testRegister() == -1) return -1;
 #endif
@@ -402,6 +406,9 @@ virConnectClose(virConnectPtr conn)
  * Get the name of the Hypervisor software used.
  *
  * Returns NULL in case of error, a static zero terminated string otherwise.
+ *
+ * See also:
+ * http://www.redhat.com/archives/libvir-list/2007-February/msg00096.html
  */
 const char *
 virConnectGetType(virConnectPtr conn)
diff --git a/src/remote_internal.c b/src/remote_internal.c
new file mode 100644 (file)
index 0000000..66487be
--- /dev/null
@@ -0,0 +1,2547 @@
+/*
+ * remote_internal.c: driver to provide access to libvirtd running
+ *   on a remote machine
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Richard Jones <rjones@redhat.com>
+ */
+
+#define _GNU_SOURCE /* for asprintf */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <rpc/xdr.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <libxml/uri.h>
+
+#include "internal.h"
+#include "driver.h"
+#include "remote_internal.h"
+#include "remote_protocol.h"
+
+#define DEBUG 0                 /* Enable verbose messages on stderr. */
+
+/* Per-connection private data. */
+#define MAGIC 999               /* private_data->magic if OK */
+#define DEAD 998                /* private_data->magic if dead/closed */
+
+struct private_data {
+    int magic;                  /* Should be MAGIC or DEAD. */
+    int sock;                   /* Socket. */
+    int uses_tls;               /* TLS enabled on socket? */
+    gnutls_session_t session;   /* GnuTLS session (if uses_tls != 0). */
+    char *type;                 /* Cached return from remoteType. */
+    int counter;                /* Generates serial numbers for RPC. */
+};
+
+#define GET_PRIVATE(conn,retcode)                                       \
+    struct private_data *priv = (struct private_data *) (conn)->privateData; \
+    assert (priv);                                                      \
+    if (priv->magic == DEAD) {                                          \
+        error (conn, VIR_ERR_INVALID_ARG,                               \
+               "tried to use a closed or uninitialised handle");        \
+        return (retcode);                                               \
+    }                                                                   \
+    assert (priv->magic == MAGIC)
+
+static int call (virConnectPtr conn, struct private_data *priv, int in_open, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret);
+static void error (virConnectPtr conn, virErrorNumber code, const char *info);
+static void server_error (virConnectPtr conn, remote_error *err);
+static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain);
+static virNetworkPtr get_nonnull_network (virConnectPtr conn, remote_nonnull_network network);
+static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src);
+static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src);
+
+/*----------------------------------------------------------------------*/
+
+/* Helper functions for remoteOpen. */
+static char *get_transport_from_scheme (char *scheme);
+
+/* Parse query string. */
+struct query_fields {
+    struct query_fields *next; /* Linked list chain. */
+    char *name;                        /* Field name (unescaped). */
+    char *value;               /* Field value (unescaped). */
+    int ignore;                        /* Ignore field in query_create. */
+};
+
+static int query_parse (const char *query,
+                        const char *separator,
+                        struct query_fields **fields_out);
+static int query_create (const struct query_fields *fields,
+                         const char *separator,
+                         char **query_out);
+static void query_free (struct query_fields *fields);
+
+/* GnuTLS functions used by remoteOpen. */
+static int initialise_gnutls (virConnectPtr conn);
+static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, int sock, int no_verify, const char *hostname);
+
+/* Supported transports. */
+enum transport {
+    trans_tls,
+    trans_unix,
+    trans_ssh,
+    trans_ext,
+    trans_tcp,
+};
+
+static int
+remoteOpen (virConnectPtr conn, const char *uri_str, int flags)
+{
+    if (!uri_str) return VIR_DRV_OPEN_DECLINED;
+
+    /* We have to parse the URL every time to discover whether
+     * it contains a transport or remote server name.  There's no
+     * way to get around this.
+     */
+    xmlURIPtr uri = xmlParseURI (uri_str);
+    if (!uri || !uri->scheme)
+        return VIR_DRV_OPEN_DECLINED; /* Decline - not a URL. */
+
+    char *transport_str = get_transport_from_scheme (uri->scheme);
+    if (!uri->server && !transport_str)
+        return VIR_DRV_OPEN_DECLINED; /* Decline - not a remote URL. */
+
+    /* What transport? */
+    enum transport transport;
+    if (!transport_str || strcasecmp (transport_str, "tls") == 0)
+        transport = trans_tls;
+    else if (strcasecmp (transport_str, "unix") == 0)
+        transport = trans_unix;
+    else if (strcasecmp (transport_str, "ssh") == 0)
+        transport = trans_ssh;
+    else if (strcasecmp (transport_str, "ext") == 0)
+        transport = trans_ext;
+    else if (strcasecmp (transport_str, "tcp") == 0)
+        transport = trans_tcp;
+    else {
+        error (NULL, VIR_ERR_INVALID_ARG,
+               "remote_open: transport in URL not recognised "
+               "(should be tls|unix|ssh|ext|tcp)");
+        return VIR_DRV_OPEN_ERROR;
+    }
+
+    /* Return code from this function, and the private data. */
+    int retcode = VIR_DRV_OPEN_ERROR;
+    struct private_data priv = { .magic = DEAD, .sock = -1 };
+    char *name = 0, *command = 0, *sockname = 0, *netcat = 0, *username = 0;
+    char *server, *port;
+    int no_verify = 0;
+    char **cmd_argv = 0;
+
+    /* Remote server defaults to "localhost" if not specified. */
+    server = strdup (uri->server ? uri->server : "localhost");
+    if (!server) {
+    out_of_memory:
+        error (NULL, VIR_ERR_NO_MEMORY, "remote_open");
+        goto failed;
+    }
+    if (uri->port != 0) {
+        if (asprintf (&port, "%d", uri->port) == -1) goto out_of_memory;
+    } else if (transport == trans_tls) {
+        port = strdup (LIBVIRTD_TLS_PORT);
+        if (!port) goto out_of_memory;
+    } else if (transport == trans_tcp) {
+        port = strdup (LIBVIRTD_TCP_PORT);
+        if (!port) goto out_of_memory;
+    } else if (transport == trans_ssh) {
+        port = strdup ("22");
+        if (!port) goto out_of_memory;
+        if (uri->user) {
+            username = strdup (uri->user);
+            if (!username) goto out_of_memory;
+        }
+    } else
+        port = NULL;           /* Port not used for unix, ext. */
+
+    /* Get the variables from the query string.
+     * Then we need to reconstruct the query string (because
+     * feasibly it might contain variables needed by the real driver,
+     * although that won't be the case for now).
+     */
+    struct query_fields *vars, *var;
+    char *query;
+#if HAVE_XMLURI_QUERY_RAW
+    query = uri->query_raw;
+#else
+    query = uri->query;
+#endif
+    if (query_parse (query, NULL, &vars) != 0) goto failed;
+
+    for (var = vars; var; var = var->next) {
+        if (strcasecmp (var->name, "name") == 0) {
+            name = strdup (var->value);
+            if (!name) goto out_of_memory;
+            var->ignore = 1;
+        } else if (strcasecmp (var->name, "command") == 0) {
+            command = strdup (var->value);
+            if (!command) goto out_of_memory;
+            var->ignore = 1;
+        } else if (strcasecmp (var->name, "socket") == 0) {
+            sockname = strdup (var->value);
+            if (!sockname) goto out_of_memory;
+            var->ignore = 1;
+        } else if (strcasecmp (var->name, "netcat") == 0) {
+            netcat = strdup (var->value);
+            if (!netcat) goto out_of_memory;
+            var->ignore = 1;
+        } else if (strcasecmp (var->name, "no_verify") == 0) {
+            no_verify = atoi (var->value);
+            var->ignore = 1;
+        }
+#if DEBUG
+        else
+            fprintf (stderr,
+                     "remoteOpen: "
+                     "passing through variable '%s' to remote end\n",
+                     var->name);
+#endif
+    }
+
+#if HAVE_XMLURI_QUERY_RAW
+    if (uri->query_raw) xmlFree (uri->query_raw);
+#else
+    if (uri->query) xmlFree (uri->query);
+#endif
+
+    if (query_create (vars, NULL,
+#if HAVE_XMLURI_QUERY_RAW
+                      &uri->query_raw
+#else
+                      &uri->query
+#endif
+                      ) != 0) goto failed;
+    query_free (vars);
+
+    /* For ext transport, command is required. */
+    if (transport == trans_ext && !command) {
+        error (NULL, VIR_ERR_INVALID_ARG, "remote_open: for 'ext' transport, command is required");
+        goto failed;
+    }
+
+    /* Construct the original name. */
+    if (!name) {
+        /* Remove the transport (if any) from the scheme. */
+        if (transport_str) {
+            assert (transport_str[-1] == '+');
+            transport_str[-1] = '\0';
+        }
+        /* Remove the username, server name and port number. */
+        if (uri->user) xmlFree (uri->user);
+        uri->user = 0;
+
+        if (uri->server) xmlFree (uri->server);
+        uri->server = 0;
+
+        uri->port = 0;
+
+        name = (char *) xmlSaveUri (uri);
+    }
+
+    assert (name);
+#if DEBUG
+    fprintf (stderr, "remoteOpen: proceeding with name = %s\n", name);
+#endif
+
+    /* Connect to the remote service. */
+    switch (transport) {
+    case trans_tls:
+        if (initialise_gnutls (conn) == -1) goto failed;
+        priv.uses_tls = 1;
+
+        /*FALLTHROUGH*/
+    case trans_tcp: {
+        // http://people.redhat.com/drepper/userapi-ipv6.html
+        struct addrinfo *res, *r;
+        struct addrinfo hints;
+        memset (&hints, 0, sizeof hints);
+        hints.ai_socktype = SOCK_STREAM;
+        hints.ai_flags = AI_ADDRCONFIG;
+        int e = getaddrinfo (server, port, &hints, &res);
+        if (e != 0) {
+            error (NULL, VIR_ERR_INVALID_ARG, gai_strerror (e));
+            goto failed;
+        }
+
+        /* Try to connect to each returned address in turn. */
+        /* XXX This loop contains a subtle problem.  In the case
+         * where a host is accessible over IPv4 and IPv6, it will
+         * try the IPv4 and IPv6 addresses in turn.  However it
+         * should be able to present different client certificates
+         * (because the commonName field in a client cert contains
+         * the client IP address, which is different for IPv4 and
+         * IPv6).  At the moment we only have a single client
+         * certificate, and no way to specify what address family
+         * that certificate belongs to.
+         */
+        for (r = res; r; r = r->ai_next) {
+            int no_slow_start = 1;
+
+            priv.sock = socket (r->ai_family, SOCK_STREAM, 0);
+            if (priv.sock == -1) {
+                error (NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+                continue;
+            }
+
+            /* Disable Nagle - Dan Berrange. */
+            setsockopt (priv.sock,
+                        IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start,
+                        sizeof no_slow_start);
+
+            if (connect (priv.sock, r->ai_addr, r->ai_addrlen) == -1) {
+                error (NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+                close (priv.sock);
+                continue;
+            }
+
+            if (priv.uses_tls) {
+                priv.session =
+                    negotiate_gnutls_on_connection
+                      (conn, priv.sock, no_verify, server);
+                if (!priv.session) {
+                    close (priv.sock);
+                    priv.sock = -1;
+                    continue;
+                }
+            }
+            goto tcp_connected;
+        }
+
+        freeaddrinfo (res);
+        goto failed;
+
+       tcp_connected:
+        freeaddrinfo (res);
+
+        // NB. All versioning is done by the RPC headers, so we don't
+        // need to worry (at this point anyway) about versioning.
+        break;
+    }
+
+    case trans_unix: {
+        if (!sockname) {
+            if (flags & VIR_CONNECT_RO)
+                sockname = strdup (LIBVIRTD_UNIX_SOCKET_RO);
+            else
+                sockname = strdup (LIBVIRTD_UNIX_SOCKET);
+        }
+
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX(addr) (sizeof (addr).sun_path)
+#endif
+        struct sockaddr_un addr;
+        memset (&addr, 0, sizeof addr);
+        addr.sun_family = AF_UNIX;
+        strncpy (addr.sun_path, sockname, UNIX_PATH_MAX (addr));
+
+        priv.sock = socket (AF_UNIX, SOCK_STREAM, 0);
+        if (priv.sock == -1) {
+            error (NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+            goto failed;
+        }
+        if (connect (priv.sock, (struct sockaddr *) &addr, sizeof addr) == -1){
+            error (NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+            goto failed;
+        }
+
+        break;
+    }
+
+    case trans_ssh: {
+        int j, nr_args = username ? 10 : 8;
+
+        command = command ? : strdup ("ssh");
+
+        // Generate the final command argv[] array.
+        //   ssh -p $port [-l $username] $hostname $netcat -U $sockname [NULL]
+        cmd_argv = malloc (nr_args * sizeof (char *));
+        j = 0;
+        cmd_argv[j++] = strdup (command);
+        cmd_argv[j++] = strdup ("-p");
+        cmd_argv[j++] = strdup (port);
+        if (username) {
+            cmd_argv[j++] = strdup ("-l");
+            cmd_argv[j++] = strdup (username);
+        }
+        cmd_argv[j++] = strdup (server);
+        cmd_argv[j++] = strdup (netcat ? netcat : "nc");
+        cmd_argv[j++] = strdup ("-U");
+        cmd_argv[j++] = strdup (sockname ? sockname : LIBVIRTD_UNIX_SOCKET);
+        cmd_argv[j++] = 0;
+        assert (j == nr_args);
+    }
+
+        /*FALLTHROUGH*/
+    case trans_ext: {
+        int pid;
+        int sv[2];
+
+        /* Fork off the external process.  Use socketpair to create a private
+         * (unnamed) Unix domain socket to the child process so we don't have
+         * to faff around with two file descriptors (a la 'pipe(2)').
+         */
+        if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+            error (NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+            goto failed;
+        }
+
+        pid = fork ();
+        if (pid == -1) {
+            error (NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+            goto failed;
+        } else if (pid == 0) { /* Child. */
+            close (sv[0]);
+            // Connect socket (sv[1]) to stdin/stdout.
+            close (0);
+            if (dup (sv[1]) == -1) perror ("dup");
+            close (1);
+            if (dup (sv[1]) == -1) perror ("dup");
+            close (sv[1]);
+
+            // Run the external process.
+            if (!cmd_argv) {
+                cmd_argv = malloc (2 * sizeof (char *));
+                cmd_argv[0] = command;
+                cmd_argv[1] = 0;
+            }
+            execvp (command, cmd_argv);
+            perror (command);
+            _exit (1);
+        }
+
+        /* Parent continues here. */
+        close (sv[1]);
+        priv.sock = sv[0];
+    }
+    } /* switch (transport) */
+
+    /* Finally we can call the remote side's open function. */
+    remote_open_args args = { &name, flags };
+
+    if (call (conn, &priv, 1, REMOTE_PROC_OPEN,
+              (xdrproc_t) xdr_remote_open_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        goto failed;
+
+    /* Finally allocate private data. */
+    conn->privateData = malloc (sizeof priv);
+    if (!conn->privateData) {
+        error (NULL, VIR_ERR_NO_MEMORY, "malloc");
+        goto failed;
+    }
+    priv.magic = MAGIC;
+    memcpy (conn->privateData, &priv, sizeof priv);
+
+    /* Successful. */
+    retcode = VIR_DRV_OPEN_SUCCESS;
+
+    /*FALLTHROUGH*/
+ failed:
+    /* Close the socket if we failed. */
+    if (retcode != VIR_DRV_OPEN_SUCCESS && priv.sock >= 0) {
+        if (priv.uses_tls && priv.session)
+            gnutls_bye (priv.session, GNUTLS_SHUT_RDWR);
+        close (priv.sock);
+    }
+
+    /* Free up the URL and strings. */
+    xmlFreeURI (uri);
+    if (name) free (name);
+    if (command) free (command);
+    if (sockname) free (sockname);
+    if (netcat) free (netcat);
+    if (username) free (username);
+    if (server) free (server);
+    if (port) free (port);
+    if (cmd_argv) {
+        char **cmd_argv_ptr = cmd_argv;
+        while (*cmd_argv_ptr) {
+            free (*cmd_argv_ptr);
+            cmd_argv_ptr++;
+        }
+        free (cmd_argv);
+    }
+
+    return retcode;
+}
+
+/* In a string "driver+transport" return a pointer to "transport". */
+static char *
+get_transport_from_scheme (char *scheme)
+{
+    char *p = strchr (scheme, '+');
+    return p ? p+1 : 0;
+}
+
+static int
+query_create (const struct query_fields *fields,
+              const char *separator,
+              char **query_out)
+{
+    /* List of characters which are safe inside names or values,
+     * apart from '@', IS_MARK and IS_ALPHANUM.  Best to escape
+     * as much as possible.  Certainly '=', '&' and '#' must NEVER
+     * be added to this list.
+     */
+    static const xmlChar *special_chars = BAD_CAST "";
+
+    int append_sep = 0, sep_len;
+    xmlBufferPtr buf;
+    xmlChar *str;
+    int rv;
+
+    if (query_out) *query_out = NULL;
+    if (!fields) return 0;
+
+    if (separator == NULL) {
+       separator = "&";
+       sep_len = 1;
+    } else
+       sep_len = xmlStrlen (BAD_CAST separator);
+
+    buf = xmlBufferCreate ();
+    if (!buf) return -1;
+
+    rv = 0;
+    while (fields) {
+       if (!fields->ignore) {
+           if (append_sep) {
+               rv = xmlBufferAdd (buf, BAD_CAST separator, sep_len);
+               if (rv != 0) goto error;
+           }
+           append_sep = 1;
+
+           str = xmlURIEscapeStr (BAD_CAST fields->name, special_chars);
+           if (!str) { rv = XML_ERR_NO_MEMORY; goto error; }
+           rv = xmlBufferAdd (buf, str, xmlStrlen (str));
+           xmlFree (str);
+           if (rv != 0) goto error;
+
+           rv = xmlBufferAdd (buf, BAD_CAST "=", 1);
+           if (rv != 0) goto error;
+           str = xmlURIEscapeStr (BAD_CAST fields->value, special_chars);
+           if (!str) { rv = XML_ERR_NO_MEMORY; goto error; }
+           rv = xmlBufferAdd (buf, str, xmlStrlen (str));
+           xmlFree (str);
+           if (rv != 0) goto error;
+       }
+
+       fields = fields->next;
+    }
+
+    if (query_out && buf->content) {
+       *query_out = (char *) xmlStrdup (buf->content);
+       if (!*query_out) {
+           rv = XML_ERR_NO_MEMORY;
+           goto error;
+       }
+    }
+
+ error:
+    if (buf)
+       xmlBufferFree (buf);
+    return rv;
+}
+
+static int
+query_parse (const char *query_,
+             const char *separator,
+             struct query_fields **fields_out)
+{
+    struct query_fields *fields, *field, **prev;
+    int sep_len;
+    const xmlChar *query = BAD_CAST query_, *end, *eq;
+    char *name, *value;
+
+    if (fields_out) *fields_out = NULL;
+    if (!query || query[0] == '\0') return 0;
+
+    if (separator == NULL) {
+       separator = "&";
+       sep_len = 1;
+    } else
+       sep_len = xmlStrlen (BAD_CAST separator);
+
+    fields = NULL;
+    prev = &fields;
+
+    while (*query) {
+       /* Find the next separator, or end of the string. */
+       end = xmlStrstr (query, BAD_CAST separator);
+       if (!end) end = query + xmlStrlen (query);
+
+       /* Find the first '=' character between here and end. */
+       eq = xmlStrchr (query, '=');
+       if (eq && eq >= end) eq = NULL;
+
+       /* Empty section (eg. "?&"). */
+       if (end == query)
+           goto next;
+       /* If there is no '=' character, then we have just "name"
+        * and consistent with CGI.pm we assume value is "".
+        */
+       else if (!eq) {
+           name = xmlURIUnescapeString ((const char *) query,
+                                        end - query, NULL);
+           value = (char *) xmlStrdup (BAD_CAST "");
+           if (!name || !value) goto out_of_memory;
+       }
+       /* Or if we have "name=" here (works around annoying
+        * problem when calling xmlURIUnescapeString with len = 0).
+        */
+       else if (eq+1 == end) {
+           name = xmlURIUnescapeString ((const char *) query,
+                                        eq - query, NULL);
+           value = (char *) xmlStrdup (BAD_CAST "");
+           if (!name || !value) goto out_of_memory;
+       }
+       /* If the '=' character is at the beginning then we have
+        * "=value" and consistent with CGI.pm we _ignore_ this.
+        */
+       else if (query == eq)
+           goto next;
+       /* Otherwise it's "name=value". */
+       else {
+           name = xmlURIUnescapeString ((const char *) query,
+                                        eq - query, NULL);
+           value = xmlURIUnescapeString ((const char *) eq+1,
+                                         end - (eq+1), NULL);
+           if (!name || !value) goto out_of_memory;
+       }
+
+       /* Allocate this field and append to the list. */
+       field = xmlMalloc (sizeof *field);
+       if (!field) goto out_of_memory;
+       field->next = NULL;
+       field->name = name;
+       field->value = value;
+       field->ignore = 0;
+       *prev = field;
+       prev = &field->next;
+
+    next:
+       query = end;
+       if (*query) query += sep_len; /* skip separator */
+    }
+
+    if (fields_out) *fields_out = fields;
+    return 0;
+
+ out_of_memory:
+    query_free (fields);
+    return XML_ERR_NO_MEMORY;
+}
+
+static void
+query_free (struct query_fields *fields)
+{
+    struct query_fields *t;
+
+    while (fields) {
+        if (fields->name) xmlFree (fields->name);
+        if (fields->value) xmlFree (fields->value);
+        t = fields;
+        fields = fields->next;
+        xmlFree (t);
+    }
+}
+
+/* GnuTLS functions used by remoteOpen. */
+static gnutls_certificate_credentials_t x509_cred;
+
+static int
+initialise_gnutls (virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+    static int initialised = 0;
+    int err;
+
+    if (initialised) return 0;
+
+    gnutls_global_init ();
+
+    /* X509 stuff */
+    err = gnutls_certificate_allocate_credentials (&x509_cred);
+    if (err) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return -1;
+    }
+
+    /* Set the trusted CA cert. */
+#if DEBUG
+    fprintf (stderr, "loading CA file %s\n", LIBVIRT_CACERT);
+#endif
+    err =
+        gnutls_certificate_set_x509_trust_file (x509_cred, LIBVIRT_CACERT,
+                                                GNUTLS_X509_FMT_PEM);
+    if (err < 0) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return -1;
+    }
+
+    /* Set the client certificate and private key. */
+#if DEBUG
+    fprintf (stderr, "loading client cert and key from files %s and %s\n",
+             LIBVIRT_CLIENTCERT, LIBVIRT_CLIENTKEY);
+#endif
+    err =
+        gnutls_certificate_set_x509_key_file (x509_cred,
+                                              LIBVIRT_CLIENTCERT,
+                                              LIBVIRT_CLIENTKEY,
+                                              GNUTLS_X509_FMT_PEM);
+    if (err < 0) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return -1;
+    }
+
+    initialised = 1;
+    return 0;
+}
+
+static int verify_certificate (virConnectPtr conn, gnutls_session_t session, const char *hostname);
+
+static gnutls_session_t
+negotiate_gnutls_on_connection (virConnectPtr conn,
+                                int sock, int no_verify, const char *hostname)
+{
+    const int cert_type_priority[3] = {
+        GNUTLS_CRT_X509,
+        GNUTLS_CRT_OPENPGP,
+        0
+    };
+    int err;
+    gnutls_session_t session;
+
+    /* Initialize TLS session 
+     */
+    err = gnutls_init (&session, GNUTLS_CLIENT);
+    if (err) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return NULL;
+    }
+
+    /* Use default priorities */
+    err = gnutls_set_default_priority (session);
+    if (err) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return NULL;
+    }
+    err =
+        gnutls_certificate_type_set_priority (session,
+                                              cert_type_priority);
+    if (err) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return NULL;
+    }
+
+    /* put the x509 credentials to the current session
+     */
+    err = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+    if (err) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return NULL;
+    }
+
+    gnutls_transport_set_ptr (session,
+                              (gnutls_transport_ptr_t) (long) sock);
+
+    /* Perform the TLS handshake. */
+ again:
+    err = gnutls_handshake (session);
+    if (err < 0) {
+        if (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED)
+            goto again;
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+        return NULL;
+    }
+
+    /* Verify certificate. */
+    if (verify_certificate (conn, session, hostname) == -1) {
+            fprintf (stderr,
+                     "remote_internal: failed to verify peer's certificate\n");
+            if (!no_verify) return NULL;
+        }
+
+    /* At this point, the server is verifying _our_ certificate, IP address,
+     * etc.  If we make the grade, it will send us a '\1' byte.
+     */
+    char buf[1];
+    int len;
+ again_2:
+    len = gnutls_record_recv (session, buf, 1);
+    if (len < 0 && len != GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
+        if (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED)
+            goto again_2;
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (len));
+        return NULL;
+    }
+    if (len != 1 || buf[0] != '\1') {
+        error (NULL, VIR_ERR_RPC,
+               "server verification (of our certificate or IP address) failed\n");
+        return NULL;
+    }
+
+#if 0
+    /* Print session info. */
+    print_info (session);
+#endif
+
+    return session;
+}
+
+static int
+verify_certificate (virConnectPtr conn ATTRIBUTE_UNUSED,
+                    gnutls_session_t session,
+                    const char *hostname)
+{
+    int ret;
+    unsigned int status;
+    const gnutls_datum_t *certs;
+    unsigned int nCerts, i;
+    time_t now;
+
+    if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0) {
+        error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (ret));
+        return -1;
+    }
+  
+    if ((now = time(NULL)) == ((time_t)-1)) {
+        error (NULL, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+        return -1;
+    }
+
+    if (status != 0) {
+        const char *reason = "Invalid certificate";
+
+        if (status & GNUTLS_CERT_INVALID)
+            reason = "The certificate is not trusted.";
+        
+        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+            reason = "The certificate hasn't got a known issuer.";
+    
+        if (status & GNUTLS_CERT_REVOKED)
+            reason = "The certificate has been revoked.";
+    
+        if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+            reason = "The certificate uses an insecure algorithm";
+    
+        error (NULL, VIR_ERR_RPC, reason);
+        return -1;
+    }
+
+    if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
+        error (NULL, VIR_ERR_RPC, "Certificate type is not X.509");
+        return -1;
+    }
+  
+    if (!(certs = gnutls_certificate_get_peers(session, &nCerts))) {
+        error (NULL, VIR_ERR_RPC, "gnutls_certificate_get_peers failed");
+        return -1;
+    }
+  
+    for (i = 0 ; i < nCerts ; i++) {
+        gnutls_x509_crt_t cert;
+
+        ret = gnutls_x509_crt_init (&cert);
+        if (ret < 0) {
+            error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (ret));
+            return -1;
+        }
+        
+        ret = gnutls_x509_crt_import (cert, &certs[i], GNUTLS_X509_FMT_DER);
+        if (ret < 0) {
+            error (NULL, VIR_ERR_GNUTLS_ERROR, gnutls_strerror (ret));
+            gnutls_x509_crt_deinit (cert);
+            return -1;
+        }
+    
+        if (gnutls_x509_crt_get_expiration_time (cert) < now) {
+            error (NULL, VIR_ERR_RPC, "The certificate has expired");
+            gnutls_x509_crt_deinit (cert);
+            return -1;
+        }
+    
+        if (gnutls_x509_crt_get_activation_time (cert) > now) {
+            error (NULL, VIR_ERR_RPC, "The certificate is not yet activated");
+            gnutls_x509_crt_deinit (cert);
+            return -1;
+        }
+    
+        if (i == 0) {
+            if (!gnutls_x509_crt_check_hostname (cert, hostname)) {
+                __virRaiseError
+                    (NULL, NULL, NULL,
+                     VIR_FROM_REMOTE, VIR_ERR_RPC,
+                     VIR_ERR_ERROR, hostname, NULL, NULL,
+                     0, 0,
+                     "Certificate's owner does not match the hostname (%s)",
+                     hostname);
+                gnutls_x509_crt_deinit (cert);
+                return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int
+remoteClose (virConnectPtr conn)
+{
+    GET_PRIVATE (conn, -1);
+
+    if (call (conn, priv, 0, REMOTE_PROC_CLOSE,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    /* Close socket. */
+    if (priv->uses_tls && priv->session)
+        gnutls_bye (priv->session, GNUTLS_SHUT_RDWR);
+    close (priv->sock);
+
+    /* See comment for remoteType. */
+    if (priv->type) free (priv->type);
+
+    return 0;
+}
+
+/* Unfortunately this function is defined to return a static string.
+ * Since the remote end always answers with the same type (for a
+ * single connection anyway) we cache the type in the connection's
+ * private data, and free it when we close the connection.
+ *
+ * See also:
+ * http://www.redhat.com/archives/libvir-list/2007-February/msg00096.html
+ */
+static const char *
+remoteType (virConnectPtr conn)
+{
+    remote_get_type_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    /* Cached? */
+    if (priv->type) return priv->type;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_GET_TYPE,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_get_type_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Stash. */
+    return priv->type = ret.type;
+}
+
+static int
+remoteVersion (virConnectPtr conn, unsigned long *hvVer)
+{
+    remote_get_version_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_GET_VERSION,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_get_version_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (hvVer) *hvVer = ret.hv_ver;
+    return 0;
+}
+
+static int
+remoteGetMaxVcpus (virConnectPtr conn, const char *type)
+{
+    remote_get_max_vcpus_args args;
+    remote_get_max_vcpus_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    args.type = (char **) &type;
+    if (call (conn, priv, 0, REMOTE_PROC_GET_MAX_VCPUS,
+              (xdrproc_t) xdr_remote_get_max_vcpus_args, (char *) &args,
+              (xdrproc_t) xdr_remote_get_max_vcpus_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.max_vcpus;
+}
+
+static int
+remoteNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info)
+{
+    remote_node_get_info_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_INFO,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_node_get_info_ret, (char *) &ret) == -1)
+        return -1;
+
+    strncpy (info->model, ret.model, 32);
+    info->model[31] = '\0';
+    info->memory = ret.memory;
+    info->cpus = ret.cpus;
+    info->mhz = ret.mhz;
+    info->nodes = ret.nodes;
+    info->sockets = ret.sockets;
+    info->cores = ret.cores;
+    info->threads = ret.threads;
+    return 0;
+}
+
+static char *
+remoteGetCapabilities (virConnectPtr conn)
+{
+    remote_get_capabilities_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_GET_CAPABILITIES,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_get_capabilities_ret, (char *)&ret) == -1)
+        return NULL;
+
+    /* Caller frees this. */
+    return ret.capabilities;
+}
+
+static int
+remoteListDomains (virConnectPtr conn, int *ids, int maxids)
+{
+    int i;
+    remote_list_domains_args args;
+    remote_list_domains_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    if (maxids > REMOTE_DOMAIN_ID_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, "maxids > REMOTE_DOMAIN_ID_LIST_MAX");
+        return -1;
+    }
+    args.maxids = maxids;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_LIST_DOMAINS,
+              (xdrproc_t) xdr_remote_list_domains_args, (char *) &args,
+              (xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.ids.ids_len > maxids) {
+        error (conn, VIR_ERR_RPC, "ret.ids.ids_len > maxids");
+        xdr_free ((xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret);
+        return -1;
+    }
+
+    for (i = 0; i < ret.ids.ids_len; ++i)
+        ids[i] = ret.ids.ids_val[i];
+
+    xdr_free ((xdrproc_t) xdr_remote_list_domains_ret, (char *) &ret);
+
+    return ret.ids.ids_len;
+}
+
+static int
+remoteNumOfDomains (virConnectPtr conn)
+{
+    remote_num_of_domains_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DOMAINS,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_num_of_domains_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static virDomainPtr
+remoteDomainCreateLinux (virConnectPtr conn,
+                         const char *xmlDesc,
+                         unsigned int flags)
+{
+    virDomainPtr dom;
+    remote_domain_create_linux_args args;
+    remote_domain_create_linux_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    args.xml_desc = (char *) xmlDesc;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_CREATE_LINUX,
+              (xdrproc_t) xdr_remote_domain_create_linux_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_create_linux_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dom = get_nonnull_domain (conn, ret.dom);
+    if (dom == NULL)
+        error (conn, VIR_ERR_RPC, "remoteDomainCreateLinux: domain not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_domain_create_linux_ret, (char *) &ret);
+
+    return dom;
+}
+
+static virDomainPtr
+remoteDomainLookupByID (virConnectPtr conn, int id)
+{
+    virDomainPtr dom;
+    remote_domain_lookup_by_id_args args;
+    remote_domain_lookup_by_id_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    args.id = id;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_ID,
+              (xdrproc_t) xdr_remote_domain_lookup_by_id_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_lookup_by_id_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dom = get_nonnull_domain (conn, ret.dom);
+    if (dom == NULL)
+        error (conn, VIR_ERR_RPC, "remoteDomainLookupByID: domain not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_id_ret, (char *) &ret);
+
+    return dom;
+}
+
+static virDomainPtr
+remoteDomainLookupByUUID (virConnectPtr conn, const unsigned char *uuid)
+{
+    virDomainPtr dom;
+    remote_domain_lookup_by_uuid_args args;
+    remote_domain_lookup_by_uuid_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    memcpy (args.uuid, uuid, VIR_UUID_BUFLEN);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_UUID,
+              (xdrproc_t) xdr_remote_domain_lookup_by_uuid_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_lookup_by_uuid_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dom = get_nonnull_domain (conn, ret.dom);
+    if (dom == NULL)
+        error (conn, VIR_ERR_RPC, "remoteDomainLookupByUUID: domain not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_uuid_ret, (char *) &ret);
+    return dom;
+}
+
+static virDomainPtr
+remoteDomainLookupByName (virConnectPtr conn, const char *name)
+{
+    virDomainPtr dom;
+    remote_domain_lookup_by_name_args args;
+    remote_domain_lookup_by_name_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    args.name = (char *) name;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_LOOKUP_BY_NAME,
+              (xdrproc_t) xdr_remote_domain_lookup_by_name_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_lookup_by_name_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dom = get_nonnull_domain (conn, ret.dom);
+    if (dom == NULL)
+        error (conn, VIR_ERR_RPC, "remoteDomainLookupByName: domain not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_domain_lookup_by_name_ret, (char *) &ret);
+
+    return dom;
+}
+
+static int
+remoteDomainSuspend (virDomainPtr domain)
+{
+    remote_domain_suspend_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SUSPEND,
+              (xdrproc_t) xdr_remote_domain_suspend_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainResume (virDomainPtr domain)
+{
+    remote_domain_resume_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_RESUME,
+              (xdrproc_t) xdr_remote_domain_resume_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainShutdown (virDomainPtr domain)
+{
+    remote_domain_shutdown_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SHUTDOWN,
+              (xdrproc_t) xdr_remote_domain_shutdown_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainReboot (virDomainPtr domain, unsigned int flags)
+{
+    remote_domain_reboot_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.flags = flags;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_REBOOT,
+              (xdrproc_t) xdr_remote_domain_reboot_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainDestroy (virDomainPtr domain)
+{
+    remote_domain_destroy_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DESTROY,
+              (xdrproc_t) xdr_remote_domain_destroy_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static char *
+remoteDomainGetOSType (virDomainPtr domain)
+{
+    remote_domain_get_os_type_args args;
+    remote_domain_get_os_type_ret ret;
+    GET_PRIVATE (domain->conn, NULL);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_OS_TYPE,
+              (xdrproc_t) xdr_remote_domain_get_os_type_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_get_os_type_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Caller frees. */
+    return ret.type;
+}
+
+static unsigned long
+remoteDomainGetMaxMemory (virDomainPtr domain)
+{
+    remote_domain_get_max_memory_args args;
+    remote_domain_get_max_memory_ret ret;
+    GET_PRIVATE (domain->conn, 0);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_MAX_MEMORY,
+              (xdrproc_t) xdr_remote_domain_get_max_memory_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_get_max_memory_ret, (char *) &ret) == -1)
+        return 0;
+
+    return ret.memory;
+}
+
+static int
+remoteDomainSetMaxMemory (virDomainPtr domain, unsigned long memory)
+{
+    remote_domain_set_max_memory_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.memory = memory;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_MAX_MEMORY,
+              (xdrproc_t) xdr_remote_domain_set_max_memory_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainSetMemory (virDomainPtr domain, unsigned long memory)
+{
+    remote_domain_set_memory_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.memory = memory;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_MEMORY,
+              (xdrproc_t) xdr_remote_domain_set_memory_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainGetInfo (virDomainPtr domain, virDomainInfoPtr info)
+{
+    remote_domain_get_info_args args;
+    remote_domain_get_info_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_INFO,
+              (xdrproc_t) xdr_remote_domain_get_info_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_get_info_ret, (char *) &ret) == -1)
+        return -1;
+
+    info->state = ret.state;
+    info->maxMem = ret.max_mem;
+    info->memory = ret.memory;
+    info->nrVirtCpu = ret.nr_virt_cpu;
+    info->cpuTime = ret.cpu_time;
+    return 0;
+}
+
+static int
+remoteDomainSave (virDomainPtr domain, const char *to)
+{
+    remote_domain_save_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.to = (char *) to;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SAVE,
+              (xdrproc_t) xdr_remote_domain_save_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainRestore (virConnectPtr conn, const char *from)
+{
+    remote_domain_restore_args args;
+    GET_PRIVATE (conn, -1);
+
+    args.from = (char *) from;
+
+    if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_RESTORE,
+              (xdrproc_t) xdr_remote_domain_restore_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainCoreDump (virDomainPtr domain, const char *to, int flags)
+{
+    remote_domain_core_dump_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.to = (char *) to;
+    args.flags = flags;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_CORE_DUMP,
+              (xdrproc_t) xdr_remote_domain_core_dump_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainSetVcpus (virDomainPtr domain, unsigned int nvcpus)
+{
+    remote_domain_set_vcpus_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.nvcpus = nvcpus;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_VCPUS,
+              (xdrproc_t) xdr_remote_domain_set_vcpus_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainPinVcpu (virDomainPtr domain,
+                     unsigned int vcpu,
+                     unsigned char *cpumap,
+                     int maplen)
+{
+    remote_domain_pin_vcpu_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    if (maplen > REMOTE_CPUMAP_MAX) {
+        error (domain->conn, VIR_ERR_RPC, "maplen > REMOTE_CPUMAP_MAX");
+        return -1;
+    }
+
+    make_nonnull_domain (&args.dom, domain);
+    args.vcpu = vcpu;
+    args.cpumap.cpumap_len = maplen;
+    args.cpumap.cpumap_val = (char *) cpumap;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_PIN_VCPU,
+              (xdrproc_t) xdr_remote_domain_pin_vcpu_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainGetVcpus (virDomainPtr domain,
+                      virVcpuInfoPtr info,
+                      int maxinfo,
+                      unsigned char *cpumaps,
+                      int maplen)
+{
+    int i;
+    remote_domain_get_vcpus_args args;
+    remote_domain_get_vcpus_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    if (maxinfo > REMOTE_VCPUINFO_MAX) {
+        error (domain->conn, VIR_ERR_RPC, "maxinfo > REMOTE_VCPUINFO_MAX");
+        return -1;
+    }
+    if (maplen > REMOTE_CPUMAPS_MAX) {
+        error (domain->conn, VIR_ERR_RPC, "maplen > REMOTE_CPUMAPS_MAX");
+        return -1;
+    }
+
+    make_nonnull_domain (&args.dom, domain);
+    args.maxinfo = maxinfo;
+    args.maplen = maplen;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_VCPUS,
+              (xdrproc_t) xdr_remote_domain_get_vcpus_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.info.info_len > maxinfo) {
+        error (domain->conn, VIR_ERR_RPC, "ret.info.info_len > maxinfo");
+        xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret);
+        return -1;
+    }
+    if (ret.cpumaps.cpumaps_len > maplen) {
+        error (domain->conn, VIR_ERR_RPC, "ret.cpumaps.cpumaps_len > maplen");
+        xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret);
+        return -1;
+    }
+
+    for (i = 0; i < ret.info.info_len; ++i) {
+        info[i].number = ret.info.info_val[i].number;
+        info[i].state = ret.info.info_val[i].state;
+        info[i].cpuTime = ret.info.info_val[i].cpu_time;
+        info[i].cpu = ret.info.info_val[i].cpu;
+    }
+
+    for (i = 0; i < ret.cpumaps.cpumaps_len; ++i)
+        cpumaps[i] = ret.cpumaps.cpumaps_val[i];
+
+    xdr_free ((xdrproc_t) xdr_remote_domain_get_vcpus_ret, (char *) &ret);
+    return ret.info.info_len;
+}
+
+static int
+remoteDomainGetMaxVcpus (virDomainPtr domain)
+{
+    remote_domain_get_max_vcpus_args args;
+    remote_domain_get_max_vcpus_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_GET_MAX_VCPUS,
+              (xdrproc_t) xdr_remote_domain_get_max_vcpus_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_get_max_vcpus_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static char *
+remoteDomainDumpXML (virDomainPtr domain, int flags)
+{
+    remote_domain_dump_xml_args args;
+    remote_domain_dump_xml_ret ret;
+    GET_PRIVATE (domain->conn, NULL);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DUMP_XML,
+              (xdrproc_t) xdr_remote_domain_dump_xml_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_dump_xml_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Caller frees. */
+    return ret.xml;
+}
+
+static int
+remoteListDefinedDomains (virConnectPtr conn, char **const names, int maxnames)
+{
+    int i;
+    remote_list_defined_domains_args args;
+    remote_list_defined_domains_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_DOMAIN_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, "maxnames > REMOTE_DOMAIN_NAME_LIST_MAX");
+        return -1;
+    }
+    args.maxnames = maxnames;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_DOMAINS,
+              (xdrproc_t) xdr_remote_list_defined_domains_args, (char *) &args,
+              (xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, "ret.names.names_len > maxnames");
+        xdr_free ((xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_list_defined_domains_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+static int
+remoteNumOfDefinedDomains (virConnectPtr conn)
+{
+    remote_num_of_defined_domains_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_DOMAINS,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_num_of_defined_domains_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static int
+remoteDomainCreate (virDomainPtr domain)
+{
+    remote_domain_create_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_CREATE,
+              (xdrproc_t) xdr_remote_domain_create_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static virDomainPtr
+remoteDomainDefineXML (virConnectPtr conn, const char *xml)
+{
+    virDomainPtr dom;
+    remote_domain_define_xml_args args;
+    remote_domain_define_xml_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    args.xml = (char *) xml;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_DEFINE_XML,
+              (xdrproc_t) xdr_remote_domain_define_xml_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_define_xml_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dom = get_nonnull_domain (conn, ret.dom);
+    if (dom == NULL)
+        error (conn, VIR_ERR_RPC, "remoteDomainDefineXML: domain not found");
+
+    xdr_free ((xdrproc_t) xdr_remote_domain_define_xml_ret, (char *) &ret);
+
+    return dom;
+}
+
+static int
+remoteDomainUndefine (virDomainPtr domain)
+{
+    remote_domain_undefine_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_UNDEFINE,
+              (xdrproc_t) xdr_remote_domain_undefine_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainAttachDevice (virDomainPtr domain, char *xml)
+{
+    remote_domain_attach_device_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.xml = xml;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_ATTACH_DEVICE,
+              (xdrproc_t) xdr_remote_domain_attach_device_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainDetachDevice (virDomainPtr domain, char *xml)
+{
+    remote_domain_detach_device_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.xml = xml;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_DETACH_DEVICE,
+              (xdrproc_t) xdr_remote_domain_detach_device_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteDomainGetAutostart (virDomainPtr domain, int *autostart)
+{
+    remote_domain_get_autostart_args args;
+    remote_domain_get_autostart_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_AUTOSTART,
+              (xdrproc_t) xdr_remote_domain_get_autostart_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_get_autostart_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (autostart) *autostart = ret.autostart;
+    return 0;
+}
+
+static int
+remoteDomainSetAutostart (virDomainPtr domain, int autostart)
+{
+    remote_domain_set_autostart_args args;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.autostart = autostart;
+
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SET_AUTOSTART,
+              (xdrproc_t) xdr_remote_domain_set_autostart_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int
+remoteNetworkOpen (virConnectPtr conn,
+                   const char *uri_str ATTRIBUTE_UNUSED,
+                   int flags ATTRIBUTE_UNUSED)
+{
+    /* If the main connection is a remote, then just catch the
+     * network open too.  Nothing is forwarded because the
+     * main remoteOpen call above will have already opened
+     * network on the remote side.
+     */
+    if (conn &&
+        conn->driver &&
+        strcmp (conn->driver->name, "remote") == 0)
+        return VIR_DRV_OPEN_SUCCESS;
+    else
+        return VIR_DRV_OPEN_DECLINED;
+}
+
+static int
+remoteNetworkClose (virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+    /* No need to pass this to the remote side, because
+     * libvirt.c will soon call remoteClose.
+     */
+    return 0;
+}
+
+static int
+remoteNumOfNetworks (virConnectPtr conn)
+{
+    remote_num_of_networks_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_NETWORKS,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_num_of_networks_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static int
+remoteListNetworks (virConnectPtr conn, char **const names, int maxnames)
+{
+    int i;
+    remote_list_networks_args args;
+    remote_list_networks_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_NETWORK_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, "maxnames > REMOTE_NETWORK_NAME_LIST_MAX");
+        return -1;
+    }
+    args.maxnames = maxnames;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_LIST_NETWORKS,
+              (xdrproc_t) xdr_remote_list_networks_args, (char *) &args,
+              (xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, "ret.names.names_len > maxnames");
+        xdr_free ((xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_list_networks_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+static int
+remoteNumOfDefinedNetworks (virConnectPtr conn)
+{
+    remote_num_of_defined_networks_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_DEFINED_NETWORKS,
+              (xdrproc_t) xdr_void, (char *) NULL,
+              (xdrproc_t) xdr_remote_num_of_defined_networks_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static int
+remoteListDefinedNetworks (virConnectPtr conn,
+                           char **const names, int maxnames)
+{
+    int i;
+    remote_list_defined_networks_args args;
+    remote_list_defined_networks_ret ret;
+    GET_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_NETWORK_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, "maxnames > REMOTE_NETWORK_NAME_LIST_MAX");
+        return -1;
+    }
+    args.maxnames = maxnames;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_LIST_DEFINED_NETWORKS,
+              (xdrproc_t) xdr_remote_list_defined_networks_args, (char *) &args,
+              (xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, "ret.names.names_len > maxnames");
+        xdr_free ((xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_list_defined_networks_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+static virNetworkPtr
+remoteNetworkLookupByUUID (virConnectPtr conn,
+                           const unsigned char *uuid)
+{
+    virNetworkPtr net;
+    remote_network_lookup_by_uuid_args args;
+    remote_network_lookup_by_uuid_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    memcpy (args.uuid, uuid, VIR_UUID_BUFLEN);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NETWORK_LOOKUP_BY_UUID,
+              (xdrproc_t) xdr_remote_network_lookup_by_uuid_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_lookup_by_uuid_ret, (char *) &ret) == -1)
+        return NULL;
+
+    net = get_nonnull_network (conn, ret.net);
+    if (net == NULL)
+        error (conn, VIR_ERR_RPC, "remoteNetworkLookupByUUID: network not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_uuid_ret, (char *) &ret);
+
+    return net;
+}
+
+static virNetworkPtr
+remoteNetworkLookupByName (virConnectPtr conn,
+                           const char *name)
+{
+    virNetworkPtr net;
+    remote_network_lookup_by_name_args args;
+    remote_network_lookup_by_name_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    args.name = (char *) name;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NETWORK_LOOKUP_BY_NAME,
+              (xdrproc_t) xdr_remote_network_lookup_by_name_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_lookup_by_name_ret, (char *) &ret) == -1)
+        return NULL;
+
+    net = get_nonnull_network (conn, ret.net);
+    if (net == NULL)
+        error (conn, VIR_ERR_RPC, "remoteNetworkLookupByName: network not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_network_lookup_by_name_ret, (char *) &ret);
+
+    return net;
+}
+
+static virNetworkPtr
+remoteNetworkCreateXML (virConnectPtr conn, const char *xmlDesc)
+{
+    virNetworkPtr net;
+    remote_network_create_xml_args args;
+    remote_network_create_xml_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    args.xml = (char *) xmlDesc;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NETWORK_CREATE_XML,
+              (xdrproc_t) xdr_remote_network_create_xml_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_create_xml_ret, (char *) &ret) == -1)
+        return NULL;
+
+    net = get_nonnull_network (conn, ret.net);
+    if (net == NULL)
+        error (conn, VIR_ERR_RPC, "remoteNetworkCreateXML: network not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_network_create_xml_ret, (char *) &ret);
+
+    return net;
+}
+
+static virNetworkPtr
+remoteNetworkDefineXML (virConnectPtr conn, const char *xml)
+{
+    virNetworkPtr net;
+    remote_network_define_xml_args args;
+    remote_network_define_xml_ret ret;
+    GET_PRIVATE (conn, NULL);
+
+    args.xml = (char *) xml;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NETWORK_DEFINE_XML,
+              (xdrproc_t) xdr_remote_network_define_xml_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_define_xml_ret, (char *) &ret) == -1)
+        return NULL;
+
+    net = get_nonnull_network (conn, ret.net);
+    if (net == NULL)
+        error (conn, VIR_ERR_RPC, "remoteNetworkDefineXML: network not found");
+
+    xdr_free ((xdrproc_t) &xdr_remote_network_define_xml_ret, (char *) &ret);
+
+    return net;
+}
+
+static int
+remoteNetworkUndefine (virNetworkPtr network)
+{
+    remote_network_undefine_args args;
+    GET_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_UNDEFINE,
+              (xdrproc_t) xdr_remote_network_undefine_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteNetworkCreate (virNetworkPtr network)
+{
+    remote_network_create_args args;
+    GET_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_CREATE,
+              (xdrproc_t) xdr_remote_network_create_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static int
+remoteNetworkDestroy (virNetworkPtr network)
+{
+    remote_network_destroy_args args;
+    GET_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_DESTROY,
+              (xdrproc_t) xdr_remote_network_destroy_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+static char *
+remoteNetworkDumpXML (virNetworkPtr network, int flags)
+{
+    remote_network_dump_xml_args args;
+    remote_network_dump_xml_ret ret;
+    GET_PRIVATE (network->conn, NULL);
+
+    make_nonnull_network (&args.net, network);
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_DUMP_XML,
+              (xdrproc_t) xdr_remote_network_dump_xml_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_dump_xml_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Caller frees. */
+    return ret.xml;
+}
+
+static char *
+remoteNetworkGetBridgeName (virNetworkPtr network)
+{
+    remote_network_get_bridge_name_args args;
+    remote_network_get_bridge_name_ret ret;
+    GET_PRIVATE (network->conn, NULL);
+
+    make_nonnull_network (&args.net, network);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_GET_BRIDGE_NAME,
+              (xdrproc_t) xdr_remote_network_get_bridge_name_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_get_bridge_name_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Caller frees. */
+    return ret.name;
+}
+
+static int
+remoteNetworkGetAutostart (virNetworkPtr network, int *autostart)
+{
+    remote_network_get_autostart_args args;
+    remote_network_get_autostart_ret ret;
+    GET_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+
+    memset (&ret, 0, sizeof ret);
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_GET_AUTOSTART,
+              (xdrproc_t) xdr_remote_network_get_autostart_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_get_autostart_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (autostart) *autostart = ret.autostart;
+
+    return 0;
+}
+
+static int
+remoteNetworkSetAutostart (virNetworkPtr network, int autostart)
+{
+    remote_network_set_autostart_args args;
+    GET_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+    args.autostart = autostart;
+
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_SET_AUTOSTART,
+              (xdrproc_t) xdr_remote_network_set_autostart_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int really_write (virConnectPtr conn, struct private_data *priv,
+                         int in_open, char *bytes, int len);
+static int really_read (virConnectPtr conn, struct private_data *priv,
+                        int in_open, char *bytes, int len);
+
+/* This function performs a remote procedure call to procedure PROC_NR.
+ *
+ * NB. This does not free the args structure (not desirable, since you
+ * often want this allocated on the stack or else it contains strings
+ * which come from the user).  It does however free any intermediate
+ * results, eg. the error structure if there is one.
+ *
+ * NB(2). Make sure to memset (&ret, 0, sizeof ret) before calling,
+ * else Bad Things will happen in the XDR code.
+ */
+static int
+call (virConnectPtr conn, struct private_data *priv,
+      int in_open /* if we are in virConnectOpen */,
+      int proc_nr,
+      xdrproc_t args_filter, char *args,
+      xdrproc_t ret_filter, char *ret)
+{
+    char buffer[REMOTE_MESSAGE_MAX];
+    char buffer2[4];
+    struct remote_message_header hdr;
+    XDR xdr;
+    int len;
+    struct remote_error rerror;
+
+    /* Get a unique serial number for this message. */
+    int serial = priv->counter++;
+
+    hdr.prog = REMOTE_PROGRAM;
+    hdr.vers = REMOTE_PROTOCOL_VERSION;
+    hdr.proc = proc_nr;
+    hdr.direction = REMOTE_CALL;
+    hdr.serial = serial;
+    hdr.status = REMOTE_OK;
+
+    /* Serialise header followed by args. */
+    xdrmem_create (&xdr, buffer, sizeof buffer, XDR_ENCODE);
+    if (!xdr_remote_message_header (&xdr, &hdr)) {
+        error (in_open ? NULL : conn,
+               VIR_ERR_RPC, "xdr_remote_message_header");
+        return -1;
+    }
+
+    if (!(*args_filter) (&xdr, args)) {
+        error (in_open ? NULL : conn, VIR_ERR_RPC, "marshalling args");
+        return -1;
+    }
+
+    /* Get the length stored in buffer. */
+    len = xdr_getpos (&xdr);
+    xdr_destroy (&xdr);
+
+    /* Length must include the length word itself (always encoded in
+     * 4 bytes as per RFC 4506).
+     */
+    len += 4;
+
+    /* Encode the length word. */
+    xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_ENCODE);
+    if (!xdr_int (&xdr, &len)) {
+        error (in_open ? NULL : conn, VIR_ERR_RPC, "xdr_int (length word)");
+        return -1;
+    }
+    xdr_destroy (&xdr);
+
+    /* Send length word followed by header+args. */
+    if (really_write (conn, priv, in_open, buffer2, sizeof buffer2) == -1 ||
+        really_write (conn, priv, in_open, buffer, len-4) == -1)
+        return -1;
+
+    /* Read and deserialise length word. */
+    if (really_read (conn, priv, in_open, buffer2, sizeof buffer2) == -1)
+        return -1;
+
+    xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE);
+    if (!xdr_int (&xdr, &len)) {
+        error (in_open ? NULL : conn,
+               VIR_ERR_RPC, "xdr_int (length word, reply)");
+        return -1;
+    }
+    xdr_destroy (&xdr);
+
+    /* Length includes length word - adjust to real length to read. */
+    len -= 4;
+
+    if (len < 0 || len > REMOTE_MESSAGE_MAX) {
+        error (in_open ? NULL : conn,
+               VIR_ERR_RPC, "packet received from server too large");
+        return -1;
+    }
+
+    /* Read reply header and what follows (either a ret or an error). */
+    if (really_read (conn, priv, in_open, buffer, len) == -1)
+        return -1;
+
+    /* Deserialise reply header. */
+    xdrmem_create (&xdr, buffer, len, XDR_DECODE);
+    if (!xdr_remote_message_header (&xdr, &hdr)) {
+        error (in_open ? NULL : conn,
+               VIR_ERR_RPC, "xdr_remote_message_header (reply)");
+        return -1;
+    }
+
+    /* Check program, version, etc. are what we expect. */
+    if (hdr.prog != REMOTE_PROGRAM) {
+        __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
+                         VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
+                         "unknown program (received %x, expected %x)",
+                         hdr.prog, REMOTE_PROGRAM);
+        return -1;
+    }
+    if (hdr.vers != REMOTE_PROTOCOL_VERSION) {
+        __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
+                         VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
+                         "unknown protocol version (received %x, expected %x)",
+                         hdr.vers, REMOTE_PROTOCOL_VERSION);
+        return -1;
+    }
+
+    /* If we extend the server to actually send asynchronous messages, then
+     * we'll need to change this so that it can recognise an asynch
+     * message being received at this point.
+     */
+    if (hdr.proc != proc_nr) {
+        __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
+                         VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
+                         "unknown procedure (received %x, expected %x)",
+                         hdr.proc, proc_nr);
+        return -1;
+    }
+    if (hdr.direction != REMOTE_REPLY) {
+        __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
+                         VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
+                         "unknown direction (received %x, expected %x)",
+                         hdr.direction, REMOTE_REPLY);
+        return -1;
+    }
+    if (hdr.serial != serial) {
+        __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
+                         VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
+                         "unknown serial (received %x, expected %x)",
+                         hdr.serial, serial);
+        return -1;
+    }
+
+    /* Status is either REMOTE_OK (meaning that what follows is a ret
+     * structure), or REMOTE_ERROR (and what follows is a remote_error
+     * structure).
+     */
+    switch (hdr.status) {
+    case REMOTE_OK:
+        if (!(*ret_filter) (&xdr, ret)) {
+            error (in_open ? NULL : conn, VIR_ERR_RPC, "unmarshalling ret");
+            return -1;
+        }
+        xdr_destroy (&xdr);
+        return 0;
+
+    case REMOTE_ERROR:
+        memset (&rerror, 0, sizeof rerror);
+        if (!xdr_remote_error (&xdr, &rerror)) {
+            error (in_open ? NULL : conn,
+                   VIR_ERR_RPC, "unmarshalling remote_error");
+            return -1;
+        }
+        xdr_destroy (&xdr);
+        server_error (in_open ? NULL : conn, &rerror);
+        xdr_free ((xdrproc_t) xdr_remote_error, (char *) &rerror);
+        return -1;
+
+    default:
+        __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE,
+                         VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0,
+                         "unknown status (received %x)",
+                         hdr.status);
+        xdr_destroy (&xdr);
+        return -1;
+    }
+}
+
+static int
+really_write (virConnectPtr conn, struct private_data *priv,
+              int in_open /* if we are in virConnectOpen */,
+              char *bytes, int len)
+{
+    char *p;
+    int err;
+
+    p = bytes;
+    if (priv->uses_tls) {
+        do {
+            err = gnutls_record_send (priv->session, p, len);
+            if (err < 0) {
+                if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN)
+                    continue;
+                error (in_open ? NULL : conn,
+                       VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+                return -1;
+            }
+            len -= err;
+            p += err;
+        }
+        while (len > 0);
+    } else {
+        do {
+            err = write (priv->sock, p, len);
+            if (err == -1) {
+                if (errno == EINTR || errno == EAGAIN)
+                    continue;
+                error (in_open ? NULL : conn,
+                       VIR_ERR_SYSTEM_ERROR, strerror (errno));
+                return -1;
+            }
+            len -= err;
+            p += err;
+        }
+        while (len > 0);
+    }
+
+    return 0;
+}
+
+static int
+really_read (virConnectPtr conn, struct private_data *priv,
+             int in_open /* if we are in virConnectOpen */,
+             char *bytes, int len)
+{
+    char *p;
+    int err;
+
+    p = bytes;
+    if (priv->uses_tls) {
+        do {
+            err = gnutls_record_recv (priv->session, p, len);
+            if (err < 0) {
+                if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN)
+                    continue;
+                error (in_open ? NULL : conn,
+                       VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err));
+                return -1;
+            }
+            if (err == 0) {
+                error (in_open ? NULL : conn,
+                       VIR_ERR_RPC, "socket closed unexpectedly");
+                return -1;
+            }
+            len -= err;
+            p += err;
+        }
+        while (len > 0);
+    } else {
+        do {
+            err = read (priv->sock, p, len);
+            if (err == -1) {
+                if (errno == EINTR || errno == EAGAIN)
+                    continue;
+                error (in_open ? NULL : conn,
+                       VIR_ERR_SYSTEM_ERROR, strerror (errno));
+                return -1;
+            }
+            if (err == 0) {
+                error (in_open ? NULL : conn,
+                       VIR_ERR_RPC, "socket closed unexpectedly");
+                return -1;
+            }
+            len -= err;
+            p += err;
+        }
+        while (len > 0);
+    }
+
+    return 0;
+}
+
+/* For errors internal to this library. */
+static void
+error (virConnectPtr conn, virErrorNumber code, const char *info)
+{
+    const char *errmsg;
+
+    errmsg = __virErrorMsg (code, info);
+    __virRaiseError (conn, NULL, NULL, VIR_FROM_REMOTE,
+                     code, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0,
+                     errmsg, info);
+}
+
+/* For errors generated on the server side and sent back to us. */
+static void
+server_error (virConnectPtr conn, remote_error *err)
+{
+    virDomainPtr dom;
+    virNetworkPtr net;
+
+    /* Get the domain and network, if set.  OK to ignore the return
+     * value of get_nonnull_* since these are informational.
+     */
+    dom = err->dom ? get_nonnull_domain (conn, *err->dom) : NULL;
+    net = err->net ? get_nonnull_network (conn, *err->net) : NULL;
+
+    /* These strings are nullable.  OK to ignore the return value
+     * of strdup since these strings are informational.
+     */
+    char *str1 = err->str1 ? strdup (*err->str1) : NULL;
+    char *str2 = err->str2 ? strdup (*err->str2) : NULL;
+    char *str3 = err->str3 ? strdup (*err->str3) : NULL;
+
+    char *message = err->message ? strdup (*err->message) : NULL;
+
+    __virRaiseError (conn, dom, net,
+                     err->domain, err->code, err->level,
+                     str1, str2, str3,
+                     err->int1, err->int2,
+                     message);
+}
+
+/* get_nonnull_domain and get_nonnull_network turn an on-wire
+ * (name, uuid) pair into virDomainPtr or virNetworkPtr object.
+ * virDomainPtr or virNetworkPtr cannot be NULL.
+ *
+ * NB. If these return NULL then the caller must return an error.
+ */
+static virDomainPtr
+get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain)
+{
+    virDomainPtr dom;
+    dom = virGetDomain (conn, domain.name, BAD_CAST domain.uuid);
+    if (dom) dom->id = domain.id;
+    return dom;
+}
+
+static virNetworkPtr
+get_nonnull_network (virConnectPtr conn, remote_nonnull_network network)
+{
+    return virGetNetwork (conn, network.name, BAD_CAST network.uuid);
+}
+
+/* Make remote_nonnull_domain and remote_nonnull_network. */
+static void
+make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
+{
+    dom_dst->name = dom_src->name;
+    memcpy (dom_dst->uuid, dom_src->uuid, VIR_UUID_BUFLEN);
+}
+
+static void
+make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src)
+{
+    net_dst->name = net_src->name;
+    memcpy (net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN);
+}
+
+/*----------------------------------------------------------------------*/
+
+static virDriver driver = {
+    .no = VIR_DRV_REMOTE,
+    .name = "remote",
+    .ver = REMOTE_PROTOCOL_VERSION,
+    .open = remoteOpen,
+    .close = remoteClose,
+       .type = remoteType,
+       .version = remoteVersion,
+       .getMaxVcpus = remoteGetMaxVcpus,
+       .nodeGetInfo = remoteNodeGetInfo,
+    .getCapabilities = remoteGetCapabilities,
+    .listDomains = remoteListDomains,
+    .numOfDomains = remoteNumOfDomains,
+    .domainCreateLinux = remoteDomainCreateLinux,
+    .domainLookupByID = remoteDomainLookupByID,
+    .domainLookupByUUID = remoteDomainLookupByUUID,
+    .domainLookupByName = remoteDomainLookupByName,
+    .domainSuspend = remoteDomainSuspend,
+    .domainResume = remoteDomainResume,
+    .domainShutdown = remoteDomainShutdown,
+    .domainReboot = remoteDomainReboot,
+    .domainDestroy = remoteDomainDestroy,
+    .domainGetOSType = remoteDomainGetOSType,
+    .domainGetMaxMemory = remoteDomainGetMaxMemory,
+    .domainSetMaxMemory = remoteDomainSetMaxMemory,
+    .domainSetMemory = remoteDomainSetMemory,
+    .domainGetInfo = remoteDomainGetInfo,
+    .domainSave = remoteDomainSave,
+    .domainRestore = remoteDomainRestore,
+    .domainCoreDump = remoteDomainCoreDump,
+    .domainSetVcpus = remoteDomainSetVcpus,
+    .domainPinVcpu = remoteDomainPinVcpu,
+    .domainGetVcpus = remoteDomainGetVcpus,
+    .domainGetMaxVcpus = remoteDomainGetMaxVcpus,
+    .domainDumpXML = remoteDomainDumpXML,
+    .listDefinedDomains = remoteListDefinedDomains,
+    .numOfDefinedDomains = remoteNumOfDefinedDomains,
+    .domainCreate = remoteDomainCreate,
+    .domainDefineXML = remoteDomainDefineXML,
+    .domainUndefine = remoteDomainUndefine,
+    .domainAttachDevice = remoteDomainAttachDevice,
+    .domainDetachDevice = remoteDomainDetachDevice,
+    .domainGetAutostart = remoteDomainGetAutostart,
+    .domainSetAutostart = remoteDomainSetAutostart,
+};
+
+static virNetworkDriver network_driver = {
+    .open = remoteNetworkOpen,
+    .close = remoteNetworkClose,
+    .numOfNetworks = remoteNumOfNetworks,
+    .listNetworks = remoteListNetworks,
+    .numOfDefinedNetworks = remoteNumOfDefinedNetworks,
+    .listDefinedNetworks = remoteListDefinedNetworks,
+    .networkLookupByUUID = remoteNetworkLookupByUUID,
+    .networkLookupByName = remoteNetworkLookupByName,
+    .networkCreateXML = remoteNetworkCreateXML,
+    .networkDefineXML = remoteNetworkDefineXML,
+    .networkUndefine = remoteNetworkUndefine,
+    .networkCreate = remoteNetworkCreate,
+    .networkDestroy = remoteNetworkDestroy,
+    .networkDumpXML = remoteNetworkDumpXML,
+    .networkGetBridgeName = remoteNetworkGetBridgeName,
+    .networkGetAutostart = remoteNetworkGetAutostart,
+    .networkSetAutostart = remoteNetworkSetAutostart,
+};
+
+/* remoteRegister:
+ *
+ * Register driver with libvirt driver system.
+ */
+int
+remoteRegister (void)
+{
+    if (virRegisterDriver (&driver) == -1) return -1;
+    if (virRegisterNetworkDriver (&network_driver) == -1) return -1;
+
+    return 0;
+}
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/remote_internal.h b/src/remote_internal.h
new file mode 100644 (file)
index 0000000..bbd2d37
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * remote_internal.h: driver to provide access to libvirtd running
+ *   on a remote machine
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Richard Jones <rjones@redhat.com>
+ */
+
+#ifndef __VIR_REMOTE_INTERNAL_H__
+#define __VIR_REMOTE_INTERNAL_H__
+
+#include <libvirt/virterror.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int remoteRegister (void);
+
+#define LIBVIRTD_TLS_PORT "16514"
+#define LIBVIRTD_TCP_PORT "16509"
+#define LIBVIRTD_UNIX_SOCKET LOCAL_STATE_DIR "/run/libvirt/libvirt-sock"
+#define LIBVIRTD_UNIX_SOCKET_RO LOCAL_STATE_DIR "/run/libvirt/libvirt-sock-ro"
+#define LIBVIRTD_CONFIGURATION_FILE SYSCONF_DIR "/libvirtd.conf"
+
+/* Defaults for PKI directory. */
+#define LIBVIRT_PKI_DIR SYSCONF_DIR "/pki"
+#define LIBVIRT_CACERT LIBVIRT_PKI_DIR "/CA/cacert.pem"
+#define LIBVIRT_CLIENTKEY LIBVIRT_PKI_DIR "/libvirt/private/clientkey.pem"
+#define LIBVIRT_CLIENTCERT LIBVIRT_PKI_DIR "/libvirt/clientcert.pem"
+#define LIBVIRT_SERVERKEY LIBVIRT_PKI_DIR "/libvirt/private/serverkey.pem"
+#define LIBVIRT_SERVERCERT LIBVIRT_PKI_DIR "/libvirt/servercert.pem"
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __VIR_REMOTE_INTERNAL_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */